文章-原址html
公司項目緣由,接觸了一下視頻流H264的編解碼知識,以前項目使用的是FFMpeg多媒體庫,利用CPU作視頻的編碼和解碼,俗稱爲軟編軟解。該方法比較通用,可是佔用CPU資源,編解碼效率不高。通常系統都會提供GPU或者專用處理器來對視頻流進行編解碼,也就是硬件編碼和解碼,簡稱爲硬編解碼。蘋果在iOS 8.0系統以前,沒有開放系統的硬件編碼解碼功能,不過Mac OS系統一直有,被稱爲Video ToolBox的框架來處理硬件的編碼和解碼,終於在iOS 8.0後,蘋果將該框架引入iOS系統。網絡
由此,開發者即可以在iOS裏面,調用Video Toolbox框架提供的接口,來對視頻進行硬件編解碼的工做,爲VOIP視頻通話,視頻流播放等應用的視頻編解碼提供了便利。session
(PS:按照蘋果WWDC2014 513《direct access to media encoding and decoding》的描述,蘋果以前提供的AVFoundation框架也使用硬件對視頻進行硬編碼和解碼,可是編碼後直接寫入文件,解碼後直接顯示。Video Toolbox框架能夠獲得編碼後的幀結構,也能夠獲得解碼後的原始圖像,所以具備更大的靈活性作一些視頻圖像處理。)數據結構
一,VideoToolbox基本數據結構。框架
Video Toolbox視頻編解碼先後須要應用的數據結構進行說明。iphone
(1)CVPixelBuffer:編碼前和解碼後的圖像數據結構。ide
(2)CMTime、CMClock和CMTimebase:時間戳相關。時間以64-bit/32-bit的形式出現。函數
(3)CMBlockBuffer:編碼後,結果圖像的數據結構。post
(4)CMVideoFormatDescription:圖像存儲方式,編解碼器等格式描述。測試
(5)CMSampleBuffer:存放編解碼先後的視頻圖像的容器數據結構。
圖1.1視頻H264編解碼先後數據結構示意圖
如圖1.1所示,編解碼先後的視頻圖像均封裝在CMSampleBuffer中,若是是編碼後的圖像,以CMBlockBuffe方式存儲;解碼後的圖像,以CVPixelBuffer存儲。CMSampleBuffer裏面還有另外的時間信息CMTime和視頻描述信息CMVideoFormatDesc。
二,硬解碼使用方法。
經過如圖2.1所示的一個典型應用,來講明如何使用硬件解碼接口。該應用場景是從網絡處傳來H264編碼後的視頻碼流,最後顯示在手機屏幕上。
圖2.1 H264典型應用場景
1,將H264碼流轉換成解碼前的CMSampleBuffer。
由圖1.1所示,解碼前的CMSampleBuffer = CMTime + FormatDesc + CMBlockBuffer。須要從H264的碼流裏面提取出以上的三個信息。最後組合成CMSampleBuffer,提供給硬解碼接口來進行解碼工做。
H264的碼流由NALU單元組成,NALU單元包含視頻圖像數據和H264的參數信息。其中視頻圖像數據就是CMBlockBuffer,而H264的參數信息則能夠組合成FormatDesc。具體來講參數信息包含SPS(Sequence Parameter Set)和PPS(Picture Parameter Set)。圖2.2顯示一個H264碼流的結構。
圖2.2 H264碼流結構
(1)提取sps和pps生成format description。
a,每一個NALU的開始碼是0x00 00 01,按照開始碼定位NALU。
b,經過類型信息找到sps和pps並提取,開始碼後第一個byte的後5位,7表明sps,8表明pps。
c,CMVideoFormatDescriptionCreateFromH264ParameterSets函數來構建CMVideoFormatDescriptionRef。具體代碼能夠見demo。
(2)提取視頻圖像數據生成CMBlockBuffer。
a,經過開始碼,定位到NALU。
b,肯定類型爲數據後,將開始碼替換成NALU的長度信息(4 Bytes)。
c,CMBlockBufferCreateWithMemoryBlock接口構造CMBlockBufferRef。具體代碼能夠見demo。
(3)根據須要,生成CMTime信息。(實際測試時,加入time信息後,有不穩定的圖像,不加入time信息反而沒有,須要進一步研究,這裏建議不加入time信息)
根據上述獲得CMVideoFormatDescriptionRef、CMBlockBufferRef和可選的時間信息,使用CMSampleBufferCreate接口獲得CMSampleBuffer數據這個待解碼的原始的數據。見圖2.3的H264數據轉換示意圖。
圖2.3 H264碼流轉換CMSampleBuffer示意圖
2,硬件解碼圖像顯示。
硬件解碼顯示的方式有兩種:
(1)經過系統提供的AVSampleBufferDisplayLayer來解碼並顯示。
AVSampleBufferDisplayLayer是蘋果提供的一個專門顯示編碼後的H264數據的顯示層,它是CALayer的子類,所以使用方式和其它CALayer相似。該層內置了硬件解碼功能,將原始的CMSampleBuffer解碼後的圖像直接顯示在屏幕上面,很是的簡單方便。圖2.4顯示了這一解碼過程。
圖2.4 AVSampleBufferDisplayLayer硬解壓後顯示圖像
顯示的接口爲[_avslayer enqueueSampleBuffer:sampleBuffer];
(2)經過VTDecompression接口來,將CMSampleBuffer解碼成圖像,將圖像經過UIImageView或者OpenGL上顯示。
a,初始化VTDecompressionSession,設置解碼器的相關信息。初始化信息須要CMSampleBuffer裏面的FormatDescription,以及設置解碼後圖像的存儲方式。demo裏面設置的CGBitmap模式,使用RGB方式存放。編碼後的圖像通過解碼後,會調用一個回調函數,將解碼後的圖像交個這個回調函數來進一步處理。咱們就在這個回調裏面,將解碼後的圖像發給control來顯示,初始化的時候要將回調指針做爲參數傳給create接口函數。最後使用create接口對session來進行初始化。
b,a中所述的回調函數能夠完成CGBitmap圖像轉換成UIImage圖像的處理,將圖像經過隊列發送到Control來進行顯示處理。
c,調用VTDecompresSessionDecodeFrame接口進行解碼操做。解碼後的圖像會交由a,b步驟設置的回調函數,來進一步的處理。
圖2.5顯示來硬解碼的過程步驟。
圖2.5 VTDecompression硬解碼過程示意圖
三,硬編碼使用方法。
硬編碼的使用也經過一個典型的應用場景來描述。首先,經過攝像頭來採集圖像,而後將採集到的圖像,經過硬編碼的方式進行編碼,最後編碼後的數據將其組合成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發送出去。
圖2.6顯示了整個硬編碼的處理邏輯。
圖2.6硬編碼處理流程示意圖
四,硬編解碼的一些編碼說明。
因爲Video Toolbox是基礎的core Foundation庫函數,C語言寫成,和使用core Foundation全部的其它功能同樣須要適應,記得Github有個同志,將其改爲了OC語言能方便調用的模式,可是地址忘了,之後有緣找到,就會提供下連接。
在iOS平臺上作視頻的解碼,通常有三種方案:
一、軟解碼方案:ffmpeg
缺點:消耗CPU太大,在iphone4s上通常720P 20幀以上就解不動了
二、硬解碼方案1:採用私有接口VideoToolBox
優勢:CPU消耗極低,解碼效率極高
缺點:要使用私有接口VideoToolBox,iOS設備必須越獄
三、硬解碼方案2:採用AVPlayer+httpserver+HttpLiveStream的組合方案
優勢:CPU消耗極低,解碼效率極高
缺點:視頻有延遲,不適合實時視頻通信
這裏給出硬解碼方案2的流程圖:
該方案本人已源代碼實現,並驗證了穩定性,在iphone5上720P 25幀CPU佔用率3%;
具體實現源代碼暫時不開源,若須要,可聯繫我,QQ:349260360 Email:manshilingkai@163.com
糾錯:採用AVPlayer,ts流分片在切換的時候會閃屏,要實現ts流切片的無縫對接,必須採用AVQueuePlayer,這個具體方案還需完善。。。
在iOS中,攝像頭錄製的視頻是mov格式的,雖然mov兼容mp4,可是有些需求須要用到mp4格式的視頻文件。在翻邊了stackoverflow以後找到了一段轉換視頻格式的代碼。以此記錄一下。
AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:[NSURL fileURLWithPath:path] options:nil]; NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset]; if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) { AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:avAsset presetName:AVAssetExportPresetPassthrough]; NSString *exportPath = [NSString stringWithFormat:@"%@/%@.mp4", [NSHomeDirectory() stringByAppendingString:@"/tmp"], @"1"]; exportSession.outputURL = [NSURL fileURLWithPath:exportPath]; NSLog(@"%@", exportPath); exportSession.outputFileType = AVFileTypeMPEG4; [exportSession exportAsynchronouslyWithCompletionHandler:^{ switch ([exportSession status]) { case AVAssetExportSessionStatusFailed: NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]); break; case AVAssetExportSessionStatusCancelled: NSLog(@"Export canceled"); break; case AVAssetExportSessionStatusCompleted: NSLog(@"轉換成功"); break; default: break; } }]; }