鍍金池/ 教程/ iOS/ 視頻工具箱和硬件加速
與四軸無人機(jī)的通訊
在沙盒中編寫腳本
結(jié)構(gòu)體和值類型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動開發(fā)
Collection View 動畫
截圖測試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個(gè)完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動布局工具箱
動畫
為 iOS 7 重新設(shè)計(jì) App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動畫解釋
響應(yīng)式 Android 應(yīng)用
初識 TextKit
客戶端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計(jì)優(yōu)雅的移動游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動畫
常見的后臺實(shí)踐
糟糕的測試
避免濫用單例
數(shù)據(jù)模型和模型對象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計(jì)的藝術(shù)
導(dǎo)航應(yīng)用
線程安全類的設(shè)計(jì)
置換測試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測試
值對象
活動追蹤
依賴注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測試 View Controllers
訪談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場
游戲
調(diào)試核對清單
View Controller 容器
學(xué)無止境
XCTest 測試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動畫
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫支持
Fetch 請求
導(dǎo)入大數(shù)據(jù)集
iOS 開發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語言標(biāo)簽
同步案例學(xué)習(xí)
依賴注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過程

視頻工具箱和硬件加速

在 OS X 和 iOS 設(shè)備上進(jìn)行視頻解碼的過程是比較復(fù)雜的。

為了詳細(xì)了解編解碼過程中所發(fā)生的事情,我們首先要了解一些基本概念。這樣才能去挖掘具體的實(shí)現(xiàn)細(xì)節(jié)。

Mac 硬件加速解碼的簡史

用 CPU 處理視頻的代價(jià)非常昂貴,而且編解碼器也非常復(fù)雜。軟解碼的流行是具有革命意義的。隨著 90 年代早期 QuickTime 1.0 和它的基于 C 語言的 API 的出現(xiàn),你可以播放一個(gè)縮略圖那么大的視頻,每個(gè)像素最多有 32,768 種可能的顏色,它們完全由 CPU 進(jìn)行解碼。在當(dāng)時(shí),只有具有專門的圖形硬件的計(jì)算機(jī)才能播放彩色視頻。

http://wiki.jikexueyuan.com/project/objc/images/23-7.gif" alt="" />

圖片來源

到 20 世紀(jì)末,使用了當(dāng)時(shí)最先進(jìn)的 MPEG-2 視頻解碼器 的 DVD 被引進(jìn)千家萬戶。隨后,蘋果將 DVD 驅(qū)動器加到了 PowerBooks (MacBook 的前身,2006 年停產(chǎn)),但是卻發(fā)現(xiàn)了一個(gè)問題:電腦電池和 G3 PowerPC CPU 的配合不夠高效,充一次電居然沒辦法播放完一整部 DVD 視頻。解決方案是在主板上添加一個(gè)C-Cube 生產(chǎn)的專用解碼芯片,來處理這些繁重的事務(wù)。這款芯片可以在 Wallstreet,Lombard 和 Pismo 三款 PowerBook 上找到,也可以在它們所對應(yīng)的專業(yè)級的桌面電腦上找到。

蘋果從沒有提供公共 API,所以在 2001 年 Mac OS X 問世之前 DVD 只能限制在自帶的應(yīng)用程序中播放。而從 Mac OS X 開始它就不再包含 DVD 播放程序了。

我們所知道的 DVD 播放器是在 OS X 10.1 開始引入的。在此之前,只能使用像 VLC 這樣的第三方軟件在 OS X 上播放 DVD。

到了 21 世紀(jì)初,CPU 和 電池的發(fā)展足以獨(dú)立解碼 MPEG-2,而不再需要一個(gè)專門的芯片 —— 至少只要沒有特別消耗資源的程序運(yùn)行在后臺 (比如 Mail.app 這樣不斷地請求一個(gè)遠(yuǎn)程服務(wù)器的進(jìn)程)。

在 00 年代中期,一個(gè)新生兒出現(xiàn)了,并且占據(jù)了著激光讀取,數(shù)字電視廣播,以及在線視頻編解碼的主導(dǎo)地位:它就是 H.264/AVC/MPEG-4 Part 10。相比前幾代,MPEG 的第四代編解碼技術(shù)有效地降低了帶寬需求。然而這是有代價(jià)的:增加了 CPU 負(fù)擔(dān)以及對專用解碼硬件的需求,特別是在像 iPhone 這樣的嵌入式設(shè)備里這個(gè)要求尤為明顯。若不使用專門的數(shù)字信號處理器 (DSP),電池的壽命會急劇下降,通常會增至 2.5 倍的能耗 —— 哪怕是使用最高效的的軟件解碼技術(shù)。

隨著 QTKit 這個(gè) Cocoa 封裝的 QuickTime API 在 OS X 10.5 Leopard 上的出現(xiàn),蘋果公開了一個(gè)高效率的軟件解碼器。這是用匯編語言深度優(yōu)化的,并且得益于在 WWDC 2008 大會上演示過的 clang 編譯器的開發(fā),這個(gè)解碼器的性能在 OS X 10.6 中得到了進(jìn)一步的提升。蘋果公司當(dāng)時(shí)沒有提到一個(gè)潛在的能力 —— 根據(jù)可用性和解碼配置的兼容性,可以動態(tài)切換到硬件解碼。這個(gè)特性是基于兩個(gè)新引入的私有框架:視頻工具箱 (Video Toolbox) 和視頻解碼加速 (Video Decode Acceleration, VDA)。

2008 年,Adobe 從原來的解碼器 (Sorenson Spark, On2 TrueMotion VP6) 切換到 H.264 上面,并且開始在它的跨平臺播放器上整合硬件加速解碼。對于 OS X,Adobe 的團(tuán)隊(duì)發(fā)現(xiàn)所需要的底層技術(shù)都是存在的,并被部署在基于蘋果的 QTKit 框架的應(yīng)用程序里,但是蘋果不對外開放。蘋果公司在 OS X 10.6.3 上有所改變,蘋果發(fā)布了一個(gè)針對 10.6 SDK 的補(bǔ)丁,里面為 VDA 添加了一個(gè)頭文件。

VDA 里面有什么?這是一個(gè)封裝了 H.264 硬件解碼器的小框架,它可能工作也可能不工作,這取決于它是否能處理所提供的編碼數(shù)據(jù)的輸入緩沖。如果解碼器可以處理上述請求,它將返回解碼的幀,否則會話創(chuàng)建失敗,或者不返回幀而只返回一個(gè)錯(cuò)誤碼。這里有四種不同的錯(cuò)誤狀態(tài) (kVDADecoderHardwareNotSupportedErr, kVDADecoderFormatNotSupportedErr, kVDADecoderConfigurationError, kVDADecoderDecoderFailedErr),哪個(gè)都不是特別詳細(xì)。除去僅有的頭文件,既沒有官方文檔,也沒有備用的解決方案。

快進(jìn)到現(xiàn)在:在 iOS 8 和 OS X 10.8,蘋果開放了完整的視頻工具箱框架。這就像一頭野獸!它可以實(shí)時(shí)編解碼視頻甚至更快。你可以啟用或者禁用硬件加速的視頻解碼,甚至可以中途改變。但是文檔并不那么讓人滿意:這個(gè)框架有 14 個(gè)頭文件,Xcode 文檔中只寫有一句話 “請移步頭文件”

此外,如果沒有實(shí)際的測試,就沒有辦法知道所支持的編解碼器,編解碼配置,或者在任意設(shè)備上可用的解碼比特率。因此,大量的測試和設(shè)備專用的代碼是有必要的。內(nèi)部來看,它和原始的 QuickTime 模塊的實(shí)現(xiàn)很相似。外部 API 在不同操作系統(tǒng)和平臺上是相同的,但實(shí)際可用的特性是不一定的。

A4 到 A6 的 iOS 設(shè)備是支持 H.264/AVC/MPEG-4 Part 10 (until profile 100 and up to level 5.1),MPEG-4 Part 2 和 H.263 的。

A7 加入了對 H.264’s profile 110 的支持,它允許各個(gè)顏色通道從 8 位編碼增加到 10 位,這使更高級別的色彩細(xì)節(jié)成為可能。這個(gè)功能通常用于電視臺或者媒體編輯行業(yè)。

A8 似乎是包含第五代解碼器 HEVC / H.265 的支持,這在支持 FaceTime 的設(shè)備的規(guī)格特性描述中有所記載,但它沒有暴露給第三方應(yīng)用程序。預(yù)計(jì)這會在以后的 iOS 版本中有改變,但是可能僅限于新的設(shè)備。

蘋果在 2013 年夏天發(fā)布了一個(gè) HEVC 開發(fā)的管理職位。

視頻工具箱

什么時(shí)候需要直接訪問視頻工具箱?

蘋果的 SDK 提供了一個(gè)開箱即用的媒體播放器,它可以播放任何 QuickTime 兼容的格式視頻。唯一不能播放的是在 iTunes 商店里購買的受 FairPlay2 這個(gè)數(shù)字版權(quán)管理保護(hù)的內(nèi)容。此外,蘋果的 SDK 包括對蘋果公司的可擴(kuò)展的 HTTP 流媒體協(xié)議 HTTP Live Streaming (HLS) 的支持,這項(xiàng)協(xié)議被用在 iOS 以及 OS X 10.7 及以上的系統(tǒng)中。HLS 通常包括一小塊存儲為 MPEG-TS 格式 的 H.264/AAC 視頻,并且允許客戶端根據(jù)可用的硬件,在不同的版本之間動態(tài)切換。

如果你要在顯示在 iOS 或者 OSX 的內(nèi)容上面覆蓋控件的話,你可以使用默認(rèn)的播放框體。根據(jù)部署的設(shè)備型號和操作系統(tǒng)版本,默認(rèn)的播放器都可以正常運(yùn)行,啟用硬件加速解碼,或者在發(fā)生錯(cuò)誤時(shí)無縫地回滾到軟件解碼。這也適用于像 AVKit 和 AVFoundation 這樣的高層框架。當(dāng)然,它們都是基于視頻工具箱的支持的,不過普通開發(fā)者并不需要知道這些。

然而除去 MP4 之外,還有更多的編碼格式,例如 MKV。也有 Adobe 和微軟開發(fā)的進(jìn)一步可擴(kuò)展的 HTTP 流媒體協(xié)議,比如 DASH 或者 Smooth 流媒體,它們也部署了一套類似的視頻編解碼器,但它們是不同的格式,從本質(zhì)上來說是不同的協(xié)議。視頻工具箱支持自定義格式或協(xié)議是輕而易舉的事情。它接受原始的視頻流作為輸入,而且允許在設(shè)備上對原始的或者已經(jīng)編碼的視頻進(jìn)行編碼。生成的結(jié)果存儲在文件里或者上傳到網(wǎng)絡(luò)流中,以便隨后訪問和進(jìn)一步處理。視頻工具箱是一種優(yōu)雅的來達(dá)到原生解決方案性能的方式。而且它的用法在 WWDC 2014 Session #513, "Direct Access to Video Encoding and Decoding 有描述,還有很基本的代碼

最后再補(bǔ)充一點(diǎn) iOS 設(shè)備上視頻工具箱開發(fā)的知識。它在 iOS 4 上作為一個(gè)私有的框架被引入,在最近的 iOS 8 開放給開發(fā)者。建立 target 低于 8.0 的項(xiàng)目時(shí),導(dǎo)入視頻工具箱不會有任何問題,因?yàn)閷?shí)際的 API 大部分沒有改變。然而,任何新建會話的請求將會因?yàn)闊o文檔的錯(cuò)誤 -12913 被終止,因?yàn)槌鲇诎踩紤],這個(gè)框架不適用于舊版本 OS 的沙盒程序。

視頻工具箱用法的基本概念

視頻工具箱是一個(gè)基于 CoreMedia,CoreVideo,CoreFoundation 框架的 C 語言 API,并且基于三種可用類型的會話:壓縮,解壓縮,像素移動。它從 CoreMedia 和 CoreVideo 框架衍生了一些不同的關(guān)于時(shí)間和幀管理的數(shù)據(jù)類型,例如 CMTime 或 CVPixelBuffer。

為了說明視頻工具箱的基本概念,以下段落將描述如何創(chuàng)建一個(gè)只包含必要的結(jié)構(gòu)和類型的解壓會話。壓縮會話和解壓縮會話是非常相似的,而在實(shí)踐中,像素的傳送會話應(yīng)該是基本用不到的。

初始化解壓會話的時(shí)候,視頻工具箱需要知道以 CMVideoFormatDescriptionRef 結(jié)構(gòu)體描述的輸入的格式,以及 —— 除非你想使用默認(rèn)的無參數(shù) —— 你通過 CFDictionary 指定的輸出格式。視頻格式的描述可以從 AVAssetTrack 實(shí)例來獲取或者如果你使用自定義分路器 (demuxer) 的話,可以通過 CMVideoFormatDescriptionCreate 手動創(chuàng)建。最后,解碼后的數(shù)據(jù)是通過異步回調(diào)機(jī)制提供的?;卣{(diào)引用和視頻格式的描述在調(diào)用 VTDecompressionSessionCreate 時(shí)需要,而設(shè)置輸出格式是可選的。

什么是視頻格式描述

它是一個(gè)描述編碼視頻的不透明結(jié)構(gòu)體。它包含一個(gè) 四字符碼 (FourCC) 來描述所使用的編碼,視頻尺寸,以及一個(gè)被叫做 extensions 的字典。這些是什么? 在 OS X 上,硬件加速解碼是可選的,默認(rèn)情況下禁用。要啟用它,必須將 kVTVideoDecoderSpecification_EnableHardwareAcceleratedVideoDecoder 設(shè)置上,或者如果硬件加速播放不可用的話,再配合將 kVTVideoDecoderSpecification_RequireHardwareAcceleratedVideoDecoder 設(shè)置為 fail。在 iOS 上這些是沒有必要的,因?yàn)橛布铀俳獯a是唯一的可用選擇,此外,extensions 允許你轉(zhuǎn)發(fā)像是 MPEG-4 Part 2 或 H.264 這樣的先進(jìn)視頻編解碼器所需要的元數(shù)據(jù) (即 kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms ESDSavcC。)。此外,它可能包含元數(shù)據(jù)來通過 CVPixelAspectRatio 鍵處理非方形像素。

視頻輸出格式

which allows direct drawing of the decoded image in an OpenGL context without copying the data back and forth between the main bus and the GPU. This is sometimes referenced as a 0-copy pipeline, as no copy of the decoded image is created for drawing.

這是一個(gè)純 CFDictionary。解壓會話的結(jié)果是一個(gè)原始的,未壓縮的視頻圖像。為了高效率的輸出,硬件解碼器輸出偏向于選擇本機(jī)的色度格式,也就是 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange。然而,有許多其他的視頻格式可以用,并且轉(zhuǎn)碼過程中有 GPU 參與,效率非常高??梢酝ㄟ^設(shè)置 kCVPixelBufferPixelFormatTypeKey 鍵來啟用,我們還需要通過 kCVPixelBufferWidthKeykCVPixelBufferHeightKey 來設(shè)置整數(shù)的輸出尺寸。有一個(gè)可選的鍵也值得一提,kCVPixelBufferOpenGLCompatibilityKey,它允許在 OpenGL 的上下文中直接繪制解碼后的圖像,而不是從總線和 CPU 之間復(fù)制數(shù)據(jù)。這有時(shí)候被稱為零拷貝通道,因?yàn)樵诶L制過程中沒有解碼的圖像被拷貝。

請記住, "memcpy 就是犯罪" - Mans Rullgard,2013 Linux 嵌入式會議, http://free-electrons.com/blog/elc-2013-videos/

數(shù)據(jù)回調(diào)記錄

VTDecompressionOutputCallbackRecord 是一個(gè)簡單的結(jié)構(gòu)體,它帶有一個(gè)指針 (decompressionOutputCallback),指向幀解壓完成后的回調(diào)方法。你需要提供可以找到這個(gè)回調(diào)方法的實(shí)例 (decompressionOutputRefCon)。VTDecompressionOutputCallback 回調(diào)方法包括七個(gè)參數(shù):

  • 回調(diào)的引用
  • 幀的引用
  • 一個(gè)狀態(tài)標(biāo)識 (包含未定義的代碼)
  • 指示同步/異步解碼,或者解碼器是否打算丟幀的標(biāo)識
  • 實(shí)際圖像的緩沖
  • 出現(xiàn)的時(shí)間戳
  • 出現(xiàn)的持續(xù)時(shí)間

所有解碼或者丟幀的時(shí)候都會調(diào)用這個(gè)回調(diào)。因此,你的實(shí)現(xiàn)必須高度優(yōu)化,嚴(yán)格控制任何拷貝。原因是解碼器會被阻塞直到回調(diào)返回,這可能導(dǎo)致譯碼器堵塞和進(jìn)一步的阻塞。此外,請注意解碼器總是會按解碼順序返回幀,而解碼順序并不一定就是播放順序。幀的重新排序是由開發(fā)者決定的。

解碼幀

一旦會話創(chuàng)建,把每一幀輸入解碼器就像在公園里散步。這個(gè)過程就是用會話的引用和要解碼的采樣緩沖,來重復(fù)地調(diào)用 VTDecompressionSessionDecodeFrame,另外還有可選的高級標(biāo)識。采樣緩沖可以從 AVAssetReaderTrackOutput 中獲得,或者作為替換,可以使用 CMBlockBufferCreateWithMemoryBlockCMSampleTimingInfoCMSampleBufferCreate,來連同時(shí)間信息一起手動地從原始的內(nèi)存塊中創(chuàng)建。

小結(jié)

在特殊配置下,視頻工具箱是一個(gè)底層的,高效率的方式來加速視頻處理進(jìn)程。上層框架 AVFoundation 允許直接把支持的媒體文件解壓并且顯示到屏幕上,或者直接壓縮到一個(gè)文件。當(dāng)需要支持自定義文件格式,流媒體協(xié)議,或需要直接訪問編解碼器鏈的時(shí)候,視頻工具箱是首選的工具。缺點(diǎn)上來說,想要掌握這些缺少文檔的 API 是需要對復(fù)雜的視頻技術(shù)有著高深的知識的。但無論如何,這都是實(shí)現(xiàn)更好的用戶體驗(yàn),更高的效率,以及延長電池壽命的必經(jīng)之路。

參考

上一篇:繪制像素到屏幕上下一篇:安全