[ffmpeg] find 编码器

背景

整理 ffmpeg 中,如何通过名字或者 id 找到对应编码器的。

具体流程

搜索函数

c 复制代码
avcodec_find_encoder  // 通过 ID 搜索编码器
avcodec_find_encoder_by_name // 通过名字搜索编码器

源码分析

ffmpeg 中所有支持的编码器都会注册到 codec_list.c 文件中,保存在 codec_list 结构体中,既有编码器也有解码器,且该结构体最后一个是 NULL,这样方便 ffmpeg 内部的迭代算法使用。

c 复制代码
static const FFCodec *codec_list[] = {
    &ff_a64multi_encoder,
    &ff_a64multi5_encoder,
    &ff_alias_pix_encoder,
    &ff_amv_encoder,
	...
	&ff_av1_decoder,
	NULL
};

搜索编码器用到的函数主要有这些,主要推测是一次遍历 codec_list 结构体,拿到结构体首先通过 av_codec_is_encoder 函数判断是不是编码器;然后在判断 id 和传入相同。 (avcodec_find_encoder_by_name 类似,只是最后一步是判断 name是否相等)

av_codec_iterate 写的方式很像 c++ 中的迭代器,index 不断加1,然后通过 codec_list 结构体最后的 NULL 作为结尾的判断。

cpp 复制代码
// allcodecs.c 中
const AVCodec *avcodec_find_encoder(enum AVCodecID id)
{
    return find_codec(id, av_codec_is_encoder);
}
static const 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 p;
        }
    }

    return experimental;
}

const AVCodec *av_codec_iterate(void **opaque)
{
    uintptr_t i = (uintptr_t)*opaque;
    const FFCodec *c = codec_list[i];
	//av_codec_init_static 只运行一次,兼容代码,可先不管
    ff_thread_once(&av_codec_static_init, av_codec_init_static);

    if (c) {
        *opaque = (void*)(i + 1);
        return &c->p;
    }
    return NULL;
}

// 判断这个 avcodec 是不是编码器
int av_codec_is_encoder(const AVCodec *avcodec)
{
    const FFCodec *const codec = ffcodec(avcodec);
    return codec && (codec->cb_type == FF_CODEC_CB_TYPE_ENCODE     ||
                     codec->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB ||
                     codec->cb_type == FF_CODEC_CB_TYPE_RECEIVE_PACKET);
}

具体例子

该结构体在 aacenc.c 文件中

主要是 FF_CODEC_ENCODE_CB,表示这个 codec 是编码器。

其他:.p.xx 这些是设置 AVCodec 结构体

c 复制代码
const FFCodec ff_aac_encoder = {
    .p.name         = "aac",
    CODEC_LONG_NAME("AAC (Advanced Audio Coding)"),
    .p.type         = AVMEDIA_TYPE_AUDIO,
    .p.id           = AV_CODEC_ID_AAC,
    .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY |
                      AV_CODEC_CAP_SMALL_LAST_FRAME,
    .priv_data_size = sizeof(AACEncContext),
    .init           = aac_encode_init,
    FF_CODEC_ENCODE_CB(aac_encode_frame),
    .close          = aac_encode_end,
    .defaults       = aac_encode_defaults,
    .p.supported_samplerates = ff_mpeg4audio_sample_rates,
    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
    .p.sample_fmts  = (const enum AVSampleFormat[]){ AV_SAMPLE_FMT_FLTP,
                                                     AV_SAMPLE_FMT_NONE },
    .p.priv_class   = &aacenc_class,
};

#define CODEC_LONG_NAME(str) .p.long_name = str
#define FF_CODEC_ENCODE_CB(func)                          \
    .cb_type           = FF_CODEC_CB_TYPE_ENCODE,         \
    .cb.encode         = (func)

细节推敲

为啥 AVCodec 可以强转为 FFCodec?

c 复制代码
int av_codec_is_encoder(const AVCodec *avcodec)
{
    **const FFCodec *const codec = ffcodec(avcodec);**
    return codec && (codec->cb_type == FF_CODEC_CB_TYPE_ENCODE     ||
                     codec->cb_type == FF_CODEC_CB_TYPE_ENCODE_SUB ||
                     codec->cb_type == FF_CODEC_CB_TYPE_RECEIVE_PACKET);
}

看了一下 FFCodec 中的结构定义,AVCodec p 是定义在FFCodec 最前面的,所以如果当前使用的 AVCodec 是用FFCodec 创建的,直接强转就能找到对应的 FFCodec 对象。如果 AVCodec 是独立创建的,强转肯定是有问题的。感觉这块写的有点 hardcode,不按 ffmpeg 约定俗成的一些规则写会有比较难查的bug。

c 复制代码
typedef struct FFCodec {
    /**
     * The public AVCodec. See codec.h for it.
     */
    AVCodec p;

    /**
     * Internal codec capabilities FF_CODEC_CAP_*.
     */
    unsigned caps_internal:29;

    /**
     * This field determines the type of the codec (decoder/encoder)
     * and also the exact callback cb implemented by the codec.
     * cb_type uses enum FFCodecType values.
     */
    unsigned cb_type:3;
	
	// ...

    /**
     * List of supported codec_tags, terminated by FF_CODEC_TAGS_END.
     */
    const uint32_t *codec_tags;
} FFCodec;
相关推荐
扶尔魔ocy3 小时前
【QT window】ffmpeg实现录音功能之无损格式--PCM
ffmpeg·pcm
止礼4 小时前
FFmpeg8.0.1 源代码的深入分析
ffmpeg
小曾同学.com5 小时前
音视频中的“透传”与“DTS音频”
ffmpeg·音视频·透传·dts
vivo互联网技术5 小时前
数字人动画云端渲染方案
前端·ffmpeg·puppeteer·web3d
止礼6 小时前
FFmpeg8.0.1 编解码流程
ffmpeg
qs70167 小时前
c直接调用FFmpeg命令无法执行问题
c语言·开发语言·ffmpeg
止礼7 小时前
FFmpeg8.0.1 Mac环境 CMake本地调试配置
macos·ffmpeg
简鹿视频1 天前
视频转mp4格式具体作步骤
ffmpeg·php·音视频·实时音视频