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

可以看到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)換,不然換了也沒用

[[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é)只為自己解惑加深印象。