目录
1、转码流程分析
2、创建一个类专门用来转码
.h文件
构造函数
打开对应的码流数据
转码得到最终的封装格式
主函数测试
转码运行结果
/*转码流程分析:
* 1、注册组件
* 2、打开视频文件
* 3、查找视频流
* 4、找到了视频流,猜测需要的封装格式是否存在
* 5、打开目标文件流
* 6、新建目标视频流
* 7、编码器参数设置
* 8、开始写入头部信息
* 9、一帧一帧的读取视频码流数据,并进行转码
* 10、转码结束写入尾巴帧,结束本次的转码,并把需要释放的资源回收
* */
#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();
}
原创不易,转载请标明出处。