homerHEVC代码阅读(26)——编码器工作线程/wfpp线程wfpp_encoder_thread

wfpp_encoder_thread执行具体的编码工作。
1、在目前的编码器中,一个帧中只有一个slice。
2、一个slice中可能有若干行CTU。
3、每一个wfpp_encoder_thread线程都以一行CTU为处理单位,因此,每个起始的CTU都是一行的最左边的CTU。
4、一个slice中wfpp线程的数量一般会等于或者少于CTU的行数,每一个线程处理一行,线程的索引就是处理的行号。

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;
}


你可能感兴趣的:(h.265,视频编码,HEVC)