畢加索《手里捧著鴿子的孩子》像素版
iOS/Android 客戶端開發(fā)同學(xué)如果想要開始學(xué)習(xí)音視頻開發(fā)侦讨,最絲滑的方式是對(duì)音視頻基礎(chǔ)概念知識(shí)有一定了解后,再借助 iOS/Android 平臺(tái)的音視頻能力上手去實(shí)踐音視頻的采集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染過程钦奋,并借助音視頻工具來分析和理解對(duì)應(yīng)的音視頻數(shù)據(jù)衰倦。
在音視頻工程示例這個(gè)欄目的前面 6 篇 AVDemo 文章中袒炉,我們拆解了音頻的采集 → 編碼 → 封裝 → 解封裝 → 解碼 → 渲染流程并基于 iOS 系統(tǒng) API 實(shí)現(xiàn)了 Demo:
你可以在關(guān)注本公眾號(hào)后,在公眾號(hào)發(fā)送消息『AVDemo』來獲取 Demo 的全部源碼樊零。
如果你看完這些 Demo梳杏,對(duì) iOS 平臺(tái)的音視頻開發(fā)多多少少會(huì)有一些認(rèn)識(shí)了,在這個(gè)基礎(chǔ)上我們來總結(jié)一下 iOS 音頻處理框架淹接,以及在前面的 Demo 中我們用到的主要 API 和數(shù)據(jù)結(jié)構(gòu)有哪些。
1叛溢、iOS 音頻框架
當(dāng)我們想要了解 iOS 的音頻處理框架時(shí)塑悼,以下是我們能比較容易找到的兩張官方架構(gòu)圖。它們分別出自Audio Unit Hosting Guide for iOS[1]和Core Audio Overview[2]這兩篇文檔楷掉。
iOS Audio Frameworks
Core Audio API Layers
但這兩篇文檔已經(jīng)比較陳舊了厢蒜,是多年之前的文檔,以至于和最新的 iOS 15 的框架有不少的出入烹植。而新版本的 iOS 官方技術(shù)文檔也沒有給出比較清晰的音頻架構(gòu)圖斑鸦。所以在這里我們就按照 Demo 中涉及的系統(tǒng) API 來挑選介紹幾個(gè)相關(guān)的 Framework:
Audio Unit Framework
Core Media Framework
Audio Toolbox Framework
AVFoundation Framework
2、Audio Unit Framework
Audio Unit Framework[3]:最底層的音頻處理 API草雕,功能強(qiáng)大巷屿,直接驅(qū)動(dòng)底層硬件,提供快速墩虹、模塊化的音頻處理嘱巾。當(dāng)你要實(shí)現(xiàn)低延遲的音頻處理(比如 VoIP)、對(duì)合成聲音進(jìn)行響應(yīng)式的播放(比如音樂游戲诫钓、合成樂器聲音)旬昭、實(shí)現(xiàn)特定的音頻能力(比如回聲消除、混音菌湃、聲音均衡)问拘、實(shí)現(xiàn)音頻處理鏈支持靈活組裝音頻處理單元時(shí),你可以選擇使用 Audio Unit 的 API。
需要注意的是骤坐,在最新的 iOS 系統(tǒng)庫(kù)架構(gòu)中绪杏,Audio Unit Framework 的實(shí)現(xiàn)都已經(jīng)遷移到 Audio Toolbox Framework 中去了。
下面是 Audio Unit 框架的主要模塊:
1)Audio Component Services[4]:定義了發(fā)現(xiàn)或油、開啟和關(guān)閉音頻單元(audio unit)以及音頻編解碼器的接口寞忿。
常用的數(shù)據(jù)類型:
AudioComponent[5]:表示音頻組件。一種音頻組件通常由 type顶岸、subtype腔彰、manufacturer 三屬性來唯一標(biāo)識(shí)。
AudioComponentDescription[6]:表示音頻組件的描述辖佣。其中 type霹抛、subtype、manufacturer 三屬性組合起來標(biāo)識(shí)一種音頻組件卷谈。
AudioComponentInstance[7]:表示音頻組件的實(shí)例杯拐。通常我們使用查找方法(AudioComponentFindNext)來找到符合描述的音頻組件,然后再去使用創(chuàng)建方法(AudioComponentInstanceNew)創(chuàng)建一個(gè)對(duì)應(yīng)的音頻組件實(shí)例世蔗。
常用的接口:
AudioComponentFindNext(...)[8]:用于查找符合描述的音頻組件端逼。
AudioComponentGetDescription(...)[9]:用于獲取一種音頻組件對(duì)應(yīng)的描述。
AudioComponentInstanceNew(...)[10]:創(chuàng)建一個(gè)音頻組件實(shí)例污淋。
AudioComponentInstanceDispose(...)[11]:釋放一個(gè)音頻組件實(shí)例顶滩。
2)Audio Unit Component Services[12]:提供了使用音頻單元(audio unit)的 C 語(yǔ)言接口。一個(gè)音頻單元(audio unit)是用來進(jìn)行音頻數(shù)據(jù)處理或者音頻數(shù)據(jù)生成的插件單元寸爆。要發(fā)現(xiàn)礁鲁、開啟、關(guān)閉音頻單元(audio unit)則可以使用 Audio Component Services赁豆。
常用的數(shù)據(jù)類型:
AudioUnit[13]仅醇,typedef AudioComponentInstance AudioUnit;。AudioUnit 就是一種 AudioComponentInstance魔种。
AudioUnitParameter[14]:表示 AudioUnit 的參數(shù)析二,一個(gè) AudioUnit 參數(shù)由 scope、element节预、parameterID 三屬性定義甲抖。
AudioUnitProperty[15]:表示 AudioUnit 的屬性,一個(gè) AudioUnitProperty 由 scope心铃、element准谚、propertyID 三屬性定義。
常用的接口:
AudioUnitInitialize(...)[16]:初始化一個(gè) AudioUnit去扣。如果初始化成功柱衔,說明 input/output 的格式是可支持的樊破,并且處于可以開始渲染的狀態(tài)。
AudioUnitUninitialize(...)[17]:卸載一個(gè) AudioUnit唆铐。一旦一個(gè) AudioUnit 被初始化后哲戚,要想改變它的狀態(tài)來響應(yīng)某些環(huán)境變化,就需要先卸載艾岂。這時(shí)候會(huì)使得 AudioUnit 釋放它的資源顺少。此后,調(diào)用者可以重新配置這個(gè) AudioUnit 來適配新的環(huán)境王浴,比如處理與之前不同的采樣率脆炎。在這之后,可以重新初始化這個(gè) AudioUnit 來應(yīng)用這些更改氓辣。
AudioUnitRender(...)[18]:渲染輸入的音頻數(shù)據(jù)秒裕,數(shù)據(jù)量為 inNumberOfFrames。渲染結(jié)果的數(shù)據(jù)存儲(chǔ)在 ioData 中钞啸。注意調(diào)用方需要提供音頻時(shí)間戳几蜻,這個(gè)時(shí)間戳應(yīng)該滿足單調(diào)遞增。
AudioUnitGetProperty(...)[19]:獲取 AudioUnit 的屬性体斩。
AudioUnitSetProperty(...)[20]:設(shè)置 AudioUnit 的屬性梭稚。
AudioUnitGetParameter(...)[21]:獲取 AudioUnit 的參數(shù)。
AudioUnitSetParameter(...)[22]:設(shè)置 AudioUnit 的參數(shù)絮吵。
常用的回調(diào):
AURenderCallback[23]:在以下幾種情況會(huì)被系統(tǒng)調(diào)用:當(dāng) AudioUnit 需要輸入采樣數(shù)據(jù)弧烤;在一個(gè)渲染操作前;在一個(gè)渲染操作后源武。
AudioUnitPropertyListenerProc[24]:當(dāng)一個(gè)指定的 AudioUnit 的 property 發(fā)生改變時(shí),會(huì)被系統(tǒng)調(diào)用想幻。
3)Output Audio Unit Services[25]:提供了 start粱栖、stop 用于 I/O 的音頻單元(通常是用于輸出的音頻單元)的 C 語(yǔ)言接口。
常用的接口:
AudioOutputUnitStart(...)[26]:?jiǎn)?dòng)一個(gè) I/O AudioUnit脏毯,同時(shí)會(huì)啟動(dòng)與之連接的 AudioUnit Processing Graph闹究。
AudioOutputUnitStop(...)[27]:關(guān)閉一個(gè) I/O AudioUnit,同時(shí)會(huì)關(guān)閉與之連接的 AudioUnit Processing Graph食店。
3渣淤、Core Media Framework
Core Media Framework[28]:定義和封裝了 AVFoundation 等更上層的媒體框架需要的媒體處理流水線(包含時(shí)間信息)以及其中使用的接口和數(shù)據(jù)類型。使用 Core Media 層的接口和數(shù)據(jù)類型可以高效的處理媒體采樣數(shù)據(jù)吉嫩、管理采樣數(shù)據(jù)隊(duì)列价认。下面是 Core Media 框架的主要模塊:
1)Sample Processing[29]:采樣數(shù)據(jù)處理。常用的數(shù)據(jù)類型:
CMSampleBuffer[30]:系統(tǒng)用來在音視頻處理的 pipeline 中使用和傳遞媒體采樣數(shù)據(jù)的核心數(shù)據(jù)結(jié)構(gòu)自娩。你可以認(rèn)為它是 iOS 音視頻處理 pipeline 中的流通貨幣用踩,攝像頭采集的視頻數(shù)據(jù)接口、麥克風(fēng)采集的音頻數(shù)據(jù)接口、編碼和解碼數(shù)據(jù)接口脐彩、讀取和存儲(chǔ)視頻接口碎乃、視頻渲染接口等等,都以它作為參數(shù)惠奸。通常梅誓,CMSampleBuffer 中要么包含一個(gè)或多個(gè)媒體采樣的 CMBlockBuffer,要么包含一個(gè) CVImageBuffer佛南。
CMSampleBufferCreateReady(...)[31]:基于媒體數(shù)據(jù)創(chuàng)建一個(gè) CMSampleBuffer梗掰。
CMSampleBufferCreate(...)[32]:創(chuàng)建一個(gè) CMSampleBuffer,支持設(shè)置數(shù)據(jù)已準(zhǔn)備好的回調(diào)共虑。
CMSampleBufferSetDataBufferFromAudioBufferList(...)[33]:為指定的 CMSampleBuffer 創(chuàng)建其對(duì)應(yīng)的 CMBlockBuffer愧怜,其中的數(shù)據(jù)拷貝自 AudioBufferList。
CMSampleBufferGetFormatDescription(...)[34]:返回 CMSampleBuffer 中的采樣數(shù)據(jù)對(duì)應(yīng)的 CMFormatDescription妈拌。
CMSampleBufferGetDataBuffer(...)[35]:返回 CMSampleBuffer 中的 CMBlockBuffer拥坛。注意調(diào)用方不會(huì)持有返回的 CMBlockBuffer,如果想要維護(hù)指向它的指針尘分,需要顯式 retain 一下猜惋。
CMSampleBufferGetPresentationTimeStamp(...)[36]:獲取 CMSampleBuffer 中所有采樣的最小的 pts 時(shí)間戳。
CMBlockBuffer[37]:一個(gè)或多個(gè)媒體采樣的的裸數(shù)據(jù)培愁。其中可以封裝:音頻采集后著摔、編碼后、解碼后的數(shù)據(jù)(如:PCM 數(shù)據(jù)定续、AAC 數(shù)據(jù))谍咆;視頻編碼后的數(shù)據(jù)(如:H.264 數(shù)據(jù))。
CMBlockBufferCreateWithMemoryBlock(...)[38]:基于內(nèi)存數(shù)據(jù)創(chuàng)建一個(gè) CMBlockBuffer私股。
CMBlockBufferGetDataPointer(...)[39]:獲取訪問 CMBlockBuffer 中數(shù)據(jù)的地址摹察。
CMFormatDescription[40]:用于描述 CMSampleBuffer 中采樣的格式信息。
CMFormatDescriptionCreate(...)[41]:創(chuàng)建一個(gè) CMFormatDescription倡鲸。
CMAudioFormatDescription[42]:typedef CMFormatDescriptionRef CMAudioFormatDescriptionRef;供嚎。CMAudioFormatDescription 是一種 CMFormatDescriptionRef。
CMAudioFormatDescriptionCreate(...)[43]:基于 AudioStreamBasicDescription 來創(chuàng)建一個(gè) CMAudioFormatDescription峭状。
CMAudioFormatDescriptionGetStreamBasicDescription(...)[44]:返回一個(gè)指向 CMFormatDescription(通常應(yīng)該是一個(gè) CMAudioFormatDescription) 中的 AudioStreamBasicDescription 的指針克滴。如果是非音頻格式,就返回 NULL优床。
CMAttachment[45]:為 CMSampleBuffer 添加支持的 metadata劝赔。
這里我們還要補(bǔ)充介紹 CoreAudioTypes Framework 中的幾種數(shù)據(jù)類型:
AudioStreamBasicDescription[46]:用于描述音頻流數(shù)據(jù)格式信息,比如采樣位深胆敞、聲道數(shù)望忆、采樣率罩阵、每幀字節(jié)數(shù)、每包幀數(shù)启摄、每包字節(jié)數(shù)稿壁、格式標(biāo)識(shí)等。
AudioBuffer[47]:存儲(chǔ)并描述音頻數(shù)據(jù)的緩沖區(qū)歉备。mData 中存儲(chǔ)著數(shù)據(jù)傅是。可以存儲(chǔ)兩種不同類型的音頻:1)單聲道音頻數(shù)據(jù)蕾羊;2)多聲道的交錯(cuò)音頻數(shù)據(jù)喧笔,這時(shí)候 mNumberChannels 指定了聲道數(shù)。
AudioBufferList[48]:一組 AudioBuffer龟再。
AudioTimeStamp[49]:從多維度來表示一個(gè)時(shí)間戳的數(shù)據(jù)結(jié)構(gòu)书闸。
2)Time Representation[50]:時(shí)間信息表示。常用的數(shù)據(jù)類型:
CMTime[51]:用 value/timescale 的方式表示時(shí)間利凑。這樣可以解決浮點(diǎn)運(yùn)算時(shí)的精度損失問題浆劲。timescale 表示時(shí)間刻度,通常在處理視頻內(nèi)容時(shí)常見的時(shí)間刻度為 600哀澈,這是大部分常用視頻幀率 24fps牌借、25fps、30fps 的公倍數(shù)割按,音頻數(shù)據(jù)常見的時(shí)間刻度就是采樣率膨报,比如 44100 或 48000。
CMTimeRange[52]:用 start+duration 的方式表示一段時(shí)間适荣。
CMSampleTimingInfo[53]:一個(gè) CMSampleBuffer 的時(shí)間戳信息现柠,包括 pts、dts弛矛、duration够吩。
3)Queues[54]:數(shù)據(jù)容器。常用的數(shù)據(jù)類型:
CMSimpleQueue[55]:一個(gè)簡(jiǎn)單地汪诉、無鎖的 FIFO 隊(duì)列废恋,可以放(void *)元素谈秫,元素不能是 NULL 或 0扒寄,如果元素是指向分配內(nèi)存的指針,其內(nèi)存生命周期要在外面自己管理拟烫「帽啵可以用作音視頻采樣數(shù)據(jù)(CMSampleBufferRef)的隊(duì)列,不過要自己加鎖硕淑。
CMBufferQueue[56]:支持存儲(chǔ)任何 CFTypeRef 類型的數(shù)據(jù)课竣,但是數(shù)據(jù)類型需要有 duration 的概念嘉赎,在創(chuàng)建 CMBufferQueue 的時(shí)候,會(huì)有一些回調(diào)于樟,其中一個(gè)必須的回調(diào)是要返回隊(duì)列中對(duì)象的 duration公条。CMBufferQueue 是設(shè)計(jì)用于在生產(chǎn)者/消費(fèi)者模型中在不同的線程中讀寫數(shù)據(jù)。通常是兩個(gè)線程(一個(gè)是生產(chǎn)者入隊(duì)線程迂曲,一個(gè)是消費(fèi)者出隊(duì)線程)靶橱,當(dāng)然更多的線程也是可以的。
CMMemoryPool[57]:內(nèi)存池容器路捧,對(duì)使用大塊的內(nèi)存有優(yōu)化关霸。一個(gè) CMMemoryPool 的實(shí)例實(shí)際上維護(hù)一個(gè)最近釋放內(nèi)存的池子用于內(nèi)存分配服務(wù)。這樣的目的是加快隨后的內(nèi)存分配杰扫。在需要重復(fù)分配大塊內(nèi)存時(shí)队寇,比如輸出視頻編碼數(shù)據(jù),可以使用這個(gè)數(shù)據(jù)結(jié)構(gòu)章姓。
4佳遣、Audio Toolbox Framework
Audio Toolbox Framework[58]:提供了音頻錄制、播放啤覆、流解析苍日、編碼格式轉(zhuǎn)換、Audio Session 管理等功能接口窗声。下面是 Audio Toolbox 框架的主要模塊:
1)Audio Units[59]:音頻單元相恃。關(guān)于 Audio Unit 的內(nèi)容,還可以參考上面講到的 Audio Unit Framework笨觅。
Audio Unit v3 Plug-Ins[60]:基于 AUv3 應(yīng)用擴(kuò)展接口來提供自定義的音效拦耐、樂器及其他音頻能力。
Audio Components[61]:定義了發(fā)現(xiàn)见剩、加載杀糯、配置和關(guān)閉音頻組件(包括音頻單元(audio unit)、音頻編解碼器(audio codec))的接口苍苞。
Audio Unit v2 (C) API[62]:配置一個(gè)音頻單元(audio unit)以及進(jìn)行音頻渲染固翰。
Audio Unit Properties[63]:獲取有關(guān)內(nèi)置混音器、均衡器羹呵、濾波器骂际、特效及音頻應(yīng)用擴(kuò)展的信息。
Audio Unit Voice I/O[64]:配置系統(tǒng)語(yǔ)音處理冈欢、響應(yīng)語(yǔ)音事件歉铝。
2)Playback and Recording[65]:音頻播放和錄制。
Audio Queue Services[66]:提供了簡(jiǎn)單的凑耻、低開銷的方式來錄制和播放音頻的 C 語(yǔ)言接口太示。支持 Linear PCM柠贤、AAC 的錄制和播放。實(shí)現(xiàn)了連接音頻硬件类缤、管理內(nèi)存臼勉、根據(jù)需要使用解碼器解碼音頻、調(diào)解錄音和播放餐弱。但是要實(shí)現(xiàn)低延遲坚俗、回聲消除、混音等功能岸裙,還得使用 AudioUnit猖败。
Audio Services[67]:提供了一組 C 語(yǔ)言接口來實(shí)現(xiàn)播放短聲或觸發(fā) iOS 設(shè)備的振動(dòng)效果。
Music Player[68]:支持播放一組音軌降允,并管理播放的各種的事件恩闻。
3)Audio Files and Formats[69]:音頻文件和格式。
Audio Format Services[70]:獲取音頻格式和編解碼器的信息剧董。
Audio File Services[71]:從磁盤或內(nèi)存讀寫各種音頻數(shù)據(jù)幢尚。
Extended Audio File Services[72]:通過組合 Audio File Services 和 Audio Converter Services 來提供讀寫音頻編碼文件或 LPCM 音頻文件。
Audio File Stream Services[73]:解析音頻流數(shù)據(jù)翅楼。
Audio File Components[74]:獲取音頻文件格式以及文件中包含的數(shù)據(jù)的信息尉剩。
Core Audio File Format[75]:解析 Core Audio 文件的結(jié)構(gòu)。
4)Utilities[76]:其他音頻功能支持毅臊。
Audio Converter Services[77]:音頻編解碼理茎。支持 LPCM 各種格式轉(zhuǎn)換靶庙,以及 LPCM 與編碼格式(如 AAC)的轉(zhuǎn)換闻妓。常用的接口:
AudioConverterNew(...)[78]:根據(jù)指定的輸入和輸出音頻格式創(chuàng)建對(duì)應(yīng)的轉(zhuǎn)換器(編解碼器)實(shí)例示绊。
AudioConverterNewSpecific(...)[79]:根據(jù)指定的 codec 來創(chuàng)建一個(gè)新的音頻轉(zhuǎn)換器(編解碼器)實(shí)例莉兰。
AudioConverterReset(...)[80]:重置音頻轉(zhuǎn)換器(編解碼器)實(shí)例,并清理它的緩沖區(qū)逗爹。
AudioConverterDispose(...)[81]:釋放音頻轉(zhuǎn)換器(編解碼器)實(shí)例飒硅。
AudioConverterGetProperty(...)[82]:獲取音頻轉(zhuǎn)換器(編解碼器)的屬性徒蟆。
AudioConverterSetProperty(...)[83]:設(shè)置音頻轉(zhuǎn)換器(編解碼器)的屬性胎挎。
AudioConverterConvertBuffer(...)[84]:只用于一種特殊的情況下將音頻數(shù)據(jù)從一種 LPCM 格式轉(zhuǎn)換為另外一種沟启,并且前后采樣率一致。這個(gè)接口不支持大多數(shù)壓縮編碼格式犹菇。
AudioConverterFillComplexBuffer(...)[85]:轉(zhuǎn)換(編碼)回調(diào)函數(shù)提供的音頻數(shù)據(jù)德迹,支持不交錯(cuò)和包格式。大部分情況下都建議用這個(gè)接口项栏,除非是要將音頻數(shù)據(jù)從一種 LPCM 格式轉(zhuǎn)換為另外一種浦辨。
AudioConverterComplexInputDataProc[86]:為AudioConverterFillComplexBuffer(...)接口提供輸入數(shù)據(jù)的回調(diào)蹬竖。
Audio Codec[87]:提供了支持將音頻數(shù)據(jù)進(jìn)行編碼格式轉(zhuǎn)換的 API沼沈。具體支持哪些編碼格式取決于系統(tǒng)提供了哪些編解碼器流酬。
5、AVFoundation Framework
AVFoundation Framework[88]是更上層的面向?qū)ο蟮囊粋€(gè)音視頻處理框架列另。它提供了音視頻資源管理芽腾、相機(jī)設(shè)備管理、音視頻處理页衙、系統(tǒng)級(jí)音頻交互管理的能力摊滔,功能非常強(qiáng)大。如果對(duì)其功能進(jìn)行細(xì)分店乐,可以分為如下幾個(gè)模塊:
Assets艰躺,音視頻資源管理。
Playback眨八,媒體播放及自定義播放行為支持腺兴。
Capture,內(nèi)置及外置的相機(jī)廉侧、麥克風(fēng)等采集設(shè)備管理页响,圖片、音視頻錄制段誊。
Editing闰蚕,音視頻編輯。
Audio连舍,音頻播放没陡、錄制和處理,App 系統(tǒng)音頻行為配置索赏。
Speech诗鸭,文本語(yǔ)音轉(zhuǎn)換。
在我們前面的 Demo 中封裝 Muxer 和 Demuxer 及設(shè)置 AudioSession 時(shí)會(huì)用到 AVFoundation Framework 的一些能力参滴,我們這里對(duì)應(yīng)地介紹一下强岸。
AVAssetWriter[89]:支持將媒體數(shù)據(jù)寫入 QuickTime 或 MPEG-4 格式的文件中,支持對(duì)多軌道的媒體數(shù)據(jù)進(jìn)行交錯(cuò)處理來提高播放和存儲(chǔ)的效率砾赔,支持對(duì)媒體采樣進(jìn)行轉(zhuǎn)碼蝌箍,支持寫入 metadata。需要注意的是暴心,一個(gè) AVAssetWriter 實(shí)例只能對(duì)應(yīng)寫一個(gè)文件妓盲,如果要寫入多個(gè)文件,需要?jiǎng)?chuàng)建多個(gè) AVAssetWriter 實(shí)例专普。
canAddInput:[90]:檢查 AVAssetWriter 是否支持添加對(duì)應(yīng)的 AVAssetWriterInput悯衬。
addInput:[91]:給 AVAssetWriter 添加一個(gè) AVAssetWriterInput。注意必須在 AVAssetWriter 開始寫入之前添加檀夹。
startWriting[92]:開始寫入筋粗。必須在配置好 AVAssetWriter 添加完 AVAssetWriterInput 做好準(zhǔn)備后再調(diào)用這個(gè)方法策橘。在調(diào)用完這個(gè)方法后,需要調(diào)用startSessionAtSourceTime:開始寫入會(huì)話娜亿,此后就可以使用對(duì)應(yīng)的 AVAssetWriterInput 來寫入媒體采樣數(shù)據(jù)丽已。
startSessionAtSourceTime:[93]:開啟寫入會(huì)話。在startWriting后調(diào)用买决,在寫入媒體采樣數(shù)據(jù)之前調(diào)用沛婴。
endSessionAtSourceTime:[94]:結(jié)束寫入會(huì)話。結(jié)束時(shí)間是會(huì)話結(jié)束時(shí)樣本數(shù)據(jù)在時(shí)間軸上的時(shí)刻督赤。如果沒有顯示調(diào)用這個(gè)方法嘁灯,系統(tǒng)會(huì)在你調(diào)用finishWritingWithCompletionHandler: 結(jié)束寫入時(shí)自動(dòng)調(diào)用。
finishWritingWithCompletionHandler:[95]:標(biāo)記 AVAssetWriter 的所有 input 為結(jié)束躲舌,完成寫入旁仿。為了保證 AVAssetWriter 完成所有采樣數(shù)據(jù)的寫入,要在調(diào)用添加數(shù)據(jù)正確返回后調(diào)用這個(gè)方法孽糖。
cancelWriting[96]:取消創(chuàng)建輸出文件枯冈。如果 AVAssetWriter 的狀態(tài)是 Failed 或 Completed,調(diào)用這個(gè)方法無效办悟,否則尘奏,調(diào)用它會(huì)阻塞調(diào)用線程,直到會(huì)話取消完成病蛉。如果 AVAssetWriter 已經(jīng)創(chuàng)建了輸出文件炫加,調(diào)用這個(gè)方法會(huì)刪除這個(gè)文件。
AVAssetWriterInput[97]:用于向 AVAssetWriter 實(shí)例的輸出文件的一個(gè)軌道添加媒體采樣數(shù)據(jù)铺然。一個(gè)實(shí)例只能對(duì)應(yīng)一個(gè)軌道媒體數(shù)據(jù)或 metadata 數(shù)據(jù)的寫入俗孝,當(dāng)使用多個(gè)實(shí)例向多個(gè)軌道寫入數(shù)據(jù)時(shí),需要注意檢查 AVAssetWriterInput 的 readyForMoreMediaData 屬性魄健。
expectsMediaDataInRealTime[98]:輸入是否為實(shí)時(shí)數(shù)據(jù)源赋铝,比如相機(jī)采集。當(dāng)設(shè)置這個(gè)值為 YES 時(shí)沽瘦,會(huì)優(yōu)化用于實(shí)時(shí)使用的輸入來精準(zhǔn)計(jì)算readyForMoreMediaData的狀態(tài)革骨。
readyForMoreMediaData[99]:表示 AVAssetWriterInput 是否已經(jīng)準(zhǔn)備好接收媒體數(shù)據(jù)。
requestMediaDataWhenReadyOnQueue:usingBlock:[100]:告訴 AVAssetWriterInput 在方便的時(shí)候去請(qǐng)求數(shù)據(jù)并寫入輸出文件析恋。在對(duì)接拉取式的數(shù)據(jù)源時(shí)良哲,可以用這個(gè)方法。
appendSampleBuffer:[101]:通過 AVAssetWriterInput 向輸出文件添加媒體數(shù)據(jù)助隧,但是添加之前媒體數(shù)據(jù)的順序需要自己處理筑凫。注意,調(diào)用這個(gè)方法添加采樣數(shù)據(jù)后,不要更改采樣數(shù)據(jù)的內(nèi)容巍实。
markAsFinished[102]:標(biāo)記 AVAssetWriterInput 為完成滓技,表示已經(jīng)完成向它添加媒體數(shù)據(jù)了。
AVAssetReader[103]:用于從 AVAsset 資源中讀取媒體數(shù)據(jù)蔫浆。這個(gè) AVAsset 可以是 QuickTime 或 MPEG-4 文件,也可以是編輯創(chuàng)作的 AVComposition姐叁。
canAddOutput:[104]:檢查 AVAssetReader 是否支持添加對(duì)應(yīng)的 AVAssetReaderOutput瓦盛。
addOutput:[105]:給 AVAssetReader 添加一個(gè) AVAssetReaderOutput。注意必須在 AVAssetReader 開始讀取之前添加外潜。
startReading[106]:開始讀取原环。
cancelReading[107]:在讀完數(shù)據(jù)之前取消讀取可以調(diào)用這個(gè)接口。
AVAssetReaderOutput[108]:一個(gè)抽象類处窥,定義了從 AVAsset 資源中讀取媒體采樣數(shù)據(jù)的接口嘱吗。通常我們可以使用AVAssetReaderTrackOutput、AVAssetReaderVideoCompositionOutput等具體的實(shí)現(xiàn)類滔驾。
AVAssetReaderTrackOutput[109]:
alwaysCopiesSampleData[110]:是否總是拷貝采樣數(shù)據(jù)谒麦。如果要修改讀取的采樣數(shù)據(jù),可以設(shè)置 YES哆致,否則就設(shè)置 NO绕德,這樣性能會(huì)更好。
copyNextSampleBuffer[111]:從 Output 拷貝下一個(gè) CMSampleBuffer摊阀。
AVAudioSession[112]:在最新版本的 iOS 系統(tǒng)庫(kù)中耻蛇,AVAudioSession 已經(jīng)遷移到 AVFAudio Framework 中了。AVAudioSession 是系統(tǒng)用來管理 App 對(duì)音頻硬件資源的使用的胞此,比如:設(shè)置當(dāng)前 App 與其他 App 同時(shí)使用音頻時(shí)臣咖,是否混音、打斷或降低其他 App 的聲音漱牵;手機(jī)靜音鍵打開時(shí)是否還可以播放聲音夺蛇;指定音頻輸入或者輸出設(shè)備;是否支持錄制或邊錄制邊播放酣胀;聲音被打斷時(shí)的通知蚊惯。我們這里只簡(jiǎn)單介紹下 Demo 中用到的接口:
setCategory:withOptions:error:[113]:設(shè)置 AudioSession 的類型和選項(xiàng)參數(shù)。比如類型為 AVAudioSessionCategoryPlayback 表示支持播放灵临;AVAudioSessionCategoryPlayAndRecord 表示同時(shí)支持播放和錄制等等截型。
setMode:error:[114]:設(shè)置 AudioSession 的模式。AudioSession 的類型和模式一起決定了 App 如何使用音頻儒溉。通常需要在激活 AudioSession 之前設(shè)置類型和模式宦焦。比如模式為 AVAudioSessionModeVideoRecording 表示當(dāng)期要錄制視頻;AVAudioSessionModeVoiceChat 表示語(yǔ)音聊天。
setActive:withOptions:error:[115]:激活或釋放 AudioSession 的使用波闹。
以上這些框架及 API 基本上可以覆蓋我們?cè)谇懊娴?Demo 中用到的能力了酝豪。
參考資料
[1]Audio Unit Hosting Guide for iOS:https://developer.apple.com/library/archive/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/AudioUnitHostingFundamentals/AudioUnitHostingFundamentals.html
[2]Core Audio Overview:https://developer.apple.com/library/archive/documentation/MusicAudio/Conceptual/CoreAudioOverview/CoreAudioEssentials/CoreAudioEssentials.html
[3]Audio Unit:https://developer.apple.com/documentation/audiounit?language=objc
[4]Audio Component Services:https://developer.apple.com/documentation/audiounit/audio_component_services?language=objc
[5]AudioComponent:https://developer.apple.com/documentation/audiotoolbox/audiocomponent?language=objc
[6]AudioComponentDescription:https://developer.apple.com/documentation/audiotoolbox/audiocomponentdescription?language=objc
[7]AudioComponentInstance:https://developer.apple.com/documentation/audiotoolbox/audiocomponentinstance?language=objc
[8]AudioComponentFindNext(...):https://developer.apple.com/documentation/audiotoolbox/1410445-audiocomponentfindnext?language=objc
[9]AudioComponentGetDescription(...):https://developer.apple.com/documentation/audiotoolbox/1410523-audiocomponentgetdescription?language=objc
[10]AudioComponentInstanceNew(...):https://developer.apple.com/documentation/audiotoolbox/1410465-audiocomponentinstancenew?language=objc
[11]AudioComponentInstanceDispose(...):https://developer.apple.com/documentation/audiotoolbox/1410508-audiocomponentinstancedispose
[12]Audio Unit Component Services:https://developer.apple.com/documentation/audiounit/audio_component_services?language=objc
[13]AudioUnit:https://developer.apple.com/documentation/audiotoolbox/audiounit?language=objc
[14]AudioUnitParameter:https://developer.apple.com/documentation/audiotoolbox/audiounitparameter?language=objc
[15]AudioUnitProperty:https://developer.apple.com/documentation/audiotoolbox/audiounitproperty?language=objc
[16]AudioUnitInitialize(...):https://developer.apple.com/documentation/audiotoolbox/1439851-audiounitinitialize?language=objc
[17]AudioUnitUninitialize(...):https://developer.apple.com/documentation/audiotoolbox/1438415-audiounituninitialize?language=objc
[18]AudioUnitRender(...):https://developer.apple.com/documentation/audiotoolbox/1438430-audiounitrender?language=objc
[19]AudioUnitGetProperty(...):https://developer.apple.com/documentation/audiotoolbox/1439840-audiounitgetproperty?language=objc
[20]AudioUnitSetProperty(...):https://developer.apple.com/documentation/audiotoolbox/1440371-audiounitsetproperty?language=objc
[21]AudioUnitGetParameter(...):https://developer.apple.com/documentation/audiotoolbox/1440055-audiounitgetparameter?language=objc
[22]AudioUnitSetParameter(...):https://developer.apple.com/documentation/audiotoolbox/1438454-audiounitsetparameter?language=objc
[23]AURenderCallback:https://developer.apple.com/documentation/audiotoolbox/aurendercallback?language=objc
[24]AudioUnitPropertyListenerProc:https://developer.apple.com/documentation/audiotoolbox/audiounitpropertylistenerproc?language=objc
[25]Output Audio Unit Services:https://developer.apple.com/documentation/audiounit/output_audio_unit_services?language=objc
[26]AudioOutputUnitStart(...):https://developer.apple.com/documentation/audiotoolbox/1439763-audiooutputunitstart?language=objc
[27]AudioOutputUnitStop(...):https://developer.apple.com/documentation/audiotoolbox/1440513-audiooutputunitstop?language=objc
[28]Core Media:https://developer.apple.com/documentation/coremedia?language=objc
[29]Sample Processing:https://developer.apple.com/documentation/coremedia?language=objc
[30]CMSampleBuffer:https://developer.apple.com/documentation/coremedia/cmsamplebuffer-u71?language=objc
[31]CMSampleBufferCreateReady(...):https://developer.apple.com/documentation/coremedia/1489513-cmsamplebuffercreateready?language=objc
[32]CMSampleBufferCreate(...):https://developer.apple.com/documentation/coremedia/1489723-cmsamplebuffercreate?language=objc
[33]CMSampleBufferSetDataBufferFromAudioBufferList(...):https://developer.apple.com/documentation/coremedia/1489725-cmsamplebuffersetdatabufferfroma?language=objc
[34]CMSampleBufferGetFormatDescription(...):https://developer.apple.com/documentation/coremedia/1489185-cmsamplebuffergetformatdescripti?language=objc
[35]CMSampleBufferGetDataBuffer(...):https://developer.apple.com/documentation/coremedia/1489629-cmsamplebuffergetdatabuffer?language=objc
[36]CMSampleBufferGetPresentationTimeStamp(...):https://developer.apple.com/documentation/coremedia/1489252-cmsamplebuffergetpresentationtim?language=objc
[37]CMBlockBuffer:https://developer.apple.com/documentation/coremedia/cmblockbuffer-u9i?language=objc
[38]CMBlockBufferCreateWithMemoryBlock(...):https://developer.apple.com/documentation/coremedia/1489501-cmblockbuffercreatewithmemoryblo?language=objc
[39]CMBlockBufferGetDataPointer(...):https://developer.apple.com/documentation/coremedia/1489264-cmblockbuffergetdatapointer?language=objc
[40]CMFormatDescription:https://developer.apple.com/documentation/coremedia/cmformatdescription-u8g?language=objc
[41]CMFormatDescriptionCreate(...):https://developer.apple.com/documentation/coremedia/1489182-cmformatdescriptioncreate?language=objc
[42]CMAudioFormatDescription:https://developer.apple.com/documentation/coremedia/cmaudioformatdescription?language=objc
[43]CMAudioFormatDescriptionCreate(...):https://developer.apple.com/documentation/coremedia/1489522-cmaudioformatdescriptioncreate?language=objc
[44]CMAudioFormatDescriptionGetStreamBasicDescription(...):https://developer.apple.com/documentation/coremedia/1489226-cmaudioformatdescriptiongetstrea?language=objc
[45]CMAttachment:https://developer.apple.com/documentation/coremedia/cmattachment?language=objc
[46]AudioStreamBasicDescription:https://developer.apple.com/documentation/coreaudiotypes/audiostreambasicdescription?language=objc
[47]AudioBuffer:https://developer.apple.com/documentation/coreaudiotypes/audiobuffer?language=objc
[48]AudioBufferList:https://developer.apple.com/documentation/coreaudiotypes/audiobufferlist?language=objc
[49]AudioTimeStamp:https://developer.apple.com/documentation/coreaudiotypes/audiotimestamp?language=objc
[50]Time Representation:https://developer.apple.com/documentation/coremedia?language=objc
[51]CMTime:https://developer.apple.com/documentation/coremedia/cmtime-u58?language=objc
[52]CMTimeRange:https://developer.apple.com/documentation/coremedia/cmtimerange-qts?language=objc
[53]CMSampleTimingInfo:https://developer.apple.com/documentation/coremedia/cmsampletiminginfo?language=objc
[54]Queues:https://developer.apple.com/documentation/coremedia?language=objc
[55]CMSimpleQueue:https://developer.apple.com/documentation/coremedia/cmsimplequeue?language=objc
[56]CMBufferQueue:https://developer.apple.com/documentation/coremedia/cmbufferqueue?language=objc
[57]CMMemoryPool:https://developer.apple.com/documentation/coremedia/cmmemorypool-u89?language=objc
[58]Audio Toolbox:https://developer.apple.com/documentation/audiotoolbox?language=objc
[59]Audio Units:https://developer.apple.com/documentation/audiotoolbox?language=objc
[60]Audio Unit v3 Plug-Ins:https://developer.apple.com/documentation/audiotoolbox/audio_unit_v3_plug-ins?language=objc
[61]Audio Components:https://developer.apple.com/documentation/audiotoolbox/audio_components?language=objc
[62]Audio Unit v2 (C) API:https://developer.apple.com/documentation/audiotoolbox/audio_unit_v2_c_api?language=objc
[63]Audio Unit Properties:https://developer.apple.com/documentation/audiotoolbox/audio_unit_properties?language=objc
[64]Audio Unit Voice I/O:https://developer.apple.com/documentation/audiotoolbox/audio_unit_voice_i_o?language=objc
[65]Playback and Recording:https://developer.apple.com/documentation/audiotoolbox?language=objc
[66]Audio Queue Services:https://developer.apple.com/documentation/audiotoolbox/audio_queue_services?language=objc
[67]Audio Services:https://developer.apple.com/documentation/audiotoolbox/audio_services?language=objc
[68]Music Player:https://developer.apple.com/documentation/audiotoolbox/music_player?language=objc
[69]Audio Files and Formats:https://developer.apple.com/documentation/audiotoolbox?language=objc
[70]Audio Format Services:https://developer.apple.com/documentation/audiotoolbox/audio_format_services?language=objc
[71]Audio File Services:https://developer.apple.com/documentation/audiotoolbox/audio_file_services?language=objc
[72]Extended Audio File Services:https://developer.apple.com/documentation/audiotoolbox/extended_audio_file_services?language=objc
[73]Audio File Stream Services:https://developer.apple.com/documentation/audiotoolbox/audio_file_stream_services?language=objc
[74]Audio File Components:https://developer.apple.com/documentation/audiotoolbox/audio_file_components?language=objc
[75]Core Audio File Format:https://developer.apple.com/documentation/audiotoolbox/core_audio_file_format?language=objc
[76]Utilities:https://developer.apple.com/documentation/audiotoolbox?language=objc
[77]Audio Converter Services:https://developer.apple.com/documentation/audiotoolbox/audio_converter_services?language=objc
[78]AudioConverterNew(...):https://developer.apple.com/documentation/audiotoolbox/1502936-audioconverternew?language=objc
[79]AudioConverterNewSpecific(...):https://developer.apple.com/documentation/audiotoolbox/1503356-audioconverternewspecific?language=objc
[80]AudioConverterReset(...):https://developer.apple.com/documentation/audiotoolbox/1503102-audioconverterreset?language=objc
[81]AudioConverterDispose(...):https://developer.apple.com/documentation/audiotoolbox/1502671-audioconverterdispose?language=objc
[82]AudioConverterGetProperty(...):https://developer.apple.com/documentation/audiotoolbox/1502731-audioconvertergetproperty?language=objc
[83]AudioConverterSetProperty(...):https://developer.apple.com/documentation/audiotoolbox/1501675-audioconvertersetproperty?language=objc
[84]AudioConverterConvertBuffer(...):https://developer.apple.com/documentation/audiotoolbox/1503345-audioconverterconvertbuffer?language=objc
[85]AudioConverterFillComplexBuffer(...):https://developer.apple.com/documentation/audiotoolbox/1503098-audioconverterfillcomplexbuffer?language=objc
[86]AudioConverterComplexInputDataProc:https://developer.apple.com/documentation/audiotoolbox/audioconvertercomplexinputdataproc?language=objc
[87]Audio Codec:https://developer.apple.com/documentation/audiotoolbox/audio_codec?language=objc
[88]AVFoundation Framework:https://developer.apple.com/documentation/avfoundation?language=objc
[89]AVAssetWriter:https://developer.apple.com/documentation/avfoundation/avassetwriter?language=objc
[90]canAddInput::https://developer.apple.com/documentation/avfoundation/avassetwriter/1387863-canaddinput?language=objc
[91]addInput::https://developer.apple.com/documentation/avfoundation/avassetwriter/1390389-addinput?language=objc
[92]startWriting:https://developer.apple.com/documentation/avfoundation/avassetwriter/1386724-startwriting?language=objc
[93]startSession(atSourceTime:):https://developer.apple.com/documentation/avfoundation/avassetwriter/1389908-startsessionatsourcetime?language=objc
[94]endSessionAtSourceTime::https://developer.apple.com/documentation/avfoundation/avassetwriter/1389921-endsessionatsourcetime?language=objc
[95]finishWritingWithCompletionHandler::https://developer.apple.com/documentation/avfoundation/avassetwriter/1390432-finishwriting?language=objc
[96]cancelWriting:https://developer.apple.com/documentation/avfoundation/avassetwriter/1387234-cancelwriting?language=objc
[97]AVAssetWriterInput:https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc
[98]expectsMediaDataInRealTime:https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1387827-expectsmediadatainrealtime?language=objc
[99]readyForMoreMediaData:https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389084-readyformoremediadata?language=objc
[100]requestMediaDataWhenReadyOnQueue:usingBlock::https://developer.apple.com/documentation/avfoundation/avassetwriterinput?language=objc
[101]appendSampleBuffer::https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1389566-appendsamplebuffer?language=objc
[102]markAsFinished:https://developer.apple.com/documentation/avfoundation/avassetwriterinput/1390122-markasfinished?language=objc
[103]AVAssetReader:https://developer.apple.com/documentation/avfoundation/avassetreader?language=objc
[104]canAddOutput::https://developer.apple.com/documentation/avfoundation/avassetreader/1387485-canaddoutput?language=objc
[105]addOutput::https://developer.apple.com/documentation/avfoundation/avassetreader/1390110-addoutput?language=objc
[106]startReading:https://developer.apple.com/documentation/avfoundation/avassetreader/1390286-startreading?language=objc
[107]cancelReading:https://developer.apple.com/documentation/avfoundation/avassetreader/1390258-cancelreading?language=objc
[108]AVAssetReaderOutput:https://developer.apple.com/documentation/avfoundation/avassetreaderoutput?language=objc
[109]AVAssetReaderTrackOutput:https://developer.apple.com/documentation/avfoundation/avassetreadertrackoutput?language=objc
[110]alwaysCopiesSampleData:https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1389189-alwayscopiessampledata?language=objc
[111]copyNextSampleBuffer:https://developer.apple.com/documentation/avfoundation/avassetreaderoutput/1385732-copynextsamplebuffer?language=objc
[112]AVAudioSession:https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc
[113]setCategory:withOptions:error::https://developer.apple.com/documentation/avfaudio/avaudiosession/1616442-setcategory?language=objc
[114]setMode:error::https://developer.apple.com/documentation/avfaudio/avaudiosession/1616614-setmode?language=objc
[115]setActive:withOptions:error::https://developer.apple.com/documentation/avfaudio/avaudiosession?language=objc