ffmpeg 将h264格式文件编码为MP4文件

本文介绍了H264转MP4的编码过程,涉及PTS、DTS和duration等时间戳概念。通过提供的实现流程图和代码示例,详细展示了如何使用ffmpeg库进行视频格式转换,包括读取输入文件、创建输出文件、编码参数设置以及时间戳转换等步骤。
摘要由CSDN通过智能技术生成

1、在编码过程中涉及到一些时间戳相关概念主要:

PTS:度量解码后的视频帧什么时候被显示出来;

DTS:读入内存中的bit流在什么时候开始送入解码器中进行解码;

duration:表示当前帧的持续时间占多少个时间单位;

相关参数的理解可以参考:https://www.jianshu.com/p/1bc03e97004e/

2、实现流程图:

 

 3、程序实现:

void MainWindow::h264Tomp4()
{
    AVFormatContext* pInFmtCtx = nullptr;
    int nInStreamIndex = -1;

    AVFormatContext* pOutFmtCtx = nullptr;
    AVStream* pOutVStream = nullptr;
    int nOutStreamIndex = -1;
    AVCodecContext* pOutCodecCtx = nullptr;


    do{
        //打开输入文件,并填充AVFormatContext上下文
        QString strInFile = "in.h264";
        if(avformat_open_input(&pInFmtCtx, strInFile.toStdString().c_str(), NULL, NULL) < 0)
        {
            qDebug() << "cannot open input file." << endl;
            break;
        }

        //查找输入文件中的流信息
        if(avformat_find_stream_info(pInFmtCtx, NULL) < 0){
            qDebug() << "cannot find stream info." << endl;
            break;
        }

        //查找视频流在流信息中的索引的索引
        for(size_t i = 0;i < pInFmtCtx->nb_streams;i++){
            if(pInFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
                nInStreamIndex = (int)i;
                break;
            }
        }

        //获取输入视频流的编码器参数
        AVCodecParameters *codecPara = pInFmtCtx->streams[nInStreamIndex]->codecpar;

        //创建AVFormatContext上下文
        QString strOutFile = "out.mp4";
        if(avformat_alloc_output_context2(&pOutFmtCtx, NULL, NULL, strOutFile.toStdString().c_str()) < 0){
            qDebug() << "cannot alloc outfile context." << endl;
            break;
        }

        //打开输出文件并填充AVFormatContext上下文数据
        if(avio_open(&pOutFmtCtx->pb, strOutFile.toStdString().c_str(), AVIO_FLAG_READ_WRITE) < 0){
            qDebug() << "cannot open output file." << endl;
            break;
        }

        //为输出AVFormatContext上下文添加视频流,并设置参数
        if(nullptr == (pOutVStream = avformat_new_stream(pOutFmtCtx, NULL)))
        {
            qDebug() << "cannot new output stream." << endl;
            break;
        }
        pOutVStream->time_base.den = 25;
        pOutVStream->time_base.num = 1;
        nOutStreamIndex = pOutVStream->index;

        //mp4中的视频也是h264编码,所以输入和输出编码器一致:
        //查找编码器
        AVCodec* outCodec = const_cast<AVCodec*>(avcodec_find_encoder(codecPara->codec_id));
        if(outCodec==NULL){
            printf("Cannot find any encoder.\n");
            break;
        }

        //创建输出编码器上下文
        pOutCodecCtx = avcodec_alloc_context3(outCodec);
        AVCodecParameters* outCodecPara = pOutFmtCtx->streams[pOutVStream->index]->codecpar;
        if(avcodec_parameters_copy(outCodecPara, codecPara)<0){
            qDebug() << "cannot copy codecparam";
            break;
        }
        if(avcodec_parameters_to_context(pOutCodecCtx, outCodecPara)<0){
            qDebug() << "avcodec_parameters_to_context failed";
            break;
        }
        pOutCodecCtx->time_base.den=25;
        pOutCodecCtx->time_base.num=1;

        //打开输出流编码器
        if(avcodec_open2(pOutCodecCtx,outCodec,NULL) < 0){
            qDebug() << "Cannot open output codec";
            break;
        }

        //写入文件头
        if(avformat_write_header(pOutFmtCtx, NULL) < 0){
            qDebug() << "Cannot write header to file";
            break;
        }

        //
        AVPacket pkt;
        std::int64_t llFrameIndex = 0;
        AVStream* inVStream = pInFmtCtx->streams[nInStreamIndex];

        //循环读取帧
        while(av_read_frame(pInFmtCtx, &pkt) >= 0){

            //判断是视频流
            if(pkt.stream_index == nInStreamIndex){

                if(pkt.pts == AV_NOPTS_VALUE){

                    //PTS:度量解码后的视频帧什么时候被显示出来
                    //DTS:读入内存中的bit流在什么时候开始送入解码器中进行解码

                    //获取时间基:即用来度量单位刻度时间
                    AVRational time_base1 = inVStream->time_base;
                    //计算两帧视频之间的实际间隔时间,单位是us
                    int64_t calc_duration = (double)AV_TIME_BASE / av_q2d(inVStream->r_frame_rate);

                    //计算PTS,即经过多少个刻度显示该帧视频
                    pkt.pts = (double)(llFrameIndex*calc_duration) / (double)(av_q2d(time_base1)*AV_TIME_BASE);
                    pkt.dts = pkt.pts;
                    //两帧间隔时间占多少个刻度
                    pkt.duration = (double)calc_duration / (double)(av_q2d(time_base1)*AV_TIME_BASE);
                    llFrameIndex++;
                }

                //将输入流中时间基的PTS转换到输出流时间基中的PTS
                pkt.pts = av_rescale_q_rnd(pkt.pts, inVStream->time_base, pOutVStream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                //将输入流中时间基的DTS转换到输出流时间基中的DTS
                pkt.dts = av_rescale_q_rnd(pkt.dts, inVStream->time_base, pOutVStream->time_base, (enum AVRounding)(AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX));
                //将输入流中时间基的duration转换到输出流时间基中的duration
                pkt.duration = av_rescale_q(pkt.duration, inVStream->time_base, pOutVStream->time_base);
                pkt.pos = -1;
                pkt.stream_index = nOutStreamIndex;
                //写入一帧数据
                if (av_interleaved_write_frame(pOutFmtCtx, &pkt) < 0) {
                    break;
                }
                av_packet_unref(&pkt);
            }
        }

        av_write_trailer(pOutFmtCtx);


    }while(0);

    if(pOutCodecCtx)
    {
        avcodec_close(pOutCodecCtx);
        avcodec_free_context(&pOutCodecCtx);
    }

    if(pInFmtCtx)
    {
        avformat_free_context(pInFmtCtx);
    }

    if(pOutFmtCtx)
    {
        if(pOutFmtCtx->pb)
        {
            avio_close(pOutFmtCtx->pb);
        }
        avformat_free_context(pOutFmtCtx);
    }

}

 4、下面使用自己写的播放器播放一下MP4文件:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小米的修行之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值