x264程式碼閱讀筆記——非常詳細
(一)common/set.h的註釋
#ifndef _SET_H
#define _SET_H 1
enum profile_e
{
PROFILE_BASELINE = 66,
PROFILE_MAIN = 77,
PROFILE_EXTENTED = 88,
PROFILE_HIGH = 100,
PROFILE_HIGH10 = 110,
PROFILE_HIGH422 = 122,
PROFILE_HIGH444 = 144
};
enum cqm4_e
{
CQM_4IY = 0,
CQM_4PY = 1,
CQM_4IC = 2,
CQM_4PC = 3
};
enum cqm8_e
{
CQM_8IY = 0,
CQM_8PY = 1
};
typedef struct//profile:外形,評測,概況,剖面
{
int i_id; //?????
int i_profile_idc; //指明所用profile
int i_level_idc; //指明所用level
int b_constraint_set0;//[畢厚傑書 160 頁,constraint_set0_flag],等於1時,表時必須遵從A.2.1 ...所指明的所有制約條件,等於0時表示不必遵從所有條件
int b_constraint_set1;//[畢厚傑書 160 頁,constraint_set0_flag],等於1時,表時必須遵從A.2.2 ...所指明的所有制約條件,等於0時表示不必遵從所有條件
int b_constraint_set2;//[畢厚傑書 160 頁,constraint_set0_flag],等於1時,表時必須遵從A.2.3 ...所指明的所有制約條件,等於0時表示不必遵從所有條件//constraint:約束;限制;強迫;強制
//注意:當constraint_set0_flag、中有兩個以上等於1時,A.2中的所有制約條件都要被遵從
int i_log2_max_frame_num;//表示影象解碼順序的最大取值
//[畢 厚傑書 160 頁,log2_max_frame_num_minus4],這個句法元素主要是為讀取另一個句法元素frame_num服務的,frame_num是最 重要的句法元素之一,它標識所屬影象的解碼順序。可以在句法表中看到,frame_num的解碼函式是ue(v),函式中的v在這裡指 定,v=log2_max_frame_num_minus + 4
int i_poc_type; //[畢厚傑書 160 頁,pic_order_cnt_type],指明瞭POC(Picture Order Count)的編碼方法、POC標識影象的播放順序。由於H.264使用了B幀預測,使得影象的解碼順序不一定等於播放順序,但它們之間存在一定的對映關 系。POC可以由frame_num通過對映關係計算得來,也可以索性由編碼器顯式地傳送。H.264一共定義了3種POC的編碼方法,這個句法元素就是 用來通知解碼器該用哪種方法來計算POC。pic_order_cnt_tye的取值範圍是[0,2]...
int i_log2_max_poc_lsb; //[畢厚傑書 160 頁,log2_max_pic_order_cnt_lsb_minus4],指明瞭變數MaxPicOrderCntLsb的值。
int b_delta_pic_order_always_zero;//[畢厚傑書 161 頁,delta_pic_order_always_zero_flag],其值等於1時句法元素delta_pic_order_cnt[0]和 delta_pic_order_cnt[1]不再片頭出現,且他們的預設值都為0。為0時上述則出現。
int i_offset_for_non_ref_pic;//[畢厚傑書 161 頁,offset_for_non_ref_pic],用來計算非參考幀或場的picture order count ,其值應在[-2e31,2e31-1]
int i_offset_for_top_to_bottom_field;//[畢厚傑書 161 頁,offset_for_top_to_bottom_field],用來計算幀的底場的picture order count 其值應在[-2e31,2e31-1]
int i_num_ref_frames_in_poc_cycle;//[畢厚傑書 161 頁,num_ref_frames_in_pic_order_cnt_cycle],用來解碼picture order count 取值應在[0,255]之間
int i_offset_for_ref_frame[256];//[畢厚傑書 161 頁,offset_for_ref_frame[i]],當picture order count type=1時用來解碼poc,這句語法對迴圈num_ref_frames_in_poc_cycle中的每一個元素指定了一個偏移
int i_num_ref_frames;//[畢厚傑書 161 頁,num_ref_frames],指定參考幀佇列的最大長度,h264規定最多可為16個參考幀,本句法元素的值最大為16。值得注意的是這個長度以 幀為單位,如果在場模式下,應該相應地擴充套件一倍。
int b_gaps_in_frame_num_value_allowed;//[畢厚傑書 161 頁,gaps_in_frame_num_value_allowed_flag],為1時表示允許句法frame_num可以不連續。當傳輸通道堵塞嚴 重時,編碼器來不及將編碼後的影象全部發出,這時允許丟棄若干幀影象。在正常情況下每一幀影象都有依次連續的frame_num值,解碼器檢查到如果 frame_num不連續,便能確定有影象被編碼器丟棄。這時,解碼器必須啟動錯誤掩藏機制來近似地恢復這些影象,因為這些影象有可能被後續影象用作參考
幀。
//當這個句法元素=0時,表示不允許frame_num不連續,即編碼器在任何情況下都不能丟棄影象。這時,H.264 允許解碼器可以不去檢查frame_num的連續性以減少計算量。這種情況下如果依然發生frame_num不連續,表示在傳輸中發生丟包,解碼器會通過 其他機制檢測到丟包的發生,然後啟動錯誤掩藏的恢復影象。
int i_mb_width;//[畢厚傑書 161 頁,pic_width_in_mbs_minus1],本句法元素加1後指明影象寬度,以巨集塊為單位。
int i_mb_height;//[畢厚傑書 161 頁,pic_height_in_map_units_minus1],本句法元素加1後指明影象的高度。
int b_frame_mbs_only;//[畢厚傑書 161 頁,frame_mbs_only_flag],本句法元素=1時,表示本序列中所有影象的編碼模式都是幀,沒有其他編碼模式存在;本句法元素=0時,表 示本序列中影象的編碼模式可能是幀,也可能是場或幀場自適應,某個影象具體是哪一種要由其他句法元素決定。
int b_mb_adaptive_frame_field;//[畢厚傑書 161 頁,mb_adaptive_frame_field_flag],指明本序列是否屬於幀場自適應模式。=1時,表明在本序列
int b_direct8x8_inference;//指明b片的直接和skip模式下運動向量的預測方法
int b_crop;//crop:剪裁 [畢厚傑書 162 頁,frame_cropping_flag],用於指明解碼器是否要將影象裁剪後輸出,如果是的話,後面緊跟著的四個句法元素分別指出左、右、上、下裁剪的寬度。
struct
{
int i_left; //[畢厚傑書 162 頁,frame_crop_left_offset],左
int i_right; //[畢厚傑書 162 頁,frame_crop_left_offset],右
int i_top; //[畢厚傑書 162 頁,frame_crop_left_offset],上
int i_bottom; //[畢厚傑書 162 頁,frame_crop_left_offset],下
} crop;//影象剪裁後輸出的引數;crop:剪裁,剪下,剪輯
int b_vui; ////[畢厚傑書 162 頁,vui_parameters_present_flag],指明vui子結構是否出現在碼流中,vui的碼流結構在...附錄指明,用以表徵視訊格式等額外資訊
struct
{
int b_aspect_ratio_info_present;
int i_sar_width;
int i_sar_height;
int b_overscan_info_present;
int b_overscan_info;
int b_signal_type_present;
int i_vidformat;
int b_fullrange;
int b_color_description_present;
int i_colorprim;
int i_transfer;
int i_colmatrix;
int b_chroma_loc_info_present;
int i_chroma_loc_top;
int i_chroma_loc_bottom;
int b_timing_info_present;
int i_num_units_in_tick;
int i_time_scale;
int b_fixed_frame_rate;
int b_bitstream_restriction;
int b_motion_vectors_over_pic_boundaries;
int i_max_bytes_per_pic_denom;
int i_max_bits_per_mb_denom;
int i_log2_max_mv_length_horizontal;
int i_log2_max_mv_length_vertical;
int i_num_reorder_frames;
int i_max_dec_frame_buffering;
} vui;
int b_qpprime_y_zero_transform_bypass;
} x264_sps_t;//x264_sps_t定義序列參考佇列的引數以及初始化
typedef struct
{
int i_id;//[畢厚傑:P146 pic_parameter_set_id] 本引數集的序號,在各片的片頭被引用
int i_sps_id;//[畢厚傑:P146 seq_parameter_set_id] 指明本影象引數集所引用的序列引數集的序號
int b_cabac;//[畢厚傑:P146 entropy_coding_mode_flag] 指明熵編碼的選擇:0時使用cavlc,1時使用cabac
int b_pic_order;//[畢厚傑:P147 pic_order_present_flag] poc的三種計算方法在片層還各需要用一些句法元素作為引數;當等於時,表示在片頭會有句句法元素指明這些引數;當不為時,表示片頭不會給出這些引數
int i_num_slice_groups;//[畢厚傑:P147 num_slice_groups_minus1] 加一表示影象中片組的個數
int i_num_ref_idx_l0_active;//[畢厚傑:P147 num_ref_idx_10_active_minus1] 指明目前參考幀佇列的長度,即有多少個參考幀(短期和長期),用於list0
int i_num_ref_idx_l1_active;//[畢厚傑:P147 num_ref_idx_11_active_minus1] 指明目前參考幀佇列的長度,即有多少個參考幀(短期和長期),用於list1
int b_weighted_pred;//[畢厚傑:P147 weighted_pred_flag] 指明是否允許p和sp片的加權預測,如果允許,在片頭會出現用以計算加權預測的句法元素
int b_weighted_bipred;//[畢厚傑:P147 weighted_bipred_idc] 指明是否允許b片的加權預測
int i_pic_init_qp;//[畢厚傑:P147 pic_init_qp_minus26] 亮度分量的量化引數的初始值
int i_pic_init_qs;//[畢厚傑:P147 pic_init_qs_minus26] 亮度分量的量化引數的初始值,用於SP和SI
int i_chroma_qp_index_offset;//[畢厚傑:P147 chroma_qp_index_offset] 色度分量的量化引數是根據亮度分量的量化引數計算出來的,本句法元素用以指明計算時用到的引數表示為在 QPC 值的表格中尋找 Cb色度分量而應加到引數 QPY 和 QSY 上的偏移,chroma_qp_index_offset 的值應在-12 到 +12範圍內(包括邊界值)
int b_deblocking_filter_control;//[畢厚傑:P147 deblocking_filter_control_present_flag] 編碼器可以通過句法元素顯式地控制去塊濾波的強度
int b_constrained_intra_pred;//[畢厚傑:P147 constrained_intra_pred_flag] 在p和b片中,幀內編碼的巨集塊的鄰近巨集塊可能是採用的幀間編碼
int b_redundant_pic_cnt;//[畢厚傑:P147 redundant_pic_cnt_present_flag] redundant_pic_cnt 對於那些屬於基本編碼影象的條帶和條帶資料分割塊應等於0。在冗餘編碼影象中的編碼條帶和編碼條帶資料分割塊的 redundant_pic_cnt 的值應大於 0。當redundant_pic_cnt 不存在時,預設其值為 0。redundant_pic_cnt的值應該在 0到 127範圍內(包括 0和127)
int b_transform_8x8_mode;//?????????????
int i_cqm_preset;//cqm:外部量化矩陣的設定
const uint8_t *scaling_list[6];
} x264_pps_t;//影象引數集[畢厚傑,P146 ]
static const uint8_t x264_cqm_jvt4i[16] =
{
6,13,20,28,
13,20,28,32,
20,28,32,37,
28,32,37,42
};
static const uint8_t x264_cqm_jvt4p[16] =
{
10,14,20,24,
14,20,24,27,
20,24,27,30,
24,27,30,34
};
static const uint8_t x264_cqm_jvt8i[64] =
{
6,10,13,16,18,23,25,27,
10,11,16,18,23,25,27,29,
13,16,18,23,25,27,29,31,
16,18,23,25,27,29,31,33,
18,23,25,27,29,31,33,36,
23,25,27,29,31,33,36,38,
25,27,29,31,33,36,38,40,
27,29,31,33,36,38,40,42
};
static const uint8_t x264_cqm_jvt8p[64] =
{
9,13,15,17,19,21,22,24,
13,13,17,19,21,22,24,25,
15,17,19,21,22,24,25,27,
17,19,21,22,24,25,27,28,
19,21,22,24,25,27,28,30,
21,22,24,25,27,28,30,32,
22,24,25,27,28,30,32,33,
24,25,27,28,30,32,33,35
};
static const uint8_t x264_cqm_flat16[64] =
{
16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16,
16,16,16,16,16,16,16,16
};
static const uint8_t * const x264_cqm_jvt[6] =
{
x264_cqm_jvt4i, x264_cqm_jvt4p,
x264_cqm_jvt4i, x264_cqm_jvt4p,
x264_cqm_jvt8i, x264_cqm_jvt8p
};
void x264_cqm_init( x264_t *h );
void x264_cqm_delete( x264_t *h );
int x264_cqm_parse_file( x264_t *h, const char *filename );
(二)
1. if( p_set_outfile_param( opt->hout, param ) ) // p_set_outfile_param = set_param_bsf 判斷輸出檔案
{
fprintf( stderr, "can't set outfile param\n" );
p_close_infile( opt->hin );
p_close_outfile( opt->hout );
return -1;
}
p_set_outfile_param = set_param_bsf , 在muxers.c中,函式原型為:
int set_param_bsf( hnd_t handle, x264_param_t *p_param )
{
return 0;
}
2. x264_picture_alloc( &pic, X264_CSP_I420, param->i_width, param->i_height );
//構造一個影象幀的初始化空間,在common.c中,函式原型為:
void x264_picture_alloc( x264_picture_t *pic, int i_csp, int i_width, int i_height )
{
pic->i_type = X264_TYPE_AUTO;
pic->i_qpplus1 = 0;
pic->img.i_csp = i_csp;
switch( i_csp & X264_CSP_MASK )
{
case X264_CSP_I420:
case X264_CSP_YV12:
pic->img.i_plane = 3;
pic->img.plane[0] = x264_malloc( 3 * i_width * i_height / 2 );
pic->img.plane[1] = pic->img.plane[0] + i_width * i_height;
pic->img.plane[2] = pic->img.plane[1] + i_width * i_height / 4;
pic->img.i_stride[0] = i_width;
pic->img.i_stride[1] = i_width / 2;
pic->img.i_stride[2] = i_width / 2;
break;
case X264_CSP_I422:
...
}
}
3. i_start = x264_mdate(); //用於編碼用時的計算,設定起始時間,在 mdate.c中,函式原型為:
int64_t x264_mdate( void )
{
#if !(defined(_MSC_VER) || defined(__MINGW32__))
struct timeval tv_date;
gettimeofday( &tv_date, NULL );
return( (int64_t) tv_date.tv_sec * 1000000 + (int64_t) tv_date.tv_usec );
#else
struct _timeb tb;
_ftime(&tb);
return ((int64_t)tb.time * (1000) + (int64_t)tb.millitm) * (1000);
#endif
}
4. 進入編碼幀
for( i_frame = 0, i_file = 0, i_progress = 0;
b_ctrl_c == 0 && (i_frame < i_frame_total || i_frame_total == 0); )
{
if( p_read_frame( &pic, opt->hin, i_frame + opt->i_seek ) )//讀取
break;
//p_read_frame() 按照h->hin提供的輸入檔案的地址,讀入影象的內容到&pic提供的儲存區的首地址
pic.i_pts = (int64_t)i_frame * param->i_fps_den;
i_file += Encode_frame( h, opt->hout, &pic );//編碼並儲存,
//Encode_frame(
h, opt->hout, &pic )實現編碼,是x264的核心部分
i_frame++;
if( opt->b_progress && param->i_log_level < X264_LOG_DEBUG &&
( i_frame_total ? i_frame * 1000 / i_frame_total > i_progress
: i_frame % 10 == 0 ) )
{
int64_t i_elapsed = x264_mdate() - i_start;
double fps = i_elapsed > 0 ? i_frame * 1000000. / i_elapsed : 0;
if( i_frame_total )
{
int eta = i_elapsed * (i_frame_total - i_frame) / ((int64_t)i_frame * 1000000);
i_progress = i_frame * 1000 / i_frame_total;
fprintf( stderr, "encoded frames: %d/%d (%.1f%%), %.2f fps, eta %d:d:d \r",
i_frame, i_frame_total, (float)i_progress / 10, fps,
eta/3600, (eta/60)`, eta` );
}
else
fprintf( stderr, "encoded frames: %d, %.2f fps \r", i_frame, fps );
fflush( stderr ); // needed in windows