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