1. 程式人生 > >H.264碼流分析

H.264碼流分析

原文轉自:http://blog.163.com/[email protected]/blog/static/193507792010498440670/

H.264標準寫得比較繁複,所以考慮在瀏覽完Whitepaper之後就開始研讀X264程式碼。X264程式碼風格還是比較清晰簡潔的。

根據對標準得理解,Picture Order Count在Slice解碼的一開始就被提及:

I0 B1 B2 P3 B4 B5 P6
I0 P3 B1 B2 P6 B4 B5

於是I0的POC是0,P3的POC是3,B1是1……

為了支援H264複雜的幀存機制,X264以專門的一個模組frame.c進行處理。

common/frame.c中包括一組幀緩衝操作函式。包括對幀進行FILO和FIFO存取,空閒幀佇列的相應操作等。

以下逐個函式分析encoder.c中編碼一幀的函式x264_encoder_encode中有關frame的呼叫:

x264_reference_update
這個函式裡最主要的工作的是將上一個參考幀放入參考幀佇列,並從空閒幀佇列中取出一幀作為當前的參考工作幀(即解碼操作的目的幀),即h->fdec。

x264_t 結構體維護著CODEC的諸多重要資訊,其中成員frames是一個指示和控制幀編碼過程的結構。

其中current是已經準備就緒可以編碼的幀,其型別已經確定;

next是尚未確定型別的幀;

unused用於回收不使用的frame結構體以備今後再次使用。

frames結構體中i_input指示當前輸入的幀的(播放順序)序號。i_delay設定為由B幀個數(執行緒個數)確定的幀緩衝延遲,在多執行緒情況下為i_delay = i_bframe + i_threads - 1。

而判斷B幀緩衝填充是否足夠則通過條件判斷:h->frames.i_input <= h->frames.i_delay + 1 - h->param.i_threads。


x264_encoder_encode 每次會以引數送入一幀待編碼的幀pic_in,函式首先會從空閒佇列中取出一幀用於承載該新幀,而它的i_frame被設定為播放順序計數,如:fenc->i_frame = h->frames.i_input++。
x264_encoder_encode在根據上述判據確定B幀緩衝充滿的情況下才進行後續編碼工作。


噹噹前佇列(current佇列)可用幀為0時,需要對next佇列中的幀進行判決,需要進行如下過程:
1. 呼叫x264_slicetype_decide
這個函式確定當前條帶(幀)的型別
其中首先呼叫x264_ratecontrol_slice_type,依據位元速率控制逐個求出next列表中所有幀的型別(雖然在當前並不全部用到,見後)。
隨後統計審查並調整next列表,保證IDR幀滿足有關最大關鍵幀間隔的要求的正常出現:

即針對frm->i_frame - h->frames.i_last_idr >= h->param.i_keyint_max作判斷。審查按順序針對所有被判定為B系或AUTO型別的幀進行(這些幀在稽核過程中被確認為B幀),直到遇到第一個不是這樣的幀。
如果某個幀被指定為IDR,則一個GOP在它之前結束。

2. 而後,即將next列表中已經判定的一系列幀(先後是一些B幀和一個非B幀)轉移到current列表中。在這個過程中:
原始序列(播放順序)B0, B1, B2, P,轉移後的順序為P, B0, B1, B2。在使用bframe_pyramid模式時,中間的B幀要前置,即上述順序變為:P, B1, B0, B2。

此時,就可以從current佇列中取出一幀,進行編碼,現在記這幀叫h->fenc。
首先做幾項和幀有關的設定工作:
1. 如果f_enc是IDR,則將最近IDR序號標記h->frames.i_last_idr設定為i_frame。
2. 根據f_enc的型別確定NAL和SLICE型別相關引數。 
3. 設定POC為2 * (h->fenc->i_frame - h->frames.i_last_idr)。並使得h->fdec和h->fenc的主要幀引數一致。

隨後進行以下一些過程:
x264_reference_build_list
在這個函式中,我們將遇到參考幀列表h->frames.reference和H.264很有特色的雙列表(h->fref0、h->fref1)。前者中放置了所有可用於參考的參考幀。
首先將所有reference列表中的幀按照POC和h->fenc的POC的大小關係不同複製到雙列表中,其中h->fref0放置POC較小的那些。然後對雙列表進行排序,使得h->fref0中的幀POC按從大到小排列,h->fref1按從小到大排列,於是這兩個列表中靠前的幀的POC比較接近當前幀h->fenc。在這裡,一個特殊的情況是,對於P幀(只需要用到列表h->fref0做參考),其排序依據是 i_frame_num而不是POC,因此需要對這種情況的列表h->fref0置位需重排序標記。最後對雙列表的長度做限制。

x264_ratecontrol_start
位元速率控制初始化,在以後專門的話題中論述。
在初始化後,從位元速率控制體中獲得全域性QP值:i_global_qp = x264_ratecontrol_qp( h )。

如果當前是B條帶(幀),則呼叫x264_macroblock_bipred_init
該函式設定一些參考幀關係矩陣,關係矩陣以fref0的索引為行座標,以fref1的索引為列座標。主要包括:dist_scale_factor,bipred_weight,矩陣值主要由相應幀的POC決定。後續討論。

x264_slice_init
內部呼叫x264_slice_header_init,它對Header進行配置,含義以後討論。

bs_init
初始化碼流工作上下文。

隨後將幀型別表現到碼流中。接著寫SEI,SPS和PPS。

然後就是最關鍵的寫條帶(即主要的編碼工作)開始的地方:
x264_slices_write
這裡通過x264_stack_align呼叫x264_slice_write,為了將棧做4位元組對齊,以提高執行效率。

x264_slice_write函式是編碼一幀的關鍵函式,將在下一章中論述。

巨集塊結構

這裡主要有一下幾個過程:
1. 初始化h->stat.frame,即全部清零。
2. 寫條帶頭:x264_slice_header_write,即把剛才x264_slice_header_init設定的一些引數寫入。
3. 如果是CABAC編碼,則初始化CABAC。有關CABAC在後續相關章節討論。
4. 遍歷一幀中的所有巨集塊,這是編碼的主要部分:
for( mb_xy = h->sh.i_first_mb, i_skip = 0; mb_xy < h->sh.i_last_mb; )
其中sh.i_first_mb和sh.i_last_mb在x264_slice_header_init中賦值,分別是0和巨集塊行列數乘積。
5. 最後輸出碼流尾部。
其間穿插一些必要的初始化和配置。

本章著重分析編碼的主要部分,包括一下過程:
需要注意的是,這裡討論的巨集塊是固定尺寸的,即Y分量計為16x16。(而運動巨集塊的尺寸則是多變的)

1. 首先根據巨集塊的序號得出巨集塊的座標。然後統計當前巨集塊的碼流位置:
int mb_spos = bs_pos(&h->out.bs) + x264_cabac_pos(&h->cabac);

2. 然後出現一句:
if( i_mb_x == 0 )
    x264_fdec_filter_row( h, i_mb_y );
這是對行進行去塊過程。詳細情況專門論述。

3. 隨後是x264_macroblock_cache_load,這裡主要進行當前巨集塊和相關資訊的提取。這個函式比較大,也在專題中論述。

4. 接下去進行解碼分析操作
x264_macroblock_analyse

5. 以及解碼實體:
x264_macroblock_encode

6. 而後處理碼流輸出,也分CABAC和CAVLC兩種情況。

7. 接著出現x264_macroblock_cache_save,和x264_macroblock_cache_load對應,儲存處理完的巨集塊。

8. 更新巨集塊統計


下面,首先看編碼主體x264_macroblock_encode的結構。
該函式按不同巨集塊型別情況進行不同的處理。包括如下型別:
P_SKIP
B_SKIP
I_16x16 - 一次完成一個塊h->mb.pic.p_fdec[0]的編碼。
   用函式指標h->predict_16x16[i_mode]進行幀內預測
I_8x8   - 分4次完成。h->mb.pic.p_fdec[0][8 * (i&1) + 8 * (i>>1) * FDEC_STRIDE]指定了當前位置。即:
   [0][1]
   [2][3]
   用函式指標完成4個巨集塊的幀內預測
I_4x4 - 分16次完成。h->mb.pic.p_fdec[0][4 * block_idx_x[i] + 4 * block_idx_y[i] * FDEC_STRIDE]指定當前位置。
   根據block_idx_x和block_idx_y的定義,其順序是:
   [0][1][4][5]
   [2][3][6][7]
   [8][9][c][d]
   [a][b][e][f]
   用函式指標完成4個巨集塊的幀內預測
Inter - 首先進行運動補償。見後續討論。
          然後分多種情形:
   1. 無失真壓縮情況:h->mb.b_lossless == TRUE(必須採用4x4DCT)
      根據上述I_4x4中描述的順序遍歷16個4x4塊。
      這裡呼叫的函式是zigzag_sub_4x4_field,完成殘差。(無損情況直接傳送殘差)
   2. 指定採用8x8DCT情況:h->mb.b_transform_8x8 == TRUE
      首先是作殘差並DCT的函式:sub16x16_dct8。
      然後對4個8x8的DCT係數塊進行處理:包括可能的去噪x264_denoise_dct,量化(標量/向量)。有關量化也在專題中論述。量化完成後進行係數掃描(對角平衡的普通掃描)。
   3. 一般情況
      
接著處理色差分量,如果是Intra的則進行預測。
最後寫Coded Block Pattern。

幀內預測

環路內濾波

H.264環路內濾波顧名思義在編碼側開啟後解碼部分必須跟隨開啟,因此是該視訊編碼方案的不可分割的組成部分。

    以下整理了Baseline情形下環路濾波的四種情形:

    All cases that may exist for Baseline
    Bs = 4: either is intra, MB edge
    Bs = 3: either is intra, block edge
    Bs = 2: both are inter, either is coded
    Bs = 1: both are inter neither is coded, different ref pictures, either MV component is no smaller than 4
    Bs = 0: o.w.

    以T264為例:
    環路濾波入口函式以光柵掃描順序對所有巨集塊實施deblock_mb。

    deblock_mb完成一個巨集塊的濾波,它包含以下步驟:

    1. 水平相鄰巨集塊濾波,即對與mb_xy-1巨集塊的邊界濾波,以及內部3條縱向塊邊界濾波;

    2. 垂直相鄰巨集塊濾波,即對與mb_xy-mb_stride巨集塊的邊界濾波,以及內部3條橫向塊邊界濾波;

    每條邊界長度均為16個畫素,等分成4份,每個對應一個最小塊大小的邊界,以此為單元進行濾波。
    對每條邊界呼叫get_strength,獲得這4個部分的Bs值。

本章討論的程式碼主要位於common/predict.c中。
x264_macroblock_cache_load函式在每個巨集塊解碼之前初始化某些狀態,在x264_slice_write函式的巨集塊處理迴圈中被呼叫。
i_mb_xy:   當前巨集塊的索引
i_mb_4x4:  當前巨集塊中第一個4x4塊的索引
i_mb_8x8:  當前巨集塊中第一個8x8塊的索引
i_top_y:   上方巨集塊的y索引
i_top_xy:  上方巨集塊的索引
i_top_4x4: 當前巨集塊中第一個4x4塊上方的4x4塊的索引
i_top_8x8: 當前巨集塊中第一個8x8塊上方的8x8塊的索引

這裡首先初始化和當前巨集塊毗鄰的已解碼塊。
這裡用到x264_scan8,它指示如下掃描結構:

   0 1 2 3 4 5 6 7
 0
 1   0 1   0 1 4 5
 2   2 3   2 3 6 7
 3         8 9 C D
 4   0 1   A B E F
 5   2 3

 在空缺部分恰好可以填入相應巨集塊的毗鄰塊。

以下逐個各種分析預測情況:

對於16x16的塊:
predict_16x16
共7種預測模式:H, V, DC, P, LEFT, TOP, 128
predict_16x16_h
用左側相鄰16x16塊的右側畫素沿水平方向覆蓋
predict_16x16_v
用上方相鄰16x16塊的底行畫素沿豎直方向覆蓋
predict_16x16_p
右上左下均值預測覆蓋(注意均值生成)
predict_16x16_dc
用左側邊沿和上方邊沿的均值作為DC預測值進行單一覆蓋
predict_16x16_dc_left
只用左側邊沿進行DC單一覆蓋
predict_16x16_dc_top
只用上方邊沿進行DC單一覆蓋
predict_16x16_dc_128
用128進行單一覆蓋

對於8x8的色差塊:
predict_8x8c_dc_128
用128進行單一覆蓋
predict_8x8c_dc_left
上下兩個4行分別以對應左邊沿4個畫素均值單一覆蓋
predict_8x8c_dc_top
左右兩個4列分別以對應上邊沿4個畫素均值單一覆蓋
predict_8x8c_dc
   s0 s1
s2 b0 b1
s3 b2 b3
s?是邊沿4畫素組
4個4x4塊分別用s0,s2均值,s1均值,s3均值和s1,s3均值單一覆蓋
predict_8x8c_h
用左側相鄰8x8塊的右邊沿畫素沿水平方向覆蓋
predict_16x16_v
用上方相鄰16x16塊的底行畫素沿豎直方向覆蓋
predict_8x8c_p
右上左下均值預測覆蓋(注意均值生成)

對於4x4的塊:(這是H264文件經常拿來作demo的)
predict_4x4_dc_128
用128進行單一覆蓋
predict_4x4_dc_left
用左邊沿4個畫素均值單一覆蓋
predict_4x4_dc_top
用上邊沿4個畫素均值單一覆蓋
predict_4x4_dc
用左側和上方邊沿共8個畫素均值單一覆蓋
predict_4x4_h
用左側相鄰4x4塊的右邊沿畫素沿水平方向覆蓋
predict_4x4_v
用上方相鄰4x4塊的底行畫素沿豎直方向覆蓋
predict_4x4_ddl
右上至左下預測覆蓋(注意均值生成)
predict_4x4_ddr
左上至右下預測覆蓋(注意均值生成)
predict_4x4_vr
左上到右下(偏下)預測覆蓋(注意均值生成)
predict_4x4_hd
左上到右下(偏右)預測覆蓋(注意均值生成)
predict_4x4_vl
右上到左下(偏下)預測覆蓋(注意均值生成)
predict_4x4_hu
左下到右上(偏右)預測覆蓋(注意均值生成)

對於8x8亮度塊:
edge中存放
predict_8x8_dc_128
用128進行單一覆蓋
predict_8x8_dc_left
用左側塊邊沿8個畫素均值單一覆蓋
predict_8x8_dc_top
用上方塊邊沿8個畫素均值單一覆蓋
predict_8x8_dc
用左側塊和上方塊邊沿共16個畫素均值單一覆蓋
predict_8x8_h
用左側邊沿預測覆蓋
predict_8x8_v
用上方邊沿預測覆蓋
predict_8x8_ddl
右上到左下預測覆蓋(注意均值生成)
predict_8x8_ddr
左上到右下預測覆蓋(注意均值生成)
predict_8x8_vr
左上到右下(偏下)預測覆蓋(注意均值生成)
predict_8x8_hd
左上到右下(偏右)預測覆蓋(注意均值生成)
predict_8x8_vl
右上到左下(偏下)預測覆蓋(注意均值生成)
predict_8x8_hu
左下到右上(偏右)預測覆蓋(注意均值生成)

以上“注意均值生成”指預測值一般由起點決定,起點由反向延長線確定,起點2倍權重和兩側點構成均值。
例如45度均值為以起始點對應45度位置的點作為中心(2倍權重),其兩側點作為補充形成的均值。 

H.264 標準規定,首先由macroblock層的mb_type匯出intra塊的預測型別macroblock prediction mode,其中包含子塊尺度資訊,有4x4,8x8和16x16三種。4x4和8x8按照標準由變換尺寸區分,4x4是最常用的模式。
在確定預測型別之後,對於含16個子塊的4x4模式,每個子塊的預測模式(即上述討論)由標準規定的判決演算法給出:
<...>


相關推薦

【視頻編解碼·學習筆記】6. H.264分析工程創建

clear href mark 一個 html filename down 創建 fail 一、準備工作: 新建一個VS工程SimpleH264Analyzer, 修改工程屬性參數-> 輸出目錄:$(SolutionDir)bin\$(Configuration)\,

H.264分析

原文轉自:http://blog.163.com/[email protected]/blog/static/193507792010498440670/ H.264標準寫得比較繁複,所以考慮在瀏覽完Whitepaper之後就開始研讀X264程式碼。X264程

H.264---格式分析

1、H.264的基本流結構 H.264 的基本流(elementary stream,ES)的結構分為兩層,包括視訊編碼層(VCL)和網路適配層(NAL)。視訊編碼層負責高效的視訊內容表示,而網路適配層負責以網路所要求的恰當的方式對資料進行打包和傳送。引入NAL並使之與VCL分離帶來的好處包括兩方面:1、使

libx264編碼---YUV影象資料編碼為h.264

編譯環境:ubuntu12.04 目標平臺:ARM Cortex A9 交叉編譯器:arm-none-linux-gnueabi-gcc 4.4.1 基本步驟:  一、移植x264庫到ARM板,請看上一篇博文移植x264編碼庫 二、測試:

海思3518EV200 SDK中獲取和儲存H.264詳解

1 /****************************************** 2 step 2: Start to get streams of each channel. 3 ************************************

使用FFMPEG類庫分離出多媒體檔案中的H 264

                在使用FFMPEG的類庫進行程式設計的過程中,可以直接輸出解複用之後的的視訊資料碼流。只需要在每次呼叫av_read_frame()之後將得到的視訊的AVPacket存為本地檔案即可。經試驗,在分離MPEG2碼流的時候,直接儲存AVPacket即可。在分離H.264碼流的時候,

H.264打包為RTP

H264碼流打包成RTP包的程式碼如下: #include <stdio.h> #include <stdlib.h> #include <conio.h> #include <string.h&g

Linux下H.264實時RTP打包與傳送

    由於專案要求在DM6467T平臺上新增實時RTP打包傳送模組,這才找了找有沒有人分享 這方面的經驗。這裡需要感謝網友:yanyuan9527,他寫的文章對我幫助很大,可以說讓一個完全小白的人瞭解了RTP打包,連結在此:http://www.chinavideo.or

H.264解析 一個SPS的nalu及獲取視訊的解析度

00 00 00 01 67 42 00 28 E9 00   A0 0B 77 FE 00 02 00 03 C4 80   00 00 03 00 80 00 00 1A 4D 88   10 94 00 00 00 01 00 00 00 01為NALu頭,‍其餘

H.264/AVC視訊編解碼技術詳解】十二、解析H.264的巨集塊結構(上)

《H.264/AVC視訊編解碼技術詳解》視訊教程已經在“CSDN學院”上線,視訊中詳述了H.264的背景、標準協議和實現,並通過一個實戰工程的形式對H.264的標準進行解析和實現,歡迎觀看! “紙上得來終覺淺,絕知此事要躬行”,只有自己按照標準文件以程式碼

H.264的RTP打包格式[FU-A]

本荷載型別允許分片一個NAL單元到幾個RTP包中。下圖 表示FU-A的RTP荷載格式。FU-A由1位元組的分片單元指示,1位元組的分片單元頭,和分片單元荷載組成。 FU指示位元組有以下格式: FU指示位元組的型別域的28,29表示FU-A和FU-B。F的使用在5。3描述。NRI域

Linux安裝SRS伺服器轉發H.264

操作步驟:2.得到srs-master.zip原始碼檔案,把它拷貝到Linux系統3.解壓檔案命令unzip srs-master.zip4.進入檔案安裝目錄命令cd srs-master/trunk5.生成預設Makefile命令./configure6.編譯命令make命

H.264 結構解析 (整理)

1. H.264簡介 MPEG(Moving Picture Experts Group)和VCEG(Video Coding Experts Group)已經聯合開發了一個比早期研發的MPEG

MP4中提取H.264

 1.獲取資料 ffmpeg讀取mp4中的H264資料,並不能直接得到NALU,檔案中也沒有儲存0x00000001的分隔符。下面這張圖為packet.data中的資料 從圖中可以發現,packet中的資料起始處沒有分隔符(0x00000001), 也不是0x65、0x

H.264編碼格式分析

mas rail head nalu 比特流 包括 val slice raw   H.264的重要性不再提了。本文主要記錄一下H.264的編碼格式。H.264官方文檔:https://github.com/jiayayao/DataSheet/tree/master/en

【mpeg2】MPEG-2簡單分析

Date: 2018.11.1 1、參考 https://blog.csdn.net/wishfly/article/details/50921570 https://blog.csdn.net/leixiaohua1020/article/details/11846185

android平臺下基於ffmpeg採集Camera資料編碼成H.264到RTMP伺服器

音視訊實踐學習 android全平臺編譯ffmpeg以及x264與fdk-aac實踐 ubuntu下使用nginx和nginx-rtmp-module配置直播推流伺服器 android全平臺編譯ffmpeg合併為單個庫實踐 android-studio使用c

H.264---編碼架構分析

一、編碼架構分析   編碼器有兩條資料通道:前向通道和重建通道。在前向通道中,編碼器的輸入是幀Fn,每幀是以16X16畫素大小的巨集塊單元組成的,每個巨集塊進行幀內或幀間預測編碼。幀間預測的參考幀為前一幀Fn‘,實際上,參考幀的數量可以多達五幀。當前塊減去其預測塊P得到殘差Dn,Dn經過

商用的分析工具/YUV viewer

                目前市場上有一些商用的碼流分析工具是比較好的,能夠輔助我們進行各個標準的碼流分析:1. Elecard StreamEye系列  可以分析H264/MPEG4/MPEG2的碼流,暫時不支援其它標準。2. H264VISA  比較好的H264碼流分析工具3. Vegas  veg

H.264---率控制策略

maximum details iss 同時 地方 ext .net https 限制 轉自:https://blog.csdn.net/yuanray/article/details/54090014 CBR(Constant Bit Rate)是以恒定比特率方式進行編碼