1. 程式人生 > >H.264中的SPS和PPS

H.264中的SPS和PPS

H.264碼流第一個 NALU是 SPS(序列引數集Sequence Parameter Set)

對應H264標準文件 7.3.2.1 序列引數集的語法進行解析

H.264碼流第二個 NALU是 PPS(影象引數集Picture Parameter Set)

對應H264標準文件 7.3.2.2 序列引數集的語法進行解析

H.264碼流第三個 NALU 是 IDR(即時解碼器重新整理)

對應H264標準文件 7.3.3 序列引數集的語法進行解析

H2.64中I幀和IDR幀的區別

   I和IDR幀都是使用幀內預測的。它們都是同一個東西而已,在編碼和解碼中為了方便,要首個I幀和其他I幀區別開,所以才把第一個首個I幀叫IDR,這樣就方便控制編碼和解碼流程。IDR幀的作用是立刻重新整理,使錯誤不致傳播,從IDR幀開始,重新算一個新的序列開始編碼。而I幀不具有隨機訪問的能力,這個功能是由IDR承擔,IDR會導致DPB(參考幀列表——這是關鍵所在)清空,而I不會。IDR影象一定是I影象,但I影象不一定是IDR影象。一個序列中可以有很多的I影象,I影象之後的影象可以引用I影象之間的影象做運動參考。一個序列中可以有很多的I影象,I影象之後的圖象可以引用I影象之間的影象做運動參考。

  對於IDR幀來說,在IDR幀之後的所有幀都不能引用任何IDR幀之前的幀的內容,與此相反,對於普通的I-幀來說,位於其之後的B-和P-幀可以引用位於普通I-幀之前的I-幀。從隨機存取的視訊流中,播放器永遠可以從一個IDR幀播放,因為在它之後沒有任何幀引用之前的幀。但是,不能在一個沒有IDR幀的視訊中從任意點開始播放,因為後面的幀總是會引用前面的幀。

在分離H.264碼流的時候,直接儲存AVPacket後的檔案可能是不能播放的。

如果視音訊複用格式是TS(MPEG2 Transport Stream),直接儲存後的檔案是可以播放的。

複用格式是FLV,MP4則不行。

經過長時間資料搜尋發現,FLV,MP4這些屬於“特殊容器”,需要經過以下處理才能得到可播放的H.264碼流:

1.分離某些封裝格式(例如MP4/FLV/MKV等)中的H.264的時候,需要首先寫入SPS和PPS,否則會導致分離出來的資料沒有SPS、PPS而無法播放。H.264碼流的SPS和PPS資訊儲存在AVCodecContext結構體的extradata中。需要使用ffmpeg中名稱為“h264_mp4toannexb”的bitstream filter(位元流過濾器)處理。有兩種處理方式:

(1)使用bitstream filter處理每個AVPacket(簡單)

把每個AVPacket中的資料(data欄位)經過bitstream filter“過濾”一遍。關鍵函式是av_bitstream_filter_filter()。示例程式碼如下

上述程式碼中,把av_bitstream_filter_filter()的輸入資料和輸出資料(分別對應第4,5,6,7個引數)都設定成AVPacket的data欄位就可以了。

需要注意的是bitstream filter需要初始化和銷燬,分別通過函式av_bitstream_filter_init()和av_bitstream_filter_close()。

經過上述程式碼處理之後,AVPacket中的資料有如下變化:

*每個AVPacket的data添加了H.264的NALU的起始碼{0,0,0,1}

*每個IDR幀資料前面添加了SPS和PPS

(2)手工新增SPS,PPS(稍微複雜)

將AVCodecContext的extradata資料經過bitstream filter處理之後得到SPS、PPS,拷貝至每個IDR幀之前。下面程式碼示例了寫入SPS、PPS的過程。通過檢視FFMPEG原始碼我們發現,AVPacket中的資料起始處沒有分隔符(0x00000001), 也不是0x65、0x67、0x68、0x41等位元組,所以可以AVPacket肯定這不是標準的nalu。其實,AVPacket前4個字表示的是nalu的長度,從第5個位元組開始才是nalu的資料。所以直接將AVPacket前4個位元組替換為0x00000001即可得到標準的nalu資料。

具體程式碼如下:

拷貝4個位元組(0x00000001

當封裝格式為MPEG2TS的時候,不存在上述問題。

2、SPS和PPS

SDP中的H.264的SPS和PPS串,包含了初始化H.264解碼器所需要的資訊引數,包括編碼所用的profile,level,影象的寬和高,deblock濾波器等。

3、相關函式介紹

(1)av_bitstream_filter_init

輸入引數:位元流過濾器的名字

輸出引數:根據位元流過濾器的建立並初始化一個位元流過濾器的上下文。

(2)av_bitstream_filter_filter

引數1:位元流過濾器的上下文

引數2:avcodeccontext輸出流的編碼器上下文.。

引數3:指定篩選器配置的引數,可能為空. 

引數4:指標被更新以指向過濾緩衝區. 

引數5:指標以位元組形式更新到已過濾的緩衝區大小. 

引數6:包含資料到過濾器的緩衝區 

引數7:在位元組緩衝區大小 

引數8:設定為非零,如果緩衝區對應於一個關鍵幀資料包