Qt结合FFmpeg解码本地视频生成.h264文件和.yuv文件
目录
1、视频解码流程分析
2、FFmpeg相关解码函数
3、创建一个类专门用来视频解码
.h文件
构造函数
打开视频文件
获取视频文件流信息并查找是否有视频流
查找视频流对应的解码器
打开解码器
循环读取每一帧并生成.h264、.yuv文件
关闭解码用到的组件
主函数测试
4、测试效果
运行效果
查看h264文件
查看yuv文件
一个视频包含有音频流、视频流、字幕流等信息,下面为视频流的解码过程以及代码实现。
1、视频解码流程分析
/*解码流程分析:* 1.注册所有组件* 2.打开文件* 3.获取文件信息* 4.看看有没有相关视频信息* 5.有视频信息,找他对应的解码器* 6.打开解码器* 7.获取一帧视频流(码流)信息* 8.读取,解码的动作* 9.得到像素数据* 重复7、8、9的动作,直到没有码流数据* 10.关闭解码器* 11.关闭视频流文件** */
2、FFmpeg相关解码函数
av_register_all():注册所有组件。avformat_open_input():打开输入视频文件。avformat_find_stream_info():获取视频文件信息。avcodec_find_decoder():查找解码器。avcodec_open2():打开解码器。av_read_frame():从输入文件读取一帧压缩数据。avcodec_decode_video2():解码一帧压缩数据。avcodec_close():关闭解码器。avformat_close_input():关闭输入视频文件。
3、创建一个类专门用来视频解码
.h文件
#include <QString>extern "C"
{#include <libavcodec/avcodec.h>#include <libavdevice/avdevice.h>#include <libavformat/avformat.h>#include <libavutil/avconfig.h>#include <libswscale/swscale.h>#include <libswresample/swresample.h>
}class fDecode
{
public:fDecode();void openMvFile(QString mvPath);//打开视频文件void findVideoStream();//查找视频流void findDecoder();//查找对应的解码器void openDecoder();//打开解码器void readFrame();//读取每一帧void closeAll();//关闭用到的组件private:AVFrame *in_frame,*out_frame;//保存解码后的像素数据int avcodec_index;//视频流所在输入视频的AVStream []数组的索引AVFormatContext* avformat_context;//封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息AVCodecContext* avcodec_context;//编码器上下文结构体,保存了视频(音频)编解码相关信息AVCodec* avcodec;//每种视频(音频)编解码器(例如H.264解码器)对应一个该结构体
};
构造函数
fDecode::fDecode()
{ //注册组件avcodec_register_all();qDebug()<<"————注册所有组件————";qDebug()<<"注册成功!";
}
打开视频文件
void fDecode::openMvFile(QString mvPath)
{qDebug()<<"————打开视频文件————";avformat_context = avformat_alloc_context();//封装格式上下文结构体开空间int res = avformat_open_input(&avformat_context,mvPath.toStdString().c_str(),NULL,NULL);//return 0 on successif (res != 0){qDebug()<<"打开失败";exit(0);}else{qDebug()<<"打开成功!";}
}
获取视频文件流信息并查找是否有视频流
void fDecode::findVideoStream()
{qDebug()<<"————获取文件信息————";this->avcodec_index = -1;int res = avformat_find_stream_info(avformat_context, NULL);//return >=0 if OKif (res < 0){qDebug()<<"获取文件信息失败";exit(0);}else{qDebug()<<"获取文件信息成功!";qDebug()<<"视频时长为:"<<avformat_context->duration/1000000.0<<"秒";qDebug()<<"视频平均混合码率为:"<<avformat_context->bit_rate/1000<<"Kbps";qDebug()<<"————获取文件视频流信息————";//nb_streams :输入视频的AVStream 个数for (int i = 0; i < avformat_context->nb_streams; i++)//遍历流{//找到视频流if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){this->avcodec_index = i;qDebug()<<"获取文件视频流信息成功!";break;}}if (-1 == this->avcodec_index){qDebug()<<"没有找到视频流";exit(0);}}return;
}
查找视频流对应的解码器
void fDecode::findDecoder()
{qDebug()<<"————查找对应的解码器————";//avcodec_index为视频流所在输入视频的AVStream []数组的索引,通过上一步查找视频流得到的//avcodec_context 编码器上下文结构体,保存了视频(音频)编解码相关信息avcodec_context = avformat_context->streams[this->avcodec_index]->codec;//根据解码器id查找解码器对应的结构体avcodec = NULL;avcodec = avcodec_find_decoder(avcodec_context->codec_id);if (NULL == avcodec){qDebug()<<"没有找到视频解码器";exit(0);}else{qDebug()<<"找到对应的解码器为:"<<avcodec->name;}
}
打开解码器
void fDecode::openDecoder()
{qDebug()<<"————打开解码器————";//avcodec_context、 avcodec 为上一步得到的int res = avcodec_open2(avcodec_context,avcodec,NULL);//zero on success, a negative value on errorif (0 != res){qDebug()<<"打开解码器失败";exit(0);}else{qDebug()<<"打开解码器成功!";}
}
循环读取每一帧并生成.h264、.yuv文件
void fDecode::readFrame()
{qDebug()<<"————读取每一帧————";AVPacket *pkt = (AVPacket *)malloc(sizeof(AVPacket));//存储一帧压缩编码数据in_frame= av_frame_alloc();//保存解码后的像素数据out_frame = av_frame_alloc();//保存解码后剔除损坏数据后的像素数据//一帧码流数据解码后得到的像素数据有多大int numByte = avpicture_get_size(AV_PIX_FMT_YUV420P, avcodec_context->width, avcodec_context->height);//开的空间用来保存像素数据uint8_t *buf = (uint8_t *)av_malloc(numByte * sizeof(uint8_t));//像素数据的填充avpicture_fill((AVPicture *)out_frame, buf, AV_PIX_FMT_YUV420P, avcodec_context->width, avcodec_context->height);//保存转换规则SwsContext *sws_content = nullptr;//转换规则的设置sws_content = sws_getContext(avcodec_context->width, avcodec_context->height ,avcodec_context->pix_fmt,avcodec_context->width, avcodec_context->height ,AV_PIX_FMT_YUV420P,SWS_BICUBIC,nullptr,nullptr,nullptr);FILE *fp_H264=NULL;FILE *fp_yuv=NULL;//以二进制格式创建或打开文件fp_H264=fopen("test.h264","wb+");fp_yuv=fopen("test.yuv","wb+");if(fp_H264 == NULL || fp_yuv == NULL){qDebug()<<"file open fail!";exit(0);}int size = avcodec_context->width * avcodec_context->height;av_new_packet(pkt,size);//循环读取每一帧while (av_read_frame(avformat_context,pkt) == 0) //return 0 if OK, < 0 on error or end of file{if(pkt->stream_index == this->avcodec_index) //如果这一帧是视频流{fwrite(pkt->data,pkt->size,1,fp_H264);//写入h264文件//解码——>得到yuv AVFrame//got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero.int got_picture_ptr = -1;avcodec_decode_video2(avcodec_context, in_frame, &got_picture_ptr, pkt);if(got_picture_ptr != 0)//做解码的操作{//把解码得到的损坏的数据剔除sws_scale(sws_content, in_frame->data, in_frame->linesize, 0, in_frame->height, //原数据out_frame->data, out_frame->linesize); //输出数据fwrite(out_frame->data[0], size, 1, fp_yuv);//写入y数据fwrite(out_frame->data[1], size/4, 1, fp_yuv);//写入u数据fwrite(out_frame->data[2], size/4, 1, fp_yuv);//写入v数据}}}av_packet_unref(pkt);//清空}//关闭h264、yuv文件fclose(fp_H264);fclose(fp_yuv);qDebug()<<"写入成功";
}
关闭解码用到的组件
void fDecode::closeAll()
{qDebug()<<"————关闭所有组件————";av_frame_free(&in_frame);av_frame_free(&out_frame);avcodec_close(avcodec_context);avformat_free_context(avformat_context);qDebug()<<"关闭成功";
}
主函数测试
int main(int argc, char *argv[])
{QApplication a(argc, argv);qDebug()<<"ffmpeg版本号:"<<avcodec_version();fDecode mvDecode;mvDecode.openMvFile("mv/Warcraft3_End.avi");//打开视频文件mvDecode.findVideoStream();//查找视频流mvDecode.findDecoder();//查找对应的解码器mvDecode.openDecoder();//打开解码器mvDecode.readFrame();//读取每一帧mvDecode.closeAll();//关闭用到的组件return 0;
}
4、测试效果
运行效果
当前目录下会生成test.h264文件和test.yuv文件
H.264压缩方法比较复杂。包含了帧内预测、帧间预测、熵编码、环路滤波等环节构成。可以将图像数据压缩100倍以上,所以可以发现未压缩的像素数据yuv文件比压缩后的码流数据h624文件大非常多。
查看h264文件
查看yuv文件
Qt结合FFmpeg解码本地视频生成.h264文件和.yuv文件相关推荐
- 【Qt+FFmpeg】FFmpeg解码本地视频流程
目录 一.视频播放器的实现框架 1.解封装(Demuxing) 2.解码(Decode) 3.音视频同步 二.FFmpeg解码视频流程 1.FFmpeg解码视频流程图 2. 代码实现 一.视频播放器的 ...
- android全平台基于ffmpeg解码本地MP4视频推流到RTMP服务器
音视频实践学习 android全平台编译ffmpeg以及x264与fdk-aac实践 ubuntu下使用nginx和nginx-rtmp-module配置直播推流服务器 android全平台编译ffm ...
- ffmpeg 解码本地裸视频流
解码本地视频流基本和解码音频流一致 视频解码过程 FFmpeg流程 关键函数啥的就不多说了 直接看我之前解码音频的文章 代码如下 #include <stdio.h> #include & ...
- ffmpeg读取本地视频,获取视频帧
本文转自:https://blog.csdn.net/yinsui1839/article/details/80519742 /******** *本代码参考自雷神博客 * **********/ # ...
- Windows上搭建Nginx RTMP服务器并使用FFmpeg实现本地视频推流
场景 RTMP RTMP协议 (1)是流媒体协议. (2)RTMP协议是 Adobe 的私有协议,未完全公开. (3)RTMP协议一般传输的是 flv,f4v 格式流. (4)RTMP一般在 TCP ...
- FFmpeg进阶: 截取视频生成gif动图
文章目录 1.封装视频滤镜 2.截取视频生成gif 3.gif优化 4.示例效果 现在互联网上很多人都通过表情包来表达自己的情绪,常用的表情包很多都是视频文件的一部分.这里就介绍一下如何通过ffmpe ...
- ffmpeg推本地视频到rtmp服务器
ffmpeg -re -i <视频文件名> -vcodec copy -f flv <rtmp://服务器地址> -re表示按帧率发送(保持视频按照时间戳发送,否则视频文件将很 ...
- Qt基于FFmpeg播放本地 H.264(H264)文件
最近在弄H264的硬件编解码,基于DM3730,但是为了调试方便,在小红帽上用FFmpeg实现了H264的软件编解码.现在弄了一个Windows的例子,给需要的同学参考一下,如果大家觉得有帮助,可以小 ...
- android推流局域网,android全平台基于ffmpeg解码本地MP4视频推流到RTMP服务器
音视频实践学习 本文目录 概述 还是先从最简单的搞起来,先从最基本的视频推流开始,要知道在电脑上使用ffmpeg完成推流,简直不要太简单,直接使用ffmpeg的推流命令即可,今天想在android平台 ...
- ffmpeg 解码本地无封装裸音频流 AAC MP3 复制代码就可以运行
⾳频解码过程 FFmpeg对应流程 关键函数说明: avcodec_find_decoder:根据指定的AVCodecID查找注册的解码器. av_parser_init:初始化AVCodecPars ...
最新文章
- 关于Action返回结果类型的事儿(下)
- 砥砺前行,比特币现金周年国际峰会正式召开
- RecyclerView添加头条目显示空白的解决方案
- Calendar类的常用成员方法
- python可以下载百度文库_百度文库随便下载,解除限制
- 鸿蒙os硬件要求,华为公布鸿蒙OS 2.0硬件安装要求:只要128K内存就能跑
- Android 渗透测试学习手册 翻译完成!
- pycharm创建django项目界面解释
- JQuery的Alert插件介绍
- 使用docker compose 测试集群网络连接性
- http状态码大全整理
- 【递归入门】组合的输出
- NAT hairpin,端口回流,回环NAT
- 计算机使用技巧爆文,自媒体原创(伪原创)爆文的写作技巧
- 2022081班李亚楠20220902
- git找回删除的stash
- CSAPP第五章家庭作业参考答案
- 成功人士分析问题的11种思维方式
- python画爱心原理_如何理解python一行代码实现一个爱心字符画?
- “闪婚”的代价:亿晶光电控股权陷“罗生门”
热门文章
- 影视App下载页源码
- Java中StringBuffer类的常用方法
- ubuntu vscode 配置字体_配置vscode终端字体,vscode终端字体
- android studio jdy08,JDY-08模块 蓝牙4.0 BLE CC2541 airsync iBeacon 兼容arduino
- 指纹机和计算机无法连接,指纹考勤系统常见问题及解决办法
- matlab色差,计算CMC色差公式的matlab程序
- Excel将数据内容导出为数据库DBF文件的操作
- dbf转成excel_dbf转excel工具(DbfToExcel) v1.2 免费版
- 屏幕取词编程学习总结
- 神经网络预测python_bp神经网络预测python