libaom 源码分析:运动搜索过程和 pattern_search 函数
AV1
AV1(AOMedia Video 1)是一种开源、免专利费的视频编码格式,旨在提高视频压缩效率和传输效果。它由开放媒体联盟(AOMedia)开发,该联盟包括谷歌、微软、亚马逊、苹果和Netflix等大型科技公司。AV1特别适合用于互联网视频流媒体传输,因为它相较于旧格式(如H.264或VP9)提供了更高的压缩效率,可以在更低的比特率下提供更高质量的视频。
AV1的主要特点:
- 更优的压缩效率:AV1在压缩效率上比H.264高达50%,比VP9高约30%。
- 免专利费:与H.265/HEVC不同,AV1无需支付许可费用,适合开发者和平台使用。
- 开源:其规范和实现公开,促进了广泛的采用。
- 广泛支持:许多现代浏览器、流媒体平台(如YouTube、Netflix)和硬件设备都已支持AV1。
- 面向未来:AV1专为高分辨率内容(如4K、8K、HDR)设计,具有强大的未来适应性。
应用领域:
- 流媒体传输:YouTube和Netflix等平台使用AV1进行高质量视频流媒体传输。
- 视频会议:由于带宽需求较低,AV1非常适合用于实时视频通话。
- 云游戏:AV1可通过提供高质量图像并降低延迟,优化云游戏体验。
如果您想了解更多关于AV1的技术细节、编码工具或具体应用,请告诉我!
libaom
Libaom指的是开源的 AV1 视频编码库和实现。AV1 是一种高效的视频编码格式,由开放媒体联盟(AOMedia)开发,旨在提供比以前的编码格式(如 VP9、HEVC(H.265)和 AVC(H.264))更高的压缩效率。它特别优化用于流媒体、网页视频、视频会议等应用。
Libaom是 AV1 的参考编码器和解码器,由 AOMedia 项目开发和维护。它被内容提供商和开发人员广泛使用,特别是那些需要将 AV1 视频编码和解码集成到其软件中的开发者,尤其是寻求通过降低带宽要求来提供高质量视频的流媒体平台。
Libaom 和 AV1 的一些特点包括:
- 更好的压缩:AV1 通常比 HEVC(H.265)提供约 30% 更好的压缩效率,在相同质量下可以减少带宽消耗,这使其成为降低数据使用量的有吸引力的选择。
- 免版权费:与 HEVC 不同,AV1 设计为免版权费,促进了它在各种应用中的采用。
- 开源:Libaom 库是开源的,这意味着开发者可以自由访问和修改代码以满足他们的需求。
Libaom 主要用于软件编码场景,由于其更复杂的算法,它的性能可能比某些硬件编码器慢。然而,凭借其高效性和潜在的成本节省,它在视频流媒体服务中越来越受欢迎。
libaom 运动搜索过程
- 流程框图:
- 主要搜索方法:FAST_BIGDIA、VFAST_DIAMOND、FAST_DIAMOND、FAST_HEX、HEX、SQUARE、BIGDIA
- 快速搜索方法与普通搜索方法区别:从源码调用看,区别在是否开启了初始搜索,即在快搜索类中关闭了 do_init_search 开关,而在普通搜索里开启了do_init_search 开关;
- 核心函数:patter_search 函数,根据不同的模版和条件进行不同的像素搜索,计算代价,选择最合适的候选点。
- pattern_search 源码详细分析:
// Generic pattern search function that searches over multiple scales.// Each scale can have a different number of candidates and shape of// candidates as indicated in the num_candidates and candidates arrays// passed into this function//用于在不同的搜索尺度和候选点形状下,搜索最佳整数像素运动矢量(Motion Vector,MV)// best_mv: 返回的最佳运动矢量// best_mv_stats: 返回的最佳矢量统计信息staticintpattern_search(FULLPEL_MV start_mv,constFULLPEL_MOTION_SEARCH_PARAMS*ms_params,intsearch_step,constintdo_init_search,int*cost_list,FULLPEL_MV*best_mv,FULLPEL_MV_STATS*best_mv_stats){//搜索步长集合staticconstintsearch_steps[MAX_MVSEARCH_STEPS]={10,9,8,7,6,5,4,3,2,1,0,};inti,s,t;conststructbuf_2d*constsrc=ms_params->ms_buffers.src;conststructbuf_2d*constref=ms_params->ms_buffers.ref;constsearch_site_config*search_sites=ms_params->search_sites;constint*num_candidates=search_sites->searches_per_step;constintref_stride=ref->stride;constintlast_is_4=num_candidates[0]==4;intbr,bc;unsignedintbestsad=UINT_MAX,raw_bestsad=UINT_MAX;intk=-1;constMV_COST_PARAMS*mv_cost_params=&ms_params->mv_cost_params;search_step=AOMMIN(search_step,MAX_MVSEARCH_STEPS-1);//获取初始搜索步长assert(search_step>=0);intbest_init_s=search_steps[search_step];// adjust ref_mv to make sure it is within MV range 确保初始 mv 再 mv 限制范围内clamp_fullmv(&start_mv,&ms_params->mv_limits);br=start_mv.row;bc=start_mv.col;//初始化成本列表if(cost_list!=NULL){cost_list[0]=cost_list[1]=cost_list[2]=cost_list[3]=cost_list[4]=INT_MAX;}intcostlist_has_sad=0;// Work out the start point for the search// 计算初始 mv 的 SAD 和误差代价,作为当前最优值的起点raw_bestsad=get_mvpred_sad(ms_params,src,get_buf_from_fullmv(ref,&start_mv),ref_stride);bestsad=raw_bestsad+mvsad_err_cost_(&start_mv,mv_cost_params);// Search all possible scales up to the search param around the center point// pick the scale of the point that is best as the starting scale of// further steps around it.//初始搜索,外层参数可选配置constuint8_t*center_address=get_buf_from_fullmv(ref,&start_mv);//指向参考帧中对应于初始运动矢量 (start_mv) 的像素位置if(do_init_search){s=best_init_s;best_init_s=-1;//遍历不同的搜索步长for(t=0;t<=s;++t){intbest_site=-1;FULLPEL_MV center_mv={br,bc};//确定运动矢量中心if(check_bounds(&ms_params->mv_limits,br,bc,1<<t)){// Call 4-point sad for multiples of 4 candidates.constintno_of_4_cand_loops=num_candidates[t]>>2;//将候选点分成每组 4 个的组//在每个步长上候选点按照 4 个为一组,计算 sad 并更新最佳 mvfor(i=0;i<no_of_4_cand_loops;i++){calc_sad4_update_bestmv(ms_params,mv_cost_params,best_mv,center_mv,center_address,&bestsad,&raw_bestsad,t,&best_site,i*4,/*cost_list=*/NULL);//一次计算 4 个候选点的 SAD}// Rest of the candidates 剩余的候选点逐一计算 sad 并更新最佳 mvconstintremaining_cand=num_candidates[t]%4;//不能整除 4 的剩余候选点calc_sad_update_bestmv(ms_params,mv_cost_params,best_mv,center_mv,center_address,&bestsad,&raw_bestsad,t,&best_site,remaining_cand,no_of_4_cand_loops*4,NULL);}else{//边界以外的候选点,逐个处理calc_sad_update_bestmv(ms_params,mv_cost_params,best_mv,center_mv,center_address,&bestsad,&raw_bestsad,t,&best_site,num_candidates[t],0,NULL);}//选择最佳位置if(best_site==-1){continue;}else{best_init_s=t;k=best_site;}}//更新最佳位置的运动矢量信息if(best_init_s!=-1){br+=search_sites->site[best_init_s][k].mv.row;bc+=search_sites->site[best_init_s][k].mv.col;center_address+=search_sites->site[best_init_s][k].offset;}}// If the center point is still the best, just skip this and move to// the refinement step.//如果中心点仍然是最佳的,跳过这步骤,移动到精细化步骤if(best_init_s!=-1){constintlast_s=(last_is_4&&cost_list!=NULL);//布尔值表达式,目的是决定精细化搜索的最低层次是否需要进一步处理intbest_site=-1;s=best_init_s;//如果 best_init_s 是有效值,算法将从该层开始向更细的层次逐步搜索//逐层优化,从粗到细,last_s 为 1 就循环 2 次,为 0 就循环 1 次for(;s>=last_s;s--){// No need to search all points the 1st time if initial search was usedif(!do_init_search||s!=best_init_s){FULLPEL_MV center_mv={br,bc};//边界检查,检查候选店是否在允许的范围内if(check_bounds(&ms_params->mv_limits,br,bc,1<<s)){// Call 4-point sad for multiples of 4 candidates.//对候选点进行批量搜索constintno_of_4_cand_loops=num_candidates[s]>>2;for(i=0;i<no_of_4_cand_loops;i++){calc_sad4_update_bestmv(ms_params,mv_cost_params,best_mv,center_mv,center_address,&bestsad,&raw_bestsad,s,&best_site,i*4,/*cost_list=*/NULL);}// Rest of the candidates 剩余候选点处理constintremaining_cand=num_candidates[s]%4;calc_sad_update_bestmv(ms_params,mv_cost_params,best_mv,center_mv,center_address,&bestsad,&raw_bestsad,s,&best_site,remaining_cand,no_of_4_cand_loops*4,NULL);}else{//候选点未通过边界检查,直接处理所有候选点calc_sad_update_bestmv(ms_params,mv_cost_params,best_mv,center_mv,center_address,&bestsad,&raw_bestsad,s,&best_site,num_candidates[s],0,NULL);}//更新最佳候选点,找到最佳候选点 mv 和中心地址if(best_site==-1){continue;}else{br+=search_sites->site[s][best_site].mv.row;bc+=search_sites->site[s][best_site].mv.col;center_address+=search_sites->site[s][best_site].offset;k=best_site;}}//精细化处理do{//初始化检查点索引intnext_chkpts_indices[PATTERN_CANDIDATES_REF];best_site=-1;next_chkpts_indices[0]=(k==0)?num_candidates[s]-1:k-1;next_chkpts_indices[1]=k;next_chkpts_indices[2]=(k==num_candidates[s]-1)?0:k+1;FULLPEL_MV center_mv={br,bc};//边界检查if(check_bounds(&ms_params->mv_limits,br,bc,1<<s)){//对 3 个候选点进行 SAD 计算,并更新最佳候选点calc_sad3_update_bestmv(ms_params,mv_cost_params,best_mv,center_mv,center_address,&bestsad,&raw_bestsad,s,&best_site,next_chkpts_indices,NULL);}else{//边界外的候选点采用备用的方式更新最佳点calc_sad_update_bestmv_with_indices(ms_params,mv_cost_params,best_mv,center_mv,center_address,&bestsad,&raw_bestsad,s,&best_site,PATTERN_CANDIDATES_REF,next_chkpts_indices,NULL);}//更新最佳候选点if(best_site!=-1){k=next_chkpts_indices[best_site];br+=search_sites->site[s][k].mv.row;bc+=search_sites->site[s][k].mv.col;center_address+=search_sites->site[s][k].offset;}}while(best_site!=-1);//直到没有更优的候选点退出循环}// Note: If we enter the if below, then cost_list must be non-NULL.//利用 cost_list 和相邻点优化搜索效率if(s==0){cost_list[0]=raw_bestsad;costlist_has_sad=1;assert(num_candidates[s]==4);if(!do_init_search||s!=best_init_s){FULLPEL_MV center_mv={br,bc};//检查边界与候选点更新if(check_bounds(&ms_params->mv_limits,br,bc,1<<s)){calc_sad4_update_bestmv(ms_params,mv_cost_params,best_mv,center_mv,center_address,&bestsad,&raw_bestsad,s,&best_site,0,cost_list);}else{calc_sad_update_bestmv(ms_params,mv_cost_params,best_mv,center_mv,center_address,&bestsad,&raw_bestsad,s,&best_site,/*num_candidates=*/4,/*cand_start=*/0,cost_list);}// 更新最佳运动矢量与中心地址if(best_site!=-1){br+=search_sites->site[s][best_site].mv.row;bc+=search_sites->site[s][best_site].mv.col;center_address+=search_sites->site[s][best_site].offset;k=best_site;}}//循环处理相邻候选点while(best_site!=-1){intnext_chkpts_indices[PATTERN_CANDIDATES_REF];best_site=-1;next_chkpts_indices[0]=(k==0)?num_candidates[s]-1:k-1;next_chkpts_indices[1]=k;next_chkpts_indices[2]=(k==num_candidates[s]-1)?0:k+1;cost_list[1]=cost_list[2]=cost_list[3]=cost_list[4]=INT_MAX;cost_list[((k+2)%4)+1]=cost_list[0];cost_list[0]=raw_bestsad;FULLPEL_MV center_mv={br,bc};//处理 3 个候选点的 sad 值并更新最佳候选点,超过边界则采用备用方式更新if(check_bounds(&ms_params->mv_limits,br,bc,1<<s)){assert(PATTERN_CANDIDATES_REF==3);calc_sad3_update_bestmv(ms_params,mv_cost_params,best_mv,center_mv,center_address,&bestsad,&raw_bestsad,s,&best_site,next_chkpts_indices,cost_list);}else{calc_sad_update_bestmv_with_indices(ms_params,mv_cost_params,best_mv,center_mv,center_address,&bestsad,&raw_bestsad,s,&best_site,PATTERN_CANDIDATES_REF,next_chkpts_indices,cost_list);}//更新最佳 mv 信息if(best_site!=-1){k=next_chkpts_indices[best_site];br+=search_sites->site[s][k].mv.row;bc+=search_sites->site[s][k].mv.col;center_address+=search_sites->site[s][k].offset;}}}}//更新最佳运动矢量best_mv->row=br;best_mv->col=bc;//同步性检查,检查当前的 center_address 是否与 best_mv 计算得到的内存地址一致assert(center_address==get_buf_from_fullmv(ref,best_mv)&&"center address is out of sync with best_mv!\n");// Returns the one-away integer pel cost/sad around the best as follows:// cost_list[0]: cost/sad at the best integer pel 最佳整像素点的 cost/sad// cost_list[1]: cost/sad at delta {0, -1} (left) from the best integer pel //左侧// cost_list[2]: cost/sad at delta { 1, 0} (bottom) from the best integer pel //底部// cost_list[3]: cost/sad at delta { 0, 1} (right) from the best integer pel //右边// cost_list[4]: cost/sad at delta {-1, 0} (top) from the best integer pel //顶部if(cost_list){if(USE_SAD_COSTLIST){calc_int_sad_list(*best_mv,ms_params,cost_list,costlist_has_sad);//计算 SAD 列表}else{calc_int_cost_list(*best_mv,ms_params,cost_list);//计算综合成本列表}}//获取最佳矢量的预测方差成本constintvar_cost=get_mvpred_var_cost(ms_params,best_mv,best_mv_stats);//返回最佳预测方差成本returnvar_cost;}