SVT-AV1源码分析函数 svt_av1_optimize_b
perf svt-av1 编码热点函数
一 函数作用
svt_av1_optimize_b这个函数是SVT-AV1(Scaleable Video Technology for AV1)编码器中的一个很关键的函数,在编码过程中对块进行优化处理,以提高编码效率和视频质量,以下是对该函数的详细解释。
函数参数
ModeDecisionContext *ctx; 模式决策上下文,包含了当前块的编码模式决策信息。
int16_t txb_skip_connect 变换块跳过上下文,用于熵编码时判断是否跳过变换块
int16_t dc_sign_context; 直流系数符号上下文,用于确定直流系数的符号。
const TranLow *coeff_ptr 输入的变换系数指针,指向当前块的变换系数。
const MacroblockPlane *p; 宏块通道信息,包含了当前处理的通道相关的信息
TranLow *qcoeff_ptr; 量化后的变换系数输出指针
TranLow *dqcoeff_ptr; 反量化后的变换系数输出指针
uint16_t *eob; //有消费令系数数量的输出指针,表示在量化后有多少个非零系数。
const QuantParam *qparam; 量化参数,包含了量化步长等信息
TxSize tx_size 变换块的大小
TxType tx_type ;变换类型,制定了使用的变换方法。
Bool is_inter 是否是帧间编码块的标志
uint8_t use_sharpness 是否使用锐化功能的标志
uint8_t delta_q_present 是否使用delta量化参数的标志
uint8_t picture_qp 图片的量化参数
uint32_t lambda 拉格朗日系数,用于码率失真优化
int plane 当前处理的通道
该函数主要功能是对输入的变换系数进行量化,反量化等处理,并计算有效非零系数数量,同时根据不同的编码模式和参数进行优化,具体包括以下几个方面。
1.1 量化处理,根据量化参数对输入的变换系数进行量化,得到量化后的系数存储在qcoeff_ptr中,量化过程会会根据picture_qp, delta_q_present 等参数调整量化步长,以控制编码后的比特率质量。
2 去量化处理,对量化后的系数进行去量化,得到去量化后的系数存储在dqcoeff_ptr中,,反量化后的系数用于后续的重建过程,以生成预测块。
3 有效非零系数计算,通过遍历量化后的系数,统计有效非零系数的数量,将结果存储在eob中,这个值对于熵编码非常重要,因为可以减少需要编码的系数数量,提高编码效率。
4 码率失真优化,根据拉格朗日乘数 lambda 进行码率失真优化,选择最优的量化参数和编码模式,以在保证视频质量的同时最小化码率。
5 上下文处理,处理txb_skip_context和dc_sign_context等上下文信息,为熵编码提供必要的上下文数据,以便更高效的对系数进行编码。
6 锐化处理 如果use_sharpness标志碑设置,函数可能会应用锐化算法,以增强图像的边缘和细节,提高视觉质量。
7 编码模式适配,根据is_inter 标志判断当前块是帧内编码还是帧间编码,相应的调整处理策略,以适应不同的编码需求。
函数的作用和意义
svt_av1_optimize_b在SVT-AV1编码器中扮演着至关重要的角色。它通过对变换系数的优化处理,直接影响了编码后的视频质量和码率。在保证视频质量的前提下,通过量化、去量化、率失真优化等手段,尽可能减少码率,提高编码效率。同时,该函数还为后续的熵编码提供了必要的数据准备,确保整个编码流程的高效性和准确性。
二, 函数源码注释
static void svt_av1_optimize_b(
ModeDecisionContext *ctx, // 模式决策上下文,包含编码参数和中间数据
int16_t txb_skip_context, // 变换块是否跳过的上下文信息
int16_t dc_sign_context, // DC系数符号的上下文信息
const TranLow *coeff_ptr, // 原始变换系数指针
const MacroblockPlane *p, // 宏块通道信息(包含量化参数等)
TranLow *qcoeff_ptr, // 量化后系数指针(输出)
TranLow *dqcoeff_ptr, // 反量化后系数指针(输出)
uint16_t *eob, // 块的非零系数结束位置(End Of Block)
const QuantParam *qparam, // 量化参数
TxSize tx_size, // 变换尺寸(如TX_4x4, TX_8x8等)
TxType tx_type, // 变换类型(如DCT, ADST等)
Bool is_inter, // 是否为inter帧(帧间预测)
uint8_t use_sharpness, // 是否使用锐化量化
uint8_t delta_q_present, // 是否存在delta量化参数
uint8_t picture_qp, // 图像级QP值
uint32_t lambda, // 率失真优化的lambda参数
int plane) // 当前处理的通道(0=Y, 1=U, 2=V)
{
// --------------------------------------------
// 初始化参数和局部变量
// --------------------------------------------
int sharpness = 0; // 锐化级别,0表示不启用
// 判断是否启用快速模式(根据不同的通道和帧类型配置)
int fast_mode = (ctx->rdoq_ctrls.eob_fast_y_inter && is_inter && !plane) ||
(ctx->rdoq_ctrls.eob_fast_y_intra && !is_inter && !plane) ||
(ctx->rdoq_ctrls.eob_fast_uv_inter && is_inter && plane) ||
(ctx->rdoq_ctrls.eob_fast_uv_intra && !is_inter && plane)
? 1 : 0;
// 获取当前变换类型的扫描顺序(如之字形扫描)
const ScanOrder *const scan_order = &av1_scan_orders[tx_size][tx_type];
const int16_t *scan = scan_order->scan; // 具体扫描顺序数组
// 计算量化缩放参数
const int shift = av1_get_tx_scale_tab[tx_size]; // 量化移位值
// 通道类型(Y/UV通道)
const PlaneType plane_type = plane;
// 变换尺寸的熵编码上下文
const TxSize txs_ctx = get_txsize_entropy_ctx_tab[tx_size];
// 变换类型分类(2D/水平/垂直)
const TxClass tx_class = tx_type_to_class[tx_type];
// 变换块的宽度对数(如4x4块bwl=2)
const int bwl = get_txb_bwl_tab[tx_size];
// 变换块的实际宽高
const int width = get_txb_wide_tab[tx_size];
const int height = get_txb_high_tab[tx_size];
// 断言检查变换尺寸参数有效性
assert(width == (1 << bwl)); // 确保宽度是2的幂次
assert(txs_ctx < TX_SIZES); // 确保熵上下文有效
// 获取变换块的系数编码代价表
const LvMapCoeffCost *txb_costs = &ctx->md_rate_est_ctx->coeff_fac_bits[txs_ctx][plane_type];
// EOB分数字节代价表
const int eob_multi_size = txsize_log2_minus4[tx_size];
const LvMapEobCost *txb_eob_costs = &ctx->md_rate_est_ctx->eob_frac_bits[eob_multi_size][plane_type];
// 计算跳过/不跳过当前块的代价
const int non_skip_cost = txb_costs->txb_skip_cost[txb_skip_context][0]; // 不跳过的代价
const int skip_cost = txb_costs->txb_skip_cost[txb_skip_context][1]; // 跳过的代价
// 计算当前EOB位置的代价
const int eob_cost = get_eob_cost(*eob, txb_eob_costs, txb_costs, tx_class);
// --------------------------------------------
// 提前退出判断:如果跳过块的代价更低,则直接返回
// --------------------------------------------
// 根据块尺寸计算提前退出阈值
int sq_size_idx = 7 - (int)svt_log2f(ctx->blk_geom->sq_size);
if (eob_cost < (int)(width height sq_size_idx * ctx->rdoq_ctrls.early_exit_th)) {
// 如果跳过代价更优,直接退出
if (skip_cost < non_skip_cost) {
return;
}
}
// --------------------------------------------
// 快速模式处理:更新EOB并快速返回
// --------------------------------------------
if (fast_mode) {
// 快速更新量化系数和EOB
update_coeff_eob_fast(eob, shift, p->dequant_qtx, scan, coeff_ptr, qcoeff_ptr, dqcoeff_ptr);
// 如果所有系数都是0,直接返回
if (*eob == 0)
return;
}
// --------------------------------------------
// 率失真参数计算
// --------------------------------------------
int rweight = 100; // 率失真权重
const int rshift = 2; // 右移位操作
// 如果启用锐化量化且存在delta QP,调整锐化参数
if (use_sharpness && delta_q_present && plane == 0) {
int diff = ctx->sb_ptr->qindex - quantizer_to_qindex[picture_qp];
if (diff < 0) { // 如果当前QP低于图像QP
sharpness = 1; // 启用锐化
rweight = 0; // 调整权重
}
}
// 计算率失真乘数(综合lambda、通道权重等)
const int64_t rdmult = (((((int64_t)lambda plane_rd_mult[is_inter][plane_type]) rweight) / 100) + 2) >> rshift;
// --------------------------------------------
// 初始化系数层级缓存
// --------------------------------------------
uint8_t levels_buf[TX_PAD_2D]; // 系数层级缓存
uint8_t *const levels = set_levels(levels_buf, width); // 设置层级指针
// 如果EOB>1,初始化层级信息(用于系数扫描)
if (*eob > 1)
svt_av1_txb_init_levels(qcoeff_ptr, width, height, levels);
// --------------------------------------------
// 率失真累积变量初始化
// --------------------------------------------
int accu_rate = eob_cost; // 累积的码率代价
int64_t accu_dist = 0; // 累积的失真代价
// --------------------------------------------
// 逆序扫描处理系数(从最后一个非零系数开始)
// --------------------------------------------
int si = *eob - 1; // 扫描索引,从EOB-1开始
const int ci = scan[si]; // 当前扫描位置的系数索引
const TranLow qc = qcoeff_ptr[ci]; // 当前量化系数值
const TranLow abs_qc = abs(qc); // 绝对值
const int sign = qc < 0; // 符号位(0正1负)
// 非零系数数量和位置记录
const int max_nz_num = 4; // 最大记录的非零系数数
int nz_num = 1; // 当前非零系数计数
int nz_ci[5] = {ci, 0, 0, 0, 0}; // 非零系数位置数组
// 根据系数绝对值大小分情况处理
if (abs_qc >= 2) {
// 处理绝对值>=2的系数(复杂情况)
update_coeff_general(
&accu_rate, &accu_dist, si, *eob, tx_size, tx_class, bwl, height,
rdmult, shift, dc_sign_context, p->dequant_qtx, scan, txb_costs,
coeff_ptr, qcoeff_ptr, dqcoeff_ptr, levels, qparam->iqmatrix);
- -si; // 移动到前一个系数
} else {
// 处理绝对值=1的系数(简单情况)
assert(abs_qc == 1);
// 获取低层级上下文
const int coeff_ctx = get_lower_levels_ctx_eob(bwl, height, si);
// 计算系数编码代价
accu_rate += get_coeff_cost_eob(
ci, abs_qc, sign, coeff_ctx, dc_sign_context, txb_costs, bwl, tx_class);
// 计算失真差异
const TranLow tqc = coeff_ptr[ci]; // 原始系数
const TranLow dqc = dqcoeff_ptr[ci]; // 反量化系数
const int64_t dist = get_coeff_dist(tqc, dqc, shift); // 量化后的失真
const int64_t dist0 = get_coeff_dist(tqc, 0, shift); // 量化为0的失真
accu_dist += dist - dist0; // 累积失真差
- -si; // 移动到前一个系数
}
// --------------------------------------------
// 宏定义:根据不同变换类型处理EOB前的系数
// --------------------------------------------
define UPDATE_COEFF_EOB_CASE(tx_class_literal) \
case tx_class_literal: \
for (; si >= 0 && nz_num <= max_nz_num && !fast_mode; --si) { \
update_coeff_eob( \
&accu_rate, &accu_dist, eob, &nz_num, nz_ci, si, \
tx_size, tx_class_literal, bwl, height, \
dc_sign_context, rdmult, shift, p->dequant_qtx, scan, \
txb_eob_costs, txb_costs, coeff_ptr, qcoeff_ptr, \
dqcoeff_ptr, levels, sharpness, qparam->iqmatrix); \
} \
break;
// 根据变换类型分派处理
switch (tx_class) {
UPDATE_COEFF_EOB_CASE(TX_CLASS_2D); // 2D变换
UPDATE_COEFF_EOB_CASE(TX_CLASS_HORIZ); // 水平变换
UPDATE_COEFF_EOB_CASE(TX_CLASS_VERT); // 垂直变换
undef UPDATE_COEFF_EOB_CASE
default: assert(false); // 其他类型断言失败
}
// --------------------------------------------
// 处理所有系数后的跳过决策
// --------------------------------------------
if (si == -1 && nz_num <= max_nz_num) {
// 更新跳过标志和系数
update_skip(&accu_rate, accu_dist, eob, nz_num, nz_ci, rdmult,
skip_cost, non_skip_cost, qcoeff_ptr, dqcoeff_ptr, sharpness);
}
// --------------------------------------------
// 宏定义:处理剩余简单系数(EOB之后)
// --------------------------------------------
define UPDATE_COEFF_SIMPLE_CASE(tx_class_literal) \
case tx_class_literal: \
for (; si >= 1; --si) { \
update_coeff_simple( \
&accu_rate, si, *eob, tx_size, \
tx_class_literal, bwl, rdmult, \
shift, p->dequant_qtx, scan, \
txb_costs, coeff_ptr, qcoeff_ptr, \
dqcoeff_ptr, levels, qparam->iqmatrix); \
} \
break;
// 根据变换类型分派处理
switch (tx_class) {
UPDATE_COEFF_SIMPLE_CASE(TX_CLASS_2D); // 2D变换
UPDATE_COEFF_SIMPLE_CASE(TX_CLASS_HORIZ); // 水平变换
UPDATE_COEFF_SIMPLE_CASE(TX_CLASS_VERT); // 垂直变换
undef UPDATE_COEFF_SIMPLE_CASE
default: assert(false); // 其他类型断言失败
}
// --------------------------------------------
// 处理DC系数(扫描位置0)
// --------------------------------------------
if (si == 0) { // 只剩下DC系数
int64_t dummy_dist = 0; // 临时失真变量(不实际使用)
update_coeff_general(
&accu_rate, &dummy_dist, si, *eob, tx_size, tx_class, bwl, height,
rdmult, shift, dc_sign_context, p->dequant_qtx, scan, txb_costs,
coeff_ptr, qcoeff_ptr, dqcoeff_ptr, levels, qparam->iqmatrix);
}
}