视频转码,就是进行不同视频格式间的转换;可以把H264、MPEG2文件→mp4等格式,也可以对mp4格式的文件→avi等格式进行转换;
一、转码函数介绍
- open_input_file():打开输入文件,并初始化相关的结构体
- open_output_file():打开输出文件,并初始化相关的结构体
- init_filters():初始化AVFilter相关的结构体
- av_read_frame():从输入文件中读取一个AVPacket
- avcodec_decode_video2():解码一个视频AVPacket(存储H.264等压缩码流数据)为AVFrame(存储YUV等非压缩的像素数据)
- avcodec_decode_video4():解码一个音频AVPacket(存储MP3等压缩码流数据)为AVFrame(存储PCM采样数据)
- filter_encode_write_frame():编码一个AVFrame
- flush_encoder():输入文件读取完毕后,输出编码器中剩余的AVPacket
二、视频转码流程图
三、 实现代码
头文件
class transcoding : public QThread
{
Q_OBJECT
public:
transcoding();
//打开 H264 的压缩码流数据
void openH264File(QString inH264file);
//转码得到相应的封装格式
void covertToDest(QString toNormalfile);
void writeTailer();//写入尾部帧
~transcoding();
private:
AVFormatContext *formatContent;
AVFormatContext *outformatContent;
int video_index;
AVPacket *pkt;
};
源文件
transcoding::transcoding()
{
//1.注册组件
av_register_all();
//视频信息结构体初始化
formatContent=avformat_alloc_context();
//目标视频信息结构体初始化
outformatContent=avformat_alloc_context();
//是否为视频流
video_index=-1;
//保存压缩码流数据
pkt=(AVPacket*)malloc(sizeof(AVPacket));
}
void transcoding::openH264File(QString inH264file)
{
//2.打开视频文件
int res=avformat_open_input(&formatContent,inH264file.toStdString().c_str(),nullptr,nullptr);
//判断是否打开成功
if(res<0)
{
qDebug()<<"打开失败";
return;
}
//打开视频文件成功,获取文件信息
res = avformat_find_stream_info(formatContent,nullptr);//查看有没有相关视频流信息
if(res<0)//判断是否有流媒体
{
qDebug()<<"没有流媒体信息";
return;
}
//3.查找视频流
for(int i=0;i<formatContent->nb_streams;i++) //i小于流的个数
{
if(formatContent->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)//视频流
{
video_index=i;//标识类型
break;
}
}
if(video_index==-1)
{
qDebug()<<"没有视频流相关信息";
return;
}
}
void transcoding::covertToDest(QString toNormalfile)
{
//4.猜测编码器
AVOutputFormat *avformat = av_guess_format(nullptr,toNormalfile.toStdString().c_str(),nullptr);
if(avformat==nullptr)
{
qDebug()<<"没有编码器";
return;
}
//保存输出视频信息的结构体
outformatContent = avformat_alloc_context();
//设置输出格式
outformatContent->oformat = avformat;
//5.打开目标文件流
//参数1:输入输出的上下文对象
//参数2:文件流路径
//参数3:文件打开格式 写的方式
//返回值<0失败
int res=avio_open(&outformatContent->pb,toNormalfile.toStdString().c_str(),AVIO_FLAG_WRITE);
if(res<0)
{
qDebug()<<"打开目标文件流失败";
return;
}
//6.新建目标视频流
//参数1:视频信息结构体
//参数2:新建流 的 返回新建流 的地址
AVStream *newStream =avformat_new_stream(outformatContent,nullptr);
if(newStream==nullptr)
{
qDebug()<<"打开视频流失败";
return;
}
qDebug()<<"newStream";
/*7.编码器对应参数设置
* 把输入的 h264 用到的编码器相关的参数给输出流用到的编码器
* 参数 1:目标视频流的编码器参数信息结构体
* 参数 2:输入视频流的编码器参数信息结构体
*/
res = avcodec_parameters_copy(newStream->codecpar,formatContent->streams[video_index]->codecpar);
qDebug()<<"res="<<res;
if(res<0)
{
qDebug()<<"参数设置失败";
return;
}
qDebug()<<"res="<<res;
//设置新的流里面 codec_tag 设置为0
newStream->codecpar->codec_tag = 0;
//8.头部信息写入
res = avformat_write_header(outformatContent,nullptr);//formatout封装格式的结构体
if(res<0)
{
qDebug()<<"写入头部信息失败";
return;
}
//9.读取码流数据
pkt = (AVPacket*)malloc(sizeof(AVPacket));
//算出这张图有多大
int size = newStream->codecpar->width*newStream->codecpar->height;
av_new_packet(pkt,size);
int frameCount=0;
//一帧一帧的读取,进行转码-显示时间基和解码时间基的设置
while(av_read_frame(formatContent,pkt)==0)
{
//视频流
if(pkt->stream_index==video_index)
{
frameCount++;
qDebug()<<"第"<<frameCount<<"帧";
//判断有没有设置过时间基
if(pkt->pts==AV_NOPTS_VALUE)
{
//时间基 time_base AVRational属性
AVRational timebase=formatContent->streams[video_index]->time_base;
//计算帧之间的长度(duration) double强制转换
int64_t duration=(double)AV_TIME_BASE/av_q2d(formatContent->streams[video_index]->r_frame_rate);
//计算显示时间基(pts):公式:(当前帧数*两帧之间的长度))/(输入时间基*AV_TIME_BASE)
pkt->pts = (double)(frameCount*duration)/(av_q2d(timebase)*AV_TIME_BASE);
//解码时间基(dts)
pkt->dts = pkt->pts;
//目标两帧之间的长度
pkt->duration = duration/(double)(av_q2d(timebase)*AV_TIME_BASE);
}
else if(pkt->pts < pkt->dts)//显示时间基小于解码时间基不做处理
{
continue;
}
//显示时间基的转换
pkt->pts = av_rescale_q_rnd(pkt->pts,formatContent->streams[video_index]->time_base,newStream->time_base,(AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
//解码时间基的转换
pkt->dts = av_rescale_q_rnd(pkt->dts,formatContent->streams[video_index]->time_base,newStream->time_base,(AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
//数据时长设置
pkt->duration = av_rescale_q(pkt->duration,formatContent->streams[video_index]->time_base,newStream->time_base);
//数据位置的设置 数据在流信息中的设置
pkt->pos = -1;
//数据包的标记:结合AV_PKT_FLAG_KEY使用 最小为1表示这一帧是一个关键帧
pkt->flags |=AV_PKT_FLAG_KEY;
//标记:当前写入的这一帧是视频流
pkt->stream_index = 0;
//转码后的数据包 写入目标视频信息结构体中
av_interleaved_write_frame(outformatContent,pkt);
}
//清空处理:重新设置包
av_packet_unref(pkt);
}
}
void transcoding::writeTailer()
{
//写入尾巴帧
av_write_trailer(outformatContent);
}
transcoding::~transcoding()
{
qDebug()<<"关闭所有转码组件";
//关闭编码器
avcodec_close(outformatContent->streams[video_index]->codec);
//关闭输出流
avio_close(outformatContent->pb);
//释放输出信息的结构体
av_free(outformatContent);
//关闭输入流
avio_close(formatContent->pb);
//释放视频信息
avformat_free_context(formatContent);
//释放输入视频信息结构体
av_free(formatContent);
}
运用
h264→mp4文件
不同格式之间转换,只需要把输入视频格式h264改成mp4等,输出格式改成你想要的mov等;