一、码率控制的相关理论请看: HEVC/H.265理论知识(9)——码率控制
二、码率控制可以分为几个几个级别,HomerHEVC只支持帧级和CTU级的码率控制
三、码率控制实际就是控制量化参数,通过量化参数来提高码率或者降低码率
四、码率控制基本都要配备缓冲区,因为码率控制不可能完全精确,因此需要使用缓冲区来匹配比特生成的速度和发送的速度
码率控制初始化,基本就是根据缓冲区计算,每一个CTU应该分配多少比特数:
- void hmr_rc_init(hvenc_engine_t* enc_engine)
- {
- /* 缓冲区的大小 */
- enc_engine->rc.vbv_size = enc_engine->vbv_size*1000;
- enc_engine->rc.vbv_fullness = enc_engine->vbv_init*1000;
- /* 每一帧平均的尺寸 */
- enc_engine->rc.average_pict_size = enc_engine->bitrate*1000/enc_engine->frame_rate;
- /* 每一个CTU平均的比特数 */
- enc_engine->rc.average_bits_per_ctu = enc_engine->rc.average_pict_size/enc_engine->pict_total_ctu;
- enc_engine->hvenc->rc = enc_engine->rc;
- }
帧级码率控制初始化,目的是计算一帧的目标比特数:
- void hmr_rc_init_pic(hvenc_engine_t* enc_engine, slice_t *currslice)
- {
- int ithreads;
- int clipped_intra_period = enc_engine->intra_period==0?20:enc_engine->intra_period;
- double intra_avg_size = 2.25*enc_engine->rc.average_pict_size*sqrt((double)clipped_intra_period);
- enc_engine->rc = enc_engine->hvenc->rc;
- enc_engine->rc.extra_bits = 0;
- switch(currslice->slice_type)
- {
- case I_SLICE:
- {
- enc_engine->rc.target_pict_size = min(intra_avg_size, enc_engine->rc.vbv_fullness);///*(2.25-((double)enc_engine->avg_dist/15000.))**/2.25*enc_engine->rc.average_pict_size*sqrt((double)clipped_intra_period);
- if(enc_engine->num_b>0)
- enc_engine->rc.target_pict_size *= (1.0+.1*enc_engine->num_b);//.5*enc_engine->rc.average_pict_size;
- break;
- }
- case P_SLICE:
- {
- enc_engine->rc.target_pict_size = (enc_engine->rc.average_pict_size*clipped_intra_period-intra_avg_size)/(clipped_intra_period-1);//.5*enc_engine->rc.average_pict_size;
- if(enc_engine->num_b>0)
- enc_engine->rc.target_pict_size *= (1.0+.1*enc_engine->num_b);//.5*enc_engine->rc.average_pict_size;
- break;
- }
- case B_SLICE:
- {
- enc_engine->rc.target_pict_size = enc_engine->rc.average_pict_size/2;
- break;
- }
- }
- #ifdef COMPUTE_AS_HM
- currslice->qp = enc_engine->pict_qp-(enc_engine->num_encoded_frames%4);
- if(currslice->qp<1)
- {
- currslice->qp=1;
- }
- #endif
- enc_engine->rc.target_bits_per_ctu = enc_engine->rc.target_pict_size/enc_engine->pict_total_ctu;
- for(ithreads=0;ithreads<enc_engine->wfpp_num_threads;ithreads++)
- {
- henc_thread_t* henc_th = enc_engine->thread[ithreads];
-
- henc_th->target_pict_size = (uint32_t)enc_engine->rc.target_pict_size;
- henc_th->num_encoded_ctus = 0;
- henc_th->num_bits = 0;
- henc_th->acc_qp = 0;
- }
- enc_engine->hvenc->rc = enc_engine->rc;
- }
如果屏幕大小改变了,那么需要调整目标比特数:
- void hmr_rc_change_pic_mode(henc_thread_t* et, slice_t *currslice)
- {
- hvenc_engine_t* enc_engine = et->enc_engine;
- int clipped_intra_period = (enc_engine->intra_period==0)?20:enc_engine->intra_period;
- double pic_size_new;
- int consumed_bitrate = 0;
- int consumed_ctus = 0;
- int current_pic_size = enc_engine->rc.target_pict_size;
- // if(enc_engine->is_scene_change)
- {
- int ithreads;
- if(et->enc_engine->gop_reinit_on_scene_change && enc_engine->rc.vbv_fullness<.5*enc_engine->rc.vbv_size)
- {
- pic_size_new = 1.*enc_engine->rc.average_pict_size*sqrt((double)clipped_intra_period);
- }
- else
- {
- pic_size_new = .75*enc_engine->rc.average_pict_size*sqrt((double)clipped_intra_period);
- }
- enc_engine->rc.target_pict_size = min(pic_size_new, enc_engine->rc.vbv_fullness);
- enc_engine->rc.target_bits_per_ctu = enc_engine->rc.target_pict_size/enc_engine->pict_total_ctu;
- for(ithreads=0;ithreads<enc_engine->wfpp_num_threads;ithreads++)
- {
- henc_thread_t* henc_th = enc_engine->thread[ithreads];
-
- henc_th->target_pict_size = (uint32_t)enc_engine->rc.target_pict_size;
- consumed_bitrate += henc_th->num_bits;
- consumed_ctus += henc_th->num_encoded_ctus;
- }
- enc_engine->rc.extra_bits = enc_engine->rc.target_pict_size*((double)consumed_ctus/et->enc_engine->pict_total_ctu)-consumed_bitrate;//clip(((double)pic_size_new/current_pic_size)*consumed_bitrate - consumed_bitrate, 0, pic_size_new/2);
- }
- enc_engine->hvenc->rc = enc_engine->rc;
- }
根据码率控制获取CTU的量化参数:
- int hmr_rc_get_cu_qp(henc_thread_t* et, ctu_info_t *ctu, cu_partition_info_t *curr_cu_info, slice_t *currslice)
- {
- int qp;
- #ifdef COMPUTE_AS_HM
- double debug_qp = currslice->qp+ctu->ctu_number%4;//28+ctu->ctu_number%4;
- if(et->enc_engine->bitrate_mode == BR_FIXED_QP)
- {
- qp = et->enc_engine->current_pict.slice.qp;
- }
- else//cbr, vbr
- {
- if(curr_cu_info->depth <= et->enc_engine->qp_depth)
- qp = (int)debug_qp;//hmr_rc_calc_cu_qp(et);
- else
- qp = curr_cu_info->parent->qp;
- }
- #else
- if(et->enc_engine->bitrate_mode == BR_FIXED_QP)
- {
- qp = et->enc_engine->current_pict.slice.qp;
- }
- else//cbr, vbr
- {
- if(curr_cu_info->depth <= et->enc_engine->qp_depth)
- {
- qp = hmr_rc_calc_cu_qp(et, ctu, currslice);
- }
- else
- qp = curr_cu_info->parent->qp;
- }
- #endif
- return qp;
- }
- int hmr_rc_calc_cu_qp(henc_thread_t* curr_thread, ctu_info_t *ctu/*, cu_partition_info_t *curr_cu_info*/, slice_t *currslice)
- {
- hvenc_engine_t* enc_engine = curr_thread->enc_engine;
- int ithreads;
- double qp;
- double pic_corrector = 0.0;
- double vbv_corrector;
- double consumed_bitrate = 0.0, entropy;
- double min_vbv_size;
- int consumed_ctus = 0;
- for(ithreads=0;ithreads<enc_engine->wfpp_num_threads;ithreads++)
- {
- henc_thread_t* henc_th = enc_engine->thread[ithreads];
-
- consumed_bitrate += henc_th->num_bits;
- consumed_ctus += henc_th->num_encoded_ctus;
- }
- entropy = 3;//sqrt(((double)enc_engine->avg_dist/3000.)*(curr_cu_info->variance))/40;//25.0;
- consumed_bitrate += enc_engine->rc.extra_bits;
- // if(consumed_ctus>0 && (currslice->slice_type != P_SLICE || enc_engine->is_scene_change))
- {
- if(consumed_bitrate>1.5*enc_engine->rc.target_bits_per_ctu*consumed_ctus)//*consumed_ctus && currslice->slice_type != I_SLICE)
- {
- if(currslice->slice_type == I_SLICE)
- pic_corrector = 2.5*.0125*(consumed_bitrate/(enc_engine->rc.target_bits_per_ctu*consumed_ctus));
- else
- pic_corrector = .0125*(consumed_bitrate/(enc_engine->rc.target_bits_per_ctu*consumed_ctus));
- }
- pic_corrector = clip(pic_corrector, 0, .5);
- }
- min_vbv_size = clip(enc_engine->rc.vbv_fullness,enc_engine->rc.vbv_fullness,enc_engine->rc.vbv_size*.95);
- if(consumed_bitrate>enc_engine->rc.target_bits_per_ctu*consumed_ctus)
- vbv_corrector = 1.0-clip((min_vbv_size-consumed_bitrate+enc_engine->rc.target_bits_per_ctu*consumed_ctus)/enc_engine->rc.vbv_size, 0.0, 1.0);
- else
- vbv_corrector = 1.0-clip((min_vbv_size)/enc_engine->rc.vbv_size, 0.0, 1.0);
- qp = ((pic_corrector+vbv_corrector)/1.)*(MAX_QP)+/*(pic_corrector-1)+*/(entropy-3.);
- //variable rate
- if(enc_engine->bitrate_mode == BR_VBR)
- {
- if(qp<enc_engine->qp_min)
- qp=enc_engine->qp_min;
- }
- if(curr_thread->enc_engine->intra_period>1)
- {
- if(currslice->slice_type == I_SLICE || (enc_engine->is_scene_change && enc_engine->gop_reinit_on_scene_change))
- {
- qp/=clip(1.5-((double)enc_engine->avg_dist/15000.),1.15,1.5);
- }
- else if(currslice->slice_type == B_SLICE && enc_engine->num_b!=enc_engine->gop_size)
- {
- qp*=clip(1.125-((double)enc_engine->avg_dist/15000.),1.15,1.5);
- }
- else if(enc_engine->is_scene_change)
- qp/=1.1;
- }
- if((enc_engine->is_scene_change) && qp<=5)
- {
- qp=5;
- }
- if(enc_engine->num_encoded_frames==0)
- {
- qp+=4;
- }
- else if(currslice->slice_type == I_SLICE && consumed_bitrate > 1.*(enc_engine->rc.target_bits_per_ctu*consumed_ctus) && enc_engine->rc.vbv_fullness<.5*enc_engine->rc.vbv_size)//control scene changes in I frames
- {
- qp+=2;
- }
- return (int)clip(qp+.5,1.0,MAX_QP);
- }