FFMpeg 源码分析(1)av_register_all()
一般我们使用FFMpeg做编解码都会先调用av_register_all()
这个函数开头,完成基本的初始化工作。而至于它具体初始化了哪些东西呢,我们直接从代码里面来看吧。
void av_register_all(void)
{
static int initialized; //标志位指示是否已经初始化过
if (initialized)
return;
initialized = 1;
avcodec_register_all(); // 注册编解码器
/* (de)muxers */ //注册复用器与解复用器
REGISTER_MUXER (A64, a64);
REGISTER_DEMUXER (AA, aa);
REGISTER_DEMUXER (AAC, aac);
REGISTER_MUXDEMUX(AC3, ac3);
REGISTER_DEMUXER (ACM, acm);
REGISTER_DEMUXER (ACT, act);
..........
}
说到这里,应该先说明一个FFMpeg里面的设计思路。向我们在这里注册这些编解码器和(解)复用器在FFMpeg的源代码里面是个什么概念呢。
在FFMepg里,像编解码器,它们都是被一个全局的链表所维护这。所谓注册其实就是往这些全局的链表里面添加节点,而每一个节点维护的是一个编码器或者解码器的相关信息以及相互操作的函数指针。
理解了这样一个注册的概念。我们就可以来看接下来的代码了。先看avcodec_register_all()
这个函数。
void avcodec_register_all(void)
{
static int initialized;
if (initialized)
return;
initialized = 1;
/* hardware accelerators */
REGISTER_HWACCEL(H263_VAAPI, h263_vaapi);
REGISTER_HWACCEL(H263_VIDEOTOOLBOX, h263_videotoolbox);
REGISTER_HWACCEL(H264_D3D11VA, h264_d3d11va);
REGISTER_HWACCEL(H264_DXVA2, h264_dxva2);
REGISTER_HWACCEL(H264_MMAL, h264_mmal);
REGISTER_HWACCEL(H264_QSV, h264_qsv);
REGISTER_HWACCEL(H264_VAAPI, h264_vaapi);
REGISTER_HWACCEL(H264_VDA, h264_vda);
REGISTER_HWACCEL(H264_VDA_OLD, h264_vda_old);
............
/* video codecs */
REGISTER_ENCODER(A64MULTI, a64multi);
REGISTER_ENCODER(A64MULTI5, a64multi5);
REGISTER_DECODER(AASC, aasc);
REGISTER_DECODER(AIC, aic);
REGISTER_ENCDEC (ALIAS_PIX, alias_pix);
REGISTER_ENCDEC (AMV, amv);
REGISTER_DECODER(ANM, anm);
REGISTER_DECODER(ANSI, ansi);
REGISTER_ENCDEC (APNG, apng);
REGISTER_ENCDEC (ASV1, asv1);
.............
}
然后我们来看一个 REGISTER_HWACCEL(H263_VIDEOTOOLBOX, h263_videotoolbox);
这样一句代码做了什么事情吧。
#define REGISTER_HWACCEL(X, x) \
{ \
extern AVHWAccel ff_##x##_hwaccel; \
if (CONFIG_##X##_HWACCEL) \
av_register_hwaccel(&ff_##x##_hwaccel); \
}
很明显这是一个宏。如果按照上句调用把它展开还原应该就是:
extern AVHWAccel ff_h263_videotoolbox_hwaccel;
if (CONFIG_H263_VIDEOTOOLBOX_HWACCEL)
av_register_hwaccel(&ff_videotoolbox_hwaccel);
第一句表明在别的地方应该有定义过这样一个变量 ff_h263_videotoolbox_hwaccel
然后我们在源代码里面搜索这个变量会发现,它别定义在了 libavcodec/videotoolbox.c 里面。
AVHWAccel ff_h263_videotoolbox_hwaccel = {
.name = "h263_videotoolbox",
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H263,
.pix_fmt = AV_PIX_FMT_VIDEOTOOLBOX,
.alloc_frame = ff_videotoolbox_alloc_frame,
.start_frame = videotoolbox_mpeg_start_frame,
.decode_slice = videotoolbox_mpeg_decode_slice,
.end_frame = videotoolbox_mpeg_end_frame,
.uninit = ff_videotoolbox_uninit,
.priv_data_size = sizeof(VTContext),
};
完整代码如上,我们先来看一下 AVHWAccel
这个类型的定义吧。
/**
* @defgroup lavc_hwaccel AVHWAccel
* @{
*/
typedef struct AVHWAccel {
/**
* Name of the hardware accelerated codec.
* The name is globally unique among encoders and among decoders (but an
* encoder and a decoder can share the same name).
*/
const char *name;
/**
* Type of codec implemented by the hardware accelerator.
*
* See AVMEDIA_TYPE_xxx
*/
enum AVMediaType type;
/**
* Codec implemented by the hardware accelerator.
*
* See AV_CODEC_ID_xxx
*/
enum AVCodecID id;
/**
* Supported pixel format.
*
* Only hardware accelerated formats are supported here.
*/
enum AVPixelFormat pix_fmt;
/**
* Hardware accelerated codec capabilities.
* see HWACCEL_CODEC_CAP_*
*/
int capabilities;
/*****************************************************************
* No fields below this line are part of the public API. They
* may not be used outside of libavcodec and can be changed and
* removed at will.
* New public fields should be added right above.
*****************************************************************
*/
struct AVHWAccel *next;
/**
* Allocate a custom buffer
*/
int (*alloc_frame)(AVCodecContext *avctx, AVFrame *frame);
/**
* Called at the beginning of each frame or field picture.
*
* Meaningful frame information (codec specific) is guaranteed to
* be parsed at this point. This function is mandatory.
*
* Note that buf can be NULL along with buf_size set to 0.
* Otherwise, this means the whole frame is available at this point.
*
* @param avctx the codec context
* @param buf the frame data buffer base
* @param buf_size the size of the frame in bytes
* @return zero if successful, a negative value otherwise
*/
int (*start_frame)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size);
/**
* Callback for each slice.
*
* Meaningful slice information (codec specific) is guaranteed to
* be parsed at this point. This function is mandatory.
* The only exception is XvMC, that works on MB level.
*
* @param avctx the codec context
* @param buf the slice data buffer base
* @param buf_size the size of the slice in bytes
* @return zero if successful, a negative value otherwise
*/
int (*decode_slice)(AVCodecContext *avctx, const uint8_t *buf, uint32_t buf_size);
/**
* Called at the end of each frame or field picture.
*
* The whole picture is parsed at this point and can now be sent
* to the hardware accelerator. This function is mandatory.
*
* @param avctx the codec context
* @return zero if successful, a negative value otherwise
*/
int (*end_frame)(AVCodecContext *avctx);
/**
* Size of per-frame hardware accelerator private data.
*
* Private data is allocated with av_mallocz() before
* AVCodecContext.get_buffer() and deallocated after
* AVCodecContext.release_buffer().
*/
int frame_priv_data_size;
/**
* Called for every Macroblock in a slice.
*
* XvMC uses it to replace the ff_mpv_decode_mb().
* Instead of decoding to raw picture, MB parameters are
* stored in an array provided by the video driver.
*
* @param s the mpeg context
*/
void (*decode_mb)(struct MpegEncContext *s);
/**
* Initialize the hwaccel private data.
*
* This will be called from ff_get_format(), after hwaccel and
* hwaccel_context are set and the hwaccel private data in AVCodecInternal
* is allocated.
*/
int (*init)(AVCodecContext *avctx);
/**
* Uninitialize the hwaccel private data.
*
* This will be called from get_format() or avcodec_close(), after hwaccel
* and hwaccel_context are already uninitialized.
*/
int (*uninit)(AVCodecContext *avctx);
/**
* Size of the private data to allocate in
* AVCodecInternal.hwaccel_priv_data.
*/
int priv_data_size;
} AVHWAccel;
可以发现 保护的操作函数有:alloc_frame , start_frame, decode_slice, end_frame, decode_mb, init, uninit
。从ff_h263_videotoolbox_hwaccel
的定义中我们可以发现,值实现了 alloc_frame, start_frame, decode_slice, end_frame, uninit
这几个方法。
那我们具体来看一下av_register_hwaccel(&ff_videotoolbox_hwaccel)
这个函数做了什么。
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;
}
这个很直白,是在做一个链表追加的操作。而它的链表定义在什么样呢?如下:
static AVHWAccel *first_hwaccel = NULL;
static AVHWAccel **last_hwaccel = &first_hwaccel;
这样,整个硬件加速器的注册过程就完成了。以 first_hwaccel
为头节点。
同样的,我们可以发现,编解码器的注册也是这个套路,只不过编解码器是用AVCodec
这个结构体来表示的。同样的,定义一个全局的 ff_x_encoder 或者 ff_x_decoder,然后在各自的实现文件中,实现AVCodec
中定义的方法。函数指针赋予 ff_x_encode 或者 ff_x_decoder。对应的 Parser 也是这样注册。
这样下来,所有的注册工作就完成了,avcodec_register_all
也就可以功成身退了。得到的结果就是几个初始化过的全局链表。
注:如果我们我们想往FFMpeg里面添加一个编解码器。那我们只要自己实现好相应的方法,然后再在avcodec_register_all
完成注册操作,这样这个自己添加的编解码就能被FFMpeg所使用到了。
好了,分析忘了avcodec_register_all()
这句代码,那么接下来的就是注册一些复用器与解复用器了。这块主要看三个宏:
#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)
其中AVOutputFormat
结构体表示复合器。AVInputFormat
表示解复合器。注册完成后将会产生两个分别以 first_iformat
和 first_oformat
为头节点的全局的链表。
至此,整个av_register_all
函数的工作就算做完了。