1. 程式人生 > >x264程式碼閱讀筆記——非常詳細

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 );

#endif

(二)

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