懒人李冰

记录我的生活、学习

FFMPEG 源码分析:av_register_all

av_register_all()几乎是所有使用 FFMPEG 编程调用的第一个函数。av_register_all()的主要功能是注册所有的 formats 和 protocols。

av_register_all 框架

首先列一下该函数的大体框架:

av_register_all
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void av_register_all(void)
{
    static int initialized;

    if(initialized)
        return;
    initialized = 1;

    avcodec_register_all();

    /*(de)muxers*/
    ......
    REGISTER_MUXER   (F4V,              f4v);
    ......
    REGISTER_MUXDEMUX(FLV,              flv);
    REGISTER_MUXDEMUX(H264,             h264);
    REGISTER_MUXDEMUX(HEVC,             hevc);
    REGISTER_MUXER   (MP4,              mp4);

    /*protocols*/
    REGISTER_PROTOCOL(RTMP,             rtmp);
    REGISTER_PROTOCOL(TCP,              tcp);
    REGISTER_PROTOCOL(UDP,              udp);
    ......
}

调用关系

从调用关系图可以看出,通过调用avcodec_register_all注册了和编解码器有关的组件;硬件加速器、解码器、编码器、Parser、Bistream Filter等,以及复用器、解复用器、协议处理。

源码分析

注册一次

initialized_once
1
2
3
4
5
static int initialized;

if (initialized)
    return;
initialized = 1;

该段代码可以看出,当调用过该函数一次后,再次调用时,该函数直接返回。
注意,这种方法在 FFMEPG 源码中非常常见。

注册 codec

1
avcodec_register_all();

该函数的作用为注册codecsparsersfilters
该函数的源码,还请访问avcodec_register_all函数。

注册复用器

REGISTER_MUXER
1
2
3
4
5
6
#define REGISTER_MUXER(X, x)                                            \
    {                                                                   \
        extern AVOutputFormat ff_##x##_muxer;                           \
        if (CONFIG_##X##_MUXER)                                         \
            av_register_output_format(&ff_##x##_muxer);                 \
    }

MP4为例,REGISTER_MUXER(MP4, mp4)展开如下:

1
2
3
extern AVOutpusFormat ff_mp4_muxer;
if(CONFIG_MP4_MUXER)
    av_register_output_format(&ff_mp4_muxer);

av_register_output_format(&ff_mp4_muxer)展开如下:

av_register_output_format
1
2
3
4
5
6
7
8
9
void av_register_output_format(AVOutputFormat *format)
{
    AVOutputFormat **p = last_oformat;

    format->next = NULL;
    while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
        p = &(*p)->next;
    last_oformat = &format->next;
}

注册解复用器

REGISTER_MUXER
1
2
3
4
5
6
#define REGISTER_DEMUXER(X, x)                                          \
    {                                                                   \
        extern AVInputFormat ff_##x##_demuxer;                          \
        if (CONFIG_##X##_DEMUXER)                                       \
            av_register_input_format(&ff_##x##_demuxer);                \
    }

av_register_input_format(&ff_mp4_muxer)展开如下:

av_register_output_format
1
2
3
4
5
6
7
8
9
void av_register_input_format(AVInputFormat *format)
{
    AVInputFormat **p = last_iformat;

    format->next = NULL;
    while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format))
        p = &(*p)->next;
    last_iformat = &format->next;
}
avpriv_atomic_ptr_cas
1
2
3
4
5
6
7
8
9
10
void *avpriv_atomic_ptr_cas(void * volatile *ptr, void *oldval, void *newval)
{
    void *ret;
    pthread_mutex_lock(&atomic_lock);
    ret = *ptr;
    if (ret == oldval)
        *ptr = newval;
    pthread_mutex_unlock(&atomic_lock);
    return ret;
}
REGISTER_MUXDEMUX
1
#define REGISTER_MUXDEMUX(X, x) REGISTER_MUXER(X, x); REGISTER_DEMUXER(X, x)

注册协议

REGISTER_PROTOCOL
1
2
3
4
5
6
#define REGISTER_PROTOCOL(X, x)                                         \
    {                                                                   \
        extern URLProtocol ff_##x##_protocol;                           \
        if (CONFIG_##X##_PROTOCOL)                                      \
            ffurl_register_protocol(&ff_##x##_protocol);                \
    }

TCP为例,REGISTER_PROTOCOL(TCP,tcp)展开如下:

1
2
3
extern URLProtocol ff_tcp_protocol;
if(CONFIG_TCP_PROTOCOL)
    ffurl_register_protocol(&ff_tcp_protocol);

ffurl_register_protocol(&ff_tcp_protocol)展开如下:

ffurl_register_protocol
1
2
3
4
5
6
7
8
9
10
int ffurl_register_protocol(URLProtocol *protocol)
{
    URLProtocol **p;
    p = &first_protocol;
    while (*p)
        p = &(*p)->next;
    *p             = protocol;
    protocol->next = NULL;
    return 0;
}