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();
}

转码运行结果

Qt结合FFmpeg转码码流数据(h264)生成不同视频格式(mp4、mov、flv、avi等)_第1张图片

原创不易,转载请标明出处。

你可能感兴趣的:(FFmpeg音视频,qt,音视频,ffmpeg,c++)