iOS 系統中,H.264 視頻流可以硬體解碼嗎? 具體如何實現?


這個問題都問了兩年多了,沒有很好的回答,我最近正好搞定了iOS的硬解碼 H.264,借這個問題來分享下經驗。

其實至少從iPhone4開始,蘋果就是支持硬體解碼了,但是硬解碼API一直是私有API,不開放給開發者使用,只有越獄才能使用,正常的App如果想提交到AppStore是不允許使用私有API的。

從iOS8開始,可能是蘋果想通了,開放了硬解碼和硬編碼API,就是名為 VideoToolbox.framework的API,需要用iOS 8以後才能使用,iOS 7.x上還不行。

這套硬解碼API是幾個純C函數,在任何OC或者 C++代碼里都可以使用。

首先要把 VideoToolbox.framework 添加到工程里,並且包含以下頭文件。

#include &


解碼主要需要以下三個函數

VTDecompressionSessionCreate 創建解碼 session

VTDecompressionSessionDecodeFrame 解碼一個frame

VTDecompressionSessionInvalidate 銷毀解碼 session


首先要創建 decode session,方法如下:

OSStatus status = VTDecompressionSessionCreate(kCFAllocatorDefault,
decoderFormatDescription,
NULL, attrs,
callBackRecord,
deocderSession);

其中 decoderFormatDescription 是 CMVideoFormatDescriptionRef 類型的視頻格式描述,這個需要用H.264的 sps 和 pps數據來創建,調用以下函數創建 decoderFormatDescription

CMVideoFormatDescriptionCreateFromH264ParameterSets

需要注意的是,這裡用的 sps和pps數據是不包含「00 00 00 01」的start code的。


attr是傳遞給decode session的屬性詞典

CFDictionaryRef attrs = NULL;
const void *keys[] = { kCVPixelBufferPixelFormatTypeKey };
// kCVPixelFormatType_420YpCbCr8Planar is YUV420
// kCVPixelFormatType_420YpCbCr8BiPlanarFullRange is NV12
uint32_t v = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
const void *values[] = { CFNumberCreate(NULL, kCFNumberSInt32Type, v) };
attrs = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);

其中重要的屬性就一個,kCVPixelBufferPixelFormatTypeKey,指定解碼後的圖像格式,必須指定成NV12,蘋果的硬解碼器只支持NV12。


callBackRecord 是用來指定回調函數的,解碼器支持非同步模式,解碼後會調用這裡的回調函數。

如果 decoderSession創建成功就可以開始解碼了。

VTDecodeFrameFlags flags = 0;
//kVTDecodeFrame_EnableTemporalProcessing | kVTDecodeFrame_EnableAsynchronousDecompression;
VTDecodeInfoFlags flagOut = 0;
CVPixelBufferRef outputPixelBuffer = NULL;
OSStatus decodeStatus = VTDecompressionSessionDecodeFrame(deocderSession,
sampleBuffer,
flags,
outputPixelBuffer,
flagOut);

其中 flags 用0 表示使用同步解碼,這樣比較簡單。
其中 sampleBuffer是輸入的H.264視頻數據,每次輸入一個frame。
先用CMBlockBufferCreateWithMemoryBlock 從H.264數據創建一個CMBlockBufferRef實例。
然後用 CMSampleBufferCreateReady創建CMSampleBufferRef實例。
這裡要注意的是,傳入的H.264數據需要Mp4風格的,就是開始的四個位元組是數據的長度而不是「00 00 00 01」的start code,四個位元組的長度是big-endian的。
一般來說從 視頻里讀出的數據都是 「00 00 00 01」開頭的,這裡需要自己轉換下。

解碼成功之後,outputPixelBuffer里就是一幀 NV12格式的YUV圖像了。
如果想獲取YUV的數據可以通過

CVPixelBufferLockBaseAddress(outputPixelBuffer, 0);
void *baseAddress = CVPixelBufferGetBaseAddress(outputPixelBuffer);

獲得圖像數據的指針,需要說明baseAddress並不是指向YUV數據,而是指向一個CVPlanarPixelBufferInfo_YCbCrBiPlanar結構體,結構體里記錄了兩個plane的offset和pitch。

但是如果想把視頻播放出來是不需要去讀取YUV數據的,因為CVPixelBufferRef是可以直接轉換成OpenGL的Texture或者UIImage的。

調用CVOpenGLESTextureCacheCreateTextureFromImage,可以直接創建OpenGL Texture


從 CVPixelBufferRef 創建 UIImage

CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
UIImage *uiImage = [UIImage imageWithCIImage:ciImage];

解碼完成後銷毀 decoder session

VTDecompressionSessionInvalidate(deocderSession)

硬解碼的基本流程就是這樣了,如果需要成功解碼播放視頻還需要一些H.264視頻格式,YUV圖像格式,OpenGL等基礎知識。


還是有很多小細節要處理的,無法在這裡一一說明了,有人有問題可以在評論里討論。

從解碼到播放,大約1000行代碼左右,主要是OpenGL渲染的代碼比較多。


蘋果官方的示例代碼:

WWDC - Apple Developer


蘋果的例子下載鏈接實效了,我也找不到那個例子,我自己寫了一個。

stevenyao/iOSHardwareDecoder · GitHub


姚大的demo是基於文件的。實際場景大多是基於流。基於此,我寫了個demo。
H264實時硬體編解碼demo


iOS 支持 H.264 視頻流硬解碼:

HTTP Live Streaming Overview: Frequently Asked Questions | http://developer.apple.com/library/ios/#documentation/networkinginternet/conceptual/streamingmediaguide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html

The protocol specification does not limit the encoder selection. However, the current Apple implementation should interoperate with encoders that produce MPEG-2 Transport Streams containing H.264 video and AAC audio (HE-AAC or AAC-LC). Encoders that are capable of broadcasting the output stream over UDP should also be compatible with the current implementation of the Apple provided segmenter software.

具體實現過程請完整閱讀以上文檔,和其它相關文檔:

HTTP Live Streaming Overview: HTTP Streaming Architecture | http://developer.apple.com/library/ios/#documentation/networkinginternet/conceptual/streamingmediaguide/HTTPStreamingArchitecture/HTTPStreamingArchitecture.html#//apple_ref/doc/uid/TP40008332-CH101-SW3


請問下,我正常解碼I幀沒有什麼問題,但是我這邊會來一種視頻流,裡面沒有I幀,全是sei數據的,之前對於06類型的sei數據沒有進行處理。這種sei數據流,我們是用來顯示無視頻信號的,但是我解不出來,就是黑屏狀態。這種樓主有什麼可以指導的嗎?


GitHub - newOcean/HDLiveStreamingSdk: ios live hardware encoder and decoder
發現一個偷懶的sdk,iOS視頻推流,播放,錄製,拍照,demo。


你好,我想請問一下,如果我要做視頻直播類型的,如何直接解碼傳過來了的H264流而不是解碼文件呢?


使用函數CMVideoFormatDescriptionCreate( ),不可以創建decoderFormatDescription嗎?如果其他codec類型,CMVideoFormatDescriptionCreateFromH264ParameterSets( )就不能用了。


可以發一份完整代碼嗎?正在學習相關內容,謝謝啦。allenchan0311@qq.com


看到你的文章,像找到了新大陸一樣。我也正在學習這方面,如果可以的話,能給我發一份code嗎,謝謝!cx0928@126.com


正在學習這方面的東西,同求代碼,謝謝!!zhu770277@163.com


可以給我發一份完整得代碼嗎?謝謝kuchengxiansheng@163.com


google到VLC的一個videotoolbox的patch
[PATCH 1/2] video chroma: add a Nv12 copy function which outputs I420
[PATCH 2/2] Add VideoToolbox based decoder
但是找了個mp4封裝的視頻,用VLCKit跑了一下,CPU變化不大,反而FPS降了一半,效果比軟解碼還差一大截,不清楚是哪個環節沒搞對。。。


具體流程可以參考github上的一個例子,https://github.com/adison/-VideoToolboxDemo 注意create session和decode session時codecCtx和packet的數據可能會有一些小差別,可以根據具體情況修改
但是對於不同的嵌入式編碼版
有的編碼器的264碼流可以解碼成功,有的編碼器死活解不了
不知道是否蘋果官方存在這方面的限制


@姚冬 按照你說的步驟調用硬體解碼的介面,發現到VTDecompressionSessionDecodeFrame這步就卡著不動了,不知道你是否有碰到這種情況?


可以給我發一份完整得代碼嗎?謝謝2410233206@qq.com


@姚冬,大神,需要你的幫助啊,我解碼出來後,用pixelBuffer創建CIImage和UIImage都提示結果為null,創建失敗。然後我將pixelBuffer用gles顯示,也遇到了圖像無色彩、圖像偏移的情況,你有遇到過類似情況嗎?


可以給我發一份完整得代碼嗎?謝謝mengqin1199@163.com


推薦閱讀:

iOS 8 新增 4000 多個 API,重要的有哪些?
一個IOS程序員,每天其實就坐著,偶爾寫寫代碼,大部分時間就看看文檔,一天8小時工作,為何還是累到爆每天?
下圖中兩種樣式的返回按鈕,視覺上的主要差別在哪?

TAG:iOS | 視頻 | H264 | iOS開發 | 信息技術IT |