1. 程式人生 > 其它 >x264中關於編碼幀存取lookahead的操作

x264中關於編碼幀存取lookahead的操作

技術標籤:x264

編碼幀的輸入順序是I B B B B P,設定4個B幀。
這裡假設每次最多能快取5幀影象,即lookahead的列表中能存5幀影象。編碼的第一幀影象是I幀或者IDR幀,首先將第一幀影象存入h->lookahead->next->list中,它的size加1,接著依次存入四幀影象,它的size為5,此時h->lookahead->next->list中的影象是:

在這裡插入圖片描述
接著對列表中的影象進行依次編碼,首先取索引為0的幀,確定幀型別為IDR幀,然後將此幀賦值給h->lookahead->last_nonb,具體操作如下:

h->
lookahead->last_nonb=h->lookahead->next->list[0];

然後確定要移動的幀數shift_frames=1;
然後將這一幀移到另一個列表中,具體操作如下:

h->lookahead->ofbuf->list[0]=h->lookahead->next->list[0];

h->lookahead->ofbuf.size加1,h->lookahead->next.size減1,h->lookahead->next->list中後面的幾幀依次向前移,
h->lookahead->next->list[i] = h->lookahead->next->list[i+1]

在這裡插入圖片描述
h->lookahead->next->list移動之後的情況

在這裡插入圖片描述
h->lookahead->ofbuf->list新增一幀之後的情況
然後又從h->lookahead->ofbuf->list中將這一幀取出存入到h->frames.current中
在這裡插入圖片描述
此時,h->frames.current中存入一幀,h->lookahead->ofbuf->list中為空。然後編碼的時候從h->frames.current中取索引為0的幀作為當前編碼幀。此時只有一幀,,不需要移動其他幀。
這一個IDR幀編碼完後,又向h->lookahead->next->list中加入新的一幀,加到列表的最後,此時還是存5幀。

接著編碼第二幀,第二幀一般是P幀。取第二幀之前要先確定這些幀的幀型別。
這些幀的型別是:
在這裡插入圖片描述
然後調整P幀B幀順序,調整前:
在這裡插入圖片描述
調整後:
在這裡插入圖片描述
對其進行重新排序:
在這裡插入圖片描述
此時h->lookahead->next->list中的順序就是重新排序後的順序。
確定要移動的幀數shift_frames=5;
將h->lookahead->next->list中的5幀依次移到h->lookahead->ofbuf->list中,順序保持不變,移動後h->lookahead->next的size為0,h->lookahead->ofbuf的size為5.
然後再將h->lookahead->ofbuf->list中的5幀依次移到h->frames.current中,順序保持不變,移動後h->lookahead->ofbuf的size為0.
編碼的時候是從h->frames.current中依次取,編完一幀,就將這幀從h->frames.current中移除,根據先入先出原則,所以總是取索引為0的幀。
當這些幀編碼完後,又會重新存入5幀到h->lookahead->next->list中。重新執行這些操作。

程式碼如下:

int     x264_encoder_encode( x264_t *h,
                             x264_nal_t **pp_nal, int *pi_nal,
                             x264_picture_t *pic_in,
                             x264_picture_t *pic_out )
{

	.....
	x264_lookahead_put_frame( h, fenc );//將當前幀加入到lookahead->next->list中
	......
	if( !h->frames.current[0] )
        x264_lookahead_get_frames( h );//實現存入h->frames.current中
    h->fenc = x264_frame_shift( h->frames.current );//實現從h->frames.current中獲取要編碼的幀
    .....
}
void x264_lookahead_get_frames( x264_t *h )
{
    if( h->param.i_sync_lookahead )
    {   /* We have a lookahead thread, so get frames from there */
        x264_pthread_mutex_lock( &h->lookahead->ofbuf.mutex );
        while( !h->lookahead->ofbuf.i_size && h->lookahead->b_thread_active )
            x264_pthread_cond_wait( &h->lookahead->ofbuf.cv_fill, &h->lookahead->ofbuf.mutex );
        lookahead_encoder_shift( h );
        x264_pthread_mutex_unlock( &h->lookahead->ofbuf.mutex );
    }
    else
    {   /* We are not running a lookahead thread, so perform all the slicetype decide on the fly */

        if( h->frames.current[0] || !h->lookahead->next.i_size )
            return;

        x264_slicetype_decide( h );//確定slice型別
    
        lookahead_update_last_nonb( h, h->lookahead->next.list[0] );
        int shift_frames = h->lookahead->next.list[0]->i_bframes + 1;
        lookahead_shift( &h->lookahead->ofbuf, &h->lookahead->next, shift_frames );//將h->lookahead->next->list中的frame賦值給h->lookahead->ofbuf->list
       
        /* For MB-tree and VBV lookahead, we have to perform propagation analysis on I-frames too. */
        if( h->lookahead->b_analyse_keyframe && IS_X264_TYPE_I( h->lookahead->last_nonb->i_type ) )
            x264_slicetype_analyse( h, shift_frames );

        lookahead_encoder_shift( h );//將h->lookahead->ofbuf->list中的幀移動到h->frames.current中
    }
}

void x264_slicetype_decide( x264_t *h )
{
	......
	if( h->param.rc.b_stat_read )
    {
        /* Use the frame types from the first pass 二路編碼之間使用第一次編碼的幀型別*/
        for( int i = 0; i < h->lookahead->next.i_size; i++ )
            h->lookahead->next.list[i]->i_type =
                x264_ratecontrol_slice_type( h, h->lookahead->next.list[i]->i_frame );
    }
    else if( (h->param.i_bframe && h->param.i_bframe_adaptive)
             || h->param.i_scenecut_threshold
             || h->param.rc.b_mb_tree
             || (h->param.rc.i_vbv_buffer_size && h->param.rc.i_lookahead) )
        x264_slicetype_analyse( h, 0 );//幀型別分析
    ......
	/* insert a bref into the sequence */
    if( h->param.i_bframe_pyramid && bframes > 1 && !brefs )
    {
        h->lookahead->next.list[(bframes-1)/2]->i_type = X264_TYPE_BREF;//設定一個BREF幀
        brefs++;
    }
    .....
    if( bframes )
    {
        int idx_list[] = { brefs+1, 1 };
        for( int i = 0; i < bframes; i++ )//調整P幀B幀順序
        {
            int idx = idx_list[h->lookahead->next.list[i]->i_type == X264_TYPE_BREF]++;
            frames[idx] = h->lookahead->next.list[i];
            frames[idx]->i_reordered_pts = h->lookahead->next.list[idx]->i_pts;
        }
        frames[0] = h->lookahead->next.list[bframes];
        frames[0]->i_reordered_pts = h->lookahead->next.list[0]->i_pts;
        memcpy( h->lookahead->next.list, frames, (bframes+1) * sizeof(x264_frame_t*) );//重排序
    }
    ......
}

PS:暫時理解這麼多。