官方針對AVAudioSession的解釋
參考Apple官方文檔, AudioSession 用來描述APP的音頻行為, 大概有以下幾類與之對應(yīng)的 Category 是闡釋其音頻行為的關(guān)鍵信息, 比如:
- 系統(tǒng)說明你的app使用音頻的模式(比如是播放還是錄音,是否支持藍(lán)牙播放姥宝,是否支持后臺播放)
- 為你的app選擇音頻的輸入輸出設(shè)備(比如輸入用的麥克風(fēng)健民,輸出是耳機(jī)、手機(jī)功放或者airplay)
- 協(xié)助管理多個音源需要播放時的行為(例如同時使用多個音樂播放app惶翻,或者突然有電話接入)
AVAudioSession的Category
Category | 手機(jī)靜音開關(guān) or 鎖屏?xí)r是否靜音 | 中斷其他APP的音頻(后臺音樂) | 允許錄音 和 播放 |
---|---|---|---|
AVAudioSessionCategoryAmbient | Yes | No | Output only |
AVAudioSessionCategorySoloAmbient(默認(rèn)) | Yes | Yes | Output only |
AVAudioSessionCategoryPlayback | No | Yes | Output only |
AVAudioSessionCategoryRecord | No(音頻錄制繼續(xù)) | Yes | Input only |
AVAudioSessionCategoryPlayAndRecord | No | Yes | Input and output |
AVAudioSessionCategoryMultiRoute | No | Yes | Input and output |
簡單說明:
- AVAudioSessionCategoryAmbient:只支持播放闹瞧,可以和其他的音樂播放器一起播放宣谈。同時尔破,當(dāng)用戶鎖屏或者靜音時也會隨著靜音.
- AVAudioSessionCategorySoloAmbient: 也是只用于播放,但是會關(guān)閉其他的音樂播放器的播放娱两,同時莺匠,當(dāng)用戶鎖屏或者靜音時也會隨著靜音。
- AVAudioSessionCategoryPlayback: 只支持播放十兢,當(dāng)鎖屏?xí)r不會靜音趣竣,此category也會關(guān)閉其他的播放器的播放;
- AVAudioSessionCategoryRecord: 只支持錄音旱物,此category會關(guān)閉其他的播放器遥缕,當(dāng)鎖屏?xí)r不會靜音。
- AVAudioSessionCategoryPlayAndRecord:即支持播放也支持錄音宵呛,比如VoIP单匣,打電話這種場景。
- AVAudioSessionCategoryMultiRoute:即支持播放也支持錄音,當(dāng)鎖屏?xí)r不會靜音户秤。這個類別可以支持多個設(shè)備輸入輸出, 比如耳機(jī), USB設(shè)備同時音頻輸出
- AVAudioSessionCategoryAudioProcessing: 主要用于音頻格式處理码秉,一般可以配合AudioUnit進(jìn)行使用。
AVAudioSession的選項Options
在前面配置Category時, 我們可以增加Options來簡單調(diào)整! 注意Options與Category類型相關(guān), 在配置前, 先判斷 Options是否匹配Category, 可以使用[AVAudioSession sharedInstance].availableCategories
來查看當(dāng)前Category支持的Options,常規(guī)的選項.
- AVAudioSessionCategoryOptionMixWithOthers, 是否可以和其他后臺App進(jìn)行混音. 簡單說就是如果后臺有音樂APP, 是否和他一起播放
- AVAudioSessionCategoryOptionDuckOthers, 是否壓低其他App聲音. 我們常見的導(dǎo)航APP在語音播報時, 就會降低其他的音樂APP的聲音, 在播報完成以后, 音樂APP聲音恢復(fù)正常.
- AVAudioSessionCategoryOptionAllowBluetooth, 如果要支持藍(lán)牙耳機(jī)虎忌,則需要設(shè)置這個選項
- AVAudioSessionCategoryOptionDefaultToSpeaker, 是否默認(rèn)用揚(yáng)聲器聲音. (在Category是
PlayAndRecord
時, 系統(tǒng)的默認(rèn)音頻輸出使用的是聽筒), 如果在VoIP模式下泡徙,希望默認(rèn)打開免提功能,需要設(shè)置這個選項
目前主要的選項有這幾種膜蠢,都有對應(yīng)的使用場景堪藐,除此之外,在iOS9還提供了
AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers
iOS10又新加了兩個
AVAudioSessionCategoryOptionAllowBluetoothA2DP
挑围、AVAudioSessionCategoryOptionAllowAirPlay
用來支持藍(lán)牙A2DP耳機(jī)和AirPlay
配置選項常見的API:
(BOOL)setCategory:(NSString *)category withOptions:(AVAudioSessionCategoryOptions)options error:(NSError **)outError;
不過這個過程礁竞,感覺缺少一個
setOption
的接口,既然已經(jīng)是當(dāng)前處于的Category杉辙,干嘛還要再設(shè)置選項的時候再指定Category呢模捂?
AVAudioSession的Mode
通過Category和Options的微調(diào), 基本覆蓋了主要的場景, 在上面的邏輯還需要針對不同的子場景進(jìn)行調(diào)整, 就需要配置Mode
, 具體7個子mode如下:
- AVAudioSessionModeDefault: 適用于所有的類別,
- AVAudioSessionModeVoiceChat:適用與AVAudioSessionCategoryPlayAndRecord蜘矢,主要用于VoIP場景狂男,此時系統(tǒng)會選擇最佳的輸入設(shè)備,比如插上耳機(jī)就使用耳機(jī)上的麥克風(fēng)進(jìn)行采集品腹。此時有個副作用岖食,他會設(shè)置類別的選項為"AVAudioSessionCategoryOptionAllowBluetooth"從而支持藍(lán)牙耳機(jī)。
- AVAudioSessionModeVideoChat:適用與AVAudioSessionCategoryPlayAndRecord舞吭, 主要用于視頻通話泡垃,系統(tǒng)也會選擇最佳的輸入設(shè)備,比如插上耳機(jī)就使用耳機(jī)上的麥克風(fēng)進(jìn)行采集并且會設(shè)置類別的選項為"AVAudioSessionCategoryOptionAllowBluetooth" 和 "AVAudioSessionCategoryOptionDefaultToSpeaker"羡鸥。
- AVAudioSessionModeGameChat:適用與AVAudioSessionCategoryPlayAndRecord蔑穴,適用于游戲App的采集和播放,比如“GKVoiceChat”對象惧浴,一般不需要手動設(shè)置存和。
- AVAudioSessionModeVideoRecording:適用與AVAudioSessionCategoryPlayAndRecord和AVAudioSessionCategoryRecord,主要用于錄制視頻衷旅。
- AVAudioSessionModeMoviePlayback:適用與AVAudioSessionCategoryPlayAndRecord哑姚,主要用于視頻播放;
- AVAudioSessionModeMeasurement:適用與AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayback芜茵,主要用于最小系統(tǒng)
另外幾種和音頻APP關(guān)系不大叙量,一般我們只需要關(guān)注VoIP或者視頻通話即可.
我們一般都是在設(shè)置Category以后再才設(shè)置 Mode:
(BOOL)setMode:(NSString *)mode error:(NSError **)outError;
AVAudioSession的一些屬性
注意app中的AVAudioSession通常使用方法[AVAudioSession sharedInstance]
使用一個單例對象去配置, 我們在啟動時, 可以獲取它的內(nèi)部的一些屬性:
NSLog(@"availableCategories: %@", [[AVAudioSession sharedInstance] availableCategories]);
NSLog(@"availableModes: %@", [[AVAudioSession sharedInstance] availableModes]);
NSLog(@"availableInputs: %@", [[AVAudioSession sharedInstance] availableInputs]);
NSLog(@"outputDataSources: %@", [[AVAudioSession sharedInstance] outputDataSources]);
NSLog(@"inputDataSources: %@", [[AVAudioSession sharedInstance] inputDataSources]);
NSLog(@"default category: %@", [[AVAudioSession sharedInstance] category]);
NSLog(@"default mode: %@", [[AVAudioSession sharedInstance] mode]);
NSLog(@"default categoryOptions: %lu", (unsigned long)[[AVAudioSession sharedInstance] categoryOptions]);
默認(rèn)情況下, availableInputs
只會擁有iPhone的前置內(nèi)置麥克風(fēng)!
2021-04-28 10:19:58.789597+0800 WBHTTPDNSSDK_Example[24486:12088553] availableCategories: (
AVAudioSessionCategoryAmbient,
AVAudioSessionCategorySoloAmbient,
AVAudioSessionCategoryPlayback,
AVAudioSessionCategoryRecord,
AVAudioSessionCategoryPlayAndRecord,
AVAudioSessionCategoryMultiRoute
)
2021-04-28 10:19:58.792350+0800 WBHTTPDNSSDK_Example[24486:12088553] availableModes: (
AVAudioSessionModeDefault,
AVAudioSessionModeVoiceChat,
AVAudioSessionModeVideoRecording,
AVAudioSessionModeMeasurement,
AVAudioSessionModeMoviePlayback,
AVAudioSessionModeVideoChat,
AVAudioSessionModeSpokenAudio,
AVAudioSessionModeVoicePrompt
)
2021-04-28 10:19:58.794185+0800 WBHTTPDNSSDK_Example[24486:12088553] availableInputs: (
"<AVAudioSessionPortDescription: 0x283c59440, type = MicrophoneBuiltIn; name = iPhone 麥克風(fēng); UID = Built-In Microphone; selectedDataSource = 前>"
)
2021-04-28 11:24:25.896577+0800 WBHTTPDNSSDK_Example[24949:12117560] outputDataSources: (
)
2021-04-28 11:24:25.898820+0800 WBHTTPDNSSDK_Example[24949:12117560] inputDataSources: (
"<AVAudioSessionDataSourceDescription: 0x28177c950, ID = 1835216945; name = 下>",
"<AVAudioSessionDataSourceDescription: 0x28177c920, ID = 1835216946; name = 前>",
"<AVAudioSessionDataSourceDescription: 0x28177c910, ID = 1835216947; name = 返回>"
)
2021-04-28 10:19:58.794562+0800 WBHTTPDNSSDK_Example[24486:12088553] default category: AVAudioSessionCategorySoloAmbient
2021-04-28 10:19:58.794817+0800 WBHTTPDNSSDK_Example[24486:12088553] default mode: AVAudioSessionModeDefault
2021-04-28 10:19:58.795797+0800 WBHTTPDNSSDK_Example[24486:12088553] default categoryOptions: 0
注意我們可以在初始條件下打印
inputDataSources
, 通過AVAudioSessionDataSourceDescription
的內(nèi)容, 可以知道設(shè)備擁有的麥克風(fēng)數(shù)目.實(shí)際上輸入數(shù)據(jù)源, 可能有多個, 我們直接通過設(shè)置Category等配置系統(tǒng)會自動幫我們選擇具體使用的麥克風(fēng)
當(dāng)我們帶上Airpod藍(lán)牙耳機(jī), 并配置Options為AVAudioSessionCategoryOptionAllowBluetooth
時, :
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
NSLog(@"availableCategories: %@", [[AVAudioSession sharedInstance] availableCategories]);
NSLog(@"availableModes: %@", [[AVAudioSession sharedInstance] availableModes]);
NSLog(@"availableInputs: %@", [[AVAudioSession sharedInstance] availableInputs]);
NSLog(@"default category: %@", [[AVAudioSession sharedInstance] category]);
NSLog(@"default mode: %@", [[AVAudioSession sharedInstance] mode]);
NSLog(@"default categoryOptions: %lu", (unsigned long)[[AVAudioSession sharedInstance] categoryOptions]);
AVAudioSessionCategoryOptionAllowBluetooth
情況下, availableInputs
會擁有iPhone的前置內(nèi)置麥克風(fēng)(有多個)以及airpod藍(lán)牙麥克風(fēng)!
2021-04-28 10:36:48.549186+0800 WBHTTPDNSSDK_Example[24659:12095941] availableCategories: (
AVAudioSessionCategoryAmbient,
AVAudioSessionCategorySoloAmbient,
AVAudioSessionCategoryPlayback,
AVAudioSessionCategoryRecord,
AVAudioSessionCategoryPlayAndRecord,
AVAudioSessionCategoryMultiRoute
)
2021-04-28 10:36:48.551766+0800 WBHTTPDNSSDK_Example[24659:12095941] availableModes: (
AVAudioSessionModeDefault,
AVAudioSessionModeVoiceChat,
AVAudioSessionModeVideoRecording,
AVAudioSessionModeMeasurement,
AVAudioSessionModeMoviePlayback,
AVAudioSessionModeVideoChat,
AVAudioSessionModeSpokenAudio,
AVAudioSessionModeVoicePrompt
)
2021-04-28 10:36:48.562432+0800 WBHTTPDNSSDK_Example[24659:12095941] availableInputs: (
"<AVAudioSessionPortDescription: 0x282df0aa0, type = MicrophoneBuiltIn; name = iPhone 麥克風(fēng); UID = Built-In Microphone; selectedDataSource = 前>",
"<AVAudioSessionPortDescription: 0x282df09d0, type = BluetoothHFP; name = free的AirPods; UID = E8:36:17:DA:DB:A7-tsco; selectedDataSource = (null)>"
)
2021-04-28 10:36:48.562916+0800 WBHTTPDNSSDK_Example[24659:12095941] default category: AVAudioSessionCategoryPlayAndRecord
2021-04-28 10:36:48.563238+0800 WBHTTPDNSSDK_Example[24659:12095941] default mode: AVAudioSessionModeDefault
2021-04-28 10:36:48.564578+0800 WBHTTPDNSSDK_Example[24659:12095941] default categoryOptions: 4
當(dāng)我們帶上 airpods 藍(lán)牙耳機(jī), 并插上有線耳機(jī), 并配置AVAudioSessionCategoryOptionAllowBluetooth
時, 能看到可用的音頻輸入方式為: 內(nèi)置麥克風(fēng), 有線麥克風(fēng), 耳機(jī)麥克風(fēng):
2021-04-28 10:36:48.549186+0800 WBHTTPDNSSDK_Example[24659:12095941] availableCategories: (
AVAudioSessionCategoryAmbient,
AVAudioSessionCategorySoloAmbient,
AVAudioSessionCategoryPlayback,
AVAudioSessionCategoryRecord,
AVAudioSessionCategoryPlayAndRecord,
AVAudioSessionCategoryMultiRoute
)
2021-04-28 10:36:48.551766+0800 WBHTTPDNSSDK_Example[24659:12095941] availableModes: (
AVAudioSessionModeDefault,
AVAudioSessionModeVoiceChat,
AVAudioSessionModeVideoRecording,
AVAudioSessionModeMeasurement,
AVAudioSessionModeMoviePlayback,
AVAudioSessionModeVideoChat,
AVAudioSessionModeSpokenAudio,
AVAudioSessionModeVoicePrompt
)
2021-04-28 10:36:48.562432+0800 WBHTTPDNSSDK_Example[24659:12095941] availableInputs: (
"<AVAudioSessionPortDescription: 0x280254b70, type = MicrophoneBuiltIn; name = iPhone 麥克風(fēng); UID = Built-In Microphone; selectedDataSource = 前>",
"<AVAudioSessionPortDescription: 0x280254be0, type = BluetoothHFP; name = free的AirPods; UID = E8:36:17:DA:DB:A7-tsco; selectedDataSource = (null)>",
"<AVAudioSessionPortDescription: 0x280254bf0, type = MicrophoneWired; name = 耳機(jī)麥克風(fēng); UID = Wired Microphone; selectedDataSource = (null)>"
)
2021-04-28 10:36:48.562916+0800 WBHTTPDNSSDK_Example[24659:12095941] default category: AVAudioSessionCategoryPlayAndRecord
2021-04-28 10:36:48.563238+0800 WBHTTPDNSSDK_Example[24659:12095941] default mode: AVAudioSessionModeDefault
2021-04-28 10:36:48.564578+0800 WBHTTPDNSSDK_Example[24659:12095941] default categoryOptions: 4
當(dāng)我們帶上 airpods 藍(lán)牙耳機(jī), 并插上有線耳機(jī), 并配置Option = 0(不配置藍(lán)牙) 時, 打印的可用音頻Inputs只有內(nèi)置的前置麥克風(fēng)和有線耳機(jī)麥克風(fēng):
2021-04-28 10:36:48.562432+0800 WBHTTPDNSSDK_Example[24659:12095941] availableInputs: (
"<AVAudioSessionPortDescription: 0x280254b70, type = MicrophoneBuiltIn; name = iPhone 麥克風(fēng); UID = Built-In Microphone; selectedDataSource = 前>",
"<AVAudioSessionPortDescription: 0x280254bf0, type = MicrophoneWired; name = 耳機(jī)麥克風(fēng); UID = Wired Microphone; selectedDataSource = (null)>"
)
AVAudioSession的激活問題
Apple 的文檔中提到, 當(dāng)我們使用 setCategory...
方法時, 如果當(dāng)前AVAudioSession已經(jīng)激活, 系統(tǒng)就會立即改變Category, 如果是未激活狀態(tài)inActive
, 則需要我們設(shè)置Active==YES
才會激活對應(yīng)狀態(tài)
The audio session’s category defines how the app uses audio. Typically, you set the category before activating the session. You can also set the category while the session is active, but this results in an immediate route change.
常見的激活A(yù)PI:
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
//設(shè)置為播放和錄音狀態(tài),以便可以在錄制完之后播放錄音
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[audioSession setActive:YES error:nil];
停止激活A(yù)PI:
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];
AVAudioSession強(qiáng)制選擇Audio Route, 也就是音頻的output位置是揚(yáng)聲器還是聽筒!!!
當(dāng)APP在使用時, 可能同時接入多個外設(shè)音頻(有線耳機(jī), 藍(lán)牙耳機(jī), 默認(rèn)揚(yáng)聲器), 默認(rèn)情況下, AudioSession會根據(jù)Last in Wins
原則選擇, 就是聲音會導(dǎo)向最后接入的設(shè)備.
默認(rèn)情況下, 聲音會從默認(rèn)揚(yáng)聲器出來. 但是當(dāng)我們設(shè)置Category是在PlayAndRecord
時, iPhone會將默認(rèn)的輸出修改成聽筒
, 也就是前置攝像頭旁邊的一個揚(yáng)聲器(支持貼耳的近距離檢測!!!), 這種場景下, 有以下兩種方式將輸出修改成揚(yáng)聲器:
- 修改Options 稱為
AVAudioSessionCategoryOptionDefaultToSpeaker
. 也就是[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&error];
此時沒有外設(shè)會從揚(yáng)聲器播放 - 直接使用 AudioSession的
overrideOutputAudioPort
方法, 也就是修改音頻的輸出接口[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
此時不論是否有外設(shè), 都從揚(yáng)聲器播放
簡單看一下這個方法的系統(tǒng)解釋:
Summary
Temporarily changes the current audio route.
Declaration
- (BOOL)overrideOutputAudioPort:(AVAudioSessionPortOverride)portOverride error:(NSError * _Nullable *)outError;
Discussion
If your app uses the AVAudioSessionCategoryPlayAndRecord category, calling this method with the AVAudioSessionPortOverrideSpeaker option causes the system to route audio to the built-in speaker and microphone regardless of other settings. This change remains in effect only until the current route changes or you call this method again with the AVAudioSessionPortOverrideNone option.
If you’d prefer to permanently enable this behavior, you should instead set the category’s AVAudioSessionCategoryOptionDefaultToSpeaker option. Setting this option routes to the speaker rather than the receiver if no other accessory such as headphones are in use.
Note
The preferred method for routing audio to the speaker instead of the receiver for speakerphone functionality is through the use of the Media Player framework’s MPVolumeView class.
也就是說這個方法只是暫時將AVAudioSessionCategoryPlayAndRecord
category情況下, 將音頻輸出強(qiáng)制輸出到AVAudioSessionPortOverrideSpeaker
揚(yáng)聲器, 在使用完以后, 再設(shè)置回AVAudioSessionPortOverrideNone
.
如果需要永久將PlayAndRecord
Category中, 音頻輸出到揚(yáng)聲器, 最好的方式是直接設(shè)置Options為AVAudioSessionCategoryOptionDefaultToSpeaker
.
具體的代碼如下:
//方法一 切換這個方法會自動切換設(shè)備 耳機(jī)的插拔會自動切換
if (_isSpeakerMode) { //揚(yáng)聲器模式
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];
}else{
// 在PlayAndRecord這個category下九串,聽筒會成為默認(rèn)的輸出設(shè)備
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:0 error:nil];
//方法二 切換這個方法會自動切換設(shè)備 耳機(jī)需要自己判斷
if ([self hasHeadset]) {
[[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:nil];
}else{
[[AVAudioSession sharedInstance] overrideOutputAudioPort:_isSpeakerMode?AVAudioSessionPortOverrideSpeaker: AVAudioSessionPortOverrideNone error:nil];
}
系統(tǒng)中斷響應(yīng)
系統(tǒng)的中斷響應(yīng)分成兩類:
- 一般性的中斷: 包括電話, 鬧鈴等, 通知類型
AVAudioSessionInterruptionNotification
- 其他APP可能占用AudioSession, 比如后臺錄音時, 打開了其他音樂視頻類APP. 通知類型
AVAudioSessionSilenceSecondaryAudioHintNotification
外面的聲音設(shè)備改變的
默認(rèn)情況下, AudioSession會在App啟動時選擇一個最優(yōu)的輸出方案, 比如插入耳機(jī)的時候, 就用耳機(jī). 但是這個過程中, 用戶可能拔出耳機(jī), 我們App要如何感知這樣的情況呢
系統(tǒng)會通過 AVAudioSessionRouteChangeNotification
來進(jìn)行通知其中在其userInfo中有鍵:AVAudioSessionRouteChangeReasonKey
來表示改變的原因绞佩,原因的鍵值有一下幾種:
AVAudioSessionRouteChangeReasonUnknown:未知原因
AVAudioSessionRouteChangeReasonNewDeviceAvailable:有新設(shè)備可用
- AVAudioSessionRouteChangeReasonOldDeviceUnavailable:老設(shè)備不可用
- AVAudioSessionRouteChangeReasonCategoryChange:類別改變了
- AVAudioSessionRouteChangeReasonOverride:App重置了輸出設(shè)置
- AVAudioSessionRouteChangeReasonWakeFromSleep:從睡眠狀態(tài)呼醒
- AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:當(dāng)前Category下沒有合適的設(shè)備
- AVAudioSessionRouteChangeReasonRouteConfigurationChange: Rotuer的配置改變了
當(dāng)前項目中可能會涉及AVAudioSession的地方
在音頻SDK中, 針對AVAudioSession都需要指定對應(yīng)的Category, mode, options等配置.
具體過程如下:
- 我們常規(guī)配置Category為:
AVAudioSessionCategoryPlayAndRecord
, 默認(rèn)音頻輸出為聽筒(前置攝像頭旁邊) - 然后Options, 可以根據(jù)是否需要支持藍(lán)牙耳機(jī):
AVAudioSessionCategoryOptionAllowBluetooth
- Options 是否需要音頻輸出到揚(yáng)聲器(手機(jī)底部):
AVAudioSessionCategoryOptionDefaultToSpeaker
我們可以在SDK使用完音頻資源以后通過如下配置停止Category的服務(wù), 這樣之前的背景音樂APP聲音會繼續(xù)播放:
[[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionInterruptionOptionShouldResume error:nil];
ps: iPhone有多個音頻輸出位置, 手機(jī)頂部是聽筒(聲音小),手機(jī)底部是一個揚(yáng)聲器(聲音大), 一般電話時, 默認(rèn)是聽筒播放聲音, 當(dāng)開啟免提就是底部揚(yáng)聲器播放聲音
在使用 Category = AVAudioSessionCategoryPlayAndRecord 默認(rèn)會使用聽筒播放音樂!
常規(guī)如下配置:
1. 使用Category = AVAudioSessionCategoryPlayAndRecord, Options = 0, Mode = Default
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:0 error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
有如下幾種情況:
當(dāng)不接外設(shè)時, 此時音頻從前置攝像頭的聽筒輸出, 聲音會非常小!!!
當(dāng)僅插入有線耳機(jī)時, 此時音頻從有線耳機(jī)輸出!
當(dāng)僅接入藍(lán)牙耳機(jī)時, 未插入有線耳機(jī)時, 音頻從聽筒輸出!!!不會從藍(lán)牙耳機(jī)輸出
當(dāng)先插入有線耳機(jī), 然后接入藍(lán)牙耳機(jī)時(根據(jù)apple策略, 聲音會優(yōu)先從藍(lán)牙耳機(jī)輸出), 此時還是會從有線耳機(jī)輸出聲音!!!不會從藍(lán)牙耳機(jī)輸出
簡單結(jié)論 , Category == PlayAndRecord, Options == 0
==> 音頻從聽筒輸出:
- 聲音優(yōu)先從有線耳機(jī)輸出, 如果沒有有線耳機(jī), 從聽筒輸出!!!
- 不論是否接入藍(lán)牙耳機(jī), 都不會從藍(lán)牙耳機(jī)輸出音頻!!!
此時, 在沒有有線耳機(jī)的情況下, 聲音太小!!! 體驗不好!!! 我們需要增加Options來進(jìn)行改進(jìn)
2. 使用Category = AVAudioSessionCategoryPlayAndRecord, Options = AVAudioSessionCategoryOptionDefaultToSpeaker , Mode = Default
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
有如下幾種情況:
當(dāng)不接外設(shè)時, 此時音頻從揚(yáng)聲器輸出, 聲音很大!!!
當(dāng)僅插入有線耳機(jī)時, 此時音頻從有線耳機(jī)輸出!!!
當(dāng)僅接入藍(lán)牙耳機(jī)時, 未插入有線耳機(jī)時, 音頻從揚(yáng)聲器輸出!!!不會從藍(lán)牙耳機(jī)輸出
當(dāng)先插入有線耳機(jī), 然后接入藍(lán)牙耳機(jī)時(根據(jù)apple策略, 聲音會優(yōu)先從藍(lán)牙耳機(jī)輸出), 此時還是會從有線耳機(jī)輸出聲音!!!不會從藍(lán)牙耳機(jī)輸出
簡單結(jié)論, Category == PlayAndRecord, Options = AVAudioSessionCategoryOptionDefaultToSpeaker
==> 默認(rèn)音頻從揚(yáng)聲器輸出:
- 聲音優(yōu)先從有線耳機(jī)輸出, 如果沒有有線耳機(jī), 從揚(yáng)聲器輸出!!!
- 不論是否接入藍(lán)牙耳機(jī), 都不會從藍(lán)牙耳機(jī)輸出音頻!!!
3. 使用Category = AVAudioSessionCategoryPlayAndRecord, Options = AVAudioSessionCategoryOptionAllowBluetooth , Mode = Default
有如下幾種情況:
當(dāng)不接外設(shè)時, 此時音頻從聽筒輸出, 聲音會非常小!!!
當(dāng)僅插入有線耳機(jī)時, 此時音頻從有線耳機(jī)輸出!!!
當(dāng)僅接入藍(lán)牙耳機(jī)時, 未插入有線耳機(jī)時, 音頻此時會從藍(lán)牙耳機(jī)輸出!!!
當(dāng)先插入有線耳機(jī), 然后接入藍(lán)牙耳機(jī)時(根據(jù)apple策略, 聲音會優(yōu)先從藍(lán)牙耳機(jī)輸出), 此時會從藍(lán)牙耳機(jī)輸出
簡單結(jié)論, Category == PlayAndRecord, Options = AVAudioSessionCategoryOptionAllowBluetooth
==> 默認(rèn)音頻從聽筒輸出:
- 聲音優(yōu)先外設(shè)輸出(據(jù)
Last in Wins
原則選擇, 就是聲音會導(dǎo)向最后接入的設(shè)備), 從聽筒輸出!!! - 此時音頻可以從藍(lán)牙耳機(jī)輸出音頻
4. 使用Category = AVAudioSessionCategoryPlayAndRecord, Options = AVAudioSessionCategoryOptionAllowBluetoot|AVAudioSessionCategoryOptionDefaultToSpeaker , Mode = Default
結(jié)論: 如果沒有外設(shè), 會從揚(yáng)聲器輸出, 如果有外設(shè), 根據(jù)Last in Wins
原則從外設(shè)(有線耳機(jī), 藍(lán)牙耳機(jī))輸出.
5. 特殊場景!!! 不論手機(jī)是否接入外設(shè)! 強(qiáng)制手機(jī)使用揚(yáng)聲器播放!!!
直接使用如下關(guān)鍵的是overrideOutputAudioPort
API, 而Options是否使用AVAudioSessionCategoryOptionAllowBluetoot|AVAudioSessionCategoryOptionDefaultToSpeaker
并不影響:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:0 error:nil]; // 這里Options
[[AVAudioSession sharedInstance] setMode:AVAudioSessionModeDefault error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
// 強(qiáng)制音頻輸出到揚(yáng)聲器
[[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
在設(shè)置overrideOutputAudioPort
為Speaker
以后, 不論是否接入外設(shè), 設(shè)備會強(qiáng)制使用揚(yáng)聲器作為音頻播放目的!!! 因此該方法的優(yōu)先級非常高!!!
一定要在設(shè)置AVAudioSessionPortOverrideSpeaker
以后, 在結(jié)束使用時候調(diào)用 [[AVAudioSession sharedInstance] overrideOutputAudioPort:0 error:nil];
將強(qiáng)制從揚(yáng)聲器輸出音頻改成系統(tǒng)默認(rèn)(也就是Options)!!! 否則可能導(dǎo)致SDK影響APP的其他服務(wù)(一般SDK不會去修改overrideOutputAudioPort!!!)
最終建議
我們的SDK在使用AVAudioSession時, 建議使用最小權(quán)限使用原則, 在即將要使用音頻資源時, 去配置AVAudioSession的相關(guān)API, 在音頻使用結(jié)束以后, 關(guān)閉!!!
另外如果需要防止其他模塊的影響, 建議使用如下代碼 ( 如果有特殊需求, 自行更改. 比如要強(qiáng)制要求不論是否擁有外設(shè), 用揚(yáng)聲器輸出音頻):
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetoot|AVAudioSessionCategoryOptionDefaultToSpeaker error:nil];
[[AVAudioSession sharedInstance] setMode:AVAudioSessionModeDefault error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
[[AVAudioSession sharedInstance] overrideOutputAudioPort:0 error:nil];
在不使用音頻邏輯時, 將AVAudioSession關(guān)閉:
[[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionInterruptionOptionShouldResume error:nil];
以上是針對Category == PlayAndRecord
情況SDK的相關(guān)邏輯!!!
另外為了更好的音頻體驗邏輯, 為了充分利用PlayAndRecord
的默認(rèn)輸出是聽筒, 其他的Category的默認(rèn)輸出是揚(yáng)聲器, 可以使用距離監(jiān)聽器, 去切換音頻播放中的一些邏輯, 如下:
- (void)sensorStateChange:(NSNotification *)notification {
//如果此時手機(jī)靠近面部放在耳朵旁寺鸥,那么聲音將通過聽筒輸出,并將屏幕變暗
if ([[UIDevice currentDevice] proximityState] == YES) {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
} else {
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
}
}
- (void)startProximityMonitering {
[[NSNotificationCenter defaultCenter] addObserver:mPlayerManager
selector:@selector(sensorStateChange:) name:@"UIDeviceProximityStateDidChangeNotification"
object:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[[UIDevice currentDevice] setProximityMonitoringEnabled:YES];
});
}
- (void)stopProximityMonitering {
[[NSNotificationCenter defaultCenter] removeObserver:mPlayerManager name:@"UIDeviceProximityStateDidChangeNotification" object:nil];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[[UIDevice currentDevice] setProximityMonitoringEnabled:NO];
});
}