一、引言
1.1 背景介绍
AV1(Alliance for Open Media Video 1)是由 Google、Netflix、Amazon 等多家公司组成的联盟开发的新一代视频编码标准。作为 VP9 的后继者,AV1 在压缩效率上相比 VP9 和 H.265/HEVC 有着显著提升,被广泛应用于流媒体传输、视频会议等领域。
在视频编码中,模式选择(Mode Selection) 是决定编码质量的关键环节。对于 Inter Frame(帧间帧,即非关键帧),编码器不仅需要评估各种帧间预测模式(如 Skip、Merge、NEARESTMV、NEWMV 等),还需要评估帧内预测模式。这是因为:
- 场景切换检测:当当前帧发生场景切换时,使用帧内预测可能比帧间预测更有效
- 局部区域特性:某些图像区域(如纹理复杂区域)使用帧内预测可能获得更好的压缩效果
- 随机访问点附近:在 GOP 结构中,某些帧可能更适合使用帧内预测
1.2 本文目标
本文将深入分析 libaom(AV1 参考软件)中 search_intra_modes_in_interframe 函数,该函数负责在帧间帧中搜索最佳帧内预测模式。我们将涵盖:
- 函数的调用关系与执行上下文
- 核心算法原理与数据结构
- 帧内预测模式的完整搜索流程
- 速率-失真优化(RD Optimization)机制
- 速度特性(Speed Features)加速策略
- 源码逐行解读
二、函数概述
2.1 函数签名与位置
文件位置 :av1/encoder/rdopt.c:5418
c
static AOM_INLINE void search_intra_modes_in_interframe(
InterModeSearchState *search_state, // [in/out] 帧间模式搜索状态
const AV1_COMP *cpi, // [in] 编码器顶层结构体
MACROBLOCK *x, // [in/out] 当前宏块数据结构
RD_STATS *rd_cost, // [out] 最佳RD代价
BLOCK_SIZE bsize, // [in] 当前块尺寸
PICK_MODE_CONTEXT *ctx, // [in/out] 模式选择上下文
const InterModeSFArgs *sf_args, // [in] 速度特性参数
unsigned int intra_ref_frame_cost, // [in] 帧内参考帧的熵编码代价
int64_t yrd_threshold) // [in] Luma intra模式的RD阈值
2.2 函数功能概述
该函数的核心职责是:在帧间帧编码过程中,评估并选择最优的帧内预测模式。
函数执行流程可概括为:
┌─────────────────────────────────────────────────────────────┐
│ search_intra_modes_in_interframe │
├─────────────────────────────────────────────────────────────┤
│ 1. Luma(亮度)模式搜索 │
│ └─→ 遍历 61 种 Luma 帧内预测模式 │
│ └─→ 对每种模式进行 RD 评估 │
│ └─→ 选择最佳 Luma 模式 │
│ │
│ 2. Chroma(色度)模式搜索 │
│ └─→ 基于最佳 Luma 模式搜索色度预测模式 │
│ └─→ 选择最佳 Chroma 模式 │
│ │
│ 3. 合并 Luma + Chroma RD 统计 │
│ └─→ 计算最终 RD 代价 │
│ └─→ 更新搜索状态(如果找到更好的模式) │
└─────────────────────────────────────────────────────────────┘
三、调用关系分析
3.1 调用栈
┌──────────────────────────────────────────────────────────────────┐
│ av1_rd_pick_inter_mode_sb │
│ (av1/encoder/rdopt.c) │
├──────────────────────────────────────────────────────────────────┤
│ 在完成帧间模式评估后,调用: │
│ │
│ 6121: skip_intra_modes_in_interframe(...) // 帧内模式跳过判断 │
│ 6126: search_intra_modes_in_interframe(...) // 帧内模式搜索 │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ search_intra_modes_in_interframe │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 内部调用的关键函数: │
│ │
│ 1. set_y_mode_and_delta_angle() // 设置帧内预测模式 │
│ └─ 定义位置: av1/encoder/intra_mode_search.c:383 │
│ │
│ 2. av1_handle_intra_y_mode() // 评估 Luma 帧内模式 │
│ └─ 定义位置: av1/encoder/intra_mode_search.c:1282 │
│ │
│ 3. av1_search_intra_uv_modes_in_interframe() // 色度模式搜索 │
│ └─ 定义位置: av1/encoder/intra_mode_search.c:1383 │
│ │
│ 4. update_search_state() // 更新搜索状态 │
│ └─ 定义位置: av1/encoder/rdopt.c:4833 │
│ │
└──────────────────────────────────────────────────────────────────┘
3.2 调用上下文源码(第 6117-6131 行)
c
#if CONFIG_COLLECT_COMPONENT_TIMING
start_timing(cpi, handle_intra_mode_time);
#endif
// Gate intra mode evaluation if best of inter is skip except when source
// variance is extremely low and also based on max intra bsize.
skip_intra_modes_in_interframe(cm, x, bsize, &search_state, sf, inter_cost,
intra_cost);
// 计算帧内参考帧的熵编码代价
const unsigned int intra_ref_frame_cost = ref_costs_single[INTRA_FRAME];
// 调用帧内模式搜索函数
search_intra_modes_in_interframe(&search_state, cpi, x, rd_cost, bsize, ctx,
&sf_args, intra_ref_frame_cost,
best_inter_yrd);
#if CONFIG_COLLECT_COMPONENT_TIMING
end_timing(cpi, handle_intra_mode_time);
#endif
关键观察:
- 帧内模式搜索发生在所有帧间模式评估完成之后
best_inter_yrd作为 Luma 帧内搜索的阈值,用于提前终止色度搜索skip_intra_modes_in_interframe()可以提前跳过整个帧内搜索过程
四、AV1 帧内预测模式体系
4.1 Luma 帧内预测模式(61 种)
AV1 定义了丰富的帧内预测模式,核心枚举定义在 av1/common/enums.h:
c
enum {
DC_PRED, // DC 预测:使用上方和左侧像素的平均值
V_PRED, // 垂直预测
H_PRED, // 水平预测
D45_PRED, // 45度方向预测
D135_PRED, // 135度方向预测
D113_PRED, // 113度方向预测
D157_PRED, // 157度方向预测
D203_PRED, // 203度方向预测
D67_PRED, // 67度方向预测
SMOOTH_PRED, // 平滑预测(水平和垂直的加权组合)
SMOOTH_V_PRED, // 垂直平滑预测
SMOOTH_H_PRED, // 水平平滑预测
PAETH_PRED, // Paeth 预测(基于最小梯度方向)
// ... 帧间模式从 NEARESTMV 开始 ...
INTRA_MODES = PAETH_PRED + 1, // PAETH_PRED 必须是最后一个帧内模式
};
4.2 LUMA_MODE_COUNT 的计算
c
// 定义在 av1/common/enums.h:395
// 包含所有帧内模式及其 8 种方向模式各自的 6 个角度增量
#define LUMA_MODE_COUNT (PAETH_PRED - DC_PRED + 1 + 6 * 8)
// 展开: (12 - 0 + 1) + 48 = 13 + 48 = 61 种 Luma 帧内模式
模式分布:
| 模式类型 | 数量 | 说明 |
|---|---|---|
| 非方向模式 | 5 | DC_PRED, SMOOTH_PRED, SMOOTH_V_PRED, SMOOTH_H_PRED, PAETH_PRED |
| 基础方向模式 | 8 | V_PRED, H_PRED, D45_PRED, D135_PRED, D113_PRED, D157_PRED, D203_PRED, D67_PRED |
| 方向模式角度增量 | 8 × 6 = 48 | 每个方向模式支持 -3 到 +3 的角度微调 |
4.3 Chroma 帧内预测模式
c
enum {
UV_DC_PRED, // 色度 DC 预测
UV_V_PRED, // 垂直预测
UV_H_PRED, // 水平预测
UV_D45_PRED, // 45度方向
UV_D135_PRED, // 135度方向
UV_D113_PRED, // 113度方向
UV_D157_PRED, // 157度方向
UV_D203_PRED, // 203度方向
UV_D67_PRED, // 67度方向
UV_SMOOTH_PRED, // 平滑预测
UV_SMOOTH_V_PRED, // 垂直平滑
UV_SMOOTH_H_PRED, // 水平平滑
UV_PAETH_PRED, // Paeth 预测
UV_CFL_PRED, // Chroma-from-Luma(从亮度重建预测色度)
UV_INTRA_MODES,
UV_MODE_INVALID,
} UV_PREDICTION_MODE;
五、核心数据结构
5.1 InterModeSearchState 结构体
c
// 定义在 av1/encoder/rdopt.c:307-346
typedef struct InterModeSearchState {
int64_t best_rd; // 当前最佳 RD 代价
int64_t best_skip_rd[2]; // Skip 模式的 RD 代价
MB_MODE_INFO best_mbmode; // 最佳模式信息
int best_rate_y; // 最佳 Luma 编码代价
int best_rate_uv; // 最佳 Chroma 编码代价
int best_mode_skippable; // 最佳模式是否可跳过
int best_skip2; // 第二次 Skip 标志
THR_MODES best_mode_index; // 最佳模式的枚举索引
int64_t best_intra_rd; // 最佳帧内模式 RD 代价 ← 关键字段
int64_t best_pred_rd[REFERENCE_MODES]; // 最佳帧间帧内 RD
// ... 其他字段 ...
IntraModeSearchState intra_search_state; // 帧内搜索状态 ← 关键字段
} InterModeSearchState;
5.2 IntraModeSearchState 结构体
c
// 定义在 av1/encoder/intra_mode_search.h:31-70
typedef struct IntraModeSearchState {
// 最佳帧内模式
PREDICTION_MODE best_intra_mode;
// 速度特性相关变量
int skip_intra_modes; // 是否跳过所有帧内模式
uint8_t directional_mode_skip_mask[INTRA_MODES]; // 方向模式跳过掩码
int dir_mode_skip_mask_ready; // 掩码是否已计算
// 色度模式搜索缓存(避免重复搜索)
int rate_uv_intra; // 色度模式的编码代价
int rate_uv_tokenonly; // 色度 token 编码代价
int64_t dist_uvs; // 色度失真
uint8_t skip_uvs; // 色度是否可跳过
UV_PREDICTION_MODE mode_uv; // 最佳色度模式
PALETTE_MODE_INFO pmi_uv; // 色度调色板信息
int8_t uv_angle_delta; // 色度角度增量
} IntraModeSearchState;
5.3 RD_STATS 结构体
c
// 速率-失真统计结构
typedef struct {
int rate; // 编码比特数(熵编码)
int64_t dist; // 失真度(SSD 或其他度量)
int64_t rdcost; // RD 代价 = RDCOST(rdmult, rate, dist)
int skip_txfm; // 是否跳过变换
} RD_STATS;
// RDCOST 宏定义
#define RDCOST(rdmult, rate, dist) ((rate) * (rdmult) + ((dist) * 16))
六、函数实现详解
6.1 整体结构概览
c
// rdopt.c:5418-5600
static AOM_INLINE void search_intra_modes_in_interframe(
InterModeSearchState *search_state, const AV1_COMP *cpi, MACROBLOCK *x,
RD_STATS *rd_cost, BLOCK_SIZE bsize, PICK_MODE_CONTEXT *ctx,
const InterModeSFArgs *sf_args, unsigned int intra_ref_frame_cost,
int64_t yrd_threshold) {
// ========== 第一部分:初始化 ==========
const AV1_COMMON *const cm = &cpi->common;
const SPEED_FEATURES *const sf = &cpi->sf;
const IntraModeCfg *const intra_mode_cfg = &cpi->oxcf.intra_mode_cfg;
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *const mbmi = xd->mi[0];
IntraModeSearchState *intra_search_state = &search_state->intra_search_state;
// 初始化最佳模式相关变量
int is_best_y_mode_intra = 0;
RD_STATS best_intra_rd_stats_y;
int64_t best_rd_y = INT64_MAX;
int best_mode_cost_y = -1;
MB_MODE_INFO best_mbmi = *xd->mi[0];
THR_MODES best_mode_enum = THR_INVALID;
// ...
// ========== 第二部分:Luma 模式搜索 ==========
// 遍历 61 种 Luma 帧内模式
for (int mode_idx = 0; mode_idx < LUMA_MODE_COUNT; ++mode_idx) {
// 1. 设置帧内预测模式
set_y_mode_and_delta_angle(...);
// 2. 速度特性过滤
if (sf_args->mode_skip_mask->pred_modes[INTRA_FRAME] & (1 << mbmi->mode))
continue;
// 3. 配置检查过滤
if (!intra_mode_cfg->enable_smooth_intra && ...)
continue;
// 4. 评估 Luma 模式
const int is_luma_result_valid = av1_handle_intra_y_mode(...);
// 5. 更新最佳 Luma 模式
if (is_luma_result_valid && intra_rd_y < yrd_threshold) {
if (intra_rd_y < best_rd_y) {
best_intra_rd_stats_y = intra_rd_stats_y;
best_rd_y = intra_rd_y;
best_mbmi = *mbmi;
// ... 保存其他状态 ...
}
}
}
// ========== 第三部分:色度模式搜索 ==========
if (is_best_y_mode_intra) {
const int intra_uv_mode_valid = av1_search_intra_uv_modes_in_interframe(...);
// ...
}
// ========== 第四部分:合并 RD 统计并更新状态 ==========
// 合并 Luma 和 Chroma 的 rate
intra_rd_stats.rate = best_intra_rd_stats_y.rate + best_mode_cost_y;
// 计算最终 RD 代价
const int64_t this_rd = RDCOST(x->rdmult, intra_rd_stats.rate, intra_rd_stats.dist);
// 更新最佳帧内 RD
if (this_rd < search_state->best_intra_rd) {
search_state->best_intra_rd = this_rd;
intra_search_state->best_intra_mode = mode;
}
// 更新全局搜索状态(如果找到更好的模式)
if (intra_rd_stats.rdcost < search_state->best_rd) {
update_search_state(...);
}
}
6.2 Luma 模式搜索核心循环
6.2.1 模式设置函数
c
// av1/encoder/intra_mode_search.c:383-401
void set_y_mode_and_delta_angle(const int mode_idx, MB_MODE_INFO *const mbmi,
int reorder_delta_angle_eval) {
if (mode_idx < INTRA_MODE_END) {
// 基础帧内模式(0-12)
mbmi->mode = intra_rd_search_mode_order[mode_idx];
mbmi->angle_delta[PLANE_TYPE_Y] = 0;
} else {
// 带有角度增量的方向模式
// 模式索引从 INTRA_MODE_END 开始
mbmi->mode = (mode_idx - INTRA_MODE_END) / (MAX_ANGLE_DELTA * 2) + V_PRED;
int delta_angle_eval_idx =
(mode_idx - INTRA_MODE_END) % (MAX_ANGLE_DELTA * 2);
if (reorder_delta_angle_eval) {
mbmi->angle_delta[PLANE_TYPE_Y] =
luma_delta_angles_order[delta_angle_eval_idx];
} else {
mbmi->angle_delta[PLANE_TYPE_Y] =
(delta_angle_eval_idx < 3 ? (delta_angle_eval_idx - 3)
: (delta_angle_eval_idx - 2));
}
}
}
关键点:
intra_rd_search_mode_order[]:预定义的最优搜索顺序,基于经验优化angle_delta:方向预测模式的角度微调,范围 [-3, +3]
6.2.2 速度特性过滤
c
// rdopt.c:5448-5492
// 1. 整体跳过检查
if (sf->intra_sf.skip_intra_in_interframe &&
search_state->intra_search_state.skip_intra_modes)
break;
// 2. 模式跳过掩码检查
if (sf_args->mode_skip_mask->pred_modes[INTRA_FRAME] & (1 << mbmi->mode))
continue;
// 3. Smooth 模式配置检查
if ((!intra_mode_cfg->enable_smooth_intra ||
cpi->sf.intra_sf.disable_smooth_intra) &&
(mbmi->mode == SMOOTH_PRED || mbmi->mode == SMOOTH_H_PRED ||
mbmi->mode == SMOOTH_V_PRED))
continue;
// 4. Paeth 模式配置检查
if (!intra_mode_cfg->enable_paeth_intra && mbmi->mode == PAETH_PRED)
continue;
// 5. 方向模式角度增量检查
if (av1_is_directional_mode(mbmi->mode) &&
!(av1_use_angle_delta(bsize) && intra_mode_cfg->enable_angle_delta) &&
mbmi->angle_delta[PLANE_TYPE_Y] != 0)
continue;
// 6. 基于已有最佳帧间模式的剪枝
if ((sf->rt_sf.mode_search_skip_flags & FLAG_SKIP_INTRA_BESTINTER) &&
(this_mode >= D45_PRED && this_mode <= PAETH_PRED)) {
if (search_state->best_mode_index != THR_INVALID &&
search_state->best_mbmode.ref_frame[0] > INTRA_FRAME)
continue;
}
// 7. 方向模式不匹配剪枝
if (sf->rt_sf.mode_search_skip_flags & FLAG_SKIP_INTRA_DIRMISMATCH) {
if (conditional_skipintra(
this_mode, search_state->intra_search_state.best_intra_mode))
continue;
}
6.3 av1_handle_intra_y_mode 函数
c
// av1/encoder/intra_mode_search.c:1282-1381
int av1_handle_intra_y_mode(IntraModeSearchState *intra_search_state,
const AV1_COMP *cpi, MACROBLOCK *x,
BLOCK_SIZE bsize, unsigned int ref_frame_cost,
const PICK_MODE_CONTEXT *ctx, RD_STATS *rd_stats_y,
int64_t best_rd, int *mode_cost_y, int64_t *rd_y,
int64_t *best_model_rd,
int64_t top_intra_model_rd[]) {
const AV1_COMMON *cm = &cpi->common;
const INTRA_MODE_SPEED_FEATURES *const intra_sf = &cpi->sf.intra_sf;
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *const mbmi = xd->mi[0];
// ========== 1. 计算已知代价 ==========
const PREDICTION_MODE mode = mbmi->mode;
const ModeCosts *mode_costs = &x->mode_costs;
const int mode_cost =
mode_costs->mbmode_cost[size_group_lookup[bsize]][mode] + ref_frame_cost;
const int skip_ctx = av1_get_skip_txfm_context(xd);
int known_rate = mode_cost;
// DC_PRED 和 PAETH_PRED 不需要额外的帧内代价惩罚
if (mode != DC_PRED && mode != PAETH_PRED) {
known_rate += av1_get_intra_cost_penalty(
cm->quant_params.base_qindex, cm->quant_params.y_dc_delta_q,
cm->seq_params->bit_depth);
}
known_rate += AOMMIN(mode_costs->skip_txfm_cost[skip_ctx][0],
mode_costs->skip_txfm_cost[skip_ctx][1]);
const int64_t known_rd = RDCOST(x->rdmult, known_rate, 0);
// 已知 RD 已超过最佳 RD,提前终止
if (known_rd > best_rd) {
intra_search_state->skip_intra_modes = 1;
return 0;
}
// ========== 2. 方向模式 HOG 剪枝 ==========
if (is_directional_mode && av1_use_angle_delta(bsize) &&
cpi->oxcf.intra_mode_cfg.enable_angle_delta) {
if (intra_sf->intra_pruning_with_hog &&
!intra_search_state->dir_mode_skip_mask_ready) {
prune_intra_mode_with_hog(...);
intra_search_state->dir_mode_skip_mask_ready = 1;
}
if (intra_search_state->directional_mode_skip_mask[mode]) return 0;
}
// ========== 3. 快速模型 RD 评估 ==========
const TX_SIZE tx_size = AOMMIN(TX_32X32, max_txsize_lookup[bsize]);
const int64_t this_model_rd =
intra_model_rd(&cpi->common, x, 0, bsize, tx_size, /*use_hadamard=*/1);
// 模型 RD 剪枝
if (prune_intra_y_mode(this_model_rd, best_model_rd, top_intra_model_rd,
intra_sf->top_intra_model_count_allowed,
model_rd_index_for_pruning))
return 0;
// ========== 4. 完整 RD 搜索 ==========
av1_init_rd_stats(rd_stats_y);
av1_pick_uniform_tx_size_type_yrd(cpi, x, rd_stats_y, bsize, best_rd);
// ========== 5. DC_PRED 的滤波帧内模式评估 ==========
if (mode == DC_PRED && av1_filter_intra_allowed_bsize(cm, bsize)) {
handle_filter_intra_mode(cpi, x, bsize, ctx, rd_stats_y, mode_cost,
best_rd, best_rd_so_far);
}
// ========== 6. 返回结果 ==========
if (rd_stats_y->rate == INT_MAX) return 0;
*mode_cost_y = intra_mode_info_cost_y(cpi, x, mbmi, bsize, mode_cost, 0);
const int rate_y = rd_stats_y->skip_txfm
? mode_costs->skip_txfm_cost[skip_ctx][1]
: rd_stats_y->rate;
*rd_y = RDCOST(x->rdmult, rate_y + *mode_cost_y, rd_stats_y->dist);
// 如果当前 RD 超过 best_rd 过多,设置跳过标志
if (best_rd < (INT64_MAX / 2) && *rd_y > (best_rd + (best_rd >> 2))) {
intra_search_state->skip_intra_modes = 1;
return 0;
}
return 1;
}
6.4 色度模式搜索
c
// av1/encoder/intra_mode_search.c:1383-1441
int av1_search_intra_uv_modes_in_interframe(
IntraModeSearchState *intra_search_state, const AV1_COMP *cpi,
MACROBLOCK *x, BLOCK_SIZE bsize, RD_STATS *rd_stats,
const RD_STATS *rd_stats_y, RD_STATS *rd_stats_uv, int64_t best_rd) {
const AV1_COMMON *cm = &cpi->common;
MACROBLOCKD *const xd = &x->e_mbd;
MB_MODE_INFO *const mbmi = xd->mi[0];
// ========== 1. 缓存检查 ==========
assert(intra_search_state->rate_uv_intra == INT_MAX);
if (intra_search_state->rate_uv_intra == INT_MAX) {
// 首次搜索色度模式
const TX_SIZE uv_tx = av1_get_tx_size(AOM_PLANE_U, xd);
// 核心色度模式搜索
av1_rd_pick_intra_sbuv_mode(cpi, x, &intra_search_state->rate_uv_intra,
&intra_search_state->rate_uv_tokenonly,
&intra_search_state->dist_uvs,
&intra_search_state->skip_uvs, bsize, uv_tx);
// 缓存色度搜索结果
intra_search_state->mode_uv = mbmi->uv_mode;
intra_search_state->pmi_uv = *pmi;
intra_search_state->uv_angle_delta = mbmi->angle_delta[PLANE_TYPE_UV];
const int uv_rate = intra_search_state->rate_uv_tokenonly;
const int64_t uv_dist = intra_search_state->dist_uvs;
const int64_t uv_rd = RDCOST(x->rdmult, uv_rate, uv_dist);
// 如果色度 RD 超过阈值,跳过所有帧内模式
if (uv_rd > best_rd) {
intra_search_state->skip_intra_modes = 1;
return 0;
}
}
// ========== 2. 复用缓存结果 ==========
rd_stats_uv->rate = intra_search_state->rate_uv_tokenonly;
rd_stats_uv->dist = intra_search_state->dist_uvs;
rd_stats_uv->skip_txfm = intra_search_state->skip_uvs;
rd_stats->skip_txfm = rd_stats_y->skip_txfm && rd_stats_uv->skip_txfm;
mbmi->uv_mode = intra_search_state->mode_uv;
return 1;
}
关键设计:
- 色度结果缓存:避免对每个 Luma 模式重复搜索色度模式
- 缓存在
IntraModeSearchState中,可在av1_search_palette_mode()中复用
6.5 状态更新机制
c
// rdopt.c:4833-4866
static INLINE void update_search_state(
InterModeSearchState *search_state, RD_STATS *best_rd_stats_dst,
PICK_MODE_CONTEXT *ctx, const RD_STATS *new_best_rd_stats,
const RD_STATS *new_best_rd_stats_y, const RD_STATS *new_best_rd_stats_uv,
THR_MODES new_best_mode, const MACROBLOCK *x, int txfm_search_done) {
const MACROBLOCKD *xd = &x->e_mbd;
const MB_MODE_INFO *mbmi = xd->mi[0];
const int skip_ctx = av1_get_skip_txfm_context(xd);
// ========== 更新全局最佳 RD ==========
search_state->best_rd = new_best_rd_stats->rdcost;
search_state->best_mode_index = new_best_mode;
*best_rd_stats_dst = *new_best_rd_stats;
// ========== 保存最佳模式信息 ==========
search_state->best_mbmode = *mbmi;
search_state->best_skip2 = skip_txfm;
search_state->best_mode_skippable = new_best_rd_stats->skip_txfm;
// ========== 保存编码代价 ==========
if (txfm_search_done) {
search_state->best_rate_y =
new_best_rd_stats_y->rate + ...;
search_state->best_rate_uv = new_best_rd_stats_uv->rate;
}
// ========== 保存变换信息 ==========
search_state->best_y_rdcost = *new_best_rd_stats_y;
memcpy(ctx->blk_skip, txfm_info->blk_skip, ...);
av1_copy_array(ctx->tx_type_map, xd->tx_type_map, ...);
}
七、速率-失真优化(RD Optimization)机制
7.1 RD 代价计算
AV1 使用经典的率失真优化框架:
RD Cost = D + λ × R
在代码中:
c
// RDCOST 宏
#define RDCOST(rdmult, rate, dist) ((rate) * (rdmult) + ((dist) * 16))
其中 rdmult(RD multiplier)与量化参数(QP)相关,控制失真和码率之间的权衡。
7.2 快速模型 RD 评估
为了加速帧内模式搜索,AV1 使用简化的 HADAMARD 变换进行快速 RD 估计:
c
const int64_t this_model_rd =
intra_model_rd(&cpi->common, x, 0, bsize, tx_size, /*use_hadamard=*/1);
模型 RD 剪枝策略:
c
// av1/encoder/intra_mode_search.c:436-460
int prune_intra_y_mode(int64_t this_model_rd, int64_t *best_model_rd,
int64_t top_intra_model_rd[], int max_model_cnt_allowed,
int model_rd_index_for_pruning) {
const double thresh_best = 1.50; // 相比最佳模型 RD 的阈值
const double thresh_top = 1.00; // 相比 top-K 模型 RD 的阈值
// 1. 更新 top-K 模型 RD 列表
for (int i = 0; i < max_model_cnt_allowed; i++) {
if (this_model_rd < top_intra_model_rd[i]) {
// 插入排序
for (int j = max_model_cnt_allowed - 1; j > i; j--) {
top_intra_model_rd[j] = top_intra_model_rd[j - 1];
}
top_intra_model_rd[i] = this_model_rd;
break;
}
}
// 2. 基于 top-K 列表剪枝
if (top_intra_model_rd[model_rd_index_for_pruning] != INT64_MAX &&
this_model_rd > thresh_top * top_intra_model_rd[model_rd_index_for_pruning])
return 1;
// 3. 基于全局最佳模型 RD 剪枝
if (this_model_rd != INT64_MAX &&
this_model_rd > thresh_best * (*best_model_rd))
return 1;
if (this_model_rd < *best_model_rd) *best_model_rd = this_model_rd;
return 0;
}
7.3 帧内代价惩罚
帧内预测模式需要额外的编码代价来指示使用了帧内参考帧而非帧间参考帧:
c
const int intra_cost_penalty = av1_get_intra_cost_penalty(
cm->quant_params.base_qindex, cm->quant_params.y_dc_delta_q,
cm->seq_params->bit_depth);
// DC_PRED 和 PAETH_PRED 不需要额外惩罚
if (mode != DC_PRED && mode != PAETH_PRED)
known_rate += intra_cost_penalty;
八、速度特性(Speed Features)加速策略
8.1 整体帧内模式跳过
c
// rdopt.c:5642-5678
static AOM_INLINE void skip_intra_modes_in_interframe(
AV1_COMMON *const cm, struct macroblock *x, BLOCK_SIZE bsize,
InterModeSearchState *search_state, const SPEED_FEATURES *const sf,
int64_t inter_cost, int64_t intra_cost) {
// 1. 基于 MV 范围的跳过
if (sf->rt_sf.prune_intra_mode_based_on_mv_range &&
bsize > sf->part_sf.max_intra_bsize && !comp_pred) {
const MV best_mv = search_state->best_mbmode.mv[0].as_mv;
const int mv_thresh = 16 << sf->rt_sf.prune_intra_mode_based_on_mv_range;
if (abs(best_mv.row) < mv_thresh && abs(best_mv.col) < mv_thresh &&
x->source_variance > 128) {
search_state->intra_search_state.skip_intra_modes = 1;
return;
}
}
// 2. 基于 Skip 模式的跳过
if ((skip_intra_in_interframe >= 2) && search_state->best_mbmode.skip_txfm) {
const int qindex_thresh[2] = { 200, MAXQ };
const int ind = (skip_intra_in_interframe >= 3) ? 1 : 0;
if (!have_newmv_in_inter_mode(search_state->best_mbmode.mode) &&
(x->qindex <= qindex_thresh[ind])) {
search_state->intra_search_state.skip_intra_modes = 1;
return;
}
}
// 3. 基于 ML 模型的跳过
if (inter_cost >= 0 && intra_cost >= 0) {
// 使用神经网络模型预测是否跳过帧内搜索
const NN_CONFIG *nn_config = (AOMMIN(cm->width, cm->height) <= 480)
? &av1_intrap_nn_config
: &av1_intrap_hd_nn_config;
// ... 特征提取和预测 ...
av1_nn_predict(nn_features, nn_config, 1, scores);
// 根据预测分数决定是否跳过
}
}
8.2 方向模式的 HOG 剪枝
c
// 使用方向梯度直方图(HOG)特征剪枝方向预测模式
if (intra_sf->intra_pruning_with_hog) {
const float thresh[4] = { -1.2f, 0.0f, 0.0f, 1.2f };
prune_intra_mode_with_hog(x, bsize, cm->seq_params->sb_size,
thresh[intra_sf->intra_pruning_with_hog - 1],
intra_search_state->directional_mode_skip_mask,
is_chroma);
}
8.3 速度特性参数表
| 特性名称 | 说明 | 典型值 |
|---|---|---|
skip_intra_in_interframe |
是否启用基于 ML 的帧内模式跳过 | 0-4 |
prune_intra_mode_based_on_mv_range |
基于 MV 范围的帧内模式跳过 | 0-2 |
intra_pruning_with_hog |
使用 HOG 特征剪枝方向模式 | 0-4 |
top_intra_model_count_allowed |
模型 RD 剪枝的 top-K 数量 | 4 |
disable_smooth_intra |
是否禁用平滑帧内模式 | 0/1 |
prune_luma_odd_delta_angles_in_intra |
剪枝奇数角度增量 | 0/1 |
九、算法流程图
9.1 完整执行流程
┌─────────────────────────────────────────────────────────────────────┐
│ 帧间帧编码开始 │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 1. 帧间模式搜索 │
│ - NEARESTMV, NEARMV, NEWMV │
│ - Skip, Merge │
│ - 复合预测模式 │
│ - 结果保存在 search_state.best_mbmode │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 2. skip_intra_modes_in_interframe() │
│ - 基于 MV 范围检查 │
│ - 基于 Skip 模式检查 │
│ - 基于源方差检查 │
│ - 基于 ML 模型预测 │
│ - 设置 skip_intra_modes 标志 │
└─────────────────────────────────────────────────────────────────────┘
│
┌─────────────┴─────────────┐
│ skip_intra_modes == 1? │
└─────────────┬─────────────┘
Yes │ No
┌────────────┐ │ ┌─────────────────────────────────┐
│ 跳过帧内搜索 │ │ │ 3. search_intra_modes_in_interframe()│
└────────────┘ │ └─────────────────────────────────┘
│ │
│ ▼
│ ┌─────────────────────────────────┐
│ │ 3.1 Luma 模式搜索循环 │
│ │ for mode_idx in [0, 61): │
│ │ - set_y_mode_and_delta_angle() │
│ │ - av1_handle_intra_y_mode() │
│ │ - 模型 RD 剪枝 │
│ │ - 完整 RD 评估 │
│ │ - 保存最佳 Luma 模式 │
│ └─────────────────────────────────┘
│ │
│ ▼
│ ┌─────────────────────────────────┐
│ │ 3.2 Chroma 模式搜索 │
│ │ - av1_search_intra_uv_modes() │
│ │ - 缓存色度搜索结果 │
│ └─────────────────────────────────┘
│ │
│ ▼
│ ┌─────────────────────────────────┐
│ │ 3.3 合并 RD 统计 │
│ │ - rate = rate_y + rate_uv │
│ │ - dist = dist_y + dist_uv │
│ │ - 计算 this_rd │
│ └─────────────────────────────────┘
│ │
│ ▼
│ ┌─────────────────────────────────┐
│ │ 3.4 更新搜索状态 │
│ │ if (this_rd < best_rd): │
│ │ update_search_state() │
│ └─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 4. 模式决策 │
│ - 比较帧间模式 vs 帧内模式 RD │
│ - 选择全局最佳模式 │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 帧间帧编码结束 │
└─────────────────────────────────────────────────────────────────────┘
十、实际应用场景
10.1 场景切换检测
当视频发生场景切换时,使用帧间预测通常无法获得良好的匹配:
c
// 示例场景
原视频序列: [场景A: 室内对话] → [场景B: 户外场景]
场景切换帧:
- 帧间预测:需要从参考帧预测完全不同的内容 → RD 代价高
- 帧内预测:直接使用当前帧的空间信息 → RD 代价相对较低
// search_intra_modes_in_interframe() 的作用
- 评估帧内模式的 RD 代价
- 如果帧内模式 RD < 帧间模式 RD,则选择帧内预测
10.2 局部纹理区域
某些图像区域(如纯色背景、噪声区域)可能不适合帧间预测:
c
// 噪声区域
- 帧间预测:运动估计/补偿可能引入噪声
- DC_PRED:直接使用平均值,可能更适合
// 纯色区域
- 帧内预测:使用相邻像素预测效果好
- 编码效率高
十一、关键代码片段汇总
11.1 核心循环
c
// 遍历所有 Luma 帧内模式
for (int mode_idx = 0; mode_idx < LUMA_MODE_COUNT; ++mode_idx) {
// 设置预测模式和角度增量
set_y_mode_and_delta_angle(
mode_idx, mbmi, sf->intra_sf.prune_luma_odd_delta_angles_in_intra);
// 评估 Luma 模式
const int is_luma_result_valid = av1_handle_intra_y_mode(
intra_search_state, cpi, x, bsize, intra_ref_frame_cost, ctx,
&intra_rd_stats_y, search_state->best_rd, &mode_cost_y, &intra_rd_y,
&best_model_rd, top_intra_model_rd);
// 更新最佳模式
if (is_luma_result_valid && intra_rd_y < yrd_threshold) {
if (intra_rd_y < best_rd_y) {
best_intra_rd_stats_y = intra_rd_stats_y;
best_mode_cost_y = mode_cost_y;
best_rd_y = intra_rd_y;
best_mbmi = *mbmi;
memcpy(best_blk_skip, x->txfm_search_info.blk_skip, ...);
av1_copy_array(best_tx_type_map, xd->tx_type_map, num_4x4);
}
}
}
11.2 最终 RD 合并
c
// 合并 Luma 和 Chroma 的 rate
intra_rd_stats.rate = best_intra_rd_stats_y.rate + best_mode_cost_y;
// 调整 tx_size 代价(对于帧内块,tx_size 始终被编码)
if (!xd->lossless[mbmi->segment_id] && block_signals_txsize(bsize)) {
best_intra_rd_stats_y.rate -= tx_size_cost(x, bsize, mbmi->tx_size);
}
// 添加色度模式的代价
if (num_planes > 1 && xd->is_chroma_ref) {
const int uv_mode_cost =
mode_costs->intra_uv_mode_cost[is_cfl_allowed(xd)][mode][mbmi->uv_mode];
intra_rd_stats.rate += intra_rd_stats_uv.rate +
intra_mode_info_cost_uv(cpi, x, mbmi, bsize, uv_mode_cost);
}
// 计算最终 RD
const int64_t this_rd = RDCOST(x->rdmult, intra_rd_stats.rate, intra_rd_stats.dist);
十二、总结与展望
12.1 函数设计亮点
-
分层搜索策略:
- Luma 模式完整遍历(61 种)
- Chroma 模式缓存复用
- 避免重复计算
-
多级剪枝机制:
- 模型 RD 快速剪枝
- 速度特性条件过滤
- ML 模型预测
-
RD 优化框架:
- 完整的率失真代价计算
- 帧内模式代价惩罚
- 模式信息熵编码代价
-
状态管理:
InterModeSearchState统一管理- 支持 Winner Mode 处理
- 便于后续优化
12.2 可改进方向
- 自适应 Luma 模式搜索顺序:基于相邻块模式动态调整
- 更精细的 ML 剪枝模型:训练更准确的跳过预测模型
- 并行化 Chroma 搜索:与 Luma 搜索并行执行
- 感知域 RD 优化:结合视觉感知模型优化 RD 权衡
12.3 参考资料
- AV1 官方规范文档
- libaom 源码仓库:
https://aomediacodec.github.io/av1-spec/ - 相关论文:
- "An Overview of AV1" - Daala 团队
- "Rate-Distortion Optimization for Video Compression" - 经典文献
附录:数据类型速查表
| 类型名 | 定义位置 | 说明 |
|---|---|---|
AV1_COMP |
av1/encoder/encoder.h |
编码器顶层结构体 |
MACROBLOCK |
av1/encoder/block.h |
宏块数据 |
MACROBLOCKD |
av1/decoder/blockd.h |
宏块解码数据 |
MB_MODE_INFO |
av1/common/blockd.h |
宏块模式信息 |
RD_STATS |
av1/encoder/rd.h |
率失真统计 |
BLOCK_SIZE |
av1/common/enums.h |
块尺寸枚举 |
PREDICTION_MODE |
av1/common/enums.h |
预测模式枚举 |
IntraModeSearchState |
av1/encoder/intra_mode_search.h |
帧内搜索状态 |
InterModeSearchState |
av1/encoder/rdopt.c |
帧间搜索状态 |
本文基于 libaom 最新版本源码分析,函数位置可能因版本迭代略有差异