FFmpeg開發筆記(三十)解析H.264碼流中的SPS幀和PPS幀

aqi00發表於2024-06-16
《FFmpeg開發實戰:從零基礎到短影片上線》一書的“2.1.1 音影片編碼的發展歷程”介紹了H.26x系列的影片編碼標準,其中H.264至今仍在廣泛使用,無論影片檔案還是網路直播,H.264標準都佔據著可觀的市場份額。

之所以H.264取得了巨大的成功,是因為它提出了一個新概念,把標準框架劃分為兩個層面,分別是影片編碼層(Video Coding Layer,簡稱VCL)和網路抽象層(Network Abstraction Layer,簡稱NAL,也稱網路提取層)。其中影片編碼層專注如何高效地表達影片的資料內容,而網路抽象層負責格式化資料並提供頭資訊,以便影片內容能夠適應各種環境的資料傳輸。
每個影片幀都包含至少一個NAL單元,對於I幀、P幀來說,因為內部資料比較多,所以可能會分為多個NAL單元。各幀的第一個NAL單元以起始碼0x00000001開頭,表示從這裡開始是一個新幀;從第二個NAL單元開始,後繼NAL單元以0x000001開頭,表示其後資料是前面NAL單元的接續。
起始碼往後的一個位元組,代表當前幀的型別,常見的幀型別有下列六種:
0x67,型別值為7,為SPS幀,表示序列引數集。
0x68,型別值為8,為PPS幀,表示影像引數集。
0x65,型別值為5,為IDR幀,即IDR影像,也稱為關鍵幀。
0x41,型別值為1,為SLICE分片,表示P幀。
0x01,型別值為1,為SLICE分片,表示B幀。
0x06,型別值為6,為SEI幀,表示輔助增強資訊。
在上述六種型別的NAL中,前三種是必不可少的,分別詳細說明如下。

一、SPS幀

SPS的全稱是Sequence Paramater Set,中文叫作序列引數集。SPS儲存著影片內容的規格引數,包括影片高度、影片寬度、幀率等等。SPS的詳細格式在H.264標準協議中(文件的7.3.2.1部分)規定,內部各欄位的取值情況如下圖所示。

根據SPS的欄位定義,得到影片寬高的計算式子如下:

width = ((pic_width_in_mbs_minus1 +1)*16) - frame_crop_left_offset*2 - frame_crop_right_offset*2;
height= ((2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 +1) * 16) - (frame_crop_top_offset * 2) - (frame_crop_bottom_offset * 2);

當影片寬度和影片高度均為16的整數倍時,frame_crop_left_offset、frame_crop_right_offset、frame_crop_top_offset、frame_crop_bottom_offset這四個欄位值均為0,且frame_mbs_only_flag欄位值為1。此時影片寬高的計算式子簡化如下:

width = (pic_width_in_mbs_minus1+1)*16;
height = (pic_height_in_map_units_minus1+1)*16;

除了影片寬高,透過SPS內部欄位還能計算影片的幀率,幀率的計算式子如下:

fps = time_scale / num_units_in_tick;

二、PPS幀

PPS的全稱是Picture Paramater Set,中文叫做影像引數集。PPS儲存著影片幀的編碼引數,包括熵編碼模式、切片分割型別、初始量化引數、色度量化引數等等。PPS的詳細格式在H.264標準協議中(文件的7.3.2.2部分)規定,內部各欄位的取值情況如下圖所示。

三、IDR幀

IDR的全稱是Instantaneous Decoding Refresh,中文叫做立即解碼重新整理。IDR一定是I幀,但I幀不一定是IDR。一旦出現IDR,就表示清除前面的序列,並且立刻渲染當前的IDR幀。
在每個H.264流的開頭,都會出現這樣的序列:SPS幀→PPS幀→IDR幀→其餘SLICE,並且SPS、PPS、IDR三種幀必定是搭配出現的,缺一不可,如果少了其中任何一幀,都會導致後續影片流解碼異常。

更多詳細的FFmpeg開發知識參見《FFmpeg開發實戰:從零基礎到短影片上線》一書。

相關文章