ffmpeg C代码实现 把视频流转换成图片保存到本地

本文介绍了一种使用C语言和FFmpeg库将视频帧转换为图片的方法。通过解析视频流,解码帧,并将其保存为JPEG格式,实现了视频帧的图片化。文章详细展示了代码实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用C代码实现,把视频中的帧转换成图片保存在本地。

#include <stdio.h>
#include <stdlib.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
#include "libavutil/log.h"
int main(){
    // char* filename = "./jichi.mp4";
    //如果本地没有视频可以使用这个直播流地址
    char* filename = "http://weblive.hebtv.com/live/hbws_bq/index.m3u8";
    AVFormatContext* avf_cxt = avformat_alloc_context();
    int ret = avformat_open_input(&avf_cxt,filename,NULL,NULL);
    if(ret < 0){
        av_log(NULL,AV_LOG_ERROR,"不能打开文件\n");
        return -1;
    }
    ret = avformat_find_stream_info(avf_cxt,NULL);
    if(ret < 0){
        av_log(NULL,AV_LOG_ERROR,"找不到流数据\n");
        goto _end;
    }
    //打印视频信息
    av_dump_format(avf_cxt,0,filename,0);
    int video_index = -1;
    for(int i = 0 ; i < avf_cxt->nb_streams; i++){
        if(avf_cxt->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
            video_index = i;
            break;
        }
    }
    if(video_index == -1){
        av_log(NULL,AV_LOG_ERROR,"没有找到视频流\n");
        goto _end;
    }
    AVCodecContext* avc_cxt = avf_cxt->streams[video_index]->codec;
    enum AVCodecID codecId = avc_cxt->codec_id;
    AVCodec* codec = avcodec_find_decoder(codecId);
    if(!codec){
        av_log(NULL,AV_LOG_ERROR,"没有找到解码器\n");
        goto _end;
    }
    ret = avcodec_open2(avc_cxt,codec,NULL);
    if(ret < 0){
        av_log(NULL,AV_LOG_ERROR,"解码器无法打开\n");
        goto _end;
    }
    //为avpacket分配内存
    AVPacket* packet = av_packet_alloc();
    //为avFrame分配内存
    AVFrame* frame = av_frame_alloc();
    while(av_read_frame(avf_cxt,packet) >= 0){
        if(packet && packet->stream_index == video_index){
            int gotFrame = 0;
            ret = avcodec_decode_video2(avc_cxt,frame,&gotFrame,packet);
            if(gotFrame){
                ret = writeJPEG(frame,avc_cxt->width,avc_cxt->height);
                if(ret == 0){
                    break;
                }
            }
        }
    }

    _end:
    av_frame_free(frame);
    avcodec_close(avc_cxt);
    avformat_free_context(avf_cxt);
    return 0;
}
int writeJPEG(AVFrame* frame,int width,int height){
    const char* out_file = "hello_world.jpg";
    //新建一个输出的AVFormatContext 并分配内存
    AVFormatContext* output_cxt = avformat_alloc_context();
    avformat_alloc_output_context2(&output_cxt,NULL,"singlejpeg",out_file);

    //设置输出文件的格式
    // output_cxt->oformat = av_guess_format("mjpeg",NULL,NULL);

    //创建和初始化一个和该URL相关的AVIOContext
    if(avio_open(&output_cxt->pb,out_file,AVIO_FLAG_READ_WRITE) < 0){
        av_log(NULL,AV_LOG_ERROR,"不能打开文件  \n");
        return -1;
    }

    //构建新的Stream
    AVStream* stream = avformat_new_stream(output_cxt,NULL);
    if(stream == NULL){
        av_log(NULL,AV_LOG_ERROR,"创建AVStream失败  \n");
        return -1;
    }
    //初始化AVStream信息
    AVCodecContext* codec_cxt = stream->codec;

    codec_cxt->codec_id = output_cxt->oformat->video_codec;
    codec_cxt->codec_type = AVMEDIA_TYPE_VIDEO;
    codec_cxt->pix_fmt = AV_PIX_FMT_YUVJ420P;
    codec_cxt->height = height;
    codec_cxt->width = width;
    codec_cxt->time_base.num = 1;
    codec_cxt->time_base.den = 25;

    //打印输出文件信息
    av_dump_format(output_cxt,0,out_file,1);

    AVCodec* codec = avcodec_find_encoder(codec_cxt->codec_id);
    if(!codec){
        av_log(NULL,AV_LOG_ERROR,"找不到编码器  \n");
        return -1;
    }

    if(avcodec_open2(codec_cxt,codec,NULL) < 0){
        av_log(NULL,AV_LOG_ERROR,"不能打开编码器  \n");
        return -1;
    }
    avcodec_parameters_from_context(stream->codecpar,codec_cxt);

    //写入文件头
    avformat_write_header(output_cxt,NULL);
    int size = codec_cxt->width * codec_cxt->height;

    AVPacket* packet;
    av_new_packet(packet,size * 3);

    int got_picture = 0;
    int result = avcodec_encode_video2(codec_cxt,packet,frame,&got_picture);
    if(result < 0){
        av_log(NULL,AV_LOG_ERROR,"编码失败  \n");
        return -1;
    }
    printf("got_picture %d \n",got_picture);
    if(got_picture == 1){
        //将packet中的数据写入本地文件
        result = av_write_frame(output_cxt,packet);
    }
    av_free_packet(packet);
    //将流尾写入输出媒体文件并释放文件数据
    av_write_trailer(output_cxt);
    if(frame){
        av_frame_unref(frame);
    }
    avio_close(output_cxt->pb);
    avformat_free_context(output_cxt);
    return 0;
}
<think>好的,用户的问题是关于如何在纯前端环境下使用ffmpeg.js将视频流转码为图片。首先,我需要确认用户的需求:他们希望在前端直接处理视频流,而不依赖后端服务。这可能是因为隐私考虑,或者希望减少服务器负载,提高处理速度。 接下来,我需要回忆ffmpeg.js的相关知识。ffmpeg.js是FFmpeg的Emscripten移植版本,可以在浏览器中运行,处理音视频文件。用户提到的视频流转换,可能需要从视频中提取帧,保存图片,比如JPEG或PNG。 首先,用户需要引入ffmpeg.js库。常见的做法是通过CDN链接,或者npm安装。但纯前端环境下,CDN可能更方便。然后,创建一个HTML页面,包含视频输入、文件上传按钮和运行按钮。用户上传视频文件后,使用FileReader读取文件内容,传递给ffmpeg.js进行处理。 处理过程中,ffmpeg.js需要指定输入文件、输出格式和参数。例如,使用`-i input.mp4`指定输入,`-r 1`设置每秒提取一帧,`-f image2 output_%03d.jpg`定义输出文件名格式。处理完成后,生成的图片数据可以通过回调函数获取,然后在前端展示或下载。 需要注意的问题包括:性能问题,因为视频转码是计算密集型任务,大文件可能导致浏览器卡顿或崩溃;浏览器兼容性,不同浏览器对WebAssembly的支持可能不同;输出文件大小,需要适当调整参数避免内存不足;以及跨域问题,如果视频来自不同源,可能需要配置CORS。 可能的相关问题包括如何优化处理速度,处理不同视频格式,提取特定帧,或处理实时视频流。这些扩展问题可能需要更深入的研究,比如使用Web Workers来避免阻塞主线程,或者调整ffmpeg参数提高效率。 需要确保提供的代码示例正确,并且步骤清晰。同时,引用用户提供的参考资料,但注意检查引用是否相关。例如,用户提供的引用中并没有直接关于ffmpeg.js的内容,可能需忽略或寻找其他可靠来源。不过,根据知识库,用户可能希望引用内部资料,但当前引用似乎不相关,所以可能不需要添加引用标识。 最后,验证步骤是否正确,比如测试代码是否能正确提取视频帧,图片是否能正常生成和下载。如果有错误,可能需要调试ffmpeg参数或检查文件读取过程。</think>以下是如何在纯前端环境下使用ffmpeg.js实现视频流转码为图片的完整解决方案: ### 一、核心实现步骤 1. **引入ffmpeg.js库** ```html <script src="https://cdn.jsdelivr.net/npm/@ffmpeg/ffmpeg@0.11.0/dist/ffmpeg.min.js"></script> ``` 2. **创建基础HTML结构** ```html <input type="file" id="videoInput"> <button onclick="convert()">开始转换</button> <div id="output"></div> ``` 3. **JavaScript处理逻辑** ```javascript const { createFFmpeg, fetchFile } = FFmpeg; const ffmpeg = createFFmpeg({ log: true }); async function convert() { const videoFile = document.getElementById('videoInput').files[0]; // 初始化ffmpeg if (!ffmpeg.isLoaded()) { await ffmpeg.load(); } // 写入内存文件系统 ffmpeg.FS('writeFile', 'input.mp4', await fetchFile(videoFile)); // 执行转码命令(每秒提取1帧) await ffmpeg.run( '-i', 'input.mp4', '-r', '1', // 帧率控制 '-f', 'image2', 'output_%03d.jpg' // 输出文件名格式 ); // 读取生成的图片 const files = ffmpeg.FS('readdir', '/'); const output = document.getElementById('output'); files.filter(f => f.startsWith('output_')).forEach(async (filename) => { const data = ffmpeg.FS('readFile', filename); const img = document.createElement('img'); img.src = URL.createObjectURL(new Blob([data.buffer], { type: 'image/jpeg' })); output.appendChild(img); }); } ``` ### 二、关键参数说明 1. **帧提取参数** - `-r 1`: 每秒提取1帧,调整数值可改变提取频率 - `-ss 00:00:05`: 从第5秒开始处理 - `-vframes 10`: 仅提取前10帧 2. **输出格式控制** ```javascript // PNG格式输出示例 await ffmpeg.run( '-i', 'input.mp4', '-r', '1', 'output_%03d.png' // 输出PNG格式 ); ``` ### 三、性能优化建议 1. **使用Web Workers** 避免阻塞主线程 2. **设置分辨率限制** 添加参数: ```bash -vf scale=320:-1 # 限制宽度为320px ``` 3. **分片处理** 对长视频分段处理: ```bash -t 10 # 每次处理10秒视频 ``` ### 四、浏览器兼容性说明 | 浏览器 | WebAssembly支持 | 建议版本 | |--------------|-----------------|------------| | Chrome | ✔️ | 57+ | | Firefox | ✔️ | 52+ | | Safari | ✔️ | 11+ | | Edge | ✔️ | 16+ | ### 五、常见问题处理 1. **内存不足错误** ```javascript // 初始化时配置内存大小 const ffmpeg = createFFmpeg({ corePath: 'https://unpkg.com/@ffmpeg/core@0.11.0/dist/ffmpeg-core.js', log: true, progress: p => console.log(p), }); ``` 2. **跨域问题处理** ```html <!-- 需要服务端设置 --> <img src="..." crossorigin="anonymous"> ```
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值