VBV 是一种帧级别的码率控制算法,它是这样一种机制: VBV 相当于一个容器,每编码一帧,都从容器内取走对应 bit 的数据;与此同时,往容器内以固定的速度输入 bit。每编码完一帧,根据容器内的充盈状态(上溢/下溢),更新接下来编码参数,使得容器的充盈程都总是处于合理的范围内。
视频缓冲检测器(VBV, Video Buffer Verifer)是 MPEG 视频缓冲模型,可以确保码率不会超过某个最大值。VBV Buffer Size 通常设置为 maximum rate 的两倍;如果客户端缓存比较小,设置 bufsize 等于 maxrate;如果想要限制码流的码率,设置 buffersize 为 maximum rate 的一半或更小。
先来看一下,x264 中关于 VBV 的几个变量定义:
12345678910111213141516
structx264_ratecontrol_t{intb_vbv;intb_vbv_min_rate;/*VBV stuff*/doublebuffer_size;//VBV buffer size, 容器的总容量int64_tbuffer_fill_final;int64_tbuffer_fill_final_min;doublebuffer_fill;//planned buffer, if all in-progress frames hit their bit budgetdoubltbuffer_rate;//# of bits added to buffer_fill after each framedoublevbv_max_rate;//# of bits added to buffer_fill per secondpredictor_t*pred;//predict frame size from satdintsingle_frame_vbv;floatrate_factor_max_increment;//Don't allow RF above(CRF + this value)}
intterminate=0;/*Avoid an infinite loop*/for(intiterations=0;iterations<1000&&terminate!=3;iterations++){doubleframe_q[3];doublecur_bits=predict_size(&rcc->pred[h->sh.i_type],q,rcc->last_satd);doublebuffer_fill_cur=rcc->buffer_fill-cur_bits;doubletarget_fill;doubletotal_duration=0;doublelast_duration=fenc_cpb_duration;frame_q[0]=h->sh.i_type==SLICE_TYPE_I?q*h->param.rc.f_ip_factor:q;frame_q[1]=frame_q[0]*h->param.rc.f_pb_factor;frame_q[2]=frame_q[0]/h->param.rc.f_ip_factor;/* Loop over the planned future frames. */for(intj=0;buffer_fill_cur>=0&&buffer_fill_cur<=rcc->buffer_size;j++){total_duration+=last_duration;buffer_fill_cur+=rcc->vbv_max_rate*last_duration;inti_type=h->fenc->i_planned_type[j];inti_satd=h->fenc->i_planned_satd[j];if(i_type==X264_TYPE_AUTO)break;i_type=IS_X264_TYPE_I(i_type)?SLICE_TYPE_I:IS_X264_TYPE_B(i_type)?SLICE_TYPE_B:SLICE_TYPE_P;cur_bits=predict_size(&rcc->pred[i_type],frame_q[i_type],i_satd);buffer_fill_cur-=cur_bits;last_duration=h->fenc->f_planned_cpb_duration[j];}/* Try to get to get the buffer at least 50% filled, but don't set an impossible goal. */target_fill=X264_MIN(rcc->buffer_fill+total_duration*rcc->vbv_max_rate*0.5,rcc->buffer_size*0.5);if(buffer_fill_cur<target_fill){q*=1.01;terminate|=1;continue;}/* Try to get the buffer no more than 80% filled, but don't set an impossible goal. */target_fill=x264_clip3f(rcc->buffer_fill-total_duration*rcc->vbv_max_rate*0.5,rcc->buffer_size*0.8,rcc->buffer_size);if(rcc->b_vbv_min_rate&&buffer_fill_cur>target_fill){q/=1.01;terminate|=2;continue;}break;}
if((pict_type==SLICE_TYPE_P||(pict_type==SLICE_TYPE_I&&rcc->last_non_b_pict_type==SLICE_TYPE_I))&&rcc->buffer_fill/rcc->buffer_size<0.5){q/=x264_clip3f(2.0*rcc->buffer_fill/rcc->buffer_size,0.5,1.0);}/* Now a hard threshold to make sure the frame fits in VBV. * This one is mostly for I-frames. */doublebits=predict_size(&rcc->pred[h->sh.i_type],q,rcc->last_satd);/* For small VBVs, allow the frame to use up the entire VBV. */doublemax_fill_factor=h->param.rc.i_vbv_buffer_size>=5*h->param.rc.i_vbv_max_bitrate/rcc->fps?2:1;/* For single-frame VBVs, request that the frame use up the entire VBV. */doublemin_fill_factor=rcc->single_frame_vbv?1:2;if(bits>rcc->buffer_fill/max_fill_factor){doubleqf=x264_clip3f(rcc->buffer_fill/(max_fill_factor*bits),0.2,1.0);q/=qf;bits*=qf;}if(bits<rcc->buffer_rate/min_fill_factor){doubleqf=x264_clip3f(bits*min_fill_factor/rcc->buffer_rate,0.001,1.0);q*=qf;}q=X264_MAX(q0,q);
minGOP vbv 调整
B 帧 QP 不直接被 VBV 调整,它由 P 帧加一个偏移量得到。这一步检查当前 P 帧和(编码顺序)到下一个 P 帧之前的 B 帧的复杂度。适当调低 qscale (调高码率预算),使得本 minGOPher 过后,缓存区没有上溢。
这些分析的结果主要用于Viterbi算法中自适应B帧的放置。Viterbi 算法的输出不仅仅在下一帧的类型判断时使用到,而且在后面N帧的类型判断中会用到,其中N是lookahead的大小。该计划实际上是一个队列:it changes over time as frames are pulled from one end and encoded using the specified frame types, frames are added to the other end as new frames enter the encoder, and the plan is recalculated. 该计划的存在对于宏块树非常重要:它意味着很多需要知道未来帧帧类型的算法,有个可信赖的精准预测。即使GOP的结构是变化的。
/* constants chosen to result in approximately the same overall bitrate as without AQ. * FIXME: while they're written in 5 significant digits, they're only tuned to 2. */floatstrength;floatavg_adj=0.f;floatbias_strength=0.f;if(h->param.rc.i_aq_mode==X264_AQ_AUTOVARIANCE||h->param.rc.i_aq_mode==X264_AQ_AUTOVARIANCE_BIASED){floatbit_depth_correction=1.f/(1<<(2*(BIT_DEPTH-8)));floatavg_adj_pow2=0.f;for(intmb_y=0;mb_y<h->mb.i_mb_height;mb_y++)for(intmb_x=0;mb_x<h->mb.i_mb_width;mb_x++){uint32_tenergy=ac_energy_mb(h,mb_x,mb_y,frame);floatqp_adj=powf(energy*bit_depth_correction+1,0.125f);frame->f_qp_offset[mb_x+mb_y*h->mb.i_mb_stride]=qp_adj;avg_adj+=qp_adj;avg_adj_pow2+=qp_adj*qp_adj;}avg_adj/=h->mb.i_mb_count;avg_adj_pow2/=h->mb.i_mb_count;strength=h->param.rc.f_aq_strength*avg_adj;avg_adj=avg_adj-0.5f*(avg_adj_pow2-14.f)/avg_adj;bias_strength=h->param.rc.f_aq_strength;}elsestrength=h->param.rc.f_aq_strength*1.0397f;for(intmb_y=0;mb_y<h->mb.i_mb_height;mb_y++)for(intmb_x=0;mb_x<h->mb.i_mb_width;mb_x++){floatqp_adj;intmb_xy=mb_x+mb_y*h->mb.i_mb_stride;if(h->param.rc.i_aq_mode==X264_AQ_AUTOVARIANCE_BIASED){qp_adj=frame->f_qp_offset[mb_xy];qp_adj=strength*(qp_adj-avg_adj)+bias_strength*(1.f-14.f/(qp_adj*qp_adj));}elseif(h->param.rc.i_aq_mode==X264_AQ_AUTOVARIANCE){qp_adj=frame->f_qp_offset[mb_xy];qp_adj=strength*(qp_adj-avg_adj);}else{uint32_tenergy=ac_energy_mb(h,mb_x,mb_y,frame);qp_adj=strength*(x264_log2(X264_MAX(energy,1))-(14.427f+2*(BIT_DEPTH-8)));}if(quant_offsets)qp_adj+=quant_offsets[mb_xy];frame->f_qp_offset[mb_xy]=frame->f_qp_offset_aq[mb_xy]=qp_adj;if(h->frames.b_have_lowres)frame->i_inv_qscale_factor[mb_xy]=x264_exp2fix8(qp_adj);}