AVCodec 结构体深度解析:编解码器的身份证

做 FFmpeg 开发,你天天跟 AVCodec 打交道。说白了,它就是编解码器的「身份证」。每个解码器、编码器在 FFmpeg 里都对应一个 AVCodec 实例。我刚开始看源码时,总觉得这结构体字段太多,后来踩过几次坑才明白——每个字段都有它的用处。

这一节,咱们就把 AVCodec 从头到脚拆一遍。重点看 name、long_name、type、id、capabilities 这些核心字段,顺便把编解码器 ID 枚举也捋清楚。

AVCodec 结构体概览

先看一眼它的定义(我简化了,去掉了一些不常用的字段):

typedef struct AVCodec {
    const char *name;           // 编解码器短名称
    const char *long_name;      // 编解码器长名称
    enum AVMediaType type;      // 媒体类型:音频/视频/字幕
    enum AVCodecID id;          // 编解码器 ID
    int capabilities;           // 能力标志位
    const AVRational *supported_framerates; // 支持的帧率
    const enum AVPixelFormat *pix_fmts;     // 支持的像素格式
    const int *supported_samplerates;       // 支持的采样率
    const enum AVSampleFormat *sample_fmts; // 支持的采样格式
    const uint64_t *channel_layouts;        // 支持的声道布局
    int priv_data_size;         // 私有数据大小
    // ... 还有一堆函数指针
} AVCodec;

嗯,这里要注意:name 和 long_name 虽然都是名字,但用途完全不同。我见过不少新手把 long_name 当 key 去查解码器,结果死活查不到。

name 与 long_name:短名是钥匙,长名是标签

字段 含义 示例 用途
name 短名称,全小写,无空格 "h264" avcodec_find_decoder_by_name() 的查找 key
long_name 长名称,可读性强 "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10" ffmpeg -codecs 命令显示用

name 是内部查找用的。avcodec_find_decoder_by_name("h264") 能直接定位到 H.264 解码器。而 long_name 纯粹给人看的,ffmpeg -codecs 命令输出里那一长串描述就是它。

我的习惯:写命令行工具时,用 name 做参数解析;写 GUI 或日志输出时,用 long_name 展示给用户。别搞反了。

type 与 id:编解码器的「性别」和「身份证号」

type 是 AVMediaType 枚举,告诉你是音频、视频还是字幕。常见的就三个:

  • AVMEDIA_TYPE_VIDEO — 视频
  • AVMEDIA_TYPE_AUDIO — 音频
  • AVMEDIA_TYPE_SUBTITLE — 字幕

id 是 AVCodecID 枚举,这才是真正的「身份证号」。每个编解码标准都有一个唯一的 ID。比如:

enum AVCodecID {
    AV_CODEC_ID_NONE,
    AV_CODEC_ID_MPEG1VIDEO,
    AV_CODEC_ID_MPEG2VIDEO,
    AV_CODEC_ID_H264,       // = 27
    AV_CODEC_ID_HEVC,       // = 173
    AV_CODEC_ID_AAC,        // = 86018
    AV_CODEC_ID_MP3,        // = 86017
    // ... 几百个
};

为什么要有 id?因为 name 可能重复或歧义。比如 "h264" 和 "libx264" 都是 H.264,但一个是解码器,一个是编码器。id 是唯一的,avcodec_find_decoder(AV_CODEC_ID_H264) 保证找到正确的解码器。

我曾经踩过的坑:有个项目里,我用 name 去匹配解码器,结果输入 "H264"(大写)就查不到。后来发现 avcodec_find_decoder_by_name 是大小写敏感的。从那以后,我统一用 id 做匹配,稳得很。

capabilities:能力标志位

capabilities 是一个 int 类型的位掩码。每个 bit 代表一种能力。常见的标志有:

宏定义 值 含义
AV_CODEC_CAP_DRAW_HORIZ_BAND 0x0001 支持逐行解码输出
AV_CODEC_CAP_DR1 0x0002 支持用户自定义帧缓冲区
AV_CODEC_CAP_DELAY 0x0020 解码器有内部延迟(B帧场景常见)
AV_CODEC_CAP_SMALL_LAST_FRAME 0x0040 最后一帧可以小于正常尺寸
AV_CODEC_CAP_HWACCEL 0x0800 支持硬件加速
AV_CODEC_CAP_VARIABLE_FRAME_SIZE 0x4000 输出帧大小可变(音频常见)

判断能力的方法很简单:

if (codec->capabilities & AV_CODEC_CAP_DELAY) {
    // 这个解码器有延迟,需要 flush 才能拿到所有帧
    avcodec_flush_buffers(ctx);
}

我个人习惯在初始化解码器后,先检查 capabilities。特别是 AV_CODEC_CAP_DELAY,如果你没处理这个标志,B 帧场景下会丢帧。

编解码器 ID 枚举的体系

AVCodecID 枚举定义在 libavcodec/codec_id.h 里。它按媒体类型分段:

  • 视频 ID:1 ~ 0xFFFF(约 200 个)
  • 音频 ID:0x10000 ~ 0x1FFFF(约 100 个)
  • 字幕 ID:0x20000 ~ 0x2FFFF(约 20 个)
  • 数据/其他:0x30000+

为什么这么分?方便快速判断类型。比如:

static inline int av_codec_is_video(enum AVCodecID id) {
    return id > AV_CODEC_ID_NONE && id < 0x10000;
}

嗯,这里要注意:AV_CODEC_ID_NONE 的值是 0,表示「未指定」。如果你从 demuxer 拿到的 id 是 0,说明这个流不认识,别硬解码。

SVG:AVCodec 核心字段关系图

下面这张图展示了 AVCodec 核心字段之间的关联,以及它们如何定位一个具体的编解码器:

AVCodec 核心字段关系图 AVCodec 实例 name long_name type id 查找 key avcodec_find_*_by_name 显示用 ffmpeg -codecs 输出 媒体类型判断 视频/音频/字幕 唯一标识 avcodec_find_decoder(id) capabilities(位掩码) AV_CODEC_CAP_DELAY AV_CODEC_CAP_DR1 AV_CODEC_CAP_HWACCEL AV_CODEC_CAP_...

实际项目中的使用模式

说了这么多理论,来点实际的。我在做播放器 SDK 时,初始化解码器的流程大概是这样的:

// 1. 从 AVStream 拿到 codecpar
AVCodecParameters *par = stream->codecpar;

// 2. 根据 id 找解码器
AVCodec *codec = avcodec_find_decoder(par->codec_id);
if (!codec) {
    // 处理找不到解码器的情况
    return AVERROR_DECODER_NOT_FOUND;
}

// 3. 检查能力
if (codec->capabilities & AV_CODEC_CAP_HWACCEL) {
    // 尝试硬件加速
    init_hwaccel(codec);
}

// 4. 创建上下文
AVCodecContext *ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(ctx, par);

// 5. 打开解码器
avcodec_open2(ctx, codec, NULL);

你看,整个流程里 id 和 capabilities 是核心。name 和 long_name 反而用得少。但调试时,long_name 很有用:

av_log(NULL, AV_LOG_INFO, "Using decoder: %s (%s)\n",
       codec->name, codec->long_name);
关键点总结:
  • name 是内部查找 key,大小写敏感
  • long_name 给人看,别用它做逻辑判断
  • type 区分音频/视频/字幕
  • id 是唯一标识,推荐用它做匹配
  • capabilities 是位掩码,用 & 操作符检查

嗯,这一节的内容差不多了。AVCodec 结构体是 FFmpeg 编解码的基石,搞懂它,后面看 avcodec_open2 的源码会轻松很多。你想想看,一个结构体里十几个字段,真正核心的就这几个——name、id、type、capabilities。其他的比如 supported_framerates、pix_fmts 这些,等讲到编解码器参数协商时再细聊。