目录
- 引言
- 整体架构概览
- 核心数据结构
- 文件依赖关系
- 函数调用关系图
- 解码流程详解
- 块级解码与重建
- [Tile 解码与多线程](#Tile 解码与多线程)
- 帧头解析
- 运动补偿与预测
- 环内滤波与后处理
- 数学公式推导
- 总结
1. 引言
decodeframe.c 是 libaom AV1 解码器的核心文件,负责将 AV1 压缩比特流解码重建为完整的视频帧。它是连接高层 OBU 解析和底层像素重建的关键桥梁。
1.1 在解码流水线中的位置
比特流输入
│
▼
┌──────────────────┐
│ obu.c │ ← OBU (Open Bitstream Unit) 解析
│ av1_read_obu_* │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ decodeframe.c │ ★ 本文件
│ 帧头解析 + Tile解码│ ← 帧级别核心逻辑
└────────┬─────────┘
│
▼
┌──────────────────┐
│ decodemv.c │ ← 运动向量解码
│ decodetxb.c │ ← 变换系数解码
│ reconinter.c │ ← 帧间重建
│ reconintra.c │ ← 帧内重建
└──────────────────┘
1.2 核心职责
| 职责 | 描述 |
|---|---|
| 帧头解析 | 读取 uncompressed_header,解析帧类型、量化参数、参考帧等 |
| Tile 管理 | 将帧划分为 Tile,管理 Tile 数据的读取和分发 |
| 块解码调度 | 递归划分块(partition),驱动每个块的解码重建流程 |
| 多线程协调 | 支持 Tile 级并行、行级并行(Row-MT)等多种并行策略 |
| 后处理编排 | 调度 CDEF、超分辨率、环路恢复等环内/环外滤波器 |
2. 整体架构概览
2.1 顶层入口函数
该文件对外暴露两个核心入口函数:
c
// 入口 1: 解析帧头 + 建好上下文
uint32_t av1_decode_frame_headers_and_setup(
AV1Decoder *pbi,
struct aom_read_bit_buffer *rb,
int trailing_bits_present);
// 入口 2: 解码 Tile 数据 + 后处理
void av1_decode_tg_tiles_and_wrapup(
AV1Decoder *pbi,
const uint8_t *data,
const uint8_t *data_end,
const uint8_t **p_data_end,
int start_tile,
int end_tile,
int initialize_flag);
2.2 流水线总览图
┌─────────────────────────────────────────────────────────────────┐
│ av1_decode_frame_headers_and_setup() │
│ │
│ ┌──────────────────────┐ ┌────────────────────────────┐ │
│ │ read_uncompressed_ │────▶│ setup_frame_size │ │
│ │ header() │ │ setup_quantization │ │
│ │ │ │ setup_segmentation │ │
│ │ 帧类型/参考帧/QP/ │ │ setup_loopfilter │ │
│ │ Tile信息/CDEF/ │ │ setup_cdef │ │
│ │ 全局运动/胶片颗粒 │ │ decode_restoration_mode │ │
│ └──────────────────────┘ │ read_global_motion │ │
│ │ read_film_grain │ │
│ └────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ av1_decode_tg_tiles_and_wrapup() │
│ │
│ ┌──────────────────────┐ ┌────────────────────────────┐ │
│ │ 选择解码策略 │ │ 后处理管道 │ │
│ │ │ │ │ │
│ │ decode_tiles() │ │ 1. Loop Filter (去块效应) │ │
│ │ decode_tiles_mt() │ │ 2. CDEF (约束方向增强) │ │
│ │ decode_tiles_row_mt()│────▶│ 3. SuperRes (超分辨率) │ │
│ │ │ │ 4. Loop Restoration │ │
│ └──────────────────────┘ └────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
3. 核心数据结构
3.1 AV1Decoder --- 解码器主上下文
c
// 位于 av1/decoder/decoder.h
typedef struct AV1Decoder {
AV1_COMMON common; // 编解码器共享数据
ThreadData td; // 主线程的解码上下文
TileDataDec *tile_data; // 每个 Tile 的数据
TileBufferDec tile_buffers[MAX_TILE_ROWS][MAX_TILE_COLS]; // Tile 比特流缓冲
DecoderCodingBlock dcb; // 解码编码块上下文
int max_threads; // 最大工作线程数
int num_workers; // 当前分配的工作线程数
AVxWorker *tile_workers; // Tile 工作线程数组
DecWorkerData *thread_data; // 线程数据数组
AV1DecTileMT tile_mt_info; // Tile 多线程队列信息
AV1DecRowMTInfo frame_row_mt_info; // 行级多线程信息
CB_BUFFER *cb_buffer_base; // 编码块缓冲基址
// ... 其他字段
} AV1Decoder;
3.2 ThreadData --- 线程解码上下文
c
typedef struct ThreadData {
DecoderCodingBlock dcb; // 解码块上下文
aom_reader *bit_reader; // 熵解码读取器
CB_BUFFER cb_buffer_base; // 当前线程的块缓冲
// 函数指针表 (策略模式)
block_visitor_fn_t read_coeffs_tx_intra_block_visit;
block_visitor_fn_t predict_and_recon_intra_block_visit;
block_visitor_fn_t read_coeffs_tx_inter_block_visit;
block_visitor_fn_t inverse_tx_inter_block_visit;
block_visitor_fn_t predict_inter_block_visit;
block_visitor_fn_t cfl_store_inter_block_visit;
uint8_t *mc_buf[2]; // 运动补偿临时缓冲
// ...
} ThreadData;
3.3 DecoderCodingBlock --- 解码块上下文
c
typedef struct DecoderCodingBlock {
MACROBLOCKD xd; // 宏块描述符 (含指向解码器状态的指针)
tran_low_t *dqcoeff_block[MAX_MB_PLANE]; // 反量化系数
eob_info *eob_data[MAX_MB_PLANE]; // EOB (非零系数末尾) 信息
int cb_offset[MAX_MB_PLANE]; // 块偏移
int txb_offset[MAX_MB_PLANE]; // 变换块偏移
int corrupted; // 损坏标记
uint8_t *mc_buf[2]; // 运动补偿缓冲
} DecoderCodingBlock;
3.4 关键设计模式:函数指针策略
ThreadData 中定义了 6 个函数指针,实现了解析/解码的解耦:
c
// 源码 2694-2713 行
static AOM_INLINE void set_decode_func_pointers(ThreadData *td,
int parse_decode_flag) {
// 初始化为空操作
td->read_coeffs_tx_intra_block_visit = decode_block_void;
// ...
// 根据标志位设置实际函数
if (parse_decode_flag & 0x1) { // 解析 (parse)
td->read_coeffs_tx_intra_block_visit = read_coeffs_tx_intra_block;
td->read_coeffs_tx_inter_block_visit = av1_read_coeffs_txb_facade;
}
if (parse_decode_flag & 0x2) { // 解码 (decode)
td->predict_and_recon_intra_block_visit =
predict_and_reconstruct_intra_block;
td->inverse_tx_inter_block_visit = inverse_transform_inter_block;
td->predict_inter_block_visit = predict_inter_block;
td->cfl_store_inter_block_visit = cfl_store_inter_block;
}
}
| flag 值 | 含义 | 使用场景 |
|---|---|---|
0x1 |
仅解析 | Row-MT 解析线程 |
0x2 |
仅解码 | Row-MT 解码线程 |
0x3 |
解析+解码 | 单线程 / Tile-MT |
4. 文件依赖关系
decodeframe.c 依赖图:
┌─────────────────────────────────────────────────────────────────┐
│ decodeframe.c │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌── 自身头文件 ──────────────────────────────────────────┐ │
│ │ decodeframe.h (函数声明) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌── 解码器内部依赖 ──────────────────────────────────────┐ │
│ │ decoder.h (AV1Decoder, ThreadData 定义) │ │
│ │ decodemv.h/.c (运动向量解码: av1_read_mode_info) │ │
│ │ decodetxb.h/.c (变换系数解码: av1_read_coeffs_txb_*) │ │
│ │ detokenize.h/.c (反量化/反变换) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌── 通用模块依赖 ────────────────────────────────────────┐ │
│ │ av1_common_int.h (AV1_COMMON 定义) │ │
│ │ blockd.h (MACROBLOCKD 定义) │ │
│ │ reconinter.h (帧间重建: build_inter_predictors) │ │
│ │ reconintra.h (帧内重建: av1_predict_intra_block) │ │
│ │ restoration.h (环路恢复) │ │
│ │ cdef.h (CDEF滤波器) │ │
│ │ warp_motion.h (扭曲运动补偿) │ │
│ │ tile_common.h (Tile 工具函数) │ │
│ │ thread_common.h (多线程基础) │ │
│ │ entropy.h (熵编码上下文: FRAME_CONTEXT) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌── 外部库依赖 ──────────────────────────────────────────┐ │
│ │ aom_dsp/bitreader.h (比特流读取器) │ │
│ │ aom_util/aom_thread.h (线程抽象层) │ │
│ │ aom_scale/yv12config.h (YUV 帧缓冲) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌── 模板包含 ────────────────────────────────────────────┐ │
│ │ reconinter_template.inc (通过 #include 直接嵌入) │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
4.1 关键文件说明
| 文件 | 提供的接口 | decodeframe.c 调用位置 |
|---|---|---|
decoder.h |
AV1Decoder 结构体定义 |
全局使用 |
decodemv.c |
av1_read_mode_info() |
decode_mbmi_block() |
decodetxb.c |
av1_read_coeffs_txb_facade() |
decode_reconstruct_tx() |
reconinter.c |
build_inter_predictors() |
dec_build_inter_predictors() |
reconintra.c |
av1_predict_intra_block_facade() |
predict_and_reconstruct_intra_block() |
obu.c |
OBU 层面的帧调度 | 调用方 (非被调用) |
5. 函数调用关系图
av1_decode_frame_headers_and_setup()
│
├── read_uncompressed_header() ← ★ 最核心的帧头解析函数
│ ├── setup_sb_size()
│ ├── setup_frame_size()
│ │ ├── setup_superres()
│ │ ├── resize_context_buffers()
│ │ ├── setup_render_size()
│ │ └── setup_buffer_pool()
│ ├── read_tile_info()
│ │ └── read_tile_info_max_tile()
│ ├── setup_quantization()
│ ├── setup_segmentation()
│ ├── setup_segmentation_dequant()
│ ├── setup_loopfilter()
│ ├── setup_cdef()
│ ├── decode_restoration_mode()
│ ├── read_tx_mode()
│ ├── read_frame_reference_mode()
│ ├── read_global_motion()
│ └── read_film_grain()
│
├── av1_check_trailing_bits()
├── av1_calculate_ref_frame_side()
└── av1_setup_block_planes()
av1_decode_tg_tiles_and_wrapup()
│
├── setup_frame_info()
├── decode_tiles() / decode_tiles_mt() / decode_tiles_row_mt()
│ │
│ ├── get_tile_buffers() ← 从比特流提取Tile数据
│ ├── decoder_alloc_tile_data() ← 分配Tile解码上下文
│ ├── set_decode_func_pointers() ← 设置函数指针表
│ │
│ └── decode_tile() ← 单Tile解码
│ │
│ └── decode_partition() ← ★ 递归块划分
│ │
│ ├── [parse_decode_flag & 1]
│ │ └── loop_restoration_read_sb_coeffs()
│ │
│ ├── read_partition() ← 读取划分类型
│ │
│ ├── [递归: DEC_PARTITION]
│ │ └── decode_partition() ← 递归子块
│ │
│ └── [叶子: DEC_BLOCK]
│ ├── parse_decode_block() (mode 0x3/0x1)
│ │ ├── decode_mbmi_block()
│ │ │ └── av1_read_mode_info()
│ │ ├── read_tx_size_vartx() / read_tx_size()
│ │ └── decode_token_recon_block() ← ★ 块重建核心
│ │
│ └── decode_block() (mode 0x2)
│ └── decode_token_recon_block()
│
├── [后处理管道]
│ ├── set_planes_to_neutral_grey()
│ ├── av1_loop_filter_frame_mt() ← 去块效应滤波
│ ├── av1_cdef_frame_mt() ← CDEF
│ ├── superres_post_decode() ← 超分辨率
│ └── av1_loop_restoration_filter_frame_mt() ← 环路恢复
│
└── [帧上下文更新]
└── cm->cur_frame->frame_context = *cm->fc
6. 解码流程详解
6.1 两步解码架构
AV1 解码器采用头解析与数据解码分离的两步架构:
步骤 1: av1_decode_frame_headers_and_setup()
├── 解析 uncompressed_header (帧类型、尺寸、QP等)
├── 建立参考帧关系
├── 初始化熵编码上下文
└── 返回: 帧头字节数 (用于定位Tile数据)
步骤 2: av1_decode_tg_tiles_and_wrapup()
├── 提取 Tile 数据
├── Tile级解码 (单线程/多线程/Row-MT)
└── 后处理管道 (LoopFilter → CDEF → SuperRes → Restoration)
6.2 帧类型处理
c
// 源码 4591 行: 帧类型读取
current_frame->frame_type = (FRAME_TYPE)aom_rb_read_literal(rb, 2);
| 帧类型 | 值 | 特征 |
|---|---|---|
KEY_FRAME |
0 | 关键帧,重置解码器状态 |
INTER_FRAME |
1 | 帧间预测帧 |
INTRA_ONLY_FRAME |
2 | 仅帧内预测 |
S_FRAME |
3 | 切换帧 |
6.3 Tile 划分策略
c
// 源码 2093 行: Uniform Tile 间距
if (tiles->uniform_spacing) {
tiles->log2_cols = tiles->min_log2_cols;
while (tiles->log2_cols < tiles->max_log2_cols) {
if (!aom_rb_read_bit(rb)) break;
tiles->log2_cols++;
}
}
Tile 数量为 2 log2_cols × 2 log2_rows 2^{\text{log2\_cols}} \times 2^{\text{log2\_rows}} 2log2_cols×2log2_rows,每个 Tile 可独立解码。
7. 块级解码与重建
7.1 decode_token_recon_block() --- 块重建核心
这是块级解码的心脏 ,通过 parse_decode_flag 控制解析/解码的拆分:
c
// 源码 904-1014 行
static AOM_INLINE void decode_token_recon_block(
AV1Decoder *const pbi, ThreadData *const td,
aom_reader *r, BLOCK_SIZE bsize)
{
// ... 变量声明 ...
if (!is_inter_block(mbmi)) {
// ===== 帧内块处理 =====
for (row = 0; row < max_blocks_high; row += mu_blocks_high) {
for (col = 0; col < max_blocks_wide; col += mu_blocks_wide) {
for (plane = 0; plane < num_planes; ++plane) {
const TX_SIZE tx_size = av1_get_tx_size(plane, xd);
for (blk_row ...) {
for (blk_col ...) {
// 步骤1: 读取变换系数 (parse)
td->read_coeffs_tx_intra_block_visit(
cm, dcb, r, plane, blk_row, blk_col, tx_size);
// 步骤2: 预测 + 重建 (decode)
td->predict_and_recon_intra_block_visit(
cm, dcb, r, plane, blk_row, blk_col, tx_size);
set_cb_buffer_offsets(dcb, tx_size, plane);
}
}
}
}
}
} else {
// ===== 帧间块处理 =====
// 步骤1: 帧间预测
td->predict_inter_block_visit(cm, dcb, bsize);
if (!mbmi->skip_txfm) {
int eobtotal = 0;
for (row ...) {
for (col ...) {
for (plane ...) {
for (blk_row ...) {
for (blk_col ...) {
// 步骤2: 解码变换系数 + 逆变换 + 重建
decode_reconstruct_tx(
cm, td, r, mbmi, plane, plane_bsize,
blk_row, blk_col, block, max_tx_size,
&eobtotal);
block += step;
}
}
}
}
}
}
td->cfl_store_inter_block_visit(cm, xd);
}
av1_visit_palette(pbi, xd, r, set_color_index_map_offset);
}
7.2 帧内块重建流程
predict_and_reconstruct_intra_block()
│
├── av1_predict_intra_block_facade() ← 生成帧内预测像素
│ ├── DC_PRED: pred[x][y] = avg(left + above)
│ ├── V_PRED: pred[x][y] = above[x]
│ ├── H_PRED: pred[x][y] = left[y]
│ ├── SMOOTH_PRED: 平滑预测
│ ├── PAETH_PRED: Paeth预测器
│ └── directional: 角度预测 (56种方向)
│
├── [if !mbmi->skip_txfm and eob > 0]
│ └── inverse_transform_block()
│ ├── 反量化: coeff[i] = dqcoeff[i] * dequant[i]
│ └── 逆变换: residual = IDCT/ADST/FlipADST/IDTX
│
├── [if plane == Y and CfL required]
│ └── cfl_store_tx() ← 存储亮度用于色度CfL预测
│
└── 重建: recon[x][y] = pred[x][y] + residual[x][y]
7.3 帧间块重建流程
predict_inter_block()
│
├── [遍历参考帧 ref = 0, 1]
│ ├── av1_setup_pre_planes() ← 设置参考帧平面
│ └── 获取 scale_factors
│
└── dec_build_inter_predictor()
└── dec_build_inter_predictors()
└── build_inter_predictors() ← 运动补偿生成预测像素
├── dec_calc_subpel_params_and_extend() ← 亚像素插值参数
│ ├── dec_calc_subpel_params() ← 计算亚像素位置
│ └── extend_mc_border() ← 扩展运动补偿边界
└── av1_build_inter_predictor() ← 实际插值滤波
├── 8-tap 常规插值
├── 双线性插值
└── 扭曲运动补偿 (Warped Motion)
decode_reconstruct_tx()
│
├── [read] td->read_coeffs_tx_inter_block_visit() ← 读取变换系数
├── [inv] td->inverse_tx_inter_block_visit() ← 逆变换+重建
│ └── inverse_transform_block()
│ ├── av1_inverse_transform_block() ← 核心逆变换
│ └── memset(dqcoeff, 0, ...) ← 清零系数缓冲
└── set_cb_buffer_offsets()
7.4 块划分的递归结构
decode_partition() 通过宏 DEC_PARTITION 和 DEC_BLOCK 实现递归划分:
c
// 源码 1339-1404 行
#define DEC_BLOCK(db_r, db_c, db_subsize)
block_visit[parse_decode_flag](pbi, td, db_r, db_c, reader, ..., db_subsize)
#define DEC_PARTITION(db_r, db_c, db_subsize)
decode_partition(pbi, td, db_r, db_c, reader, db_subsize, parse_decode_flag)
switch (partition) {
case PARTITION_NONE: DEC_BLOCK(mi_row, mi_col, subsize); break;
case PARTITION_HORZ: // 水平分割为2块
case PARTITION_VERT: // 垂直分割为2块
case PARTITION_SPLIT: // 四叉树分割为4块 (递归 DEC_PARTITION)
case PARTITION_HORZ_A: // 上1/4 + 上1/4 + 下1/2
case PARTITION_HORZ_B: // 上1/2 + 下1/4 + 下1/4
case PARTITION_VERT_A: // 左1/4 + 左1/4 + 右1/2
case PARTITION_VERT_B: // 左1/2 + 右1/4 + 右1/4
case PARTITION_HORZ_4: // 水平4等分
case PARTITION_VERT_4: // 垂直4等分
}
AV1 支持 10 种块划分方式:
PARTITION_NONE PARTITION_HORZ PARTITION_VERT
┌──────────┐ ┌──────────┐ ┌─────┬────┐
│ │ │ │ │ │ │
│ N×N │ │ N×(N/2) │ │ │ │
│ │ │ │ │ │ │
│ │ ├──────────┤ │ │ │
│ │ │ N×(N/2) │ │ │ │
└──────────┘ └──────────┘ └─────┴────┘
PARTITION_SPLIT PARTITION_HORZ_A/B PARTITION_VERT_A/B
┌─────┬────┐ ┌─────┬────┐ ┌───┬───────┐
│ │ │ │ │ │ │ │ │
│ SPL │SPL │ │ 1/4 │ 1/4│ │ │ │
│ │ │ ├─────┴────┤ ├───┤ │
├─────┼────┤ │ │ │ │ │
│ │ │ │ 1/2 │ │ │ │
│ SPL │SPL │ │ │ │ │ │
└─────┴────┘ └──────────┘ └───┴───────┘
8. Tile 解码与多线程
8.1 三种并行策略
c
// 源码 5270-5292 行: 策略选择
void av1_decode_tg_tiles_and_wrapup(...) {
if (pbi->max_threads > 1 && pbi->row_mt)
*p_data_end = decode_tiles_row_mt(...); // 策略A: Row-MT
else if (pbi->max_threads > 1 && tile_count_tg > 1)
*p_data_end = decode_tiles_mt(...); // 策略B: Tile-MT
else
*p_data_end = decode_tiles(...); // 策略C: 单线程
}
| 策略 | 并行粒度 | 线程数 | 适用场景 |
|---|---|---|---|
| 单线程 | 无 | 1 | 单Tile小帧 / 调试 |
| Tile-MT | Tile 级 | ≤ Tile数 | 多Tile的大帧 |
| Row-MT | SB行 级 | ≤ Tile内SB行数×2 | 高分辨率视频 |
8.2 Tile-MT 工作流程
decode_tiles_mt()
│
├── decode_mt_init() ← 创建线程池
├── get_tile_buffers() ← 提取Tile数据
├── tile_mt_queue() ← 按Tile大小排序任务
├── reset_dec_workers() ← 设置 tile_worker_hook
├── launch_dec_workers() ← 启动工作线程
│ │
│ └── tile_worker_hook() ← 工作线程入口
│ └── [循环]
│ ├── get_dec_job_info() ← 获取Tile任务
│ ├── tile_worker_hook_init()
│ └── decode_tile() ← 解码单个Tile
│
└── sync_dec_workers() ← 同步所有线程
8.3 Row-MT: 解析-解码流水线
Row-MT 是更精细的并行策略,将解析与解码分离为两阶段:
时间线: ──────────────────────────────────────────────────────▶
线程1 (解析): SBRow0 SBRow1 SBRow2 SBRow3 SBRow4
线程2 (解码): [等待] SBRow0 SBRow1 SBRow2 SBRow3
线程3 (解码): [等待] [等待] [等待] SBRow0 SBRow1
关键同步:
- sync_read(): 解码线程等待上方SB行+nsync距离
- sync_write(): 解析/解码线程通知下方SB行可以继续
c
// 源码 2564-2614 行: 行间同步
static INLINE void sync_read(AV1DecRowMTSync *dec_row_mt_sync, int r, int c) {
const int nsync = dec_row_mt_sync->sync_range;
if (r && !(c & (nsync - 1))) {
pthread_mutex_lock(&dec_row_mt_sync->mutex_[r - 1]);
while (c > dec_row_mt_sync->cur_sb_col[r - 1] - nsync -
dec_row_mt_sync->intrabc_extra_top_right_sb_delay) {
pthread_cond_wait(&dec_row_mt_sync->cond_[r - 1], mutex);
}
pthread_mutex_unlock(&dec_row_mt_sync->mutex_[r - 1]);
}
}
9. 帧头解析
9.1 read_uncompressed_header() 解析的语法元素
该函数(4487-5135行)按 AV1 规范解析帧头中的所有语法元素:
c
// 帧头主要语法元素解析序列
// 1. 帧类型和显示控制
cm->show_existing_frame // 直接显示已有帧?
current_frame->frame_type // KEY/INTER/INTRA_ONLY/S_FRAME
cm->show_frame // 本帧是否显示?
cm->showable_frame // 是否可被show_existing_frame引用?
// 2. 错误恢复与工具开关
features->error_resilient_mode
features->disable_cdf_update
features->allow_screen_content_tools
features->cur_frame_force_integer_mv
// 3. Frame ID (可选)
cm->current_frame_id
current_frame->order_hint
// 4. 参考帧管理
features->primary_ref_frame
current_frame->refresh_frame_flags
cm->remapped_ref_idx[0..6]
// 5. 帧尺寸
setup_frame_size() / setup_frame_size_with_refs()
// 6. 编码工具标志
features->allow_intrabc // 帧内块拷贝
features->allow_ref_frame_mvs // 参考帧运动向量
features->allow_warped_motion // 扭曲运动
features->allow_high_precision_mv // 高精度MV
features->interp_filter // 插值滤波器类型
features->switchable_motion_mode // 可切换运动模式
// 7. 全局运动
read_global_motion() // 7个参考帧的全局运动参数
// 8. 胶片颗粒
read_film_grain() // 胶片颗粒合成参数
9.2 量化参数设置
c
// 源码 1785-1821 行
static AOM_INLINE void setup_quantization(
CommonQuantParams *quant_params, int num_planes,
bool separate_uv_delta_q, struct aom_read_bit_buffer *rb)
{
quant_params->base_qindex = aom_rb_read_literal(rb, QINDEX_BITS); // 基础QP
quant_params->y_dc_delta_q = read_delta_q(rb); // Y DC偏移
if (num_planes > 1) {
// UV 分量可分别设置delta QP
quant_params->u_dc_delta_q = read_delta_q(rb);
quant_params->u_ac_delta_q = read_delta_q(rb);
// ...
}
quant_params->using_qmatrix = aom_rb_read_bit(rb); // 量化矩阵开关
}
10. 运动补偿与预测
10.1 亚像素运动补偿
c
// 源码 559-643 行: dec_calc_subpel_params()
// 计算运动补偿的亚像素参数
static AOM_INLINE void dec_calc_subpel_params(
const MV *const src_mv, InterPredParams *const inter_pred_params,
MACROBLOCKD *const xd, ...)
{
if (is_scaled) {
// 缩放情况: 使用 scale_factors 将MV映射到参考帧空间
int pos_y = av1_scaled_y(orig_pos_y, sf);
int pos_x = av1_scaled_x(orig_pos_x, sf);
subpel_params->subpel_x = pos_x & SCALE_SUBPEL_MASK;
subpel_params->subpel_y = pos_y & SCALE_SUBPEL_MASK;
} else {
// 非缩放: 直接使用1/8像素精度的MV
subpel_params->subpel_x = (mv_q4.col & SUBPEL_MASK) << SCALE_EXTRA_BITS;
subpel_params->subpel_y = (mv_q4.row & SUBPEL_MASK) << SCALE_EXTRA_BITS;
}
}
10.2 运动补偿边界扩展
当参考块超出参考帧边界时,需要进行边界像素扩展:
c
// 源码 449-483 行: build_mc_border()
static AOM_INLINE void build_mc_border(
const uint8_t *src, int src_stride, uint8_t *dst, int dst_stride,
int x, int y, int b_w, int b_h, int w, int h)
{
const uint8_t *ref_row = src - x - y * src_stride;
if (y >= h) ref_row += (h - 1) * src_stride; // 超出下边界 → 取最后一行
do {
int left = x < 0 ? -x : 0; // 左边界 → 填充像素
int right = x + b_w > w ? x + b_w - w : 0; // 右边界 → 填充像素
int copy = b_w - left - right; // 有效像素
if (left) memset(dst, ref_row[0], left); // 左侧填充
if (copy) memcpy(dst + left, ref_row + x + left, copy); // 复制有效像素
if (right) memset(dst + left + copy, ref_row[w - 1], right); // 右侧填充
dst += dst_stride;
++y;
if (y > 0 && y < h) ref_row += src_stride;
} while (--b_h);
}
10.3 全局运动补偿
AV1 支持帧级别的全局运动模型,用于处理摄像机平移、旋转、缩放等:
c
// 源码 4301-4369 行: 全局运动类型
TransformationType type:
IDENTITY → 无变换
TRANSLATION → 仅平移: 2参数
ROTZOOM → 旋转+缩放: 4参数
AFFINE → 仿射变换: 6参数
仿射变换矩阵:
[x'] [h11 h12 h13] [x]
[y'] = [h21 h22 h23] [y]
[1]
11. 环内滤波与后处理
11.1 后处理流水线
已解码帧 (YUV)
│
▼
┌─────────────────┐
│ Loop Filter │ 去块效应滤波器 (Deblocking Filter)
│ 滤波强度: 0-63 │ 平滑块边界的不连续性
└────────┬────────┘
│
▼
┌─────────────────┐
│ CDEF │ 约束方向增强滤波器
│ 滤波强度: 0-15 │ 沿边缘方向滤波,保留细节
│ 方向搜索: 8方向 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ SuperRes │ 超分辨率上采样
│ 缩放分母: 8-16 │ width' = width * 8 / denominator
└────────┬────────┘
│
▼
┌─────────────────┐
│ Loop Restore │ 环路恢复滤波器
│ - Wiener Filter │ 维纳滤波 (7-tap可分离)
│ - SGRPROJ │ 自引导投影滤波
└────────┬────────┘
│
▼
最终输出帧
11.2 CDEF 设置
c
// 源码 1765-1778 行
static AOM_INLINE void setup_cdef(AV1_COMMON *cm,
struct aom_read_bit_buffer *rb) {
cdef_info->cdef_damping = aom_rb_read_literal(rb, 2) + 3;
cdef_info->cdef_bits = aom_rb_read_literal(rb, 2);
cdef_info->nb_cdef_strengths = 1 << cdef_info->cdef_bits;
for (int i = 0; i < cdef_info->nb_cdef_strengths; i++) {
cdef_info->cdef_strengths[i] = aom_rb_read_literal(rb, CDEF_STRENGTH_BITS);
cdef_info->cdef_uv_strengths[i] = aom_rb_read_literal(rb, CDEF_STRENGTH_BITS);
}
}
11.3 环路恢复滤波器
c
// 源码 1514-1568 行
static AOM_INLINE void decode_restoration_mode(
AV1_COMMON *cm, struct aom_read_bit_buffer *rb) {
for (int p = 0; p < num_planes; ++p) {
if (aom_rb_read_bit(rb)) {
rsi->frame_restoration_type =
aom_rb_read_bit(rb) ? RESTORE_SGRPROJ : RESTORE_WIENER;
} else {
rsi->frame_restoration_type =
aom_rb_read_bit(rb) ? RESTORE_SWITCHABLE : RESTORE_NONE;
}
}
}
| 恢复类型 | 描述 |
|---|---|
RESTORE_NONE |
不恢复 |
RESTORE_WIENER |
维纳滤波: 7×7可分离FIR滤波器 |
RESTORE_SGRPROJ |
自引导投影: 基于引导图的边缘保持滤波 |
RESTORE_SWITCHABLE |
每个恢复单元可自适应选择 |
12. 数学公式推导
12.1 亚像素插值
AV1 使用 1/8 像素精度的运动补偿。对于 1/8 精度的运动向量,插值过程为:
水平插值 (8-tap DCT-IF 滤波器):
P ( x + α , y ) = ∑ i = − 3 4 h i ( α ) ⋅ R ( x + i , y ) P(x + \alpha, y) = \sum_{i=-3}^{4} h_i(\alpha) \cdot R(x + i, y) P(x+α,y)=i=−3∑4hi(α)⋅R(x+i,y)
其中 h i ( α ) h_i(\alpha) hi(α) 是基于 DCT 的插值滤波器系数, α ∈ { 0 , 1 8 , 2 8 , . . . , 7 8 } \alpha \in \{0, \frac{1}{8}, \frac{2}{8}, ..., \frac{7}{8}\} α∈{0,81,82,...,87}。
垂直插值同理,然后对两个方向的结果取平均。
12.2 逆变换与重建
解码器端的重建过程可以表示为:
反量化:
C ^ u v = DQCoeff u v × DeQuant Q P u v \hat{C}uv = \text{DQCoeff}uv \times \text{DeQuant}QPuv C^uv=DQCoeffuv×DeQuantQPuv
其中 DeQuant \text{DeQuant} DeQuant 是依赖于 QP 的反量化矩阵。
逆变换 (以 DCT 为例):
R i j = ∑ u = 0 N − 1 ∑ v = 0 N − 1 T u i ⋅ T v j ⋅ C ^ u v Rij = \sum_{u=0}^{N-1}\sum_{v=0}^{N-1} T_ui \cdot T_vj \cdot \hat{C}uv Rij=u=0∑N−1v=0∑N−1Tui⋅Tvj⋅C^uv
其中 T k n = cos ( π k ( 2 n + 1 ) 2 N ) T_kn = \cos\left(\frac{\pi k (2n+1)}{2N}\right) Tkn=cos(2Nπk(2n+1)) 是 DCT 基函数。
重建:
Recon i j = Clip ( Pred i j + R i j , 0 , 2 bit_depth − 1 ) \text{Recon}ij = \text{Clip}\left(\text{Pred}ij + Rij, 0, 2^{\text{bit\_depth}} - 1\right) Reconij=Clip(Predij+Rij,0,2bit_depth−1)
12.3 维纳滤波 (Wiener Filter)
环路恢复中的维纳滤波器是一个可分离的 7-tap FIR 滤波器:
x ^ p = ∑ k = − 3 3 h v k ⋅ ∑ l = − 3 3 h h l ⋅ y p + l ⋅ stride h + k ⋅ stride v \hat{x}p = \sum_{k=-3}^{3} h_vk \cdot \sum_{l=-3}^{3} h_hl \cdot yp + l \\cdot \\text{stride}_h + k \\cdot \\text{stride}_v x^p=k=−3∑3hvk⋅l=−3∑3hhl⋅yp+l⋅strideh+k⋅stridev
其中 h h h_h hh 和 h v h_v hv 是水平和垂直滤波器系数,满足对称性:
h 0 = h 6 , h 1 = h 5 , h 2 = h 4 , h 3 = − 2 ∑ i = 0 2 h i h0 = h6, \quad h1 = h5, \quad h2 = h4, \quad h3 = -2\sum_{i=0}^{2} hi h0=h6,h1=h5,h2=h4,h3=−2i=0∑2hi
这确保了滤波器具有单位 DC 增益。
12.4 量化与 QP 映射
AV1 的基础 QP 范围是 0-255,QP 到量化步长的映射:
Q step = 2 Q P − 4 6 ≈ 0.625 × 2 Q P / 6 Q_{\text{step}} = 2^{\frac{QP - 4}{6}} \approx 0.625 \times 2^{QP/6} Qstep=26QP−4≈0.625×2QP/6
DC 系数的反量化:
DeQuant DC = dc_qlookup clamp ( Q P + Δ Q P DC , 0 , 255 ) \text{DeQuant}_{\text{DC}} = \text{dc\_qlookup}\\text{clamp}(QP + \\Delta QP_{\\text{DC}}, 0, 255) DeQuantDC=dc_qlookupclamp(QP+ΔQPDC,0,255)
13. 总结
13.1 设计亮点
| 特性 | 实现方式 |
|---|---|
| 解析/解码分离 | parse_decode_flag + 函数指针策略,支持 Row-MT 流水线 |
| 递归块划分 | decode_partition() 的宏驱动递归,支持10种划分模式 |
| 多级并行 | Tile-MT (粗粒度) + Row-MT (细粒度) 两级并行 |
| 统一的重建接口 | decode_token_recon_block() 统一处理帧内/帧间块 |
| 完整的后处理链 | LoopFilter → CDEF → SuperRes → Restoration 顺序执行 |
13.2 关键函数速查表
| 函数 | 行数 | 职责 |
|---|---|---|
av1_decode_frame_headers_and_setup |
5169 | 帧头解析入口 |
av1_decode_tg_tiles_and_wrapup |
5271 | Tile解码+后处理入口 |
read_uncompressed_header |
4487 | 帧头语法元素解析 |
decode_partition |
1260 | 递归块划分调度 |
decode_token_recon_block |
904 | 块级解码+重建 |
decode_tile |
2716 | 单Tile解码 |
decode_tiles |
2757 | Tile序列解码(单线程) |
decode_tiles_mt |
3566 | Tile多线程解码 |
decode_tiles_row_mt |
3748 | Row-MT流水线解码 |
predict_and_reconstruct_intra_block |
216 | 帧内块预测+重建 |
predict_inter_block |
847 | 帧间块预测 |
inverse_transform_block |
154 | 逆变换 |
set_decode_func_pointers |
2694 | 函数指针策略配置 |
13.3 性能关键路径
解码器的性能瓶颈主要集中在以下几个环节:
- 熵解码 (
aom_read_symbol/aom_read_cdf): 比特级的算术解码,是单线程的瓶颈 - 运动补偿 (
build_inter_predictors): 涉及大量插值计算和内存访问 - 逆变换 (
av1_inverse_transform_block): 矩阵乘法和累加计算 - 环内滤波: 尤其是 CDEF 和 Loop Restoration 的大核卷积
多线程策略正是为了将这些计算密集型任务分配到多个核心上并行执行。
参考资源
本文基于 libaom-main 分支源码分析,文件版本可能随时间更新。