一个编码器可以有若干个编码器引擎,各个引擎之间相互独立,每一个引擎由一个线程来运行,在编码器引擎内部会创建编码线程(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; }