x264代碼剖析(七):encode()函數之x264_encoder_encode()函數


x264代碼剖析(七):encode()函數之x264_encoder_encode()函數

 

        encode()函數是x264的主干函數,主要包括x264_encoder_open()函數、x264_encoder_headers()函數、x264_encoder_encode()函數與x264_encoder_close()函數四大部分,其中x264_encoder_encode()函數是其核心部分,具體的H.264視頻編碼算法均在此模塊。上兩篇博文主要分析了x264_encoder_open()函數與x264_encoder_headers()函數,本文主要學習x264_encoder_encode()函數。

 

        在《x264代碼剖析(三):主函數main()、解析函數parse()與編碼函數encode()》的介紹中,我們知道x264_encoder_encode()函數被encode_frame()函數調用,而encode_frame()函數被encode()函數調用,encode()函數又被main()函數調用。由於main()函數、encode()函數與encode_frame()函數對應的代碼已經分析完畢,本文主要分析x264_encoder_encode()函數。x264_encoder_encode()函數編碼一幀YUVH.264碼流,對應的函數關系圖如下,主要調用了下面的函數:

        x264_frame_pop_unused():獲取1x264_frame_t類型結構體fenc。如果frames.unused[]隊列不為空,就調用x264_frame_pop()unused[]隊列取1個現成的;否則就調用x264_frame_new()創建一個新的。

        x264_frame_copy_picture():將輸入的圖像數據拷貝至fenc

        x264_lookahead_put_frame():將fenc放入lookahead.next.list[]隊列,等待確定幀類型。

        x264_lookahead_get_frames():通過lookahead分析幀類型。該函數調用了x264_slicetype_decide()x264_slicetype_analyse()x264_slicetype_frame_cost()等函數。經過一些列分析之后,最終確定了幀類型信息,並且將幀放入frames.current[]隊列。

        x264_frame_shift():從frames.current[]隊列取出1幀用於編碼。

        x264_reference_update():更新參考幀隊列。

        x264_reference_reset():如果為IDR幀,調用該函數清空參考幀列表。

        x264_reference_hierarchy_reset():如果是非IDRI幀、P幀、B幀(可做為參考幀),調用該函數。

        x264_reference_build_list():創建參考幀列表list0list1

        x264_ratecontrol_start():開啟碼率控制。

        x264_slice_init():創建 Slice Header

        x264_slices_write():編碼數據(最關鍵的步驟)。其中調用了x264_slice_write()完成了編碼的工作(注意“x264_slices_write()”和“x264_slice_write()”名字差了一個“s”)。

        x264_encoder_frame_end():編碼結束后做一些后續處理,例如記錄一些統計信息。其中調用了x264_encoder_encapsulate_nals()封裝NALU(添加起始碼),調用x264_frame_push_unused()fenc重新放回frames.unused[]隊列,並且調用x264_ratecontrol_end()結束碼率控制。




 

        下面對x264_encoder_encode()函數源碼進行分析,如下:

 

/******************************************************************/
/******************************************************************/
/*
======Analysed by RuiDong Fang
======Csdn Blog:http://blog.csdn.net/frd2009041510
======Date:2016.03.10
*/
/******************************************************************/
/******************************************************************/

/************====== x264_encoder_encode()函數 ======************/
/*
功能:編碼一幀數據
*/
/****************************************************************************
* x264_encoder_encode:
* XXX: i_poc : is the poc of the current given picture
* i_frame : is the number of the frame being coded
* ex: type frame poc
* I 0 2*0
* P 1 2*3
* B 2 2*1
* B 3 2*2
* P 4 2*6
* B 5 2*4
* B 6 2*5
****************************************************************************/
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_t *thread_current, *thread_prev, *thread_oldest;
int i_nal_type, i_nal_ref_idc, i_global_qp;
int overhead = NALU_OVERHEAD;

#if HAVE_OPENCL
if( h->opencl.b_fatal_error )
return -1;
#endif

if( h->i_thread_frames > 1 )
{
thread_prev = h->thread[ h->i_thread_phase ];
h->i_thread_phase = (h->i_thread_phase + 1) % h->i_thread_frames;
thread_current = h->thread[ h->i_thread_phase ];
thread_oldest = h->thread[ (h->i_thread_phase + 1) % h->i_thread_frames ];
x264_thread_sync_context( thread_current, thread_prev );
x264_thread_sync_ratecontrol( thread_current, thread_prev, thread_oldest );
h = thread_current;
}
else
{
thread_current =
thread_oldest = h;
}
h->i_cpb_delay_pir_offset = h->i_cpb_delay_pir_offset_next;

/* no data out */
*pi_nal = 0;
*pp_nal = NULL;

/* ------------------- Setup new frame from picture -------------------- */
if( pic_in != NULL )
{
if( h->lookahead->b_exit_thread )
{
x264_log( h, X264_LOG_ERROR, "lookahead thread is already stopped\n" );
return -1;
}

/* 1: Copy the picture to a frame and move it to a buffer */
x264_frame_t *fenc = x264_frame_pop_unused( h, 0 );//////////////////步驟1:fenc存儲了編碼幀(獲取一幀的空間fenc,用來存放待編碼的幀)
if( !fenc )
return -1;

if( x264_frame_copy_picture( h, fenc, pic_in ) < 0 ));//////////////////外部像素數據傳遞到內部系統,pic_in(外部結構體x264_picture_t)到fenc(內部結構體x264_frame_t)
return -1;

//寬和高都確保是16的整數倍(宏塊寬度的整數倍)
if( h->param.i_width != 16 * h->mb.i_mb_width ||
h->param.i_height != 16 * h->mb.i_mb_height )
x264_frame_expand_border_mod16( h, fenc );//擴展至16整數倍

fenc->i_frame = h->frames.i_input++;

if( fenc->i_frame == 0 )
h->frames.i_first_pts = fenc->i_pts;
if( h->frames.i_bframe_delay && fenc->i_frame == h->frames.i_bframe_delay )
h->frames.i_bframe_delay_time = fenc->i_pts - h->frames.i_first_pts;

if( h->param.b_vfr_input && fenc->i_pts <= h->frames.i_largest_pts )
x264_log( h, X264_LOG_WARNING, "non-strictly-monotonic PTS\n" );

h->frames.i_second_largest_pts = h->frames.i_largest_pts;
h->frames.i_largest_pts = fenc->i_pts;

if( (fenc->i_pic_struct < PIC_STRUCT_AUTO) || (fenc->i_pic_struct > PIC_STRUCT_TRIPLE) )
fenc->i_pic_struct = PIC_STRUCT_AUTO;

if( fenc->i_pic_struct == PIC_STRUCT_AUTO )
{
#if HAVE_INTERLACED
int b_interlaced = fenc->param ? fenc->param->b_interlaced : h->param.b_interlaced;
#else
int b_interlaced = 0;
#endif
if( b_interlaced )
{
int b_tff = fenc->param ? fenc->param->b_tff : h->param.b_tff;
fenc->i_pic_struct = b_tff ? PIC_STRUCT_TOP_BOTTOM : PIC_STRUCT_BOTTOM_TOP;
}
else
fenc->i_pic_struct = PIC_STRUCT_PROGRESSIVE;
}

if( h->param.rc.b_mb_tree && h->param.rc.b_stat_read )
{
if( x264_macroblock_tree_read( h, fenc, pic_in->prop.quant_offsets ) )
return -1;
}
else
x264_stack_align( x264_adaptive_quant_frame, h, fenc, pic_in->prop.quant_offsets );

if( pic_in->prop.quant_offsets_free )
pic_in->prop.quant_offsets_free( pic_in->prop.quant_offsets );

//降低分辨率處理(原來的一半),線性內插
//注意這里並不是6抽頭濾波器的半像素內插
if( h->frames.b_have_lowres )
x264_frame_init_lowres( h, fenc );

/* 2: Place the frame into the queue for its slice type decision */
x264_lookahead_put_frame( h, fenc );));//////////////////步驟2:fenc放入lookahead.next.list[]隊列,等待確定幀類型

if( h->frames.i_input <= h->frames.i_delay + 1 - h->i_thread_frames )
{
/* Nothing yet to encode, waiting for filling of buffers */
pic_out->i_type = X264_TYPE_AUTO;
return 0;
}
}
else
{
/* signal kills for lookahead thread */
x264_pthread_mutex_lock( &h->lookahead->ifbuf.mutex );
h->lookahead->b_exit_thread = 1;
x264_pthread_cond_broadcast( &h->lookahead->ifbuf.cv_fill );
x264_pthread_mutex_unlock( &h->lookahead->ifbuf.mutex );
}

h->i_frame++;
/* 3: The picture is analyzed in the lookahead */
if( !h->frames.current[0] )
x264_lookahead_get_frames( h );//////////////////步驟3:通過lookahead分析幀類型

if( !h->frames.current[0] && x264_lookahead_is_empty( h ) )
return x264_encoder_frame_end( thread_oldest, thread_current, pp_nal, pi_nal, pic_out );

/* ------------------- Get frame to be encoded ------------------------- */
/* 4: get picture to encode */
h->fenc = x264_frame_shift( h->frames.current );//////////////////步驟4:從frames.current[]隊列取出1幀[0]用於編碼

/* If applicable, wait for previous frame reconstruction to finish */
if( h->param.b_sliced_threads )
if( x264_threadpool_wait_all( h ) < 0 )
return -1;

if( h->i_frame == 0 )
h->i_reordered_pts_delay = h->fenc->i_reordered_pts;
if( h->reconfig )
{
x264_encoder_reconfig_apply( h, &h->reconfig_h->param );
h->reconfig = 0;
}
if( h->fenc->param )
{
x264_encoder_reconfig_apply( h, h->fenc->param );
if( h->fenc->param->param_free )
{
h->fenc->param->param_free( h->fenc->param );
h->fenc->param = NULL;
}
}

// ok to call this before encoding any frames, since the initial values of fdec have b_kept_as_ref=0
//更新參考幀隊列frames.reference[].若為B幀則不更新
//重建幀fdec移植參考幀列表,新建一個fdec
if( x264_reference_update( h ) ));//////////////////更新參考幀隊列
return -1;
h->fdec->i_lines_completed = -1;

if( !IS_X264_TYPE_I( h->fenc->i_type ) )
{
int valid_refs_left = 0;
for( int i = 0; h->frames.reference[i]; i++ )
if( !h->frames.reference[i]->b_corrupt )
valid_refs_left++;
/* No valid reference frames left: force an IDR. */
if( !valid_refs_left )
{
h->fenc->b_keyframe = 1;
h->fenc->i_type = X264_TYPE_IDR;
}
}

if( h->fenc->b_keyframe )
{
h->frames.i_last_keyframe = h->fenc->i_frame;
if( h->fenc->i_type == X264_TYPE_IDR )
{
h->i_frame_num = 0;
h->frames.i_last_idr = h->fenc->i_frame;
}
}
h->sh.i_mmco_command_count =
h->sh.i_mmco_remove_from_end = 0;
h->b_ref_reorder[0] =
h->b_ref_reorder[1] = 0;
h->fdec->i_poc =
h->fenc->i_poc = 2 * ( h->fenc->i_frame - X264_MAX( h->frames.i_last_idr, 0 ) );

/* ------------------- Setup frame context ----------------------------- */
/* 5: Init data dependent of frame type */
//步驟5:確定幀類型
if( h->fenc->i_type == X264_TYPE_IDR )
{
//I與IDR區別
//注意IDR會導致參考幀列清空,而I不會
//I圖像之后的圖像可以引用I圖像之間的圖像做運動參考
/* reset ref pictures */
i_nal_type = NAL_SLICE_IDR;
i_nal_ref_idc = NAL_PRIORITY_HIGHEST;
h->sh.i_type = SLICE_TYPE_I;
x264_reference_reset( h );//////////////////若是IDR幀,則清空所有參考幀
h->frames.i_poc_last_open_gop = -1;
}
else if( h->fenc->i_type == X264_TYPE_I )
{
//I與IDR區別
//注意IDR會導致參考幀列清空,而I不會
//I圖像之后的圖像可以引用I圖像之間的圖像做運動參考
i_nal_type = NAL_SLICE;
i_nal_ref_idc = NAL_PRIORITY_HIGH; /* Not completely true but for now it is (as all I/P are kept as ref)*/
h->sh.i_type = SLICE_TYPE_I;
x264_reference_hierarchy_reset( h );//////////////////如果是非IDR的I幀,調用該函數
if( h->param.b_open_gop )
h->frames.i_poc_last_open_gop = h->fenc->b_keyframe ? h->fenc->i_poc : -1;
}
else if( h->fenc->i_type == X264_TYPE_P )
{
i_nal_type = NAL_SLICE;
i_nal_ref_idc = NAL_PRIORITY_HIGH; /* Not completely true but for now it is (as all I/P are kept as ref)*/
h->sh.i_type = SLICE_TYPE_P;
x264_reference_hierarchy_reset( h ););//////////////////如果是非IDR的P幀,調用該函數
h->frames.i_poc_last_open_gop = -1;
}
else if( h->fenc->i_type == X264_TYPE_BREF )
{
//可以作為參考幀的B幀,這是個特色
i_nal_type = NAL_SLICE;
i_nal_ref_idc = h->param.i_bframe_pyramid == X264_B_PYRAMID_STRICT ? NAL_PRIORITY_LOW : NAL_PRIORITY_HIGH;
h->sh.i_type = SLICE_TYPE_B;
x264_reference_hierarchy_reset( h ););//////////////////如果是非IDR的B幀(可做為參考幀),調用該函數
}
else /* B frame */
{
//最普通
i_nal_type = NAL_SLICE;
i_nal_ref_idc = NAL_PRIORITY_DISPOSABLE;
h->sh.i_type = SLICE_TYPE_B;
}

//重建幀與編碼幀的賦值
h->fdec->i_type = h->fenc->i_type;
h->fdec->i_frame = h->fenc->i_frame;
h->fenc->b_kept_as_ref =
h->fdec->b_kept_as_ref = i_nal_ref_idc != NAL_PRIORITY_DISPOSABLE && h->param.i_keyint_max > 1;

h->fdec->mb_info = h->fenc->mb_info;
h->fdec->mb_info_free = h->fenc->mb_info_free;
h->fenc->mb_info = NULL;
h->fenc->mb_info_free = NULL;

h->fdec->i_pts = h->fenc->i_pts;
if( h->frames.i_bframe_delay )
{
int64_t *prev_reordered_pts = thread_current->frames.i_prev_reordered_pts;
h->fdec->i_dts = h->i_frame > h->frames.i_bframe_delay
? prev_reordered_pts[ (h->i_frame - h->frames.i_bframe_delay) % h->frames.i_bframe_delay ]
: h->fenc->i_reordered_pts - h->frames.i_bframe_delay_time;
prev_reordered_pts[ h->i_frame % h->frames.i_bframe_delay ] = h->fenc->i_reordered_pts;
}
else
h->fdec->i_dts = h->fenc->i_reordered_pts;
if( h->fenc->i_type == X264_TYPE_IDR )
h->i_last_idr_pts = h->fdec->i_pts;

/* ------------------- Init ----------------------------- */
/* build ref list 0/1 */
x264_reference_build_list( h, h->fdec->i_poc );//////////////////創建參考幀列表list0和list1

/* ---------------------- Write the bitstream -------------------------- */
/* Init bitstream context */
//用於輸出
if( h->param.b_sliced_threads )
{
for( int i = 0; i < h->param.i_threads; i++ )
{
bs_init( &h->thread[i]->out.bs, h->thread[i]->out.p_bitstream, h->thread[i]->out.i_bitstream );
h->thread[i]->out.i_nal = 0;
}
}
else
{
bs_init( &h->out.bs, h->out.p_bitstream, h->out.i_bitstream );
h->out.i_nal = 0;
}

if( h->param.b_aud )
{
int pic_type;

if( h->sh.i_type == SLICE_TYPE_I )
pic_type = 0;
else if( h->sh.i_type == SLICE_TYPE_P )
pic_type = 1;
else if( h->sh.i_type == SLICE_TYPE_B )
pic_type = 2;
else
pic_type = 7;

x264_nal_start( h, NAL_AUD, NAL_PRIORITY_DISPOSABLE );
bs_write( &h->out.bs, 3, pic_type );
bs_rbsp_trailing( &h->out.bs );
if( x264_nal_end( h ) )
return -1;
overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;
}

h->i_nal_type = i_nal_type;
h->i_nal_ref_idc = i_nal_ref_idc;

if( h->param.b_intra_refresh )
{
if( IS_X264_TYPE_I( h->fenc->i_type ) )
{
h->fdec->i_frames_since_pir = 0;
h->b_queued_intra_refresh = 0;
/* PIR is currently only supported with ref == 1, so any intra frame effectively refreshes
* the whole frame and counts as an intra refresh. */
h->fdec->f_pir_position = h->mb.i_mb_width;
}
else if( h->fenc->i_type == X264_TYPE_P )
{
int pocdiff = (h->fdec->i_poc - h->fref[0][0]->i_poc)/2;
float increment = X264_MAX( ((float)h->mb.i_mb_width-1) / h->param.i_keyint_max, 1 );
h->fdec->f_pir_position = h->fref[0][0]->f_pir_position;
h->fdec->i_frames_since_pir = h->fref[0][0]->i_frames_since_pir + pocdiff;
if( h->fdec->i_frames_since_pir >= h->param.i_keyint_max ||
(h->b_queued_intra_refresh && h->fdec->f_pir_position + 0.5 >= h->mb.i_mb_width) )
{
h->fdec->f_pir_position = 0;
h->fdec->i_frames_since_pir = 0;
h->b_queued_intra_refresh = 0;
h->fenc->b_keyframe = 1;
}
h->fdec->i_pir_start_col = h->fdec->f_pir_position+0.5;
h->fdec->f_pir_position += increment * pocdiff;
h->fdec->i_pir_end_col = h->fdec->f_pir_position+0.5;
/* If our intra refresh has reached the right side of the frame, we're done. */
if( h->fdec->i_pir_end_col >= h->mb.i_mb_width - 1 )
{
h->fdec->f_pir_position = h->mb.i_mb_width;
h->fdec->i_pir_end_col = h->mb.i_mb_width - 1;
}
}
}

if( h->fenc->b_keyframe )
{
//每個關鍵幀前面重復加上SPS和PPS
/* Write SPS and PPS */
if( h->param.b_repeat_headers )
{
/* generate sequence parameters */
x264_nal_start( h, NAL_SPS, NAL_PRIORITY_HIGHEST );
x264_sps_write( &h->out.bs, h->sps );
if( x264_nal_end( h ) )
return -1;
/* Pad AUD/SPS to 256 bytes like Panasonic */
if( h->param.i_avcintra_class )
h->out.nal[h->out.i_nal-1].i_padding = 256 - bs_pos( &h->out.bs ) / 8 - 2*NALU_OVERHEAD;
overhead += h->out.nal[h->out.i_nal-1].i_payload + h->out.nal[h->out.i_nal-1].i_padding + NALU_OVERHEAD;

/* generate picture parameters */
x264_nal_start( h, NAL_PPS, NAL_PRIORITY_HIGHEST );
x264_pps_write( &h->out.bs, h->sps, h->pps );
if( x264_nal_end( h ) )
return -1;
if( h->param.i_avcintra_class )
h->out.nal[h->out.i_nal-1].i_padding = 256 - h->out.nal[h->out.i_nal-1].i_payload - NALU_OVERHEAD;
overhead += h->out.nal[h->out.i_nal-1].i_payload + h->out.nal[h->out.i_nal-1].i_padding + NALU_OVERHEAD;
}

/* when frame threading is used, buffering period sei is written in x264_encoder_frame_end */
if( h->i_thread_frames == 1 && h->sps->vui.b_nal_hrd_parameters_present )
{
x264_hrd_fullness( h );
x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
x264_sei_buffering_period_write( h, &h->out.bs );
if( x264_nal_end( h ) )
return -1;
overhead += h->out.nal[h->out.i_nal-1].i_payload + SEI_OVERHEAD;
}
}

/* write extra sei */
//下面很大一段代碼用於寫入SEI(一部分是為了適配其他的解碼器)
for( int i = 0; i < h->fenc->extra_sei.num_payloads; i++ )
{
x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
x264_sei_write( &h->out.bs, h->fenc->extra_sei.payloads[i].payload, h->fenc->extra_sei.payloads[i].payload_size,
h->fenc->extra_sei.payloads[i].payload_type );
if( x264_nal_end( h ) )
return -1;
overhead += h->out.nal[h->out.i_nal-1].i_payload + SEI_OVERHEAD;
if( h->fenc->extra_sei.sei_free )
{
h->fenc->extra_sei.sei_free( h->fenc->extra_sei.payloads[i].payload );
h->fenc->extra_sei.payloads[i].payload = NULL;
}
}

if( h->fenc->extra_sei.sei_free )
{
h->fenc->extra_sei.sei_free( h->fenc->extra_sei.payloads );
h->fenc->extra_sei.payloads = NULL;
h->fenc->extra_sei.sei_free = NULL;
}

//特殊的SEI信息(Avid等解碼器需要)
if( h->fenc->b_keyframe )
{
/* Avid's decoder strictly wants two SEIs for AVC-Intra so we can't insert the x264 SEI */
if( h->param.b_repeat_headers && h->fenc->i_frame == 0 && !h->param.i_avcintra_class )
{
/* identify ourself */
x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
if( x264_sei_version_write( h, &h->out.bs ) )
return -1;
if( x264_nal_end( h ) )
return -1;
overhead += h->out.nal[h->out.i_nal-1].i_payload + SEI_OVERHEAD;
}

if( h->fenc->i_type != X264_TYPE_IDR )
{
int time_to_recovery = h->param.b_open_gop ? 0 : X264_MIN( h->mb.i_mb_width - 1, h->param.i_keyint_max ) + h->param.i_bframe - 1;
x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
x264_sei_recovery_point_write( h, &h->out.bs, time_to_recovery );
if( x264_nal_end( h ) )
return -1;
overhead += h->out.nal[h->out.i_nal-1].i_payload + SEI_OVERHEAD;
}
}

if( h->param.i_frame_packing >= 0 && (h->fenc->b_keyframe || h->param.i_frame_packing == 5) )
{
x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
x264_sei_frame_packing_write( h, &h->out.bs );
if( x264_nal_end( h ) )
return -1;
overhead += h->out.nal[h->out.i_nal-1].i_payload + SEI_OVERHEAD;
}

/* generate sei pic timing */
if( h->sps->vui.b_pic_struct_present || h->sps->vui.b_nal_hrd_parameters_present )
{
x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
x264_sei_pic_timing_write( h, &h->out.bs );
if( x264_nal_end( h ) )
return -1;
overhead += h->out.nal[h->out.i_nal-1].i_payload + SEI_OVERHEAD;
}

/* As required by Blu-ray. */
if( !IS_X264_TYPE_B( h->fenc->i_type ) && h->b_sh_backup )
{
h->b_sh_backup = 0;
x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
x264_sei_dec_ref_pic_marking_write( h, &h->out.bs );
if( x264_nal_end( h ) )
return -1;
overhead += h->out.nal[h->out.i_nal-1].i_payload + SEI_OVERHEAD;
}

if( h->fenc->b_keyframe && h->param.b_intra_refresh )
h->i_cpb_delay_pir_offset_next = h->fenc->i_cpb_delay;

/* Filler space: 10 or 18 SEIs' worth of space, depending on resolution */
if( h->param.i_avcintra_class )
{
/* Write an empty filler NAL to mimic the AUD in the P2 format*/
x264_nal_start( h, NAL_FILLER, NAL_PRIORITY_DISPOSABLE );
x264_filler_write( h, &h->out.bs, 0 );
if( x264_nal_end( h ) )
return -1;
overhead += h->out.nal[h->out.i_nal-1].i_payload + NALU_OVERHEAD;

/* All lengths are magic lengths that decoders expect to see */
/* "UMID" SEI */
x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
if( x264_sei_avcintra_umid_write( h, &h->out.bs ) < 0 )
return -1;
if( x264_nal_end( h ) )
return -1;
overhead += h->out.nal[h->out.i_nal-1].i_payload + SEI_OVERHEAD;

int unpadded_len;
int total_len;
if( h->param.i_height == 1080 )
{
unpadded_len = 5780;
total_len = 17*512;
}
else
{
unpadded_len = 2900;
total_len = 9*512;
}
/* "VANC" SEI */
x264_nal_start( h, NAL_SEI, NAL_PRIORITY_DISPOSABLE );
if( x264_sei_avcintra_vanc_write( h, &h->out.bs, unpadded_len ) < 0 )
return -1;
if( x264_nal_end( h ) )
return -1;

h->out.nal[h->out.i_nal-1].i_padding = total_len - h->out.nal[h->out.i_nal-1].i_payload - SEI_OVERHEAD;
overhead += h->out.nal[h->out.i_nal-1].i_payload + h->out.nal[h->out.i_nal-1].i_padding + SEI_OVERHEAD;
}//寫入SEI代碼結束

/* Init the rate control */
/* FIXME: Include slice header bit cost. */
x264_ratecontrol_start( h, h->fenc->i_qpplus1, overhead*8 );////////////////////////碼率控制單元開始
i_global_qp = x264_ratecontrol_qp( h );

pic_out->i_qpplus1 =
h->fdec->i_qpplus1 = i_global_qp + 1;

if( h->param.rc.b_stat_read && h->sh.i_type != SLICE_TYPE_I )
{
x264_reference_build_list_optimal( h );
x264_reference_check_reorder( h );
}

if( h->i_ref[0] )
h->fdec->i_poc_l0ref0 = h->fref[0][0]->i_poc;

/* ------------------------ Create slice header ----------------------- */
x264_slice_init( h, i_nal_type, i_global_qp );//////////////////////////創建Slice Header

/*------------------------- Weights -------------------------------------*/
//加權預測
if( h->sh.i_type == SLICE_TYPE_B )
x264_macroblock_bipred_init( h );

x264_weighted_pred_init( h );

if( i_nal_ref_idc != NAL_PRIORITY_DISPOSABLE )
h->i_frame_num++;

/* Write frame */
h->i_threadslice_start = 0;
h->i_threadslice_end = h->mb.i_mb_height;
if( h->i_thread_frames > 1 )
{
x264_threadpool_run( h->threadpool, (void*)x264_slices_write, h );
h->b_thread_active = 1;
}
else if( h->param.b_sliced_threads )
{
if( x264_threaded_slices_write( h ) )
return -1;
}
else
if( (intptr_t)x264_slices_write( h ) )////////////////////////真正的編碼——編碼1個圖像幀(注意這里“slices”后面有“s”)
return -1;

return x264_encoder_frame_end( thread_oldest, thread_current, pp_nal, pi_nal, pic_out );//////////////////結束的時候做一些處理,記錄一些統計信息:輸出NALU、輸出重建幀
}


        從源代碼可以看出,x264_encoder_encode()的流程大致如下:

1)調用x264_frame_pop_unused獲取一個空的fencx264_frame_t類型)用於存儲一幀編碼像素數據。

2)調用x264_frame_copy_picture()將外部結構體的pic_inx264_picture_t類型)的數據拷貝給內部結構體的fencx264_frame_t類型)。

3)調用x264_lookahead_put_frame()fenc放入Lookahead模塊的隊列中,等待確定幀類型。

4)調用x264_lookahead_get_frames()分析Lookahead模塊中一個幀的幀類型。分析后的幀保存在frames.current[]中。

5)調用x264_frame_shift()frames.current[]中取出分析幀類型之后的fenc

6)調用x264_reference_update()更新參考幀隊列frames.reference[]

7)如果編碼幀fencIDR幀,調用x264_reference_reset()清空參考幀隊列frames.reference[]

8)調用x264_reference_build_list()創建參考幀列表List0List1

9)根據選項做一些配置:

          a)、如果b_aud不為0,輸出AUD類型NALU

          b)、在當前幀是關鍵幀的情況下,如果b_repeat_headers不為0,調用x264_sps_write()x264_pps_write()輸出SPSPPS

          c)、輸出一些特殊的SEI信息,用於適配各種解碼器。

10)調用x264_slice_init()初始化Slice Header信息。

11)調用x264_slices_write()進行編碼。該部分是libx264的核心,在后續文章中會詳細分析。

12)調用x264_encoder_frame_end()做一些編碼后的后續處理。



注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com