(转载)ffmpe---实现将视频存储为图片jpg
原文出自:https://mp.weixin.qq.com/s/z1LXwRVPlDqxn_p3DIFOag
#include <iostream> #include <stdio.h> #include <Windows.h> using namespace std; extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavdevice/avdevice.h" #include "libavutil/pixfmt.h" } /*================================================================================== * 将AVFrame(YUV420格式)保存为JPEG格式的图片 ===================================================================================*/ int SaveAsJPEG(AVFrame* pFrame, int width, int height, int index) { // 输出文件路径 char out_file[MAX_PATH] = { 0 }; //sprintf_s(out_file, sizeof(out_file), "%s%d.jpg", "E:/QT/test_ffmpegSavePic/ffmpeg/output/", index); sprintf_s(out_file, sizeof(out_file), "%s%d.jpg", "../output/", index); // 分配AVFormatContext对象 AVFormatContext* pFormatCtx = avformat_alloc_context(); // 设置输出文件格式 pFormatCtx->oformat = av_guess_format("mjpeg", NULL, NULL); // 创建并初始化一个和该url相关的AVIOContext if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) { printf("Couldn't open output file."); return -1; } // 构建一个新stream AVStream* pAVStream = avformat_new_stream(pFormatCtx, 0); if (pAVStream == NULL) { return -1; } // 设置该stream的信息 AVCodecContext* pCodecCtx = pAVStream->codec; pCodecCtx->codec_id = pFormatCtx->oformat->video_codec; pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO; pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P; pCodecCtx->width = width; pCodecCtx->height = height; pCodecCtx->time_base.num = 1; pCodecCtx->time_base.den = 25; //打印输出相关信息 av_dump_format(pFormatCtx, 0, out_file, 1); //================================== 查找编码器 ==================================// AVCodec* pCodec = avcodec_find_encoder(pCodecCtx->codec_id); if (!pCodec) { printf("Codec not found."); return -1; } // 设置pCodecCtx的解码器为pCodec if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { printf("Could not open codec."); return -1; } //================================Write Header ===============================// avformat_write_header(pFormatCtx, NULL); int y_size = pCodecCtx->width * pCodecCtx->height; //==================================== 编码 ==================================// // 给AVPacket分配足够大的空间 AVPacket pkt; av_new_packet(&pkt, y_size * 3); int got_picture = 0; int ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture); if (ret < 0) { printf("Encode Error.\n"); return -1; } if (got_picture == 1) { pkt.stream_index = pAVStream->index; ret = av_write_frame(pFormatCtx, &pkt); } av_free_packet(&pkt); //Write Trailer av_write_trailer(pFormatCtx); //if (pFrame){ // av_frame_unref(pFrame); //} if (pAVStream) { avcodec_close(pAVStream->codec); } avio_close(pFormatCtx->pb); avformat_free_context(pFormatCtx); return 0; } //没有完全源代码,自己修改过的 void FFmpegDemo::on_pictureBtn2_clicked() { //1.FFMPEG初始化操作 av_register_all(); //初始化FFMPEG 调用了这个才能正常适用编码器和解码器 //=========================== 创建AVFormatContext结构体 ===============================// //分配一个AVFormatContext,FFMPEG所有的操作都要通过这个AVFormatContext来进行 AVFormatContext *pFormatCtx = avformat_alloc_context(); //==================================== 打开文件 ======================================// char *file_path = "./2.mp4";//这里必须使用左斜杠 int ret = avformat_open_input(&pFormatCtx, file_path, NULL, NULL); if (ret != 0) { cout << "open error!" << endl; return ; } //循环查找视频中包含的流信息,直到找到视频类型的流 //便将其记录下来 保存到videoStream变量中 int i; int videoStream; //=================================== 获取视频流信息 ===================================// if (avformat_find_stream_info(pFormatCtx, NULL) < 0) { cout << "Could't find stream infomation." << endl; return ; } videoStream = -1; for (i = 0; i < pFormatCtx->nb_streams; i++) { if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { videoStream = i; } } //如果videoStream为-1 说明没有找到视频流 if (videoStream == -1) { cout << "Didn't find a video stream." << endl; return ; } //================================= 查找解码器 ===================================// AVCodecContext* pCodecCtx = pFormatCtx->streams[videoStream]->codec; AVCodec* pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == NULL) { cout << "Codec not found." << endl; return ; } //================================ 打开解码器 ===================================// if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)// 具体采用什么解码器ffmpeg经过封装 我们无须知道 { cout << "Could not open codec." << endl; return ; } //================================ 设置数据转换参数 ================================// SwsContext * img_convert_ctx; img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, //源地址长宽以及数据格式 pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUVJ420P, //目的地址长宽以及数据格式 SWS_BICUBIC, NULL, NULL, NULL);//算法类型 AV_PIX_FMT_YUVJ420P AV_PIX_FMT_BGR24 //==================================== 分配空间 ==================================// //一帧图像数据大小 int numBytes = avpicture_get_size(AV_PIX_FMT_YUVJ420P, pCodecCtx->width, pCodecCtx->height); unsigned char *out_buffer; out_buffer = (unsigned char *)av_malloc(numBytes * sizeof(unsigned char)); AVFrame * pFrame; pFrame = av_frame_alloc(); AVFrame * pFrameRGB; pFrameRGB = av_frame_alloc(); avpicture_fill((AVPicture *)pFrameRGB, out_buffer, AV_PIX_FMT_YUVJ420P, pCodecCtx->width, pCodecCtx->height); //会将pFrameRGB的数据按RGB格式自动"关联"到buffer 即pFrameRGB中的数据改变了 out_buffer中的数据也会相应的改变 //=========================== 分配AVPacket结构体 ===============================// int y_size = pCodecCtx->width * pCodecCtx->height; AVPacket *packet = (AVPacket *)malloc(sizeof(AVPacket)); //分配一个packet av_new_packet(packet, y_size); //分配packet的数据 //2.循环采集视频流数据 将其转换为图片 //=========================== 读取视频信息 ===============================// int index = 0; //读取的是一帧视频 数据存入一个AVPacket的结构中 while (av_read_frame(pFormatCtx, packet) >= 0) { //此时数据存储在packet中 if (packet && packet->stream_index == videoStream) { //视频解码函数 解码之后的数据存储在 pFrame中 int got_picture = 0; ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet); if (ret < 0) { cout << "decode error." << endl; return; } //转换一帧图像 sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height, //源 pFrameRGB->data, pFrameRGB->linesize); //目的 SaveAsJPEG(pFrameRGB, pCodecCtx->width, pCodecCtx->height, index++); //保存图片 } } free(packet); av_frame_free(&pFrameRGB); av_frame_free(&pFrame); avcodec_close(pCodecCtx); avformat_free_context(pFormatCtx); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!