vx 搜索『gjzkeyframe』 關(guān)注『關(guān)鍵幀Keyframe』來及時獲得最新的音視頻技術(shù)文章输玷。
這個公眾號會路線圖 式的遍歷分享音視頻技術(shù):音視頻基礎(chǔ)(完成) → 音視頻工具(完成) → 音視頻工程示例(進(jìn)行中) → 音視頻工業(yè)實(shí)戰(zhàn)(準(zhǔn)備)。
iOS/Android 客戶端開發(fā)同學(xué)如果想要開始學(xué)習(xí)音視頻開發(fā)辣苏,最絲滑的方式是對音視頻基礎(chǔ)概念知識有一定了解后崔兴,再借助 iOS/Android 平臺的音視頻能力上手去實(shí)踐音視頻的采集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染
過程印机,并借助音視頻工具來分析和理解對應(yīng)的音視頻數(shù)據(jù)。
在音視頻工程示例這個欄目的 13 篇 AVDemo 文章中鞭衩,我們拆解了音頻和視頻的采集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染
流程并基于 iOS 系統(tǒng) API 實(shí)現(xiàn)了 Demo:
- 音頻 Demo 合集:
- 視頻 Demo 合集:
如果你看完這些 Demo学搜,對 iOS 平臺的音視頻開發(fā)多多少少會有一些認(rèn)識了。在《iOS 音頻處理框架及重點(diǎn) API 合集》一文中论衍,我們總結(jié)了一下 iOS 音頻處理框架以及音頻相關(guān)的 Demo 中用到的主要 API 和數(shù)據(jù)結(jié)構(gòu)瑞佩。
接下來,我們再來總結(jié)一下 iOS 視頻處理框架以及視頻相關(guān)的 Demo 中用到的主要 API 和數(shù)據(jù)結(jié)構(gòu)坯台。
1炬丸、iOS 視頻框架
當(dāng)我們想要了解 iOS 的視頻處理框架時,以下是我們能比較容易找到的兩張官方架構(gòu)圖蜒蕾。它們出自 AVFoundation Programming Guide[1] 這篇已經(jīng)過陳舊過時的文檔稠炬。
時至今日,iOS 平臺的視頻處理框架已經(jīng)有了很多更新咪啡,上圖中很多在 OS X 上的模塊也有了 iOS 版本的實(shí)現(xiàn)首启。雖然有變化,但是上面的架構(gòu)圖瑟匆,對我們了解現(xiàn)在 iOS 平臺的視頻處理框架還是有參考價值的闽坡。
根據(jù)我們的視頻 Demo 中涉及的系統(tǒng) API,我們這里挑選介紹幾個相關(guān)的 Framework:
- Video Toolbox Framework
- Core Media Framework
- Core Video Framework
- AVFoundation Framework
2愁溜、Video Toolbox Framework
Video Toolbox Framework[2] 主要用于支持視頻硬編碼和硬解碼疾嗅。這里我們主要介紹一下編碼和解碼相關(guān)的 API:
1)Data Compression[3]:通過一個編碼 Session 來管理對輸入視頻數(shù)據(jù)的壓縮操作。
- VTCompressionSession[4]:編碼器 Session冕象。
- VTCompressionSessionCreate(...)[5]:創(chuàng)建編碼器 Session代承。
- VTSessionSetProperty(...)[6]:設(shè)置編解碼器 Session 的屬性。
- VTCompressionSessionPrepareToEncodeFrames(...)[7]:讓編碼器盡量初始化編碼需要的資源渐扮。這個方法調(diào)用是可選的论悴,如果沒有調(diào)用這個方法,所有編碼需要的資源初始化會在第一次調(diào)用
VTCompressionSessionEncodeFrame(...)
時做墓律。 - VTCompressionSessionEncodeFrame(...)[8]:送數(shù)據(jù)給編碼器編碼膀估。被編碼好的數(shù)據(jù)可能不會立即返回,所以在不要修改送給編碼器的
CVPixelBuffer
耻讽。 - VTCompressionOutputCallback[9]:編碼數(shù)據(jù)回調(diào)察纯。
- VTCompressionSessionCompleteFrames(...)[10]:強(qiáng)制編碼器完成所有或者指定時間點(diǎn)(completeUntilPresentationTimeStamp)及之前的所有幀。
- VTCompressionSessionInvalidate(...)[11]:終止編碼。調(diào)用該方法后饼记,記得用 CFRelease 釋放編碼器香伴。
- VTEncodeInfoFlags[12]:編碼時返回編碼操作相關(guān)信息,可以在調(diào)用
VTCompressionSessionEncodeFrame(...)
等編碼接口時傳入具则,也會在編碼數(shù)據(jù)回調(diào)中收到即纲。有如下值: - kVTEncodeInfo_Asynchronous[13]:表示異步編碼。
- kVTEncodeInfo_FrameDropped[14]:表示該幀被丟棄博肋。
- VTIsHardwareDecodeSupported(...)[15]:當(dāng)前系統(tǒng)和硬件是否支持指定編解碼器類型低斋。
2)Data Decompression[16]:通過一個解碼 Session 來管理對輸入視頻數(shù)據(jù)的解壓縮操作。
- VTDecompressionSession[17]:解碼器 Session束昵。
- VTDecompressionSessionCreate(...)[18]:創(chuàng)建解碼器 Session拔稳。解碼數(shù)據(jù)通過傳入的回調(diào)(outputCallback)返回。
- VTSessionSetProperty(...)[19]:設(shè)置編解碼器 Session 的屬性锹雏。
- VTDecompressionSessionDecodeFrame(...)[20]:解碼送入的數(shù)據(jù)。
- VTDecompressionOutputCallbackRecord[21]:解碼數(shù)據(jù)回調(diào)术奖。
- VTDecompressionSessionFinishDelayedFrames(...)[22]:指示解碼器完成所有輸入數(shù)據(jù)的解碼礁遵。默認(rèn)情況下,解碼器不會無限期的延遲解碼某一幀采记,除非該幀在輸入給解碼器時被設(shè)置了
kVTDecodeFrame_EnableTemporalProcessing
這個 VTDecodeFrameFlags佣耐。這個方法會在所有延遲的幀解碼輸出后返回,要等待它們唧龄,則需要調(diào)用VTDecompressionSessionWaitForAsynchronousFrames(...)
兼砖。 - VTDecompressionSessionWaitForAsynchronousFrames(...)[23]:等待所有異步或延遲的幀解碼完成后再返回。調(diào)用這個方法后會自動調(diào)用
VTDecompressionSessionFinishDelayedFrames(...)
既棺,所以使用方不用自己調(diào)讽挟。 - VTDecompressionSessionInvalidate(...)[24]:終止解碼。調(diào)用該方法后丸冕,記得用 CFRelease 釋放解碼器耽梅。
- VTDecodeInfoFlags[25]:解碼時返回解碼操作相關(guān)信息,可以在調(diào)用
VTDecompressionSessionDecodeFrame(...)
等解碼接口時傳入胖烛,也會在解碼數(shù)據(jù)回調(diào)中收到眼姐。有如下值: - kVTDecodeInfo_Asynchronous[26]:異步解碼。
- kVTDecodeInfo_FrameDropped[27]:幀被丟棄佩番。
- kVTDecodeInfo_ImageBufferModifiable[28]:可以修改 Buffer众旗。
- VTDecodeFrameFlags[29]:用于指導(dǎo)解碼器行為的指令。
- kVTDecodeFrame_EnableAsynchronousDecompression[30]:告訴解碼器當(dāng)前幀可以異步解碼趟畏。但是非強(qiáng)制的贡歧。設(shè)置允許異步解碼之后,解碼器會同時解碼幾幀數(shù)據(jù),帶來的后果是艘款,解碼總體時間更短持际,但是前面幾幀回調(diào)的時間可能長一些。
- kVTDecodeFrame_DoNotOutputFrame[31]:告訴解碼器不對該幀回調(diào)解碼輸出數(shù)據(jù)哗咆,而是返回 NULL蜘欲。某些情況我們不需要解碼器輸出幀,比如發(fā)生解碼器狀態(tài)錯誤的時候晌柬。
- kVTDecodeFrame_1xRealTimePlayback(...)[32]:告訴解碼器可以使用低功耗模式解碼姥份,設(shè)置之后處理器消耗會變少,解碼速度會變慢年碘,通常我們不會設(shè)置這個參數(shù)澈歉,因?yàn)橛步獯a使用的是專用處理器,不消耗 CPU屿衅,所以越快越好埃难。
- kVTDecodeFrame_EnableTemporalProcessing(...)[33]:通知解碼器需要處理幀序,設(shè)置之后解碼回調(diào)會變慢涤久,因?yàn)闊o論是異步解碼還是 pts涡尘、dts 不相等的時候都需要進(jìn)行幀排序,會耗時响迂,蘋果官方文檔不建議我們使用這個參數(shù)考抄。
3、Core Media Framework
在前面介紹 iOS 音頻處理框架時蔗彤,我們已經(jīng)介紹過 Core Media Framework[34] 了川梅,這個 Framework 中定義和封裝了 AVFoundation 等更上層的媒體框架需要的媒體處理流水線(包含時間信息)以及其中使用的接口和數(shù)據(jù)類型。使用 Core Media 層的接口和數(shù)據(jù)類型可以高效的處理媒體采樣數(shù)據(jù)然遏、管理采樣數(shù)據(jù)隊(duì)列贫途。這里,我們著重介紹一下其中跟視頻處理相關(guān)的部分啦鸣。
1)Sample Processing[35]:采樣數(shù)據(jù)處理潮饱。常用的數(shù)據(jù)類型:
- CMSampleBuffer[36]:系統(tǒng)用來在音視頻處理的 pipeline 中使用和傳遞媒體采樣數(shù)據(jù)的核心數(shù)據(jù)結(jié)構(gòu)。你可以認(rèn)為它是 iOS 音視頻處理 pipeline 中的流通貨幣诫给,攝像頭采集的視頻數(shù)據(jù)接口香拉、麥克風(fēng)采集的音頻數(shù)據(jù)接口、編碼和解碼數(shù)據(jù)接口中狂、讀取和存儲視頻接口凫碌、視頻渲染接口等等,都以它作為參數(shù)胃榕。通常盛险,CMSampleBuffer 中要么包含一個或多個媒體采樣的 CMBlockBuffer瞄摊,要么包含一個 CVImageBuffer(也作 CVPixelBuffer)。
- kCMSampleAttachmentKey_NotSync[42]:Sync Sample 即 IDR 幀苦掘,可以用這個 key 對應(yīng)的值來判斷當(dāng)前幀是否是 IDR 幀换帜,當(dāng)對應(yīng)的值為 kCFBooleanFalse 表示是 IDR 幀。這個屬性會被寫入媒體文件或從媒體文件中讀取鹤啡。
- kCMSampleAttachmentKey_PartialSync[43]:當(dāng)前幀是否 Partial Sync Sample惯驼,Partial Sync Sample 可以不依賴前序幀就完成解碼(可認(rèn)為是普通的 I 幀),兩個連續(xù)的 Partial Sync Sample 隨后的幀也可以不依賴這兩幀的前序的幀完成解碼递瑰。當(dāng)設(shè)置一個幀為 Partial Sync Sample 時祟牲,需要同時設(shè)置
kCMSampleAttachmentKey_PartialSync
和kCMSampleAttachmentKey_NotSync
兩個屬性為 kCFBooleanTrue(可以認(rèn)為是 I 幀,但是又區(qū)別于 IDR 幀)抖部。這個屬性會被寫入媒體文件或從媒體文件中讀取说贝。 - kCMSampleAttachmentKey_DependsOnOthers[44]:當(dāng)前幀是否依賴其他幀才能完成解碼。如果對應(yīng)的值為 kCFBooleanTrue慎颗,表示依賴乡恕。比如,P 或 B 幀哗总。這個屬性會被寫入媒體文件或從媒體文件中讀取几颜。
- kCMSampleAttachmentKey_IsDependedOnByOthers[45]:表示當(dāng)前幀是否被其他幀依賴。如果對應(yīng)的值為 kCFBooleanFalse讯屈,表示不被其他幀依賴,這時候是可以丟掉該幀的县习。這個屬性會被寫入媒體文件或從媒體文件中讀取涮母。
- kCMSampleAttachmentKey_DisplayImmediately[46]:如果對應(yīng)的值為 kCFBooleanTrue,表示當(dāng)前幀應(yīng)該馬上渲染躁愿,即使還未到其 pts 時間叛本。一般是在運(yùn)行時來使用這個屬性來觸發(fā)一些渲染操作,比如
AVSampleBufferDisplayLayer
就可能用到彤钟。這個屬性不會被寫入媒體文件来候。 - kCMSampleAttachmentKey_DoNotDisplay[47]:表示當(dāng)前幀是否只解碼不渲染。一般是在運(yùn)行時來使用這個屬性來觸發(fā)一些渲染操作逸雹,比如
AVSampleBufferDisplayLayer
就可能用到营搅。這個屬性不會被寫入媒體文件。 - kCMSampleAttachmentKey_EarlierDisplayTimesAllowed[48]:表示后面的幀是否有更早的顯示時間梆砸。
- kCMSampleAttachmentKey_HasRedundantCoding[49]:表示當(dāng)前幀是否有冗余編碼转质。
- CMSampleBufferGetFormatDescription(...)[37]:返回 CMSampleBuffer 中的采樣數(shù)據(jù)對應(yīng)的 CMFormatDescription。
- CMSampleBufferGetDataBuffer(...)[38]:返回 CMSampleBuffer 中的 CMBlockBuffer帖世。注意調(diào)用方不會持有返回的 CMBlockBuffer休蟹,如果想要維護(hù)指向它的指針,需要顯式 retain 一下。
- CMSampleBufferGetPresentationTimeStamp(...)[39]:獲取 CMSampleBuffer 中所有采樣的最小的 pts 時間戳赂弓。因?yàn)?CMSampleBuffer 中的采樣是按照解碼順序存儲的绑榴,展示順序可能與解碼順序一致,也可能不一致盈魁。
- CMSampleBufferGetDecodeTimeStamp(...)[40]:獲取 CMSampleBuffer 中所有采樣的第一個采樣的 dts 時間戳翔怎。在 CMSampleBuffer 中,采樣是以解碼順序存儲的备埃,即使與展示順序不一致姓惑。
- CMSampleBufferGetSampleAttachmentsArray(...)[41]:獲取 CMSampleBuffer 中采用數(shù)據(jù)對應(yīng)的附屬數(shù)據(jù)(attachment)數(shù)組。這些附屬數(shù)據(jù)可能有下面這些 key:
- CMBlockBuffer[50]:一個或多個媒體采樣的的裸數(shù)據(jù)按脚。其中可以封裝:音頻采集后于毙、編碼后、解碼后的數(shù)據(jù)(如:PCM 數(shù)據(jù)辅搬、AAC 數(shù)據(jù))唯沮;視頻編碼后的數(shù)據(jù)(如:H.264 數(shù)據(jù))。
- CMBlockBufferGetDataPointer(...)[51]:獲取訪問 CMBlockBuffer 中數(shù)據(jù)的地址堪遂。
- CMFormatDescription[52]:用于描述 CMSampleBuffer 中采樣的格式信息介蛉。
- CMFormatDescriptionCreate(...)[53]:創(chuàng)建一個 CMFormatDescription。
- CMVideoCodecType[54]:
typedef FourCharCode CMVideoCodecType
溶褪,視頻編碼類型币旧。 - CMMediaType[55]:
typedef FourCharCode CMMediaType
,媒體類型猿妈。
- CMVideoFormatDescription[56]:
typedef CMFormatDescriptionRef CMVideoFormatDescriptionRef;
CMVideoFormatDescription 是一種 CMFormatDescriptionRef吹菱。 - CMVideoFormatDescriptionCreate(...)[57]:基于 CMVideoCodecType 來創(chuàng)建一個 CMVideoFormatDescription。
- CMVideoFormatDescriptionGetDimensions(...)[58]:返回視頻編碼后的像素尺寸
CMVideoDimensions
彭则。
2)Time Representation[59]:時間信息表示鳍刷。常用的數(shù)據(jù)類型:
- CMTime[60]:用 value/timescale 的方式表示時間。這樣可以解決浮點(diǎn)運(yùn)算時的精度損失問題俯抖。timescale 表示時間刻度输瓜,通常在處理視頻內(nèi)容時常見的時間刻度為 600,這是大部分常用視頻幀率 24fps芬萍、25fps尤揣、30fps 的公倍數(shù),音頻數(shù)據(jù)常見的時間刻度就是采樣率担忧,比如 44100 或 48000芹缔。
- CMTimeRange[61]:用 start+duration 的方式表示一段時間。
- CMSampleTimingInfo[62]:一個 CMSampleBuffer 的時間戳信息瓶盛,包括 pts最欠、dts示罗、duration。
3)Queues[63]:數(shù)據(jù)容器芝硬。常用的數(shù)據(jù)類型:
- CMSimpleQueue[64]:一個簡單地蚜点、無鎖的 FIFO 隊(duì)列,可以放
(void *)
元素拌阴,元素不能是 NULL 或 0绍绘,如果元素是指向分配內(nèi)存的指針,其內(nèi)存生命周期要在外面自己管理迟赃∨憔校可以用作音視頻采樣數(shù)據(jù)(CMSampleBufferRef)的隊(duì)列,不過要自己加鎖纤壁。 - CMBufferQueue[65]:支持存儲任何 CFTypeRef 類型的數(shù)據(jù)左刽,但是數(shù)據(jù)類型需要有 duration 的概念,在創(chuàng)建 CMBufferQueue 的時候酌媒,會有一些回調(diào)欠痴,其中一個必須的回調(diào)是要返回隊(duì)列中對象的 duration。CMBufferQueue 是設(shè)計(jì)用于在生產(chǎn)者/消費(fèi)者模型中在不同的線程中讀寫數(shù)據(jù)秒咨。通常是兩個線程(一個是生產(chǎn)者入隊(duì)線程喇辽,一個是消費(fèi)者出隊(duì)線程),當(dāng)然更多的線程也是可以的雨席。
- CMMemoryPool[66]:內(nèi)存池容器菩咨,對使用大塊的內(nèi)存有優(yōu)化。一個 CMMemoryPool 的實(shí)例實(shí)際上維護(hù)一個最近釋放內(nèi)存的池子用于內(nèi)存分配服務(wù)陡厘。這樣的目的是加快隨后的內(nèi)存分配旦委。在需要重復(fù)分配大塊內(nèi)存時,比如輸出視頻編碼數(shù)據(jù)雏亚,可以使用這個數(shù)據(jù)結(jié)構(gòu)。
4摩钙、Core Video Framework
Core Video Framework[67] 主要用于支持?jǐn)?shù)字視頻及數(shù)字圖像幀的處理罢低,提供基于處理 Pipeline 的 API,并且同時支持 Metal 和 OpenGL胖笛。
這里我們主要介紹 CoreVideo Framework 中的幾種數(shù)據(jù)類型:
- CVImageBuffer[68]:其中包含媒體流中 CMSampleBuffers 的格式描述网持、每個采樣的寬高和時序信息、緩沖級別和采樣級別的附屬信息长踊。緩沖級別的附屬信息是指緩沖區(qū)整體的信息功舀,比如播放速度、對后續(xù)緩沖數(shù)據(jù)的操作等身弊。采樣級別的附屬信息是指單個采樣的信息辟汰,比如視頻幀的時間戳列敲、是否關(guān)鍵幀等。其中可以封裝:視頻采集后帖汞、解碼后等未經(jīng)編碼的數(shù)據(jù)(如:YCbCr 數(shù)據(jù)戴而、RGBA 數(shù)據(jù))。
- CVPixelBuffer[69]:
typedef CVImageBufferRef CVPixelBufferRef
翩蘸,像素緩沖區(qū)所意。這是 iOS 平臺進(jìn)行視頻編解碼及圖像處理相關(guān)最重要的數(shù)據(jù)結(jié)構(gòu)之一。它是在CVImageBuffer
的基礎(chǔ)上實(shí)現(xiàn)了內(nèi)存存儲催首。并且扶踊,CVPixelBuffer 還可以實(shí)現(xiàn) CPU 和 GPU 共享內(nèi)存,為圖像處理提供更高的效率郎任。 - CVPixelBufferLockBaseAddress(...)[70]:鎖定 Pixel Buffer 的內(nèi)存基地址秧耗。當(dāng)使用 CPU 讀取 Pixel 數(shù)據(jù)時,需要讀取時鎖定涝滴,讀完解鎖绣版。如果在鎖定時,帶了
kCVPixelBufferLock_ReadOnly
的 lockFlags歼疮,解鎖時也要帶上杂抽。但是,如果使用 GPU 讀取 Pixel 數(shù)據(jù)時韩脏,則沒有必要鎖定缩麸,反而會影響性能。 - CVPixelBufferUnlockBaseAddress(...)[71]:解鎖 Pixel Buffer 的內(nèi)存基地址赡矢。
- CVPixelBufferGetBaseAddress(...)[72]:返回 Pixel Buffer 的內(nèi)存基地址杭朱,但是根據(jù) Buffer 的類型及創(chuàng)建場景的不同,返回的值的含義也有區(qū)別吹散。對于 Chunky Buffers弧械,返回的是坐標(biāo) (0, 0) 像素的內(nèi)存地址;對于 Planar Buffers空民,返回的是對應(yīng)的 CVPlanarComponentInfo 結(jié)構(gòu)體的內(nèi)存地址或者 NULL(如果不存在 CVPlanarComponentInfo 結(jié)構(gòu)體的話)刃唐,所以,對于 Planar Buffers界轩,最好用
CVPixelBufferGetBaseAddressOfPlane(...)
和CVPixelBufferGetBytesPerRowOfPlane(...)
來獲取其中數(shù)據(jù)画饥。獲取 Pixel Buffer 的基地址時,需要先用CVPixelBufferLockBaseAddress(...)
加鎖浊猾。 - CVPixelBufferGetHeight(...)[73]:返回 Buffer 的像素高度抖甘。
- CVPixelBufferGetWidth(...)[74]:返回 Buffer 的像素寬度。
- CVPixelBufferIsPlanar(...)[75]:判斷 Pixel Buffer 是否是 Planar 類型葫慎。
- CVPixelBufferGetBaseAddressOfPlane(...)[76]:根據(jù)指定的 Plane Index 來獲取對應(yīng)的基地址衔彻。獲取 Pixel Buffer 的基地址時薇宠,需要先用
CVPixelBufferLockBaseAddress(...)
加鎖。怎么理解 Plane 呢米奸?其實(shí)主要跟顏色模型有關(guān)昼接,比如:存儲 YUV420P 時,有 Y悴晰、U慢睡、V 這 3 個 Plane;存儲 NV12 時铡溪,有 Y漂辐、UV 這 2 個 Plane;存儲 RGBA 時棕硫,有 R髓涯、G、B哈扮、A 這 4 個 Plane纬纪;而在 Packed 存儲模式中,因?yàn)樗蟹至康南袼厥墙豢棿鎯Φ幕猓灾挥?1 個 Plane包各。 - CVPixelBufferGetPlaneCount(...)[77]:返回 Pixel Buffer 中 Plane 的數(shù)量。
- CVPixelBufferGetBytesPerRowOfPlane(...)[78]:返回指定 Index 的 Plane 的每行字節(jié)數(shù)靶庙。
- CVPixelBufferGetHeightOfPlane(...)[79]:返回指定 Index 的 Plane 的高度问畅。非 Planar 類型,返回 0六荒。
- CVPixelBufferGetWidthOfPlane(...)[80]:返回指定 Index 的 Plane 的寬度护姆。非 Planar 類型,返回 0。
- CVPixelBufferLockBaseAddress(...)[70]:鎖定 Pixel Buffer 的內(nèi)存基地址秧耗。當(dāng)使用 CPU 讀取 Pixel 數(shù)據(jù)時,需要讀取時鎖定涝滴,讀完解鎖绣版。如果在鎖定時,帶了
5、AVFoundation Framework
AVFoundation Framework[81] 是更上層的面向?qū)ο蟮囊粋€音視頻處理框架。它提供了音視頻資源管理、相機(jī)設(shè)備管理镜会、音視頻處理、系統(tǒng)級音頻交互管理的能力,功能非常強(qiáng)大睁蕾。如果對其功能進(jìn)行細(xì)分,可以分為如下幾個模塊:
- Assets族阅,音視頻資源管理篓跛。
- Playback,媒體播放及自定義播放行為支持坦刀。
- Capture愧沟,內(nèi)置及外置的相機(jī)蔬咬、麥克風(fēng)等采集設(shè)備管理,圖片沐寺、音視頻錄制林艘。
- Editing,音視頻編輯混坞。
- Audio狐援,音頻播放、錄制和處理究孕,App 系統(tǒng)音頻行為配置啥酱。
- Speech,文本語音轉(zhuǎn)換厨诸。
在我們前面的 Demo 中封裝 Video Capture镶殷、Muxer、Demuxer 及設(shè)置 AudioSession 時會用到 AVFoundation Framework 的一些能力微酬,我們這里對應(yīng)地介紹一下绘趋。
1)Video Capture
關(guān)于 iOS 視頻采集相關(guān)的架構(gòu),可以參考下面兩張圖:
- AVCaptureDevice[82]:為音頻和視頻采集會話提供輸入的設(shè)備颗管,并且可以提供相關(guān)硬件設(shè)備的控制能力陷遮,比如:攝像頭選擇、曝光忙上、對焦拷呆、景深、縮放疫粥、閃光燈茬斧、夜景、幀率梗逮、白平衡项秉、ISO、HDR慷彤、顏色空間娄蔼、幾何失真等等。這里不過多介紹底哗,只介紹我們 Demo 中用到的一些接口:
- -lockForConfiguration:[83]:在配置硬件相關(guān)的屬性時岁诉,需要先調(diào)用這個方法來鎖定。
- -unlockForConfiguration:[84]:配置完后跋选,解鎖涕癣。
- -activeFormat[85]:屬性,獲取或設(shè)置當(dāng)前采集設(shè)備采集的媒體格式前标。
- -activeVideoMinFrameDuration[86]:屬性坠韩,獲取或設(shè)置最低幀率距潘。
- -activeVideoMaxFrameDuration:屬性,獲取或設(shè)置最高幀率只搁。
- AVCaptureDeviceInput[87]:采集輸入音比,從采集設(shè)備提供采集數(shù)據(jù)給采集會話。
- AVCaptureDeviceFormat[88]:用于采集設(shè)備的媒體格式或采集配置氢惋,比如視頻分辨率洞翩、幀率等。
- AVCaptureDevicePosition[89]:采集設(shè)備物理位置明肮。
- AVCaptureSession[90]:采集會話菱农。用于管理采集活動,協(xié)調(diào)采集數(shù)據(jù)在采集設(shè)備和采集輸出對象之間的流轉(zhuǎn)柿估。
- -sessionPreset[91]:獲取或設(shè)置采集預(yù)設(shè)配置循未,設(shè)定采集輸出的質(zhì)量級別或碼率。
- -addInput:[92]:為采集會話添加輸入秫舌。
- -addOutput:[93]:為采集會話添加輸出的妖。
- -addConnection:[94]:為采集會話添加連接對象。
- AVCaptureSessionPreset[95]:采集預(yù)設(shè)配置足陨,表示采集輸出的質(zhì)量級別或碼率嫂粟。比如:AVCaptureSessionPresetLow、AVCaptureSessionPresetHigh墨缘、AVCaptureSessionPreset1280x720星虹、AVCaptureSessionPreset1920x1080 等。
- AVCaptureSessionRuntimeErrorNotification[96]:采集會話是否發(fā)生錯誤的通知镊讼。
- AVCaptureConnection[97]:在采集會話中連接一對采集輸入和輸出宽涌。可以設(shè)置采集視頻鏡像蝶棋、防抖等卸亮。
- -videoMirrored[98]:經(jīng)過 Connection 的視頻是否鏡像。
- -preferredVideoStabilizationMode[99]:設(shè)置采集圖像防抖模式玩裙。
- AVCaptureVideoDataOutput[100]:采集視頻的輸出對象兼贸。提供訪問視頻幀進(jìn)行圖像處理的能力,可以通過
-captureOutput:didOutputSampleBuffer:fromConnection:
回調(diào)獲取采集的數(shù)據(jù)吃溅。 - -setSampleBufferDelegate:queue:[101]:設(shè)置采集視頻的回調(diào)和任務(wù)隊(duì)列溶诞。
- -captureOutput:didOutputSampleBuffer:fromConnection::采集視頻輸出數(shù)據(jù)回調(diào)。
- -alwaysDiscardsLateVideoFrames[102]:采集視頻輸出時决侈,當(dāng)幀到的太晚是否丟棄很澄。默認(rèn) YES。如果設(shè)置 NO,會給
-captureOutput:didOutputSampleBuffer:fromConnection:
更多時間處理幀甩苛,但是這時候內(nèi)存占用可能會更大。
- AVCaptureVideoPreviewLayer[103]:可以用來渲染采集視頻數(shù)據(jù)的 Core Animation Layer俏站。
- -setVideoGravity:[104]:設(shè)置渲染的內(nèi)容填充模式讯蒲。
2)Muxer
- AVAssetWriter[105]:支持將媒體數(shù)據(jù)寫入 QuickTime 或 MPEG-4 格式的文件中,支持對多軌道的媒體數(shù)據(jù)進(jìn)行交錯處理來提高播放和存儲的效率肄扎,支持對媒體采樣進(jìn)行轉(zhuǎn)碼墨林,支持寫入 metadata。需要注意的是犯祠,一個 AVAssetWriter 實(shí)例只能對應(yīng)寫一個文件旭等,如果要寫入多個文件,需要創(chuàng)建多個 AVAssetWriter 實(shí)例衡载。
- canAddInput:[106]:檢查 AVAssetWriter 是否支持添加對應(yīng)的 AVAssetWriterInput搔耕。
- addInput:[107]:給 AVAssetWriter 添加一個 AVAssetWriterInput。注意必須在 AVAssetWriter 開始寫入之前添加痰娱。
- startWriting[108]:開始寫入弃榨。必須在配置好 AVAssetWriter 添加完 AVAssetWriterInput 做好準(zhǔn)備后再調(diào)用這個方法。在調(diào)用完這個方法后梨睁,需要調(diào)用
startSessionAtSourceTime:
開始寫入會話鲸睛,此后就可以使用對應(yīng)的 AVAssetWriterInput 來寫入媒體采樣數(shù)據(jù)。 - startSessionAtSourceTime:[109]:開啟寫入會話坡贺。在
startWriting
后調(diào)用官辈,在寫入媒體采樣數(shù)據(jù)之前調(diào)用。 - endSessionAtSourceTime:[110]:結(jié)束寫入會話遍坟。結(jié)束時間是會話結(jié)束時樣本數(shù)據(jù)在時間軸上的時刻拳亿。如果沒有顯示調(diào)用這個方法,系統(tǒng)會在你調(diào)用
finishWritingWithCompletionHandler:
結(jié)束寫入時自動調(diào)用政鼠。 - finishWritingWithCompletionHandler:[111]:標(biāo)記 AVAssetWriter 的所有 input 為結(jié)束风瘦,完成寫入。為了保證 AVAssetWriter 完成所有采樣數(shù)據(jù)的寫入公般,要在調(diào)用添加數(shù)據(jù)正確返回后調(diào)用這個方法万搔。
- cancelWriting[112]:取消創(chuàng)建輸出文件。如果 AVAssetWriter 的狀態(tài)是 Failed 或 Completed官帘,調(diào)用這個方法無效瞬雹,否則,調(diào)用它會阻塞調(diào)用線程刽虹,直到會話取消完成酗捌。如果 AVAssetWriter 已經(jīng)創(chuàng)建了輸出文件,調(diào)用這個方法會刪除這個文件。
- AVAssetWriterInput[113]:用于向 AVAssetWriter 實(shí)例的輸出文件的一個軌道添加媒體采樣數(shù)據(jù)胖缤。一個實(shí)例只能對應(yīng)一個軌道媒體數(shù)據(jù)或 metadata 數(shù)據(jù)的寫入尚镰,當(dāng)使用多個實(shí)例向多個軌道寫入數(shù)據(jù)時,需要注意檢查 AVAssetWriterInput 的 readyForMoreMediaData 屬性哪廓。
- expectsMediaDataInRealTime[114]:輸入是否為實(shí)時數(shù)據(jù)源狗唉,比如相機(jī)采集。當(dāng)設(shè)置這個值為 YES 時涡真,會優(yōu)化用于實(shí)時使用的輸入來精準(zhǔn)計(jì)算
readyForMoreMediaData
的狀態(tài)分俯。 - readyForMoreMediaData[115]:表示 AVAssetWriterInput 是否已經(jīng)準(zhǔn)備好接收媒體數(shù)據(jù)。
- requestMediaDataWhenReadyOnQueue:usingBlock:[116]:告訴 AVAssetWriterInput 在方便的時候去請求數(shù)據(jù)并寫入輸出文件哆料。在對接拉取式的數(shù)據(jù)源時缸剪,可以用這個方法。
- appendSampleBuffer:[117]:通過 AVAssetWriterInput 向輸出文件添加媒體數(shù)據(jù)东亦,但是添加之前媒體數(shù)據(jù)的順序需要自己處理杏节。注意,調(diào)用這個方法添加采樣數(shù)據(jù)后讥此,不要更改采樣數(shù)據(jù)的內(nèi)容拢锹。
- markAsFinished[118]:標(biāo)記 AVAssetWriterInput 為完成,表示已經(jīng)完成向它添加媒體數(shù)據(jù)了萄喳。
- expectsMediaDataInRealTime[114]:輸入是否為實(shí)時數(shù)據(jù)源狗唉,比如相機(jī)采集。當(dāng)設(shè)置這個值為 YES 時涡真,會優(yōu)化用于實(shí)時使用的輸入來精準(zhǔn)計(jì)算
3)Demuxer
- AVAssetReader[119]:用于從 AVAsset 資源中讀取媒體數(shù)據(jù)卒稳。這個 AVAsset 可以是 QuickTime 或 MPEG-4 文件,也可以是編輯創(chuàng)作的 AVComposition他巨。
- canAddOutput:[120]:檢查 AVAssetReader 是否支持添加對應(yīng)的 AVAssetReaderOutput充坑。
- addOutput:[121]:給 AVAssetReader 添加一個 AVAssetReaderOutput。注意必須在 AVAssetReader 開始讀取之前添加染突。
- startReading[122]:開始讀取捻爷。
- cancelReading[123]:在讀完數(shù)據(jù)之前取消讀取可以調(diào)用這個接口。
- AVAssetReaderOutput[124]:一個抽象類份企,定義了從 AVAsset 資源中讀取媒體采樣數(shù)據(jù)的接口也榄。通常我們可以使用
AVAssetReaderTrackOutput
、AVAssetReaderVideoCompositionOutput
等具體的實(shí)現(xiàn)類司志。 - AVAssetReaderTrackOutput[125]:
- alwaysCopiesSampleData[126]:是否總是拷貝采樣數(shù)據(jù)甜紫。如果要修改讀取的采樣數(shù)據(jù),可以設(shè)置 YES骂远,否則就設(shè)置 NO囚霸,這樣性能會更好。
- copyNextSampleBuffer[127]:從 Output 拷貝下一個 CMSampleBuffer激才。
- AVAudioSession[128]:在最新版本的 iOS 系統(tǒng)庫中拓型,AVAudioSession 已經(jīng)遷移到 AVFAudio Framework 中了额嘿。AVAudioSession 是系統(tǒng)用來管理 App 對音頻硬件資源的使用的,比如:設(shè)置當(dāng)前 App 與其他 App 同時使用音頻時劣挫,是否混音册养、打斷或降低其他 App 的聲音;手機(jī)靜音鍵打開時是否還可以播放聲音压固;指定音頻輸入或者輸出設(shè)備捕儒;是否支持錄制或邊錄制邊播放;聲音被打斷時的通知邓夕。我們這里只簡單介紹下 Demo 中用到的接口:
- setCategory:withOptions:error:[129]:設(shè)置 AudioSession 的類型和選項(xiàng)參數(shù)。比如類型為 AVAudioSessionCategoryPlayback 表示支持播放阎毅;AVAudioSessionCategoryPlayAndRecord 表示同時支持播放和錄制等等焚刚。
- setMode:error:[130]:設(shè)置 AudioSession 的模式。AudioSession 的類型和模式一起決定了 App 如何使用音頻扇调。通常需要在激活 AudioSession 之前設(shè)置類型和模式矿咕。比如模式為 AVAudioSessionModeVideoRecording 表示當(dāng)期要錄制視頻;AVAudioSessionModeVoiceChat 表示語音聊天狼钮。
- setActive:withOptions:error:[131]:激活或釋放 AudioSession 的使用碳柱。
以上這些框架及 API 基本上可以覆蓋我們在前面的 Demo 中用到的能力了。
參考資料
[1]
AVFoundation Programming Guide: https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/00_Introduction.html
[2]
Video Toolbox Framework: https://developer.apple.com/documentation/videotoolbox?language=objc
[3]
Data Compression: https://developer.apple.com/documentation/videotoolbox?language=objc
[4]
VTCompressionSession: https://developer.apple.com/documentation/videotoolbox/vtcompressionsession?language=objc
[5]
VTCompressionSessionCreate(...): https://developer.apple.com/documentation/videotoolbox/1428285-vtcompressionsessioncreate?language=objc
[6]
VTSessionSetProperty(...): https://developer.apple.com/documentation/videotoolbox/1536144-vtsessionsetproperty?language=objc
[7]
VTCompressionSessionPrepareToEncodeFrames(...): https://developer.apple.com/documentation/videotoolbox/1428283-vtcompressionsessionpreparetoenc?language=objc
[8]
VTCompressionSessionEncodeFrame(...): https://developer.apple.com/documentation/videotoolbox/1428287-vtcompressionsessionencodeframe?language=objc
[9]
VTCompressionOutputCallback: https://developer.apple.com/documentation/videotoolbox/vtcompressionoutputcallback?language=objc
[10]
VTCompressionSessionCompleteFrames(...): https://developer.apple.com/documentation/videotoolbox/1428303-vtcompressionsessioncompletefram?language=objc
[11]
VTCompressionSessionInvalidate(...): https://developer.apple.com/documentation/videotoolbox/1428295-vtcompressionsessioninvalidate?language=objc
[12]
VTEncodeInfoFlags: https://developer.apple.com/documentation/videotoolbox/vtencodeinfoflags?language=objc
[13]
kVTEncodeInfo_Asynchronous: https://developer.apple.com/documentation/videotoolbox/vtencodeinfoflags/kvtencodeinfo_asynchronous?language=objc
[14]
kVTEncodeInfo_FrameDropped: https://developer.apple.com/documentation/videotoolbox/vtencodeinfoflags/kvtencodeinfo_framedropped?language=objc
[15]
VTIsHardwareDecodeSupported(...): https://developer.apple.com/documentation/videotoolbox/2887343-vtishardwaredecodesupported?language=objc
[16]
Data Decompression: https://developer.apple.com/documentation/videotoolbox?language=objc
[17]
VTDecompressionSession: https://developer.apple.com/documentation/videotoolbox/vtdecompressionsession?language=objc
[18]
VTDecompressionSessionCreate(...): https://developer.apple.com/documentation/videotoolbox/1536134-vtdecompressionsessioncreate?language=objc
[19]
VTSessionSetProperty(...): https://developer.apple.com/documentation/videotoolbox/1536144-vtsessionsetproperty?language=objc
[20]
VTDecompressionSessionDecodeFrame(...): https://developer.apple.com/documentation/videotoolbox/1536071-vtdecompressionsessiondecodefram?language=objc
[21]
VTDecompressionOutputCallbackRecord: https://developer.apple.com/documentation/videotoolbox/vtdecompressionoutputcallbackrecord?language=objc
[22]
VTDecompressionSessionFinishDelayedFrames(...): https://developer.apple.com/documentation/videotoolbox/1536101-vtdecompressionsessionfinishdela?language=objc
[23]
VTDecompressionSessionWaitForAsynchronousFrames(...): https://developer.apple.com/documentation/videotoolbox/1536066-vtdecompressionsessionwaitforasy?language=objc
[24]
VTDecompressionSessionInvalidate(...): https://developer.apple.com/documentation/videotoolbox/1536093-vtdecompressionsessioninvalidate?language=objc
[25]
VTDecodeInfoFlags: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags?language=objc
[26]
kVTDecodeInfo_Asynchronous: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags/kvtdecodeinfo_asynchronous?language=objc
[27]
kVTDecodeInfo_FrameDropped: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags/kvtdecodeinfo_framedropped?language=objc
[28]
kVTDecodeInfo_ImageBufferModifiable: https://developer.apple.com/documentation/videotoolbox/vtdecodeinfoflags/kvtdecodeinfo_imagebuffermodifiable?language=objc
[29]
VTDecodeFrameFlags: https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags?language=objc
[30]
kVTDecodeFrame_EnableAsynchronousDecompression: https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_enableasynchronousdecompression?language=objc
[31]
kVTDecodeFrame_DoNotOutputFrame: https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_donotoutputframe?language=objc
[32]
kVTDecodeFrame_1xRealTimePlayback(...): https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_1xrealtimeplayback?language=objc
[33]
kVTDecodeFrame_EnableTemporalProcessing(...): https://developer.apple.com/documentation/videotoolbox/vtdecodeframeflags/kvtdecodeframe_enabletemporalprocessing?language=objc
[34]
Core Media: https://developer.apple.com/documentation/coremedia?language=objc
[35]
Sample Processing: https://developer.apple.com/documentation/coremedia?language=objc
[36]
CMSampleBuffer: https://developer.apple.com/documentation/coremedia/cmsamplebuffer-u71?language=objc
[37]
CMSampleBufferGetFormatDescription(...): https://developer.apple.com/documentation/coremedia/1489185-cmsamplebuffergetformatdescripti?language=objc
[38]
CMSampleBufferGetDataBuffer(...): https://developer.apple.com/documentation/coremedia/1489629-cmsamplebuffergetdatabuffer?language=objc
[39]
CMSampleBufferGetPresentationTimeStamp(...): https://developer.apple.com/documentation/coremedia/1489252-cmsamplebuffergetpresentationtim?language=objc
[40]
CMSampleBufferGetDecodeTimeStamp(...): https://developer.apple.com/documentation/coremedia/1489404-cmsamplebuffergetdecodetimestamp?language=objc
[41]
CMSampleBufferGetSampleAttachmentsArray(...): https://developer.apple.com/documentation/coremedia/1489189-cmsamplebuffergetsampleattachmen?language=objc
[42]
kCMSampleAttachmentKey_NotSync: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_notsync?language=objc
[43]
kCMSampleAttachmentKey_PartialSync: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_partialsync?language=objc
[44]
kCMSampleAttachmentKey_DependsOnOthers: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_dependsonothers?language=objc
[45]
kCMSampleAttachmentKey_IsDependedOnByOthers: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_isdependedonbyothers?language=objc
[46]
kCMSampleAttachmentKey_DisplayImmediately: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_displayimmediately?language=objc
[47]
kCMSampleAttachmentKey_DoNotDisplay: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_donotdisplay?language=objc
[48]
kCMSampleAttachmentKey_EarlierDisplayTimesAllowed: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_earlierdisplaytimesallowed?language=objc
[49]
kCMSampleAttachmentKey_HasRedundantCoding: https://developer.apple.com/documentation/coremedia/kcmsampleattachmentkey_hasredundantcoding?language=objc
[50]
CMBlockBuffer: https://developer.apple.com/documentation/coremedia/cmblockbuffer-u9i?language=objc
[51]
CMBlockBufferGetDataPointer(...): https://developer.apple.com/documentation/coremedia/1489264-cmblockbuffergetdatapointer?language=objc
[52]
CMFormatDescription: https://developer.apple.com/documentation/coremedia/cmformatdescription-u8g?language=objc
[53]
CMFormatDescriptionCreate(...): https://developer.apple.com/documentation/coremedia/1489182-cmformatdescriptioncreate?language=objc
[54]
CMVideoCodecType: https://developer.apple.com/documentation/coremedia/cmvideocodectype?language=objc
[55]
CMMediaType: https://developer.apple.com/documentation/coremedia/1564193-cmmediatype/
[56]
CMVideoFormatDescription: https://developer.apple.com/documentation/coremedia/cmvideoformatdescription?language=objc
[57]
CMVideoFormatDescriptionCreate(...): https://developer.apple.com/documentation/coremedia/1489743-cmvideoformatdescriptioncreate?language=objc
[58]
CMVideoFormatDescriptionGetDimensions(...): https://developer.apple.com/documentation/coremedia/1489287-cmvideoformatdescriptiongetdimen?language=objc
[59]
Time Representation: https://developer.apple.com/documentation/coremedia?language=objc
[60]
CMTime: https://developer.apple.com/documentation/coremedia/cmtime-u58?language=objc
[61]
CMTimeRange: https://developer.apple.com/documentation/coremedia/cmtimerange-qts?language=objc
[62]
CMSampleTimingInfo: https://developer.apple.com/documentation/coremedia/cmsampletiminginfo?language=objc
[63]
Queues: https://developer.apple.com/documentation/coremedia?language=objc
[64]
CMSimpleQueue: https://developer.apple.com/documentation/coremedia/cmsimplequeue?language=objc
[65]
CMBufferQueue: https://developer.apple.com/documentation/coremedia/cmbufferqueue?language=objc
[66]
CMMemoryPool: https://developer.apple.com/documentation/coremedia/cmmemorypool-u89?language=objc
[67]
Core Video Framework: https://developer.apple.com/documentation/corevideo?language=objc
[68]
CVImageBuffer: https://developer.apple.com/documentation/corevideo/cvimagebuffer-q40
[69]
CVPixelBuffer: https://developer.apple.com/documentation/corevideo/cvpixelbuffer?language=objc
[70]
CVPixelBufferLockBaseAddress(...): https://developer.apple.com/documentation/corevideo/1457128-cvpixelbufferlockbaseaddress?language=objc
[71]
CVPixelBufferUnlockBaseAddress(...): https://developer.apple.com/documentation/corevideo/1456843-cvpixelbufferunlockbaseaddress?language=objc
[72]
CVPixelBufferLockBaseAddress(...): https://developer.apple.com/documentation/corevideo/1457115-cvpixelbuffergetbaseaddress?language=objc
[73]
CVPixelBufferGetHeight(...): https://developer.apple.com/documentation/corevideo/1456666-cvpixelbuffergetheight?language=objc
[74]
CVPixelBufferGetWidth(...): https://developer.apple.com/documentation/corevideo/1457241-cvpixelbuffergetwidth?language=objc
[75]
CVPixelBufferIsPlanar(...): https://developer.apple.com/documentation/corevideo/1456805-cvpixelbufferisplanar?language=objc
[76]
CVPixelBufferGetBaseAddressOfPlane(...): https://developer.apple.com/documentation/corevideo/1456821-cvpixelbuffergetbaseaddressofpla?language=objc
[77]
CVPixelBufferGetPlaneCount(...): https://developer.apple.com/documentation/corevideo/1456976-cvpixelbuffergetplanecount?language=objc
[78]
CVPixelBufferGetBytesPerRowOfPlane(...): https://developer.apple.com/documentation/corevideo/1456711-cvpixelbuffergetbytesperrowofpla?language=objc
[79]
CVPixelBufferGetHeightOfPlane(...): https://developer.apple.com/documentation/corevideo/1456698-cvpixelbuffergetheightofplane?language=objc
[80]
CVPixelBufferGetWidthOfPlane(...): https://developer.apple.com/documentation/corevideo/1456830-cvpixelbuffergetwidthofplane?language=objc
[81]
AVFoundation Framework: https://developer.apple.com/documentation/avfoundation?language=objc
[82]
AVCaptureDevice: https://developer.apple.com/documentation/avfoundation/avcapturedevice?language=objc
[83]
-lockForConfiguration:: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1387810-lockforconfiguration?language=objc
[84]
-unlockForConfiguration:: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1387917-unlockforconfiguration?language=objc
[85]
-activeFormat: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1389221-activeformat?language=objc
[86]
-activeVideoMinFrameDuration: https://developer.apple.com/documentation/avfoundation/avcapturedevice/1389290-activevideominframeduration?language=objc
[87]
AVCaptureDeviceInput: https://developer.apple.com/documentation/avfoundation/avcapturedeviceinput?language=objc
[88]
AVCaptureDeviceFormat: https://developer.apple.com/documentation/avfoundation/avcapturedeviceformat?language=objc
[89]
AVCaptureDevicePosition: https://developer.apple.com/documentation/avfoundation/avcapturedeviceposition?language=objc
[90]
AVCaptureSession: https://developer.apple.com/documentation/avfoundation/avcapturesession?language=objc
[91]
-sessionPreset: https://developer.apple.com/documentation/avfoundation/avcapturesession/1389696-sessionpreset?language=objc
[92]
-addInput:: https://developer.apple.com/documentation/avfoundation/avcapturesession/1387239-addinput?language=objc
[93]
-addOutput:: https://developer.apple.com/documentation/avfoundation/avcapturesession/1387325-addoutput?language=objc
[94]
-addConnection:: https://developer.apple.com/documentation/avfoundation/avcapturesession/1389687-addconnection?language=objc
[95]
AVCaptureSessionPreset: https://developer.apple.com/documentation/avfoundation/avcapturesessionpreset?language=objc
[96]
AVCaptureSessionRuntimeErrorNotification: https://developer.apple.com/documentation/avfoundation/avcapturesessionruntimeerrornotification?language=objc
[97]
AVCaptureConnection: https://developer.apple.com/documentation/avfoundation/avcaptureconnection?language=objc
[98]
-videoMirrored: https://developer.apple.com/documentation/avfoundation/avcaptureconnection/1389172-videomirrored?language=objc
[99]
preferredVideoStabilizationMode: https://developer.apple.com/documentation/avfoundation/avcaptureconnection/1620484-preferredvideostabilizationmode?language=objc
[100]
AVCaptureVideoDataOutput: https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput?language=objc
[101]
-setSampleBufferDelegate:queue:: https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput/1389008-setsamplebufferdelegate?language=objc
[102]
-alwaysDiscardsLateVideoFrames: https://developer.apple.com/documentation/avfoundation/avcapturevideodataoutput/1385780-alwaysdiscardslatevideoframes?language=objc
[103]
AVCaptureVideoPreviewLayer: https://developer.apple.com/documentation/avfoundation/avcapturevideopreviewlayer?language=objc
[104]
-setVideoGravity:: https://developer.apple.com/documentation/watchkit/wkinterfacemovie/1628130-setvideogravity?language=objc
[105]
AVAssetWriter: https://developer.apple.com/documentation/avfoundation/avassetwriter?language=objc
[106]
canAddInput:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1387863-canaddinput?language=objc
[107]
addInput:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1390389-addinput?language=objc
[108]
startWriting: https://developer.apple.com/documentation/avfoundation/avassetwriter/1386724-startwriting?language=objc
[109]
startSession(atSourceTime:): https://developer.apple.com/documentation/avfoundation/avassetwriter/1389908-startsessionatsourcetime?language=objc
[110]
endSessionAtSourceTime:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1389921-endsessionatsourcetime?language=objc
[111]
finishWritingWithCompletionHandler:: https://developer.apple.com/documentation/avfoundation/avassetwriter/1390432-finishwriting?language=objc
[112]
cancelWriting: https://developer.apple.com/documentation/avfoundation/avassetwriter/1387234-cancelwriting?language=objc
[113]
AVAssetWriterInput: https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc
[114]
expectsMediaDataInRealTime: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1387827-expectsmediadatainrealtime?language=objc
[115]
readyForMoreMediaData: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389084-readyformoremediadata?language=objc
[116]
requestMediaDataWhenReadyOnQueue:usingBlock:: https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc
[117]
appendSampleBuffer:: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389566-appendsamplebuffer?language=objc
[118]
markAsFinished: https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1390122-markasfinished?language=objc
[119]
AVAssetReader: https://developer.apple.com/documentation/avfoundation/avassetreader?language=objc
[120]
canAddOutput:: https://developer.apple.com/documentation/avfoundation/avassetreader/1387485-canaddoutput?language=objc
[121]
addOutput:: https://developer.apple.com/documentation/avfoundation/avassetreader/1390110-addoutput?language=objc
[122]
startReading: https://developer.apple.com/documentation/avfoundation/avassetreader/1390286-startreading?language=objc
[123]
cancelReading: https://developer.apple.com/documentation/avfoundation/avassetreader/1390258-cancelreading?language=objc
[124]
AVAssetReaderOutput: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput?language=objc
[125]
AVAssetReaderTrackOutput: https://developer.apple.com/documentation/avfoundation/avassetreadertrackoutput?language=objc
[126]
alwaysCopiesSampleData: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1389189-alwayscopiessampledata?language=objc
[127]
copyNextSampleBuffer: https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1385732-copynextsamplebuffer?language=objc
[128]
AVAudioSession: https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc
[129]
setCategory:withOptions:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession/1616442-setcategory?language=objc
[130]
setMode:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession/1616614-setmode?language=objc
[131]
setActive:withOptions:error:: https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc