1. 程式人生 > >[9]【ffmpeg原始碼分析 1】av_register_all()和avcodec_register_all()

[9]【ffmpeg原始碼分析 1】av_register_all()和avcodec_register_all()

日期:2016.10.18
作者:isshe
github:github.com/isshe
郵箱:[email protected]

前言

接下來打算學習一下編解碼,不過好像很難的樣子, 希望能看懂。

1. av_register_all()

  • 所在檔案:allformats.c
  • 主要程式碼:(每個巨集只列舉一個)
void av_register_all(void)
{
    static int initialized;

    if (initialized)
        return;

    //註冊編解碼器
    avcodec_register_all();

    /* (de)muxers */
//註冊複用器 REGISTER_MUXER (A64, a64); //註冊解複用器 REGISTER_DEMUXER (AA, aa); //兩個同時註冊 REGISTER_MUXDEMUX(AC3, ac3);

1.1 REGISTER_MUXER巨集

  • 程式碼:
#define REGISTER_MUXER(X, x)                                            \
    {                                                                   \
        extern
AVOutputFormat ff_##x##_muxer; \ if (CONFIG_##X##_MUXER) \ av_register_output_format(&ff_##x##_muxer); \ }
  • “##”號起連線作用,例如:a##b = ab.
  • ff_##x##_muxer, 一個與x對應的AVOutputFormat.
  • 其實這些外部變數,一般定義的時候就賦了初值。(限於篇幅,具體的示例在下面編解碼相關的地方)

1.1.1 av_register_output_format()

  • 註冊一個與該格式相應的AVFormat結構
  • 相關程式碼:
//head of registered output format linked list 
static AVOutputFormat *first_oformat = NULL;
static AVOutputFormat **last_oformat = &first_oformat;

void av_register_output_format(AVOutputFormat *format)
{
    AVOutputFormat **p = last_oformat;

    // Note, format could be added after the first 2 checks but that implies that *p is no longer NULL
    //檢查連結串列中有無指定的format,沒有就加入。
    //在前面兩個條件成立後才把format連線到p連結串列中。
    while(p != &format->next && !format->next && avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
        p = &(*p)->next;

    if (!format->next)
        last_oformat = &format->next;
}
  • last_oformat是一個二次指標。
  • avpriv_atomic_ptr_cas把format放到p連結串列中。
  • 遍歷連結串列,把指定format加到連結串列後面。

1.2 REGISTER_DEMUXER 巨集

  • 和REGISTER_MUXER()類似。

1.3 REGISTER_MUXDEMUX 巨集

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

2. avcodec_register_all()

  • 所在檔案:allcodecs.c
  • 主要程式碼:(每種情況列舉一個)
void avcodec_register_all(void)
{
    static int initialized;

    if (initialized)
        return;
    initialized = 1;

    //硬體加速器
    /* hardware accelerators */
    REGISTER_HWACCEL(H263_CUVID,        h263_cuvid);

    //注意以下函式適用於視訊和音訊
    //註冊編碼器
    REGISTER_ENCODER(A64MULTI,          a64multi);
    //註冊解碼器
    REGISTER_DECODER(AASC,              aasc);
    //兩個同時註冊
    REGISTER_ENCDEC (ALIAS_PIX,         alias_pix);
    //註冊分析器???不懂
    REGISTER_PARSER(AAC,                aac);

2.1 REGISTER_HWACCEL 巨集

  • 註冊硬體加速器巨集
  • 程式碼:
#define REGISTER_HWACCEL(X, x)                                          \
    {                                                                   \
        extern AVHWAccel ff_##x##_hwaccel;                              \
        if (CONFIG_##X##_HWACCEL)                                       \
            av_register_hwaccel(&ff_##x##_hwaccel);                     \
    }
  • extern的那個變數在原始碼中沒找著…

2.1.1 av_register_hwaccel()

  • 註冊硬體加速器的函式
  • 程式碼:和前面的muter類似。
void av_register_hwaccel(AVHWAccel *hwaccel)
{
    AVHWAccel **p = last_hwaccel;
    hwaccel->next = NULL;
    while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, hwaccel))
        p = &(*p)->next;
    last_hwaccel = &hwaccel->next;
}

2.2 REGISTER_ENCODER巨集

  • 註冊編碼器的巨集
  • 程式碼:
#define REGISTER_ENCODER(X, x)                                          \
    {                                                                   \
        extern AVCodec ff_##x##_encoder;                                \
        if (CONFIG_##X##_ENCODER)                                       \
            avcodec_register(&ff_##x##_encoder);                        \
    }

2.2.1 ff_##x##_encoder變數

  • 用aac示例:ff_aac_encoder變數
AVCodec ff_aac_encoder = {
    .name           = "aac",
    .long_name      = NULL_IF_CONFIG_SMALL("AAC (Advanced Audio Coding)"),
    .type           = AVMEDIA_TYPE_AUDIO,
    .id             = AV_CODEC_ID_AAC,
    .priv_data_size = sizeof(AACEncContext),
    .init           = aac_encode_init,   //初始化
    .encode2        = aac_encode_frame,  //編碼函式
    .close          = aac_encode_end,    //
    .defaults       = aac_encode_defaults,
    .supported_samplerates = mpeg4audio_sample_rates,
    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
    .capabilities   = AV_CODEC_CAP_SMALL_LAST_FRAME | AV_CODEC_CAP_DELAY,
    .sample_fmts    = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP,
                                                     AV_SAMPLE_FMT_NONE },
    .priv_class     = &aacenc_class,
};
  • 可見,這些外部變數都是已經賦值好的,解碼的【或其他】也類似。
  • 下面呼叫的註冊函式就是把這麼一個結構放到連結串列中。

2.2.2 avcodec_register

  • 註冊編碼器的函式
  • 程式碼:
av_cold void avcodec_register(AVCodec *codec)
{
    AVCodec **p;
    avcodec_init();      //!!!
    p = last_avcodec;  
    codec->next = NULL;

    while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, codec))
        p = &(*p)->next;
    last_avcodec = &codec->next;

    if (codec->init_static_data)
        codec->init_static_data(codec);
}

2.3 REGISTER_DECODER 巨集

  • 和編碼類似

2.4 REGISTER_ENCDEC 巨集

  • 同時註冊解碼和編碼器

2.5 REGISTER_PARSER 巨集

  • 程式碼:
#define REGISTER_PARSER(X, x)                                           \
    {                                                                   \
        extern AVCodecParser ff_##x##_parser;                           \
        if (CONFIG_##X##_PARSER)                                        \
            av_register_codec_parser(&ff_##x##_parser);                 \
    }

av_register_codec_parser()

  • 程式碼:
void av_register_codec_parser(AVCodecParser *parser)
{
    do {
        parser->next = av_first_parser;
    } while (parser->next != avpriv_atomic_ptr_cas((void * volatile *)&av_first_parser, parser->next, parser));
}

參考資料

程式碼下載