當前位置:編程學習大全網 - 源碼下載 - H264 視頻流的解析

H264 視頻流的解析

以最流行的H.264編碼為例,對於視頻流的解析要了解視頻幀在RTP包中作為荷載是如何承載和存放的。先回顧壹些基礎的背景知識,再寫壹個小程序來分析RTP 視頻流。

視頻就是壹幅幅圖片以每秒幾十幅的速度播放,這些圖片稱幀(frame), 播放速度稱為幀率(FPS - Frame Per Second)

準確地說,視頻是由壹系列圖像組成的動作序列,並且該序列中的每個圖像都將在要顯示的動作序列的時間軸中接替前壹個圖像。 這些靜止圖像稱為視頻幀。每個視頻幀之間的時間差越小,刷新率就越高,並且視頻中的運動表現得越自然。

現代視頻編碼將這些幀分為三類

信息幀用幀內壓縮,用作關鍵幀

預測幀 Predictive Frame 用幀間壓縮,反映之前的 I-frame 的變化

雙向預測幀 Bidirectional Predictive Frames 使得總體壓縮更高, 它參考了之前的 I-frame 和之後的 P-frame

訪問單元序列,按解碼順序由瞬時解碼刷新 (IDR) 訪問單元和零個或多個非 IDR 訪問單元組成,包括所有後續訪問單元,直到但不包括任何後續 IDR 訪問單元。

主要編碼圖片為 IDR 圖片的訪問單元。

僅包含 I 或 SI 類型的slice 的編碼圖片,這會在解碼過程中導致“重置”。 在對 IDR 圖片進行解碼之後,可以根據在 IDR 圖片之前解碼的任何圖片,在沒有幀間預測的情況下,對按照解碼順序的所有後續編碼圖片進行解碼。

解碼過程使用的圖片的編碼表示,用於符合 H.264 的比特流。

主要編碼圖片包含圖片的所有宏塊。

圖片或其中壹部分的編碼表示。 對符合 H.264 的比特流的解碼過程不應使用冗余編碼圖片的內容。 冗余編碼圖片的內容可由解碼過程用於包含錯誤或損失的比特流。

用於指代編碼切片(slice)和編碼數據分區(data partition) 的 NAL 單元的統稱。

基於以上的概念, H.264 將這些視頻幀進行了分組,這些組稱為 GOP(Group of Picture), 在這些組中的第壹個視頻幀通常都是 I-Frame

以壹個視頻通信的應用程序為例,視頻編碼程序會將采樣的視頻圖片從 RGB 格式轉為 YUV 格式,再將它們打包為 RTP packet ,如下圖所示

讓我們從外而內,看看視頻 RTP 包的結構

針對 H264視頻幀,RTP 頭中的某些字段有如下設置

為 RTP 包中時間戳所指示的訪問單元的最後壹個數據包中設置 marker=1,這樣可以用來進行有效的播放緩沖區處理。

在FU-A中的 marker 設定為只有最後壹包才會設定 marker=1,其它則為 0

根據 RFC 3550 的定義來設置和使用,對於單NALU和非交錯打包模式,序列號用於確定NALU的解碼順序。

RTP 時間戳設置為視頻內容的采樣時間戳, 必須使用 90 kHz 時鐘頻率。

例如 H264的采樣率為 90khz, 幀率 frame rate =15 那麽每個包的時間戳 Timestamp 的步長約為 90000/15 = 6000

RTP 包的荷載中包含 H.264 中的視頻流內容,也就是 NAL 網絡抽象層

NAL Unit 是 header 和 playload 組成的

NAL Unit Header 就是壹個字節,格式如下:

forbidden_zero_bit: 壹個比特, H.264 規範將值 1 聲明為語法違反規範。

NRI 兩個比特,即 nal_ref_idc 稱 NAL 參考索引

值 00 表示 NAL 單元的內容不用於重建用於圖片間預測的參考圖片,此類 NAL 單元可以丟棄,而不會危及參考圖片的完整性。

大於 00 的值表示需要對 NAL 單元進行解碼以保持參考圖片的完整性。

例如:

H.264 規定如下的 NAL unit type , 其 NRI 必須為 0

NAL 類型以 5 個比特來表示

為適合於通過 RTP 協議在網絡上傳輸, H264 的視頻包大體上分為三種:

在 RFC6184 中有如下規定

在視頻會議中,壹般打包模式選擇為非交錯模式,會使用下面三種單元,不使用 STAP-B, FU-B 和 MTAP 單元

如果某幀較大不能單獨打包,但是該幀內部單獨的 NALU 比較小,可以使用STAP 方式合並多個NALU打包發送,但是這些 NALU 的時間戳必須壹致,打包後的 RTP 時間戳也必須壹致,壹個 STAP 單元包含多個子單元,每個子單元之前會有壹個 NAL unit size 來指明這個子單元的長度, 這裏只以 STAP-A 為例, STAP-B 也就是多了壹個 DON(Decoding Order Number)

例如下面的例子

如果壹個視頻幀的 NALU 過大(超過MTU)需要拆分成多個包,可以使用 FU 方式來拆分並打到不同的RTP包裏,那麽這幾個包的RTP時間戳是壹樣的;

以 FU-A 為例 (FU-B 的區別就是多了壹個 DON), 如圖所示

其中 FU indicator 字節的格式如下

它就是壹個普通的 NAL header, 只是它的 type 等於 28(FU-A) 或 29 (FU-B)

其後的 FU header 字節的格式如下

含義如下

當設置為 1 時,Start 位指示分片 NAL 單元的開始。 當隨後的 FU 有效載荷不是分片 NAL 單元有效載荷的開始時,起始位設置為零。

當設置為 1 時,End 位表示分片 NAL 單元的結束,即有效載荷的最後壹個字節也是分片 NAL 單元的最後壹個字節。 當隨後的 FU 有效載荷不是分片 NAL 單元的最後壹個片段時,結束位設置為零。

實際被分片的 NAL 單元的類型,參見 RFC6184 的 Table 7-1

在 /cisco/openh264 中有相關結構和類型的詳細定義, 至於聚合包和分片包,openh264 中本身並未定義

在 webrtc 的 video_coding 中有相關的定義

third_party/webrtc/modules/video_coding/codecs/h264/include/h264_globals.h

我自己也寫了壹個小例子,演示如下

其中用到的壹個工具類 rtputil 源碼就不貼了,參見

編譯運行

其中用到的壹個工具類 MediaUtil 源碼就不貼了,參見

編譯運行

運行結果如下,

  • 上一篇:JAVA編程;定義兩個整型數組a和b,並分別填入值。使用Arrays類的常用方法,完成以下操作:
  • 下一篇:橡皮擦中文源代碼
  • copyright 2024編程學習大全網