iOS 音頻-AVAudioSession

AVAudioSession 概述

最近在做 webrtc 采集與播放音頻,使用AVAudioSession進行播放與錄音功能
蘋果的官方圖:


1320629-cfd1eacecf2cacae.png

可以看到AVAudioSession就是用來管理多個APP對音頻硬件設(shè)備(麥克風(fēng),揚聲器)的資源使用。
舉例一下AVAudioSession可以做這些事情
設(shè)置自己的APP是否和其他APP音頻同時存在,還是中斷其他APP聲音
在手機調(diào)到靜音模式下,自己的APP音頻是否可以播放出聲音
電話或者其他APP中斷自己APP的音頻的事件處理
指定音頻輸入和輸出的設(shè)備(比如是聽筒輸出聲音,還是揚聲器輸出聲音)
是否支持錄音,錄音同時是否支持音頻播放

[AVAudioSession sharedInstance] 設(shè)置方法

通過主場景、模式、細(xì)節(jié)選項的設(shè)置可以使app在音頻方面更加人性化。
每次的麥克風(fēng)轉(zhuǎn)換都是硬件調(diào)用,會有一定幾毫秒延時,所以錄影錄或音后設(shè)定延時等待釋放后才轉(zhuǎn)換,不然換了也沒用


屏幕快照 2019-05-18 下午1.59.22.png

[[AVAudioSession sharedInstance]setActive:YES error:nil];//馬上設(shè)置
[[AVAudioSession sharedInstance]setActive:NO error:nil];//交出音頻會話
配置音頻設(shè)置,如采樣率,I / O緩沖區(qū)持續(xù)時間和通道數(shù) 處理音頻輸出更改 。
通過overrideOutputAudioPort:error:方法設(shè)置音頻會話的overrideOutputAudioPort屬性

音頻會話場景分類設(shè)置:

說明:以下分類并不是一個應(yīng)用只能使用一個分類,可以根據(jù)場景來切換不同的分類。

主場景分類;

Category iOS下目前有七種,每種Category都對應(yīng)是否支持下面四種能力
Interrupts non-mixable apps audio:是否打斷不支持混音播放的APP
Silenced by the Silent switch:是否會響應(yīng)手機靜音鍵開關(guān)
Supports audio input:是否支持音頻錄制
Supports audio output:是否支持音頻播放

主場景Category 描述 可與其他app共享混合播放 是否可播放/錄音 是否會被靜音鍵或鎖屏鍵靜音 是否可在后臺運行
AVAudioSessionCategoryAmbient 背景聲音,用于以非語音為主的應(yīng)用。 可混合 只支持播放 會靜音 不可運行
AVAudioSessionCategorySoloAmbient 默認(rèn)category 不可混合 只支持播放 會靜音 不可運行
AVAudioSessionCategoryPlayback 用于播放音樂,用于以語音為主的應(yīng)用 不可混合 只支持播放 不會靜音 可運行(另說明)
AVAudioSessionCategoryRecord 用于錄音,除了來電鈴聲、鬧鐘、日歷提醒等系統(tǒng)聲音外其他聲音都不會被播放,只執(zhí)行錄音 不可混合 只支持錄音 不會靜音(鎖屏仍可錄制) 可運行
AVAudioSessionCategoryPlayAndRecord 用于播放和錄音同時存在時,只有它允許修改默許音頻播放設(shè)備:聽筒還是外放,在該Category下聲音的默認(rèn)出口為聽筒或者耳機。 默認(rèn)不引起 支持播放,支持錄制 不會靜音 可運行
AVAudioSessionCategoryMultiRoute 多種輸入輸出,例如可以耳機、USB設(shè)備同時播放 不可混合 支持播放,支持錄制 不會靜音 可運行
AVAudioSessionCategoryAudioProcessing 硬件解碼音頻 不可混合 不支持播放,不支持錄制 不會靜音 不可運行,可請求更多時間完成處理

AVAudioSessionCategoryAmbient : 此類別適用于‘伴奏模式’應(yīng)用,只支持音頻播放。例如用戶在使用音樂應(yīng)用播放時播放的伴奏。比如玩游戲的時候還想聽QQ音樂的歌,那么把游戲播放背景音就設(shè)置成這種類別。當(dāng)使用該類別時,來自其他應(yīng)用程序的音頻會與當(dāng)前的音頻混合。這個 Category,音頻會被靜音鍵和鎖屏鍵靜音。并且不會打斷其他應(yīng)用的音頻播放。這種類別基本使用所有App的背景場景。

AVAudioSessionCategorySoloAmbient : 系統(tǒng)默認(rèn)會話Category, 只支持音頻播放。默認(rèn)情況下,使用該類別意味著 應(yīng)用程序的音頻不可混合,激活應(yīng)用中的會話將打斷其他應(yīng)用的音頻播放。如允許混合,則改用 AVAudioSessionCategoryAmbient。音頻會被靜音鍵和鎖屏鍵靜音。

AVAudioSessionCategoryPlayback :只支持音頻播放。靜音開關(guān)或者鎖屏不會音響音頻的播放。默認(rèn)情況下,使用此類別意味著,應(yīng)用的音頻不可混合,激活音頻會話將中斷其它不可混合的音頻會話。適用于音頻是主要功能的APP,像網(wǎng)易云這些音樂app,鎖屏后依然可以播放。

需要注意一下,選擇支持在靜音鍵切到靜音狀態(tài)以及鎖屏鍵切到鎖屏狀態(tài)下仍然可以播放音頻 Category 時,必須在應(yīng)用中開啟支持后臺音頻功能,詳見 UIBackgroundModes

AVAudioSessionCategoryRecord :錄制音頻的Category,只支持音頻錄制,不支持播放。只要該會話處于活動狀態(tài),此類別會使系統(tǒng)上的所有輸出停止,比如微信語音的錄制,就要用到這個類別。除非需要防止播放其它的聲音,否則建議使用 AVAudioSessionCategoryPlayAndRecord。

用戶必須授權(quán)音頻錄制權(quán)限(iPhone 麥克風(fēng)權(quán)限)。
此類別會話會被 電話呼叫、鬧鐘或者其它非混音音頻會話中斷

AVAudioSessionCategoryPlayAndRecord : 支持音頻播放和錄制的Category,用于語音聊天應(yīng)用,例如VoIP(互聯(lián)網(wǎng)語音協(xié)議)、打電話 應(yīng)用程序(如微信)。音頻的輸入和輸出不需要同步進行,也可以同步進行。
靜音鍵開啟和鎖屏都不會影響音頻繼續(xù)播放。如要在應(yīng)用程序轉(zhuǎn)換到后臺時繼續(xù)播放(鎖屏情況下)在xcode中設(shè)置 UIBackgroundModes 即可。此類別適用于同時錄制和播放(語音連麥),也適用于錄音/播放(IM語音條)。 但不能同時播放。默認(rèn)情況下,使用此類別意味著應(yīng)用程序的音頻不可混合。激活會話將終端任何其他音頻會話也是不可混合的。要允許為此類別混音,請使用AVAudioSessionCategoryOptionMixWithOthers選項

只有它允許修改默許音頻播放設(shè)備:聽筒還是外放,在該Category下聲音的默認(rèn)出口為聽筒或者耳機。
用戶必須授權(quán)音頻錄制權(quán)限(iPhone 麥克風(fēng)權(quán)限)
此類別支持Airplay的鏡像版本。但是,如果AVAudioSessionModeVoiceChat模式與此類別一起使用,則AirPlay鏡像將被禁用。

AVAudioSessionCategoryAudioProcessing,主要用于音頻格式處理,硬件解碼音頻,不支持播放和錄制,一般可以配合AudioUnit進行使用。例如,在執(zhí)行離線音頻格式轉(zhuǎn)換時。此類別禁用播放(音頻輸出)和禁用錄音(音頻輸入)。當(dāng)您的應(yīng)用處于后臺時,音頻處理通常不會繼續(xù)。 但是,當(dāng)您的應(yīng)用移至后臺時,您可以請求更多時間來完成處理。

AVAudioSessionCategoryMultiRoute : 用于將不同音頻數(shù)據(jù)流同時路由到不同輸出設(shè)備的Category。此類別支持音頻播放和錄制,允許多條音頻流的同步輸入和輸出。 例如,比如USB連接外部揚聲器輸出音頻,藍牙耳機同時播放另一路音頻這種特殊需求。 使用這個類別需要對可用音頻路由的功能有更詳細(xì)的了解,并與之互動。
路由更改可能會使部分或全部多路由配置失效。 使用AVAudioSessionCategoryMultiRoute類別時,必須注冊以觀察AVAudioSessionRouteChangeNotification通知并根據(jù)需要更新配置。 這個類別可以支持多個設(shè)備輸入輸出。

說明:當(dāng)使用AVAudioSessionCategoryPlayback、AVAudioSessionCategoryRecord分類時,要想實現(xiàn)后臺播放,需要在Info.plist文件里添加Required background modes的數(shù)組,在下面添加名為App plays audio or streams audio/video using AirPlay的字符串。

主場景細(xì)節(jié)options、modes設(shè)置

上面介紹了Category七種主場景,實際開發(fā)需求中有時候需要對Category進行微調(diào)整,這就需要設(shè)置兩個參數(shù)Mode和Options。通常在激活會話之前設(shè)置類別和模式。但也可在會話處于活躍狀態(tài)時設(shè)置類別模式,但這會導(dǎo)致立即更改。

建議使用setCategory:mode:options:error:method同時設(shè)置它們,而不是單獨設(shè)置類別和模式屬性。(根據(jù)APP 兼容版本不同,使用不同的方法)

AVAudioSession Mode

通過上面的七大類別,我們基本覆蓋了常用的主場景,在每個主場景中可以通過Option進行微調(diào)。為此CoreAudio提供了七大比較常見微調(diào)后的子場景。叫做各個類別的模式。

模式 兼容的 Category 場景
AVAudioSessionModeDefault All 默認(rèn)模式
AVAudioSessionModeVoiceChat AVAudioSessionCategoryPlayAndRecord 雙向語音通信 VoIP
AVAudioSessionModeVideoChat AVAudioSessionCategoryPlayAndRecord 在線視頻會議,視頻通話
AVAudioSessionModeGameChat AVAudioSessionCategoryPlayAndRecord Game Kit的語音聊天服務(wù),GKVoiceChat自動設(shè)置,適用于游戲App的采集和播放
AVAudioSessionModeVideoRecording AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord 錄制視頻
AVAudioSessionModeMoviePlayback AVAudioSessionCategoryPlayback 視頻播放。如果應(yīng)用正在播放電影內(nèi)容,請指定此模式
AVAudioSessionModeMeasurement AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayback 最小系統(tǒng)

下面逐一介紹下每個Mode

AVAudioSessionModeDefault,默認(rèn)模式,與所有的 Category 兼容

AVAudioSessionModeVoiceChat,主要用于執(zhí)行雙向語音通信VoIP場景。只能是 AVAudioSessionCategoryPlayAndRecord Category下。在這個模式系統(tǒng)會自動配置AVAudioSessionCategoryOptionAllowBluetooth 這個選項。系統(tǒng)會自動選擇最佳的內(nèi)置麥克風(fēng)組合支持語音聊天,比如插上耳機就使用耳機上的麥克風(fēng)進行采集。使用此模式時,該設(shè)備的音調(diào)君合針對語音進行了優(yōu)化,并且允許路線組僅縮小為適用于語音聊天的路線。如果應(yīng)用程序未將其模式設(shè)置為其中一個聊天模式(語音,視頻或游戲),則AVAudioSessionModeVoiceChat模式將被隱式設(shè)置。另一方面,如果應(yīng)用程序先前已將其類別設(shè)置為AVAudioSessionCategoryPlayAndRecord并將其模式設(shè)置為AVAudioSessionModeVideoChat或AVAudioSessionModeGameChat,則實例化語音處理I / O音頻單元不會導(dǎo)致模式發(fā)生更改。

AVAudioSessionModeVideoChat,主要用于視頻聊天、在線視頻會議類型應(yīng)用,比如QQ視頻、FaceTime。只能是 AVAudioSessionCategoryPlayAndRecord Category下。適在這個模式系統(tǒng)會自動配置 AVAudioSessionCategoryOptionAllowBluetooth 和 AVAudioSessionCategoryOptionDefaultToSpeaker 選項。系統(tǒng)會自動選擇最佳的內(nèi)置麥克風(fēng)組合支持視頻聊天。使用此模式時,設(shè)備的音調(diào)均衡針對語音進行了優(yōu)化,并且允許音頻路由組僅縮減為適合視頻聊天的設(shè)置。比如插上耳機就使用耳機上的麥克風(fēng)進行采集并且會設(shè)置類別的選項為"AVAudioSessionCategoryOptionAllowBluetooth" 和 "AVAudioSessionCategoryOptionDefaultToSpeaker"。

AVAudioSessionModeGameChat,該模式由Game Kit代表使用Game Kit的語音聊天服務(wù)的應(yīng)用程序設(shè)置,適用于游戲App的采集和播放。使用 GKVoiceChat 對象的應(yīng)用會自動設(shè)置這個模式和 AVAudioSessionCategoryPlayAndRecord Category。實際參數(shù)和AVAudioSessionModeVideoChat一致。不要直接設(shè)置此模式。 如果需要類似的行為并且未使用GKVoiceChat對象,請改為使用AVAudioSessionModeVoiceChat或AVAudioSessionModeVideoChat。

AVAudioSessionModeVideoRecording,適用于使用攝像頭采集視頻錄制的應(yīng)用。只能是 AVAudioSessionCategoryPlayAndRecord 和 AVAudioSessionCategoryRecord 這兩個 Category下。在具有多個內(nèi)置麥克風(fēng)的設(shè)備上,使用距攝像頭最近的麥克風(fēng)。此模式會導(dǎo)致系統(tǒng)提供適當(dāng)?shù)囊纛l信號處理。這個模式搭配 AVCaptureSession API 結(jié)合來用可以更好地控制音視頻的輸入輸出路徑。例如,設(shè)置 automaticallyConfiguresApplicationAudioSession 屬性,系統(tǒng)會自動選擇最佳輸出路徑。(設(shè)置自動配置應(yīng)用音頻會話屬性會根據(jù)使用的設(shè)備和攝像機自動選擇最佳輸入路由。)

AVAudioSessionModeMeasurement,最小化系統(tǒng)。如果應(yīng)用正在執(zhí)行音頻輸入或輸出的測試。此模式適用于需要將輸入和輸出信號的系統(tǒng)提供的信號處理量將至最低的應(yīng)用程序。如果在具有多個內(nèi)置麥克風(fēng)的設(shè)備上錄制,則使用主麥克風(fēng)。只用于 AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryRecord、AVAudioSessionCategoryPlayback 這幾種 Category。

AVAudioSessionModeMoviePlayback,適用于播放視頻的應(yīng)用。只用于 AVAudioSessionCategoryPlayback 這個Category。使用此模式時,將采用信號處理來增強某些音頻路由(如內(nèi)置揚聲器或耳機)的電影播放。系統(tǒng)也會選擇最佳的輸入設(shè)備,比如插上耳機就使用耳機上的麥克風(fēng)進行采集

AVAudioSessionModeSpokenAudio : 用于播放語音并暫停其他語音app。當(dāng)想要在另一個應(yīng)用播放短語音頻時暫停當(dāng)前音頻時,用于持續(xù)說話音頻的模式。在iOS 8和更低版本以及iOS 9中,如果不設(shè)置此模式,偶爾從導(dǎo)航和應(yīng)用程序中聽到的語音與音頻混合在一起,或造成兩種音頻的混淆。 此模式通過為中斷應(yīng)用程序使用AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers音頻會話類別選項來避免此問題。 中斷應(yīng)用程序的音頻結(jié)束后,可以恢復(fù)中斷的語音。

可以在設(shè)置Category之后再設(shè)置模式。

當(dāng)然,這些模式只是CoreAduio總結(jié)的,不一定完全滿足要求,對于具體的模式,在iOS10中還是可以微調(diào)的。通過接口:
(BOOL)setCategory:(NSString *)category mode:(NSString *)mode options:(AVAudioSessionCategoryOptions)options error:(NSError **)outError
但是在iOS9及以下就只能在Category上調(diào)了,其實本質(zhì)是一樣的,可以認(rèn)為是個API接口封裝。

AVAudioSession Options

在設(shè)置完類別后,可以通過

@property(readonly) AVAudioSessionCategoryOptions categoryOptions;
屬性,查看當(dāng)前類別設(shè)置了哪些選項,注意這里的返回值是AVAudioSessionCategoryOptions,實際是多個options的“|”運算。默認(rèn)情況下是0。
可以使用options去微調(diào)Category行為,如下表

Option Option功能說明 兼容的 Category
AVAudioSessionCategoryOptionMixWithOthers 支持和其他APP音頻混合 AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryPlayback AVAudioSessionCategoryMultiRoute
AVAudioSessionCategoryOptionDuckOthers 系統(tǒng)智能調(diào)低其他APP音頻音量,突出本app的音量(導(dǎo)航地圖) AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryPlayback AVAudioSessionCategoryMultiRoute
AVAudioSessionCategoryOptionAllowBluetooth 支持藍牙音頻輸入 AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayAndRecord
AVAudioSessionCategoryOptionDefaultToSpeaker 設(shè)置默認(rèn)輸出音頻到揚聲器,即免提 AVAudioSessionCategoryPlayAndRecord
AVAudioSessionCategoryOptionInterrupt SpokenAudioAndMixWithOthers app偶爾的使用音頻播放 AVAudioSessionCategoryPlayback AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryMultiRoute
AVAudioSessionCategoryOptionAllowBluetoothA2DP 立體聲藍牙 AVAudioSessionCategoryPlayAndRecord
AVAudioSessionCategoryOptionAllowAirPlay 遠程AirPlay設(shè)備 AVAudioSessionCategoryPlayAndRecord

AVAudioSessionCategoryOptionMixWithOthers :確定來自此會話的音頻是否與來自其他音頻應(yīng)用中活動會話的音頻混合。當(dāng)一個app即包含audio輸入又包含輸出的時候,設(shè)置這個選項在激活會話時不會打斷其他應(yīng)用程序的音頻播放 。
如果會話類別是AVAudioSessionCategoryAmbient,則此選項會自動設(shè)置。如果設(shè)置了AVAudioSessionCategoryOptionDuckOthers或AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers選項,則會自動設(shè)置此選項。如果清除此選項,激活會話會中斷其他音頻會話。 如果設(shè)置了此選項,則應(yīng)用程序的音頻會與后臺應(yīng)用程序中的音頻(如音樂應(yīng)用程序)混合在一起。
例如:AVAudioSessionCategoryPlayback實現(xiàn)的一個背景音,但是呢,又想和QQ音樂并存,那么可以在AVAudioSessionCategoryPlayback類別下再設(shè)置這個選項,就可以實現(xiàn)共存了。

AVAudioSessionCategoryOptionDuckOthers : 激活會話時降低其他程序的音頻播放聲音(音量降低) ,主要是體現(xiàn)當(dāng)前音頻的重要性,此時就是通過設(shè)置這個選項來對其他音樂App進行了壓制。比如說開車聽歌的時候,導(dǎo)航的聲音就屬于比較重要的或者是需要特別關(guān)注的就可以是用這個選項 。會影響其他應(yīng)用,不用時需要deactivate audio session
設(shè)置此標(biāo)志隱式設(shè)置AVAudioSessionCategoryOptionMixWithOthers標(biāo)志。如果清除此選項,激活會話會中斷其他音頻會話。如果設(shè)置了此選項,則應(yīng)用程序的音頻會與后臺應(yīng)用程序中的音頻(如音樂應(yīng)用程序)混合在一起。與當(dāng)前應(yīng)用混合時,來自其他應(yīng)用的音頻會減少音量。如果您希望通過音樂或其他當(dāng)前正在播放的音頻聽到您應(yīng)用中的音頻(例如,導(dǎo)航應(yīng)用中的語音提示),請設(shè)置此選項。如果您的應(yīng)用程序提供了偶爾的口語音頻,例如在轉(zhuǎn)彎的導(dǎo)航應(yīng)用程序或練習(xí)應(yīng)用程序中,則還應(yīng)該設(shè)置AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers選項。

AVAudioSessionCategoryOptionAllowBluetooth : 藍牙免提設(shè)備是否顯示為可用輸入路由。如果要支持藍牙耳機電話,則需要設(shè)置這個選項。
需要設(shè)置此選項才能將音頻輸入和輸出路由到配對的藍牙免提模式(HFP)設(shè)備。 如果清除此選項,則配對的藍牙HFP不會顯示為可用的音頻輸入路由。
如果應(yīng)用程序使用setPreferredInput:error:方法選擇藍牙HFP輸入,則輸出將自動更改為相應(yīng)的藍牙HFP輸出。 同樣,使用MPVolumeView對象的路由選擇器選擇藍牙HFP輸出,會自動將輸入更改為相應(yīng)的藍牙HFP輸入。 因此,即使僅選擇了輸入或輸出,音頻輸入和輸出也將始終路由至Bluetooth HFP設(shè)備。
只有音頻會話類別為AVAudioSessionCategoryPlayAndRecord或AVAudioSessionCategoryRecord時,才能設(shè)置此選項。

AVAudioSessionCategoryOptionDefaultToSpeaker :在沒有其他通道的時候默認(rèn)選擇內(nèi)置揚聲器而不是接收器。如果在VoIP模式下,希望默認(rèn)打開免提功能,需要設(shè)置這個選項。此選項只能在使用AVAudioSessionCategoryPlayAndRecord類別時設(shè)置。 它用于修改類別的路由行為,以便在沒有使用其他配件(如耳機)的情況下,音頻始終會路由至揚聲器而不是接收器。 例如,插入頭戴式耳機將導(dǎo)致路由變?yōu)轭^戴式耳機麥克風(fēng)/耳機,并且拔下頭戴式耳機將導(dǎo)致路由更換為內(nèi)置麥克風(fēng)/揚聲器(與內(nèi)置麥克風(fēng)/接收器相反) 已設(shè)置。
如果使用USB輸入專用附件,音頻輸入將來自附件,如果沒有插入耳機,則輸出將路由到耳機(如果連接)或揚聲器。

AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers :播放此應(yīng)用的音頻內(nèi)容時,是否暫停來自其他應(yīng)用的連續(xù)語音內(nèi)容。
設(shè)置此選項還會設(shè)置AVAudioSessionCategoryOptionMixWithOthers。
如果清除此選項,音頻會話中的音頻會中斷其他會話。
如果設(shè)置了此選項,則您的音頻會與其他音頻會話混合使用,但會中斷(并停止)使用AVAudioSessionModeSpokenAudio音頻會話模式的音頻會話。只要會話處于活動狀態(tài),其他應(yīng)用程序的音頻就會暫停。音頻會話停用后,中斷的應(yīng)用程序的音頻恢復(fù)。
如果突然播放一段語音,例如導(dǎo)航應(yīng)用,使用此選項。這可以避免兩個口語音頻應(yīng)用程序混合時出現(xiàn)干擾問題。除非有特殊的原因,否則建議設(shè)置為AVAudioSessionCategoryOptionDuckOthers選項。當(dāng)其他音頻不是說音頻時,避免其他音頻而不是中斷它。

AVAudioSessionCategoryOptionAllowBluetoothA2DP : 立體聲藍牙。
高級音頻分布配置文件(A2DP)是一種立體聲,僅輸出配置文件,適用于較高帶寬音頻使用情況,如音樂播放。如果應(yīng)用程序的音頻會話配置為使用AVAudioSessionCategoryAmbient,AVAudioSessionCategorySoloAmbient或AVAudioSessionCategoryPlayback類別,系統(tǒng)將自動路由至A2DP端口。
從iOS 10.0開始,使用AVAudioSessionCategoryPlayAndRecord類別的應(yīng)用程序還可以允許將輸出路由到配對的藍牙A2DP設(shè)備。要啟用此行為,您需要在設(shè)置音頻會話的類別時傳遞此類別選項。使用AVAudioSessionCategoryMultiRoute或AVAudioSessionCategoryRecord類別的音頻會話隱式清除此選項。
如果清除此選項,則配對的藍牙A2DP設(shè)備不會顯示為可用的音頻輸出路由。
如果此選項和AVAudioSessionCategoryOptionAllowBluetooth選項均已設(shè)置,則當(dāng)單個設(shè)備同時支持免提配置文件(HFP)和高級音頻分布配置文件(A2DP)時,免提端口將獲得更高的路由優(yōu)先級。

AVAudioSessionCategoryOptionAllowAirPlay : 遠程AirPlay設(shè)備。
只有在音頻會話類別為AVAudioSessionCategoryPlayAndRecord時,才能顯式設(shè)置此選項。 對于大多數(shù)其他音頻會話類別,此選項是隱式設(shè)置的。 使用AVAudioSessionCategoryMultiRoute或AVAudioSessionCategoryRecord類別的音頻會話隱式清除此選項。
如果清除此選項,則AirPlay設(shè)備不會顯示為可用的音頻輸出路線。
如果設(shè)置了此選項,這些設(shè)備將顯示為可用的輸出路徑。

通過Category和合適的Mode和Options的搭配我們可以調(diào)優(yōu)出我們的效果,下面舉兩個應(yīng)用場景:
用過高德地圖的都知道,在后臺播放QQ音樂的時候,如果導(dǎo)航語音出來,QQ音樂不會停止,而是被智能壓低和混音,等導(dǎo)航語音播報完后,QQ音樂正常播放,這里我們需要后臺播放音樂,所以Category使用AVAudioSessionCategoryPlayback,需要混音和智能壓低其他APP音量,所以O(shè)ptions選用 AVAudioSessionCategoryOptionMixWithOthers和AVAudioSessionCategoryOptionDuckOthers
代碼示例如下

BOOL isSuccess = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionDuckOthers error:&setCategoryError];

又或者我希望AVAudioSessionCategoryPlayAndRecord這個Category默認(rèn)的音頻由揚聲器播放,那么可以調(diào)用這個接口去調(diào)整Category

  • (BOOL)setCategory:(NSString *)category withOptions:(AVAudioSessionCategoryOptions)options error:(NSError **)outError

通過選擇合適和Category,mode和options,就可以調(diào)優(yōu)音頻的輸入輸出,來滿足日常開發(fā)需求(需要注意的是Category,mode,option是搭配使用的,而不是簡單組合,也就是說某種Category支持某些mode和option,從上面的表中也可以看出這一點)

音頻中斷處理

AVAudioSession提供了多種Notifications來進行此類狀況的通知。其中將來電話、鬧鈴響等都?xì)w結(jié)為一般性的中斷,用AVAudioSessionInterruptionNotification來通知。
我們可以通過監(jiān)聽AVAudioSessionInterruptionNotification這個key獲取音頻中斷事件
回調(diào)回來Userinfo有鍵值

AVAudioSessionInterruptionTypeKey:
取值A(chǔ)VAudioSessionInterruptionTypeBegan表示中斷開始,我們應(yīng)該暫停播放和采集。
取值A(chǔ)VAudioSessionInterruptionTypeEnded表示中斷結(jié)束,我們可以繼續(xù)播放和采集。
AVAudioSessionInterruptionOptionKey: 當(dāng)前只有一種值A(chǔ)VAudioSessionInterruptionOptionShouldResume表示此時也應(yīng)該恢復(fù)繼續(xù)播放和采集。
中斷開始:我們需要做的是保存好播放狀態(tài),上下文,更新用戶界面等
中斷結(jié)束:我們要做的是恢復(fù)好狀態(tài)和上下文,更新用戶界面,根據(jù)需求準(zhǔn)備好之后選擇是否激活我們session。

而將其他App占據(jù)AudioSession的時候用AVAudioSessionSilenceSecondaryAudioHintNotification來進行通知。其回調(diào)回來的userInfo鍵為:
AVAudioSessionSilenceSecondaryAudioHintTypeKey
可能包含的值:
AVAudioSessionSilenceSecondaryAudioHintTypeBegin: 表示其他App開始占據(jù)Session
AVAudioSessionSilenceSecondaryAudioHintTypeEnd: 表示其他App開始釋放Session

選擇不同的音頻播放技術(shù),處理中斷方式也有差別,具體如下:
System Sound Services:使用 System Sound Services 播發(fā)音頻,系統(tǒng)會自動處理,不受APP控制,當(dāng)中斷發(fā)生時,音頻播放會靜音,當(dāng)中斷結(jié)束后,音頻播放會恢復(fù)。
AV Foundation framework:AVAudioPlayer 類和 AVAudioRecorder 類提供了中斷開始和結(jié)束的 Delegate 回調(diào)方法來處理中斷。中斷發(fā)生,系統(tǒng)會自動停止播放,需要做的是記錄播放時間等狀態(tài),更新用戶界面,等中斷結(jié)束后,再次調(diào)用播放方法,系統(tǒng)會自動激活session。
Audio Queue Services, I/O audio unit:使用aduio unit這些技術(shù)需要處理中斷,需要做的是記錄播放或者錄制的位置,中斷結(jié)束后自己恢復(fù)audio session。
OpenAL:使用 OpenAL 播放時,同樣需要自己監(jiān)聽中斷。管理 OpenAL上下文,用戶中斷結(jié)束后恢復(fù)audio session。

需要注意的是:1. 有中斷開始事件,不一定對應(yīng)有中斷結(jié)束事件,所以需要在用戶進入前臺,點擊UI操作的時候,需要保存好播放狀態(tài)和對Audio Session管理,以便不影響APP的音頻功能。2.音頻資源競爭上,一定是電話優(yōu)先。

線路改變事件比如耳機拔入拔出

當(dāng)收到線路變換通知后,在NSNotificationCenter中對AVAudioSessionRouteChangeNotification進行注冊。在其userInfo中有鍵:AVAudioSessionRouteChangeReasonKey
取值A(chǔ)VAudioSessionRouteChangeReasonOldDeviceUnavailable時,表示有設(shè)備斷開。根據(jù)人性化原則,在耳機拔出時應(yīng)該使正在播放的聲音暫停。
取值A(chǔ)VAudioSessionRouteChangeReasonNewDeviceAvailable時,表示有設(shè)備連接。
AVAudioSessionRouteChangeReasonKey : 表示改變的原因

枚舉值 意義
AVAudioSessionRouteChangeReasonUnknown 未知原因
AVAudioSessionRouteChangeReasonNewDeviceAvailable 有新設(shè)備可用
AVAudioSessionRouteChangeReasonOldDeviceUnavailable 老設(shè)備不可用
AVAudioSessionRouteChangeReasonCategoryChange 類別改變了
AVAudioSessionRouteChangeReasonOverride App重置了輸出設(shè)置
AVAudioSessionRouteChangeReasonWakeFromSleep 從睡眠狀態(tài)呼醒
AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory 當(dāng)前Category下沒有合適的設(shè)備

Audio Session Error Codes

由AVAudioSession方法返回的NSError對象中使用的錯誤代碼。
AVAudioSessionErrorCodeNone
操作成功。
AVAudioSessionErrorCodeMediaServicesFailed
嘗試在媒體服務(wù)失敗期間或之后使用音頻會話。
AVAudioSessionErrorCodeIsBusy
嘗試將其音頻會話設(shè)置為非活動狀態(tài),但仍在播放和/或錄制。
AVAudioSessionErrorCodeIncompatibleCategory
試圖執(zhí)行當(dāng)前類別中不允許的操作。
AVAudioSessionErrorCodeCannotInterruptOthers
嘗試在應(yīng)用程序處于后臺時使不可混音的音頻會話處于活動狀態(tài)。
AVAudioSessionErrorCodeMissingEntitlement
試圖執(zhí)行應(yīng)用程序沒有所需權(quán)利的操作。
AVAudioSessionErrorCodeSiriIsRecording
Siri正在錄制時嘗試執(zhí)行不允許的操作。
AVAudioSessionErrorCodeCannotStartPlaying
試圖開始音頻播放,但不允許播放。
AVAudioSessionErrorCodeCannotStartRecording
試圖開始錄音,但失敗了。
AVAudioSessionErrorCodeBadParam
試圖將屬性設(shè)置為非法值。
AVAudioSessionErrorInsufficientPriority
該應(yīng)用程序不允許設(shè)置音頻類別,因為它正在被另一個應(yīng)用程序使用。
AVAudioSessionErrorCodeResourceNotAvailable
由于設(shè)備沒有足夠的硬件資源來完成操作而失敗的操作。
AVAudioSessionErrorCodeUnspecified
沒有更多的錯誤信息可用。當(dāng)音頻系統(tǒng)處于不一致狀態(tài)時,通常會產(chǎn)生這種錯誤類型。

代碼:

AVAudioSession* session = [AVAudioSession sharedInstance];
  [session setCategory:AVAudioSessionCategoryPlayAndRecord
           withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker |
                       AVAudioSessionCategoryOptionDuckOthers |
                       AVAudioSessionCategoryOptionAllowBluetooth
                 error:nil];
  [session setMode:AVAudioSessionModeVoiceChat error:nil];
[[NSNotificationCenter defaultCenter]
      addObserver:self
         selector:@selector(handleRouteChange:)
             name:AVAudioSessionRouteChangeNotification
           object:[AVAudioSession sharedInstance]];
- (void)handleRouteChange:(NSNotification*)notification {
  NSDictionary* interuptionDict = notification.userInfo;
  NSInteger routeChangeReason = [[interuptionDict
      valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
  switch (routeChangeReason) {
    case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
      ISLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
      // 插入耳機時關(guān)閉揚聲器播放
      break;
    case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
      ISLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
      // 拔出耳機時的處理為開啟揚聲器播放
      break;
    case AVAudioSessionRouteChangeReasonCategoryChange:
      // called at start - also when other audio wants to play
      ISLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
      break;
  }
}
 [[NSNotificationCenter defaultCenter]
      addObserver:self
         selector:@selector(handleAudioInterrupted:)
             name:AVAudioSessionInterruptionNotification
           object:[AVAudioSession sharedInstance]];
- (void)handleAudioInterrupted:(NSNotification*)notification {
  ISLog(@"音頻打斷通知 the notification is %@", notification);
  if (!_webRtcAvailable) {
    return;
  }
  if (AVAudioSessionInterruptionTypeBegan ==
      [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue]) {
    LOGE("音頻打斷開始 begin");
  } else if (AVAudioSessionInterruptionTypeEnded ==
             [notification.userInfo[AVAudioSessionInterruptionTypeKey]
                 intValue]) {
    LOGE("音頻打斷結(jié)束 begin - end");
  }
}

雜記

AudioSession Route

默認(rèn)音頻輸出為揚聲器
在PlayAndRecord這個category下,默認(rèn)音頻輸出為聽筒。
揚聲器,聽筒,耳機,藍牙音箱之間的切換
利用MPVolumeView來讓用戶切換到揚聲器,
通過overrideOutputAudioPort方法來切換到揚聲器
修改category 的option為AVAudioSessionCategoryOptionDefaultToSpeaker
多個外接音頻接收設(shè)備時(耳機,藍牙音箱等),將遵循last-in wins的原則來選擇外接設(shè)備,即聲音將被導(dǎo)向最后接入的設(shè)備。

通過AVAudioSessionPortOverride選擇音頻輸出

AVAudioSessionPortOverrideNone
不要覆蓋輸出音頻端口。使用此選項將音頻輸出設(shè)備返回到當(dāng)前音頻類別的默認(rèn)狀態(tài)。
AVAudioSessionPortOverrideSpeaker
覆蓋當(dāng)前輸入和輸出,設(shè)置音頻輸出設(shè)備為內(nèi)置揚聲器和麥克風(fēng)。僅適用于AVAudioSessionCategoryPlayAndRecord類別。
代碼例子

[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:error];

通過AVAudioSessionCategoryOptions選擇音頻輸出設(shè)備

會話的類別和模式一起定義應(yīng)用程序如何使用音頻。通常,在激活會話之前設(shè)置類別和模式。您還可以在會話處于活動狀態(tài)時設(shè)置類別或模式,但這會導(dǎo)致立即更改音頻輸出設(shè)備。
代碼例子:
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&error];

AVAudioSessionCategoryOptions 枚舉值:

Input

AVAudioSessionPortUSBAudio
AVAudioSessionPortHeadsetMic
AVAudioSessionPortBuiltInMic

Output

AVAudioSessionPortUSBAudio
AVAudioSessionPortLineOut
AVAudioSessionPortHeadphones
AVAudioSessionPortHDMI
AVAudioSessionPortBuiltInSpeaker

overrideOutputAudioPort:方法和AVAudioSessionPortOverride的對比

共同點:使用僅適用于AVAudioSessionCategoryPlayAndRecord類別。
區(qū)別
overrideOutputAudioPort:是臨時覆蓋。任何音頻輸出改變或中斷將導(dǎo)致音頻被路由回到其正常輸出設(shè)備,遵循最后入勝的規(guī)則
AVAudioSessionCategoryOptionDefaultToSpeaker,在沒有使用耳機等其他附件時,音頻將總是路由到揚聲器而不是接收器。
舉個例子
方法1, 設(shè)置之后,如果此時插入耳機,在拔掉。播放的聲音會從聽筒輸出,而不是回到揚聲器。
方法2, 設(shè)置之后,始終輸出到揚聲器,而不是其他接收器,如果沒有耳機。(簡要的說,就是如果有個藍牙音箱,哪怕接上都不會有聲音輸出到藍牙音響,插上耳機,則會有聲音輸出到耳機。)

AVAudioSession總結(jié)

AVAudioSession的作用就是管理音頻這一唯一硬件資源的分配,通過調(diào)優(yōu)合適的AVAudioSession來適配我們的APP對于音頻的功能需求。切換音頻場景時候,需要相應(yīng)的切換AVAudioSession。
AVAudioSession構(gòu)建了一個音頻使用生命周期的上下文。當(dāng)前狀態(tài)是否可以錄音、對其他App有怎樣的影響、是否響應(yīng)系統(tǒng)的靜音鍵、如何感知來電話了等都可以通過它來實現(xiàn)。尤為重要的是AVAudioSession不僅可以和AVFoundation中的AVAudioPlyaer/AVAudioRecorder配合,其他錄音/播放工具比如AudioUnit、AudioQueueService也都需要他進行錄音、靜音等上下文配合。

AVAudioSessionCategoryPlayAndRecord類別下

  • (BOOL)setMode:(AVAudioSessionMode)mode error:(NSError **)outError
    設(shè)置這個屬性會導(dǎo)致藍牙耳機連接失敗,不去設(shè)置這個屬性藍牙耳機可以使用,一直找不到原因,不知道是不是跟 webrtc 有關(guān)系。。。 后來找到一個方案,在 系統(tǒng)版本 10.0 以上設(shè)置AVAudioSessionCategoryOptionAllowBluetoothA2DP 這個Option使用藍牙耳機,10.0 以下首次設(shè)置AVAudioSession不設(shè)置mode,音頻打斷時候再去設(shè)置就不會出現(xiàn)藍牙不能使用的問題了,具體為什么還在查找中
    感覺CoreAudio缺少一個setOption的接口,Category已經(jīng)設(shè)置過,為何設(shè)置選項的時候再指定Category,這里不太理解。。。

結(jié)合網(wǎng)上文案總結(jié)只為自己解惑加深印象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容