SVT-AV1 svt_aom_motion_estimation_kernel 函数分析

void *svt_aom_motion_estimation_kernel(void *input_ptr) // 运动估计内核主函数,接收线程输入参数

{

// 从输入参数中获取线程上下文指针

EbThreadContext * thread_ctx = (EbThreadContext *)input_ptr;

// 从线程上下文中获取运动估计上下文指针

MotionEstimationContext_t *me_context_ptr = (MotionEstimationContext_t *)thread_ctx->priv;

// 输入结果包装器指针,用于接收来自上游的数据

EbObjectWrapper * in_results_wrapper_ptr;

// 输出结果包装器指针,用于向下游传递处理结果

EbObjectWrapper * out_results_wrapper;

for (;;) { // 无限循环,持续处理输入的图片数据

// 从输入FIFO队列中获取完整的图片决策结果对象

EB_GET_FULL_OBJECT(me_context_ptr->picture_decision_results_input_fifo_ptr,

&in_results_wrapper_ptr);

// 从包装器中提取图片决策结果指针

PictureDecisionResults *in_results_ptr = (PictureDecisionResults *)

in_results_wrapper_ptr->object_ptr;

// 从输入结果中获取图片父控制集指针,包含当前图片的所有控制信息

PictureParentControlSet *pcs = (PictureParentControlSet *)

in_results_ptr->pcs_wrapper->object_ptr;

// 获取序列控制集指针,包含编码序列的全局配置参数

SequenceControlSet *scs = pcs->scs;

// 根据输入任务类型设置相应的运动估计类型

if (in_results_ptr->task_type == TASK_TFME)

// 如果是时域滤波运动估计任务,设置为MCTF模式

me_context_ptr->me_ctx->me_type = ME_MCTF;

else if (in_results_ptr->task_type == TASK_PAME || in_results_ptr->task_type == TASK_SUPERRES_RE_ME)

// 如果是预分析运动估计或超分辨率重新运动估计任务,设置为开环模式

me_context_ptr->me_ctx->me_type = ME_OPEN_LOOP;

else if (in_results_ptr->task_type == TASK_DG_DETECTOR_HME)

// 如果是动态GOP检测分层运动估计任务,设置为检测器模式

me_context_ptr->me_ctx->me_type = ME_DG_DETECTOR;

// 运动估计内核信号推导:根据任务类型设置具体的ME参数

if ((in_results_ptr->task_type == TASK_PAME) ||

(in_results_ptr->task_type == TASK_SUPERRES_RE_ME))

// 对于预分析或超分辨率重新ME任务,推导标准运动估计信号

svt_aom_sig_deriv_me(scs, pcs, me_context_ptr->me_ctx);

else if (in_results_ptr->task_type == TASK_TFME)

// 对于时域滤波任务,推导特定的时域滤波运动估计信号

svt_aom_sig_deriv_me_tf(pcs, me_context_ptr->me_ctx);

// 处理预分析运动估计和超分辨率重新运动估计任务

if ((in_results_ptr->task_type == TASK_PAME) ||

(in_results_ptr->task_type == TASK_SUPERRES_RE_ME)) {

// 1/16分辨率图片缓冲区指针,用于粗粒度运动搜索

EbPictureBufferDesc *sixteenth_picture_ptr;

// 1/4分辨率图片缓冲区指针,用于中等粒度运动搜索

EbPictureBufferDesc *quarter_picture_ptr;

// 填充后的输入图片缓冲区指针,用于边界处理

EbPictureBufferDesc *input_padded_pic;

// 原始输入图片缓冲区指针

EbPictureBufferDesc *input_pic;

// 预分析引用对象指针,包含各种分辨率的图片数据

EbPaReferenceObject *pa_ref_obj_;

// 断言检查引用图片的生命周期计数 (调试版本使用)

//assert((int)pcs->pa_ref_pic_wrapper->live_count > 0);

// 从图片控制集中获取预分析引用对象

pa_ref_obj_ = (EbPaReferenceObject *)

pcs->pa_ref_pic_wrapper->object_ptr;

// 设置1/4和1/16分辨率的ME输入缓冲区,已经过滤或抽取

// 获取1/4分辨率下采样图片指针,用于分层运动估计的中层

quarter_picture_ptr = (EbPictureBufferDesc *)

pa_ref_obj_->quarter_downsampled_picture_ptr;

// 获取1/16分辨率下采样图片指针,用于分层运动估计的最粗层

sixteenth_picture_ptr = (EbPictureBufferDesc *)

pa_ref_obj_->sixteenth_downsampled_picture_ptr;

// 获取填充后的输入图片,确保边界像素可用于运动搜索

input_padded_pic = (EbPictureBufferDesc *)pa_ref_obj_->input_padded_pic;

// 获取增强后的输入图片,通常是经过预处理的原始图片

input_pic = pcs->enhanced_pic;

// 图片分段处理:将图片划分为多个段以支持并行处理

// 获取当前处理的段索引

uint32_t segment_index = in_results_ptr->segment_index;

// 计算图片宽度包含的64x64块数量(向上取整)

uint32_t pic_width_in_b64 = (pcs->aligned_width + scs->b64_size - 1) / scs->b64_size;

// 计算图片高度包含的64x64块数量(向上取整)

uint32_t picture_height_in_b64 = (pcs->aligned_height + scs->b64_size - 1) / scs->b64_size;

// Y方向段索引

uint32_t y_segment_index;

// X方向段索引

uint32_t x_segment_index;

// 将一维段索引转换为二维坐标(x,y)

SEGMENT_CONVERT_IDX_TO_XY(segment_index, x_segment_index, y_segment_index, pcs->me_segments_column_count);

// 计算当前段在X方向的起始64x64块索引

uint32_t x_b64_start_index = SEGMENT_START_IDX(x_segment_index, pic_width_in_b64, pcs->me_segments_column_count);

// 计算当前段在X方向的结束64x64块索引

uint32_t x_b64_end_index = SEGMENT_END_IDX(x_segment_index, pic_width_in_b64, pcs->me_segments_column_count);

// 计算当前段在Y方向的起始64x64块索引

uint32_t y_b64_start_index = SEGMENT_START_IDX(y_segment_index, picture_height_in_b64, pcs->me_segments_row_count);

// 计算当前段在Y方向的结束64x64块索引

uint32_t y_b64_end_index = SEGMENT_END_IDX(y_segment_index, picture_height_in_b64, pcs->me_segments_row_count);

// 初始化运动估计跳过标志

Bool skip_me = FALSE;

// 检查当前图片是否应该跳过运动估计(例如在多遍编码中)

if (svt_aom_is_pic_skipped(pcs)) //判断图片是否被跳过

skip_me = TRUE;

// 为第一遍编码跳过ME,因为ME已经执行过了

if (!skip_me) {

// 只对非I帧执行运动估计(I帧只有帧内预测)

if (pcs->slice_type != I_SLICE) {

// 如果引用图片的分辨率与输入不同,使用缩放的源引用

svt_aom_use_scaled_source_refs_if_needed(pcs,

input_pic,

pa_ref_obj_,

&input_padded_pic,

&quarter_picture_ptr,

&sixteenth_picture_ptr);

// 64x64块级运动估计循环:遍历当前段内的所有64x64块

// Y方向遍历当前段内的64x64块

for (uint32_t y_b64_index = y_b64_start_index; y_b64_index < y_b64_end_index; ++y_b64_index) {

// X方向遍历当前段内的64x64块

for (uint32_t x_b64_index = x_b64_start_index; x_b64_index < x_b64_end_index; ++x_b64_index) {

// 计算当前64x64块在整个图片中的线性索引

uint32_t b64_index = (uint16_t)(x_b64_index + y_b64_index * pic_width_in_b64);

// 计算当前64x64块在图片中的X坐标起始位置(像素单位)

uint32_t b64_origin_x = x_b64_index * scs->b64_size;

// 计算当前64x64块在图片中的Y坐标起始位置(像素单位)

uint32_t b64_origin_y = y_b64_index * scs->b64_size;

// 从输入图片加载64x64块到中间缓冲区

// 计算当前64x64块在输入图片缓冲区中的起始位置索引

uint32_t buffer_index = (input_pic->org_y + b64_origin_y) * input_pic->stride_y +

input_pic->org_x + b64_origin_x;

#ifdef ARCH_X86_64

// 获取当前64x64块在填充图片中的起始指针

uint8_t *src_ptr = &input_padded_pic->buffer_y[buffer_index];

// 计算实际的块高度(处理图片边界情况)

uint32_t b64_height = (pcs->aligned_height - b64_origin_y) < BLOCK_SIZE_64

? pcs->aligned_height - b64_origin_y : BLOCK_SIZE_64;

// 数据预取优化:提前加载数据到CPU缓存以提高访问速度

// 预取提示级别:_MM_HINT_T0(L1), _MM_HINT_T1(L2), _MM_HINT_T2(L3), _MM_HINT_NTA(非时间局部性)

for (uint32_t i = 0; i < b64_height; i++) {

// 计算每行数据的地址并预取到L3缓存

char const *p = (char const *)(src_ptr + i * input_padded_pic->stride_y);

_mm_prefetch(p, _MM_HINT_T2);

}

#endif

// 设置运动估计上下文中的64x64块源数据指针

me_context_ptr->me_ctx->b64_src_ptr = &input_padded_pic->buffer_y[buffer_index];

// 设置源数据的行步长(stride)

me_context_ptr->me_ctx->b64_src_stride = input_padded_pic->stride_y;

// 加载1/4分辨率抽取的超级块到1/4中间超级块缓冲区

if (me_context_ptr->me_ctx->enable_hme_level1_flag) {

// 计算1/4分辨率图片中对应位置的缓冲区索引(坐标右移1位即除以2)

buffer_index = (quarter_picture_ptr->org_y + (b64_origin_y >> 1)) * quarter_picture_ptr->stride_y +

quarter_picture_ptr->org_x + (b64_origin_x >> 1);

// 设置1/4分辨率64x64块缓冲区指针

me_context_ptr->me_ctx->quarter_b64_buffer = &quarter_picture_ptr->buffer_y[buffer_index];

// 设置1/4分辨率缓冲区的行步长

me_context_ptr->me_ctx->quarter_b64_buffer_stride = quarter_picture_ptr->stride_y;

}

// 加载1/16分辨率抽取的超级块到1/16中间超级块缓冲区

if (me_context_ptr->me_ctx->enable_hme_level0_flag) {

// 计算1/16分辨率图片中对应位置的缓冲区索引(坐标右移2位即除以4)

buffer_index = (sixteenth_picture_ptr->org_y + (b64_origin_y >> 2)) * sixteenth_picture_ptr->stride_y +

sixteenth_picture_ptr->org_x + (b64_origin_x >> 2);

// 设置1/16分辨率64x64块缓冲区指针

me_context_ptr->me_ctx->sixteenth_b64_buffer = &sixteenth_picture_ptr->buffer_y[buffer_index];

// 设置1/16分辨率缓冲区的行步长

me_context_ptr->me_ctx->sixteenth_b64_buffer_stride = sixteenth_picture_ptr->stride_y;

}

// 设置运动估计类型为开环模式

me_context_ptr->me_ctx->me_type = ME_OPEN_LOOP;

// 配置运动估计搜索参数(仅适用于PAME和超分辨率重新ME任务)

if ((in_results_ptr->task_type == TASK_PAME) || (in_results_ptr->task_type == TASK_SUPERRES_RE_ME)) {

// 设置要搜索的引用列表数量:P帧只搜索List0,B帧搜索List0+List1

me_context_ptr->me_ctx->num_of_list_to_search =

(pcs->slice_type == P_SLICE) ? 1 /*只搜索List 0*/

: 2 /*搜索List 0 + 1*/;

// 设置List0中要搜索的引用图片数量

me_context_ptr->me_ctx->num_of_ref_pic_to_search[0] = pcs->ref_list0_count_try;

// 对于B帧,设置List1中要搜索的引用图片数量

if (pcs->slice_type == B_SLICE)

me_context_ptr->me_ctx->num_of_ref_pic_to_search[1] = pcs->ref_list1_count_try;

// 设置当前图片的时域层索引

me_context_ptr->me_ctx->temporal_layer_index = pcs->temporal_layer_index;

// 设置当前图片是否为引用图片标志

me_context_ptr->me_ctx->is_ref = pcs->is_ref;

// 处理帧缩放(超分辨率或调整大小)情况下的引用图片配置

if (pcs->frame_superres_enabled || pcs->frame_resize_enabled) {

// 遍历所有引用列表(List0和List1)

for (int i = 0; i < me_context_ptr->me_ctx->num_of_list_to_search; i++) {

// 遍历当前列表中的所有引用图片

for (int j = 0; j < me_context_ptr->me_ctx->num_of_ref_pic_to_search[i]; j++) {

// 断言检查引用图片的生命周期计数(调试版本)

//assert((int)pcs->ref_pa_pic_ptr_array[i][j]->live_count > 0);

// 获取超分辨率分母索引

uint8_t sr_denom_idx = svt_aom_get_denom_idx(pcs->superres_denom);

// 获取调整大小分母索引

uint8_t resize_denom_idx = svt_aom_get_denom_idx(pcs->resize_denom);

// 获取引用对象指针

EbPaReferenceObject *ref_object =

(EbPaReferenceObject *)pcs->ref_pa_pic_ptr_array[i][j]->object_ptr;

// 设置缩放后的全分辨率引用图片指针

me_context_ptr->me_ctx->me_ds_ref_array[i][j].picture_ptr =

ref_object->downscaled_input_padded_picture_ptr[sr_denom_idx][resize_denom_idx];

// 设置缩放后的1/4分辨率引用图片指针

me_context_ptr->me_ctx->me_ds_ref_array[i][j].quarter_picture_ptr =

ref_object->downscaled_quarter_downsampled_picture_ptr[sr_denom_idx][resize_denom_idx];

// 设置缩放后的1/16分辨率引用图片指针

me_context_ptr->me_ctx->me_ds_ref_array[i][j].sixteenth_picture_ptr =

ref_object->downscaled_sixteenth_downsampled_picture_ptr[sr_denom_idx][resize_denom_idx];

// 设置引用图片的编号

me_context_ptr->me_ctx->me_ds_ref_array[i][j].picture_number =

ref_object->picture_number;

}

}

} else {

// 处理正常情况(无帧缩放)下的引用图片配置

// 遍历所有引用列表(List0和List1)

for (int i = 0; i < me_context_ptr->me_ctx->num_of_list_to_search; i++) {

// 遍历当前列表中的所有引用图片

for (int j = 0; j < me_context_ptr->me_ctx->num_of_ref_pic_to_search[i]; j++) {

// 断言检查引用图片的生命周期计数(调试版本)

//assert((int)pcs->ref_pa_pic_ptr_array[i][j]->live_count > 0);

// 获取引用对象指针

EbPaReferenceObject *ref_object =

(EbPaReferenceObject *)pcs->ref_pa_pic_ptr_array[i][j]->object_ptr;

// 设置原始全分辨率引用图片指针

me_context_ptr->me_ctx->me_ds_ref_array[i][j].picture_ptr =

ref_object->input_padded_pic;

// 设置原始1/4分辨率引用图片指针

me_context_ptr->me_ctx->me_ds_ref_array[i][j].quarter_picture_ptr =

ref_object->quarter_downsampled_picture_ptr;

// 设置原始1/16分辨率引用图片指针

me_context_ptr->me_ctx->me_ds_ref_array[i][j].sixteenth_picture_ptr =

ref_object->sixteenth_downsampled_picture_ptr;

// 设置引用图片的编号

me_context_ptr->me_ctx->me_ds_ref_array[i][j].picture_number =

ref_object->picture_number;

}

}

}

}

// 调用64x64块级运动估计核心函数

// 传入图片控制集、块索引、块坐标、ME上下文和输入图片

svt_aom_motion_estimation_b64(pcs,

b64_index,

b64_origin_x,

b64_origin_y,

me_context_ptr->me_ctx,

input_pic);

// 处理全局运动估计(仅适用于PAME和超分辨率重新ME任务)

if ((in_results_ptr->task_type == TASK_PAME) || (in_results_ptr->task_type == TASK_SUPERRES_RE_ME)) {

// 获取互斥锁,保护共享的已处理块计数器

svt_block_on_mutex(pcs->me_processed_b64_mutex);

// 增加已处理的64x64块计数

pcs->me_processed_b64_count++;

// 检查是否所有超级块的ME都已完成,以便执行全局运动估计

if (pcs->me_processed_b64_count == pcs->b64_total_count) {

// 如果启用了全局运动且满足条件,执行全局运动估计

if (pcs->gm_ctrls.enabled && (!pcs->gm_ctrls.pp_enabled || pcs->gm_pp_detected)) {

svt_aom_global_motion_estimation(pcs, input_pic);

} else {

// 当全局运动关闭时,初始化全局运动状态为OFF

memset(pcs->is_global_motion, FALSE, MAX_NUM_OF_REF_PIC_LIST * REF_LIST_MAX_DEPTH);

}

}

// 释放互斥锁

svt_release_mutex(pcs->me_processed_b64_mutex);

}

}

}

}

// 如果未启用环内最优帧内搜索且启用了时域预测布局,执行开环帧内搜索

if (scs->in_loop_ois == 0 && pcs->tpl_ctrls.enable)

// Y方向遍历当前段内的64x64块

for (uint32_t y_b64_index = y_b64_start_index; y_b64_index < y_b64_end_index; ++y_b64_index)

// X方向遍历当前段内的64x64块

for (uint32_t x_b64_index = x_b64_start_index; x_b64_index < x_b64_end_index; ++x_b64_index) {

// 计算当前64x64块的线性索引

uint32_t b64_index = (uint16_t)(x_b64_index + y_b64_index * pic_width_in_b64);

// 对当前块执行开环帧内搜索,为TPL(时域预测布局)提供信息

svt_aom_open_loop_intra_search_mb(pcs, b64_index, input_pic);

}

}

// 从输出FIFO队列获取空的结果对象

svt_get_empty_object(me_context_ptr->motion_estimation_results_output_fifo_ptr,

&out_results_wrapper);

// 从结果包装器中提取运动估计结果指针

MotionEstimationResults *out_results = (MotionEstimationResults *)

out_results_wrapper->object_ptr;

// 将输入的图片控制集包装器传递给输出结果

out_results->pcs_wrapper = in_results_ptr->pcs_wrapper;

// 传递段索引

out_results->segment_index = segment_index;

// 传递任务类型

out_results->task_type = in_results_ptr->task_type;

// 释放输入结果对象,表示已处理完成

svt_release_object(in_results_wrapper_ptr);

// 将完整的结果对象发送到输出队列,供下游模块使用

svt_post_full_object(out_results_wrapper);

} else if (in_results_ptr->task_type == TASK_TFME) {

// 处理时域滤波运动估计任务

// 全局运动预处理(仅针对基础B帧)

if (pcs->gm_ctrls.pp_enabled && pcs->gm_pp_enabled && in_results_ptr->segment_index==0)

// 执行全局运动预处理器,为时域滤波准备参数

svt_aom_gm_pre_processor(

pcs,

pcs->temp_filt_pcs_list);

// 开始时域滤波处理

// 设置运动估计类型为运动补偿时域滤波(MCTF)

me_context_ptr->me_ctx->me_type = ME_MCTF;

// 初始化时域滤波,处理指定段的数据

svt_av1_init_temporal_filtering(

pcs->temp_filt_pcs_list, pcs, me_context_ptr, in_results_ptr->segment_index);

// 释放输入结果对象

svt_release_object(in_results_wrapper_ptr);

} else if (in_results_ptr->task_type == TASK_DG_DETECTOR_HME) {

// 处理动态GOP检测分层运动估计任务

// 执行0级分层运动估计检测器,用于动态GOP结构优化

dg_detector_hme_level0(pcs, in_results_ptr->segment_index);

// 释放输入结果对象

svt_release_object(in_results_wrapper_ptr);

}

} // 主循环结束

// 函数返回NULL,表示线程正常退出

return NULL;

}

// clang-format on

相关推荐
NAGNIP9 小时前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
冬奇Lab10 小时前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab10 小时前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP14 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年14 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼14 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS14 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区15 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈16 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang16 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx