在编码器中,码率控制有两个重要的参数,1 时间基准 2 当前帧时间戳
一 为什么需要这两个
因为编码器的码率控制是以时间s为单位的,比如1000kbps,意思是1s有1000kbits的数据量,然后在编码器中并没有时间的概念,
只有时间基准和时间戳,这两个合并可以计算出来时间,所以编码器的码率控制都是以这两个参数为基础计算出来的。
时间基准:很多编码器设置的都是1/fps, 但是也有时候需要直接传入ms
为单位的帧pts,这样的话,时间基准就需要设置为1/1000, 还有如果像ts/mp4格式解码出来的视频帧,时间基准是1/90000,这样编码
的时候就设置成1/90000。
时间戳:当时间基准为1/fps时, 时间戳每次+1,依次递增,这样编码出来的帧之后,需要把时间戳scale为容器(flv,rtmp,mp4, ts等)需要的基准。
看下x264编码器中时间戳在码率控制中的作用。以下分析代码:
enum pic_struct_e
{
PIC_STRUCT_AUTO = 0, // automatically decide (default)
PIC_STRUCT_PROGRESSIVE = 1, // progressive frame
// "TOP" and "BOTTOM" are not supported in x264 (PAFF only)
PIC_STRUCT_TOP_BOTTOM = 4, // top field followed by bottom
PIC_STRUCT_BOTTOM_TOP = 5, // bottom field followed by top
PIC_STRUCT_TOP_BOTTOM_TOP = 6, // top field, bottom field, top field repeated
PIC_STRUCT_BOTTOM_TOP_BOTTOM = 7, // bottom field, top field, bottom field repeated
PIC_STRUCT_DOUBLE = 8, // double frame
PIC_STRUCT_TRIPLE = 9, // triple frame
};
这个枚举标示了一个帧需要占用多少个时间基准单元,这种是在设置的时间基准为1/fps,并且关闭了b_vfr_input的时候使用的。
b_vfr_input意思是变动帧率输入,这样编码器计算帧之间的duration, 直接用前后帧时间戳相减。见代码
h->lookahead->next.list[i]->i_duration = 2 * (h->lookahead->next.list[i+1]->i_pts - h->lookahead->next.list[i]->i_pts);
当b_vfr_input关闭的时候,时间基准需要被设置为1/fps, 要不然无法计算出帧duration。见代码
h->lookahead->next.list[i]->i_duration = delta_tfi_divisor[h->lookahead->next.list[i]->i_pic_struct];
delta_tfi_divisor是一个数组,预先设定了,某种类型的帧,duration为多少个时间基准单元。
接下来计算得到时间基准之后,开始用于码率控制。以crf码控为例
clip_qscale 为x264编码器中主要的帧级别的码控函数
double fenc_cpb_duration = (double)h->fenc->i_cpb_duration *
h->sps->vui.i_num_units_in_tick / h->sps->vui.i_time_scale;
首先根据时间基准把duration换算成以秒为单位的时间。
total_duration += last_duration; //累加到当前编码帧的duration中,得到当前编码的总帧的duration,
通过总的编码帧duration以及
for( int j = 0; buffer_fill_cur >= 0 && buffer_fill_cur <= rcc->buffer_size; j++ )
{//先通过当前给定的qp值计算出来,需要消耗多少bits
total_duration += last_duration;
buffer_fill_cur += rcc->vbv_max_rate * last_duration;
int i_type = h->fenc->i_planned_type[j];
int i_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];
}
然后按照当前的vbv码率控制给的参数计算出来,total_duration时长的帧最多能消耗多少bits
target_fill = X264_MIN( rcc->buffer_fill + total_duration * rcc->vbv_max_rate * 0.5, rcc->buffer_size * 0.5 );
然后比较这两,如果不足,就调大qp值。 qp的默认值为crf值
if( buffer_fill_cur < target_fill )
{
q *= 1.01;
terminate |= 1;
continue;
}
//VBV码率控制buf的限制,和上面的maxrate控制类似
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;
}
最多调整1000次,最终得到一个合适的qp值。