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

问题描述 投票:1回答:1

需要帮助,最近,我使用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文件,它失败了,导致文件持续时间为零,当拖动时间进度条时,我可以清楚地看到相框,看来编码包缓冲区数据是正确的,但是timestamp是错误,我在配置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
ffmpeg h.264 libavcodec
1个回答
0
投票

您的问题可能是在多路复用期间您的帧的时基错误。根据格式,the muxer can change the stream timebase

流时基应该设置为呼叫者希望用于该流的时基(注意,复用器实际使用的时基可以是不同的,如稍后将描述的)。 [...] 请注意,发送到复用器的数据包的时序信息必须位于相应的AVStream时基中。该时基由复用器设置(在avformat_write_header()步骤中),并且可能与调用者请求的时基不同。

因此,在编写框架之前,必须将其时基从创建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

© www.soinside.com 2019 - 2024. All rights reserved.