天天看點

H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的

        視訊有很多種編碼标準,H.264,H.265,AV1等等,其中我們可能最常見的便是H.264,是以,本文我們就主要來分下下H.264編碼的碼流結果具體是怎麼樣的。

一、前置知識

        了解H264碼流結構之前,需要先了解一些前置的知識。

1、I/P/B幀和GOP

        需要先了解圖像序列的層次結構,也就是一幀幀視訊幀的一些概念。這些可以參考我前面的一篇文章 視訊相關的一些基本概念 ,會比較系統和具體地講解。

2、Slice

        上面主要是圖像序列的層次結構,而圖像的内部層次結構是怎麼樣的,這主要涉及Slice這個概念。

        Slice其實就是“片”的概念,圖像内的層次結構就是一幀圖像可以劃分成一個或多個 Slice,而一個 Slice 包含多個宏塊,且一個宏塊又可以劃分成多個不同尺寸的子塊。大概就是類似下面這樣的結構圖:

H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的

二、H264碼流結構

1、碼流格式

        H264 碼流有兩種格式:一種是 Annexb 格式;一種是 MP4 格式。

(1)、Annexb 格式使用起始碼來表示一個編碼資料的開始。起始碼本身不是圖像編碼的内容,隻是用來分隔用的。起始碼有兩種,一種是 4 位元組的“00 00 00 01”,一種是 3 位元組的“00 00 01”。

        由于圖像編碼出來的資料中也有可能出現“00 00 00 01”和“00 00 01”的資料。為了防止出現這種情況,H264 會将圖像編碼資料中的下面的幾種位元組串做如下處理:

        “00 00 00”修改為“00 00 03 00”;

        “00 00 01”修改為“00 00 03 01”;

        “00 00 02”修改為“00 00 03 02”;

        “00 00 03”修改為“00 00 03 03”。

        其實也就是轉義,同樣地在解碼端,我們在去掉起始碼之後,也需要将對應的位元組串轉換回來。

H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的

 (2)、MP4 格式沒有起始碼,而是在圖像編碼資料的開始使用了 4 個位元組作為長度辨別,用來表示編碼資料的長度,這樣我們每次讀取 4 個位元組,計算出編碼資料長度,然後取出編碼資料,再繼續讀取 4 個位元組得到長度,一直繼續下去就可以取出所有的編碼資料了。

H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的

         這兩種格式差别不大,接下來我們主要使用 Annexb 格式來講解 H264 碼流結構。

2、SPS和PPS

        視訊編碼的時候還有一些編碼參數資料的,為了能夠将一些通用的編碼參數提取出來,不在圖像編碼資料中重複,H264 設計了兩個重要的參數集:一個是 SPS(序列參數集);一個是 PPS(圖像參數集)。

        SPS 主要包含的是圖像的寬、高、YUV 格式和位深等基本資訊;PPS 則主要包含熵編碼類型、基礎 QP 和最大參考幀數量等基本編碼資訊。

        如果沒有 SPS、PPS 裡面的基礎資訊,之後的 I 幀、P 幀、B 幀就都沒辦法進行解碼。是以 SPS 和 PPS 是至關重要的。

        這樣的話,H264碼流主要包含了 SPS、PPS、I 幀、P 幀和 B 幀。由于幀又可以劃分成一個或多個 Slice。是以,幀在碼流中實際上是以 Slice 的形式呈現的。是以,H264 的碼流主要是由 SPS、PPS、I Slice、P Slice和B Slice 組成的。如下圖所示:

H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的

 3、NALU

        上面說到H264碼流的組成部分,但是每個部分是如何區分開?為了解決這個問題,H264 設計了 NALU(網絡抽象層單元)。SPS 是一個 NALU、PPS 是一個 NALU、每一個 Slice 也是一個 NALU。每一個 NALU 又都是由一個 1 位元組的 NALU Header 和若幹位元組的 NALU Data 組成的。而對于每一個 Slice NALU,其 NALU Data 又是由 Slice Header 和 Slice Data 組成,并且 Slice Data 又是由一個個 MB Data 組成。其結構如下:

H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的

        其中,NALU Header總共占用 1 個位元組,具體如下圖所示。

H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的

        其中,

        --> F:forbidden_zero_bit,占 1bit,禁止位,H264 碼流必須為 0;

        --> NRI: nal_ref_idc,占 2bits,可以取 00~11,表示目前 NALU 的重要性。參考幀、SPS 和 PPS 對應的 NALU 必須要大于 0;

        --> Type: nal_unit_type,占 5bits,表示 NALU 類型。其取值如下表所示。

H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的

         有了這個,我們解析出 NALU Header 的 Type 字段,查詢表格就可以得到哪個 NALU 是 SPS,哪個是 PPS,以及哪個是 IDR 幀了。

        不過NALU 類型隻區分了 IDR Slice 和非 IDR Slice,至于非 IDR Slice 是普通 I Slice、P Slice 還是 B Slice,則需要繼續解析 Slice Header 中的 Slice Type 字段得到。

        下面我們通過兩個例子來看看常見的 NALU 裡的 NALU Header 是怎樣的:

H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的

         下面我們再來看一個實際碼流的例子,看看在實際編碼出來的二進制資料中,各種 NALU 是怎麼“放置”在資料中的。下圖是用二進制檢視工具打開實際編碼後的碼流資料。我們可以看到在碼流的開始部分是一個起始碼,之後緊接着是一個 SPS 的 NALU。在 SPS 後面是一個 PPS 的 NALU。然後就是一個 IDR Slice 的 NALU 和一個非 IDR Slice NALU。

H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的
H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的

 三、如何判斷哪幾個 Slice 是同一幀的

         根據上面的分析,在H264 碼流中,幀是以 Slice 的方式呈現的,或者可以說在 H264 碼流裡是沒有“幀“這種資料的,隻有 Slice。

        那麼有個問題,一幀有幾個 Slice 是不知道的。也就是說碼流中沒有字段表示一幀包含幾個 Slice。既然沒有辦法知道一幀有幾個 Slice,那我們如何知道多 Slice 編碼時一幀的開始和結束分别對應哪個 Slice 呢?

        其實,Slice NALU 由 NALU Header 和 NALU Data 組成,其中 NALU Data 裡面就是 Slice 資料,而 Slice 資料又是由 Slice Header 和 Slice Data 組成。在 Slice Header 開始的地方有一個 first_mb_in_slice 的字段,表示目前 Slice 的第一個宏塊 MB 在目前編碼圖像中的序号。我們隻要解析出這個宏塊的序号出來。

        --> 如果 first_mb_in_slice 的值等于 0,就代表了目前 Slice 的第一個宏塊是一幀的第一個宏塊,也就是說目前 Slice 就是一幀的第一個 Slice。

        --> 如果 first_mb_in_slice 的值不等于 0,就代表了目前 Slice 不是一幀的第一個 Slice。并且,使用同樣的方式一直往下找,直到找到下一個 first_mb_in_slice 為 0 的 Slice,就代表新的一幀的開始,那麼其前一個 Slice 就是前一幀的最後一個 Slice 了。

H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的

         其中,first_mb_in_slice 是以無符号指數哥倫布編碼的,需要使用對應的解碼方式才能解碼出來。但是有一個小技巧,如果隻是需要判斷 first_mb_in_slice 是不是等于 0,不需要計算出實際值的話,隻需要通過下面的方式計算就可以了。

H.264碼流結構是怎麼樣的一、前置知識二、H264碼流結構 三、如何判斷哪幾個 Slice 是同一幀的

         以上便是對H264碼流結構的講解。

繼續閱讀