ffmpeg - 使用ffmpeg libavcodec将视频流编码为H264,为什么持续时间为零

标签 ffmpeg h.264 libavcodec

需要帮助,最近我正在使用ffmpeg libavcodec解码视频文件然后编码为H264并写入mp4媒体容器,最后媒体文件的持续时间为零,以下是我的代码工作流程:

AVFormatContext*    input_format_context = NULL;
AVFormatContext*    output_format_context = NULL;
AVIOContext*        output_io_context = NULL;
AVCodecContext*     input_codec_context = NULL;
AVCodecContext*     output_codec_context = NULL;
AVCodec*            codec = NULL;
AVStream*           input_stream = NULL;
AVStream*           output_stream = NULL;
AVFrame*            frame = NULL;

int convert_init(const char* input_filename, const char* output_filename)
{
    /** Allocate a new encode context */
    avformat_open_input(&input_format_context, 
            input_filename, NULL, NULL); 

    /** Get information on the input file (number of streams etc.). */
    avformat_find_stream_info(input_format_context, NULL);  

    /** Open the output file to write to it. */
    avio_open(&output_io_context, output_filename, 
            AVIO_FLAG_WRITE);

    /** Create a new format context for the output container format. */
    output_format_context = avformat_alloc_context();

    /** Associate the output file (pointer) with the container format context. */
    output_format_context->pb = output_io_context;

    /** Guess the desired container format based on the file extension. */
    output_format_context->oformat = av_guess_format(NULL, 
            output_filename, NULL);

    av_strlcpy((output_format_context)->filename, output_filename, 
            sizeof(output_format_context->filename));

    /** stream0 is the video stream */
    AVStream* input_stream = input_format_context->streams[0];


    /** 
     * Init the input_codec_context 
     */

    /** Find a decoder for the audio stream. */
    codec = avcodec_find_decoder(input_stream->codecpar->codec_id);

    /** Allocate a new decode context */
    input_codec_context = avcodec_alloc_context3(codec);

    /** Initialize the stream parameters with demuxer information */
    avcodec_parameters_to_context(input_codec_context, 
            input_stream->codecpar);    

    /** Open the decoder for the stream. */
    avcodec_open2(input_codec_context, codec, NULL);                                

    /** 
     *  Create an output stream for writing encoded data 
     *
     *  AM I MISSING SOMETHING ?
     *
     */
    output_stream = avformat_new_stream(output_format_context, NULL);

    /** 
     * Init the output_codec_context 
     */
    /** Find a encoder for the output video stream, using H264. */
    codec = avcodec_find_encoder(AV_CODEC_ID_H264);     

    /** Allocate an encode context. */
    output_codec_context = avcodec_alloc_context3(codec);  

    /** 
     *  Setup encode context parameters.
     *  
     *  AM I MISSING SOMETHING ?
     *
     * */
    output_codec_context->bit_rate = input_codec_context->bit_rate;
    output_codec_context->width = input_codec_context->width;
    output_codec_context->height = input_codec_context->height;
    output_codec_context->time_base = (AVRational){1, 25};
    output_codec_context->framerate = (AVRational){25, 1};
    output_codec_context->gop_size = 10;
    output_codec_context->max_b_frames = 1;
    output_codec_context->pix_fmt = AV_PIX_FMT_YUV420P;
    output_codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

    /** Setup output_stream codecpar. */
    avcodec_parameters_from_context(output_stream->codecpar, 
            codec_context);

    /** Alloc an av frame */
    frame = av_frame_alloc();           

}

void convert_it()
{
    AVPacket input_packet;
    AVPacket output_packet;

    /** Write the media file container header */
    avformat_write_header(output_format_context, NULL);

    /** 
     * decode frames and encode to H264 
     * */
    while (1) { 
        av_init_packet(&input_packet);
        input_packet.data = NULL;
        input_packet.size = 0;

        av_init_packet(&output_packet);
        output_packet.data = NULL;
        output_packet.size = 0;

        /** Read a frame to decode */
        av_read_frame(input_format_context, &input_packet);         
        if (av_read_frame is end of file) {
            break;
        }
        ...
        ...

        /** Decoding... */
        avcodec_send_packet(input_codec_context, &input_packet);
        ...
        ...

        /** Get a decoded frame */
        avcodec_receive_frame(input_codec_context, frame);
        ...
        ...

        /** Make the frame writable, is it necessary ?? */
        av_frame_make_writable(frame);

        /** Encode to H264 */
        avcodec_send_frame(output_codec_context, frame);
        ...
        ...

        /** Get a encoded packet */
        avcodec_receive_packet(output_codec_context, &output_packet);

        /** 
         * Write the packet to output.  
         * Here is the point! should I configure the parameters 
         * in packet such as 'pts', 'dts', 'duration', etc, if so, 
         * hwo? or I just directly write the packet into output? 
         */
        av_interleaved_write_frame(output_format_context, &packet);                 
    }

    /** Write the media file container trailer */
    av_write_trailer(output_format_context);

}

int main() {
    convert_init("./sample.avi", "./output.mp4");
    convert_it();

}

使用VLC或QuickTime播放output.mp4文件,失败,导致文件时长为零,拖拽时间进度条时,可以清晰看到画面帧,看来编码包缓冲数据是正确的,但是时间戳是错误的,我在配置 output_stream 或数据包时是否遗漏了什么?以下是来自 ffprobe 的消息。
ffprobe output.mp4
ffprobe version 3.3.3 Copyright (c) 2007-2017 the FFmpeg developers
  built with Apple LLVM version 8.1.0 (clang-802.0.42)
  configuration: --enable-shared --enable-libmp3lame
  libavutil      55. 58.100 / 55. 58.100
  libavcodec     57. 89.100 / 57. 89.100
  libavformat    57. 71.100 / 57. 71.100
  libavdevice    57.  6.100 / 57.  6.100
  libavfilter     6. 82.100 /  6. 82.100
  libswscale      4.  6.100 /  4.  6.100
  libswresample   2.  7.100 /  2.  7.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'output.mp4':
  Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2avc1mp41
    encoder         : Lavf57.71.100
  Duration: 00:00:00.06, start: 0.000000, bitrate: 6902181 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 720x408 [SAR 1:1 DAR 30:17], 13929056 kb/s, 90k fps, 90k tbr, 90k tbn, 50 tbc (default)
    Metadata:
      handler_name    : VideoHandler

最佳答案

您的问题可能是在复用过程中帧的时基错误。
根据格式,the muxer can change the stream timebase .

The stream timebase should be set to the timebase that the caller desires to use for this stream (note that the timebase actually used by the muxer can be different, as will be described later).
[...]
Do note that the timing information on the packets sent to the muxer must be in the corresponding AVStream's timebase. That timebase is set by the muxer (in the avformat_write_header() step) and may be different from the timebase requested by the caller.



因此,在编写帧之前,您必须将其时基从创建 FormatContext 时设置的理论时基转换为流实际使用的时基。为此,您可以使用 av_packet_rescale_ts功能。

前任:av_packet_rescale_ts( packet, codec_contex->time_base, // your theoric timebase format_context->streams[packet->stream_index]->time_base); // the actual timebase

关于ffmpeg - 使用ffmpeg libavcodec将视频流编码为H264,为什么持续时间为零,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45997264/

相关文章:

audio - webm 实时输入到 HLS 实时输出

FFmpeg 无法读取现有的 .bmp 帧序列来制作 .avi 文件;怎么了?

windows-7 - 使用 SinkWriter 编码视频时更改 h.264 质量

ffmpeg - 通过 RTSP 进行 MJPEG 流传输

encoding - 如何设置 libavcodec 以使用 4 :2:2 chroma when encoding MPEG-2 4:2:2 profile?

audio - 无法使用 png 和 mp3 音频生成 mp4

c++ - 如何并行化这个 for 循环以快速将 YUV422 转换为 RGB888?

video - H.264 视频和 MPEG-4 视频有什么区别?

C++ FFmpeg 刷新后如何继续编码?

ffmpeg - libavformat 在编码时生成没有大小的 flv 文件