x264原始碼簡單分析:熵編碼(Entropy Encoding)部分,x264entropy

來源:互聯網
上載者:User

x264原始碼簡單分析:熵編碼(Entropy Encoding)部分,x264entropy

=====================================================

H.264原始碼分析文章列表:

【編碼 - x264】

x264原始碼簡單分析:概述

x264原始碼簡單分析:x264命令列工具(x264.exe)

x264原始碼簡單分析:編碼器主幹部分-1

x264原始碼簡單分析:編碼器主幹部分-2

x264原始碼簡單分析:x264_slice_write()

x264原始碼簡單分析:濾波(Filter)部分

x264原始碼簡單分析:宏塊分析(Analysis)部分-幀內宏塊(Intra)

x264原始碼簡單分析:宏塊分析(Analysis)部分-幀間宏塊(Inter)

x264原始碼簡單分析:宏塊編碼(Encode)部分

x264原始碼簡單分析:熵編碼(Entropy Encoding)部分

FFmpeg與libx264介面原始碼簡單分析

【解碼 - libavcodec H.264 解碼器】

FFmpeg的H.264解碼器原始碼簡單分析:概述

FFmpeg的H.264解碼器原始碼簡單分析:解析器(Parser)部分

FFmpeg的H.264解碼器原始碼簡單分析:解碼器主幹部分

FFmpeg的H.264解碼器原始碼簡單分析:熵解碼(EntropyDecoding)部分

FFmpeg的H.264解碼器原始碼簡單分析:宏塊解碼(Decode)部分-幀內宏塊(Intra)

FFmpeg的H.264解碼器原始碼簡單分析:宏塊解碼(Decode)部分-幀間宏塊(Inter)

FFmpeg的H.264解碼器原始碼簡單分析:環路濾波(Loop Filter)部分

=====================================================


本文記錄x264的 x264_slice_write()函數中調用的x264_macroblock_write_cavlc()的原始碼。x264_macroblock_write_cavlc()對應著x264中的熵編碼模組。熵編碼模組主要完成了編碼資料輸出的功能。



函數呼叫歷程圖

熵編碼(Entropy Encoding)部分的原始碼在整個x264中的位置如所示。


單擊查看更清晰的圖片
熵編碼(Entropy Encoding)部分的函數調用關係如所示。
 單擊查看更清晰的圖片
可以看出,熵編碼模組包含兩個函數x264_macroblock_write_cabac()和x264_macroblock_write_cavlc()。如果輸出設定為CABAC編碼,則會調用x264_macroblock_write_cabac();如果輸出設定為CAVLC編碼,則會調用x264_macroblock_write_cavlc()。本文選擇CAVLC編碼輸出函數x264_macroblock_write_cavlc()進行分析。該函數調用了如下函數:
x264_cavlc_mb_header_i():寫入I宏塊MB Header資料。包含幀內預測模式等。
x264_cavlc_mb_header_p():寫入P宏塊MB Header資料。包含MVD、參考幀序號等。
x264_cavlc_mb_header_b():寫入B宏塊MB Header資料。包含MVD、參考幀序號等。
x264_cavlc_qp_delta():寫入QP。
x264_cavlc_block_residual():寫入殘差資料。


x264_slice_write()x264_slice_write()是x264項目的核心,它完成了編碼了一個Slice的工作。有關該函數的分析可以參考文章《x264原始碼簡單分析:x264_slice_write()》。本文分析其調用的x264_macroblock_write_cavlc()函數。


x264_macroblock_write_cavlc()x264_macroblock_write_cavlc()用於以CAVLC編碼的方式輸出H.264碼流。該函數的定義位於encoder\cavlc.c,如下所示。
/***************************************************************************** * x264_macroblock_write: * * 注釋和處理:雷霄驊 * http://blog.csdn.net/leixiaohua1020 * leixiaohua1020@126.com *****************************************************************************/void x264_macroblock_write_cavlc( x264_t *h ){    bs_t *s = &h->out.bs;    const int i_mb_type = h->mb.i_type;    int plane_count = CHROMA444 ? 3 : 1;    int chroma = !CHROMA444;#if RDO_SKIP_BS    s->i_bits_encoded = 0;#else    const int i_mb_pos_start = bs_pos( s );    int       i_mb_pos_tex;#endif    if( SLICE_MBAFF        && (!(h->mb.i_mb_y & 1) || IS_SKIP(h->mb.type[h->mb.i_mb_xy - h->mb.i_mb_stride])) )    {        bs_write1( s, MB_INTERLACED );#if !RDO_SKIP_BS        h->mb.field_decoding_flag = MB_INTERLACED;#endif    }#if !RDO_SKIP_BS    if( i_mb_type == I_PCM )    {        static const uint8_t i_offsets[3] = {5,23,0};        uint8_t *p_start = s->p_start;        bs_write_ue( s, i_offsets[h->sh.i_type] + 25 );        i_mb_pos_tex = bs_pos( s );        h->stat.frame.i_mv_bits += i_mb_pos_tex - i_mb_pos_start;        bs_align_0( s );        for( int p = 0; p < plane_count; p++ )            for( int i = 0; i < 256; i++ )                bs_write( s, BIT_DEPTH, h->mb.pic.p_fenc[p][i] );        if( chroma )            for( int ch = 1; ch < 3; ch++ )                for( int i = 0; i < 16>>CHROMA_V_SHIFT; i++ )                    for( int j = 0; j < 8; j++ )                        bs_write( s, BIT_DEPTH, h->mb.pic.p_fenc[ch][i*FENC_STRIDE+j] );        bs_init( s, s->p, s->p_end - s->p );        s->p_start = p_start;        h->stat.frame.i_tex_bits += bs_pos(s) - i_mb_pos_tex;        return;    }#endif    if( h->sh.i_type == SLICE_TYPE_P )        x264_cavlc_mb_header_p( h, i_mb_type, chroma );//寫入P宏塊MB Header資料-CAVLC    else if( h->sh.i_type == SLICE_TYPE_B )        x264_cavlc_mb_header_b( h, i_mb_type, chroma );//寫入B宏塊MB Header資料-CAVLC    else //if( h->sh.i_type == SLICE_TYPE_I )        x264_cavlc_mb_header_i( h, i_mb_type, 0, chroma );//寫入I宏塊MB Header資料-CAVLC#if !RDO_SKIP_BS    i_mb_pos_tex = bs_pos( s );    h->stat.frame.i_mv_bits += i_mb_pos_tex - i_mb_pos_start;#endif    /* Coded block pattern */    if( i_mb_type != I_16x16 )        bs_write_ue( s, cbp_to_golomb[chroma][IS_INTRA(i_mb_type)][(h->mb.i_cbp_chroma << 4)|h->mb.i_cbp_luma] );    /* transform size 8x8 flag */    if( x264_mb_transform_8x8_allowed( h ) && h->mb.i_cbp_luma )        bs_write1( s, h->mb.b_transform_8x8 );    if( i_mb_type == I_16x16 )    {        x264_cavlc_qp_delta( h );        /* DC Luma */        for( int p = 0; p < plane_count; p++ )        {            x264_cavlc_block_residual( h, DCT_LUMA_DC, LUMA_DC+p, h->dct.luma16x16_dc[p] );            /* AC Luma */            if( h->mb.i_cbp_luma )                for( int i = p*16; i < p*16+16; i++ )                    x264_cavlc_block_residual( h, DCT_LUMA_AC, i, h->dct.luma4x4[i]+1 );        }    }    else if( h->mb.i_cbp_luma | h->mb.i_cbp_chroma )    {        x264_cavlc_qp_delta( h );        //殘差資料        x264_cavlc_macroblock_luma_residual( h, plane_count );    }    if( h->mb.i_cbp_chroma )    {        /* Chroma DC residual present */        x264_cavlc_block_residual( h, DCT_CHROMA_DC, CHROMA_DC+0, h->dct.chroma_dc[0] );        x264_cavlc_block_residual( h, DCT_CHROMA_DC, CHROMA_DC+1, h->dct.chroma_dc[1] );        if( h->mb.i_cbp_chroma == 2 ) /* Chroma AC residual present */        {            int step = 8 << CHROMA_V_SHIFT;            for( int i = 16; i < 3*16; i += step )                for( int j = i; j < i+4; j++ )                    x264_cavlc_block_residual( h, DCT_CHROMA_AC, j, h->dct.luma4x4[j]+1 );        }    }#if !RDO_SKIP_BS    h->stat.frame.i_tex_bits += bs_pos(s) - i_mb_pos_tex;#endif}

從原始碼可以看出,x264_macroblock_write_cavlc()的流程大致如下:
(1)根據Slice類型的不同,調用不同的函數輸出宏塊頭(MB Header):
a)對於P Slice,調用x264_cavlc_mb_header_p()
b)對於B Slice,調用x264_cavlc_mb_header_b()
c)對於I Slice,調用x264_cavlc_mb_header_i()
(2)調用x264_cavlc_qp_delta()輸出宏塊QP值
(3)調用x264_cavlc_block_residual()輸出CAVLC編碼的殘差資料
下文將會分別分析其中涉及到的幾個函數。


x264_cavlc_mb_header_i()x264_cavlc_mb_header_i()用於輸出I Slice中宏塊的宏塊頭(MB Header)。該函數的定義位於encoder\cavlc.c,如下所示。
//寫入I宏塊Header資料-CAVLCstatic void x264_cavlc_mb_header_i( x264_t *h, int i_mb_type, int i_mb_i_offset, int chroma ){    bs_t *s = &h->out.bs;    if( i_mb_type == I_16x16 )    {        bs_write_ue( s, i_mb_i_offset + 1 + x264_mb_pred_mode16x16_fix[h->mb.i_intra16x16_pred_mode] +                        h->mb.i_cbp_chroma * 4 + ( h->mb.i_cbp_luma == 0 ? 0 : 12 ) );    }    else //if( i_mb_type == I_4x4 || i_mb_type == I_8x8 )    {        int di = i_mb_type == I_8x8 ? 4 : 1;        bs_write_ue( s, i_mb_i_offset + 0 );        if( h->pps->b_transform_8x8_mode )            bs_write1( s, h->mb.b_transform_8x8 );        /* Prediction: Luma */        for( int i = 0; i < 16; i += di )        {        //寫入Intra4x4宏塊的幀內預測模式        //獲得幀內模式的預測值(通過左邊和上邊的塊)            int i_pred = x264_mb_predict_intra4x4_mode( h, i );            //獲得當前幀內模式            int i_mode = x264_mb_pred_mode4x4_fix( h->mb.cache.intra4x4_pred_mode[x264_scan8[i]] );            if( i_pred == i_mode )                bs_write1( s, 1 );  //如果當前模式正好等於預測值/* b_prev_intra4x4_pred_mode */            else                bs_write( s, 4, i_mode - (i_mode > i_pred) );//否則傳送差值(差值=當前模式-預測模式)        }    }    if( chroma )        bs_write_ue( s, x264_mb_chroma_pred_mode_fix[h->mb.i_chroma_pred_mode] );}

從原始碼可以看出,x264_cavlc_mb_header_i()在宏塊為Intra16x16和Intra4x4的時候做了不同的處理。在Intra4x4幀內編碼的宏塊中,每個4x4的子塊都有自己的幀內預測方式。H.264碼流中並不是直接儲存了每個子塊的幀內預測方式(不利於壓縮)。而是優先通過有周圍塊的資訊推測當前塊的幀內預測模式。具體的方法就是擷取到左邊塊和上邊塊的預測模式,然後取它們的最小值作為當前塊的預測模式。X264中有關這一部分的實現位於x264_mb_predict_intra4x4_mode()函數中。


x264_mb_predict_intra4x4_mode()x264_mb_predict_intra4x4_mode()用於在Intra4x4宏塊中獲得當前塊模式的預測值,定義如下所示。
//獲得Intra4x4幀內模式的預測值static ALWAYS_INLINE int x264_mb_predict_intra4x4_mode( x264_t *h, int idx ){//左邊4x4塊的幀內預測模式    const int ma = h->mb.cache.intra4x4_pred_mode[x264_scan8[idx] - 1];    //上邊4x4塊的幀內預測模式    const int mb = h->mb.cache.intra4x4_pred_mode[x264_scan8[idx] - 8];    //取左邊和上邊的最小值,作為預測值    const int m  = X264_MIN( x264_mb_pred_mode4x4_fix(ma),                             x264_mb_pred_mode4x4_fix(mb) );    if( m < 0 )        return I_PRED_4x4_DC;    return m;}

x264_cavlc_mb_header_i()會將x264_mb_predict_intra4x4_mode()得到的預測值與當前宏塊實際的預測模式進行比較,如果正好相等則可以略去不傳,如果不等的話則傳送它們的差值。


x264_cavlc_mb_header_p()x264_cavlc_mb_header_p()用於輸出P Slice中宏塊的宏塊頭(MB Header)。該函數的定義位於encoder\cavlc.c,如下所示。
//寫入P宏塊Header資料-CAVLCstatic ALWAYS_INLINE void x264_cavlc_mb_header_p( x264_t *h, int i_mb_type, int chroma ){    bs_t *s = &h->out.bs;    if( i_mb_type == P_L0 )    {        if( h->mb.i_partition == D_16x16 )        {            bs_write1( s, 1 );            //寫入參考幀序號            if( h->mb.pic.i_fref[0] > 1 )                bs_write_te( s, h->mb.pic.i_fref[0] - 1, h->mb.cache.ref[0][x264_scan8[0]] );            /*             * 向碼流中寫入MVD             * * 運動向量緩衝mv[] * 第3個參數是mv資料的起始點(scan8[]序號),在這裡是mv[scan8[0]] * * 寫入了Y *   | * --+-------------- *   | 0 0 0 0 0 0 0 0 *   | 0 0 0 0 Y 1 1 1 *   | 0 0 0 0 1 1 1 1 *   | 0 0 0 0 1 1 1 1 *   | 0 0 0 0 1 1 1 1 */            x264_cavlc_mvd( h, 0, 0, 4 );        }        else if( h->mb.i_partition == D_16x8 )        {            bs_write_ue( s, 1 );            /*             * 向碼流中寫入參考幀序號、MVD * 寫入了Y *   | * --+-------------- *   | 0 0 0 0 0 0 0 0 *   | 0 0 0 0 Y 1 1 1 *   | 0 0 0 0 1 1 1 1 *   | 0 0 0 0 Y 2 2 2 *   | 0 0 0 0 2 2 2 2 */            if( h->mb.pic.i_fref[0] > 1 )            {                bs_write_te( s, h->mb.pic.i_fref[0] - 1, h->mb.cache.ref[0][x264_scan8[0]] );                bs_write_te( s, h->mb.pic.i_fref[0] - 1, h->mb.cache.ref[0][x264_scan8[8]] );            }            x264_cavlc_mvd( h, 0, 0, 4 );            x264_cavlc_mvd( h, 0, 8, 4 );        }        else if( h->mb.i_partition == D_8x16 )        {            bs_write_ue( s, 2 );            /*             * 向碼流中寫入參考幀序號、MVD * 寫入了Y *   | * --+-------------- *   | 0 0 0 0 0 0 0 0 *   | 0 0 0 0 Y 1 Y 2 *   | 0 0 0 0 1 1 2 2 *   | 0 0 0 0 1 1 2 2 *   | 0 0 0 0 1 1 2 2 */            if( h->mb.pic.i_fref[0] > 1 )            {                bs_write_te( s, h->mb.pic.i_fref[0] - 1, h->mb.cache.ref[0][x264_scan8[0]] );                bs_write_te( s, h->mb.pic.i_fref[0] - 1, h->mb.cache.ref[0][x264_scan8[4]] );            }            x264_cavlc_mvd( h, 0, 0, 2 );            x264_cavlc_mvd( h, 0, 4, 2 );        }    }    else if( i_mb_type == P_8x8 )    {        int b_sub_ref;        if( (h->mb.cache.ref[0][x264_scan8[0]] | h->mb.cache.ref[0][x264_scan8[ 4]] |             h->mb.cache.ref[0][x264_scan8[8]] | h->mb.cache.ref[0][x264_scan8[12]]) == 0 )        {            bs_write_ue( s, 4 );            b_sub_ref = 0;        }        else        {            bs_write_ue( s, 3 );            b_sub_ref = 1;        }        /* sub mb type */        if( h->param.analyse.inter & X264_ANALYSE_PSUB8x8 )            for( int i = 0; i < 4; i++ )                bs_write_ue( s, subpartition_p_to_golomb[ h->mb.i_sub_partition[i] ] );        else            bs_write( s, 4, 0xf );        /* ref0 */        //參考幀序號        if( b_sub_ref )        {            bs_write_te( s, h->mb.pic.i_fref[0] - 1, h->mb.cache.ref[0][x264_scan8[0]] );            bs_write_te( s, h->mb.pic.i_fref[0] - 1, h->mb.cache.ref[0][x264_scan8[4]] );            bs_write_te( s, h->mb.pic.i_fref[0] - 1, h->mb.cache.ref[0][x264_scan8[8]] );            bs_write_te( s, h->mb.pic.i_fref[0] - 1, h->mb.cache.ref[0][x264_scan8[12]] );        }        //寫入8x8塊的子塊的MVD        for( int i = 0; i < 4; i++ )            x264_cavlc_8x8_mvd( h, i );    }    else //if( IS_INTRA( i_mb_type ) )        x264_cavlc_mb_header_i( h, i_mb_type, 5, chroma );}

從原始碼可以看出,x264_cavlc_mb_header_p()主要完成了輸出P宏塊參考幀序號和運動向量的功能。對於P16x16、P16x8、P8x16、P8x8這幾種方式採用了類似的輸出方式。需要注意運動向量資訊在H.264中是以MVD(運動向量差值)的方式儲存的(而不是直接儲存)。一個宏塊真正的運動向量應該使用下式計算:
MV=預測MV+MVD其中“預測MV”是由當前宏塊的左邊,上邊,以及右上方宏塊的MV預測而來。預測的方式就是取這3個塊的中值(注意不是平均值)。X264中輸出MVD的函數是x264_cavlc_mvd()。

x264_cavlc_mvd()x264_cavlc_mvd()用於輸出運動向量的MVD資訊。該函數的定義如下所示。
//寫入MVDstatic void x264_cavlc_mvd( x264_t *h, int i_list, int idx, int width ){    bs_t *s = &h->out.bs;    ALIGNED_4( int16_t mvp[2] );    //獲得預測MV    x264_mb_predict_mv( h, i_list, idx, width, mvp );    //實際儲存MVD    //MVD=MV-預測MV    bs_write_se( s, h->mb.cache.mv[i_list][x264_scan8[idx]][0] - mvp[0] );    bs_write_se( s, h->mb.cache.mv[i_list][x264_scan8[idx]][1] - mvp[1] );}

從原始碼可以看出,x264_cavlc_mvd()首先調用x264_mb_predict_mv()通過左邊,上邊和右上宏塊的運動向量推算出預測運動向量,然後將當前實際運動向量與預測運動向量相減後輸出。


x264_mb_predict_mv()x264_mb_predict_mv()用於獲得預測的運動向量。該函數的定義如下所示。
//獲得預測的運動向量MV(通過取中值)void x264_mb_predict_mv( x264_t *h, int i_list, int idx, int i_width, int16_t mvp[2] ){    const int i8 = x264_scan8[idx];    const int i_ref= h->mb.cache.ref[i_list][i8];    int     i_refa = h->mb.cache.ref[i_list][i8 - 1];    int16_t *mv_a  = h->mb.cache.mv[i_list][i8 - 1];    int     i_refb = h->mb.cache.ref[i_list][i8 - 8];    int16_t *mv_b  = h->mb.cache.mv[i_list][i8 - 8];    int     i_refc = h->mb.cache.ref[i_list][i8 - 8 + i_width];    int16_t *mv_c  = h->mb.cache.mv[i_list][i8 - 8 + i_width];    // Partitions not yet reached in scan order are unavailable.    if( (idx&3) >= 2 + (i_width&1) || i_refc == -2 )    {        i_refc = h->mb.cache.ref[i_list][i8 - 8 - 1];        mv_c   = h->mb.cache.mv[i_list][i8 - 8 - 1];        if( SLICE_MBAFF            && h->mb.cache.ref[i_list][x264_scan8[0]-1] != -2            && MB_INTERLACED != h->mb.field[h->mb.i_mb_left_xy[0]] )        {            if( idx == 2 )            {                mv_c = h->mb.cache.topright_mv[i_list][0];                i_refc = h->mb.cache.topright_ref[i_list][0];            }            else if( idx == 8 )            {                mv_c = h->mb.cache.topright_mv[i_list][1];                i_refc = h->mb.cache.topright_ref[i_list][1];            }            else if( idx == 10 )            {                mv_c = h->mb.cache.topright_mv[i_list][2];                i_refc = h->mb.cache.topright_ref[i_list][2];            }        }    }    if( h->mb.i_partition == D_16x8 )    {        if( idx == 0 )        {            if( i_refb == i_ref )            {                CP32( mvp, mv_b );                return;            }        }        else        {            if( i_refa == i_ref )            {                CP32( mvp, mv_a );                return;            }        }    }    else if( h->mb.i_partition == D_8x16 )    {        if( idx == 0 )        {            if( i_refa == i_ref )            {                CP32( mvp, mv_a );                return;            }        }        else        {            if( i_refc == i_ref )            {                CP32( mvp, mv_c );                return;            }        }    }    int i_count = (i_refa == i_ref) + (i_refb == i_ref) + (i_refc == i_ref);    //如果可參考運動向量的個數大於1個    if( i_count > 1 )    {median://取中值//x264_median_mv()內部調用了2次x264_median(),分別求了運動向量的x分量和y分量的中值        x264_median_mv( mvp, mv_a, mv_b, mv_c );    }    else if( i_count == 1 ) //如果可參考運動向量的個數只有1個    {    //直接賦值        if( i_refa == i_ref )            CP32( mvp, mv_a );        else if( i_refb == i_ref )            CP32( mvp, mv_b );        else            CP32( mvp, mv_c );    }    else if( i_refb == -2 && i_refc == -2 && i_refa != -2 )        CP32( mvp, mv_a );    else        goto median;}

可以看出x264_mb_predict_mv()去了左邊,上邊,右上宏塊運動向量的中值作為預測的運動向量。其中的x264_median_mv()是一個取中值的函數。

 x264_cavlc_qp_delta()x264_cavlc_qp_delta()用於輸出宏塊的QP資訊。該函數的定義如下所示。
//QPstatic void x264_cavlc_qp_delta( x264_t *h ){    bs_t *s = &h->out.bs;    //相減    int i_dqp = h->mb.i_qp - h->mb.i_last_qp;    /* Avoid writing a delta quant if we have an empty i16x16 block, e.g. in a completely     * flat background area. Don't do this if it would raise the quantizer, since that could     * cause unexpected deblocking artifacts. */    if( h->mb.i_type == I_16x16 && !(h->mb.i_cbp_luma | h->mb.i_cbp_chroma)        && !h->mb.cache.non_zero_count[x264_scan8[LUMA_DC]]        && !h->mb.cache.non_zero_count[x264_scan8[CHROMA_DC+0]]        && !h->mb.cache.non_zero_count[x264_scan8[CHROMA_DC+1]]        && h->mb.i_qp > h->mb.i_last_qp )    {#if !RDO_SKIP_BS        h->mb.i_qp = h->mb.i_last_qp;#endif        i_dqp = 0;    }    if( i_dqp )    {        if( i_dqp < -(QP_MAX_SPEC+1)/2 )            i_dqp += QP_MAX_SPEC+1;        else if( i_dqp > QP_MAX_SPEC/2 )            i_dqp -= QP_MAX_SPEC+1;    }    bs_write_se( s, i_dqp );}

在這裡需要注意,QP資訊在H.264碼流中是以“QP位移值”的形式儲存的。“QP位移值”指的是當前宏塊和上一個宏塊之間的差值。因此x264_cavlc_qp_delta()中使用當前宏塊的QP減去上一個宏塊的QP之後再進行輸出。


x264_cavlc_macroblock_luma_residual()x264_cavlc_macroblock_luma_residual()用於將殘差資料以CAVLC編碼的方式輸出出來。該函數的定義如下所示。
static ALWAYS_INLINE void x264_cavlc_macroblock_luma_residual( x264_t *h, int plane_count ){    if( h->mb.b_transform_8x8 )    {        /* shuffle 8x8 dct coeffs into 4x4 lists */        for( int p = 0; p < plane_count; p++ )            for( int i8 = 0; i8 < 4; i8++ )                if( h->mb.cache.non_zero_count[x264_scan8[p*16+i8*4]] )                    h->zigzagf.interleave_8x8_cavlc( h->dct.luma4x4[p*16+i8*4], h->dct.luma8x8[p*4+i8],                                                     &h->mb.cache.non_zero_count[x264_scan8[p*16+i8*4]] );    }    for( int p = 0; p < plane_count; p++ )        FOREACH_BIT( i8, 0, h->mb.i_cbp_luma )            for( int i4 = 0; i4 < 4; i4++ )                x264_cavlc_block_residual( h, DCT_LUMA_4x4, i4+i8*4+p*16, h->dct.luma4x4[i4+i8*4+p*16] );}

從原始碼可以看出,x264_cavlc_macroblock_luma_residual()調用了x264_cavlc_block_residual()進行殘差資料的輸出。由於x264_cavlc_block_residual()的原始碼還沒有看過,就不再深入分析了。

至此有關x264熵編碼模組的原始碼就分析完畢了。




雷霄驊

leixiaohua1020@126.com

http://blog.csdn.net/leixiaohua1020


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.