前言
????回憶一個(gè)場(chǎng)景宣增,我們使用iPhone 打開(kāi)一首歌曲货抄,音頻從內(nèi)置揚(yáng)聲器中播放出來(lái),此時(shí)有電話撥入烹吵,音樂(lè)會(huì)立即停止并處于暫停狀態(tài)碉熄。此時(shí)聽(tīng)到的是手機(jī)呼叫的鈴聲,當(dāng)我們掛掉電話后肋拔,剛才的音樂(lè)再次響起锈津。在這一過(guò)程中 iOS 提供了一個(gè)可管理的音頻環(huán)境,通過(guò) 音頻會(huì)話(Audio Session)來(lái)管理應(yīng)用程序凉蜂、應(yīng)用程序間和設(shè)備級(jí)別的音頻行為琼梆。
音頻會(huì)話介紹
????音頻會(huì)話在應(yīng)用程序和操作系統(tǒng)之間扮演者中間人的角色性誉,它提供了一種簡(jiǎn)單實(shí)用的方法使得系統(tǒng)得知應(yīng)用程序應(yīng)該如何與 iOS 音頻環(huán)境進(jìn)行交互。開(kāi)發(fā)者不需要了解與音頻硬件交互的具體細(xì)節(jié)茎杂,只需要對(duì)應(yīng)用程序的行為進(jìn)行抽象的配置错览,并把對(duì)該行為的管理委托給音頻會(huì)話,可確保對(duì)用戶的音頻體驗(yàn)進(jìn)行最佳管理煌往。
????所有 iOS 應(yīng)用程序啟動(dòng)后倾哺,都具有一個(gè)默認(rèn)音頻會(huì)話,無(wú)論是否使用刽脖。默認(rèn)音頻會(huì)話來(lái)自于以下一些預(yù)配置:
- 支持音頻播放羞海,但不允許錄音。
- 在 iOS 中曲管,將響鈴/靜音開(kāi)關(guān)設(shè)置為靜音模式會(huì)使應(yīng)用程序正在播放的任何音頻靜音却邓。
- 在 iOS 中,當(dāng)設(shè)備被鎖定時(shí)院水,應(yīng)用程序的音頻會(huì)靜音腊徙。
- 當(dāng)應(yīng)用程序播放音頻時(shí),任何其他后臺(tái)音頻(例如音樂(lè)應(yīng)用程序正在播放的音頻)都會(huì)被靜音檬某。
音頻會(huì)話類別
????默認(rèn)音頻會(huì)話提供了很多實(shí)用的功能撬腾,但在大多數(shù)情況下,開(kāi)發(fā)者應(yīng)該對(duì)其進(jìn)行自定義以更好地滿足應(yīng)用程序的需求橙喘。要更改行為时鸵,需要配置應(yīng)用的音頻會(huì)話。幸運(yùn)的是厅瞎,通過(guò)設(shè)置”類別“功能饰潜,可以很容易地定制我們的特殊需求。
????表達(dá)音頻行為的主要機(jī)制是音頻會(huì)話類別和簸。通過(guò)設(shè)置類別彭雾,可以指定應(yīng)用程序是使用輸入還是輸出,是否希望音樂(lè)與音頻一起繼續(xù)播放等等锁保。AV Foundation 定義了許多音頻會(huì)話類別薯酝,可讓開(kāi)發(fā)者自定義音頻行為。下面表格總結(jié)了 AV Foundation 定義的 6 種類別行為細(xì)節(jié)爽柒。
類別 | 作用 | 是否允許混音 | 音頻的輸入與輸出 | 由響鈴/靜音開(kāi)關(guān)和屏幕鎖定靜音 |
---|---|---|---|---|
AVAudioSessionCategoryAmbient |
游戲吴菠、效率應(yīng)用程序 | Yes | 僅輸出 | Yes |
AVAudioSessionCategorySoloAmbient (默認(rèn)) |
游戲、效率應(yīng)用程序 | No | 僅輸出 | Yes |
AVAudioSessionCategoryPlayback |
音頻和視頻播放器 | 可選 | 僅輸出 | No |
AVAudioSessionCategoryRecord |
錄音機(jī)浩村、音頻捕捉 | No | 僅輸入 | No |
AVAudioSessionCategoryPlayAndRecord |
Voip做葵、語(yǔ)言聊天 | 可選 | 輸入和輸出 | No |
AVAudioSessionCategoryMultiRoute |
使用外部硬件的高級(jí) A/V應(yīng)用程序 | No | 輸入和輸出 | No |
????可以通過(guò) setCategory:mode:options:error:
設(shè)置上述音頻會(huì)話類別,其中一些分類可以通過(guò)使用 options
和 modes
進(jìn)一步自定義附加行為心墅。示例如下:
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error;
if (![session setCategory:AVAudioSessionCategoryPlayback error:&error]) {
NSLog(@"Category Error: %@", [error localizedDescription]);
}
if (![session setActive:YES error:&error]) {
NSLog(@"Activation Error: %@", [error localizedDescription]);
}
音頻中斷及處理
????音頻中斷是應(yīng)用程序音頻會(huì)話的停用——它會(huì)立即停止音頻酿矢。當(dāng)來(lái)自其它應(yīng)用程序的音頻會(huì)話被激活并且該會(huì)話未被系統(tǒng)分類以與我們的應(yīng)用程序的音頻混合時(shí)榨乎,就會(huì)發(fā)生中斷。在會(huì)話處于非活動(dòng)狀態(tài)后瘫筐,系統(tǒng)會(huì)發(fā)送一條“被中斷”消息蜜暑,可以通過(guò)保存狀態(tài)、更新用戶界面等來(lái)響應(yīng)該消息策肝。
????應(yīng)用程序可能會(huì)在中斷后暫停肛捍。當(dāng)用戶接聽(tīng)電話時(shí)會(huì)發(fā)生這種情況。如果用戶轉(zhuǎn)而忽略呼叫或解除警報(bào)之众,系統(tǒng)會(huì)發(fā)出“中斷結(jié)束”消息篇梭,并且應(yīng)用程序會(huì)繼續(xù)運(yùn)行。要恢復(fù)音頻酝枢,必須重新激活音頻會(huì)話。下圖說(shuō)明了回放應(yīng)用程序的音頻會(huì)話中斷之前悍手、期間和之后的事件順序帘睦。
中斷事件(在此示例中為 FaceTime 請(qǐng)求的到達(dá))按如下方式進(jìn)行坦康。編號(hào)的步驟對(duì)應(yīng)于圖中的數(shù)字竣付。
- 應(yīng)用程序處于活動(dòng)狀態(tài),正在播放音頻滞欠。
- FaceTime 請(qǐng)求到達(dá)古胆。系統(tǒng)激活 FaceTime 應(yīng)用程序的音頻會(huì)話。
- 系統(tǒng)會(huì)停用當(dāng)前音頻會(huì)話筛璧。此時(shí)逸绎,應(yīng)用程序中的播放已停止。
- 系統(tǒng)會(huì)發(fā)布通知夭谤,表明會(huì)話已被停用棺牧。
- 通知處理程序需要采取適當(dāng)?shù)拇胧@缋嗜澹梢愿掠脩艚缑娌⒈4嬖谕V裹c(diǎn)恢復(fù)播放所需的信息颊乘。
- 如果用戶解除中斷(忽略傳入的 FaceTime 請(qǐng)求),系統(tǒng)會(huì)發(fā)布通知醉锄,指示中斷已結(jié)束乏悄。
- 通知處理程序會(huì)采取適合中斷結(jié)束的操作。例如恳不,它可能會(huì)更新用戶界面檩小、重新激活音頻會(huì)話并恢復(fù)播放。
- (圖中未顯示妆够。)如果用戶接聽(tīng)電話识啦,而不是在第 6 步消除中斷负蚊,則應(yīng)用程序?qū)和!?/li>
????在開(kāi)發(fā)音頻功能時(shí)颓哮,我們要確保應(yīng)用程序可以正確地處理中斷事件家妆,音頻會(huì)話相關(guān)代碼以處理中斷可確保應(yīng)用程序的音頻在來(lái)電、時(shí)鐘或日歷警報(bào)響起或其他應(yīng)用程序激活其音頻會(huì)話時(shí)繼續(xù)正常運(yùn)行冕茅。
首先需要得到中斷出現(xiàn)的通知伤极,可以選擇注冊(cè)應(yīng)用程序的 AVAudioSession
發(fā)送的通知 AVAudioSessionInterruptionNotification
。
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
????在系統(tǒng)發(fā)送通知調(diào)用 handlerInterruption:
時(shí)傳遞的NSNotification
實(shí)例包含一個(gè) userInfo
提供中斷詳細(xì)信息的填充字典姨伤∩谄海可以通過(guò)從字典中檢索 AVAudioSessionInterruptionType
值來(lái)確定中斷的類型,中斷類型指示中斷是已經(jīng)開(kāi)始還是已經(jīng)結(jié)束乍楚。
typedef NS_ENUM(NSUInteger, AVAudioSessionInterruptionType) {
AVAudioSessionInterruptionTypeBegan = 1, ///< the system has interrupted your audio session
AVAudioSessionInterruptionTypeEnded = 0, ///< the interruption has ended
};
當(dāng)中斷出現(xiàn)時(shí)当编,類型為
AVAudioSessionInterruptionTypeBegan
,需要采取的動(dòng)作就是暫停音頻播放以及 UI 界面的處理徒溪。當(dāng)中斷結(jié)束時(shí)忿偷,類型為
AVAudioSessionInterruptionTypeEnded
,userInfo
中可能包含一個(gè)AVAudioSessionInterruptionOptions
值臊泌,指示音頻會(huì)話是否以及重新激活以及它是否可以再次播放鲤桥。如果選項(xiàng)值為AVAudioSessionInterruptionOptionShouldResume
,則可以繼續(xù)播放渠概。
響應(yīng)路由的變化
????當(dāng)應(yīng)用程序運(yùn)行時(shí)茶凳,用戶可能會(huì)插入或拔出耳機(jī),或使用帶有音頻連接的擴(kuò)展塢播揪。iOS 人機(jī)界面指南描述了應(yīng)用程序應(yīng)如何響應(yīng)此類事件贮喧。要實(shí)施這些建議,可以通過(guò)音頻會(huì)話處理音頻硬件路由的更改猪狈。
????音頻硬件路由是音頻信號(hào)的有線電子通路塞淹。當(dāng)設(shè)備的用戶插入或拔出耳機(jī)時(shí),系統(tǒng)會(huì)發(fā)生線路改變罪裹, AVAudioSession
會(huì)廣播一個(gè)描述該變化的通知AVAudioSessionRouteChangeNotification
給所有相關(guān)的監(jiān)聽(tīng)者饱普。下圖描述了錄入和播放期間各種路線變化的事件:
????如上圖所示,在應(yīng)用啟動(dòng)后状共,系統(tǒng)會(huì)初步確定音頻路由套耕。當(dāng)應(yīng)用程序運(yùn)行時(shí),它會(huì)繼續(xù)監(jiān)控活動(dòng)路線峡继。首先考慮用戶在應(yīng)用程序中點(diǎn)擊“錄制”按鈕的情況冯袍,由圖左側(cè)的“錄制開(kāi)始”框表示。
????在錄制過(guò)程中,用戶可以插入或拔出耳機(jī)康愤,請(qǐng)參見(jiàn)圖中左下角的菱形決策元素儡循。作為響應(yīng),系統(tǒng)發(fā)送包含更改原因和先前路由的路由更改通知征冷,應(yīng)用應(yīng)停止錄制择膝。
????播放的情況類似,但結(jié)果不同检激,如圖右側(cè)所示肴捉。如果用戶在播放過(guò)程中拔下耳機(jī),應(yīng)用應(yīng)暫停音頻叔收。如果用戶在播放過(guò)程中插入耳機(jī)齿穗,應(yīng)用應(yīng)該只允許繼續(xù)播放。那么應(yīng)該怎么做呢饺律?
????首先需要注冊(cè) AVAudioSession
發(fā)送的通知AVAudioSessionRouteChangeNotification
窃页,該通知包含一個(gè) userInfo
字典,攜帶了通知發(fā)送的原因及前一個(gè)路由的描述复濒。
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleRouteChange:)
name:AVAudioSessionRouteChangeNotification
object:[AVAudioSession sharedInstance]];
????收到通知后查看保存在userInfo 字典中 AVAudioSessionRouteChangeReasonKey
判斷路由變更的原因:
-
AVAudioSessionRouteChangeReasonNewDeviceAvailable
腮出,連接新設(shè)備 -
AVAudioSessionRouteChangeReasonOldDeviceUnavailable
,移除設(shè)備
????當(dāng)有設(shè)備斷開(kāi)時(shí)芝薇,獲取 userInfo
中描述前一個(gè)路由信息 的AVAudioSessionRouteChangePreviousRouteKey
,其整合在一個(gè)輸入 NSArray
和一個(gè)輸出 NSArray
中作儿,判斷其中第一個(gè)是否為 AVAudioSessionPortHeadphones
(耳機(jī)接口)洛二。
- (void)handleRouteChange:(NSNotification *)notification {
NSDictionary *info = notification.userInfo;
AVAudioSessionRouteChangeReason reason =
[info[AVAudioSessionRouteChangeReasonKey] unsignedIntValue];
if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
AVAudioSessionRouteDescription *previousRoute =
info[AVAudioSessionRouteChangePreviousRouteKey];
AVAudioSessionPortDescription *previousOutput = previousRoute.outputs[0];
NSString *portType = previousOutput.portType;
if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
//暫停
}
}
}
開(kāi)啟后臺(tái)播放
????音頻應(yīng)用程序所需的一項(xiàng)常見(jiàn)功能是后臺(tái)播放音頻。啟用此功能后攻锰,當(dāng)用戶切換到另一個(gè)應(yīng)用程序或鎖定他們的 iOS 設(shè)備時(shí)晾嘶,應(yīng)用程序的音頻可以繼續(xù)播放。在 iOS 中啟用 AirPlay 流式傳輸和畫(huà)中畫(huà)播放等高級(jí)播放功能也需要此功能娶吞。
????配置這些功能的最簡(jiǎn)單方法是使用 Xcode垒迂。在 Xcode 中選擇應(yīng)用程序的目標(biāo),然后選擇 Capabilities
選項(xiàng)卡妒蛇。在“功能”選項(xiàng)卡下机断,將“后臺(tái)模式”開(kāi)關(guān)設(shè)置為“開(kāi)”,然后從可用模式列表中選擇“音頻绣夺、AirPlay 和畫(huà)中畫(huà)”選項(xiàng)吏奸。