目录
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等)】原创不易,转载请标明出处。
推荐阅读
- linux远程开发|linux远程开发——使用vs2019远程连接linux
- Qt-大屏电子看板|Qt编写可视化大屏电子看板系统32-模块10大屏地图
- 思维|天气预报(牛客)
- 算法|Acwing1230. K倍区间
- c++|2022/3/29 每日思维
- acwing|【acwing】846. 树的重心**
- 算法笔记练习题|(5.6B)n的阶乘
- 华师oj|2857. 编辑距离
- 数据结构与算法学习|数据结构与算法作业8(排序算法的应用)