iOS 視頻處理框架及重點(diǎn) API 合集丨音視頻工程示例

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学搜,對 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)過陳舊過時的文檔稠炬。

AVFoundation Stack on iOS .png
AVFoundation Stack on OS X .png

時至今日,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_PartialSynckCMSampleAttachmentKey_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。

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),可以參考下面兩張圖:

AVCaptureConnection 連接單或多輸入和單輸出 .png
AVCaptureSession 配置多組輸入輸出 .png
  • 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ù)了萄喳。

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ù)的接口也榄。通常我們可以使用 AVAssetReaderTrackOutputAVAssetReaderVideoCompositionOutput 等具體的實(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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末熬芜,一起剝皮案震驚了整個濱河市莲镣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌涎拉,老刑警劉巖瑞侮,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鼓拧,居然都是意外死亡半火,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門季俩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來钮糖,“玉大人,你說我怎么就攤上這事酌住〉旯椋” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵赂韵,是天一觀的道長娱节。 經(jīng)常有香客問我,道長祭示,這世上最難降的妖魔是什么肄满? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任谴古,我火速辦了婚禮,結(jié)果婚禮上稠歉,老公的妹妹穿的比我還像新娘掰担。我一直安慰自己,他們只是感情好怒炸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布带饱。 她就那樣靜靜地躺著,像睡著了一般阅羹。 火紅的嫁衣襯著肌膚如雪勺疼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天捏鱼,我揣著相機(jī)與錄音执庐,去河邊找鬼。 笑死导梆,一個胖子當(dāng)著我的面吹牛轨淌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播看尼,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼递鹉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了藏斩?” 一聲冷哼從身側(cè)響起躏结,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎灾茁,沒想到半個月后窜觉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡北专,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年禀挫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拓颓。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡语婴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出驶睦,到底是詐尸還是另有隱情砰左,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布场航,位于F島的核電站缠导,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏溉痢。R本人自食惡果不足惜僻造,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一憋他、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧髓削,春花似錦竹挡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宝泵,卻和暖如春好啰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背儿奶。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工坎怪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人廓握。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像嘁酿,于是被迫代替她去往敵國和親隙券。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容