(本文主要是自己的学习笔记,如果有误,请留言,一起讨论和更正。)
这里采用x264的代码进行走读的方式,来学习qp在码流控制中过程。

在ABR模式下,当我们设置一个bitrate的平均码率以后,x264是如何通过控制码率的算法来尽量达到这个我们设置的平均码率的呢?
要了解这个过程,可能我们还需要了解另外一个参数,就是qp编码量化值的概念。

接下来我们首先qp量化值,在上层的设置中,我们需要知道有哪些参数的设置直接影响我们编码过程中对帧的qp值的计算?

rc中的影响参数:
int i_qp_constant; /* 0-51 constant:常数,常量,这个值其实基本是相当于设置为P帧用的*/
int i_qp_min; /* 允许的最小量化值 || min allowed QP value */
int i_qp_max; /* 允许的最大量化值 || max allowed QP value */
int i_qp_step; /* 帧间最大量化步长 || max QP step between frames */
float f_ip_factor; //factor:因子, 因数,要素,I帧和P帧之间的因子
float f_pb_factor; //P帧和B帧之间的计算量发因子
/*量化曲线(quantizer curve)压缩因子。0.0 => 恒定比特率,1.0 => 恒定量化值。*/
/*qp时域变化灵活度参数,值越低,qp值变动越大*/
float f_qcompress; /* 0.0 => cbr, 1.0 => constant qp */
/*时间上模糊量化,减少QP的波动*/
float f_qblur; /* temporally blur quants */
/*时间上模糊复杂性,减少QP的波动(before curve compression)*/
float f_complexity_blur; /* temporally blur complexity */ mb-tree结构中的影响参数:
/*色度量化步长偏移量*/
int i_chroma_qp_offset;/*这个参数值,主要是用于调整luma和chroma平面间,qp值差异的一个参数*/

先从打开编码器的函数x264_encoder_open开始
在x264_sps_init函数中,我们可以看到设置sps的mb_width和mb_height的计算

sps->i_mb_width = ( param->i_width +  ) / ;
sps->i_mb_height= ( param->i_height + ) / ;

所以设置参数:

h->mb.i_mb_width = h->sps->i_mb_width;
h->mb.i_mb_height = h->sps->i_mb_height;
h->mb.i_mb_count = h->mb.i_mb_width * h->mb.i_mb_height;

同时在这个地方之前,还有一个初始化函数:

h->chroma_qp_table = i_chroma_qp_table +  + h->pps->i_chroma_qp_index_offset;
if( x264_cqm_init( h ) < ) //这个函数没怎么看懂里面的实现,主要内容应该是对量化矩阵的初始化进行设置

接下来看到码率控制函数的第一个函数x264_ratecontrol_new:

if( h->param.rc.b_mb_tree )//这里如果打开了mb-tree功能的话
{
h->param.rc.f_pb_factor = ;
rc->qcompress = ;
}
else
rc->qcompress = h->param.rc.f_qcompress;
rc->bitrate = h->param.rc.i_bitrate * (h->param.b_avcintra_compat ? . : .);
rc->rate_tolerance = h->param.rc.f_rate_tolerance;/*设置码率容忍度*/
rc->nmb = h->mb.i_mb_count;/*宏块的数量*/
rc->last_non_b_pict_type = -;
rc->cbr_decay = 1.0;

接着是插入了一个码率初始化重配置函数:x264_ratecontrol_init_reconfigurable
这个函数里面主要其实对vbv的设置,即根据我们的参数,他调整一些vbv缓冲区大小,以及计算crf模式下的qp相关参数设置:

if( h->param.rc.i_rc_method == X264_RC_CRF )
{
/* Arbitrary rescaling to make CRF somewhat similar to QP.
* Try to compensate for MB-tree's effects as well. */
/*意思是:任意重新调整rate_factor_constant码率因子这个参数,使CRF有点类似于QP*/
/*同时考虑宏块数mb-tree带来的影响*/
/*从代码意思来看的话,就是当我们在crf模式下设置了i_rf_constant值以后,
因为f_qcompress和mb-tree的影响,我们需要重新调整计算这个值
*/
double base_cplx = h->mb.i_mb_count * (h->param.i_bframe ? : );
double mbtree_offset = h->param.rc.b_mb_tree ? (1.0-h->param.rc.f_qcompress)*13.5 : ;
rc->rate_factor_constant = pow( base_cplx, - rc->qcompress )
/ qp2qscale( h->param.rc.f_rf_constant + mbtree_offset + QP_BD_OFFSET );
}
//如果有设置VBV的话,这个地方计算根据设置的一些VBV相关的参数,
if( h->param.rc.i_vbv_max_bitrate > && h->param.rc.i_vbv_buffer_size > )
if( rc->b_vbv_min_rate )
h->param.rc.i_vbv_max_bitrate = h->param.rc.i_bitrate; if( h->param.rc.i_vbv_buffer_size < (int)(h->param.rc.i_vbv_max_bitrate / rc->fps) )
{
h->param.rc.i_vbv_buffer_size = h->param.rc.i_vbv_max_bitrate / rc->fps;
} int kilobit_size = h->param.b_avcintra_compat ? : ;
int vbv_buffer_size = h->param.rc.i_vbv_buffer_size * kilobit_size;
int vbv_max_bitrate = h->param.rc.i_vbv_max_bitrate * kilobit_size; h->sps->vui.hrd.i_bit_rate_unscaled = vbv_max_bitrate;
h->sps->vui.hrd.i_cpb_size_unscaled = vbv_buffer_size; if( rc->b_vbv_min_rate )
rc->bitrate = (double)h->param.rc.i_bitrate * kilobit_size;
rc->buffer_rate = vbv_max_bitrate / rc->fps;/*得到一帧的平均码流最大值*/
rc->vbv_max_rate = vbv_max_bitrate; /*vbv的的最大码率*/
rc->buffer_size = vbv_buffer_size; /*vbv缓冲区大小,这个是用户设置的那个vbv大小么?*/
rc->single_frame_vbv = rc->buffer_rate * 1.1 > rc->buffer_size;/*这里判断是否是单帧的vbv*/
rc->cbr_decay = 1.0 - rc->buffer_rate / rc->buffer_size
* 0.5 * X264_MAX(, 1.5 - rc->buffer_rate * rc->fps / rc->bitrate);/*计算衰减系数*/
if( h->param.rc.i_rc_method == X264_RC_CRF && h->param.rc.f_rf_constant_max )
{
rc->rate_factor_max_increment = h->param.rc.f_rf_constant_max - h->param.rc.f_rf_constant;
if( rc->rate_factor_max_increment <= )
{
rc->rate_factor_max_increment = ;
}
}

接下来针对ABR平均码率模式下的码率参数初始化值

if( rc->b_abr )/*在平均码率ABR模式下的设置*/
{
/* FIXME ABR_INIT_QP is actually used only in CRF */
#define ABR_INIT_QP (( h->param.rc.i_rc_method == X264_RC_CRF ? h->param.rc.f_rf_constant : 24 ) + QP_BD_OFFSET)
rc->accum_p_norm = .;
rc->accum_p_qp = ABR_INIT_QP * rc->accum_p_norm;/*这里计算决定I frame的量化qp值,从这个计算可以看出来,I帧的量化值QP是比较低的*/
/* estimated ratio that produces a reasonable QP for the first I-frame */
/*预估比率,对于第一个I帧计算一个可信的qp值*/
rc->cplxr_sum = . * pow( 7.0e5, rc->qcompress ) * pow( h->mb.i_mb_count, 0.5 );
rc->wanted_bits_window = 1.0 * rc->bitrate / rc->fps;
rc->last_non_b_pict_type = SLICE_TYPE_I;
}
/*计算ip,pb的offset值,通过ip,pb factor因子*/
rc->ip_offset = 6.0 * log2f( h->param.rc.f_ip_factor );
rc->pb_offset = 6.0 * log2f( h->param.rc.f_pb_factor );
rc->qp_constant[SLICE_TYPE_P] = h->param.rc.i_qp_constant;/*可以看的出来,qp的值我们在上层其实是设置给P帧用的*/
rc->qp_constant[SLICE_TYPE_I] = x264_clip3( h->param.rc.i_qp_constant - rc->ip_offset + 0.5, , QP_MAX );/*然后I帧和B帧都是通过factor因子来计算出他们的qp值*/
rc->qp_constant[SLICE_TYPE_B] = x264_clip3( h->param.rc.i_qp_constant + rc->pb_offset + 0.5, , QP_MAX );
h->mb.ip_offset = rc->ip_offset + 0.5; rc->lstep = pow( , h->param.rc.i_qp_step / 6.0 );/*这里计算帧和帧之间的最大量化步长值*/
rc->last_qscale = qp2qscale( );/*线性量化器:即拉格朗日乘子*/

=================================================================================================
跳过x264_encoder_open函数,我们看一下x264内部的码率控制算法,这个里面有几个很重要的函数:

我们先从x264_ratecontrol_start函数开始,这个函数的从前面一段的注释介绍来看的话,意思是在编码一帧数据之前,先为他获取一个合适的QP量化值(通过预测)。

x264_ratecontrol_start相关内容分析:

if( rc->b_vbv )
{
rc->row_pred = &rc->row_preds[h->sh.i_type];
rc->buffer_rate = h->fenc->i_cpb_duration * rc->vbv_max_rate * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
update_vbv_plan( h, overhead );/*根据所有帧计划的大小来临时更新vbv*/ const x264_level_t *l = x264_levels;
while( l->level_idc != && l->level_idc != h->param.i_level_idc )
l++; int mincr = l->mincr;/*获取h264 level级别上规定的最小压宿比*/
//计算一帧数据的最大多大
if( h->i_frame == )
{
//第一帧做特殊处理
//384 * ( Max( PicSizeInMbs, fR * MaxMBPS ) + MaxMBPS * ( tr( 0 ) - tr,n( 0 ) ) ) / MinCR
double fr = . / ;
int pic_size_in_mbs = h->mb.i_mb_width * h->mb.i_mb_height;
rc->frame_size_maximum = * BIT_DEPTH * X264_MAX( pic_size_in_mbs, fr*l->mbps ) / mincr;
}
else
{
//384 * MaxMBPS * ( tr( n ) - tr( n - 1 ) ) / MinCR
rc->frame_size_maximum = * BIT_DEPTH * ((double)h->fenc->i_cpb_duration * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale) * l->mbps / mincr;
}
}
//然后针对不同的模式,计算预估得到一个合适的qp量化值
if( rc->b_abr )
{
q = qscale2qp( rate_estimate_qscale( h ) );
}
else if( rc->b_2pass )
{
rce->new_qscale = rate_estimate_qscale( h );
q = qscale2qp( rce->new_qscale );
}
else /* CQP */
{
/*这里再CQP模式下,对于参考B帧的情况下,qp取值为B帧和P帧的QP值的一半,
而对于其他帧类型,直接取预设的qp值就OK*/
if( h->sh.i_type == SLICE_TYPE_B && h->fdec->b_kept_as_ref )
q = ( rc->qp_constant[ SLICE_TYPE_B ] + rc->qp_constant[ SLICE_TYPE_P ] ) / ;
else
q = rc->qp_constant[ h->sh.i_type ]; if( zone )
{
if( zone->b_force_qp )
q += zone->i_qp - rc->qp_constant[SLICE_TYPE_P];
else
q -= *log2f( zone->f_bitrate_factor );
}
}
/*更新累积的qp信息*/
accum_p_qp_update( h, rc->qpm );

接下来重点在分析一下中间调用的rate_estimate_qscale函数内容.
这个函数的的主要内容是做根据码率预估量化步长值
rate_estimate_qscale内容:(参考https://www.twblogs.net/a/5c993101bd9eee491b62698b)

該函數主要進行qscale的初始化和調整,是碼率控制部分的核心之一,另一個是get_scale。
0、計算SATD和圖像的模糊複雜度
1、在get_scale中,按複雜度採用指數模型得到qscale
rcc->last_qscale = pow( rce->blurred_complexity, 1 - rcc->qcompress )/rate_factor
2、在get_scale中根據複雜度和目標比特數調整qp
q /= rate_factor; //rcc->wanted_bits_window / rcc->cplxr_sum
3、根據已編碼幀的實際比特數和目標比特數的偏差再次調整qp
overflow = x264_clip3f( 1.0 + (total_bits - wanted_bits) / abr_buffer, .5, 2 );
q *= overflow;

源代码的内容,跟上面有些许出入,可能是因为源码的版本问题吧。
x264_ratecontrol_start:

if( rc->b_vbv )
{
rc->row_pred = &rc->row_preds[h->sh.i_type];
rc->buffer_rate = h->fenc->i_cpb_duration * rc->vbv_max_rate * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
update_vbv_plan( h, overhead );/*根据所有帧计划的大小来临时更新vbv*/ const x264_level_t *l = x264_levels;
while( l->level_idc != && l->level_idc != h->param.i_level_idc )
l++; int mincr = l->mincr;/*获取h264 level级别上规定的最小压宿比*/
if( h->i_frame == )
{
//第一帧做特殊处理
//384 * ( Max( PicSizeInMbs, fR * MaxMBPS ) + MaxMBPS * ( tr( 0 ) - tr,n( 0 ) ) ) / MinCR
double fr = . / ;
int pic_size_in_mbs = h->mb.i_mb_width * h->mb.i_mb_height;
rc->frame_size_maximum = * BIT_DEPTH * X264_MAX( pic_size_in_mbs, fr*l->mbps ) / mincr;
}
else
{
//384 * MaxMBPS * ( tr( n ) - tr( n - 1 ) ) / MinCR
rc->frame_size_maximum = * BIT_DEPTH * ((double)h->fenc->i_cpb_duration * h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale) * l->mbps / mincr;
} if( rc->b_abr )
{
q = qscale2qp( rate_estimate_qscale( h ) );//这个函数是最关键的函数,预测调整量化值的获取
}
else if( rc->b_2pass )
{
rce->new_qscale = rate_estimate_qscale( h );
q = qscale2qp( rce->new_qscale );
}
else /* CQP */
{
/*这里再CQP模式下,对于参考B帧的情况下,qp取值为B帧和P帧的QP值的一半,
而对于其他帧类型,直接取预设的qp值就OK*/
if( h->sh.i_type == SLICE_TYPE_B && h->fdec->b_kept_as_ref )
q = ( rc->qp_constant[ SLICE_TYPE_B ] + rc->qp_constant[ SLICE_TYPE_P ] ) / ;
else
q = rc->qp_constant[ h->sh.i_type ];
} rc->qpa_rc = rc->qpa_rc_prev = rc->qpa_aq = rc->qpa_aq_prev = ;
rc->qp = x264_clip3( q + 0.5f, , QP_MAX );
h->fdec->f_qp_avg_rc = h->fdec->f_qp_avg_aq = rc->qpm = q;
if( rce )
rce->new_qp = rc->qp;
/*更新累积的qp信息*/
accum_p_qp_update( h, rc->qpm );

而accum_p_qp_update的函数内容,主要是更新qp的累加器中的值:

x264_ratecontrol_t *rc = h->rc;
rc->accum_p_qp *= .;
rc->accum_p_norm *= .;
rc->accum_p_norm += ;
if( h->sh.i_type == SLICE_TYPE_I )
rc->accum_p_qp += qp + rc->ip_offset;
else
rc->accum_p_qp += qp;

这里总结一下:
1:在CQP的模式下:
1)如果这个帧的类型是一个可以被参考的B帧类型,那么这个帧的QP=(b_QP+p_QP)/2来得到
2)否则直接获取之前对于每个帧类型的相应常量QP值
2:如果其他模式的话,通过rate_estimate_qscale来预测获取一个适当的QP值。

在这中间,有一个很复杂也很关键的函数rate_estimate_qscale,他是预测量化QP值的函数。
这个函数主要是根据到目前为止使用的实际bits,更新1帧的qscale。
看下这个函数rate_estimate_qscale源码里面做了什么:

//获取统计到的总bit数
int64_t total_bits = *(h->stat.i_frame_size[SLICE_TYPE_I]
+ h->stat.i_frame_size[SLICE_TYPE_P]
+ h->stat.i_frame_size[SLICE_TYPE_B])
- rcc->filler_bits_sum;
if( pict_type == SLICE_TYPE_B )
{
/* B-frames don't have independent ratecontrol, but rather get the
* average QP of the two adjacent P-frames + an offset */
/*上面这句注释的意思是说,B帧没有独立的码率控制,而采取的方式是获取两个临近的P帧的平均QP值和一个偏移值offset
但从代码来看的话,他这里对临近两个帧的类型是有做区分的,I帧和P帧的处理方式不同*/
int i0 = IS_X264_TYPE_I(h->fref_nearest[]->i_type);
int i1 = IS_X264_TYPE_I(h->fref_nearest[]->i_type);
int dt0 = abs(h->fenc->i_poc - h->fref_nearest[]->i_poc);/*求这个帧在图像播放顺序中和前面帧的播放顺序的差值的绝对值?*/
int dt1 = abs(h->fenc->i_poc - h->fref_nearest[]->i_poc);
float q0 = h->fref_nearest[]->f_qp_avg_rc;/*获取临近帧0在码率控制下得到的平均qp值*/
float q1 = h->fref_nearest[]->f_qp_avg_rc;/*获取临近帧1在码率控制下得到的平均qp值*/ if( h->fref_nearest[]->i_type == X264_TYPE_BREF )
q0 -= rcc->pb_offset/;
if( h->fref_nearest[]->i_type == X264_TYPE_BREF )
q1 -= rcc->pb_offset/; if( i0 && i1 )/*如果最临近的两个帧是I帧,那么qp=这两个q的平均值+ip_offset*/
q = (q0 + q1) / + rcc->ip_offset;
else if( i0 )/*如果临近帧0为I帧,就取另外一个临近帧的q*/
q = q1;
else if( i1 )
q = q0;
else/*如果前面两个临近帧都没有I帧的话,根据临近关系,取加权平均值*/
q = (q0*dt1 + q1*dt0) / (dt0 + dt1); /*如果这个帧会被作为参考帧的话,需要稍微降低qp值,即参考的B帧量化值需要比没有参考的B帧的量化值要稍微跟低一些*/
if( h->fenc->b_kept_as_ref )
q += rcc->pb_offset/;
else
q += rcc->pb_offset; /*预测当前B帧的帧数据大小,根据前面参考帧的SATD和当前选择的qp量化值来预测这个帧的大小*/
if( rcc->b_2pass && rcc->b_vbv )
rcc->frame_size_planned = qscale2bits( &rce, qp2qscale( q ) );
else
rcc->frame_size_planned = predict_size( rcc->pred_b_from_p, qp2qscale( q ), h->fref[][h->i_ref[]-]->i_satd ); if( rcc->b_vbv )/*在vbv下,还需要限制*/
rcc->frame_size_planned = X264_MIN( rcc->frame_size_planned, rcc->frame_size_maximum );
h->rc->frame_size_estimated = rcc->frame_size_planned;/*记录下这个预测的帧数据大小值*/ /* For row SATDs */
if( rcc->b_vbv )
rcc->last_satd = x264_rc_analyse_slice( h );/*计算SATD*/
rcc->qp_novbv = q;
return qp2qscale( q );
}
else{
//这里只分析1 pass的过程
double abr_buffer = * rcc->rate_tolerance * rcc->bitrate;/*自适应码率缓冲区大小*/
/*上面注释的意思是说:根据前面已经应用的所有帧,以及我们期望的平均码率来计算量化值。
主要是依据当前帧的复杂度相对于到目前为止的平均复杂度调整该定量(使用2pass RCEQ)。
如果到目前为止,总大小和目标相差的比较远,我们就会调高或者调小这个定量。
结果:根据码率的容忍度大小,在品质和码率精度上权衡结果。如果是一个比较大的码率宽容度的话,
那比特率分配接近于2pass模式:
总结:1、計算出一個Qp值,如果將該值應用到當前所有的幀,則可以獲得目標平均嗎率
2、根據當前幀的複雜度和平均複雜度的差距,調整QP
3、如果total size和目標相差太多,再調整QP
*/
double wanted_bits, overflow = ;
/*计算SATD值,这个SATD值可以衡量生成的码流大小。
参考:SATD是将残差做hadamard变换再取绝对值的总和,它作为一种简单的时频变换,能在一定程度上衡量生成码流的大小*/
rcc->last_satd = x264_rc_analyse_slice( h );//计算SATD值,这个SATD值
rcc->short_term_cplxsum *= 0.5;/*从代码来看的话,这个地方其实就是SATD值的记录*/
rcc->short_term_cplxcount *= 0.5;
/*在很早的版本中:rcc->short_term_cplxsum += rcc->last_satd*/
rcc->short_term_cplxsum += rcc->last_satd / (CLIP_DURATION(h->fenc->f_duration) / BASE_FRAME_DURATION);
rcc->short_term_cplxcount ++; /*get_qscale会根据模糊复杂度用一个指数模型计算出一个q,然后再根据码率因子进行调整来得到qp*/
if( h->param.rc.i_rc_method == X264_RC_CRF )
{
q = get_qscale( h, &rce, rcc->rate_factor_constant, h->fenc->i_frame );
}
else
{
//在ABR模式下,wanted_bits_window代表理想值,而cplxr_sum代表预测值,这两个的比例作为调整qp的码率因子
q = get_qscale( h, &rce, rcc->wanted_bits_window / rcc->cplxr_sum, h->fenc->i_frame ); /*上面的注释的意思是:ABR模式可能在CBR中会适得其反,所以就甭管了*/
/*即便是帧复杂度为0,也不用管他*/
if( !rcc->b_vbv_min_rate && rcc->last_satd )
{
// FIXME is it simpler to keep track of wanted_bits in ratecontrol_end?
int i_frame_done = h->i_frame + - h->i_thread_frames;
double time_done = i_frame_done / rcc->fps;
if( h->param.b_vfr_input && i_frame_done > )
time_done = ((double)(h->fenc->i_reordered_pts - h->i_reordered_pts_delay)) * h->param.i_timebase_num / h->param.i_timebase_den;
wanted_bits = time_done * rcc->bitrate;/*根据目前帧的时间位置在关系来算我们想要这个帧的大小*/
if( wanted_bits > )
{
abr_buffer *= X264_MAX( , sqrt( time_done ) );
overflow = x264_clip3f( 1.0 + (total_bits - wanted_bits) / abr_buffer, ., );
q *= overflow;/*根据帧数据的溢出情况,我们来调整量化值,调整方式是q*overflow的比例*/
}
}
}
if( pict_type == SLICE_TYPE_I && h->param.i_keyint_max >
&& rcc->last_non_b_pict_type != SLICE_TYPE_I )
{
/*这里针对单独的I帧的时候,直接通过累加器中的qp(accum_p_qp)和accum_p_norm的比值来直接获得I帧的q量化
其实原因也很简单,I帧因为比较特殊,所以这个地方是特殊处理的*/
q = qp2qscale( rcc->accum_p_qp / rcc->accum_p_norm );
q /= fabs( h->param.rc.f_ip_factor );
}
else if( h->i_frame > )
{
if( h->param.rc.i_rc_method != X264_RC_CRF )
{
double lmin = rcc->last_qscale_for[pict_type] / rcc->lstep;
double lmax = rcc->last_qscale_for[pict_type] * rcc->lstep;
if( overflow > 1.1 && h->i_frame > )
lmax *= rcc->lstep;
else if( overflow < 0.9 )
lmin /= rcc->lstep;
/*通过上面计算的溢出情况和码率控制中设置的帧间量化步长对量化值再做一个约束*/
q = x264_clip3f(q, lmin, lmax);/*取一个合适的中间值*/
}
}
else if( h->param.rc.i_rc_method == X264_RC_CRF && rcc->qcompress != )
{
q = qp2qscale( ABR_INIT_QP ) / fabs( h->param.rc.f_ip_factor );
}
rcc->qp_novbv = qscale2qp( q ); //FIXME use get_diff_limited_q() ?
/*按一階模型根據satd估計分配的bits,結合vbv限制,修正qscale*/
q = clip_qscale( h, pict_type, q );/*这里会对量化值q再做一个约束性的计算,通过预测算出来的码率溢出情况*/ rcc->last_qscale_for[pict_type] = rcc->last_qscale = q; }

而在编码一帧数据以后,我们通过x264_ratecontrol_end函数来上报反馈统计数据信息。
x264_ratecontrol_end函数的内容:

/*统计宏块相关信息*/
h->stat.frame.i_mb_count_skip = mbs[P_SKIP] + mbs[B_SKIP];
h->stat.frame.i_mb_count_i = mbs[I_16x16] + mbs[I_8x8] + mbs[I_4x4];
h->stat.frame.i_mb_count_p = mbs[P_L0] + mbs[P_8x8];
for( int i = B_DIRECT; i < B_8x8; i++ )
h->stat.frame.i_mb_count_p += mbs[i];
/*计算当前重构帧的qp信息*/
h->fdec->f_qp_avg_rc = rc->qpa_rc /= h->mb.i_mb_count;
h->fdec->f_qp_avg_aq = (float)rc->qpa_aq / h->mb.i_mb_count;
h->fdec->f_crf_avg = h->param.rc.f_rf_constant + h->fdec->f_qp_avg_rc - rc->qp_novbv; /*如果是ABR模式的话*/
if( rc->b_abr )
{
/*计算复杂度之和*/
if( h->sh.i_type != SLICE_TYPE_B )
rc->cplxr_sum += bits * qp2qscale( rc->qpa_rc ) / rc->last_rceq;
else
{
/* Depends on the fact that B-frame's QP is an offset from the following P-frame's.
* Not perfectly accurate with B-refs, but good enough. */
rc->cplxr_sum += bits * qp2qscale( rc->qpa_rc ) / (rc->last_rceq * fabs( h->param.rc.f_pb_factor ));
}
rc->cplxr_sum *= rc->cbr_decay;/*这个地方需要乘以一个衰减系数,这个衰减系数是通过vbv的大小和码率的计算在前期得到的*/
rc->wanted_bits_window += h->fenc->f_duration * rc->bitrate;/*这个地方统计,因为码率我们是说1s的时间内,产生多少的数据量,所以这里需要用时间单位来计算*/
rc->wanted_bits_window *= rc->cbr_decay;
}
/*如果宏块qp可变*/
if( h->mb.b_variable_qp )
{
if( h->sh.i_type == SLICE_TYPE_B )
{
rc->bframe_bits += bits;
if( h->fenc->b_last_minigop_bframe )
{
update_predictor( rc->pred_b_from_p, qp2qscale( rc->qpa_rc ),
h->fref[][h->i_ref[]-]->i_satd, rc->bframe_bits / rc->bframes );
rc->bframe_bits = ;
}
}
}
// 更新VBV, 控制下溢,上溢
*filler = update_vbv( h, bits );
rc->filler_bits_sum += *filler * ;

总结:
1)在ABR模式下,f_rf_constant和i_qp_constant这两个参数是无用的,只有在CRF的时候,才有效,默认的初始值QP为24。
同时在ABR模式下影响比较大的主要有这么几个参数:
f_ip_factor,f_pb_factor; //帧类型之间的因子
i_qp_min,i_qp_max; //qp的最小量化和最大量化值,放置部分画质被压烂
i_qp_step; //帧间最大量化步长,主要是用于约束帧于帧之间的量化值
i_bitrate; //设置平均码率
f_rate_tolerance; //码率的容忍度,也就是运行上下码率的浮动
i_vbv_max_bitrate; /*平均码率模式下,最大瞬时码率,默认0(与-B设置相同) */
i_vbv_buffer_size; /*码率控制缓冲区的大小,单位kbit,默认0 ,*/
f_qcompress //

转载请注明出处:https://www.cnblogs.com/lihaiping/p/11891564.html

(原)x264代码中的码流控制学习的更多相关文章

  1. 剥开比原看代码12:比原是如何通过/create-account-receiver创建地址的?

    作者:freewind 比原项目仓库: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchai ...

  2. 剥开比原看代码11:比原是如何通过接口/create-account创建帐户的

    作者:freewind 比原项目仓库: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchai ...

  3. 剥开比原看代码03:比原是如何监听p2p端口的

    作者:freewind 比原项目仓库: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchai ...

  4. grunt-css-sprite css 代码中的切片合并

    安装插件:npm install grunt-css-sprite --save-dev grunt-css-sprite主要功能:1.对 css 文件进行处理,收集切片序列,生成雪碧图2.在原css ...

  5. Derek解读Bytom源码-protobuf生成比原核心代码

    作者:Derek 简介 Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchain/bytom ...

  6. x264代码剖析(十五):核心算法之宏块编码中的变换编码

    x264代码剖析(十五):核心算法之宏块编码中的变换编码 为了进一步节省图像的传输码率.须要对图像进行压缩,通常採用变换编码及量化来消除图像中的相关性以降低图像编码的动态范围.本文主要介绍变换编码的相 ...

  7. 从别人的代码中学习golang系列--01

    自己最近在思考一个问题,如何让自己的代码质量逐渐提高,于是想到整理这个系列,通过阅读别人的代码,从别人的代码中学习,来逐渐提高自己的代码质量.本篇是这个系列的第一篇,我也不知道自己会写多少篇,但是希望 ...

  8. Linux内核设计第四周学习总结 使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用

    陈巧然原创作品 转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验目的: 使用库函数A ...

  9. vnpy源码阅读学习(5):关于MainEngine的代码阅读

    关于MainEngine的代码阅读 在入口文件中,我们看到了除了窗体界面的产生,还有关于MainEngine和EventEngin部分.今天来学习下MainEngine的代码. 首先在run代码中,我 ...

随机推荐

  1. Android Studio--Logcat

    他只是坐在那里,嘴里说:“做这个!做那个!当然,什么都不会发生,光说不做是没有用的” --哈里·杜鲁门“论<总统的权利>” 移动端的技术演进愈演愈烈,原生突破口已被打破. flutter. ...

  2. SpringMVC使用@Valid注解进行数据验证

    SpringMVC使用@Valid注解进行数据验证   from:https://blog.csdn.net/zknxx/article/details/52426771 我们在做Form表单提交的时 ...

  3. 201671010403 陈倩倩 实验十四 团队项目评审&课程学习总结

    一:实验名称:团队项目评审&课程学习总结 二:实验目的与要求 (1)掌握软件项目评审会流程: (2)反思总结课程学习内容. 三:实验步骤 任务一:按照团队项目结对评审名单,由项目组扮演乙方,结 ...

  4. 项目Beta冲刺(团队) ——随笔集合

    课程名称:软件工程1916|W(福州大学) 作业要求:项目β冲刺(团队) 团队名称:葫芦娃队 作业目标:汇总这次冲刺项目的所有随笔文件. 队员学号 队员昵称 博客地址 041602421 der hi ...

  5. Spring Boot 中的测试:JUnit

    官方文档:https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html

  6. oracle中删除表:drop、delete、truncate

    相同点,使用drop delete truncate 都会删除表中的内容 drop table 表名 delete from 表名(后面不跟where语句,则删除表中所有的数据) truncate t ...

  7. arduino入门笔记

    以 ARDUINO® UNO R3为例 一.将板子与电脑连接 初次使用会自动安装驱动. Arduino Uno通过USB连接到计算机或外部电源自动获取电源,因此此时能看到电源指示灯会亮. 我的L13也 ...

  8. python--模块学习之xml模块

    xml即可扩展标记语言,它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言. 本文主要学习的ElementTree是python的XML处理模块,它提供了一个轻量级的对象 ...

  9. linux查看大文件

    du -h --max-depth=1

  10. plotly-dash 简单使用(一)

    plotly-dash 是一个很不错的dashboard 开发平台,基于python 编写,提供了很便捷的dashboard 开发模型 同时扩展上也比较灵活我们可以编写自己的组件. 以下是一个简单的项 ...