homerHEVC代码阅读(25)——编码器引擎线程函数encoder_engine_thread

一个编码器可以有若干个编码器引擎,各个引擎之间相互独立,每一个引擎由一个线程来运行,在编码器引擎内部会创建编码线程(wfpp)来执行具体的编码工作。

// 编码器引擎线程函数
THREAD_RETURN_TYPE encoder_engine_thread(void *h)
{
    LOG_TRACE("%s\n","encoder_engine_thread begin");
    int avg_qp = 0.0;
    hvenc_engine_t* enc_engine = (hvenc_engine_t*)h;
    picture_t *currpict = &enc_engine->current_pict;
    slice_t *currslice = &currpict->slice;
    int n, i, num_threads;

    // 激活信号量,表示输入容器和输出容器都可以使用了
    if(enc_engine->index==0)// && enc_engine->num_encoded_frames==0)
    {
        SEM_POST(enc_engine->output_wait);
        SEM_POST(enc_engine->input_wait);
    }

    // 一直循环,直到编码器停止运行
    while(enc_engine->hvenc->run)
    {
        // 输出对象集
        output_set_t* ouput_sets;// = &enc_engine->hvenc->output_sets[enc_engine->num_encoded_frames & NUM_OUTPUT_NALUS_MASK];

        // 输出的nal单元个数
        int		output_nalu_cnt = 0;

        // nal单元列表的长度
        int		nalu_list_size = NALU_SET_SIZE;

        // nal单元列表
        nalu_t	**output_nalu_list;// = ouput_sets->nalu_list;
        int		engine;

        // 等待数据输入(这里的数据就是图像帧)
        SEM_WAIT(enc_engine->input_wait);

        //get next image
        // 获取一帧
        // 我们注意到HOMER_enc_encode的实质就是把一帧放到输入容器中
        // 而get_frame_to_encode函数就是刚刚好把一帧从输入容器中取出来,并且放到了picture—t的img2encode中
        if(!get_frame_to_encode(enc_engine->hvenc, &enc_engine->current_pict.img2encode))//get next image to encode and init type
        {
            SEM_POST(enc_engine->input_signal);
            return THREAD_RETURN;
        }

        //entra seccion critica
        // 设置引擎的最后的poc
        enc_engine->last_poc = enc_engine->hvenc->poc;
        
        // 增加编码器的poc
        enc_engine->hvenc->poc++;
        
        // 设置已经编码的帧的数量
        enc_engine->num_encoded_frames = enc_engine->hvenc->num_encoded_frames;
        enc_engine->hvenc->num_encoded_frames++;

        ouput_sets = &enc_engine->hvenc->output_sets[enc_engine->num_encoded_frames % NUM_OUTPUT_NALUS(enc_engine->hvenc->num_encoder_engines)];//relative to encoder
        output_nalu_list = ouput_sets->nalu_list;

        memset(output_nalu_list, 0, (nalu_list_size)*sizeof(output_nalu_list[0]));

        // 设置片的nal单元
        enc_engine->slice_nalu = &enc_engine->slice_nalu_list[enc_engine->num_encoded_frames_in_engine % STREAMS_PER_ENGINE];//relative to engine

        // 比特流初始化
        hmr_bitstream_init(&enc_engine->slice_nalu->bs);

        // 片初始化
        hmr_slice_init(enc_engine, &enc_engine->current_pict, &currpict->slice);

        if(enc_engine->bitrate_mode != BR_FIXED_QP)
        {
            // 这个函数什么都没做
            if(currslice->poc==0)
                hmr_rc_init_seq(enc_engine);

            enc_engine->rc = enc_engine->hvenc->rc;

            // 这个函数是空的,什么都没做
            hmr_rc_init_pic(enc_engine, &currpict->slice);
        }

        // 率失真初始化
        hmr_rd_init(enc_engine, &currpict->slice);

        //get free img for decoded blocks
        // 取出一帧参考帧,这个参考帧还是空的
        cont_get(enc_engine->hvenc->cont_empty_reference_wnds,(void**)&enc_engine->curr_reference_frame);

        // 参考帧的时域层信息
        enc_engine->curr_reference_frame->temp_info.poc = currslice->poc;//assign temporal info to decoding window for future use as reference

        // 应用图像参考集
        apply_reference_picture_set(enc_engine->hvenc, currslice);

        //reference prunning must be done in a selective way
        // 重新填上一个参考帧到cont_empty_reference_wnds中
        if(enc_engine->hvenc->reference_picture_buffer[enc_engine->hvenc->reference_list_index]!=NULL)
            cont_put(enc_engine->hvenc->cont_empty_reference_wnds,enc_engine->hvenc->reference_picture_buffer[enc_engine->hvenc->reference_list_index]);

        // 把刚刚取出来的参考帧放在参考图像buffer中
        enc_engine->hvenc->reference_picture_buffer[enc_engine->hvenc->reference_list_index] = enc_engine->curr_reference_frame;
        enc_engine->hvenc->reference_list_index = (enc_engine->hvenc->reference_list_index+1)&MAX_NUM_REF_MASK;

        // 平均的失真
        enc_engine->avg_dist = enc_engine->hvenc->avg_dist;

        // 初始化wfpp线程上下文
        for(n = 0; n<enc_engine->wfpp_num_threads;n++)
        {
            INIT_SYNC_THREAD_CONTEXT(enc_engine, enc_engine->thread[n]);
            //reset remafore

            // 重置信号量
            SEM_RESET(enc_engine->thread[n]->synchro_wait[0])
        }

        // 通知大家可以往容器中放数据了
        SEM_POST(enc_engine->input_signal);

        // 创建线程,调用wfpp_encoder_thread函数进行实际的编码
        CREATE_THREADS((&enc_engine->hthreads[0]), wfpp_encoder_thread, enc_engine->thread, enc_engine->wfpp_num_threads);

        // 等待线程执行结束(即等待上面被取出来的那一帧编码完成)
        JOIN_THREADS(enc_engine->hthreads, enc_engine->wfpp_num_threads-1);

        //calc average distortion
        // 计算平均的失真
        if(enc_engine->num_encoded_frames == 0 || currslice->slice_type != I_SLICE || enc_engine->intra_period==1)
        {
            enc_engine->avg_dist = 0;
            for(n = 0;n<enc_engine->wfpp_num_threads;n++)
            {
                henc_thread_t* henc_th = enc_engine->thread[n];

                enc_engine->avg_dist+= henc_th->acc_dist;
                avg_qp+=henc_th->acc_qp;
            }
            enc_engine->avg_dist /= enc_engine->pict_total_ctu*enc_engine->num_partitions_in_cu;
            enc_engine->avg_dist = clip(enc_engine->avg_dist,.1,enc_engine->avg_dist);
            if(currslice->slice_type == I_SLICE)
                enc_engine->avg_dist*=1.5;
            else if(enc_engine->is_scene_change)
                enc_engine->avg_dist*=1.375;
        }
        //		else if(currslice->slice_type == I_SLICE)
        //			enc_engine->avg_dist/=1.5;

        enc_engine->hvenc->avg_dist = enc_engine->avg_dist;

        // 平均的量化步长
        avg_qp = (avg_qp+(enc_engine->pict_total_ctu>>1))/enc_engine->pict_total_ctu;

#ifndef COMPUTE_AS_HM

        // 如果不是固定的量化步长
        if(enc_engine->bitrate_mode != BR_FIXED_QP)//modulate avg qp for differential encoding
        {
            // 图像的量化步长
            enc_engine->pict_qp = clip(avg_qp,/*MIN_QP*/1,MAX_QP);

            // 如果片的类型是I,而且I帧的周期是1,那么从hmr_rc_compensate_qp_from_intra函数计算量化步长
            if(currslice->slice_type == I_SLICE && enc_engine->intra_period!=1)
                enc_engine->pict_qp = hmr_rc_compensate_qp_from_intra(enc_engine->avg_dist, enc_engine->pict_qp);
        }
#endif

#ifdef COMPUTE_AS_HM
        if(enc_engine->intra_period>1)
            hmr_deblock_filter(enc_engine, currslice);

        hmr_sao_encode_ctus_hm(enc_engine, currslice);

        if(enc_engine->intra_period>1)
            reference_picture_border_padding(&enc_engine->curr_reference_frame->img);
#endif

        // 如果不是固定的量化步长,那么调用hmr_rc_end_pic(这个函数的功能还不清楚):返回速率控制
        if(enc_engine->bitrate_mode != BR_FIXED_QP)
            hmr_rc_end_pic(enc_engine, currslice);

        enc_engine->hvenc->avg_dist = enc_engine->avg_dist;
        enc_engine->is_scene_change = 0;

        //sync to other modules
        // 同步到其他的引擎模块
        for(engine = 0;engine<enc_engine->hvenc->num_encoder_engines;engine++)
        {
            // 取得每一个引擎对象
            hvenc_engine_t* phvenc_engine = enc_engine->hvenc->encoder_engines[engine];

            // 如果其他模块正在处理的帧的poc大于当前的该模块的帧的poc,那么其他模块需要和当前模块进行同步
            // 因为速率控制总是以最小(最慢)的那个为标准
            if(phvenc_engine->current_pict.slice.poc > enc_engine->current_pict.slice.poc)
            {
                phvenc_engine->rc.vbv_fullness = enc_engine->rc.vbv_fullness;
                phvenc_engine->rc.acc_avg = enc_engine->rc.acc_avg;
                phvenc_engine->rc.acc_rate= enc_engine->rc.acc_rate;
                if(!phvenc_engine->is_scene_change)
                    phvenc_engine->avg_dist= enc_engine->avg_dist;
            }
        }




        //----------------------------end calc average statistics (distortion, qp)----------------------------------

        // 从这里开始,所有的预测、变换、量化、熵编码等工作全部交给wfpp线程去做
        // 然后引擎线程一直在这里等待,等待缓冲区中有处理完成的帧,如果有就往下进行处理!
        SEM_WAIT(enc_engine->output_wait);

        // 如果是一个GOP的开头
        if(currslice->nalu_type == NALU_CODED_SLICE_IDR_W_RADL)
        {
            hmr_bitstream_init(&enc_engine->hvenc->vps_nalu.bs);
            hmr_bitstream_init(&enc_engine->hvenc->sps_nalu.bs);
            hmr_bitstream_init(&enc_engine->hvenc->pps_nalu.bs);

            // vps的编码
            enc_engine->hvenc->vps_nalu.nal_unit_type = NALU_TYPE_VPS;
            enc_engine->hvenc->vps_nalu.temporal_id = enc_engine->hvenc->vps_nalu.rsvd_zero_bits = 0;
            output_nalu_list[output_nalu_cnt++] = &enc_engine->hvenc->vps_nalu;
            hmr_put_vps_header(enc_engine->hvenc);//vps header

            // sps编码
            enc_engine->hvenc->sps_nalu.nal_unit_type = NALU_TYPE_SPS;
            enc_engine->hvenc->sps_nalu.temporal_id = enc_engine->hvenc->sps_nalu.rsvd_zero_bits = 0;
            output_nalu_list[output_nalu_cnt++] = &enc_engine->hvenc->sps_nalu;
            hmr_put_seq_header(enc_engine->hvenc);//seq header

            // pps编码
            enc_engine->hvenc->pps_nalu.nal_unit_type = NALU_TYPE_PPS;
            enc_engine->hvenc->pps_nalu.temporal_id = enc_engine->hvenc->pps_nalu.rsvd_zero_bits = 0;
            output_nalu_list[output_nalu_cnt++] = &enc_engine->hvenc->pps_nalu;
            hmr_put_pic_header(enc_engine->hvenc);//pic header
        }
        //slice header
        // slice头编码
        enc_engine->slice_nalu->nal_unit_type = currslice->nalu_type;
        enc_engine->slice_nalu->temporal_id = enc_engine->slice_nalu->rsvd_zero_bits = 0;

        // 存储这个片对应的nal单元
        output_nalu_list[output_nalu_cnt++] = enc_engine->slice_nalu;

        // 写入片头
        hmr_put_slice_header(enc_engine, currslice);//slice header

        // 如果启用了wfpp功能,那么就编码wfpp入口点
        if(enc_engine->wfpp_enable)
            hmr_slice_header_code_wfpp_entry_points(enc_engine);

        // 编码拖尾比特
        hmr_bitstream_rbsp_trailing_bits(&enc_engine->slice_bs);

        // 统计比特数
        for(i=0;i<enc_engine->num_sub_streams;i++)
        {
            memcpy(&enc_engine->slice_bs.bitstream[enc_engine->slice_bs.streambytecnt], enc_engine->aux_bs[i].bitstream, enc_engine->aux_bs[i].streambytecnt);
            enc_engine->slice_bs.streambytecnt += enc_engine->aux_bs[i].streambytecnt;
        }

        //escribimos la nalu
        // 写入NAL单元
        hmr_bitstream_put_nal_unit_header(&enc_engine->slice_nalu->bs, currslice->nalu_type, 0, 0);

        // 写入EBSP信息(图像增强信息)
        hmr_bitstream_nalu_ebsp(&enc_engine->slice_bs,&enc_engine->slice_nalu->bs);

#ifdef WRITE_REF_FRAMES
        wnd_write2file(&enc_engine->curr_reference_frame->img);
#endif

        //		enc_engine->num_encoded_frames++;

        // 打印编码结果
#ifdef DBG_TRACE_RESULTS
        {
            char stringI[] = "I";
            char stringP[] = "P";
            char stringB[] = "B";
            char *frame_type_str;
            frame_type_str=currpict->img2encode->img_type==IMAGE_I?stringI:currpict->img2encode->img_type==IMAGE_P?stringP:stringB;

            printf("\r\nengine:%d, frame:%d, %s, bits:%d,", enc_engine->index,enc_engine->num_encoded_frames, frame_type_str, enc_engine->slice_bs.streambytecnt*8);
            //printf("\r\nmodule:%d, frame:%d, %s, target:%.0f, bits:%d,", enc_engine->index,enc_engine->num_encoded_frames, frame_type_str, enc_engine->rc.target_pict_size, enc_engine->slice_bs.streambytecnt*8);
#ifdef COMPUTE_METRICS

            homer_psnr(&enc_engine->current_pict, &enc_engine->curr_reference_frame->img, enc_engine->pict_width, enc_engine->pict_height, enc_engine->current_psnr);
            enc_engine->accumulated_psnr[0] += enc_engine->current_psnr[Y_COMP];
            enc_engine->accumulated_psnr[1] += enc_engine->current_psnr[U_COMP];
            enc_engine->accumulated_psnr[2] += enc_engine->current_psnr[V_COMP];

            printf("PSNRY: %.2f, PSNRU: %.2f,PSNRV: %.2f, ", enc_engine->current_psnr[Y_COMP], enc_engine->current_psnr[U_COMP], enc_engine->current_psnr[V_COMP]);
            printf("Average PSNRY: %.2f, PSNRU: %.2f,PSNRV: %.2f, ", enc_engine->accumulated_psnr[Y_COMP]/(enc_engine->num_encoded_frames+1), enc_engine->accumulated_psnr[U_COMP]/(enc_engine->num_encoded_frames+1), enc_engine->accumulated_psnr[V_COMP]/(enc_engine->num_encoded_frames+1));
#endif
            printf("vbv: %.2f, ", enc_engine->rc.vbv_fullness/enc_engine->rc.vbv_size);
            //			printf("avg_dist: %.2f, ", enc_engine->avg_dist);
            //			printf("sao_mode:[%d,%d,%d],sao_type:[%d,%d,%d,%d,%d] lambda:%.2f, ", enc_engine->sao_debug_mode[0],enc_engine->sao_debug_mode[1],enc_engine->sao_debug_mode[2],enc_engine->sao_debug_type[0],enc_engine->sao_debug_type[1],enc_engine->sao_debug_type[2],enc_engine->sao_debug_type[3],enc_engine->sao_debug_type[4], enc_engine->sao_lambdas[0]);
            //			printf("rc.target_pict_size: %.2f", enc_engine->rc.target_pict_size);
            //#ifndef COMPUTE_AS_HM
            printf("qp: %d, ", enc_engine->pict_qp);
            //#endif
            printf("pts: %d, ", enc_engine->current_pict.img2encode->temp_info.pts);
            fflush(stdout);
        }
#endif
        //prunning of references must be done in a selective way
        //		if(enc_engine->reference_picture_buffer[enc_engine->reference_list_index]!=NULL)
        //			cont_put(enc_engine->cont_empty_reference_wnds,enc_engine->reference_picture_buffer[enc_engine->reference_list_index]);

        //#ifdef COMPUTE_AS_HM
        //		reference_picture_border_padding(&enc_engine->curr_reference_frame->img);
        //#endif
        //fill padding in reference picture
        //		reference_picture_border_padding(&enc_engine->curr_reference_frame->img);
        //		enc_engine->reference_picture_buffer[enc_engine->reference_list_index] = enc_engine->curr_reference_frame;
        //		enc_engine->reference_list_index = (enc_engine->reference_list_index+1)&MAX_NUM_REF_MASK;
        //		enc_engine->last_poc++;

        ouput_sets->pts = enc_engine->current_pict.img2encode->temp_info.pts;
        ouput_sets->image_type = enc_engine->current_pict.img2encode->img_type;
        put_available_frame(enc_engine->hvenc, enc_engine->current_pict.img2encode);

        ouput_sets->num_nalus = output_nalu_cnt;
        ouput_sets->frame = enc_engine->curr_reference_frame;
        cont_put(enc_engine->hvenc->output_hmr_container, ouput_sets);

        enc_engine->num_encoded_frames_in_engine++;

        SEM_POST(enc_engine->output_signal);
    }

    LOG_TRACE("%s\n","encoder_engine_thread end");

    return THREAD_RETURN;
}


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