5、当一行CTU被处理完成的时候,处理当前行的wfpp线程就会从当前行cu_current_y跳到cu_current_y+wfpp_num_threads行继续处理。
// wfpp线程/工作线程 THREAD_RETURN_TYPE wfpp_encoder_thread(void *h) { LOG_TRACE("%s\n","wfpp_encoder_thread begin"); henc_thread_t* et = (henc_thread_t*)h; int gcnt=0; // 获取当前的图像 picture_t *currpict = &et->enc_engine->current_pict; // 或去当前的片 slice_t *currslice = &currpict->slice; // ctu信息 ctu_info_t* ctu; //printf(" +wfpp_encoder_thread %d\r\n", et->index); // 当前CTU的坐标(以CTU个数为坐标,而不是像素) // 解释一下这里:每一个wfpp_encoder_thread线程都以一行CTU为处理单位,因此,每个起始的CTU都是一行的最左边的CTU // 一个slice中可能有若干行CTU // 一个slice中wfpp线程的数量一般会等于或者少于CTU的行数,每一个线程处理一行,线程的索引就是处理的行号 // 当一行CTU被处理完成的时候,处理当前行的wfpp线程就会从当前行cu_current_y跳到cu_current_y+wfpp_num_threads行继续处理 // 在目前的编码器中,一个帧中只有一个slice et->cu_current_x = 0; et->cu_current_y = et->index; ctu = &et->enc_engine->ctu_info[et->cu_current_y*et->pict_width_in_ctu]; // 如果线程的索引是0 // 那么它就是当前片的第一个线程,处理当前片的第一行CTU,因此需要初始化熵编码器、还要初始化比特流 if(et->index==0) { et->ec = &et->enc_engine->ec_list[0]; et->ee = et->enc_engine->ee_list[0]; et->ee->bs = &et->enc_engine->aux_bs[0]; // 比特流初始化 hmr_bitstream_init(et->ee->bs); //resetEntropy // 重置熵编码 ee_start_entropy_model(et->ee, currslice->slice_type, currslice->qp, et->pps->cabac_init_flag);//Init CABAC contexts //cabac - reset binary encoder and entropy et->ee->ee_start(et->ee->b_ctx); et->ee->ee_reset_bits(et->ee->b_ctx);//ee_reset(&enc_engine->ee); } // 遍历当前行的每一个CTU while(et->cu_current < et->pict_total_ctu)//all ctus loop { int bits_allocated; // 当前cu(CTU)的编号 et->cu_current = et->pict_width_in_ctu*(et->cu_current_y)+et->cu_current_x; // 下一个CTU的编号 et->cu_next = et->cu_current+min(1,et->pict_width_in_ctu-et->cu_current_x); // 如果当前CTU的纵坐标为0,表示它在图像的顶部 if(et->cu_current_y == 0)// && ((et->cu_current_x & GRAIN_MASK) == 0)) { SEM_POST(et->synchro_wait[0]); et->dbg_sem_post_cnt++; PRINTF_SYNC("SEM_POST1: ctu_num:%d, thread_id:%d, dbg_sem_post_cnt:%d\r\n", et->cu_current, et->index, et->dbg_sem_post_cnt); } // 已经编码的帧数是0 if(et->enc_engine->num_encoded_frames == 0)// && ((et->cu_current_x & GRAIN_MASK) == 0)) { SEM_POST(et->synchro_wait[1]); } // 等待 { SEM_WAIT_MULTIPLE(et->synchro_wait, et->num_wait_sem); } // CTU初始化 ctu = init_ctu(et); //Prepare Memory // 把MBs从图像中移动到当前的MB窗口 mem_transfer_move_curr_ctu_group(et, et->cu_current_x, et->cu_current_y); //move MBs from image to currMbWnd // 复制左边和上方的信息,以用于帧内预测 mem_transfer_intra_refs(et, ctu);//copy left and top info for intra prediction copy_ctu(ctu, et->ctu_rd); // bits_allocated = hmr_bitstream_bitcount(et->ee->bs); // 档次、层重置 PROFILER_RESET(intra); //map spatial features and neighbours in recursive partition structure // 创建当前CTU的邻居 create_partition_ctu_neighbours(et, ctu, ctu->partition_list); // 如果不是I帧就进行帧间预测 if(currslice->slice_type != I_SLICE && !et->enc_engine->is_scene_change)// && (ctu->ctu_number & 0x1) == 0) { int ll; // 帧间预测 // 虽然名为帧间预测,但是在某种特定情况下,会进行帧内预测 motion_inter(et, ctu); for(ll = 0; ll<et->num_partitions_in_cu;ll++) { // 统计帧内预测的信息 if(ctu->pred_mode[ll]==INTRA_MODE) et->num_intra_partitions++; } } else { //make ctu intra prediction // 帧内预测 motion_intra(et, ctu, gcnt); et->num_intra_partitions += et->num_partitions_in_cu; } PROFILER_ACCUMULATE(intra); et->num_total_partitions += et->num_partitions_in_cu; et->num_total_ctus++; et->acc_qp += ctu->partition_list[0].qp; et->acc_dist += ctu->partition_list[0].distortion; ctu->distortion = ctu->partition_list[0].distortion; // mem_transfer_decoded_blocks(et, ctu); wnd_copy_16bit(et->transform_quant_wnd[0], ctu->coeff_wnd); #ifndef COMPUTE_AS_HM // 重要的函数:这里进行sao、去方块滤波和熵编码等操作 hmr_deblock_sao_pad_sync_ctu(et, currslice, ctu); #endif // 如果当前的线程的CTU横坐标大于等于2(即该CTU不是帧的最左边) 而且 // 当前行不是帧的最后一行,那么唤醒下一行的wfpp线程 if(et->cu_current_x>=2 && et->cu_current_y+1 != et->pict_height_in_ctu)//notify next wpp thread { SEM_POST(et->synchro_signal[0]); et->dbg_sem_post_cnt++; PRINTF_SYNC("SEM_POST2: ctu_num:%d, thread_id:%d, dbg_sem_post_cnt:%d\r\n", et->cu_current, et->index, et->dbg_sem_post_cnt); } // 下一个CTU et->cu_current_x++; //sync entropy contexts between wpp // 如果当前线程刚好处理完成两个CTU,那么唤醒下一行的wfpp线程 if(et->cu_current_x == 2 && et->cu_current_y+1 != et->pict_height_in_ctu) { SEM_POST(et->synchro_signal[0]); et->dbg_sem_post_cnt++; PRINTF_SYNC("SEM_POST3: ctu_num:%d, thread_id:%d, dbg_sem_post_cnt:%d\r\n", et->cu_current, et->index, et->dbg_sem_post_cnt); } //notify last synchronization as this line goes two ctus ahead from next line in wfpp // 如果当前线程处理完成了该行的所有CTU,唤醒下一行的wfpp线程 if(et->cu_current_x==et->pict_width_in_ctu && et->cu_current_y+1 != et->pict_height_in_ctu)// && ((et->cu_current_x & GRAIN_MASK) == 0)) { SEM_POST(et->synchro_signal[0]); et->dbg_sem_post_cnt++; PRINTF_SYNC("SEM_POST4: ctu_num:%d, thread_id:%d, dbg_sem_post_cnt:%d\r\n", et->cu_current, et->index, et->dbg_sem_post_cnt); } // 如果当前线程处理完成了该行的所有CTU,那么跳到第cu_current_y+wfpp_num_threads行继续处理 if(et->cu_current_x==et->pict_width_in_ctu) { et->cu_current_y+=et->wfpp_num_threads; et->cu_current_x=0; } et->cu_current = et->pict_width_in_ctu*(et->cu_current_y)+et->cu_current_x; } //LOG_TRACE("%s\n","wfpp_encoder_thread end"); return THREAD_RETURN; }