【问题标题】:FFMpeg: write h264 stream to mp4 container without changesFFMpeg:将 h264 流写入 mp4 容器而不进行更改
【发布时间】:2017-07-23 12:54:32
【问题描述】:

美好的一天。

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

我想捕获 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  

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

最好的问候

【问题讨论】:

    标签: c++ video ffmpeg stream


    【解决方案1】:

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

    【讨论】:

    • 感谢您的回答,szatmary。但我是 ffmpeg 的新手,我不完全明白我应该做什么。我唯一有用的字段是 AVStream 结构中用于输入和输出流的 time_base 字段。 pts\dts的计算方法有哪些?
    • 时基是一个比率,比如 1/30,这意味着时间戳向前移动 1/30 秒。因此,如果您每秒有 30 帧,则 pts 将每帧增加一。如果时基是 1/3000,它将增加 100。查看编解码器上下文时基,并在该基数中写入帧时间戳。
    • 问题是我通常不知道输入流的fps。字段 pts 和 dts 包含 AV_NOPTS_VALUE,所以我不能调用 av_rescale_ 来获取正确的 pts\dts 输出流。
    • ffmpeg 也不知道。你必须从某个地方得到它。如果它是实时流,您可以在捕获每一帧时查看时钟。或者您必须检查捕获设备的设置。
    猜你喜欢
    • 2014-06-14
    • 1970-01-01
    • 1970-01-01
    • 2011-11-07
    • 1970-01-01
    • 1970-01-01
    • 2012-05-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多