FFmpeg音视频|Qt结合FFmpeg转码码流数据(h264)生成不同视频格式(mp4、mov、flv、avi等)

目录
1、转码流程分析
2、创建一个类专门用来转码
.h文件
构造函数
打开对应的码流数据
转码得到最终的封装格式
主函数测试
转码运行结果
1、转码流程分析

/*转码流程分析: * 1、注册组件 * 2、打开视频文件 * 3、查找视频流 * 4、找到了视频流,猜测需要的封装格式是否存在 * 5、打开目标文件流 * 6、新建目标视频流 * 7、编码器参数设置 * 8、开始写入头部信息 * 9、一帧一帧的读取视频码流数据,并进行转码 * 10、转码结束写入尾巴帧,结束本次的转码,并把需要释放的资源回收 * */

2、创建一个类专门用来转码 .h文件
#include extern "C" { #include #include #include #include #include #include }class transCoding { public: transCoding(); //打开对应的码流数据 void openH264file(QString fileName); //转码得到最终的封装格式:格式是不固定的 void coverTovideo(QString fileName); private: AVStream *newStream; //新建视频流AVOutputFormat *avoutput_format; //输出封装格式上下文结构体int avcodec_index; //视频流所在输入视频的AVStream []数组的索引AVFormatContext *in_avformat_context,*out_avformat_context; //封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息};

构造函数
transCoding::transCoding() { //注册所有组件 av_register_all(); in_avformat_context = avformat_alloc_context(); out_avformat_context = avformat_alloc_context(); }

打开对应的码流数据
void transCoding::openH264file(QString fileName) { //打开视频文件int res = avformat_open_input(&in_avformat_context, fileName.toStdString().c_str(), nullptr, nullptr); if (res != 0) { qDebug()<<"打开失败"; exit(0); } else { qDebug()<<"打开成功!"; qDebug()<avcodec_index = -1; res = avformat_find_stream_info(in_avformat_context, NULL); //return >=0 if OK if (res < 0) { qDebug()<<"获取文件信息失败"; exit(0); } else { qDebug()<<"获取文件信息成功!"; qDebug()<<"视频时长为:"<duration/1000000.0<<"秒"; qDebug()<<"视频平均混合码率为:"<bit_rate/1000<<"Kbps"; qDebug()<<"————获取文件视频流信息————"; //nb_streams :输入视频的AVStream 个数 for (int i = 0; i < in_avformat_context->nb_streams; i++)//遍历流 { //找到视频流 if (in_avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { this->avcodec_index = i; qDebug()<<"获取文件视频流信息成功!"; break; } } if (-1 == this->avcodec_index) { qDebug()<<"没有找到视频流"; exit(0); } else { qDebug()<<"找到视频流"; } } qDebug()<<"输入准备完成!"; }

转码得到最终的封装格式
可以转码生成mp4、mov、flv、avi等视频格式。
void transCoding::coverTovideo(QString fileName) { qDebug()<<"猜测编码器"; //猜测编码器 //猜测 avoutput_format = av_guess_format(nullptr, fileName.toStdString().c_str(), nullptr); //判断有没有匹配到格式 if(avoutput_format == nullptr) { qDebug()<<"没有匹配到!"; return; } else { qDebug()<<"匹配到!"; }//输出封装格式文件的格式设置 out_avformat_context->oformat = avoutput_format; //打开视频流 /* 参数一:AVIOContent:输入输出上下文对象 参数二:文件流的路径 参数三:文件打开的方式,以写入的方式打开 @return >= 0 in case of success, a negative value corresponding to an */ int res = avio_open(&out_avformat_context->pb, fileName.toStdString().c_str(), AVIO_FLAG_WRITE); if(res < 0) { qDebug()<<"avio_open error!"; return; } else { qDebug()<<"avio_open success!"; }//新建视频流 参数一:保存视频信息的结构体 newStream = avformat_new_stream(out_avformat_context, nullptr); if(newStream == nullptr) { qDebug()<<"新建视频流失败!"; return; } else { qDebug()<<"新建视频流成功!"; }//编码器参数的设置 res = avcodec_parameters_copy(newStream->codecpar, in_avformat_context->streams[avcodec_index]->codecpar); if(res < 0) { qDebug()<<"copy error!"; return; } else { qDebug()<<"copy success!"; }newStream->codecpar->codec_tag = 0; qDebug()<<"————写入头部信息————"; //写入头部信息 res = avformat_write_header(out_avformat_context, nullptr); if(res < 0) { qDebug()<<"write header error!"; return; } else { qDebug()<<"write header success!"; }//一帧一帧读取码流数据,转码AVPacket *pkt = nullptr; pkt = (AVPacket *)malloc(sizeof(AVPacket)); int size = newStream->codecpar->width*newStream->codecpar->height; av_new_packet(pkt, size); int frame_count = 0; qDebug()<<"————循环去读一帧一帧的数据————"; while(av_read_frame(in_avformat_context, pkt) == 0) { if(pkt->stream_index == avcodec_index) { frame_count++; //转码 //查看是否有做时间基的设置 if(pkt->pts == AV_NOPTS_VALUE)//没有设置 { //qDebug()<<"时间基的转换"; //时间基的转换 AVRational time_base1 = in_avformat_context->streams[avcodec_index]->time_base; //计算两帧码流数据之间的长度 int64_t duration = (double)AV_TIME_BASE/av_q2d(in_avformat_context->streams[avcodec_index]->r_frame_rate); //计算显示的时间基(当前帧数*两帧之间的长度)/(输入时间基*AV_TIME_BASE)pkt->pts = (double)(frame_count * duration)/(double)(av_q2d(time_base1) * AV_TIME_BASE); pkt->dts = pkt->pts; //没有B帧 pkt->duration = duration/(double)(av_q2d(time_base1) * AV_TIME_BASE); } else if(pkt->pts < pkt->dts) { continue; }pkt->pts = av_rescale_q_rnd(pkt->pts, in_avformat_context->streams[avcodec_index]->time_base, newStream->time_base, (AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX)); pkt->dts = av_rescale_q_rnd(pkt->dts, in_avformat_context->streams[avcodec_index]->time_base, newStream->time_base, (AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX)); pkt->duration = av_rescale_q(pkt->duration, in_avformat_context->streams[avcodec_index]->time_base, newStream->time_base); //准备写入pkt->pos = -1; pkt->flags |= AV_PKT_FLAG_KEY; pkt->stream_index = 0; av_interleaved_write_frame(out_avformat_context, pkt); } //清空av_packet_unref(pkt); }//写入尾巴帧 qDebug()<<"写入尾巴帧"; av_write_trailer(out_avformat_context); //关闭编码器 avcodec_close(out_avformat_context->streams[avcodec_index]->codec); //删掉编码器 av_freep(out_avformat_context->streams[avcodec_index]->codec); //关闭各种流 qDebug()<<"关闭各种流"; avio_close(out_avformat_context->pb); av_free(out_avformat_context); avformat_close_input(&in_avformat_context); av_free(in_avformat_context); qDebug()<<"释放包"; //av_packet_free(&pkt); qDebug()<<"转码成功"; }

主函数测试
工程目录下的fileOut文件夹下有个2022_2_7_0_41.h264文件,通过转码生成test.mp4。
#include "transcoding.h" #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); transCoding transCode; transCode.openH264file(QString("fileOut/2022_2_7_0_41.h264")); //打开视频文件transCode.coverTovideo(QString("fileOut/test.mp4")); //转码生成对应的封装格式视频return a.exec(); }

也可以将mp4格式的视频转换成其他格式的视频,如下为将上一步转码得到的test.mp4转换成test.mov。
int main(int argc, char *argv[]) { QApplication a(argc, argv); transCoding transCode; transCode.openH264file(QString("fileOut/test.mp4")); //打开视频文件transCode.coverTovideo(QString("fileOut/test.mov")); //转码生成对应的封装格式视频return a.exec(); }

转码运行结果
FFmpeg音视频|Qt结合FFmpeg转码码流数据(h264)生成不同视频格式(mp4、mov、flv、avi等)
文章图片


【FFmpeg音视频|Qt结合FFmpeg转码码流数据(h264)生成不同视频格式(mp4、mov、flv、avi等)】原创不易,转载请标明出处。

    推荐阅读