SVT-AV1帧内编码代码分析

一 代码逻辑流程

帧内块划分和模式决策核心逻辑位置

1 块划分决策核心函数

主要入口函数:

svt_aom_mode_decision_sb() 超级块模式决策

处理整个SUperBlock的模式决策

遍历所有块,调用process_block() 处理每个块

块划分决策函数:

svt_aom_d1_non_square_block_decision()

非方形块NSQ划分决策

计算划分成分,决定是否采用非方形划分

svt_aom_d2_inter_depth_block_decision()

深度间划分决策

比较不同深度的成本,决定是否继续划分

update_d1_data()

更新d1数据,包括d1划分决策

2 模式决策核心函数

单个块模式决策

md_encode_block()

单个块的模式决策主函数

包含多阶段模式决策流程 (MDS0->MDS1->MDS2->MDS3)

帧内候选生成

inject_intra_condidates()

注入帧内预测候选模式

生成DC,方向,角度等帧内模式候选

generate_md_stage_0_cand

生成模式决策阶段0的候选

包括帧内和帧间候选

模式决策阶段

md_stage_0() 快速评估阶段

md_stage_1() 第一次全循环评估

md_stage_2() 第二次循环评估

md_stage_3() 最终全循环评估

3 关键文件位置

product_coding_loop.c 主要模式决策流程

svt_aom_decision_sb() #SuperBlock模式决策入口

md_encode_block() 单个模式决策

process_block() 块处理入口

full_loop.c 块划分决策

svt_aom_d1_non_square_block_decision() 非方形块决策

svt_aom_d2_inter_depth_block_decision() 深度间决策

mode_decision.c #模式决策核心逻辑

inject_intra_candidates() 帧内候选注入

generate_md_stage_0_cand() 候选生成

md_stage_0/1/2/3() 多阶段决策

enc_mode_config.c 模式决策配置

set_intra_ctrls() 帧内控制参数设置

4 执行流程

svt_aom_mode_decision_sb()

process_block()

md_encode_block()

generate_md_stage_0_cand() #生成候选 包括帧内

inject_intra_candidates() 注入帧内候选

md_stage_0() 快速评估

md_stage_1() 第一次全循环

md_staget_2() 第二次全循环

md_stage_3() 最终评估

块划分决策在svt_aom_mode_decision_sb() 中通过 update_d1_data()和update_d2_decision()完成

二 核心函数 svt_aom_mode_decision_sb 解析

复制代码
函数功能:遍历SuperBlock中所有需要处理的块,对每个块执行模式决策,然后输出该SB的最优模式分布/划分结果`
`处理流程:`
`对每个块,通过多个MD阶段选择最佳模式,随着阶段推进,精度提高但是候选模式数量减少`
`基于块成本,为父块选择最佳划分,如果存在NSQ形状`
`最后执行深度间决策,确定最终的划分结构`
`scs:` `序列控制集指针,包含序列级别的配置信息`
`pcs:图像控制集指针,包含当前图像的控制信息`
`ctx:` `模式决策上下文指针,包含当前SB的模式决策状态和结果`
`mdc_sb_data:` `SUperBlock的模式决策数据指针,包含需要处理的块列表和划分标志。`
`void` `svt_aom_mode_decision_sb(SequenceControlSet *scs, PictureCOntrolSet *pcs, ModeDecisionCOntext *ctx, `
`const MdcSbData *const mdc_sb_data)`
`{`
`//更新SuperBlock的邻居数组,用于帧内预测和上下文建模`
`//邻居数组存储了已经编码块的重建像素,供当前块参考使用`
    `update_neighbour_arrays(pcs, ctx);`
`//获取输入图像指针,从图像控制集中获取增强后的图像,可能经过预处理`
    `EbPictureBufferDesc*` `input_pic` `= pcs->ppcs->enhanced_pic;`    

`//如果需要16位图像,对输入图像进行填充处理,仅仅在SB级别执行一次`
`//高比特深度模式决策需要16位数据,即使编码器使用8位MD也可能需要16位图像用于后续处理`
    `if (ctx->hbd_md)` `{`
    `//如果使用高比特深度模式决策,将输入图像填充为16位格式并更新指针`
        `input_pic = pad_hbd_pictures(scs, pcs, ctx, input_pic);`
    `}` `else if (ctx->encoder_bit_depth > EB_EIGHT_BIT && ctx->bypass_encdec && ctx->pd_pass == PD_PASS_1)` `{`
    `//如果编码器比特深度大于8位,且使用8位MD但绕过编码解码过程,且处于PD_PASS_1阶段`
    `//虽然现在不需要16位图像,但是后续会需要,所以提前填充,但不改变input_pic指针`
        `pad_hbd_pictures(scs, pcs, ctx, input_pic);`
    `}`
    
    `//初始化用于跟踪块的变量`
    `//leaf_count,` `当前SB中需要处理的叶子块,最终划分块` `数量`
    `uint32_t` `leaf_count = mdc_sb_data->leaf_count;`
    `//leaf_data_array` `叶子块数据数组指针,包含每个块的位置,形状等信息`
    `const EbMdcLeafData` `*const leaf_data_array = mdc_sb_data->leaf_data_array;`
    `//md_early_exit_sq: 方形块早期退出标志,如果为1则跳过当前方形块的所有后续处理`
    `Bool` `md_early_exit_sq =` `0;/`
    `//next_non_skip_blk_idx_mds:` `下一个不跳过的块的MDS索引,用于早期退出优化`
    `uint32_t` `next_non_skip_blk_idx_mds = 0;`
    `//coded_area_sb` `SB中已经编码的亮度区域,像素数,用于统计和优化`
    `ctx->coded_area_sb` `= 0;`
    `//coded_area_sb_uv` `SB中已经编码的色度区域` `像素数,用于统计和优化`
    `ctx->coded_area_sb_uv` `= 0;`
    `//params_status` `参数状态标志,1表示需要重置参数,某些特性会修改设置`
    `ctx->params_status` `= 0;`
    `//copied_neigh_arrays` `是否已经复制邻居数组标志,用于判断是否需要恢复邻居数组`
    `ctx->copied_neigh_arrays` `= 0;`
    `//遍历所有标记为需要处理的块,叶子块`
    `for (uint32_t blk_idx = 0; blk_idx < leaf_count; blk_idx++)` `{`
    `//base_blk_idx_mds` `当前块的基础MDS索引,方形块的索引`
        `uint32_t` `base_blk_idx_mds = leaf_data_array[blk_idx].mds_idx;`
    `//leaf_data_ptr 当前叶子块的数组指针,包含该块的所有形状和配置信息`
    `const` `EbMdcLeafData` `*const leaf_data_ptr = &leaf_data_array[blk_idx];`
    `//blk_split_flag` `块划分标志,表示该块是否应该继续划分`
    `const uint8_t` `blk_split_flag = mdc_sb_data->split_flag[blk_idx];`
    `//获取当前块的集合信息,尺寸位置,形状等`
    `ctx->blk_geom` `= get_blk_geom_mds(base_blk_idx_mds);`
    `//获取当前块的模式决策数据结构指针,用于存储该块的决策结果`
    `ctx->blk_ptr = &ctx->md_blk_arr_nsq[base_blk_idx_mds];`
    
     `//重置设置,以防被前一个块覆盖`
     `//只有当使用会改变设置的特性时才需要重置设置`
      `if (ctx->params_status == 1)` `{`
      `//重新推导信号参数,这些参数可能被前一个块修改`
          `svt_aom_sig_deriv_enc_dec(scs, pcs, ctx);`
          `//重置参数状态标志`
          `ctx->params_status = 0;`
      `}`
      `//断言检查,基础块索引必须等于其几何信息的方形块索引`
      `//这确保我们处理的是正确的块`
      `//初始化块数据,设置块的初始化状态,QP,lambda等参数`
      `init_block_data(pcs, ctx, blk_split_flag, base_blk_idx_mds);`
      
      `//更新方形块的左侧和上方划分邻居信息`
      `//这些信息用于推导划分的码率成本,上下文建模`
      `update_part_neighs(ctx);`
      
      `//在高能量区域使用更保守的NSQ,设置`
      `//高能量区域通常包含复杂纹理,需要更仔细的划分决策`
      `if (!ctx->md_disallow_nsq_search && ctx->nsq_search_ctrls.high_energy_weight && ctx->detect_high_freq_ctrls.enabled)` `{`
      `//根据高能量检测结果更新NSQ搜索设置`
          `update_nsq_settings(pcs, ctx);`
      `}`
      `//检查当前深度的成本,如果大于父块成本,提前退出`
      `//如果只使用预测深度,pred_depth_only 不会跳过,所以不需要检查`
      `if (!ctx->pd_pass == PD_PASS_1 && ctx->pred_depth_only)` `{`
      `//比较当前深度和父深度的长呢改变呢,如果当前成本更高则设置早期退出`
      `check_curr_to_parent_cost(scs, pcs, ctx, &next_non_skip_blk_idx_mds, &md_early_exit_sq);    `
      `}`
    `//如果需要测试超过1个NSQ形状或将要划分,则将邻居数组复制到临时缓冲区以便后续重用`
    `//方形块,SQ不需要更新邻居数组,所以不需要复制`
    `//条件,形状数量` `> 2, 或2` `形状数量` `> 1且第一个形状不是方形,或3,当前块会继续划分`
    `const bool copy_neigh_arrays` `(leaf_data_array[blk_idx].tot_shapes > 2 ||                                    (leaf_data_array[blk_idx].tot_shapes > 1 &&                                     leaf_data_array[blk_idx].shapes[0] != PART_N)) ||            ctx->md_blk_arr_nsq[ctx->blk_geom->sqi_mds].split_flag;`
    
    `//遍历在当前深度设置的所有要测试的形状`
    `//shape_idx` `形状索引,从0开始遍历所有可能的形状`
    `//如果设置了早期退出标志,md_early_exit_sq, 则停止遍历`
    `}`
    `for (uint32_t shape_idx = 0; shape_idx < leaf_data_array[blk_idx].tot_shapes && !md_early_exit_sq;`
    `shape_idx++)` `{`
    `//shape,` `当前要测试的形状类型,PART_N` `方形,PART_H` `水平划分,PART_V` `垂直划分等`
    `Part` `shape = leaf_data_array[blk_idx].shapes[shape_idx];`
    `//shape_block_cnt` `该形状包含的子块数量,例如H划分有2个子块,SPLIT划分有4个子块`
    `uint8_t shape_block_cnt` `= num_ns_per_shape[shape];`
    `//blk_idx_mds, 计算当前形状中第一个子块的MDS索引`
    `//根据SB大小,128或64` `使用不同的偏移表`
    `uint32_t blk_idx_mds = base_blk_idx_mds` `+` `(ctx->blk_geom->sq_size == 128 ? ns_blk_offset_128_md[shape] : ns_blk_offset_md[shape]);`
    `//遍历该形状中的所有子块`
    `//nsi` `子块在该形状中的索引` `nsi = non-squar index`
    `//每次循环后,blk_idx_mds` `递增以指向下一个子块`
    `for (uint32_t nsi = 0; nsi < shape_block_cnt; nsi++, blk_idx_mds++)` `{`
    `//获取正在测试的形状中当前块的集合信息和块指针`
        `//blk_geom` `当前子块的几何信息,尺寸位置等`
        `ctx->blk_geom = get_blk_geom_mds(blk_idx_mds);`
        `//blk_ptr 当前子块的模式决策数据结构指针`
        `ctx->blk_ptr =` `&ctx->md_blk_arr_nsq[blk_idx_mds];`
    
        `//初始化当前子块的数据,设置初始状态,参数等`
        `init_block_data(pcs, ctx, blk_split_flag, blk_idx_mds);`
        `//处理当前块,执行完整的模式决策流程`
        `//包括,候选生成,多阶段评估,最佳模式选择等`
        `process_blok(pcs, ctx, leaf_data_ptr, input_pic);`
        `//md_early_exit_nsq`  `NSQ早期退出标志,初始化为0`
        `Bool md_early_exit_nsq = 0;`
        `//更新d1` `深度1` `数据,计算当前形状的成本,更新父块信息,决定是否提前退出`
        `//如果当前形状的成本已经足够好,可以跳过后续形状的测试`
        `update_d1_data(pcs, ctx, blk_idx_mds, &md_early_exit_nsq, shape_idx, copy_neigh_arrays);`
        `//如果设置了NSQ早期退出标志,跳出内层循环,不再测试该形状的其他子块`
        `if (md_early_exit_nsq) {`
            `break;`
        `}`
    `}`
    `}`
    
    `//现在已经检查完所有d1块,更新d2,深度2,既深度间信息`
    `//如果已经复制邻居数组且当前方形块会继续划分,需要恢复邻居数组`
    `//将临时缓冲区[1]中的内容恢复到` `0 中,因为最后一个NS块已经处理完成`
    `if (ctx->copied_neigh_arrays && ctx->md_blk_arr_nsq[ctx->blk_geom->sqi_mds].split_flag)`
        `svt_aom_copy_neighbour_arrays(` `//恢复,将1恢复到0,处理完最后一个NS块后`
        `pcs, ` `//图像控制集`
        `ctx, ` `///模式决策上下文`
        `1, ` `//源缓冲区索引,从1复制`
        `0, ` `//目标缓冲区索引,复制到0`
        `ctx->blk_geom->sqi_mds);` `/方形块的MDS索引`
    `//在最终d1块之后执行d2深度间决策`
    `//比较不同深度的成本,决定是否继续划分到更深的层次`
    `update_d2_decision(pcs, ctx);`
    `//如果启用了自身度跳过控制,且当前块可以进一步划分,且是有效块`
    `if (ctx->skip_sub_depth_ctrls.enable &&`
    `ctx->md_blk_aee_nsq[ctx->blk_geom->sqi_mds].split_flag &&` `//可以进一步划分`
    `ctx->avail_blk_flag[ctx->blk_geom->sqi_mds])` `{`
    `//有效块`
        `//如果块尺寸小于等于最大跳过尺寸,且满足跳过条件1`
        `//则设置下一个不跳过的块索引,并设置方形块早期退出标志`
        `if (ctx->blk_geom->sq_size <= ctx->skip_sub_depth_ctrls.max_size && eval_sub_depth_skip_cond1(ctx))` `{`
        `//计算下一个不跳过的块索引,跳过当前块的子块`
            `next_non_skip_blk_idx_mds = ctx->blk_geom->sqi_mds + ctx->blk_geom->ns_depth_offset;`
        `//设置早期退出标志,跳过当前方形块的后续处理`
            `md_early_exit_sq =` `1;`
        `}`
    `}`
    `/重置已经复制邻居数组标志,为下一个块做准备`
    `ctx->copied_neigh_arrays` `= 0;`
`}`
`
相关推荐
fantasy_arch5 天前
SVT-AV1 B帧决策和mini-GOP决策分析
算法·av1
fantasy_arch5 天前
AV1视频编码位于图像边界的超级块划分
计算机视觉·音视频·av1
fantasy_arch12 天前
SVT-AV1帧类型决策-场景切换检测
前端·网络·av1
fantasy_arch15 天前
pd_process.c 文件源码分析
c语言·数据库·视频编解码·av1
聊天QQ:4877392781 个月前
MATLAB环境下基于隐马尔可夫模型-高斯混合模型-期望最大化的图像分割算法 算法运行环境为M...
av1
咨询QQ688238861 个月前
No.1081 三菱PLC与组态王组态智能车库控制系统
av1
fatiaozhang95273 个月前
高安版_中兴B860AV3.2M_晶晨S905L3B_安卓9_兼容uwe5621ds无线-线刷固件包
android·电脑·电视盒子·av1·刷机固件
fantasy_arch4 个月前
SVT-AV1编码器中实现WPP依赖管理核心调度
java·前端·av1
fantasy_arch4 个月前
SVT-AV1 svt_aom_motion_estimation_kernel 函数分析
人工智能·算法·av1