【Qt+FFmpeg】视频转码详细流程

视频转码,就是进行不同视频格式间的转换;可以把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等;

猜你喜欢

转载自blog.csdn.net/logani/article/details/127600062