首先明確一點(diǎn),VBV調(diào)整只針對(duì)I幀和P幀,B幀的q值是根據(jù)p幀來決定的
1、初始化
在encode_open--x264_ratecontrol_new--x264_ratecontrol_init_reconfigurable的時(shí)候會(huì)初始化VBV的參數(shù),VBV_BUFFER_SIZE、VBV_MAX_Bitrate
int kilobit_size = h->param.i_avcintra_class ? 1024 : 1000;
int vbv_buffer_size = h->param.rc.i_vbv_buffer_size * kilobit_size;//對(duì)應(yīng)--vbv-bufsize選項(xiàng)
int vbv_max_bitrate = h->param.rc.i_vbv_max_bitrate * kilobit_size;//對(duì)應(yīng)vbv-maxrate
...
if( rc->b_vbv_min_rate )
rc->bitrate = (double)h->param.rc.i_bitrate * kilobit_size;
rc->buffer_rate = vbv_max_bitrate / rc->fps;//最大碼率除以幀數(shù)即一幀的碼率
rc->vbv_max_rate = vbv_max_bitrate;
rc->buffer_size = vbv_buffer_size;
同時(shí)初始化buffer_fill_final、buffer_fill_final_min,表示buffer最后填充的字節(jié)數(shù),以及至少要填充多少才行
rc->buffer_fill_final =
rc->buffer_fill_final_min = rc->buffer_size * h->param.rc.f_vbv_buffer_init * h->sps->vui.i_time_scale;
//buffersize乘以至少要填充滿多少才能播放的系數(shù),最后再乘以一個(gè)量綱(每個(gè)文件的量綱不一樣,需要進(jìn)行轉(zhuǎn)化)
rc->b_vbv = 1;
rc->b_vbv_min_rate = !rc->b_2pass
&& h->param.rc.i_rc_method == X264_RC_ABR
&& h->param.rc.i_vbv_max_bitrate <= h->param.rc.i_bitrate;
2、逐幀參數(shù)初始化
encoder_encode編碼幀在編碼前,進(jìn)入x264_ratecontrol_start控制函數(shù),在這里面需要算出每一幀的qp值。
這里有幾個(gè)變量,需要特別注意
rc->buffer_fill //計(jì)劃緩沖區(qū),規(guī)劃的緩沖區(qū)大小,注意與buffer_fill_final量綱不同
rc->buffer_rate //每一幀編碼后,加大緩沖區(qū)的大小,因?yàn)橐粠怯袝r(shí)長(zhǎng)的
代碼如下
//時(shí)長(zhǎng)乘以 1s可以加到buffer_fill的大小,并換算成以time_scale的量綱
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(從函數(shù)名可以看出事更新預(yù)算的,后面還有個(gè)updata_vbv函數(shù))更新rc->buffer_fill,由于是多線程編碼,因此在更新1s內(nèi)的buffer緩存預(yù)算時(shí),需要將各個(gè)線程所產(chǎn)生的比特?cái)?shù)考慮在內(nèi)??创a
//buffer_fill_final_min的更新是編碼完一幀后,在updata_vbv中用編碼產(chǎn)生的大小來更新預(yù)算,與buffer_fill量綱不同
rcc->buffer_fill = h->thread[0]->rc->buffer_fill_final_min / h->sps->vui.i_time_scale;
if( h->i_thread_frames > 1 )
{
int j = h->rc - h->thread[0]->rc;
for( int i = 1; i < h->i_thread_frames; i++ )
{
x264_t *t = h->thread[ (j+i)%h->i_thread_frames ];
double bits = t->rc->frame_size_planned;
if( !t->b_thread_active )//該線程處于編碼狀態(tài)
continue;
bits = X264_MAX(bits, t->rc->frame_size_estimated);
rcc->buffer_fill -= bits;//每個(gè)線程產(chǎn)生的位數(shù)用來填充緩沖區(qū),所以預(yù)算減小
rcc->buffer_fill = X264_MAX( rcc->buffer_fill, 0 );
rcc->buffer_fill += t->rc->buffer_rate;//該幀時(shí)長(zhǎng)能擴(kuò)展的buffer大小
rcc->buffer_fill = X264_MIN( rcc->buffer_fill, rcc->buffer_size );
}
}
rcc->buffer_fill = X264_MIN( rcc->buffer_fill, rcc->buffer_size );
rcc->buffer_fill -= overhead;//每個(gè)NAL的頭部共40位
舉個(gè)例子來說,如圖所示是一個(gè)文件的碼率圖

那么在編碼的過程中,通過將每一幀開始前的buffer_fill打出來可以看到如下變化。一開始初始狀態(tài)下,約為80000的0.9倍,接著第一幀IDR幀編碼后預(yù)算減少。

更新完緩沖區(qū)大小后,就開始評(píng)估出qscale,qscale的評(píng)估是根據(jù)復(fù)雜度(ABR)、crf值來確定的(具體下篇再說)。再由qscale換算出qp值,到這里得到的是一個(gè)未經(jīng)過VBV控制的QP值,因此還需要經(jīng)過VBV進(jìn)行調(diào)整。調(diào)整的過程將分成兩個(gè)過程:幀級(jí)、宏塊級(jí)碼率控制。
3、幀級(jí)碼率控制
上一步得到的qp值還未經(jīng)過vbv的調(diào)整,因此可能存在上溢、或者下溢(部分算法不負(fù)責(zé)下溢),因此還需要通過VBV進(jìn)行幀層級(jí)的調(diào)整,調(diào)整主要在函數(shù)clip_qscale內(nèi)實(shí)現(xiàn)。
幀級(jí)控制在內(nèi)部又分為經(jīng)典算法和lookahead兩種。經(jīng)典較為簡(jiǎn)單,不細(xì)說。lookahead的做法則是通過評(píng)估出該q值下的幀大小,進(jìn)行擴(kuò)大或者縮小qp值。
里面幾個(gè)重要的變量:
buffer_fill_cur只實(shí)現(xiàn)預(yù)算的緩存大小扣去當(dāng)前幀的預(yù)測(cè)大小后,剩下的空間,即剩下的預(yù)算。
target_fill即花掉預(yù)算后,你得剩下多少空間,這個(gè)空間不能太大,也不能太小。
看代碼
for( int j = 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;//上一幀所增加的預(yù)算
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 );//將該幀拿去編碼,所花費(fèi)的空間
buffer_fill_cur -= cur_bits;//扣除花費(fèi),即剩下的預(yù)算
last_duration = h->fenc->f_planned_cpb_duration[j];
}
//目標(biāo)上限,至少要保證buffer50%填滿
//但是如果buffer里可用數(shù)據(jù)量少的話,50%的條件太苛刻
//那么在原來基礎(chǔ)上新增數(shù)據(jù)預(yù)算的50%即可,公式中的兩個(gè)0.5都是經(jīng)驗(yàn)值
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 )
{
//當(dāng)前剩下的預(yù)算小于目標(biāo)可用預(yù)算
//說明用掉太多數(shù)據(jù)了,所以應(yīng)該增大q值
q *= 1.01;
terminate |= 1;
continue;
}
//目標(biāo)下限,不要剩太多的預(yù)算。
//源代碼中是控制在50%到80%的區(qū)間。
target_fill = x264_clip3f( rcc->buffer_fill - total_duration * rcc->vbv_max_rate * 0.5, rcc->buffer_size * 0.8, rcc->buffer_size );
//對(duì)于不要求控制碼率下限的情況下,不需要調(diào)小q值
if( rcc->b_vbv_min_rate&& buffer_fill_cur > target_fill )
{
//大的話說明數(shù)據(jù)太少,因此需要調(diào)小q值
q /= 1.01;
terminate |= 2;
continue;
}
按上個(gè)例子來說,把每幀調(diào)整過程中當(dāng)前剩余預(yù)算的大小打印出來,

根據(jù)設(shè)置,至少要剩8000*0.5=4000以上,同時(shí)最多只能64000到8000中的一個(gè)數(shù)字。
從局部放大圖可以看出,一開始q值較大的時(shí)候,預(yù)算用掉比較少,所以剩的比較多,因此需要調(diào)小q值,經(jīng)過多次微調(diào),要嗎剛好落在8000要嗎,預(yù)算全部用掉且還不夠(這個(gè)時(shí)候需要回調(diào))。

而在最后階段,q值使得剩下的預(yù)算太少了低于4000了,就意味著數(shù)據(jù)量太多,所以需要調(diào)大q值,直至調(diào)整到4000的水平。

調(diào)整結(jié)束后,看一張buffer_fill的預(yù)算圖,可以看出實(shí)現(xiàn)了碼率的控制。預(yù)算80000就將碼率限制在80000左右,圖中第二個(gè)點(diǎn),預(yù)算很少,這是因?yàn)槟且粠瑒偤檬堑谝粠腎DR幀,因此信息量比較多。另外也可以看出總共調(diào)整了140多次,而源文件共有480幀,因此只有I、P幀進(jìn)入到VBV的調(diào)整階段。

4、minGOP vbv 調(diào)整
因?yàn)锽幀的大小是根據(jù)P幀決定的,因此,當(dāng)收到P幀時(shí),還需要將B幀考慮進(jìn)預(yù)算,防止超出預(yù)算。
if( h->sh.i_type == SLICE_TYPE_P && !rcc->single_frame_vbv )
{
int nb = rcc->bframes;
double bits = predict_size( &rcc->pred[h->sh.i_type], q, rcc->last_satd );
double pbbits = bits;
double bbits = predict_size( rcc->pred_b_from_p, q * h->param.rc.f_pb_factor, rcc->last_satd );
double space;
double bframe_cpb_duration = 0;
double minigop_cpb_duration;
for( int i = 0; i < nb; i++ )
{
bframe_cpb_duration += h->fenc->f_planned_cpb_duration[i];
}
if( bbits * nb > bframe_cpb_duration * rcc->vbv_max_rate )
nb = 0;
pbbits += nb * bbits;//這個(gè)minigop消耗的位數(shù)
//b幀和p幀帶來的預(yù)算
minigop_cpb_duration = bframe_cpb_duration + fenc_cpb_duration;
space = rcc->buffer_fill + minigop_cpb_duration*rcc->vbv_max_rate - rcc->buffer_size;
//預(yù)算超出buffersize的部分,仍然不夠存放這個(gè)MiniGop,這時(shí)需要增大預(yù)算,防止上溢
if( pbbits < space )
{//適當(dāng)調(diào)低qscale (調(diào)高碼率預(yù)算), 使得本minGOP過后,緩存區(qū)沒有上溢。
q *= X264_MAX( pbbits / space, bits / (0.5 * rcc->buffer_size) );
}
q = X264_MAX( q0/2, q );
}
到這里結(jié)束后,還有根據(jù)整幀大小,再進(jìn)一步微調(diào)q值,然后根據(jù)最后的q值去算出整幀預(yù)算的大小rcc->frame_size_planned,同時(shí)將這個(gè)q值更新到h->rc->accum_p_qp中。
5、宏塊級(jí)碼率控制
只在VBV模式下才有效,以行為控制單元。控制方式與幀級(jí)控制類似,預(yù)測(cè)大小大了,就增大q值,反之減小。代碼如下
//對(duì)于多線程slice編碼的情況需要將各個(gè)線程的slice加入統(tǒng)計(jì)
//采用多線程幀編碼的情況下不使用
if( h->param.b_sliced_threads )
{
float size_of_other_slices_planned = 0;
for( int i = 0; i < h->param.i_threads; i++ )
if( h != h->thread[i] )
{
size_of_other_slices += h->thread[i]->rc->frame_size_estimated;
size_of_other_slices_planned += h->thread[i]->rc->slice_size_planned;
}
float weight = rc->slice_size_planned / rc->frame_size_planned;
size_of_other_slices = (size_of_other_slices - size_of_other_slices_planned) * weight + size_of_other_slices_planned;
}
//剩下的預(yù)算
float buffer_left_planned = rc->buffer_fill - rc->frame_size_planned;
buffer_left_planned = X264_MAX( buffer_left_planned, 0.f );
//計(jì)算出當(dāng)前slice的大小
float b1 = bits_so_far + predict_row_size_to_end( h, y, rc->qpm ) + size_of_other_slices;
...
//控制上溢
//判斷邏輯如下:
// 1 &&(2||3||4)
//1、處于qp_MAX的低通濾波器下
//2、當(dāng)前row加入后,會(huì)使得大小超出,整幀預(yù)算(包含容忍度)
//3、當(dāng)前row加入后,已經(jīng)超出了預(yù)算,并且量化值小于未經(jīng)過vbv調(diào)整后的量化值
//4、當(dāng)前row加入后,預(yù)算緩沖區(qū)不足(寫成rc->buffer_fill - b1 < buffer_left_planned * 0.5,比較好理解)
while( rc->qpm < qp_max&& ((b1 > rc->frame_size_planned + rc_tol) ||
(b1 > rc->frame_size_planned && rc->qpm < rc->qp_novbv) ||
(b1 > rc->buffer_fill - buffer_left_planned * 0.5f)) )
{
rc->qpm += step_size;//加大q值
b1 = bits_so_far + predict_row_size_to_end( h, y, rc->qpm ) + size_of_other_slices;
}
...
//控制下溢
// 1&& 2 && 3 && 4 && 5
//1、qp_min 的高通濾波器中
//2、比前一行的qp值小
//3、調(diào)整后的qp值大于未調(diào)整的qp值,才有回調(diào)的必要
//4、預(yù)估大小不上溢
//5、預(yù)估大小占用率沒有達(dá)到80%,與上面的0.5對(duì)應(yīng)控制在50%和80%之間,這兩個(gè)取值來源于經(jīng)驗(yàn)值
while( rc->qpm > qp_min && rc->qpm < prev_row_qp
&& (rc->qpm > h->fdec->f_row_qp[0] || rc->single_frame_vbv)
&& (b2 < max_frame_size)
&& ((b2 < rc->frame_size_planned * 0.8f) || (b2 < b_max)) )
{
b1 = b2;
rc->qpm -= step_size;
b2 = bits_so_far + predict_row_size_to_end( h, y, rc->qpm ) + size_of_other_slices;
}
//調(diào)小之后,仍然要防止上溢
while( rc->qpm < qp_absolute_max && (b1 > max_frame_size) )
{
rc->qpm += step_size;
b1 = bits_so_far + predict_row_size_to_end( h, y, rc->qpm ) + size_of_other_slices;
}
通過來來回回的3次調(diào)整,完成宏塊級(jí)碼率的控制。將上面例子qp值的變化過程打印出來。可見qp值處于不斷的完善調(diào)整過程中

6、編碼結(jié)束,更新信息
得到每個(gè)宏塊的qp值,x264就用這個(gè)pq值去量化DCT變化后的數(shù)據(jù)。最后,以上的一些信息都是通過預(yù)估得到的,因此編碼后需要用實(shí)際值去更新。更新的操作在x264_rate control_end函數(shù)中完成,對(duì)于VBV來說,最主要的得到下次控制時(shí)VBV Buffer fill這個(gè)緩存預(yù)算的值,在程序中也就是buffer fill final這個(gè)變量(它只是與buffer fill的量綱不同,實(shí)際是一個(gè)性質(zhì))。
//編碼后的數(shù)據(jù)量乘以量綱
uint64_t buffer_diff = (uint64_t)bits * h->sps->vui.i_time_scale;
rct->buffer_fill_final -= buffer_diff;
rct->buffer_fill_final_min -= buffer_diff;
//溢出的話,即花光了預(yù)算,所以就要初始化預(yù)算為0
if( rct->buffer_fill_final_min < 0 )
{
double underflow = (double)rct->buffer_fill_final_min / h->sps->vui.i_time_scale;
if( rcc->rate_factor_max_increment && rcc->qpm >= rcc->qp_novbv + rcc->rate_factor_max_increment )
x264_log( h, X264_LOG_DEBUG, "VBV underflow due to CRF-max (frame %d, %.0f bits)\n", h->i_frame, underflow );
else
x264_log( h, X264_LOG_WARNING, "VBV underflow (frame %d, %.0f bits)\n", h->i_frame, underflow );
rct->buffer_fill_final =
rct->buffer_fill_final_min = 0;
}
//新增預(yù)算量
if( h->param.i_avcintra_class )
buffer_diff = buffer_size;
else
buffer_diff = (uint64_t)bitrate * h->sps->vui.i_num_units_in_tick * h->fenc->i_cpb_duration;
rct->buffer_fill_final += buffer_diff;
rct->buffer_fill_final_min += buffer_diff;
//更新后的預(yù)算量如果超出邊界,就要進(jìn)行裁剪
if( rct->buffer_fill_final > buffer_size )
{
if( h->param.rc.b_filler )
{
//使用filler選項(xiàng)
//buffer fill final 和buffer size都是以i_time_scale為單位的,同時(shí)換算成bit需要乘以8
int64_t scale = (int64_t)h->sps->vui.i_time_scale * 8;
//要換算成字節(jié)的話,需要再除8,
//就是(buffer_fill_final-buffer_size)/(h->sps->vui.i_time_scale bits*8)
//+scale-1 因?yàn)楹竺嬉詓cale,就是實(shí)現(xiàn)普通的向上取整的方法
filler = (rct->buffer_fill_final - buffer_size + scale - 1) / scale;
bits = h->param.i_avcintra_class ? filler * 8 : X264_MAX( (FILLER_OVERHEAD - h->param.b_annexb), filler ) * 8;
buffer_diff = (uint64_t)bits * h->sps->vui.i_time_scale;
rct->buffer_fill_final -= buffer_diff;
rct->buffer_fill_final_min -= buffer_diff;
}
else
{
//直接取最小
rct->buffer_fill_final = X264_MIN( rct->buffer_fill_final, buffer_size );
rct->buffer_fill_final_min = X264_MIN( rct->buffer_fill_final_min, buffer_size );
}
}
[1]、http://www.pianshen.com/article/4198342118/
[2]、https://onlivetest.wordpress.com/2015/02/20/x264-rate-control-part-one/
[3]、https://blog.csdn.net/nonmarking/article/details/50737198