x264閱讀記錄-2

x264閱讀記錄-2 算法

7. x264_encoder_encode函數-1數組

查看該函數代碼(Encoder.c文件)能夠發現,該函數中註釋很詳細,對編碼的整個步驟展現的也相對比較清晰。數據結構

在查看具體的代碼以前,咱們須要瞭解牽扯到x264幀管理過程當中的三個數組:多線程

  • x264_frame_t *current[X264_BFRAME_MAX*4+3];/*已肯定幀類型,待編碼幀,每個GOP在編碼前,每一幀的類型在編碼前已經肯定。當進行編碼時,從這裏取出一幀數據。*/  
  •         x264_frame_t *next[X264_BFRAME_MAX*4+3];//還沒有肯定幀類型的待編碼幀,當肯定後,會將此數組中的幀轉移到current數組中去。  
  •         x264_frame_t *unused[X264_BFRAME_MAX*4 + X264_THREAD_MAX*2 + 16+4];/*這個數組用於回收那些在編碼中分配的frame空間,當有新的須要時,直接拿過來用,不用從新分配新的空間,提升效率*/  

A. Setup new frame from picture 根據picture中的數據創建frame數據結構less

這兒要區分輸入數據是否爲NULL,若是爲NULL就說明是要從緩衝中獲取B幀來進行編碼,不然直接根據輸入數據創建當前要編碼的幀結構:ide

步驟1(這個步驟是在一個if判斷語句以內進行操做的,判斷條件就是傳入的數據是否爲NULL,聯繫前面對代碼的閱讀能夠知道,若是是進行B幀的編碼的話,參數就是NULL,這時是不須要取picture中的數據來創建frame數據結構的,由於在next列表中是存在有幀的,這時只須要從next列表中取就能夠了)    函數

/* 1: Copy the picture to a frame and move it to a buffer */優化

首先是ui

           /* Get the picture(fenc) from the start of List(h->frames.unused) */編碼

        x264_frame_t *fenc = x264_frame_get ( h->frames .unused );

x264_frame_get 函數的代碼能夠看到是直接從h ->frames .unused這個列表中直接把第一個元素去了出來,而後後面的元素依次前移。

其次是

           /* Cpy data from pic_in to fenc for further processing */

        x264_frame_copy_picture( h , fenc , pic_in );

查看x264_frame_copy_picture 代碼能夠知道,根據pic_in中圖像的顏色空間狀況進行了數據的複製。

 

i_frame是用來標示圖像的原始順序(播放順序)的:

 fenc-> i_frame = h ->frames. i_input++; //frame count, i_frame is in presentation(raw yuv input) order.

 

接下來:

     /* Put the picture(fenc) at the end of Next List(h->frames.next) */

        x264_frame_put( h ->frames. next, fenc );

函數x264_frame_put的代碼告訴咱們,咱們是將當幀fenc添加到了frames.next列表中。

 

current數組中不存在幀的時候,就須要填充這個數組:

步驟2

 /* 2: Select frame types */

        if( h-> frames.next [0] == NULL )

            return 0;          

/* Decide the Slice(Frame) type for Slices in next list, Return Once we found a Non-B frame*/

        x264_slicetype_decide( h );

步驟3

 /* 3: move some B-frames and 1 non-B to encode queue(current list) from next queue */

 

首先是找出第一個非B幀,同時統計出B幀以前的非B幀的數目。

        while( IS_X264_TYPE_B ( h->frames .next [bframes ]->i_type ) )

            bframes++;

而後是:

           /* Move 1 non-B frame into current List*/

   x264_frame_put( h ->frames. current, x264_frame_get( &h ->frames. next[ bframes] ) );

.....

接下來是跟前面統計的B幀的數目bframes,從next的列表的開頭(由於x264_frame_get函數就是從列表開頭開始操做)依次取出相應的B幀放入到current列表最後(由於x264_frame_put函數就是不斷的將參數中給出的幀添加到隊尾的)

 這主要是由於B幀必須等後面的非B幀編碼結束後才能編碼,因此把暫時不編的一系列B幀存入隊列中,一直到非B幀才取出進行編碼,以後再進行前面的B幀編碼

       /* Move the consequent B frames to current list from next list*/

   while( bframes -- )

       x264_frame_put( h ->frames. current, x264_frame_get( h ->frames. next ) );

 

下面對A步驟進行一下總結:

這個步驟其實就是一個準備工做,就是爲了下一步進行幀編碼的時候能夠取到正確的幀,爲此主要是在3個和幀管理相關的列表之間轉移幀。

 

B. 選取幀進行編碼    

/* ------------------- Get frame to be encoded ------------------------- */

步驟4current列表中取出第一幀進行編碼

    /* 4: get picture to encode(The first frame in current list) */

    h-> fenc = x264_frame_get( h ->frames. current );

    if( h-> fenc == NULL )

    {

        /* Nothing yet to encode (ex: waiting for I/P with B frames) ??*/

        /* waiting for filling bframe buffer */

        pic_out->i_type = X264_TYPE_AUTO ; //??

        return 0;

    }

 

 

C.創建幀上下文

/* ------------------- Setup frame context ----------------------------- */

步驟5    /* 5: Init data dependant of frame type */

根據幀的類型來進行變量賦值,主要是這3個變量:i_nal_type i_nal_ref_idci_slice_type  

須要注意的是:對於IDR幀,須要調用函數x264_reference_reset來重置參考幀列表。(IDR幀是兩個GOP之間的邊界)

i_poc是幀在GOP中的順序,i_frame是幀的播放順序。

 

D. 初始化

/* ------------------- Init                ----------------------------- */

首先,爲當前幀創建參考幀列表

    /* build ref list 0/1 for current encoding frame.*/

    x264_reference_build_list( h, h-> fdec-> i_poc, i_slice_type );

其次,初始化碼率控制相關

    /* Init the rate control */

    x264_ratecontrol_start( h, i_slice_type, h->fenc ->i_qpplus1 );

      // Get the qp for macroblock.

    i_global_qp = x264_ratecontrol_qp( h );

 

接下來,若是是B幀,須要爲雙向參考作好準備:

   if( i_slice_type == SLICE_TYPE_B )

        x264_macroblock_bipred_init( h );

E. 建立Slice

    /* ------------- Create(Initialize) slice header  ----------------------- */

    x264_slice_init( h, i_nal_type, i_slice_type , i_global_qp );

F. 寫比特流

    /* ---------------------- Write the bitstream -------------------------- */

首先是:初始化比特流上下文

    /* Init bitstream context */

    h-> out. i_nal = 0;

    bs_init( & h-> out. bs, h-> out. p_bitstream, h->out .i_bitstream );

注意:access unit delimiters NALU的一種類型,用於分割不一樣的訪問單元。

而後是:寫SPSPPS

    /* Write SPS and PPS */

因爲SPSPPS是一組圖像共同的信息,只有當本幀圖像是IDR是才須要寫SPSPPS信息。

         /* generate sequence parameters */

        x264_nal_start( h , NAL_SPS , NAL_PRIORITY_HIGHEST );

        x264_sps_write( &h ->out. bs, h-> sps );

        x264_nal_end( h );

 

        /* generate picture parameters */

        x264_nal_start( h , NAL_PPS , NAL_PRIORITY_HIGHEST );

        x264_pps_write( &h ->out. bs, h-> pps );

        x264_nal_end( h );

x264_nal_start x264_nal_end 兩個函數完成對NALU的碼流寫入。x264_sps_write x264_pps_write 函數就是根據H.264規定的語法結構進行相應信息的寫入。

 

接下來是:對幀的數據進行編碼,並寫碼流

    /* Write frame ,The Key function*/

    i_frame_size = x264_slices_write( h );

x264_slices_write 是進行具體編碼的核心函數。

 

注意:這個地方不明白爲何要執行下列操做?

    /* restore CPU state (before using float again ??) */

    x264_cpu_restore( h-> param.cpu );

以後可能還要有一些操做:若是P幀編碼出錯,就從新編碼成I幀。設置碼流特徵信息,設置碼流輸出。碼流控制更新(x264_ratecontrol_end),更新參考幀(x264_reference_update),重置緩衝區(x264_frame_put),計算編碼狀態信息。

 

最後是:結束碼流

 /* End bitstream, set output  */

    *pi_nal = h-> out. i_nal;

    *pp_nal = h-> out. nal;

G. 在編碼完當前幀以後,更新編碼器的相關狀態     

/* ---------------------- Update encoder state ------------------------- */

 

首先:更新碼流控制相關

  /* update rc */

    x264_cpu_restore( h-> param.cpu );

    x264_ratecontrol_end( h, i_frame_size * 8 );

而後:更新參考幀列表(同時回收了當前幀所使用的空間)

    /* handle references , Update references after Encoing One frame.*/

    if( i_nal_ref_idc != NAL_PRIORITY_DISPOSABLE )//

        x264_reference_update( h );

      // Put back.

    x264_frame_put( h-> frames.unused , h ->fenc );

 

H. 計算相關的統計數據

    /* ---------------------- Compute/Print statistics --------------------- */

包括各種型幀的數目,PSNR 等。

 

 

8. x264_encoder_encode函數-2

給出該函數的調用圖:

對於其中的一些函數,已經進行了簡單分析,就再也不分析了,主要分析一些重要的函數。

1x264_slicetype_decide

用於判斷next list中的slice的類型。

這個函數中涉及到x264在何種狀況下怎樣決定幀的類型,同時對GOP的大小進行了限定。

2x264_ratecontrol_startx264_ratecontrol_qp

和碼率控制相關。

3x264_slice_init

 

主要包括頭信息的初始化和宏塊信息初始化。

4x264_sps_writex264_pps_write

從調用函數能夠看到,主要是調用了和寫碼流相關的函數:bs_write, bs_write_ue, bs_write_se, bs_write1,

這些函數都是將具體的信息採用熵編碼的方式轉換爲碼流。

5 x264_slices_write

這個是核心函數。

這個函數是根據是否採用多線程而造成了兩個分支

 

9. x264_slice_write函數

1)首先是對存儲幀統計數據的結構體進行初始化

    /* init stats */

    memset( & h-> stat. frame, 0, sizeof (h ->stat. frame) );

2因爲本函數是對slice進行編碼,因此先調用x264_nal_start來將slice相關的一些信息寫入到NALU中。

3slice的頭信息寫入到NALU中:x264_slice_header_write

主要是調用相關的碼流寫入函數。

4若是採用了CABAC編碼的話,須要進行相關初始化。

主要調用x264_cabac_context_initx264_cabac_encode_init 

5for循環:這是對slice中的每個宏塊進行編碼。

bs_pos函數用於輸出比特流的當前當前讀寫位置。

x264_macroblock_cache_load函數是將已編碼數據參數和待編碼數據裝入到h->mb.cache中。

x264_macroblock_analyse函數完成模式選擇:* Slice I: choose I_4x4 or I_16x16 mode

         * Slice P: choose between using P mode or intra (4x4 or 16x16)

x264_macroblock_encode函數是根據選定的模式進行編碼。

宏塊編碼完成以後,根據所採用的熵編碼方式來進行碼流的寫入:x264_macroblock_write_cabacx264_macroblock_write_cavlc

 

 x264_macroblock_cache_save函數保存已編碼的信息,爲了下一次的編碼作參考。

 

而後統計編碼狀態數據,若是使用了動態qp, x264_ratecontrol_mb進行qp調整。

 

6bs_rbsp_trailing,寫入編碼結尾數據。

7x264_nal_end結束NALU

 

10. x264_macroblock_cache_loadx264_macroblock_cache_save函數

x264_macroblock_cache_load將參考幀中某位置的(重建後)數據保存進cache,供參考和反覆使用。

x264_macroblock_cache_save在分析和編碼後將當前塊寫進cache

這兩個函數都是在操做h->mb.cache在而h->mb.cache的設計是很是巧妙的,這裏還要牽扯到數組x264_scan8用來索引cache

 

11. x264_macroblock_analyse函數-1

這個函數是進行H.264中主要的一個工做:模式選擇,因此調用的函數也不少。這兒主要分析其流程。

1)調用x264_macroblock_init函數

 主要是初始化變量,如幀間、幀內的satd值、初始化MV的範圍等一些參數。

 

2)針對不一樣類型的幀進行分析。

A. 對於I,調用x264_mb_analyse_intra進行幀內預測。在這個函數中,分別對16x16, 4x4 8x8進行模式預測,計算SATD。而後進行比較16x16,8x8,4x4cost,得到幀內編碼的宏塊的模式。

注意到最後的模式是使用h->mb .i_type來記錄的,而且只是宏塊的模式,對於具體的模式是記錄在x264_mb_analysis_t結構體的元素i_predict16x16i_predict8x8[2][2]i_predict4x4[4][4]中。

 

B. 對於P,首先判斷是不是skip模式,只有在左,上,左上,右上有一個是skip模式的時候,這時mb纔多是skip模式。而後調用函數x264_macroblock_probe_pskip來判斷當前宏塊是否能夠採用skip模式進行編碼。

當不採用p_skip模式時,就須要從幀間模式中選擇合適的模式來編碼。

首先是調用函數x264_mb_analyse_load_costs來對全部可能的MV來提早計算costlambda*nbits.

 

這兒代碼中的註釋好像有些問題,對我有些誤導。我仔細看了這兒的代碼以後,具體理解是:

對於16x16,16x8,8x16,8x8,8x4,4x8,4x4,這些塊的劃分方式,代碼中是先對16x16進行分析,調用函數x264_mb_analyse_inter_p16x16,而後能夠計算獲得相應的運動矢量和l0.me16x16 .cost(其中還要考慮到參考幀部分,運動估計)。

 

在開啓對宏塊進行子劃分的狀況下,先對8x8模式進行分析,調用的是函數x264_mb_analyse_inter_p8x8x264_mb_analyse_inter_p8x8_mixed_ref(主要看是否容許子塊採用不一樣的參考幀),對48x8塊進行分析,包括運動估計,參考幀選擇等,最後獲得運動矢量、參考幀和l0.i_cost8x8l0.me8x8[i].cost

 

接下來比較一下16x168x8下的cost,若是8x8l0.i_cost8x8較小,就須要進行子劃分8x4,4x84x4的分析。這樣,對於48x8塊,在for循環中進行依次分析。也是首先調用x264_mb_analyse_inter_p4x4函數對8x8塊中的44x4塊進行分析(運動估計,參考幀選擇),最後計算獲得i_cost4x4[i8x8],將這個i_cost4x4[i8x8]l0.me8x8[i].cost比較,看是否要進行8x4,4x8的劃分,對應的調用函數x264_mb_analyse_inter_p8x4x264_mb_analyse_inter_p4x8,獲得相應的l0.i_cost8x4 [i ]l0.i_cost4x8 [i ],並通過和i_cost4x4[i]進行比較,選出最優的子劃分保存在h->mb .i_sub_partition [i]中。通過這一系列操做,8x8模式就分析完了。

 

再接着分析16x88x16兩種模式,分別調用函數x264_mb_analyse_inter_p16x8x264_mb_analyse_inter_p8x16,計算獲得l0.i_cost16x8l0.i_cost8x16,並通過比較獲得最優劃分保存在mb.i_partition中。這樣就完成了模式的選擇。

 

完成模式選擇以後,爲了提升運動估計的精確度,對最優模式劃分進行亞像素運動搜索,調用函數x264_me_refine_qpel

 

因爲在P幀中一樣能夠採用幀內預測,因此接着調用函數x264_mb_analyse_intra進行幀內模式選擇,還有可能調用x264_mb_analyse_intra_chroma對色度進行模式選擇。

 

最終通過一系列的比較,獲得最優的模式保存在h->mb .i_type中。

 

C. 對於B,因爲B幀能夠雙向參考,因此比較複雜。

首先調用x264_mb_predict_mv_direct16x16判斷是否採用B_Direct模式,若是採用直接模式,則分別進行空間和時間的直接預測。這裏B幀雙向參考,前向和後向的參考和p幀的方向是相似的。

也調用x264_mb_analyse_load_costs函數來進行運動估計cost方面的初始化。

接着調用函數x264_mb_analyse_inter_b16x16來對16x16劃分方式分析,其中對L0L1兩個參考列表中的幀都進行了運動搜索。

接下來對8x8方式進行分析,調用的是函數x264_mb_analyse_inter_b8x8,對48x8塊進行了操做,獲得i_cost8x8bi。並將i_cost8x8bi16x16狀況進行比較,若是i_cost8x8bi較小的話,須要根據48x8塊在x264_mb_analyse_inter_b8x8函數中決定的子劃分方式mb.i_sub_partition [i]來決定是採用8x16仍是16x8,調用相應的函數x264_mb_analyse_inter_b16x8x264_mb_analyse_inter_b8x16,計算獲得相應的cost以後,就能比較獲得最優的劃分方式,保存在  h-> mb. i_partition中。

從上面能夠看到,在x264B幀中宏塊不支持8x8如下的劃分的。

這樣完成模式選擇以後,一樣對最優的劃分模式進行亞像素的運動搜索,與P幀狀況相似。仍是調用函數x264_me_refine_qpel,只是調用的次數更多。

 

一樣採用了函數x264_mb_analyse_intra進行了幀內模式選擇,最後從幀內和幀間中選出最佳的模式,保存在h->mb .i_type中。

 

3根據上面的分析更新宏塊的信息,在函數x264_analyse_update_cache中完成。

 

12.x264_macroblock_analyse函數-2

上圖就展現了x264_macroblock_analyse函數中調用函數。

1x264_mb_analyse_init函數

 x264_mb_analyse_init()函數的功能包括:初始化碼率控制的模型參數(碼率控制依然基於Lagrangian率失真優化算法,因此初始化lambda係數),把各宏塊分類的Cost設爲COST_MAX,計算MV範圍,快速決定Intra宏塊。

 

2)x264_mb_analyse_intra 函數

這個函數進行幀內的模式選擇,主要包括對16x16,4x48x8三種大小遍歷全部模式並比較其cost

首先,對於16x16模式,調用函數predict_16x16_mode_available來判斷VHDCPlane四種模式中有哪些是可用的(由於對於不一樣位置的宏塊,其左、上,左上和右上位置的宏塊不必定都是存在的).結果保存在數組predict_mode中(記錄了哪些模式可用),而後在for循環中對可行的模式進行預測計算,在循環中選出最好的一種模式。

A. h->predict_16x16 是函數指針數組,是經過x264_predict_16x16_init( h->param.cpu, h->predict_16x16 );來完成賦值的,具體代碼在x264_encoder_open函數中。經過x264_predict_16x16_init的代碼能夠看到,h->predict_16x16 中就是Intra16x16所對應的4種預測方式(可是考慮到邊界的狀況,x264中是有7種方式),因此,代碼:

h->predict_16x16 [i_mode ]( p_dst );

就完成了相應i_mode模式的預測計算,p_dst指向的就是相應的預測值。

 

B. h->pixf.mbcmp是函數指針數組,而這兒的h->pixf.mbcmp[PIXEL_16x16]就是該數組的第一個元素,能夠發現這是一個函數,根據輸入的參數能夠計算獲得一個值,通過查找,最終發現,mbcmp的值就是h->pixf中的的sadsatd中的一個,具體代碼是在x264_encoder_open函數中:

memcpy( h-> pixf. mbcmp,( h-> mb. b_lossless || h->param .analyse .i_subpel_refine <= 1 ) ? h-> pixf. sad : h->pixf .satd  sizeof(h ->pixf. mbcmp) );

這樣由p_srcp_dst就計算獲得失真SADSATD

 

C. 函數bs_size_ue用於對所採用的模式進行碼率大小的估算,而後計算cost(仍然是基於Lagrange的):

i_sad = h ->pixf. mbcmp[PIXEL_16x16 ]( p_dst , FDEC_STRIDE , p_src,FENC_STRIDE ) + a-> i_lambda * bs_size_ue ( x264_mb_pred_mode16x16_fix[i_mode ] );

 

接下來,根據b_mbrd變量來採用不一樣的方式(這兒對b_mbrd並不理解其含義)。調用x264_mb_analyse_intra_chroma對色度進行模式選擇。和亮度16x16相似,採用了predict_8x8chroma_mode_available函數來選取合適的模式,而後利用for循環選出cost最小的模式,操做方式和上面的分析都是相似的。以後,有可能要調用函數x264_rd_cost_mb如今尚未弄明白這是爲何?!

 

對於Intra4x4,採用的是分紅164x4塊來處理的。因此整個過程式在for循環中的。

A. 爲了減小編碼時的比特數x264先對所要採用的模式進行了一個預測,若是預測中的話就只須要一個比特,而預測不中的話就須要4個比特來保存所採用的模式。

x264_mb_predict_intra4x4_mode函數代碼能夠看到,預測的方式就是根據左和上的宏塊的預測模式中選擇較小的那個模式。

B. 仍是老方法,調用函數predict_4x4_mode_available來找出哪些模式是可行的,而後針對這些模式利用predict_4x4進行預測,再計算cost,從中選擇最優模式。

C. 爲了下一個4x4塊編碼使用,須要對當前4x4塊進行編碼(這兒我還不是很理解):

      /* Luma prediction */

 h-> predict_4x4[a ->i_predict4x4[ x][ y]]( p_dst_by );

 /* encoding the current 4x4 Block, Update the reconstructed data(fdec). */

 x264_mb_encode_i4x4( h , idx , a ->i_qp );

  /* Cache the intra 4x4 prediction mode of current Block */

  h->mb .cache .intra4x4_pred_mode [x264_scan8 [idx ]] = a->i_predict4x4 [x ][y ]; //Update the prediction mode for later use??.

 

D. 須要注意的是爲了和Intra16x16進行比較,這兒所採用的cost計算方式:

 a-> i_sad_i4x4 += a ->i_lambda * 24;    /* from JVT (SATD0) */ //??

  //RDO率失真優化模式下,宏塊總代價cost_intra4*4 = 16 4*4 小塊的最佳 cost 求和 + 4 * 6 * lambda_mode.
  //
此處因爲還未進行4X4代價計算,只是預先增長4 * 6 * lambda_mode.
  //
當採用4X4分塊時,因爲每一個4X4塊的最優預測編碼模式都須要進行編碼傳輸,這樣,相比較於16X16模式就多了傳輸比特數,
  //
爲了合理公平比較,規定每一個8*8塊加一個6*lambda_mode,所以就等因而加了一個 4 * 6 * lambda_mode.

 

一樣出現b_mbrd,之後弄懂了再討論。

 

對於Intra8x8,採用的是分紅48x8塊來處理,仍然是在for循環中進行的。

A. 爲了減小比特數,一樣進行了模式預測,和Intra4x4時採用的是一樣的函數,只是參數有些改變。

B. 調用函數predict_4x4_mode_available來找到可行的模式。

C. Intra4x4相似,也是對當前8x8塊進行編碼:

/* Luma prediction for each 8x8 Block. */

h-> predict_8x8[a ->i_predict8x8[ x][ y]]( p_dst_by, h ->mb. i_neighbour8[idx ] );

/* Calculate the reconstructed data,Store the prediction data in p_dec */

x264_mb_encode_i8x8( h , idx , a ->i_qp );

  /* Cache the intra 8x8 prediction mode for each 8x8 Block */

x264_macroblock_cache_intra8x8_pred( h , 2*x , 2*y, a ->i_predict8x8[ x][ y] );

 

這樣Intra16x16Intra4x4Intra8x8模式都選擇完了,獲得了三種狀況下的cost,而後進行比較獲得最優的劃分方式:

         i_cost = analysis .i_sad_i16x16 ; // SAD cost of i_16x16

        h-> mb. i_type = I_16x16 ;

        if( analysis.i_sad_i4x4 < i_cost )

        {

            i_cost = analysis .i_sad_i4x4 ;

            h-> mb. i_type = I_4x4 ;

        }

        if( analysis.i_sad_i8x8 < i_cost )

          h-> mb. i_type = I_8x8 ; // We Only remember the Macro-block type.

 

 

3x264_macroblock_probe_pskipx264_macroblock_probe_bskip

這兩個函數是判斷是否要採用skip模式。雖然是分別針對p幀和B幀,可是兩個函數最終都是調用的同一個函數x264_macroblock_probe_skip,只是調用的時候參數是不同的。下面是x264_macroblock_probe_skip的調用方式:

這個函數主要原理就是對宏塊進行預測,而後計算殘差,而後對殘差進行DCT變換,量化,而後利用x264_mb_decimate_score函數來爲DCT係數打分,看是否能夠將這些係數設置爲0,這樣就不用對這個宏塊編碼了,即skip模式,對於不一樣的塊,有不一樣的閾值來限制。

 

4x264_mb_analyse_load_costs函數

關於這個函數,代碼中給出的註釋是:/* initialize an array of lambda*nbits for all possible mvs */

應該是對運動矢量所須要的cost進行提早計算,p_cost_mv[i]用於保存這些值。因爲MVD的值可能爲正也可爲負,因此操做中對於正負MVD的運動矢量cost計算是同樣的。操做的時候是先將p_cost_mv[i]的指針移動到數組中間的,而後從中間往兩邊賦值的。

網上有人指出,這個數組存在內存泄露的問題,確實我找了好屢次都沒有找到關於這個對這個數組釋放內存的操做。

 

5x264_mb_analyse_inter_p16x16函數

這個函數實現的是16x16的運動搜索。

首先利用宏LOAD_FENC加載須要編碼的宏塊數據,而後是一個for循環,該循環是參考幀的循環,從最近的一個參考幀開始搜索,一直到最遠的一個參考幀。在循環中,先利用宏LOAD_HPELS載入參考幀數據,而後調用x264_mb_predict_mv_16x16函數尋找運動矢量的預測值MVP,主要以上、右上、左塊運動矢量的中值做爲預測值,並存儲在m.mvp中。而後調用x264_mb_predict_mv_ref16x16函數,來尋找候選運動矢量。這些候選者包括:空間相鄰的左、左上、上、右上塊的MV;第0個參考幀中的當前塊、右邊塊、下邊快運動矢量乘以時間差權重。接下來調用x264_me_search_ref進行運動搜索。搜索時先從全部候選運動矢量中選出最佳的起點,而後使用小鑽石法、六邊形法、UMH或者全搜索搜索出最佳的整像素位置。在x264_me_search_ref函數的最後,同時調用了函數refine_subpel了進行1/21/4運動搜索,而且都是使用小鑽石法。在搜索出最佳運動矢量後,若是當前是最近一個參考幀,並且最佳SA(T)D小與檢測門限,則嘗試對其進行P_SKIP編碼。最後調用函數x264_macroblock_cache_ref保存搜索結果。

 

6x264_mb_analyse_inter_p8x8x264_mb_analyse_inter_p8x8_mixed_ref函數

這兩個函數實現對8x8模式的運動搜索。這兩個函數區別在於,函數x264_mb_analyse_inter_p8x8只使用在16x16運動搜索中用到的幀。候選MV的數目也是有限制的。而x264_mb_analyse_inter_p8x8_mixed_ref則沒有限制,這些8x8塊的參考幀能夠是不一樣的幀。

 

7x264_mb_analyse_inter_p4x4函數

8x8塊內的44x4小塊進行運動搜索的分析。和x264_mb_analyse_inter_p8x8是相似的。

 

8x264_mb_analyse_inter_p8x4x264_mb_analyse_inter_p4x8函數

這兩個函數是對8x8塊的兩個8x4或兩個4x8進行運動搜索。和上面的都是相似的。

 

9x264_mb_cache_mv_p8x8函數

這個函數是爲每一個8x8塊保存MV

 

10x264_mb_analyse_inter_p16x8x264_mb_analyse_inter_p8x16函數

這兩個函數完成對16x16塊中的兩個16x8塊或兩個8x16塊的搜索。

爲了加快搜索的速度,x264對這兩個函數所採用的參考幀進行了限制:Only search in the reference frame found in the inter_8x8 motion estimation.  The mv candidates is also limited. 也就是隻採用在x264_mb_analyse_inter_p8x8_mixed_ref中使用到的參考幀。

 

(11)x264_me_refine_qpel函數

這個函數是完成亞像素的運動搜索,其實在該函數中是調用了refine_subpel來具體實施的。

 

12x264_mb_predict_mv_direct16x16函數

判斷是否可使用Direct模式。

13x264_mb_mc 函數

這個函數是根據運動搜索完成以後的MV來進行宏塊的運動補償。

在這個函數中根據不一樣的宏塊類型進行運動補償,下面是該函數調用的函數:

該函數在運動補償時是和採用的參考序列式相關的。下面是一個流程圖:

 

14x264_mb_analyse_inter_b16x16 函數

這對16x16進行運動搜索,因爲是針對B幀的,因此涉及到了採用L0L1參考幀列表的狀況。

 

15x264_mb_analyse_inter_b8x8函數

這是B幀對應的8x8運動搜索,仍是宏塊分爲48x8塊進行。

 

16x264_mb_analyse_inter_b16x8x264_mb_analyse_inter_b8x16函數

B幀對應的16x88x16搜索,將宏塊分紅兩個16x8或兩個8x16.

 

 

13. x264_mb_analyse_inter_p16x16函數

1x264_mb_predict_mv_16x16

這個函數是對宏塊的運動矢量進行預測,從而獲得MVP。在進行MVP計算中採用的是左A,上B,右上C和左上D四個宏塊的運動矢量。

在函數中要判斷一下C是否存在的,若是C存在,則是優先使用CMV,若是C不存在,那麼就用D來代替C

而後統計當前宏塊所參考的參考幀序號與鄰塊ABCABD所參考的參考幀序號相同數 。若是與ABCABD中的兩個或兩個以上宏塊的參考幀相同,那麼直接調用x264_median_mv來取這三個鄰塊的運動矢量的中值做爲預測運動矢量。若是隻有一個相同時,預測運動矢量設置爲該鄰塊的運動矢量。

 

2x264_mb_predict_mv_ref16x16

在代碼中給出了註釋,指出這個函數並非標準要求的,而是x264爲了提升編碼器系能而本身添加的。由於標準中並無要求在運動估計以前給出候選MV,而是隻要求找到最優MV便可。x264爲了加快MV的搜索,採用了候選MV的方式來預測搜索起點

 

3x264_me_search_ref

這個函數就是具體進行運動搜索。搜索時從候選運動矢量中選取起點,而後根據設定進行運動搜索。最後調用了refine_subpel進行亞像素運動搜索。

相關文章
相關標籤/搜索