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);`
`
相关推荐
罗西的思考17 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX18 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面18 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号18 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽19 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI19 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者20 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者20 小时前
【征程6】校准量化中HistogramObserver解析
算法
OuO-221 小时前
笔试强训 Day 34:ISBN 号码、kotori 和迷宫、矩阵最长递增路径
java·算法·矩阵
程序喵大人1 天前
【C++进阶】STL算法与函数对象 - 04 find、count和any_of把查询写成意图
开发语言·c++·算法