1. 程式人生 > >FFmpeg的H.264解碼器原始碼簡單分析:熵解碼(Entropy Decoding)部分

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

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

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

【編碼 - x264】

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

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


本文分析FFmpeg的H.264解碼器的熵解碼(Entropy Decoding)部分的原始碼。FFmpeg的H.264解碼器呼叫decode_slice()函式完成了解碼工作。這些解碼工作可以大體上分為3個步驟:熵解碼,巨集塊解碼以及環路濾波。本文分析這3個步驟中的第1個步驟。

函式呼叫關係圖

熵解碼(Entropy Decoding)部分的原始碼在整個H.264解碼器中的位置如下圖所示。
熵解碼(Entropy Decoding)部分的原始碼的呼叫關係如下圖所示。
從圖中可以看出,FFmpeg的熵解碼方面的函式有兩個:ff_h264_decode_mb_cabac()和ff_h264_decode_mb_cavlc()。ff_h264_decode_mb_cabac()用於解碼CABAC編碼方式的H.264資料,ff_h264_decode_mb_cavlc()用於解碼CAVLC編碼方式的H.264資料。本文挑選了ff_h264_decode_mb_cavlc()函式進行分析。
ff_h264_decode_mb_cavlc()呼叫了很多的讀取指數哥倫布編碼資料的函式,例如get_ue_golomb_long(),get_ue_golomb(),get_se_golomb(),get_ue_golomb_31()等。此外在解碼殘差資料的時候,呼叫了decode_residual()函式,而decode_residual()會呼叫get_vlc2()函式讀取CAVLC編碼資料。
總而言之,“熵解碼”部分的作用就是按照H.264語法和語義的規定,讀取資料(巨集塊型別、運動向量、參考幀、殘差等)並且賦值到FFmpeg H.264解碼器中相應的變數上。需要注意的是,“熵解碼”部分並不使用這些變數還原視訊資料。還原視訊資料的功能在下一步“巨集塊解碼”步驟中完成。
在開始看ff_h264_decode_mb_cavlc()之前先回顧一下decode_slice()函式。

decode_slice()

decode_slice()用於解碼H.264的Slice。該函式完成了“熵解碼”、“巨集塊解碼”、“環路濾波”的功能。它的定義位於libavcodec\h264_slice.c,如下所示。
//解碼slice
//三個主要步驟:
//1.熵解碼(CAVLC/CABAC)
//2.巨集塊解碼
//3.環路濾波
//此外還包含了錯誤隱藏程式碼
static int decode_slice(struct AVCodecContext *avctx, void *arg)
{
    H264Context *h = *(void **)arg;
    int lf_x_start = h->mb_x;

    h->mb_skip_run = -1;

    av_assert0(h->block_offset[15] == (4 * ((scan8[15] - scan8[0]) & 7) << h->pixel_shift) + 4 * h->linesize * ((scan8[15] - scan8[0]) >> 3));

    h->is_complex = FRAME_MBAFF(h) || h->picture_structure != PICT_FRAME ||
                    avctx->codec_id != AV_CODEC_ID_H264 ||
                    (CONFIG_GRAY && (h->flags & CODEC_FLAG_GRAY));

    if (!(h->avctx->active_thread_type & FF_THREAD_SLICE) && h->picture_structure == PICT_FRAME && h->er.error_status_table) {
        const int start_i  = av_clip(h->resync_mb_x + h->resync_mb_y * h->mb_width, 0, h->mb_num - 1);
        if (start_i) {
            int prev_status = h->er.error_status_table[h->er.mb_index2xy[start_i - 1]];
            prev_status &= ~ VP_START;
            if (prev_status != (ER_MV_END | ER_DC_END | ER_AC_END))
                h->er.error_occurred = 1;
        }
    }
    //CABAC情況
    if (h->pps.cabac) {
        /* realign */
        align_get_bits(&h->gb);

        /* init cabac */
        //初始化CABAC解碼器
        ff_init_cabac_decoder(&h->cabac,
                              h->gb.buffer + get_bits_count(&h->gb) / 8,
                              (get_bits_left(&h->gb) + 7) / 8);

        ff_h264_init_cabac_states(h);
        //迴圈處理每個巨集塊
        for (;;) {
            // START_TIMER
        	//解碼CABAC資料
            int ret = ff_h264_decode_mb_cabac(h);
            int eos;
            // STOP_TIMER("decode_mb_cabac")
            //解碼巨集塊
            if (ret >= 0)
                ff_h264_hl_decode_mb(h);

            // FIXME optimal? or let mb_decode decode 16x32 ?
            //巨集塊級幀場自適應。很少接觸
            if (ret >= 0 && FRAME_MBAFF(h)) {
                h->mb_y++;

                ret = ff_h264_decode_mb_cabac(h);
                //解碼巨集塊
                if (ret >= 0)
                    ff_h264_hl_decode_mb(h);
                h->mb_y--;
            }
            eos = get_cabac_terminate(&h->cabac);

            if ((h->workaround_bugs & FF_BUG_TRUNCATED) &&
                h->cabac.bytestream > h->cabac.bytestream_end + 2) {
            	//錯誤隱藏
                er_add_slice(h, h->resync_mb_x, h->resync_mb_y, h->mb_x - 1,
                             h->mb_y, ER_MB_END);
                if (h->mb_x >= lf_x_start)
                    loop_filter(h, lf_x_start, h->mb_x + 1);
                return 0;
            }
            if (h->cabac.bytestream > h->cabac.bytestream_end + 2 )
                av_log(h->avctx, AV_LOG_DEBUG, "bytestream overread %"PTRDIFF_SPECIFIER"\n", h->cabac.bytestream_end - h->cabac.bytestream);
            if (ret < 0 || h->cabac.bytestream > h->cabac.bytestream_end + 4) {
                av_log(h->avctx, AV_LOG_ERROR,
                       "error while decoding MB %d %d, bytestream %"PTRDIFF_SPECIFIER"\n",
                       h->mb_x, h->mb_y,
                       h->cabac.bytestream_end - h->cabac.bytestream);
                er_add_slice(h, h->resync_mb_x, h->resync_mb_y, h->mb_x,
                             h->mb_y, ER_MB_ERROR);
                return AVERROR_INVALIDDATA;
            }
            //mb_x自增
            //如果自增後超過了一行的mb個數
            if (++h->mb_x >= h->mb_width) {
            	//環路濾波
                loop_filter(h, lf_x_start, h->mb_x);
                h->mb_x = lf_x_start = 0;
                decode_finish_row(h);
                //mb_y自增(處理下一行)
                ++h->mb_y;
                //巨集塊級幀場自適應,暫不考慮
                if (FIELD_OR_MBAFF_PICTURE(h)) {
                    ++h->mb_y;
                    if (FRAME_MBAFF(h) && h->mb_y < h->mb_height)
                        predict_field_decoding_flag(h);
                }
            }
            //如果mb_y超過了mb的行數
            if (eos || h->mb_y >= h->mb_height) {
                tprintf(h->avctx, "slice end %d %d\n",
                        get_bits_count(&h->gb), h->gb.size_in_bits);
                er_add_slice(h, h->resync_mb_x, h->resync_mb_y, h->mb_x - 1,
                             h->mb_y, ER_MB_END);
                if (h->mb_x > lf_x_start)
                    loop_filter(h, lf_x_start, h->mb_x);
                return 0;
            }
        }
    } else {
    	//CAVLC情況
    	//迴圈處理每個巨集塊
        for (;;) {
        	//解碼巨集塊的CAVLC
            int ret = ff_h264_decode_mb_cavlc(h);
            //解碼巨集塊
            if (ret >= 0)
                ff_h264_hl_decode_mb(h);

            // FIXME optimal? or let mb_decode decode 16x32 ?
            if (ret >= 0 && FRAME_MBAFF(h)) {
                h->mb_y++;
                ret = ff_h264_decode_mb_cavlc(h);

                if (ret >= 0)
                    ff_h264_hl_decode_mb(h);
                h->mb_y--;
            }

            if (ret < 0) {
                av_log(h->avctx, AV_LOG_ERROR,
                       "error while decoding MB %d %d\n", h->mb_x, h->mb_y);
                er_add_slice(h, h->resync_mb_x, h->resync_mb_y, h->mb_x,
                             h->mb_y, ER_MB_ERROR);
                return ret;
            }

            if (++h->mb_x >= h->mb_width) {
            	//環路濾波
                loop_filter(h, lf_x_start, h->mb_x);
                h->mb_x = lf_x_start = 0;
                decode_finish_row(h);
                ++h->mb_y;
                if (FIELD_OR_MBAFF_PICTURE(h)) {
                    ++h->mb_y;
                    if (FRAME_MBAFF(h) && h->mb_y < h->mb_height)
                        predict_field_decoding_flag(h);
                }
                if (h->mb_y >= h->mb_height) {
                    tprintf(h->avctx, "slice end %d %d\n",
                            get_bits_count(&h->gb), h->gb.size_in_bits);

                    if (   get_bits_left(&h->gb) == 0
                        || get_bits_left(&h->gb) > 0 && !(h->avctx->err_recognition & AV_EF_AGGRESSIVE)) {
                    	//錯誤隱藏
                        er_add_slice(h, h->resync_mb_x, h->resync_mb_y,
                                     h->mb_x - 1, h->mb_y, ER_MB_END);

                        return 0;
                    } else {
                        er_add_slice(h, h->resync_mb_x, h->resync_mb_y,
                                     h->mb_x, h->mb_y, ER_MB_END);

                        return AVERROR_INVALIDDATA;
                    }
                }
            }

            if (get_bits_left(&h->gb) <= 0 && h->mb_skip_run <= 0) {
                tprintf(h->avctx, "slice end %d %d\n",
                        get_bits_count(&h->gb), h->gb.size_in_bits);

                if (get_bits_left(&h->gb) == 0) {
                    er_add_slice(h, h->resync_mb_x, h->resync_mb_y,
                                 h->mb_x - 1, h->mb_y, ER_MB_END);
                    if (h->mb_x > lf_x_start)
                        loop_filter(h, lf_x_start, h->mb_x);

                    return 0;
                } else {
                    er_add_slice(h, h->resync_mb_x, h->resync_mb_y, h->mb_x,
                                 h->mb_y, ER_MB_ERROR);

                    return AVERROR_INVALIDDATA;
                }
            }
        }
    }
}

可以看出decode_slice()的的流程如下:

(1)判斷H.264碼流是CABAC編碼還是CAVLC編碼,進入不同的處理迴圈。

(2)如果是CABAC編碼,首先呼叫ff_init_cabac_decoder()初始化CABAC解碼器。然後進入一個迴圈,依次對每個巨集塊進行以下處理:

a)呼叫ff_h264_decode_mb_cabac()進行CABAC熵解碼

b)呼叫ff_h264_hl_decode_mb()進行巨集塊解碼

c)解碼一行巨集塊之後呼叫loop_filter()進行環路濾波

d)此外還有可能呼叫er_add_slice()進行錯誤隱藏處理

(3)如果是CABAC編碼,直接進入一個迴圈,依次對每個巨集塊進行以下處理:

a)呼叫ff_h264_decode_mb_cavlc()進行CAVLC熵解碼

b)呼叫ff_h264_hl_decode_mb()進行巨集塊解碼

c)解碼一行巨集塊之後呼叫loop_filter()進行環路濾波

d)此外還有可能呼叫er_add_slice()進行錯誤隱藏處理

可以看出,出了熵解碼以外,巨集塊解碼和環路濾波的函式是一樣的。下面詳細看一下CAVLC熵解碼函式ff_h264_decode_mb_cavlc()。

ff_h264_decode_mb_cavlc()

ff_h264_decode_mb_cavlc()完成了FFmpeg H.264解碼器中“熵解碼”的功能。“熵解碼”部分的作用就是按照H.264語法和語義的規定,讀取資料(巨集塊型別、運動向量、參考幀、殘差等)並且賦值到FFmpeg H.264解碼器中相應的變數上。具體說來就是完成了解析H.264碼流中Slice Data的功能。該函式比較複雜,它的定義位於libavcodec\h264_cavlc.c,如下所示。
/*
 * 註釋:雷霄驊
 * [email protected]
 * http://blog.csdn.net/leixiaohua1020
 *
 * 解碼巨集塊的CAVLC資料
 * 解碼Slice Data(注意不包含Slice Header)
 *
 */
int ff_h264_decode_mb_cavlc(H264Context *h){
    int mb_xy;
    int partition_count;
    unsigned int mb_type, cbp;
    int dct8x8_allowed= h->pps.transform_8x8_mode;
    //如果是YUV420或者YUV422,需要處理色度(YUV444中的UV直接當亮度處理)
    int decode_chroma = h->sps.chroma_format_idc == 1 || h->sps.chroma_format_idc == 2;
    const int pixel_shift = h->pixel_shift;
    unsigned local_ref_count[2];
    //mb_xy的計算方法
    mb_xy = h->mb_xy = h->mb_x + h->mb_y*h->mb_stride;

    tprintf(h->avctx, "pic:%d mb:%d/%d\n", h->frame_num, h->mb_x, h->mb_y);
    cbp = 0; /* avoid warning. FIXME: find a solution without slowing
                down the code */
    //slice_type_nos意思是SI/SP 被對映為 I/P (即沒有SI/SP這種幀)
    //處理Skip巨集塊-不攜帶任何資料
	//解碼器通過周圍已重建的巨集塊的資料來恢復skip塊
    if(h->slice_type_nos != AV_PICTURE_TYPE_I){
    	//熵編碼為CAVLC時候特有的欄位
        if(h->mb_skip_run==-1)
            h->mb_skip_run= get_ue_golomb_long(&h->gb);

        if (h->mb_skip_run--) {
            if(FRAME_MBAFF(h) && (h->mb_y&1) == 0){
                if(h->mb_skip_run==0)
                    h->mb_mbaff = h->mb_field_decoding_flag = get_bits1(&h->gb);
            }
            decode_mb_skip(h);
            return 0;
        }
    }
    if (FRAME_MBAFF(h)) {
        if( (h->mb_y&1) == 0 )
            h->mb_mbaff = h->mb_field_decoding_flag = get_bits1(&h->gb);
    }

    h->prev_mb_skipped= 0;
    //獲取巨集塊型別(I,B,P)
    //I片中只允許出現I巨集塊
    //P片中即可以出現P巨集塊也可以出現I巨集塊
    //B片中即可以出現B巨集塊也可以出現I巨集塊
    //這個語義含義比較複雜,需要查表
    mb_type= get_ue_golomb(&h->gb);
    //B
    if(h->slice_type_nos == AV_PICTURE_TYPE_B){
    	//b_mb_type_info儲存了B巨集塊的型別
    	//type代表巨集塊型別
    	//partition_count代表巨集塊分割槽數目
        if(mb_type < 23){
            partition_count= b_mb_type_info[mb_type].partition_count;
            mb_type=         b_mb_type_info[mb_type].type;
        }else{
            mb_type -= 23;
            goto decode_intra_mb;
        }
        //P
    }else if(h->slice_type_nos == AV_PICTURE_TYPE_P){
    	//p_mb_type_info儲存了P巨集塊的型別
    	//type代表巨集塊型別
    	//partition_count代表巨集塊分割槽數目(一般為1,2,4)
        if(mb_type < 5){
            partition_count= p_mb_type_info[mb_type].partition_count;
            mb_type=         p_mb_type_info[mb_type].type;
        }else{
            mb_type -= 5;
            goto decode_intra_mb;
        }
    }else{
    	//i_mb_type_info儲存了I巨集塊的型別
    	//注意i_mb_type_info和p_mb_type_info、b_mb_type_info是不一樣的:
    	//type:巨集塊型別。只有MB_TYPE_INTRA4x4,MB_TYPE_INTRA16x16(基本上都是這種),MB_TYPE_INTRA_PCM三種
    	//pred_mode:幀內預測方式(四種:DC,Horizontal,Vertical,Plane)。
    	//cbp:指亮度和色度分量的各小塊的殘差的編碼方案,所謂編碼方案有以下幾種:
    	//      0) 所有殘差(包括 DC、AC)都不編碼。
    	//      1) 只對 DC 係數編碼。
    	//      2) 所有殘差(包括 DC、AC)都編碼。
       av_assert2(h->slice_type_nos == AV_PICTURE_TYPE_I);
        if(h->slice_type == AV_PICTURE_TYPE_SI && mb_type)
            mb_type--;
decode_intra_mb:
        if(mb_type > 25){
            av_log(h->avctx, AV_LOG_ERROR, "mb_type %d in %c slice too large at %d %d\n", mb_type, av_get_picture_type_char(h->slice_type), h->mb_x, h->mb_y);
            return -1;
        }
        partition_count=0;
        cbp= i_mb_type_info[mb_type].cbp;
        h->intra16x16_pred_mode= i_mb_type_info[mb_type].pred_mode;
        mb_type= i_mb_type_info[mb_type].type;
    }
    //隔行
    if(MB_FIELD(h))
        mb_type |= MB_TYPE_INTERLACED;

    h->slice_table[ mb_xy ]= h->slice_num;
    //I_PCM不常見
    if(IS_INTRA_PCM(mb_type)){
        const int mb_size = ff_h264_mb_sizes[h->sps.chroma_format_idc] *
                            h->sps.bit_depth_luma;

        // We assume these blocks are very rare so we do not optimize it.
        h->intra_pcm_ptr = align_get_bits(&h->gb);
        if (get_bits_left(&h->gb) < mb_size) {
            av_log(h->avctx, AV_LOG_ERROR, "Not enough data for an intra PCM block.\n");
            return AVERROR_INVALIDDATA;
        }
        skip_bits_long(&h->gb, mb_size);

        // In deblocking, the quantizer is 0
        h->cur_pic.qscale_table[mb_xy] = 0;
        // All coeffs are present
        memset(h->non_zero_count[mb_xy], 16, 48);
        //賦值
        h->cur_pic.mb_type[mb_xy] = mb_type;
        return 0;
    }

    //
    local_ref_count[0] = h->ref_count[0] << MB_MBAFF(h);
    local_ref_count[1] = h->ref_count[1] << MB_MBAFF(h);

    /* 設定上左,上,上右,左巨集塊的索引值和巨集塊型別
     * 這4個巨集塊在解碼過程中會用到
     * 位置如下圖所示
     *
	 * +----+----+----+
	 * | UL |  U | UR |
	 * +----+----+----+
	 * | L  |    |
	 * +----+----+
	 */
    fill_decode_neighbors(h, mb_type);
    //填充Cache
    fill_decode_caches(h, mb_type);

    /*
     *
     * 關於多次出現的scan8
     *
     * scan8[]是一個表格。表格中儲存了一整個巨集塊的資訊,每一個元素代表了一個“4x4塊”(H.264中最小的處理單位)。
     * scan8[]中的“8”,意思應該是按照8x8為單元來掃描?
     * 因此可以理解為“按照8x8為單元來掃描4x4的塊”?
     *
     * scan8中按照順序分別儲存了Y,U,V的索引值。具體的儲存還是在相應的cache中。
     *
     * PS:“4x4”貌似是H.264解碼器中最小的“塊”單位
     *
     * cache中首先儲存Y,然後儲存U和V。cache中的儲存方式如下所示。
     * 其中數字代表了scan8[]中元素的索引值
     * scan8[]中元素的值則代表了其代表的變數在cache中的索引值
     * +---+---+---+---+---+---+---+---+---+
     * |   | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
     * +---+---+---+---+---+---+---+---+---+
     * | 0 | 48|   |   |   |  y|  y|  y|  y|
     * | 1 |   |   |   |  y|  0|  1|  4|  5|
     * | 2 |   |   |   |  y|  2|  3|  6|  7|
     * | 3 |   |   |   |  y|  8|  9| 12| 13|
     * | 4 |   |   |   |  y| 10| 11| 14| 15|
     * | 5 | 49|   |   |   |  u|  u|  u|  u|
     * | 6 |   |   |   |  u| 16| 17| 20| 21|
     * | 7 |   |   |   |  u| 18| 19| 22| 23|
     * | 8 |   |   |   |  u| 24| 25| 28| 29|
     * | 9 |   |   |   |  u| 26| 27| 30| 31|
     * |10 | 50|   |   |   |  v|  v|  v|  v|
     * |11 |   |   |   |  v| 32| 33| 36| 37|
     * |12 |   |   |   |  v| 34| 35| 38| 39|
     * |13 |   |   |   |  v| 40| 41| 44| 45|
     * |14 |   |   |   |  v| 42| 43| 46| 47|
     * |---+---+---+---+---+---+---+---+---+
     * |   |
     *
     */


    //mb_pred
    //分成3種情況進行預測工作:
    //1.幀內預測
    //2.劃分為4個塊(此時每個8x8的塊可以再次劃分為4種類型)
    //3.其他型別(包括16x16,16x8,8x16,這些劃分不可再次劃分)
    if(IS_INTRA(mb_type)){
        //情況1:幀內巨集塊
        int pred_mode;
//            init_top_left_availability(h);
        //如果是幀內4x4,幀內預測方式需要特殊處理(9種)
        if(IS_INTRA4x4(mb_type)){
            int i;
            int di = 1;
            //先不考慮這種相對特殊情況,認為di=1
            if(dct8x8_allowed && get_bits1(&h->gb)){
                mb_type |= MB_TYPE_8x8DCT;
                di = 4;
            }

//                fill_intra4x4_pred_table(h);
            //對於一個巨集塊(16x16)來說,包含了4*4=16個4x4幀內預測的塊
            //所以迴圈16次
            /*
			 * 幀內預測:16x16 巨集塊被劃分為16個4x4子塊
			 *
			 * +----+----+----+----+
			 * |    |    |    |    |
			 * +----+----+----+----+
			 * |    |    |    |    |
			 * +----+----+----+----+
			 * |    |    |    |    |
			 * +----+----+----+----+
			 * |    |    |    |    |
			 * +----+----+----+----+
			 *
			 */
            for(i=0; i<16; i+=di){
            	//獲得對Intra4x4的預測模式的預測值(挺繞口,確實是這樣)
            	//這個預測模式由左邊和上邊塊的預測模式(取最小值)推導主來
                int mode= pred_intra_mode(h, i);
                //這1bit是dcPredModePredictedFlag,如果為1,則直接使用推匯出來的預測模式
                if(!get_bits1(&h->gb)){
                	//否則就使用讀取出來的預測模式
                    const int rem_mode= get_bits(&h->gb, 3);
                    mode = rem_mode + (rem_mode >= mode);
                }

                if(di==4)
                    fill_rectangle( &h->intra4x4_pred_mode_cache[ scan8[i] ], 2, 2, 8, mode, 1 );
                else
                    h->intra4x4_pred_mode_cache[ scan8[i] ] = mode;//賦值
                /*
                 * 將mode填充至intra4x4_pred_mode_cache
                 *
				 * 用簡單圖形表示intra4x4_pred_mode_cache如下。數字代表填充順序(一共填充16次)
				 *   |
				 * --+-------------------
				 *   | 0 0 0 0  0  0  0  0
				 *   | 0 0 0 0  1  2  5  6
				 *   | 0 0 0 0  3  4  7  8
				 *   | 0 0 0 0  9 10 13 14
				 *   | 0 0 0 0 11 12 15 16
				 *
				 */

            }
            //將巨集塊的Cache中的intra4x4_pred_mode拷貝至整張圖片的intra4x4_pred_mode變數中
            write_back_intra_pred_mode(h);
            if( ff_h264_check_intra4x4_pred_mode(h) < 0)
                return -1;
        }else{
        	//幀內16x16的檢測:檢查巨集塊上方和左邊的資料是否可用
            h->intra16x16_pred_mode= ff_h264_check_intra_pred_mode(h, h->intra16x16_pred_mode, 0);
            if(h->intra16x16_pred_mode < 0)
                return -1;
        }
        if(decode_chroma){
        	//色度幀內預測的檢測,和亮度一樣
            pred_mode= ff_h264_check_intra_pred_mode(h, get_ue_golomb_31(&h->gb), 1);
            if(pred_mode < 0)
                return -1;
            h->chroma_pred_mode= pred_mode;
        } else {
            h->chroma_pred_mode = DC_128_PRED8x8;
        }
    }else if(partition_count==4){
    	//情況2:巨集塊劃分為4
    	//為什麼巨集塊劃分為4的時候要單獨處理?因為巨集塊劃分為4的時候,每個8x8的子巨集塊還可以進一步劃分為2個4x8,2個8x4(4x8),或者4個4x4。
    	//而其他方式的巨集塊劃分(例如16x16,16x8,8x16等)是不可以這樣再次劃分的
    	/*
		 * 16x16 巨集塊被劃分為4個8x8子塊
		 *
		 * +--------+--------+
		 * |        |        |
		 * |   0    |   1    |
		 * |        |        |
		 * +--------+--------+
		 * |        |        |
		 * |   2    |   3    |
		 * |        |        |
		 * +--------+--------+
		 *
		 */
        int i, j, sub_partition_count[4], list, ref[2][4];
        //獲得8x8子塊的巨集塊型別
        //後續的很多程式碼都是迴圈處理4個8x8子塊
        //所以很多for()迴圈的次數都是為4
        if(h->slice_type_nos == AV_PICTURE_TYPE_B){
            //B巨集塊
        	//4個子塊
            for(i=0; i<4; i++){
            	//子巨集塊的預測型別
                h->sub_mb_type[i]= get_ue_golomb_31(&h->gb);
                if(h->sub_mb_type[i] >=13){
                    av_log(h->avctx, AV_LOG_ERROR, "B sub_mb_type %u out of range at %d %d\n", h->sub_mb_type[i], h->mb_x, h->mb_y);
                    return -1;
                }
                sub_partition_count[i]= b_sub_mb_type_info[ h->sub_mb_type[i] ].partition_count;
                h->sub_mb_type[i]=      b_sub_mb_type_info[ h->sub_mb_type[i] ].type;
            }
            if( IS_DIRECT(h->sub_mb_type[0]|h->sub_mb_type[1]|h->sub_mb_type[2]|h->sub_mb_type[3])) {
                ff_h264_pred_direct_motion(h, &mb_type);
                h->ref_cache[0][scan8[4]] =
                h->ref_cache[1][scan8[4]] =
                h->ref_cache[0][scan8[12]] =
                h->ref_cache[1][scan8[12]] = PART_NOT_AVAILABLE;
            }
        }else{
            av_assert2(h->slice_type_nos == AV_PICTURE_TYPE_P); //FIXME SP correct ?
            //P巨集塊
            //4個子塊
            for(i=0; i<4; i++){
                h->sub_mb_type[i]= get_ue_golomb_31(&h->gb);
                if(h->sub_mb_type[i] >=4){
                    av_log(h->avctx, AV_LOG_ERROR, "P sub_mb_type %u out of range at %d %d\n", h->sub_mb_type[i], h->mb_x, h->mb_y);
                    return -1;
                }
                //p_sub_mb_type_info儲存了P子巨集塊的型別,和前面的p_mb_type_info類似
                //type代表巨集塊型別
                //partition_count代表巨集塊分割槽數目
                sub_partition_count[i]= p_sub_mb_type_info[ h->sub_mb_type[i] ].partition_count;
                h->sub_mb_type[i]=      p_sub_mb_type_info[ h->sub_mb_type[i] ].type;
            }
        }
        //8x8塊的子巨集塊的參考幀序號
        for(list=0; list<h->list_count; list++){
            int ref_count = IS_REF0(mb_type) ? 1 : local_ref_count[list];
            //4個子塊
            for(i=0; i<4; i++){
                if(IS_DIRECT(h->sub_mb_type[i])) continue;
                if(IS_DIR(h->sub_mb_type[i], 0, list)){
                    unsigned int tmp;
                    if(ref_count == 1){
                        tmp= 0;
                    }else if(ref_count == 2){
                        tmp= get_bits1(&h->gb)^1;
                    }else{
                    	//參考幀序號
                        tmp= get_ue_golomb_31(&h->gb);
                        if(tmp>=ref_count){
                            av_log(h->avctx, AV_LOG_ERROR, "ref %u overflow\n", tmp);
                            return -1;
                        }
                    }
                    //儲存
                    ref[list][i]= tmp;
                }else{
                 //FIXME
                    ref[list][i] = -1;
                }
            }
        }

        if(dct8x8_allowed)
            dct8x8_allowed = get_dct8x8_allowed(h);

        //8x8塊的子巨集塊的運動向量
        //依次處理L0和L1
        for(list=0; list<h->list_count; list++){
        	//4個子塊
            for(i=0; i<4; i++){
                if(IS_DIRECT(h->sub_mb_type[i])) {
                    h->ref_cache[list][ scan8[4*i] ] = h->ref_cache[list][ scan8[4*i]+1 ];
                    continue;
                }
                h->ref_cache[list][ scan8[4*i]   ]=h->ref_cache[list][ scan8[4*i]+1 ]=
                h->ref_cache[list][ scan8[4*i]+8 ]=h->ref_cache[list][ scan8[4*i]+9 ]= ref[list][i];

                if(IS_DIR(h->sub_mb_type[i], 0, list)){
                    const int sub_mb_type= h->sub_mb_type[i];
                    const int block_width= (sub_mb_type & (MB_TYPE_16x16|MB_TYPE_16x8)) ? 2 : 1;
                    //8x8塊的子塊(可能是8x8,8x4,4x8,4x4)的運動向量
                    //依次處理,數量為sub_partition_count
                    for(j=0; j<sub_partition_count[i]; j++){
                        int mx, my;
                        //scan8索引
                        const int index= 4*i + block_width*j;
                        int16_t (* mv_cache)[2]= &h->mv_cache[list][ scan8[index] ];
                        //先獲取“預測MV”(取中值),結果存入mx,my
                        pred_motion(h, index, block_width, list, h->ref_cache[list][ scan8[index] ], &mx, &my);
                        //獲取MVD並且累加至“預測MV”
                        //MV=預測MV+MVD
                        mx += get_se_golomb(&h->gb);
                        my += get_se_golomb(&h->gb);
                        tprintf(h->avctx, "final mv:%d %d\n", mx, my);

                        if(IS_SUB_8X8(sub_mb_type)){
                        	//8x8子巨集塊的巨集塊劃分方式為8x8(等同於沒劃分)
                        	//則把mv_cache中的4個塊對應的值都賦值成一樣的
                        	//即:[0],[1],[0+8],[1+8]
                        	//PS:stride(代表一行元素個數)為8(即“+8”代表是下一行)
                        	/*
							 * +----+----+
							 * |         |
							 * +    +    +
							 * |         |
							 * +----+----+
							 *
							 */
                            mv_cache[ 1 ][0]=
                            mv_cache[ 8 ][0]= mv_cache[ 9 ][0]= mx;
                            mv_cache[ 1 ][1]=
                            mv_cache[ 8 ][1]= mv_cache[ 9 ][1]= my;
                        }else if(IS_SUB_8X4(sub_mb_type)){
                        	//如果是8x4子巨集塊
							//則把mv_cache中的橫向的2個塊對應的值都賦值成一樣的
							//即:[0],[1]
                        	/*
							 * +----+----+
							 * |         |
							 * +----+----+
							 * |         |
							 * +----+----+
							 *
							 */
                            mv_cache[ 1 ][0]= mx;
                            mv_cache[ 1 ][1]= my;
                        }else if(IS_SUB_4X8(sub_mb_type)){
                        	//如果是4x8子巨集塊
							//則把mv_cache中縱向的2個塊對應的值都賦值成一樣的
							//即:[0],[0+8]
                        	/*
							 * +----+----+
							 * |    |    |
							 * +    +    +
							 * |    |    |
							 * +----+----+
							 *
							 */
                            mv_cache[ 8 ][0]= mx;
                            mv_cache[ 8 ][1]= my;
                        }
                        //賦值
                        //PS:如果是4x4子巨集塊劃分的話,則不會觸發上面的if else語句,即分別得到4個4x4塊的運動向量
                        mv_cache[ 0 ][0]= mx;
                        mv_cache[ 0 ][1]= my;

						/*
						 * mv_cache賦值方式如下
						 * scan8[0]代表了cache裡面亮度Y的起始點,取值12
						 * 如果全部都是4x4劃分的話,mv_cache填充順序即按照scan8中元素中的順序:
						 * scan8[0],scan8[1],scan8[2],scan8[3],scan8[4],scan8[5]......
						 * 即:
						 *	4 +  1 * 8, 5 +  1 * 8, 4 +  2 * 8, 5 +  2 * 8,
    					 *	6 +  1 * 8, 7 +  1 * 8, 6 +  2 * 8, 7 +  2 * 8,
    					 *	4 +  3 * 8, 5 +  3 * 8, 4 +  4 * 8, 5 +  4 * 8,......
						 * 用簡單圖形表示mv_cache如下。數字代表填充順序(一共填充16次)
						 *   |
						 * --+-------------------
						 *   | 0 0 0 0  0  0  0  0
						 *   | 0 0 0 0  1  2  5  6
						 *   | 0 0 0 0  3  4  7  8
						 *   | 0 0 0 0  9 10 13 14
						 *   | 0 0 0 0 11 12 15 16
						 *
						 *  如果全部是8x8劃分的話,mv_cache填充順序即按照scan8中元素中的順序:
						 *  scan8[0],scan8[4],scan8[8],scan8[16]......
						 *  填充後賦值3個元素
						 *  用簡單圖形表示mv_cache如下。數字代表填充順序(一共填充4次)
						 *   |
						 * --+-------------------
						 *   | 0 0 0 0  0  0  0  0
						 *   | 0 0 0 0  1  1  2  2
						 *   | 0 0 0 0  1  1  2  2
						 *   | 0 0 0 0  3  3  4  4
						 *   | 0 0 0 0  3  3  4  4
						 *
						 *	如果全部是8x4劃分的話,mv_cache填充順序即按照scan8中元素中的順序:
						 *  scan8[0],scan8[2],scan8[4],scan8[6]......
						 *  填充後賦值右邊1個元素
						 *  用簡單圖形表示mv_cache如下。數字代表填充順序(一共填充8次)
						 *   |
						 * --+-------------------
						 *   | 0 0 0 0  0  0  0  0
						 *   | 0 0 0 0  1  1  3  3
						 *   | 0 0 0 0  2  2  4  4
						 *   | 0 0 0 0  5  5  7  7
						 *   | 0 0 0 0  6  6  8  8
						 *
						 *  如果全部是4x8劃分的話,mv_cache填充順序即按照scan8中元素中的順序:
						 *  scan8[0],scan8[1],scan8[4],scan8[5],scan8[8],scan8[9]......
						 *  填充後賦值下邊1個元素
						 *  用簡單圖形表示mv_cache如下。數字代表填充順序(一共填充8次)
						 *   |
						 * --+-------------------
						 *   | 0 0 0 0  0  0  0  0
						 *   | 0 0 0 0  1  2  3  4
						 *   | 0 0 0 0  1  2  3  4
						 *   | 0 0 0 0  5  6  7  8
						 *   | 0 0 0 0  5  6  7  8
						 *
						 *   其他劃分的不同組合,可以參考上面的填充順序
						 */
                    }
                }else{
                    uint32_t *p= (uint32_t *)&h->mv_cache[list][ scan8[4*i] ][0];
                    p[0] = p[1]=
                    p[8] = p[9]= 0;
                }
            }
        }
    }else if(IS_DIRECT(mb_type)){
    	//Direct模式
        ff_h264_pred_direct_motion(h, &mb_type);
        dct8x8_allowed &= h->sps.direct_8x8_inference_flag;
    }else{
    	//情況3:既不是幀內巨集塊(情況1),巨集塊劃分數目也不為4(情況2)
    	//這種情況下不存在8x8的子巨集塊再次劃分這樣的事情
        int list, mx, my, i;
         //FIXME we should set ref_idx_l? to 0 if we use that later ...
        if(IS_16X16(mb_type)){
        	/*
        	 * 16x16 巨集塊
        	 *
        	 * +--------+--------+
        	 * |                 |
        	 * |                 |
        	 * |                 |
        	 * +        +        +
        	 * |                 |
        	 * |                 |
        	 * |                 |
        	 * +--------+--------+
        	 *
        	 */
        	//運動向量對應的參考幀
        	//L0和L1
            for(list=0; list<h->list_count; list++){
                    unsigned int val;
                    if(IS_DIR(mb_type, 0, list)){
                        if(local_ref_count[list]==1){
                            val= 0;
                        } else if(local_ref_count[list]==2){
                            val= get_bits1(&h->gb)^1;
                        }else{
                        	//參考幀影象序號
                            val= get_ue_golomb_31(&h->gb);
                            if (val >= local_ref_count[list]){
                                av_log(h->avctx, AV_LOG_ERROR, "ref %u overflow\n", val);
                                return -1;
                            }
                        }
                        //填充ref_cache
                        //fill_rectangle(資料起始點,寬,高,一行資料個數,資料值,每個資料佔用的byte)
                        //scan8[0]代表了cache裡面亮度Y的起始點
                        /*
                         * 在這裡相當於在ref_cache[list]填充了這樣的一份資料(val=v):
                         *   |
                         * --+--------------
                         *   | 0 0 0 0 0 0 0 0
                         *   | 0 0 0 0 v v v v
                         *   | 0 0 0 0 v v v v
                         *   | 0 0 0 0 v v v v
                         *   | 0 0 0 0 v v v v
                         */
                    fill_rectangle(&h->ref_cache[list][ scan8[0] ], 4, 4, 8, val, 1);
                    }
            }
            //運動向量
            for(list=0; list<h->list_count; list++){
                if(IS_DIR(mb_type, 0, list)){
                	//預測MV(取中值)
                    pred_motion(h, 0, 4, list, h->ref_cache[list][ scan8[0] ], &mx, &my);
                    //MVD從碼流中獲取
                    //MV=預測MV+MVD
                    mx += get_se_golomb(&h->gb);
                    my += get_se_golomb(&h->gb);
                    tprintf(h->avctx, "final mv:%d %d\n", mx, my);
                    //填充mv_cache
					//fill_rectangle(資料起始點,寬,高,一行資料個數,資料值,每個資料佔用的byte)
					//scan8[0]代表了cache裡面亮度Y的起始點
					/*
					 * 在這裡相當於在mv_cache[list]填充了這樣的一份資料(val=v):
					 *   |
					 * --+--------------
					 *   | 0 0 0 0 0 0 0 0
					 *   | 0 0 0 0 v v v v
					 *   | 0 0 0 0 v v v v
					 *   | 0 0 0 0 v v v v
					 *   | 0 0 0 0 v v v v
					 */
                    fill_rectangle(h->mv_cache[list][ scan8[0] ], 4, 4, 8, pack16to32(mx,my), 4);
                }
            }
        }
        else if(IS_16X8(mb_type)){ //16x8
        	/*
        	 * 16x8 巨集塊劃分
        	 *
        	 * +--------+--------+
        	 * |        |        |
        	 * |        |        |
        	 * |        |        |
        	 * +--------+--------+
        	 *
        	 */
        	//運動向量對應的參考幀
            for(list=0; list<h->list_count; list++){
            		//橫著的2個
                    for(i=0; i<2; i++){
                    	//儲存在val
                        unsigned int val;
                        if(IS_DIR(mb_type, i, list)){
                            if(local_ref_count[list] == 1) {
                                val= 0;
                            } else if(local_ref_count[list] == 2) {
                                val= get_bits1(&h->gb)^1;
                            }else{
                                val= get_ue_golomb_31(&h->gb);
                                if (val >= local_ref_count[list]){
                                    av_log(h->avctx, AV_LOG_ERROR, "ref %u overflow\n", val);
                                    return -1;
                                }
                            }
                        }else
                            val= LIST_NOT_USED&0xFF;
                        //填充ref_cache
						//fill_rectangle(資料起始點,寬,高,一行資料個數,資料值,每個資料佔用的byte)
						//scan8[0]代表了cache裡面亮度Y的起始點
						/*
						 * 在這裡相當於在ref_cache[list]填充了這樣的一份資料(第一次迴圈val=1,第二次迴圈val=2):
						 *   |
						 * --+--------------
						 *   | 0 0 0 0 0 0 0 0
						 *   | 0 0 0 0 1 1 1 1
						 *   | 0 0 0 0 1 1 1 1
						 *   | 0 0 0 0 2 2 2 2
						 *   | 0 0 0 0 2 2 2 2
						 */
                        fill_rectangle(&h->ref_cache[list][ scan8[0] + 16*i ], 4, 2, 8, val, 1);
                    }
            }
            //運動向量
            for(list=0; list<h->list_count; list++){
            	//2個
                for(i=0; i<2; i++){
                	//儲存在val
                    unsigned int val;
                    if(IS_DIR(mb_type, i, list)){
                    	//預測MV
                        pred_16x8_motion(h, 8*i, list, h->ref_cache[list][scan8[0] + 16*i], &mx, &my);
                        //MV=預測MV+MVD
                        mx += get_se_golomb(&h->gb);
                        my += get_se_golomb(&h->gb);
                        tprintf(h->avctx, "final mv:%d %d\n", mx, my);
                        //打包?
                        val= pack16to32(mx,my);
                    }else
                        val=0;
                    //填充mv_cache
					//fill_rectangle(資料起始點,寬,高,一行資料個數,資料值,每個資料佔用的byte)
					//scan8[0]代表了cache裡面亮度Y的起始點
					/*
					 * 在這裡相當於在ref_cache[list]填充了這樣的一份資料(第一次迴圈val=1,第二次迴圈val=2):
					 *   |
					 * --+--------------
					 *   | 0 0 0 0 0 0 0 0
					 *   | 0 0 0 0 1 1 1 1
					 *   | 0 0 0 0 1 1 1 1
					 *   | 0 0 0 0 2 2 2 2
					 *   | 0 0 0 0 2 2 2 2
					 */
                    fill_rectangle(h->mv_cache[list][ scan8[0] + 16*i ], 4, 2, 8, val, 4);
                }
            }
        }else{ //8x16?
        	/*
        	 * 8x16 巨集塊劃分
        	 *
        	 * +--------+
        	 * |        |
        	 * |        |
        	 * |        |
        	 * +--------+
        	 * |        |
        	 * |        |
        	 * |        |
        	 * +--------+
        	 *
        	 */
            av_assert2(IS_8X16(mb_type));
            for(list=0; list<h->list_count; list++){
            	//豎著的2個
                    for(i=0; i<2; i++){
                        unsigned int val;
                        if(IS_DIR(mb_type, i, list)){ //FIXME optimize
                            if(local_ref_count[list]==1){
                                val= 0;
                            } else if(local_ref_count[list]==2){
                                val= get_bits1(&h->gb)^1;
                            }else{
                                val= get_ue_golomb_31(&h->gb);
                                if (val >= local_ref_count[list]){
                                    av_log(h->avctx, AV_LOG_ERROR, "ref %u overflow\n", val);
                                    return -1;
                                }
                            }
                        }else
                            val= LIST_NOT_USED&0xFF;
                        //填充ref_cache
						//fill_rectangle(資料起始點,寬,高,一行資料個數,資料值,每個資料佔用的byte)
						//scan8[0]代表了cache裡面亮度Y的起始點
						/*
						 * 在這裡相當於在ref_cache[list]填充了這樣的一份資料(第一次迴圈val=1,第二次迴圈val=2):
						 *   |
						 * --+--------------
						 *   | 0 0 0 0 0 0 0 0
						 *   | 0 0 0 0 1 1 2 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
						 */
                        fill_rectangle(&h->ref_cache[list][ scan8[0] + 2*i ], 2, 4, 8, val, 1);
                    }
            }
            for(list=0; list<h->list_count; list++){
                for(i=0; i<2; i++){
                    unsigned int val;
                    if(IS_DIR(mb_type, i, list)){
                    	//預測MV
                        pred_8x16_motion(h, i*4, list, h->ref_cache[list][ scan8[0] + 2*i ], &mx, &my);
                        //MV=預測MV+MVD
                        mx += get_se_golomb(&h->gb);
                        my += get_se_golomb(&h->gb);
                        tprintf(h->avctx, "final mv:%d %d\n", mx, my);

                        val= pack16to32(mx,my);
                    }else
                        val=0;
                    //填充mv_cache
                    //fill_rectangle(資料起始點,寬,高,一行資料個數,資料值,每個資料佔用的byte)
					//scan8[0]代表了cache裡面亮度Y的起始點
					/*
					 * 在這裡相當於在mv_cache[list]填充了這樣的一份資料(第一次迴圈val=1,第二次迴圈val=2):
					 *   |
					 * --+--------------
					 *   | 0 0 0 0 0 0 0 0
					 *   | 0 0 0 0 1 1 2 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
					 */
                    fill_rectangle(h->mv_cache[list][ scan8[0] + 2*i ], 2, 4, 8, val, 4);
                }
            }
        }
    }
    //將巨集塊的Cache中的MV拷貝至整張圖片的motion_val變數中
    if(IS_INTER(mb_type))
        write_back_motion(h, mb_type);

    //Intra16x16的CBP位於mb_type中,其他型別的巨集塊的CBP需要單獨讀取
    if(!IS_INTRA16x16(mb_type)){
    	//獲取CBP
        cbp= get_ue_golomb(&h->gb);

        if(decode_chroma){
        	//YUV420,YUV422的情況
            if(cbp > 47){
                av_log(h->avctx, AV_LOG_ERROR, "cbp too large (%u) at %d %d\n", cbp, h->mb_x, h->mb_y);
                return -1;
            }
            //獲取CBP
            if(IS_INTRA4x4(mb_type)) cbp= golomb_to_intra4x4_cbp[cbp];
            else                     cbp= golomb_to_inter_cbp   [cbp];
        }else{
            if(cbp > 15){
                av_log(h->avctx, AV_LOG_ERROR, "cbp too large (%u) at %d %d\n", cbp, h->mb_x, h->mb_y);
                return -1;
            }
            if(IS_INTRA4x4(mb_type)) cbp= golomb_to_intra4x4_cbp_gray[cbp];
            else                     cbp= golomb_to_inter_cbp_gray[cbp];
        }
    } else {
        if (!decode_chroma && cbp>15) {
            av_log(h->avctx, AV_LOG_ERROR, "gray chroma\n");
            return AVERROR_INVALIDDATA;
        }
    }

    if(dct8x8_allowed && (cbp&15) && !IS_INTRA(mb_type)){
        mb_type |= MB_TYPE_8x8DCT*get_bits1(&h->gb);
    }
    //賦值CBP
    h->cbp=
    h->cbp_table[mb_xy]= cbp;
    //賦值mb_type
    h->cur_pic.mb_type[mb_xy] = mb_type;

    /*
     * 亮度cbp取值(只有低4位有意義):
     * 變數的最低位位元從最低位開始,每1位對應1個子巨集塊,該位等於1時表明對應子巨集塊殘差係數被傳送;
     * 該位等於0時表明對應子巨集塊殘差全部不被傳送
     * 色度cbp取值:
     * 0,代表所有殘差都不被傳送
     * 1,只傳送DC
     * 2,傳送DC+AC
     */

    //cbp不為0,才有殘差資訊
    if(cbp || IS_INTRA16x16(mb_type)){
        int i4x4, i8x8, chroma_idx;
        int dquant;
        int ret;
        GetBitContext *gb= IS_INTRA(mb_type) ? h->intra_gb_ptr : h->inter_gb_ptr;
        const uint8_t *scan, *scan8x8;
        const int max_qp = 51 + 6*(h->sps.bit_depth_luma-8);

        if(IS_INTERLACED(mb_type)){
            scan8x8= h->qscale ? h->field_scan8x8_cavlc : h->field_scan8x8_cavlc_q0;
            scan= h->qscale ? h->field_scan : h->field_scan_q0;
        }else{
            scan8x8= h->qscale ? h->zigzag_scan8x8_cavlc : h->zigzag_scan8x8_cavlc_q0;
            scan= h->qscale ? h->zigzag_scan : h->zigzag_scan_q0;
        }
        //QP量化引數的偏移值
        dquant= get_se_golomb(&h->gb);
        //由前一個巨集塊的量化引數累加得到本巨集塊的QP
        h->qscale += dquant;
        //注:slice中第1個巨集塊的計算方法(不存在前一個巨集塊了):
        //QP = 26 + pic_init_qp_minus26 + slice_qp_delta

        if(((unsigned)h->qscale) > max_qp){
            if(h->qscale<0) h->qscale+= max_qp+1;
            else            h->qscale-= max_qp+1;
            if(((unsigned)h->qscale) > max_qp){
                av_log(h->avctx, AV_LOG_ERROR, "dquant out of range (%d) at %d %d\n", dquant, h->mb_x, h->mb_y);
                return -1;
            }
        }

        h->chroma_qp[0]= get_chroma_qp(h, 0, h->qscale);
        h->chroma_qp[1]= get_chroma_qp(h, 1, h->qscale);
        //解碼殘差-亮度
        if( (ret = decode_luma_residual(h, gb, scan, scan8x8, pixel_shift, mb_type, cbp, 0)) < 0 ){
            return -1;
        }
        h->cbp_table[mb_xy] |= ret << 12;

        if (CHROMA444(h)) {
            //YUV444,把U,V都當成亮度處理
            if( decode_luma_residual(h, gb, scan, scan8x8, pixel_shift, mb_type, cbp, 1) < 0 ){
                return -1;
            }
            if( decode_luma_residual(h, gb, scan, scan8x8, pixel_shift, mb_type, cbp, 2) < 0 ){
                return -1;
            }
        } else {
        	//解碼殘差-色度
            const int num_c8x8 = h->sps.chroma_format_idc;
            //色度CBP位於高4位
            //0:不傳
            //1:只傳DC
            //2:DC+AC
            if(cbp&0x30){
            	//如果傳了的話
            	//就要解碼殘差資料
            	//2個分量
                for(chroma_idx=0; chroma_idx<2; chroma_idx++)
                    if (decode_residual(h, gb, h->mb + ((256 + 16*16*chroma_idx) << pixel_shift),
                                        CHROMA_DC_BLOCK_INDEX+chroma_idx,
                                        CHROMA422(h) ? chroma422_dc_scan : chroma_dc_scan,
                                        NULL, 4*num_c8x8) < 0) {
                        return -1;
                    }
            }
            //如果傳遞了AC係數
            if(cbp&0x20){
            	 //2個分量
                for(chroma_idx=0; chroma_idx<2; chroma_idx++){
                    const uint32_t *qmul = h->dequant4_coeff[chroma_idx+1+(IS_INTRA( mb_type ) ? 0:3)][h->chroma_qp[chroma_idx]];
                    int16_t *mb = h->mb + (16*(16 + 16*chroma_idx) << pixel_shift);
                    for (i8x8 = 0; i8x8<num_c8x8; i8x8++) {
                        for (i4x4 = 0; i4x4 < 4; i4x4++) {
                            const int index = 16 + 16*chroma_idx + 8*i8x8 + i4x4;
                            if (decode_residual(h, gb, mb, index, scan + 1, qmul, 15) < 0)
                                return -1;
                            mb += 16 << pixel_shift;
                        }
                    }
                }
            }else{
            	/*
				 * non_zero_count_cache:
				 * 每個4x4塊的非0係數個數的快取
				 *
				 * 在這裡把U,V都填充為0
				 * non_zero_count_cache[]內容如下所示
				 * 圖中v=0,上面的塊代表Y,中間的塊代表U,下面的塊代表V
				 *   |
				 * --+--------------
				 *   | 0 0 0 0 0 0 0 0
				 *   | 0 0 0 0 x x x x
				 *   | 0 0 0 0 x x x x
				 *   | 0 0 0 0 x x x x
				 *   | 0 0 0 0 x x x x
				 *   | 0 0 0 0 0 0 0 0
				 *   | 0 0 0 0 v v v v
				 *   | 0 0 0 0 v v v v
				 *   | 0 0 0 0 v v v v
				 *   | 0 0 0 0 v v v v
				 *   | 0 0 0 0 0 0 0 0
				 *   | 0 0 0 0 v v v v
				 *   | 0 0 0 0 v v v v
				 *   | 0 0 0 0 v v v v
				 *   | 0 0 0 0 v v v v
                 */
                fill_rectangle(&h->non_zero_count_cache[scan8[16]], 4, 4, 8, 0, 1);
                fill_rectangle(&h->non_zero_count_cache[scan8[32]], 4, 4, 8, 0, 1);
            }
        }
    }else{

    	/*
    	 * non_zero_count_cache:
    	 * 每個4x4塊的非0係數個數的快取
    	 *
    	 * cbp為0時,既不傳DC,也不傳AC,即全部賦值為0
    	 *
    	 * non_zero_count_cache[]內容如下所示
    	 * 圖中v=0,上面的塊代表Y,中間的塊代表U,下面的塊代表V
		 *   |
		 * --+--------------
		 *   | 0 0 0 0 0 0 0 0
		 *   | 0 0 0 0 v v v v
		 *   | 0 0 0 0 v v v v
		 *   | 0 0 0 0 v v v v
		 *   | 0 0 0 0 v v v v
		 *   | 0 0 0 0 0 0 0 0
		 *   | 0 0 0 0 v v v v
		 *   | 0 0 0 0 v v v v
		 *   | 0 0 0 0 v v v v
		 *   | 0 0 0 0 v v v v
		 *   | 0 0 0 0 0 0 0 0
		 *   | 0 0 0 0 v v v v
		 *   | 0 0 0 0 v v v v
		 *   | 0 0 0 0 v v v v
		 *   | 0 0 0 0 v v v v
    	 *
    	 */
        fill_rectangle(&h->non_zero_count_cache[scan8[ 0]], 4, 4, 8, 0, 1);
        fill_rectangle(&h->non_zero_count_cache[scan8[16]], 4, 4, 8, 0, 1);
        fill_rectangle(&h->non_zero_count_cache[scan8[32]], 4, 4, 8, 0, 1);
    }
    //賦值QP
    h->cur_pic.qscale_table[mb_xy] = h->qscale;

    //將巨集塊的non_zero_count_cache拷貝至整張圖片的non_zero_count變數中
    write_back_non_zero_count(h);

    return 0;
}

ff_h264_decode_mb_cavlc()的定義有將近1000行程式碼,算是一個比較複雜的函數了。我在其中寫了不少註釋,因此不再對原始碼進行詳細的分析。下面先簡單梳理一下它的流程:

(1)解析Skip型別巨集塊

(2)獲取mb_type

(3)填充當前巨集塊左邊和上邊巨集塊的資訊(後面的預測中會用到)

(4)根據mb_type的不同,分成三種情況進行預測工作:

a)巨集塊是幀內預測

i.如果巨集塊是Intra4x4型別,則需要單獨解析幀內預測模式。

ii.如果巨集塊是Intra16x16型別,則不再做過多處理。

b)巨集塊劃分為4個塊(此時每個8x8的塊可以再次劃分為4種類型)

這個時候每個8x8的塊可以再次劃分為8x8、8x4、4x8、4x4幾種子塊。需要分別處理這些小的子塊:

i.解析子塊的參考幀序號

ii.解析子塊的運動向量

c)其它型別(包括16x16,16x8,8x16幾種劃分,這些劃分不可再次劃分)

這個時候需要判斷巨集塊的型別為16x16,16x8還是8x16,然後作如下處理:

i.解析子巨集塊的參考幀序號

ii.解析子巨集塊的運動向量

(5)解碼殘差資訊

(6)將巨集塊的各種資訊輸出到整個圖片相應的變數中


下面簡單總結一下ff_h264_decode_mb_cavlc()中涉及到的一些知識點。

mb_type

mb_type是巨集塊的型別的索引。FFmpeg H.264解碼器中使用i_mb_type_info[]儲存了I巨集塊的型別資訊;使用p_mb_type_info[]儲存了P巨集塊的型別資訊;使用b_mb_type_info[]儲存了B巨集塊的型別資訊。使用“X_mb_type_info[mb_type]”的方式(“X”可以取“i”、“p”、“b”)可以獲得該型別巨集塊的資訊。例如獲得B巨集塊的分塊數可以使用下面這句程式碼。
int partition_count= b_mb_type_info[mb_type].partition_count;
下面看一下這幾個陣列的定義。

i_mb_type_info[]

i_mb_type_info[]儲存了I巨集塊的型別。其中的元素為IMbInfo型別的結構體。IMbInfo型別結構體的定義如下所示。
typedef struct IMbInfo {
    uint16_t type;
    uint8_t pred_mode;//幀內預測模式
    uint8_t cbp;// Coded Block Pattern,高4位為色度,低4位為亮度
} IMbInfo;
i_mb_type_info[]的定義如下。
//I巨集塊的mb_type
/*
 * 規律:
 * pred_mode總是Vertical->Horizontal->DC->Plane(記住幀內預測中Vertical排在第0個)
 * cbp:傳送資料量越來越大(前半部分不傳亮度殘差)
 * 按照資料量排序
 *
 * 只有Intra_16x16巨集塊型別,CBP的值不是由句法元素給出,而是通過mb_type得到。
 *
 * CBP(Coded Block Pattern)
 * 色度CBP含義:
 * 0:不傳殘差
 * 1:只傳DC
 * 2:傳送DC+AC
 * 亮度CBP(只有最低4位有定義)含義:
 * 變數的最低位位元從最低位開始,每一位對應一個子巨集塊,該位等於1時表明對應子巨集塊殘差係數被傳送;該位等於0
 * 時表明對應子巨集塊殘差全部不被傳送,解碼器把這些殘差係數賦為0。
 */

static const IMbInfo i_mb_type_info[26] = {
    { MB_TYPE_INTRA4x4,  -1,  -1 },//pred_mode還需要單獨獲取
    { MB_TYPE_INTRA16x16, 2,   0 },//cbp:0000+0
    { MB_TYPE_INTRA16x16, 1,   0 },
    { MB_TYPE_INTRA16x16, 0,   0 },
    { MB_TYPE_INTRA16x16, 3,   0 },
    { MB_TYPE_INTRA16x16, 2,  16 },//cbp:0000+1<<4
    { MB_TYPE_INTRA16x16, 1,  16 },
    { MB_TYPE_INTRA16x16, 0,  16 },
    { MB_TYPE_INTRA16x16, 3,  16 },
    { MB_TYPE_INTRA16x16, 2,  32 },//cbp:0000+2<<4
    { MB_TYPE_INTRA16x16, 1,  32 },
    { MB_TYPE_INTRA16x16, 0,  32 },
    { MB_TYPE_INTRA16x16, 3,  32 },
    { MB_TYPE_INTRA16x16, 2,  15 +  0 },//cbp:1111+0<<4
    { MB_TYPE_INTRA16x16, 1,  15 +  0 },
    { MB_TYPE_INTRA16x16, 0,  15 +  0 },
    { MB_TYPE_INTRA16x16, 3,  15 +  0 },
    { MB_TYPE_INTRA16x16, 2,  15 + 16 },//cbp:1111+1<<4
    { MB_TYPE_INTRA16x16, 1,  15 + 16 },
    { MB_TYPE_INTRA16x16, 0,  15 + 16 },
    { MB_TYPE_INTRA16x16, 3,  15 + 16 },
    { MB_TYPE_INTRA16x16, 2,  15 + 32 },//cbp:1111+2<<4
    { MB_TYPE_INTRA16x16, 1,  15 + 32 },
    { MB_TYPE_INTRA16x16, 0,  15 + 32 },
    { MB_TYPE_INTRA16x16, 3,  15 + 32 },
    { MB_TYPE_INTRA_PCM,  -1, -1 },//特殊
};

p_mb_type_info[]

p_mb_type_info[]儲存了P巨集塊的型別。其中的元素為PMbInfo型別的結構體。PMbInfo型別結構體的定義如下所示。
typedef struct PMbInfo {
    uint16_t type;//巨集塊型別
    uint8_t partition_count;//分塊數量
} PMbInfo;
p_mb_type_info[]的定義如下。
//P巨集塊的mb_type
/*
 * 規律:
 * 巨集塊劃分尺寸從大到小(子巨集塊數量逐漸增多)
 * 先是“胖”(16x8)的,再是“瘦”(8x16)的
 * MB_TYPE_PXL0中的“X”代表巨集塊的第幾個分割槽,只能取0或者1
 * MB_TYPE_P0LX中的“X”代表巨集塊參考的哪個List。P巨集塊只能參考list0
 *
 */
static const PMbInfo p_mb_type_info[5] = {
    { MB_TYPE_16x16 | MB_TYPE_P0L0,                               1 },//沒有“P1”
    { MB_TYPE_16x8  | MB_TYPE_P0L0 | MB_TYPE_P1L0,                2 },
    { MB_TYPE_8x16  | MB_TYPE_P0L0 | MB_TYPE_P1L0,                2 },
    { MB_TYPE_8x8   | MB_TYPE_P0L0 | MB_TYPE_P1L0,                4 },
    { MB_TYPE_8x8   | MB_TYPE_P0L0 | MB_TYPE_P1L0 | MB_TYPE_REF0, 4 },
};

b_mb_type_info[]

b_mb_type_info[]儲存了B巨集塊的型別。其中的元素為PMbInfo型別的結構體。在這裡需要注意,p_mb_type_info[]和b_mb_type_info[]中的元素的型別是一樣的,都是PMbInfo型別的結構體。
b_mb_type_info[]的定義如下。
//B巨集塊的mb_type
/*
 * 規律:
 * 巨集塊劃分尺寸從大到小(子巨集塊數量逐漸增多)
 * 先是“胖”(16x8)的,再是“瘦”(8x16)的
 * 每個分割槽參考的list越來越多(意見越來越不一致了)
 *
 * MB_TYPE_PXL0中的“X”代表巨集塊的第幾個分割槽,只能取0或者1
 * MB_TYPE_P0LX中的“X”代表巨集塊參考的哪個List。B巨集塊參考list0和list1
 *
 */
static const PMbInfo b_mb_type_info[23] = {
    { MB_TYPE_DIRECT2 | MB_TYPE_L0L1,                                              1, },
    { MB_TYPE_16x16   | MB_TYPE_P0L0,                                              1, },//沒有“P1”
    { MB_TYPE_16x16   | MB_TYPE_P0L1,                                              1, },
    { MB_TYPE_16x16   | MB_TYPE_P0L0 | MB_TYPE_P0L1,                               1, },
    { MB_TYPE_16x8    | MB_TYPE_P0L0 | MB_TYPE_P1L0,                               2, },//兩個分割槽(每個分割槽兩個參考幀)都參考list0
    { MB_TYPE_8x16    | MB_TYPE_P0L0 | MB_TYPE_P1L0,                               2, },
    { MB_TYPE_16x8    | MB_TYPE_P0L1 | MB_TYPE_P1L1,                               2, },//兩個分割槽(每個分割槽兩個參考幀)都參考list1
    { MB_TYPE_8x16    | MB_TYPE_P0L1 | MB_TYPE_P1L1,                               2, },
    { MB_TYPE_16x8    | MB_TYPE_P0L0 | MB_TYPE_P1L1,                               2, },//0分割槽(兩個參考幀)參考list0,1分割槽(兩個參考幀)參考list1
    { MB_TYPE_8x16    | MB_TYPE_P0L0 | MB_TYPE_P1L1,                               2, },
    { MB_TYPE_16x8    | MB_TYPE_P0L1 | MB_TYPE_P1L0,                               2, },
    { MB_TYPE_8x16    | MB_TYPE_P0L1 | MB_TYPE_P1L0,                               2, },
    { MB_TYPE_16x8    | MB_TYPE_P0L0 | MB_TYPE_P1L0 | MB_TYPE_P1L1,                2, },
    { MB_TYPE_8x16    | MB_TYPE_P0L0 | MB_TYPE_P1L0 | MB_TYPE_P1L1,                2, },
    { MB_TYPE_16x8    | MB_TYPE_P0L1 | MB_TYPE_P1L0 | MB_TYPE_P1L1,                2, },
    { MB_TYPE_8x16    | MB_TYPE_P0L1 | MB_TYPE_P1L0 | MB_TYPE_P1L1,                2, },
    { MB_TYPE_16x8    | MB_TYPE_P0L0 | MB_TYPE_P0L1 | MB_TYPE_P1L0,                2, },
    { MB_TYPE_8x16    | MB_TYPE_P0L0 | MB_TYPE_P0L1 | MB_TYPE_P1L0,                2, },
    { MB_TYPE_16x8    | MB_TYPE_P0L0 | MB_TYPE_P0L1 | MB_TYPE_P1L1,                2, },
    { MB_TYPE_8x16    | MB_TYPE_P0L0 | MB_TYPE_P0L1 | MB_TYPE_P1L1,                2, },
    { MB_TYPE_16x8    | MB_TYPE_P0L0 | MB_TYPE_P0L1 | MB_TYPE_P1L0 | MB_TYPE_P1L1, 2, },
    { MB_TYPE_8x16    | MB_TYPE_P0L0 | MB_TYPE_P0L1 | MB_TYPE_P1L0 | MB_TYPE_P1L1, 2, },
    { MB_TYPE_8x8     | MB_TYPE_P0L0 | MB_TYPE_P0L1 | MB_TYPE_P1L0 | MB_TYPE_P1L1, 4, },
};


填充當前巨集塊左邊和上邊巨集塊的資訊

在巨集塊預測的時候需要用到當前巨集塊左邊、上左、上邊,上右位置的巨集塊有關的資訊。因此在預測前需要先填充這些資訊。H.264解碼器中呼叫了fill_decode_neighbors()和fill_decode_caches()兩個函式填充這些資訊。fill_decode_caches()函式我目前還沒有仔細看,在這裡簡單分析一下fill_decode_neighbors()函式

fill_decode_neighbors()

fill_decode_neighbors()用於設定當前巨集塊左邊、上左、上邊,上右位置的巨集塊的索引值和巨集塊型別,定義位於libavcodec\h264_mvpred.h,如下所示。
/* 設定上左,上,上右,左巨集塊的索引值和巨集塊型別
 * 這4個巨集塊在解碼過程中會用到
 * 位置如下圖所示
 *
 * +----+----+----+
 * | UL |  U | UR |
 * +----+----+----+
 * | L  |    |
 * +----+----+
 */
static void fill_decode_neighbors(H264Context *h, int mb_type)
{
    const int mb_xy = h->mb_xy;
    int topleft_xy, top_xy, topright_xy, left_xy[LEFT_MBS];
    static const uint8_t left_block_options[4][32] = {
        { 0, 1, 2, 3, 7, 10, 8, 11, 3 + 0 * 4, 3 + 1 * 4, 3 + 2 * 4, 3 + 3 * 4, 1 + 4 * 4, 1 + 8 * 4, 1 + 5 * 4, 1 + 9 * 4 },
        { 2, 2, 3, 3, 8, 11, 8, 11, 3 + 2 * 4, 3 + 2 * 4, 3 + 3 * 4, 3 + 3 * 4, 1 + 5 * 4, 1 + 9 * 4, 1 + 5 * 4, 1 + 9 * 4 },
        { 0, 0, 1, 1, 7, 10, 7, 10, 3 + 0 * 4, 3 + 0 * 4, 3 + 1 * 4, 3 + 1 * 4, 1 + 4 * 4, 1 + 8 * 4, 1 + 4 * 4, 1 + 8 * 4 },
        { 0, 2, 0, 2, 7, 10, 7, 10, 3 + 0 * 4, 3 + 2 * 4, 3 + 0 * 4, 3 + 2 * 4, 1 + 4 * 4, 1 + 8 * 4, 1 + 4 * 4, 1 + 8 * 4 }
    };

    h->topleft_partition = -1;
    //上方巨集塊。當前巨集塊減去一行
    //top_xy=mb_xy-mb_stride
    top_xy = mb_xy - (h->mb_stride << MB_FIELD(h));

    /* Wow, what a mess, why didn't they simplify the interlacing & intra
     * stuff, I can't imagine that these complex rules are worth it. */
    //上左巨集塊。上方巨集塊減1
    topleft_xy    = top_xy - 1;
    //上右巨集塊。上方巨集塊加1
    topright_xy   = top_xy + 1;
    //左邊巨集塊。當前巨集塊減1
    left_xy[LBOT] = left_xy[LTOP] = mb_xy - 1;
    h->left_block = left_block_options[0];