[ffmpeg] h.264解碼所用的主要緩衝區介紹

在進行h264解碼過程當中,有兩個最重要的結構體,分別爲H264Picture、H264SliceContext。html

 

H264Picture

H264Picture用於維護一幀圖像以及與該圖像相關的語法元素。其中佔用大片內存的結構體成員有如下幾個:數組

typedef struct H264Picture {
    AVFrame *f;
    int8_t *qscale_table; 
    int16_t (*motion_val[2])[2]; 
    uint32_t *mb_type; 
    int8_t *ref_index[2]; 
} H264Picture;
Menber/Size[2] Description
f
W x H (frame in pixels) x YUV
維護視頻的一幀,主要的存儲空間由AVBufferRef提供,存儲的是這一幀的像素數據,因爲視頻中每一個像素分有YUV三個份量,所以會有三塊大內存,分別存儲這三個份量的像素數據[1]。若是視頻是以interlaced來進行編碼的,則會對一幀分爲上下場進行編碼,不過在解碼的時候這兩個場會被合併,由這一成員維護。
image
qscale_table
W x H (frame in MBs)
記錄一幀中全部宏塊的QP。每一個宏塊都有獨立的QP,QP值由SPS、PPS、slice以及宏塊中的QP相關語法元素計算得來。QP除了用於對殘差係數進行逆量化以外還在去塊濾波中起到判別真假濾波邊界的做用。
image
motion_val
W x H (frame in 4x4 blocks) x 2 x 2
記錄一幀中全部4x4塊的運動向量。4x4塊是運動向量做用的最小單位,該表格會記錄inter宏塊中各個4x4塊的運動向量。若是該塊在進行編碼時採用的是雙向預測,那麼在解碼的時候就會獲得前向以及後向共兩個運動向量,所以motion_val是個長度爲2的數組,分別指向前向以及後向運動向量表,表中的每一項表示一個運動向量。一個運動向量分爲x與y兩個份量。
image
mb_type
W x H (frame in MBs)
記錄一幀中全部宏塊的類型。即每一個宏塊解碼出來的語法元素mb_type。
image
ref_index
W x H (frame in 8x8 blocks) x 2
記錄一幀中全部8x8塊的參考圖像索引。8x8塊是參考圖像做用的最小單位,該表格會記錄inter宏塊中各個8x8塊的參考圖像索引。若是該塊在編碼時採用的是雙向預測,那麼在解碼的時候就會獲得前向以及後向共兩個參考圖像索引,所以ref_index是個長度爲2的數組,分別指向前向以及後向參考圖像索引表,表中的每一項存儲一個索引。
image

 

 

H264SliceContext

h.264解碼時,各個slice之間相對來講較爲獨立,所以對於從一個slice解碼出來的各個語法元素,會用一個結構體來進行維護,這個結構體就是H264SliceContext。在對slice解碼過程當中涉及到的大多數據的存取都是經過該結構體來完成。其中佔用較大內存,而且會被頻繁使用的語法元素相關的結構體成員有如下幾個:ide

typedef struct H264SliceContext {
    int8_t intra4x4_pred_mode_cache[5 * 8];
    int8_t(*intra4x4_pred_mode);
    DECLARE_ALIGNED(8, uint8_t, non_zero_count_cache)[15 * 8];
    DECLARE_ALIGNED(16, int16_t, mv_cache)[2][5 * 8][2];
    DECLARE_ALIGNED(8,  int8_t, ref_cache)[2][5 * 8];
    DECLARE_ALIGNED(16, uint8_t, mvd_cache)[2][5 * 8][2];
    uint8_t direct_cache[5 * 8];
    ///< as a DCT coefficient is int32_t in high depth, we need to reserve twice the space.
    DECLARE_ALIGNED(16, int16_t, mb)[16 * 48 * 2];
    DECLARE_ALIGNED(16, int16_t, mb_luma_dc)[3][16 * 2];
    uint8_t (*mvd_table[2])[2];
}
Menber Description
intra4x4_pred_mode_cache 存儲當前宏塊及其Left,Top方向的每一個4x4塊的intra4x4預測模式,有以下用途:
1. 對當前宏塊進行幀內預測,也就是經過intra4x4預測模式來構建宏塊的像素數據[7]
2. 在進行當前宏塊的intra4x4預測模式的預測時,須要根據每個4x4塊其A(左)、B(上)塊的intra4x4預測模式來進行當前預測模式的預測[6]
image
intra4x4_pred_mode 存儲當前宏塊所在的行以及前一行宏塊的intra4x4預測模式[17]。可是要注意的是,這裏只對每一個宏塊提供8個intra4x4預測模式的存儲位置,而實際所用到的區域只有7個,這7個intra4x4預測模式分別位於當前宏塊的最底下一行(Bottom)以及最右邊一列(Right)[4]
當前宏塊的這7個intra4x4預測模式將會做爲後面所解碼的宏塊的Left、Top方向的intra4x4預測模式使用,即會用intra4x4_pred_mode來填充intra4x4_pred_mode_cache的Left、Top的位置[3]
image
non_zero_count_cache 存儲當前宏塊及其Left、Top方向的每一個4x4塊中非零係數的個數,有YUV三個份量。有以下用途:
1. 在cabac解碼語法元素coded_block_flag時,須要當前塊的A(左)以及B(上)的非零係數數目來選取上下文索引[16]
2. 在進行去塊濾波時,會根據邊界兩邊的塊是否含有非零係數來肯定濾波強度[13]
3. 4x4塊non_zero_count的值會根據當前宏塊的CBP的值來進行設定,若是一個4x4塊沒有非零係數,則沒有必要進行係數的逆量化逆變換了。
image
mv_cache 存儲當前宏塊及其Left、Top、Top-Right、Top-Left方向的mv。有以下用途:
1. 在進行mv預測時,會根據當前塊的A、B、C、D的mv來獲得當前塊的mvp[10]
2. 若是當前塊是B_Direct,而且採用的是spatial預測,則會根據當前塊的A、B、C來肯定當前塊的ref以及mv[9]
3. 在進行運動補償時,須要經過當前塊的mv來生成像素數據[11]
4. 在進行去塊濾波時,會根據邊界兩邊的mv的差值來肯定濾波強度[13]
image
ref_cache 存儲當前宏塊及其Left、Top、Top-Right、Top-Left方向的ref。有以下用途:
1. 在進行ref的cabac解碼時須要根據其A以及B方塊的ref來選取上下文索引值[14]
2. 在進行mv預測的時候,會根據解碼出來的當前塊的ref以及A、B、C、D的ref來獲得mvp[10]
3. 在進行運動補償時,須要經過當前塊的ref來生成像素數據[11]
4. 在進行去塊濾波時,會根據邊界兩邊是否爲同一個ref,或者是否有一樣的參考幀數目來肯定濾波強度[13]
5. 若是當前塊是B_Direct,而且採用的是spatial預測,則會根據A、B、C塊的ref來肯定當前塊的ref以及mv[9]
image
mvd_cache 存儲當前宏塊以及其Left、Top的mvd。有以下用途:
1. 經過當前塊的mvd以及預測所得的mvp獲得正確的mv[8]
2. 在進行當前塊的mvd的cabac解碼時須要根據其A、B塊的mvd來選取上下文索引值[15]
image
direct_cache 存儲當前宏塊的Left、Top的塊的sub_mb_type(以8x8塊爲單位),主要用於判斷這些塊是否爲B_Direct。在進行ref的cabac解碼時須要根據當前塊的A、B的塊是否爲B_Direct來選取上下文索引值[14]
image
mb 存儲當前宏塊的YUV的像素殘差的變換系數。在編碼的時候宏塊像素殘差的編碼順序爲變換、量化、而後熵編碼就能獲得碼流數據;而在解碼時,宏塊的碼流在通過熵解碼後,而後執行逆量化,會獲得宏塊殘差像素的變換系數,這些係數會被存在mb當中,對這些殘差係數執行逆變換後,就能獲得像素殘差。
image
mb_luma_dc 存儲當前宏塊的DC係數。在編碼時,若是當前宏塊採用的預測模式爲intra16x16,那麼像素殘差在進行4x4的變換後會獲得16個DC係數以及15x16個AC係數,在進行量化後,這16個DC係數會排列在一塊兒先進行熵編碼,而後熵編碼這16x15個AC係數;那麼在解碼時,若是當前宏塊的預測模式爲intra16x16,那麼在執行熵解碼後會獲得16個DC係數,這些DC係數會被寫入mb_luma_dc當中。在mb_luma_dc當中的這些DC係數在進行逆量化後就會被寫入mb,造成16個4x4的像殘差係數[12]
image
mvd_table 存儲當前宏塊所在的行以及前一行宏塊的mvd[17]。可是要注意的是,這裏只對每一個宏塊提供8個mvd的存儲位置,而實際所用到的區域只有7個,這7個mvd分別位於當前宏塊的最底下一行(Bottom)以及最右邊一列(Right)[5]
當前宏塊的這7個mvd將會做爲後面所解碼的宏塊的Left、Top方向的mvd使用,即會用於填充mvd_cache的Left、Top的位置[3]
image

其中名稱中含有「cache」這一名稱的結構體成員都須要當前宏塊的周邊塊的信息,這些信息都是在fill_decode_cache中寫入到成員的數組中的,而當前宏塊中的信息則是在熵解碼後直接或者間接存儲到cache結構體成員中。oop

這些包含cache字段的成員中基本都有DECLARE_ALIGNED修飾,這個宏主要用於向編譯器聲明這些成員爲8或者16byte對齊。緣由是爲了提高處理速度,這些成員大多須要用SIMD指令進行處理,而SIMD指令在執行時,若是內存操做數不是對齊的,則有可能會出現性能降低[18]性能

這些結構體成員被命名爲cache也是有緣由的。在計算機原理中,當進行內存訪問時,爲了提升數據訪問速度,通常都會對所訪問的內存及其周邊內存區域(即一個cache line)一同取入cache當中,若是某個代碼段會頻繁訪問數據,而且大部分數據都在cache當中,即cache命中率高,那麼這個代碼段的執行效率就會獲得很好的提高;若是大部分數據不在cache中,即cache命中率低,就會在數據訪問上浪費大量時間。通常的處理器的L1 cache僅幾十k字節的容量,所以在執行數據處理的時候,若是不是頻繁訪問的內存區域,有可能很快就會被從cache中清除。基於這些理論,如今返回來觀察h264頻繁訪問的數據,能夠發現:優化

  1. 這些以cache命名的結構體成員除了包含當前宏塊的數據以外,還包含其周邊塊的數據,特別是上一行的數據。在實際進行數據的排列的時候,是以宏塊行爲單位從左到右進行排列的,所以即便宏塊在空間位置上是上下相鄰,可是在內存中也會間隔較遠,頗有可能不在同一cache line中。
  2. 解碼一個宏塊所須要訪問的數據繁多,解碼器爲每一幀的每種數據都分配了各自的內存塊,這些內存塊都佔用至關大的內存空間,所以不一樣的數據不可能在同一cache line中。
  3. 解碼一個宏塊須要屢次訪問各個內存塊中的不一樣數據,而且訪問的代碼段較爲分散。因爲cache空間有限,若是直接處理內存塊內的數據,就有可能會致使cache line被頻繁替換,使得在進行數據訪問的時候cache命中率較低,從而在數據訪問上耗費較多時間。

爲了針對上述問題進行優化,ffmpeg把在進行宏塊解碼時頻繁訪問到的數據集中到了H264SliceContext結構體中,而且用名稱包含cache字段的成員存儲宏塊及其周邊的數據。如此一來,就使得宏塊解碼過程當中的數據訪問的內存範圍大大縮小,只有在開頭的填充這些成員以及末尾的數據寫回的時候纔會訪問到各個分散的內存塊,以此來提高內存的cache命中率。ui

 

 

還有一些未被介紹的緩衝區,指向這些緩衝區的指針是H264Context結構體的成員,主要在ff_h264_alloc_tables中進行內存分配。編碼

 

Reference:spa

  1. update_frame_pool, video_get_buffer
  2. alloc_picture, init_table_pools
  3. fill_decode_cache
  4. write_back_intra_pred_mode
  5. write_back_motion_list
  6. 8.3.1.1 Derivation process for Intra4x4PredMode/ Intra Luma Prediction (pred_intra_mode)
  7. 8.3.1.2 Intra_4x4 sample prediction / Intra Luma Prediction (hl_decode_mb_predict_luma, pred4x4)
  8. 8.4.1 Derivation process for motion vector components and reference indices (DECODE_CABAC_MB_MVD)
  9. 8.4.1.2.2 Derivation process for spatial direct luma motion vector and reference index prediction mode (pred_spatial_direct_motion)
  10. 8.4.1.3 Derivation process for luma motion vector prediction / h.264 mvp求解過程 (pred_motion)
  11. 8.4.2.2 Fractional sample interpolation process (mc_part_std,mc_dir_part)
  12. 8.5.2 Specification of transform decoding process for luma samples of Intra_16x16 macroblock prediction
    mode (decode_cabac_luma_residual, hl_decode_mb_predict_luma, h264_luma_dc_dequant_idct)
  13. 8.7.2.1 Derivation process for the luma content dependent boundary filtering strength / 估算邊界強度 (check_mv, filter_mb_dir)
  14. 9.3.3.1.1.6 Derivation process of ctxIdxInc for the syntax elements ref_idx_l0 and ref_idx_l1 (decode_cabac_mb_ref)
  15. 9.3.3.1.1.7 Derivation process of ctxIdxInc for the syntax elements mvd_l0 and mvd_l1 (DECODE_CABAC_MB_MVD)
  16. 9.3.3.1.1.9 Derivation process of ctxIdxInc for the syntax element coded_block_flag (get_cabac_cbf_ctx)
  17. ff_h264_alloc_tables
  18. Intel® 64 and IA-32 Architectures Optimization Reference Manual 4.4 STACK AND DATA ALIGNMENT
相關文章
相關標籤/搜索