ffmpeg:一个简单的格式转换器

        参考ffmpegAPI提供的例子实现了一个简单的封装格式转换器,在实际转换时候,需要考虑对h264的两种封装格式分别是h264和avc1。
       AVC1 描述:H.264 bitstream without start codes.一般通过ffmpeg转码生成的视频,是不带起始码0×00000001的。
      H264 描述:H.264 bitstream with start codes.一般对于一下HDVD等电影的压制格式,是带有起始码0×00000001的。
       一般的像(mp4、mkv、flv)的封装格式都是没有startcode的AVC1。 而像avi的封装格式则是有start codes的H264 。ffmpeg提供了一个名称为“h264_mp4toannexb”的bitstream filter。其会将像mp4这样的没有start code的格式转换为像avi这样的有start code。 使用方法如下: 

//注册filter
 AVBitStreamFilterContext * vbsf = av_bitstream_filter_init("h264_mp4toannexb");

//转换
if (pkt.stream_index == AVMEDIA_TYPE_VIDEO &&
                    in_stream->codecpar->codec_id == AV_CODEC_ID_H264) {
                AVPacket fpkt = pkt;
                int a = av_bitstream_filter_filter(vbsf,
                                                   out_stream->codec, NULL, &fpkt.data, &fpkt.size,
                                                   pkt.data, pkt.size, pkt.flags & AV_PKT_FLAG_KEY);
                pkt.data = fpkt.data;
                pkt.size = fpkt.size;
            }
一个完整的格式转换代码如下:
if(In_File.isEmpty() || In_File.isNull()
            || Out_File.isEmpty() || Out_File.isNull())
        return -1;

    AVOutputFormat *ofmt = NULL;

    AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL;
    AVPacket pkt;

    int ret,i;
    int frame_index = 0;

    // --[2]初始化输入文件的格式上下文
    //将文件头信息复制给avformatContext
    if((ret = avformat_open_input(&ifmt_ctx,In_File.toLocal8Bit(),NULL,NULL)) < 0)
    {
        qDebug()<<"could not open input file!";
        goto end;
    }
    //将流的参数信息复制给ifmt_ctx->streams
    if((ret = avformat_find_stream_info(ifmt_ctx,NULL)) < 0)
    {
        qDebug()<<"can not find a stream!\n";
        goto end;
    }

#if USE_DEBUG
    //cout<<"----------------------Input file Information-----------------"<<endl;
    av_dump_format(ifmt_ctx,0,In_File.toLocal8Bit(),0);
    // cout<<"-------------------------------------------------------------"<<endl;
#endif
    //[2]

            ifmt_ctx->streams[0]->codec_info_nb_frames)*(ifmt_ctx->streams[0]->codec_info_nb_frames
            +ifmt_ctx->streams[1]->codec_info_nb_frames);
    emit sendProgressMaxValue(frame_num);


    //[3] --初始化输出文件的格式上下文
    avformat_alloc_output_context2(&ofmt_ctx,NULL,NULL,Out_File.toLocal8Bit());
    if(!ofmt_ctx)
    {
        qDebug()<<"could not creat output context";
        goto end;
    }
    ofmt = ofmt_ctx->oformat;
    //[3]

    //[4] --将输入文件内容复制到输出文件中
    for( i =0;i<ifmt_ctx->nb_streams;++i)
    {
        //获取输入流
        AVStream *in_stream = ifmt_ctx->streams[i];
        //创建输出流
        AVStream *out_stream = avformat_new_stream(ofmt_ctx,in_stream->codec->codec);
        if(!out_stream)
        {
            qDebug()<<"failed allocating output stram";
            ret = AVERROR_UNKNOWN;
            goto end;
        }

        //复制输入流的AVCodeContext到输出流的AVCodeContext
        out_stream->time_base.num = in_stream->time_base.num;
        out_stream->time_base.den = in_stream->time_base.den;
        out_stream->avg_frame_rate = in_stream->avg_frame_rate;
        if(avcodec_copy_context(out_stream->codec,in_stream->codec) < 0)
        {
            qDebug()<<"failed to copy CodecContext!";
            goto end;
        }

        out_stream->codec->codec_tag = 0;
        if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
            out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;

    }
    //[4]

#if USE_DEBUG
    //cout<<"----------------------Output file Information-----------------"<<endl;
    av_dump_format(ofmt_ctx,0,Out_File.toLocal8Bit(),1);
    //cout<<"-------------------------------------------------------------"<<endl;
#endif

    //打开输出文件
    if(!(ofmt->flags & AVFMT_NOFILE))
    {
        ret = avio_open(&ofmt_ctx->pb,Out_File.toLocal8Bit(),AVIO_FLAG_WRITE);
        if (ret < 0) {
            qDebug()<<"Could not open output file: "<<Out_File.toLocal8Bit();
            goto end;
        }
    }

    //写头文件
    if(avformat_write_header(ofmt_ctx,NULL) < 0)
    {
        qDebug()<<"Error occurred when opening output file";
        goto end;
    }

    AVBitStreamFilterContext * vbsf = av_bitstream_filter_init("h264_mp4toannexb");

    //写入文件内容
    while(1)
    {
        AVStream *in_stream,*out_stream;
        ret = av_read_frame(ifmt_ctx,&pkt);
        if(ret < 0)
            break;
        in_stream  = ifmt_ctx->streams[pkt.stream_index];
        out_stream = ofmt_ctx->streams[pkt.stream_index];

        //qDebug()<<pkt.pts<<" "<<pkt.dts;
        QStringList inExt = QString(ifmt_ctx->iformat->extensions).split(",");
        QStringList outExt = QString(ofmt_ctx->oformat->extensions).split(",");
        //qDebug()<<"inExt:"<<inExt;
        //qDebug()<<"outExt:"<<outExt;
        if(!((inExt.contains("mp4") || inExt.contains("mkv") || inExt.contains("flv")) &&
             (outExt.contains("mp4") || outExt.contains("mkv")|| outExt.contains("flv"))))
        {qDebug()<<"run";
            if (pkt.stream_index == AVMEDIA_TYPE_VIDEO &&
                    in_stream->codecpar->codec_id == AV_CODEC_ID_H264) {
                AVPacket fpkt = pkt;
                int a = av_bitstream_filter_filter(vbsf,
                                                   out_stream->codec, NULL, &fpkt.data, &fpkt.size,
                                                   pkt.data, pkt.size, pkt.flags & AV_PKT_FLAG_KEY);
                pkt.data = fpkt.data;
                pkt.size = fpkt.size;
            }
        }

        //转换PTS/DTS
        /* copy packet */
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
        pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);
        pkt.pos = -1;

        //写入
        if(av_interleaved_write_frame(ofmt_ctx,&pkt) < 0)
        {
            qDebug()<<"Error muxing packet";
            break;
        }
#if USE_DEBUG
        qDebug()<<"Write "<<frame_index<<" frames to output file";
#endif
        av_free_packet(&pkt);
        frame_index++;
    }

    //写文件尾
    av_write_trailer(ofmt_ctx);

end:
    avformat_close_input(&ifmt_ctx);
    if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))
        avio_close(ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);
    av_bitstream_filter_close(vbsf);
    if (ret < 0 && ret != AVERROR_EOF) {
        qDebug()<<"Error occurred.";
        return -1;
    }

    return 1;
}
本人使用Qt完成了格式转换器的GUI,并提供了一个进度条来预估当前转换进度。程序效果如下:
     github下载地址: https://github.com/pencilmonster/Remuxer
参考文献:
h264封装
视频复用器
AVC1、h264
点击打开链接



发布了109 篇原创文章 · 获赞 168 · 访问量 27万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章