SVT-AV1 B帧决策和mini-GOP决策分析

一 函数主要调用

复制代码
//确定是否可以从预分配缓冲区释放图片`
`//满足以下任一条件时,可以开始处理预分配缓冲区中的图片`
`1 缓冲区中有帧内帧IDR或CRA`
`2 缓冲区中的帧数达到Mini-GOP的大小`
`3 到达序列结束EOS`
`4 预测结构时低延迟P或者低延迟B,低延迟模式立即处理`
`if (enc_ctx->pre_assignment_buffer_intra_count > 0 || //如果缓冲区中有帧内帧`
`(enc_ctx->pre_assignment_buffer_count == (1 << next_mg_hierarchical_levels)) || //或者缓冲区帧数达到mini-GOP大小`
`(enc_ctx->pre_assignment_buffer_eos_flag == TRUE) || /或到达序列结束`
`(pcs->pred_structure == SVT_AV1_pred_LOW_DELAY_P) //或是低延迟P结构`
`(pcs->pred_structure == SVT_AV1_PRED_LOW_DELAY_B) //或是低延迟B结构`
`)`
`{`
    `//一旦预分配缓冲区中有足够的帧,可以设置mini-GOP结构`
    `set_mini_gop_structure(scs, enc_ctx, pcs, ctx);//设置Mini_gop结构,确定每个mini-GOP的起始和结束索引`
    `//遍历所有mini-gop`
    `for (unsigned int mini_gop_index = 0; mini_gop_index < ctx->total_number_of_mini_gops; ++mini_gop_index) {`
    `//遍历每个mini-GOP`
        `bool pre_assignment_buffer_first_pass_flag = true;//标记这是预分配缓冲区的第一遍处理`
        `//获取mini-GOP中的第一个PCS`
`        pcs = (PictureParentControlSet*)enc_ctx->pre_assignment_buffer[ctx->mini_gop_start_index[mini_gop_index]]->object_ptr;  // 从预分配缓冲区中获取mini-GOP起始位置的PCS`
        
        `//计算当前mini-gop和上一个mini-gop之间的时间层差异`
        `pcs->hierarchical_layers_diff = (int32_t)enc_ctx->previous_mini_gop_hierarchical_levels - (int32_t)pcs->hierarchical_levels;  // 计算层次级别差值(用于检测mini-GOP切换)`
    `//如果mini-gop切换,设置init_pred_struct_position_flag为True`
    `pcs->init_pred_struct_position_flag = enc_ctx->is_mini_gop_changed = (pcs->hierarchical_layers_diff != 0);//如果层次几倍差值不为0,说明mini-gop切换了`
    `//跟踪最新实现的Mini-gop的层次级别数`
    `enc_ctx->previous_mini_gop_hierarchical_levels = ctx->mini_gop_hierarchical_levels[mini_gop_index];//更新上一个mini-gop的层级别数`
    `ctx->cut_short_ra_mg = 0;//重置随机访问mini-GOP被提前结束标志`
`}`
`}`
`

二 mini-GOP的决策

复制代码
1 set_mini_gop_structure`
`这是mini-gop决策的主函数,决定如何将预分配缓冲区中的图片组织成mini-gop`
`主要功能:`
`确定mini-gop的层次级别`
`确定mini-gop 的起始和结束索引`
`调用相关函数进行图片窗口分割`
`2 generate_picture_window_split `
`生成图片窗口分割,决定如何将图片分割成多个mini-gop`
`主要功能:`
`根据mini_gop_activity_array 决定哪些mini-gop配置可用`
`确定每个mini-gop的起始,结束索引和长度`
`确定每个mini-gop的层次级别`
`3 get_pred_struct_for_all_frames`
`为mini-gop中的所有帧获取预测结构`
`主要功能:`
`为每个帧分配预测结构指针`
`设置每个帧的层次级别`
`层次级别决定B帧的数量和位置。`
`4 update_pred_struct_and_pic_type`
`根据预测结构设置帧类型IPB`
`主要功能:`
`根据预测结构位置决定帧类型`
`层次级别越高,B帧越多`

`B帧决策机制`
`B帧决策不是单独的函数,而是通过层次结构,hierarchical levels`
`层次级别=0,只有P帧`
`层次级别=1, 少量B帧`
`层次级别=2-5:更多B帧,形成层次化B帧结构`
`svt_aom_picture_decision_kernel`
`->set_mini_gop_structure 决定mini-GOP结构`
`->initialize_mini_gop_activity_array 初始化mini-gop活动数组`
`->generate_picture_window_split 生成图片窗口分割`
`->handle_incomplete_picture_window_map 处理不完整的图片窗口`
`->get_pred_struct_for_all_frames 为所有帧获取预测结构`
`->update_pred_struct_and_pic_type 根据预测结构设置IPB类型`
`

三 核心决策函数 calc_mini_gop_activity

复制代码
功能:计算并决策mini-GOP的活动度,决定是使用6层,还是5层的mini-GOP结构`
`这个函数是动态mini-GOP决策核心函数,通过比较6层结构和5层结构的活动度,复杂度,运动向量等信息,来决定使用哪种结构,6层结构可以提供更好的压缩效率,需要更多的参考帧和更复杂的预测结构,5层结构更简单,压缩效率可能略低。`
`决策逻辑:`
`如果6层结构的活动度很高,两个5层子结构的活动度相似且较低,则倾向于使用6层结构,`
`如果6层结构的距离较大,而两个5层结构的距离较小,则倾向使用6层结构,如果两个5层结构都有较高的zoom` `out活动,则倾向于使用6层结构。`
`输入参数:`
`ctx` `图像决策上下文指针,包含mini-gop活动数组等状态信息`
`enc_ctx` `编码上下文指针,包含mini-gop计数,前一个mini-gop的层次级别等信息。`
`top_layer_idx` `顶层,在mini-gop活动数组中的索引`
`top_layer_dist` `顶层的平均距离,通过early` `HME计算得出`
`top_layer_perc_active` `顶层6层结构,的活动度百分比0-100,表示有运动向量的块的比例`
`top_layer_perc_cplx` `顶层` `6层结构,的复杂度百分比0-100,基于运动估计的失真`
`sub_layer_idx0` `第一个字层5层结构,在mini-gop活动数组中的索引`
`sub_layer_dist0` `第一个字层` `5层结构的平均距离distortion`

`sub_layer_perc_active` `第一个字层` `5层结构,的活动度百分比`
`sub_layer_perc_cplx` `第一个字层` `5层结构的复杂度百分比`
`sub_layer_idx1` `第二个字层` `5层结构,在mini-gop活动数组中的索引`
`sub_layer_dist1` `第二个子层` `5层结构的平均距离distortion`
`sub_layer1_perc_active` `第二个子层` `的活动度百分比`
`sub_layer1_perc_cplx` `第二个字层` `5层结构的负责度百分比`

`top_layer_mv_in_out_count` `顶层6层结构,的运动向量进出计算当前未使用`
`sub_layer_mv_in_out_count1` `第一个子层` `5层结构的运动向量进出计算,用于检测zoom` `out活动`
`sub_layer_mv_in_out_count2` `第二个子层5层结构的运动向量进出计算,用于检测zoom out活动`
`返回值`
`决策结果`
`如果满足条件,将top_layer_idx对应的活动数组项设置为TRUE` `使用6层结构`
`同时将sub_layer_idx0` `和sub_layer_idx1` `对应的活动更数组项设置为False`
`如果不满足条件,保持活动数组不变` `默认使用5层结构`

`static void` `calc_mini_gop_activity(`
 `PictureDecisionContext*` `ctx,` `//图像决策上下文指针,包含`
 `EncodeContext*` `enc_ctx,` `//编码上下文指针,包含Mini-GOP计数,前一个mini-gop的层次级别等信息`
 `uint64_t` `top_layer_idx,` `//顶层` `6层结构,在mini-gop活动数组中的索引`
 `uint64_t` `top_layer_dist;//顶层` `6层结构,的活动度百分比` `0` `-100,` `表示有运动向量的块的比例`
 `uint8_t` `top_layer_perc_active,` `//顶层` `6层结构,的活动度百分比0` `-` `100,表示有运动向量的块的比例`
 `uint8_t` `top_layer_perc_cplx,` `//顶层` `6层结构,的复杂度百分比0-100。基于运动估计的失真`
 `uint64_t` `sub_layer_idx0,` `//第一个子层在mini-gop活动数组中的索引`
 `uint8_t` `sub_layer0_perc_active,` `//第一个子层` `5层结构` `的活动度百分比`
 `uint8_t sub_layer0_perc_cplx,` `//第一个子层复杂度百分比`
 `uint64_t sub_layer_idx1, //第二个子层` `5层结构,在mini-gop活动数组中的索引`
 `uint64_t sub_layer_dist1,` `//第二个子层` `5层结构,的平均距离distortion`
 `uint8_t sub_layer1_perc_active, //第二个子层` `5层结构的活动度百分比`
 `uint8_t sub_layer1_perc_cplx,` `//第二个子层的复杂度百分比`
 `int16_t top_layer_mv_in_out_count, //顶层` `6层结构,的运动向量进出计数,当前未使用,用于未来扩展`
 `int16_t sub_layer_mv_in_out_count1,` `//第二个子层` `5层结构,的运动向量进出计数,用于检测zoom out,` `活动`
 `int16_t sub_layer_mv_in_out_count2` `//第二个子层` `5层结构,的运动向量进出计数,用于检测zoom out` `活动`
`)` `{`
    `top_layer_mv_in_out_count;//标记top_layer_mv_in_out_count` `参数当前未使用,避免编译器警告`
    `//计算偏置` `bias值,用于在比较6层和5层结构时给6层结构一定的优势`
    `//偏置的作用是模拟6层结构在码率方面的优势,因为6层结构可以提供更好的压缩率`
    `//偏置是前一个mini-gop结构的函数,目的是减少在同一个GOP内的切换`
    `//除非与前一个mini-GOP相比有显著变化,否则将保持6层结构`
    `//todo:` `使偏置成为预设的函数,预设越高,偏置越倾向于不使用6层结构`
`int bias` `= (enc_ctx->mini_gop_cnt_per_gop > 1 && enc_ctx->previous_mini_gop_hierarchical_levels == 5)` `? 25:75;`
    `//如果当前GOP中已经有多个mini-gop,且前一个mini-gop使用的5层结构,则偏置设为25` `更倾向于保持5层结构`
    `//否则偏置设为75,更倾向于使用6层结构,因为6层结构在码率方面有优势`
    
`//条件1,检查活动度的分布情况`
`//这个条件要求:`
`//1` `顶层` `6层结构的活动度很高` `>=` `95%,` `说明6层结构中有很多块运动`
`//2` `两个子层` `5层结构,的活动度不能差异太大,不能一个很高` `>= 95%,而另一个很低` `<` `75%`
`//这样可以确保两个5层子结构的活动度相似,避免因为活动度差异导致的不平衡。`
    `const bool` `cond1` `= top_layer_perc_active` `>=` `95 &&` `//顶层活动度必须很高` `>=` `95%`
    `!(sub_layer0_perc_active >= 95 && sub_layer1_perc_active < 75)` `&&` `//不能是,第一个子层很高` `>= 95%.` `而第二个子层很低`
    `!(sub_layer0_perc_active < 75 && sub_layer1_perc_active >= 95); //不能是,第一个子层很低` `< 75%,而第二个子层很高,` `>= 95%`    
`//这个条件的目的是确保两个5层子结构的活动度相似,如果差异太大,说明不适合使用6层结构`
`//条件2` `cond2,` `检查距离,复杂度和偏置的综合情况`
`//这个条件要求:`
`//1 顶层` `6层结构,的距离较大` `> LOW_DIST_TH` `= 512,说明6层结构的跨度较大`
`//2两个子层` `5层结构,的距离都较小` `< HIGH_DIST_TH` `= 4608` `说明子层内部的相关性较高`
`//3` `顶层有复杂度` ` >` `0 说明6层结构有一定的编码复杂度`
`//4 两个子层的复杂度都较低` `<` `25%,` `说明5层结构的编码复杂度较低`
`//5 两个子层的平均距离小于顶层结构在码率方面的优势,通过偏置来模拟`
`//这个条件考虑了6层结构在码率方面的优势,通过偏置来模拟`
`//如果 (sub_layer_dist0 + sub_layer_dist1) / 2 < (bias * top_layer_dist) / 100`
`//说明即使考虑了6层结构的码率优势,5层结构的平均距离仍然更小,因此倾向于使用6层结构`
`const bool` `cond2` `= top_layer_dist > LOW_DIST_TH` `&& //顶层距离必须较大` `> 512,说明6层结构宽度大`
    `sub_layer_dist0` `< HIGH_DIST_TH` `&& //第一个子层距离必须较小,`  `说明子层内部相关性高`
    `sub_layer_dist1 < HIGH_DIST_TH &&` `//第二个子层距离必须较小,说明子层内部相关性高`
    `top_layer_perc_cplx` `> 0 &&//顶层必须有复杂度,说明6层结构有一定的编码复杂度`
    `sub_layer0_perc_cplx` `< 25` `&& //第一个子层复杂度必须较低(< 25%),说明5层结构编码复杂度低`
    `sub_layer1_perc_cplx` `<` `25` `&&` `//第二个子层复杂度必须较低` `< 25%,说明5层结构编码复杂度低`
    `(sub_layer_dist0 + sub_layer_dist1)/2)` `<` `(bias * top_layer_dist)/100;` `//两个子层的平均距离必须小于顶层的距离乘以偏置百分比`
    `//这个条件买的目的是,如果6层结构的跨度大,但是两个5层子结构的内部相关性高且复杂度低`
    `//且即使考虑了6层结构的码率优势,5层结构的平均距离仍然较小,则倾向于使用6层结构`
    `//条件3,cond3,` `检查zoom` `out活动,运动向量向外的情况`
    `//这个条件要求:`
    `//1 两个子层的最小运动向量进出计算都较大` `> 40,` `说明两个子层都有较多的zoom` `out活动`
    `//2两个子层的最大运动向量进出计算也较大` `> 55, 进一步确认zoom out活动的存在`
    `//zoom out活动通常发生在场景中有物体远离相机或场景整体缩小的情形`
    `//这种情况下,使用6层结构更合适,因为6层结构可以更好处理这种大范围的运动`
    `const bool cond3 = MIN(sub_layer_mv_in_out_count1, sub_layer_mv_in_out_count2)` `>` `40` `&&` `//两个子层的最小运动向量进出计数必须较大` `> 40`
    `MAX(sub_layer_mv_in_out_count1, sub_layer_mv_in_out_count2)` `> 55;//两个子层的最大运行向量进出计数必须较大` `> 55`
    `//这个条件的目的是检测` `zoom out活动,如果两个子层都有较多的zoom out活动,则倾向于使用6层结构`
    `//最终决策:如果满足条件1,并且满足条件2或者条件3中的任意一个`
    `if (convd1 && (cond2 || cond3))` `{`
    `/如果满足条件1,并且满足条件2或者条件3中的任意一个`
        `/将顶层` `层结构,的活动数组设置为TRUE,表示使用6层结构`
        `ctx->mini_gop_activity_array[top_layer_idx]` `= TRUE;`
        `//将第一个子层` `层结构` `的活动数组项设置为FALSE,表示不使用5层结构`
        `ctx->mini_gop_activity_array[sub_layer_idx0]` `=`  `FALSE;`
        `//将第二层` `层5结构,的活动数组项设置为FALSE,表示不使用5层结构`
        `ctx->mini_gop_activity_array[sub_layer_idx1]` `= FALSE;`
    `}`
    `//如果条件不满足,则保持活动数组不变,默认使用5层结构因为5层结构在活动数组中的初始值可能是TRUE`
`}`
`

四 调用关系

复制代码
eval_sub_mini_gop()`
`->early_hme()二分计算cost和统计信息`
`calc_mini_gop_activity()->根据上面计算的结果,判断minigo结构`

`eval_sub_mini_gop(//二分判断`
`            ctx,`
`            enc_ctx,`
`            L6_INDEX,`
`            L5_0_INDEX,`
`            L5_1_INDEX,`
`            start_pcs,`
`            mid_pcs,`
`            end_pcs);`
`
相关推荐
声声codeGrandMaster2 小时前
逻辑回归-泰坦尼克号
算法·机器学习·逻辑回归
mu_guang_2 小时前
算法图解2-选择排序
数据结构·算法·排序算法
fantasy_arch3 小时前
AV1视频编码位于图像边界的超级块划分
计算机视觉·音视频·av1
xiaowu0803 小时前
IEnumerable、IEnumerator接口与yield return关键字的相关知识
java·开发语言·算法
报错小能手3 小时前
数据结构 b+树
数据结构·b树·算法
元亓亓亓3 小时前
LeetCode热题100--64. 最小路径和--中等
算法·leetcode·职场和发展
mit6.8243 小时前
回溯+位运算|前缀和优化背包
算法
努力学算法的蒟蒻3 小时前
day49(12.30)——leetcode面试经典150
算法·leetcode·面试
天赐学c语言3 小时前
12.30 - 合并区间 && C++中class和C语言中struct的区别
c语言·c++·算法·leecode