[FFmpeg学习]从视频中获取图片

从视频中获取图片是一个比较直观的例子,这里从一个基础的例子来查看FFmpeg相关api的使用,从mp4文件中获取一帧图像,保存为jpeg格式图片,mp4文件比较好准备,一般手机录屏文件就是mp4格式。

原理还是比较清楚,得到一个AVFrame后,再使用jpeg的编码器来转换

int getpic() {std::string filename = "test.mp4";     // 输入MP4文件名std::string outputFilename = "output.jpg";  // 输出图片文件名int targetSecond = 1;    // 目标秒数AVFormatContext* formatContext = nullptr;if (avformat_open_input(&formatContext, filename.c_str(), nullptr, nullptr) != 0) {std::cerr << "Error opening input file" << std::endl;return -1;}if (avformat_find_stream_info(formatContext, nullptr) < 0) {std::cerr << "Error finding stream information" << std::endl;avformat_close_input(&formatContext);return -1;}const AVCodec* codec = nullptr;int videoStreamIndex = -1;for (unsigned int i = 0; i < formatContext->nb_streams; i++) {if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {videoStreamIndex = i;codec = avcodec_find_decoder(formatContext->streams[i]->codecpar->codec_id);break;}}if (videoStreamIndex == -1 || codec == nullptr) {std::cerr << "Error finding video stream or decoder" << std::endl;avformat_close_input(&formatContext);return -1;}AVCodecContext* codecContext = avcodec_alloc_context3(codec);if (codecContext == nullptr) {std::cerr << "Error allocating codec context" << std::endl;avformat_close_input(&formatContext);return -1;}if (avcodec_parameters_to_context(codecContext, formatContext->streams[videoStreamIndex]->codecpar) < 0) {std::cerr << "Error setting codec parameters" << std::endl;avcodec_free_context(&codecContext);avformat_close_input(&formatContext);return -1;}if (avcodec_open2(codecContext, codec, nullptr) < 0) {std::cerr << "Error opening codec" << std::endl;avcodec_free_context(&codecContext);avformat_close_input(&formatContext);return -1;}AVPacket packet;av_init_packet(&packet);// 计算目标时间戳int64_t targetTimestamp = targetSecond * AV_TIME_BASE;// 查找目标时间戳所对应的帧AVFrame* frame = av_frame_alloc();bool foundTargetFrame = false;int count = 0;while (av_read_frame(formatContext, &packet) >= 0) {if (packet.stream_index == videoStreamIndex) {int response = avcodec_send_packet(codecContext, &packet);if (response < 0) {std::cerr << "Error sending packet to decoder" << std::endl;break;}count++;response = avcodec_receive_frame(codecContext, frame);if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {continue;}else if (response < 0) {std::cerr << "Error receiving frame from decoder" << std::endl;break;}// 检查帧的时间戳是否接近目标时间戳/*if (frame->pts >= targetTimestamp - (AV_TIME_BASE / 2) && frame->pts <= targetTimestamp + (AV_TIME_BASE / 2)) {foundTargetFrame = true;break;}*/if (count == 20) {foundTargetFrame = true;char outname[] = "out.jpg";
//                savePicture(frame, outname);break;}}av_packet_unref(&packet);}if (!foundTargetFrame) {std::cerr << "Target frame not found" << std::endl;av_frame_free(&frame);avcodec_free_context(&codecContext);avformat_close_input(&formatContext);return -1;}// 将帧的数据保存为JPEG图片AVFrame* rgbFrame = av_frame_alloc();if (rgbFrame == nullptr) {std::cerr << "Error allocating RGB frame" << std::endl;av_frame_free(&frame);avcodec_free_context(&codecContext);avformat_close_input(&formatContext);return -1;}/*struct SwsContext* swsContext= sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt,codecContext->width, codecContext->height, AV_PIX_FMT_RGB24,SWS_BILINEAR, nullptr, nullptr, nullptr);if (swsContext == nullptr) {std::cerr << "Error creating SwsContext" << std::endl;av_frame_free(&frame);av_frame_free(&rgbFrame);avcodec_free_context(&codecContext);avformat_close_input(&formatContext);return -1;}// 分配RGB帧的缓冲区int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, codecContext->width, codecContext->height, 1);uint8_t* buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, AV_PIX_FMT_RGB24, codecContext->width, codecContext->height, 1);// 将解码后的帧转换为RGB格式sws_scale(swsContext, frame->data, frame->linesize, 0, codecContext->height, rgbFrame->data, rgbFrame->linesize);*/// 保存RGB帧为JPEG图片const AVCodec* jpegCodec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);if (jpegCodec == nullptr) {std::cerr << "Error finding JPEG encoder" << std::endl;av_frame_free(&frame);av_frame_free(&rgbFrame);avcodec_free_context(&codecContext);avformat_close_input(&formatContext);return -1;}AVCodecContext* jpegCodecContext = avcodec_alloc_context3(jpegCodec);if (jpegCodecContext == nullptr) {std::cerr << "Error allocating JPEG codec context" << std::endl;av_frame_free(&frame);av_frame_free(&rgbFrame);avcodec_free_context(&codecContext);avformat_close_input(&formatContext);return -1;}jpegCodecContext->pix_fmt = AV_PIX_FMT_YUVJ420P;jpegCodecContext->width = codecContext->width;jpegCodecContext->height = codecContext->height;// 设置编码器时间基jpegCodecContext->time_base = { 1, 25 };//formatContext->streams[videoStreamIndex]->time_base;if (avcodec_open2(jpegCodecContext, jpegCodec, nullptr) < 0) {std::cerr << "Error opening JPEG codec" << std::endl;av_frame_free(&frame);av_frame_free(&rgbFrame);avcodec_free_context(&codecContext);avcodec_free_context(&jpegCodecContext);avformat_close_input(&formatContext);return -1;}AVPacket jpegPacket;av_init_packet(&jpegPacket);jpegPacket.data = nullptr;jpegPacket.size = 0;if (avcodec_send_frame(jpegCodecContext, frame) < 0) {//rgbFramestd::cerr << "Error sending frame to JPEG encoder" << std::endl;av_frame_free(&frame);av_frame_free(&rgbFrame);avcodec_free_context(&codecContext);avcodec_free_context(&jpegCodecContext);avformat_close_input(&formatContext);return -1;}if (avcodec_receive_packet(jpegCodecContext, &jpegPacket) < 0) {std::cerr << "Error receiving packet from JPEG encoder" << std::endl;av_frame_free(&frame);av_frame_free(&rgbFrame);avcodec_free_context(&codecContext);avcodec_free_context(&jpegCodecContext);avformat_close_input(&formatContext);return -1;}// 保存JPEG图像到文件FILE* outputFile = fopen(outputFilename.c_str(), "wb");if (outputFile == nullptr) {std::cerr << "Error opening output file" << std::endl;av_frame_free(&frame);av_frame_free(&rgbFrame);avcodec_free_context(&codecContext);avcodec_free_context(&jpegCodecContext);avformat_close_input(&formatContext);return -1;}fwrite(jpegPacket.data, 1, jpegPacket.size, outputFile);fclose(outputFile);// 清理资源av_frame_free(&frame);av_frame_free(&rgbFrame);av_packet_unref(&packet);av_packet_unref(&jpegPacket);avcodec_free_context(&codecContext);return 1;
}

获取的图片看上去不是太清晰,字有些糊掉了

从AVFrame保存为jpg图片的处理可以有另外的一个方式,有些差异,

int savePicture(AVFrame* pFrame, char* out_name) {//编码保存图片int width = pFrame->width;int height = pFrame->height;AVCodecContext* pCodeCtx = NULL;AVFormatContext* pFormatCtx = avformat_alloc_context();// 设置输出文件格式pFormatCtx->oformat = av_guess_format("mjpeg", NULL, NULL);// 创建并初始化输出AVIOContextif (avio_open(&pFormatCtx->pb, out_name, AVIO_FLAG_READ_WRITE) < 0) {printf("Couldn't open output file.");return -1;}// 构建一个新streamAVStream* pAVStream = avformat_new_stream(pFormatCtx, 0);if (pAVStream == NULL) {return -1;}AVCodecParameters* parameters = pAVStream->codecpar;parameters->codec_id = pFormatCtx->oformat->video_codec;parameters->codec_type = AVMEDIA_TYPE_VIDEO;parameters->format = AV_PIX_FMT_YUVJ420P;parameters->width = pFrame->width;parameters->height = pFrame->height;const AVCodec* pCodec = avcodec_find_encoder(pAVStream->codecpar->codec_id);  //查找编码器if (!pCodec) {printf("Could not find encoder\n");return -1;}pCodeCtx = avcodec_alloc_context3(pCodec);   //为AVCodecContext分配内存if (!pCodeCtx) {fprintf(stderr, "Could not allocate video codec context\n");exit(1);}if ((avcodec_parameters_to_context(pCodeCtx, pAVStream->codecpar)) < 0) {fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",av_get_media_type_string(AVMEDIA_TYPE_VIDEO));return -1;}//  AVRational tmp = { 1, 25 };pCodeCtx->time_base = { 1, 25 };if (avcodec_open2(pCodeCtx, pCodec, NULL) < 0) {   //打开编码器printf("Could not open codec.");return -1;}int ret = avformat_write_header(pFormatCtx, NULL);if (ret < 0) {printf("write_header fail\n");return -1;}int y_size = width * height;//Encode// 给AVPacket分配足够大的空间AVPacket pkt;av_new_packet(&pkt, y_size * 3);// 编码数据ret = avcodec_send_frame(pCodeCtx, pFrame);if (ret < 0) {printf("Could not avcodec_send_frame.");return -1;}// 得到编码后数据ret = avcodec_receive_packet(pCodeCtx, &pkt);if (ret < 0) {printf("Could not avcodec_receive_packet");return -1;}ret = av_write_frame(pFormatCtx, &pkt);if (ret < 0) {printf("Could not av_write_frame");return -1;}av_packet_unref(&pkt);//Write Trailerav_write_trailer(pFormatCtx);avcodec_close(pCodeCtx);avio_close(pFormatCtx->pb);avformat_free_context(pFormatCtx);return 0;
}

参考资料

FFmpeg将视频转换成一帧一帧的jpeg图片(代码实现)_ffmpeg把视频转为一帧帧图片-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/468046.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络——09Web-and-HTTP

Web and HTTP 一些术语 Web页&#xff1a;由一些对象组成对象可以是HTML文件、JPEG图像&#xff0c;JAVA小程序&#xff0c;声音剪辑文件等Web页含有一个基本的HTML文件&#xff0c;该基本HTML文件又包含若干对象的引用&#xff08;链接&#xff09;通过URL对每个对象进行引用…

C#,巴都万数列(Padonve Number)的算法与源代码

1 巴都万数列&#xff08;Padovan Sequence&#xff09; 巴都万数列&#xff08;Padovan Sequence&#xff09;是一个整数数列。 首数个值为1, 1, 1, 2, 2, 3, 4, 5, 7, 9, 12, 16, 21, 28, 37 ... 此数列以建筑师理察巴都万命名&#xff0c;他的论文Dom&#xff08;1994年&a…

九、java 继承

文章目录 java 继承3.1 根父类Object3.2 方法重写3.3 继承案例&#xff1a;图形类继承体系3.4 继承的细节3.4.1 构造方法3.4.2 重名与静态绑定3.4.3 重载和重写3.4.4 父子类型转换3.4.5 继承访问权限protected3.4.6 可见性重写3.4.7 防止继承final 3.5 继承是把双刃剑3.5.1 继承…

Android13多媒体框架概览

Android13多媒体框架概览 Android 多媒体框架 Android 多媒体框架旨在为 Java 服务提供可靠的接口。它是一个系统&#xff0c;包括多媒体应用程序、框架、OpenCore 引擎、音频/视频/输入的硬件设备&#xff0c;输出设备以及一些核心动态库&#xff0c;比如 libmedia、libmedi…

【C++初阶】第三站:类和对象(中) -- 类的6个默认成员函数

目录 前言 类的6个默认成员函数 构造函数 概念 特性 析构函数 概念 特性 拷贝构造函数 概念 特征 赋值运算符重载 运算符重载 赋值运算符重载 const成员 const修饰类成员函数 取地址及const取地址操作符重载 本章总结&#xff1a; 前言 有时候我们写好了一个栈&#xff0c;头脑…

Excel

1、Excel的学习路径 2、掌握excel的基础要求 01、保证新版本 02、培养好的数据表格习惯 03、主动性探索 04、多联系 一、函数 二、文本清洗函数 三、常见文本的清洗函数 获取k的位置 FIND("k",P2,1) 从第1个位置开始在位置P2&#xff0c;查询字段k&#x…

【MySQL】索引事务

MySQL索引事务 1. 索引1.1 概念1.2 作用1.3 使用场景1.4 使用1.5 案例 2. 事务2.2 事物的概念2.3 使用 3. 内容重点总结 1. 索引 1.1 概念 索引是一种特殊的文件&#xff0c;包含着对数据表里所有记录的引用指针。可以对表中的一列或多列创建索引&#xff0c; 并指定索引的类…

数列极限

一、数列基础 1. 判断数列an是否有界 判断|an|是否小于等于M 2. 判断数列an是否单调 对所有正整数n&#xff0c;有an1&#xff1e;an&#xff0c;则称的单调递增数列。 对所有正整数n&#xff0c;有an1&#xff1c;an&#xff0c;则称的单调递减数列。 3. 重要数列 二、数列…

中小学信息学奥赛CSP-J认证 CCF非专业级别软件能力认证-入门组初赛模拟题第一套(完善程序题)

CCF认证CSP-J入门组模拟测试题第一套 三、完善程序题 第一题 九宫格 请完善下面的程序,将1~9个数字分别填人3x3的九宫格中,第一行的三个数字组成一个三位数。要使第二行的三位数是第一行的2倍,第三行的三位数是第一行的3倍且每个格子里的数字都不能重复,现在要求输出所有的填…

音视频剪辑|FFMPEG|windows10下的音视频格式转换,遮挡填充,GIF动图制作,背景音频抽取,替换

前言&#xff1a; 最近对于音视频和图像的处理问题比较感兴趣&#xff0c;但发现很多目前需要的功能要么需要付费但不会过于麻烦&#xff0c;要么比较麻烦&#xff0c;很可能某个功能实现需要安装很多软件 例如&#xff0c;视频转GIF动图&#xff0c;该功能的实现要么使用Pho…

Vue源码系列讲解——模板编译篇【三】(HTML解析器)

目录 1. 前言 2. HTML解析器内部运行流程 3. 如何解析不同的内容 3.1 解析HTML注释 3.2 解析条件注释 3.3 解析DOCTYPE 3.4 解析开始标签 3.5 解析结束标签 3.6 解析文本 4. 如何保证AST节点层级关系 5. 回归源码 5.1 HTML解析器源码 5.2 parseEndTag函数源码 6. …

HTML世界之第二重天

目录 一、HTML 格式化 1.HTML 文本格式化标签 2.HTML "计算机输出" 标签 3.HTML 引文, 引用, 及标签定义 二、HTML 链接 1.HTML 链接 2.HTML 超链接 3.HTML 链接语法 4.文本链接 5.图像链接 6.锚点链接 7.下载链接 8.Target 属性 9.Id 属性 三、HTML …