iOS硬編解碼相關知識

一、軟編與硬編概念

1、軟編碼:使用CPU進行編碼。

實現直接、簡單,參數調整方便,升級易,但CPU負載重,性能較硬編碼低,低碼率下質量通常比硬編碼要好一點。

2、硬編碼:不使用CPU進行編碼,使用顯卡GPU,專用的DSP、FPGA、ASIC芯片等硬件進行編碼。

性能高,低碼率下通常質量低于軟編碼器,但部分產品在GPU硬件平臺移植了優秀的軟編碼算法(如X264)的,質量基本等同于軟編碼。

蘋果在iOS 8.0系統之前,沒有開放系統的硬件編碼解碼功能,不過Mac OS系統一直有,被稱為Video ToolBox的框架來處理硬件的編碼和解碼,終于在iOS 8.0(即WWDC 2014 513)后,蘋果將該框架引入iOS系統。

二、H.264編碼原理

H.264是新一代的編碼標準,以高壓縮高質量和支持多種網絡的流媒體傳輸著稱,在編碼方面,我理解的理論依據是:參照一段時間內圖像的統計結果表明,在相鄰幾幅圖像畫面中,一般有差別的像素只有10%以內的點,亮度差值變化不超過2%,而色度差值的變化只有1%以內。所以對于一段變化不大圖像畫面,我們可以先編碼出一個完整的圖像幀A,隨后的B幀就不編碼全部圖像,只寫入與A幀的差別,這樣B幀的大小就只有完整幀的1/10或更小!B幀之后的C幀如果變化不大,我們可以繼續以參考B的方式編碼C幀,這樣循環下去。這段圖像我們稱為一個序列(序列就是有相同特點的一段數據),當某個圖像與之前的圖像變化很大,無法參考前面的幀來生成,那我們就結束上一個序列,開始下一段序列,也就是對這個圖像生成一個完整幀A1,隨后的圖像就參考A1生成,只寫入與A1的差別內容。

需要注意的是:

在H264協議里定義了三種幀,完整編碼的幀叫I幀,參考之前的I幀生成的只包含差異部分編碼的幀叫P幀,還有一種參考前后的幀編碼的幀叫B幀。

H264采用的核心算法是幀內壓縮和幀間壓縮,幀內壓縮是生成I幀的算法,幀間壓縮是生成B幀和P幀的算法。

三、序列的說明

在H264中圖像以序列為單位進行組織,一個序列是一段圖像編碼后的數據流,以I幀開始,到下一個I幀結束。

一個序列的第一個圖像叫做 IDR 圖像(立即刷新圖像),IDR 圖像都是 I 幀圖像。H.264 引入 IDR 圖像是為了解碼的重同步,當解碼器解碼到 IDR 圖像時,立即將參考幀隊列清空,將已解碼的數據全部輸出或拋棄,重新查找參數集,開始一個新的序列。這樣,如果前一個序列出現重大錯誤,在這里可以獲得重新同步的機會。IDR圖像之后的圖像永遠不會使用IDR之前的圖像的數據來解碼。

一個序列就是一段內容差異不太大的圖像編碼后生成的一串數據流。當運動變化比較少時,一個序列可以很長,因為運動變化少就代表圖像畫面的內容變動很小,所以就可以編一個I幀,然后一直P幀、B幀了。當運動變化多時,可能一個序列就比較短了,比如就包含一個I幀和3、4個P幀。

四、對三種幀的簡單介紹

I、B、P各幀是根據壓縮算法的需要,是人為定義的,它們都是實實在在的物理幀。一般來說,I幀的壓縮率是7(跟JPG差不多),P幀是20,B幀可以達到50。可見使用B幀能節省大量空間,節省出來的空間可以用來保存多一些I幀,這樣在相同碼率下,可以提供更好的畫質。

B-012.png

說明:

I幀:紅色;P幀:藍色;B幀:綠色。

五、H264壓縮算法的說明

1、分組:把幾幀圖像分為一組(GOP,也就是一個序列),為防止運動變化,幀數不宜取多。

2、定義幀:將每組內各幀圖像定義為三種類型,即I幀、B幀和P幀;

3、預測幀:以I幀做為基礎幀,以I幀預測P幀,再由I幀和P幀預測B幀;

4、數據傳輸:最后將I幀數據與預測的差值信息進行存儲和傳輸。

5、幀內(Intraframe)壓縮也稱為空間壓縮(Spatial compression)。

當壓縮一幀圖像時,僅考慮本幀的數據而不考慮相鄰幀之間的冗余信息,這實際上與靜態圖像壓縮類似。幀內一般采用有損壓縮算法,由于幀內壓縮是編碼一個完整的圖像,所以可以獨立的解碼、顯示。幀內壓縮一般達不到很高的壓縮,跟編碼jpeg差不多。

6、幀間(Interframe)壓縮。

相鄰幾幀的數據有很大的相關性,或者說前后兩幀信息變化很小的特點。也即連續的視頻其相鄰幀之間具有冗余信息,根據這一特性,壓縮相鄰幀之間的冗余量就可以進一步提高壓縮量,減小壓縮比。幀間壓縮也稱為時間壓縮(Temporal compression),它通過比較時間軸上不同幀之間的數據進行壓縮。幀間壓縮一般是無損的。幀差值(Frame differencing)算法是一種典型的時間壓縮法,它通過比較本幀與相鄰幀之間的差異,僅記錄本幀與其相鄰幀的差值,這樣可以大大減少數據量。

7、有損(Lossy)壓縮和無損(Lossy less)壓縮。

無損壓縮也即壓縮前和解壓縮后的數據完全一致。多數的無損壓縮都采用RLE行程編碼算法。

有損壓縮意味著解壓縮后的數據與壓縮前的數據不一致。在壓縮的過程中要丟失一些人眼和人耳所不敏感的圖像或音頻信息,而且丟失的信息不可恢復。幾乎所有高壓縮的算法都采用有損壓縮,這樣才能達到低數據率的目標。丟失的數據率與壓縮比有關,壓縮比越小,丟失的數據越多,解壓縮后的效果一般越差。此外,某些有損壓縮算法采用多次重復壓縮的方式,這樣還會引起額外的數據丟失。

六、DTS與PTS的區別

DTS主要用于視頻的解碼,在解碼階段使用.

PTS主要用于視頻的同步和輸出.

在display的時候使用.在沒有B frame的情況下.DTS和PTS的輸出順序是一樣的。

下面給出一個GOP為15的例子,其解碼的參照frame及其解碼的順序都在里面:

B-001.png

如上圖:

I frame 的解碼不依賴于任何的其它的幀.而p frame的解碼則依賴于其前面的I frame或者P frame.B frame的解碼則依賴于其前的最近的一個I frame或者P frame 及其后的最近的一個P frame.

七、iOS系統 H.264視頻硬件編解碼說明

1、VideoToolbox的介紹

在iOS中,與視頻相關的Framework庫有5個,從頂層開始分別是 AVKit -> AVFoundation -> VideoToolbox -> Core Media -> Core Video

其中VideoToolbox可以將視頻解壓到CVPixelBuffer,也可以壓縮到CMSampleBuffer
但是我們常用的是CMSampleBuffer.

2、VideoToolbox中的對象

1)CVPixelBuffer

編碼前和解碼后的圖像數據結構(未壓縮光柵圖像緩存區-Uncompressed Raster Image Buffer)

B-002.png

2)CVPixelBufferPool

存放CVPixelBuffer

B-003.png

3)pixelBufferAttributes

CFDictionary對象,可能包含了視頻的寬高,像素格式類型(32RGBA, YCbCr420),是否可以用于OpenGL ES等相關信息

4)CMTime

時間戳相關。時間以 64-big/32-bit形式出現。 分子是64-bit的時間值,分母是32-bit的時標(time scale)

5)CMClock

時間戳相關。時間以 64-big/32-bit形式出現。 分子是64-bit的時間值,分母是32-bit的時標(time scale)。它封裝了時間源,其中CMClockGetHostTimeClock()封裝了mach_absolute_time()

6)CMTimebase

時間戳相關。時間以 64-big/32-bit形式出現。CMClock上的控制視圖。提供了時間的映射:CMTimebaseSetTime(timebase, kCMTimeZero); 速率控制:
CMTimebaseSetRate(timebase, 1.0);

7)CMBlockBuffer

編碼后,結果圖像的數據結構

8)CMVideoFormatDescription

編解碼前后的視頻圖像均封裝在CMSampleBuffer中,如果是編碼后的圖像,以CMBlockBuffe方式存儲;解碼后的圖像,以CVPixelBuffer存儲。

9)CMSampleBuffer

存放編解碼前后的視頻圖像的容器數據結構。如圖所示,編解碼前后的視頻圖像均封裝在CMSampleBuffer中,如果是編碼后的圖像,以CMBlockBuffer方式存儲;解碼后的圖像,以CVPixelBuffer存儲。CMSampleBuffer里面還有另外的時間信息CMTime和視頻描述信息CMVideoFormatDesc。

B-004.png

八、硬解碼

目標:如何將從網絡處傳來H.264編碼后的視頻碼流顯示在手機屏幕上?

實現步驟如下:

1、將 H.264碼流轉換為 CMSampleBuffer

CMSampleBuffer = CMTime + FormatDesc + CMBlockBuffer

需要從H.264的碼流里面提取出以上的三個信息。最后組合成CMSampleBuffer,提供給硬解碼接口來進行解碼工作。

在H.264的語法中,有一個最基礎的層,叫做Network Abstraction Layer, 簡稱為NAL。H.264流數據正是由一系列的NAL單元(NAL Unit, 簡稱NAUL)組成的。

B-005.png

H264的碼流由NALU單元組成,一個NALU可能包含有:

  1. 視頻幀,視頻幀也就是視頻片段,具體有 P幀, I幀,B幀


    B-006.png

2)H.264屬性合集-FormatDesc(包含 SPS和PPS),即流數據中,屬性集合可能是這樣的:


B-007.png

經過處理之后,在Format Description中則是:

B-008.png

需要注意的是:

要從基礎的流數據將SPS和PPS轉化為Format Desc中的話,需要調用CMVideoFormatDescriptionCreateFromH264ParameterSets()方法。

3)NALU header

對于流數據來說,一個NAUL的Header中,可能是0x00 00 01或者是0x00 00 00 01作為開頭(兩者都有可能,下面以0x00 00 01作為例子)。0x00 00 01因此被稱為開始碼(Start code).


B-009.png

總結以上知識,我們知道H264的碼流由NALU單元組成,NALU單元包含視頻圖像數據和H264的參數信息。其中視頻圖像數據就是CMBlockBuffer,而H264的參數信息則可以組合成FormatDesc。具體來說參數信息包含SPS(Sequence Parameter Set)和PPS(Picture Parameter Set).如下圖顯示了一個H.264碼流結構:

B-010.png
(1)提取sps和pps生成FormatDesc
  • 每個NALU的開始碼是0x00 00 01,按照開始碼定位NALU
  • 通過類型信息找到sps和pps并提取,開始碼后第一個byte的后5位,7代表sps,8代表pps
  • 使用CMVideoFormatDescriptionCreateFromH264ParameterSets函數來構建CMVideoFormatDescriptionRef
(2)提取視頻圖像數據生成CMBlockBuffer
  • 通過開始碼,定位到NALU
  • 確定類型為數據后,將開始碼替換成NALU的長度信息(4 Bytes)
  • 使用CMBlockBufferCreateWithMemoryBlock接口構造CMBlockBufferRef
(3)根據需要,生成CMTime信息。

(實際測試時,加入time信息后,有不穩定的圖像,不加入time信息反而沒有,需要進一步研究,這里建議不加入time信息)

根據上述得到CMVideoFormatDescriptionRef、CMBlockBufferRef和可選的時間信息,使用CMSampleBufferCreate接口得到CMSampleBuffer數據這個待解碼的原始的數據。如下圖所示的H264數據轉換示意圖。

B-011.png

2、將 CMSampleBuffer顯示出來

顯示的方式有兩種:

1)、將CMSampleBuffers提供給系統的AVSampleBufferDisplayLayer 直接顯示

使用方式和其它CALayer類似。該層內置了硬件解碼功能,將原始的CMSampleBuffer解碼后的圖像直接顯示在屏幕上面,非常的簡單方便。

2)、利用OPenGL渲染

通過VTDecompression接口來,將CMSampleBuffer解碼成圖像,將圖像通過UIImageView或者OpenGL上顯示。

初始化VTDecompressionSession,設置解碼器的相關信息。初始化信息需要CMSampleBuffer里面的FormatDescription,以及設置解碼后圖像的存儲方式。demo里面設置的CGBitmap模式,使用RGB方式存放。編碼后的圖像經過解碼后,會調用一個回調函數,將解碼后的圖像交個這個回調函數來進一步處理。我們就在這個回調里面,將解碼后的圖像發給control來顯示,初始化的時候要將回調指針作為參數傳給create接口函數。最后使用create接口對session來進行初始化。

上所述的回調函數可以完成CGBitmap圖像轉換成UIImage圖像的處理,將圖像通過隊列發送到Control來進行顯示處理。

調用VTDecompresSessionDecodeFrame接口進行解碼操作。解碼后的圖像會交由以上兩步驟設置的回調函數,來進一步的處理。

九、硬解碼

硬編碼的使用也通過一個典型的應用場景來描述。首先,通過攝像頭來采集圖像,然后將采集到的圖像,通過硬編碼的方式進行編碼,最后編碼后的數據將其組合成H264的碼流通過網絡傳播。

1、攝像頭采集數據

攝像頭采集,iOS系統提供了AVCaptureSession來采集攝像頭的圖像數據。設定好session的采集解析度。再設定好input和output即可。output設定的時候,需要設置delegate和輸出隊列。在delegate方法,處理采集好的圖像。

圖像輸出的格式,是未編碼的CMSampleBuffer形式。

2、使用VTCompressionSession進行硬編碼

1)初始化VTCompressionSession

VTCompressionSession初始化的時候,一般需要給出width寬,height長,編碼器類型kCMVideoCodecType_H264等。然后通過調用VTSessionSetProperty接口設置幀率等屬性,demo里面提供了一些設置參考,測試的時候發現幾乎沒有什么影響,可能需要進一步調試。最后需要設定一個回調函數,這個回調是視頻圖像編碼成功后調用。全部準備好后,使用VTCompressionSessionCreate創建session

2)提取攝像頭采集的原始圖像數據給VTCompressionSession來硬編碼

攝像頭采集后的圖像是未編碼的CMSampleBuffer形式,利用給定的接口函數CMSampleBufferGetImageBuffer從中提取出CVPixelBufferRef,使用硬編碼接口VTCompressionSessionEncodeFrame來對該幀進行硬編碼,編碼成功后,會自動調用session初始化時設置的回調函數。

3)利用回調函數,將因編碼成功的CMSampleBuffer轉換成H264碼流,通過網絡傳播。

基本上是硬解碼的一個逆過程。解析出參數集SPS和PPS,加上開始碼后組裝成NALU。提取出視頻數據,將長度碼轉換成開始碼,組長成NALU。將NALU發送出去。


本文主要為轉載學習,部分細節有刪改。


相關資料傳送:

iOS8系統H264視頻硬件編解碼說明

簡單談談硬編碼和軟編碼

I,P,B幀和PTS,DTS的關系

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,836評論 6 540
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,275評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,904評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,633評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,368評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,736評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,740評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,919評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,481評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,235評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,427評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,968評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,656評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,055評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,348評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,160評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,380評論 2 379

推薦閱讀更多精彩內容