ffmpeg源碼分析五:ffmpeg調用x264編碼器的過程分析

該文將以X264編碼器爲例,解釋說明FFMPEG是怎麼調用第三方編碼器來進行編碼的。



全部編碼器和解碼器都是在avcodec_register_all()函數中註冊的。從中能夠找到視頻的H264解碼器和X264編碼器:ide

REGISTER_DECODER(H264,              h264);函數

REGISTER_ENCODER(LIBX264,           libx264);
ui

他們都是經過一下宏進行相應的註冊的:編碼

#define REGISTER_DECODER(X, x)                                          \
    {                                                                   \
        extern AVCodec ff_##x##_decoder;                                \
        if (CONFIG_##X##_DECODER)                                       \
            avcodec_register(&ff_##x##_decoder);                        \
    }
spa


#define REGISTER_ENCODER(X, x)                                          \
    {                                                                   \
        extern AVCodec ff_##x##_encoder;                                \
        if (CONFIG_##X##_ENCODER)                                       \
            avcodec_register(&ff_##x##_encoder);                        \
    }
.net

注 冊的過程發生在avcodec_register(AVCodec *codec)函數中,實際上就是向全局鏈表last_avcodec中加入libx264_encoder、h264_decoder特定的編解碼器, 輸入參數AVCodec是一個結構體,能夠理解爲編解碼器的基類,其中不只包含了名稱,id等屬性,並且包含了以下函數指針,讓每一個具體的編解碼器擴展類 實現。指針

    int (*init)(AVCodecContext *);code

    int (*encode_sub)(AVCodecContext *, uint8_t *buf, int buf_size,
                      const struct AVSubtitle *sub);
    /**
     * Encode data to an AVPacket.
     *
     * @param       avctx          codec context
     * @param       avpkt          output AVPacket (may contain a user-provided buffer)
     * @param[in]  frame          AVFrame containing the raw data to be encoded
     * @param[out] got_packet_ptr encoder sets to 0 or 1 to indicate that a
     *                            non-empty packet was returned in avpkt.
     * @return  0 on success, negative error code on failure
     */
    int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr);
    int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);
    int (*close)(AVCodecContext *);
    /**
     * Flush buffers.
     * Will be called when seeking
     */
    void (*flush)(AVCodecContext *);
視頻

繼續追蹤libx264,也就是X264的靜態編碼庫,它在FFMPEG編譯的時候被引入做爲H.264編碼器。在libx264.c中有以下代碼接口

AVCodec ff_libx264_encoder = {
    .name             = "libx264",
    .long_name        = NULL_IF_CONFIG_SMALL("libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10"),
    .type             = AVMEDIA_TYPE_VIDEO,
    .id               = AV_CODEC_ID_H264,
    .priv_data_size   = sizeof(X264Context),
    .init             = X264_init,
    .encode2          = X264_frame,
    .close            = X264_close,
    .capabilities     = CODEC_CAP_DELAY | CODEC_CAP_AUTO_THREADS,
    .priv_class       = &x264_class,
    .defaults         = x264_defaults,
    .init_static_data = X264_init_static,
};

這裏具體對來自AVCodec的屬性和方法進行賦值。其中
    .init = X264_init,
    .encode2 = X264_frame,
    .close = X264_close,
將函數指針指向了具體函數,這三個函數將使用libx264靜態庫中提供的API,也就是X264的主要接口函數進行具體實現。

上面看到的X264Context封裝了X264所須要的上下文管理數據,
typedef struct X264Context {
    AVClass        *class;
    x264_param_t    params;
    x264_t         *enc;
    x264_picture_t  pic;
    uint8_t        *sei;
    int             sei_size;
    char *preset;
    char *tune;
    char *profile;
    char *level;
    int fastfirstpass;
    char *wpredp;
    char *x264opts;
    float crf;
    float crf_max;
    int cqp;
    int aq_mode;
    float aq_strength;
    char *psy_rd;
    int psy;
    int rc_lookahead;
    int weightp;
    int weightb;
    int ssim;
    int intra_refresh;
    int bluray_compat;
    int b_bias;
    int b_pyramid;
    int mixed_refs;
    int dct8x8;
    int fast_pskip;
    int aud;
    int mbtree;
    char *deblock;
    float cplxblur;
    char *partitions;
    int direct_pred;
    int slice_max_size;
    char *stats;
    int nal_hrd;
    char *x264_params;
} X264Context;
它屬於結構體AVCodecContext的void *priv_data變量,定義了每種編解碼器私有的上下文屬性,AVCodecContext也相似上下文基類同樣。
能夠用類圖來表示大概的編解碼器組合。


編解碼器打開操做是在transcode_init() -> init_input_stream() -> avcodec_open2()完成的,對具體的編解碼器進行初始化。例如X264_init()

具體的編碼操做是在transcode() -> transcode_step() -> reap_filters() -> do_video_out() 或 do_audio_out() -> avcodec_encode_video2() 或 avcodec_encode_audio2()。而後經過函數指針調用特定的編解碼器。例如X264_frame()。

最後經過avcodec_close()關閉編解碼器。例如X264_close()。

相關文章
相關標籤/搜索