FFMpeg:将h264流写入mp4容器而不做任何更改

时间:2017-03-03 12:05:00

标签: c++ video ffmpeg stream

美好的一天。

为简洁起见,代码省略了错误处理和内存管理。

我想捕获h264视频流并将其打包到mp4容器而不做任何更改。由于我不控制流的来源,我不能对流结构做出假设。这样我必须探测输入流。

    AVProbeData probeData;
    probeData.buf_size = s->BodySize();
    probeData.buf = s->GetBody();
    probeData.filename = "";

    AVInputFormat* inFormat = av_probe_input_format(&probeData, 1);  

此代码正确定义了h264流。

接下来,我创建输入格式上下文

    unsigned char* avio_input_buffer = reinterpret_cast<unsigned char*> (av_malloc(AVIO_BUFFER_SIZE));

    AVIOContext* avio_input_ctx = avio_alloc_context(avio_input_buffer, AVIO_BUFFER_SIZE,
        0, this, &read_packet, NULL, NULL);

    AVFormatContext* ifmt_ctx = avformat_alloc_context();
    ifmt_ctx->pb = avio_input_ctx;

    int ret = avformat_open_input(&ifmt_ctx, NULL, inFormat, NULL);

设置图片大小,

    ifmt_ctx->streams[0]->codec->width = ifmt_ctx->streams[0]->codec->coded_width = width;
    ifmt_ctx->streams[0]->codec->height = ifmt_ctx->streams[0]->codec->coded_height = height;

创建输出格式上下文,

    unsigned char* avio_output_buffer = reinterpret_cast<unsigned char*>(av_malloc(AVIO_BUFFER_SIZE));

    AVIOContext* avio_output_ctx = avio_alloc_context(avio_output_buffer, AVIO_BUFFER_SIZE,
        1, this, NULL, &write_packet, NULL);

    AVFormatContext* ofmt_ctx = nullptr;
    avformat_alloc_output_context2(&ofmt_ctx, NULL, "mp4", NULL);
    ofmt_ctx->pb = avio_output_ctx;

    AVDictionary* dict = nullptr;
    av_dict_set(&dict, "movflags", "faststart", 0);
    av_dict_set(&dict, "movflags", "frag_keyframe+empty_moov", 0);

    AVStream* outVideoStream = avformat_new_stream(ofmt_ctx, nullptr);

    avcodec_copy_context(outVideoStream->codec, ifmt_ctx->streams[0]->codec);

    ret = avformat_write_header(ofmt_ctx, &dict);

初始化完成。此外,还有从h264流到mp4容器的移位数据包。我不计算pts和dts,因为源数据包中包含AV_NOPTS_VALUE。

    AVPacket pkt;
    while (...)
    {
        ret = av_read_frame(ifmt_ctx, &pkt);
        ret = av_interleaved_write_frame(ofmt_ctx, &pkt);
        av_free_packet(&pkt);
    }

此外,我编写预告片和免费分配的内存。就这些。代码工作,我得到了可玩的mp4文件。

现在问题是:生成的文件的流特征并不完全符合源流的特征。特别是,fps和比特率高于应有的值。

作为示例,下面是输出源流的ffplay.exe

 Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'd:/movies/source.mp4':0/0
 Metadata:
     major_brand     : isom
     minor_version   : 1
     compatible_brands: isom
     creation_time   : 2014-04-14T13:03:54.000000Z
 Duration: 00:00:58.08, start: 0.000000, bitrate: 12130 kb/s
 Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661),yuv420p, 1920x1080, 12129 kb/s, 25 fps, 25 tbr, 25 tbn, 50 tbc (default)
 Metadata:
     handler_name    : VideoHandler
 Switch subtitle stream from #-1 to #-1 vq= 1428KB sq=    0B f=0/0
 Seek to 49% ( 0:00:28) of total duration ( 0:00:58)       B f=0/0
     30.32 M-V: -0.030 fd=  87 aq=    0KB vq= 1360KB sq=    0B f=0/0  

和结果流(包含源流的一部分)

 Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'd:/movies/target.mp4':f=0/0
 Metadata:
     major_brand     : isom
     minor_version   : 512
     compatible_brands: isomiso2avc1iso6mp41
     encoder         : Lavf57.56.101
 Duration: 00:00:11.64, start: 0.000000, bitrate: 18686 kb/s
 Stream #0:0(und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p, 1920x1080, 18683 kb/s, 38.57 fps, 40 tbr, 90k tbn, 50 tbc (default)
 Metadata:
     handler_name    : VideoHandler
 Switch subtitle stream from #-1 to #-1 vq= 2309KB sq=    0B f=0/0
     5.70 M-V:  0.040 fd= 127 aq=    0KB vq= 2562KB sq=    0B f=0/0  

所以有一个问题,我在复制流时错过了什么?我将不胜感激任何帮助。

祝你好运

1 个答案:

答案 0 :(得分:1)

I dont calculate pts and dts这是你的问题。帧速率和比特率都是时间为分母的比率。但是不写pts / dts你最终会得到比你想要的更短的视频。 h.264没有为每一帧加时间戳。那是容器的工作。您必须根据已知的帧速率或其他值来编制时间戳。