Bumblebee
Bumblebee

Reputation: 21

FFMpeg: write h264 stream to mp4 container without changes

Good day.

For brevity, the code omits error handling and memory management.

I want to capture h264 video stream and pack it to mp4 container without changes. Since i don't control the source of stream, i can not make assumptions about stream structure. In this way i must probe input stream.

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

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

This code correctly defines h264 stream.

Next, i create input format context,

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

set image size,

    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;

create output format context,

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

Initialization is done. Further there is a shifting packets from h264 stream to mp4 container. I dont calculate pts and dts, because source packet has AV_NOPTS_VALUE in them.

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

Further i write trailer and free allocated memory. That is all. Code works and i got playable mp4 file.

Now the problem: the stream characteristics of the resulting file is not completely consisent with the characteristics of the source stream. In particular, fps and bitrate is higher than it should be.

As sample, below is output ffplay.exe for source stream

 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  

and for resulting stream (contains part of source stream)

 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  

So there is a question, what i missed when copying stream? I will be grateful for any help.

Best regards

Upvotes: 2

Views: 3638

Answers (1)

szatmary
szatmary

Reputation: 31100

I dont calculate pts and dts This is your problem. frame rate and bit rate are both ratios where time is the denominator. But not writing pts/dts you end up with a video shorter than you want. h.264 does not timestamp every frame. that is the containers job. You must make up time stamps from the known frame rate, or another value.

Upvotes: 2

Related Questions