我们以avcodec_find_encoder(AV_CODEC_ID_AAC)和avcodec_find_decoder(AV_CODEC_ID_AAC)来分析avcodec_find_encoder和avcodec_find_decoder两个函数。
一、avcodec_find_encoder()
avcodec_find_encoder(AV_CODEC_ID_AAC);
查找ID值为AV_CODEC_ID_AAC的AAC编码器。avcodec_find_encoder()的源代码在文件libavcodec\allcodecs.c中,如下所示:
AVCodec *avcodec_find_encoder(enum AVCodecID id)
{
return find_codec(id, av_codec_is_encoder);
}
从源代码可以看出avcodec_find_encoder()调用了一个find_encdec(),注意它的第二个参数是一个函数指针,av_codec_is_encoder函数后面会用到。
下面我们看一下find_codec()函数的源码,如下所示:
libavcodec\allcodecs.c
static AVCodec *find_codec(enum AVCodecID id, int (*x)(const AVCodec *))
{
const AVCodec *p, *experimental = NULL;
void *i = 0;
id = remap_deprecated_codec_id(id);
while ((p = av_codec_iterate(&i))) {
if (!x(p))
continue;
if (p->id == id) {
if (p->capabilities & AV_CODEC_CAP_EXPERIMENTAL && !experimental) {
experimental = p;
} else
return (AVCodec*)p;
}
}
return (AVCodec*)experimental;
}
find_codec()函数既可以用来查找编码器,也可以用来查找解码器。find_codec()中有一个循环,该循环会遍历AVCodec结构的链表,逐一比较输入的ID和每一个编码器或者解码器的ID,直到找到ID取值相等的编码器或者解码器。
在这里有几点需要注意:
(1)av_codec_iterate是一个遍历器,遍历变量codec_list,存储AVCodec的数组。av_codec_iterate的定义见下面。
(2)remap_deprecated_codec_id()用于将一些过时的编码器ID映射到新的编码器ID。
(3)函数的第二个参数x,就是avcodec_find_encoder调用find_codec传递过来的函数av_codec_is_encoder。如果是avcodec_find_decoder调用find_codec传递过来的函数就是av_codec_is_decoder。即find_codec()函数既可以用来查找编码器,也可以用来查找解码器。编码器和解码器都存在变量codec_list中。把满足要求的AVCodec的ID值与传入的ID值进行比较,如果相等,就是我们要找的解码器或者编码器。
const AVCodec *av_codec_iterate(void **opaque)
{
uintptr_t i = (uintptr_t)*opaque;
const AVCodec *c = codec_list[i];
ff_thread_once(&av_codec_static_init, av_codec_init_static);
if (c)
*opaque = (void*)(i + 1);
return c;
}
libavcodec\utils.c
int av_codec_is_encoder(const AVCodec *codec)
{
return codec && (codec->encode_sub || codec->encode2 ||codec->send_frame);
}
从源代码可以看出,av_codec_is_encoder()判断了一下AVCodec是否包含了encode2()或者encode_sub()接口函数。当满足这些要求的时候,我们可以认为该编码器是合法的,但是ID值不一定和我们需要的相匹配。
libavcodec\codec_list.c
static const AVCodec * const codec_list[] = {
.......
&ff_aac_encoder,
.....
&ff_aac_decoder,
......
NULL };
libavcodec\aacenc.c
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,
};
二、avcodec_find_decoder()
avcodec_find_decoder(AV_CODEC_ID_AAC);
查找ID值为AV_CODEC_ID_AAC的AAC解码器。该函数与avcodec_find_encoder()逻辑差不多,这里就不多做分析了。
AVCodec *avcodec_find_decoder(enum AVCodecID id)
{
return find_codec(id, av_codec_is_decoder);
}
static AVCodec *find_codec(enum AVCodecID id, int (*x)(const AVCodec *))
{
const AVCodec *p, *experimental = NULL;
void *i = 0;
id = remap_deprecated_codec_id(id);
while ((p = av_codec_iterate(&i))) {
if (!x(p))
continue;
if (p->id == id) {
if (p->capabilities & AV_CODEC_CAP_EXPERIMENTAL && !experimental) {
experimental = p;
} else
return (AVCodec*)p;
}
}
return (AVCodec*)experimental;
}
const AVCodec *av_codec_iterate(void **opaque)
{
uintptr_t i = (uintptr_t)*opaque;
const AVCodec *c = codec_list[i];
ff_thread_once(&av_codec_static_init, av_codec_init_static);
if (c)
*opaque = (void*)(i + 1);
return c;
}
AVCodec ff_aac_decoder = {
.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(AACContext),
.init = aac_decode_init,
.close = aac_decode_close,
.decode = aac_decode_frame,
.sample_fmts = (const enum AVSampleFormat[]) {
AV_SAMPLE_FMT_FLTP, AV_SAMPLE_FMT_NONE
},
.capabilities = AV_CODEC_CAP_CHANNEL_CONF | AV_CODEC_CAP_DR1,
.caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,
.channel_layouts = aac_channel_layout,
.flush = flush,
.priv_class = &aac_decoder_class,
.profiles = NULL_IF_CONFIG_SMALL(ff_aac_profiles),
};
int av_codec_is_decoder(const AVCodec *codec)
{
return codec && (codec->decode || codec->receive_frame);
}