FFmpeg 是如何实现多态的?

众所周知,FFmpeg 在解码的时候,无论输入文件是 MP4 文件还是 FLV 文件,或者其它文件格式,都能正确解封装、解码,而代码不需要针对不同的格式做出任何改变,这是面向对象中很常见的多态特性,但 FFmpeg 是用 C 语言编写的,那么它是如何使用 C 语言实现了多态特性的呢?

要解决这个问题,首先需要从函数 av_register_all 说起。

av_register_all
av_register_all 是几乎所有 FFmpeg 程序中第一个被调用的函数,用于注册在编译 FFmpeg 时设置了 --enable 选项的封装器、解封装器、编码器、解码器等。源码如下:

#define REGISTER_MUXER(X, x)                                            \
    {                                                                  \
        extern AVOutputFormat ff_##x##_muxer;                          \
        if (CONFIG_##X##_MUXER)                                        \
            av_register_output_format(&ff_##x##_muxer);                \
    }

#define REGISTER_DEMUXER(X, x)                                          \
    {                                                                  \
        extern AVInputFormat ff_##x##_demuxer;                          \
        if (CONFIG_##X##_DEMUXER)                                      \
            av_register_input_format(&ff_##x##_demuxer);                \
    }

#define REGISTER_MUXDEMUX(X, x) REGISTER_MUXER(X, x); REGISTER_DEMUXER(X, x)

static void register_all(void)
{
    // 注册编解码器
    avcodec_register_all();

// 注册封装器、解封装器
    /* (de)muxers */
    REGISTER_MUXER  (A64,              a64);
    REGISTER_DEMUXER (AA,              aa);
    REGISTER_DEMUXER (AAC,              aac);
    REGISTER_MUXDEMUX(AC3,              ac3);
    REGISTER_MUXDEMUX(FLV,              flv);
    REGISTER_MUXDEMUX(GIF,              gif);
    ...

/* image demuxers */
    REGISTER_DEMUXER (IMAGE_BMP_PIPE,        image_bmp_pipe);
    REGISTER_DEMUXER (IMAGE_JPEG_PIPE,      image_jpeg_pipe);
    REGISTER_DEMUXER (IMAGE_SVG_PIPE,        image_svg_pipe);
    REGISTER_DEMUXER (IMAGE_WEBP_PIPE,      image_webp_pipe);
    REGISTER_DEMUXER (IMAGE_PNG_PIPE,        image_png_pipe);
    ...

/* external libraries */
    REGISTER_MUXER  (CHROMAPRINT,      chromaprint);
    ...
}

void av_register_all(void)
{
    static AVOnce control = AV_ONCE_INIT;

ff_thread_once(&control, register_all);
}

define 里的 ## 用于拼接两个字符串,比如 REGISTER_DEMUXER(AAC, aac) ,它等效于:
extern AVInputFormat ff_aac_demuxer;
if(CONFIG_AAC_DEMUXER) av_register_input_format(&ff_aac_demuxer);

可以看出,编译 ffmpeg 时类似于 “–enable-muxer=xxx” 这样的选项在此时发挥了作用,它决定是否注册某个格式对应的(解)封装器,以便之后处理该格式的时候找到这个(解)封装器。

av_register_input_format
av_register_input_format、av_register_output_format 源码如下:
/** head of registered input format linked list */
static AVInputFormat *first_iformat = NULL;
/** head of registered output format linked list */
static AVOutputFormat *first_oformat = NULL;

static AVInputFormat **last_iformat = &first_iformat;
static AVOutputFormat **last_oformat = &first_oformat;

void av_register_input_format(AVInputFormat *format)
{
    AVInputFormat **p = last_iformat;

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/71d5d8e08b4b358f5a1d62e4ce55de4e.html