An audio session is a singleton object that you employ to set the audio context for your app and to express to the system your intentions for your app’s audio behavior.
上面是摘自官方文檔的介紹。大體就是說明一下 AVAudioSession 是一個單例對象,并用來為音頻類app向系統(tǒng)傳遞一些配置信息。 其實在iOS7以前甚至是現(xiàn)在為止還有相當(dāng)一部人使用的是AudioToolBox框架下的AudioSession,不過AudioSession在SDK7中已經(jīng)被蘋果標明* depracated*。所以我們主要就以AVAudioSession為主,與時俱進嘛!
AVAudioSession 的主要功能包括以下幾點功能:
- 向系統(tǒng)說明你的app使用音頻的模式(比如是播放還是錄音,是否支持藍牙播放,是否支持后臺播放)
- 為你的app選擇音頻的輸入輸出設(shè)備(比如輸入用的麥克風(fēng),輸出是耳機、手機功放或者airplay)
- 協(xié)助管理多個音源需要播放時的行為(例如同時使用多個音樂播放app,或者突然有電話接入)
下面就開始 AVAudioSession相關(guān)功能的使用,大家可以用我們上一篇寫好的 ——離線歌曲播放的例子來進行演練。
激活音頻會話
細心的朋友可能已經(jīng)發(fā)現(xiàn)我們上一篇寫好的音頻播放有很大的局限性,比如:不支持后臺播放、當(dāng)手機處于禁音狀態(tài)時也無法正常輸出聲音。這個時候就需要我們?nèi)ゼせ钜纛l會話,也就是 AVAudioSession 。激活的方法非常簡單大家可以在初始化播放器的時候加上下面一段代碼
//激活音頻會話
[[AVAudioSession sharedInstance]setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
[[AVAudioSession sharedInstance]setActive:YES error:nil];
這段代碼的意思呢就是設(shè)置我們app音頻會話的category(模式)和option(選項),下面這張表就是幾種可選的category及其效果,大家在實際開發(fā)中可以根據(jù)自己的實際情況去選擇對應(yīng)的參數(shù):
| 會話類型 | 說明 | 是否要求輸入 | 是否要求輸出 | 是否遵從靜音鍵 |
|---|---|---|---|---|
| AVAudioSessionCategoryAmbient | 混音播放,可以與其他音頻應(yīng)用同時播放 | 否 | 是 | 是 |
| AVAudioSessionCategorySoloAmbient | 獨占播放 | 否 | 是 | 是 |
| AVAudioSessionCategoryPlayback | 后臺播放,獨占 | 否 | 是 | 否 |
| AVAudioSessionCategoryRecord | 錄音模式 | 是 | 否 | 否 |
| AVAudioSessionCategoryPlayAndRecord | 播放和錄音,此時可以錄音也可以播放 | 是 | 是 | 否 |
| AVAudioSessionCategoryAudioProcessing | 硬件解碼音頻,此時不能播放和錄制 | 否 | 否 | 否 |
| AVAudioSessionCategoryMultiRoute | 多種輸入輸出,例如可以耳機、USB設(shè)備同時播放 | 是 | 是 | 否 |
激活音頻會話后,我們就可以開始執(zhí)行一系列跟 AVAudioSession 的服務(wù)了。
后臺播放及遠程控制
可以這么說,不支持后臺播放的音頻類app是不完整的,但是要我們的app支持后臺播放外,還需要給我們的項目設(shè)置一些參數(shù)。
網(wǎng)上很多教程都是教的直接修改plist文件,我這里也放出來這種方法:在info.plist 文件中加入如下參數(shù)Required background modes并在item 0中寫入App plays audio or streams audio/video using AirPlay。如下圖

但是Xcode其實在很早的版本就已經(jīng)提供了圖形化的操作界面,更為的直觀,不知大家是沒有發(fā)現(xiàn)還是什么原因,相關(guān)的一些配置都是直接修改的plist,下面就是圖形化的設(shè)置方法。如圖

好了,這個時候重新運行我們的程序home到后臺后,我們的播放器就不會暫停播放了。不過這個時候我們呼出系統(tǒng)的控制臺發(fā)現(xiàn)我們播放的歌曲并沒有在這顯示,這是因為我們呢沒有告訴系統(tǒng)我們的app接受遠程控制,下面讓我們的播放器響應(yīng)遠程。
首先要在初始化播放器的時候或者是appDelegate的- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions;這個方法中加入這段代碼
//告訴系統(tǒng),我們要接受遠程控制事件
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
如果你的app是以音頻播放為主,我建議你的代碼是寫在appDelegate中。重寫appDelegate的canBecomeFirstResponder方法讓我們的appDelegate可以成為第一響應(yīng)者,已響應(yīng)控制臺的操作。
- (BOOL)canBecomeFirstResponder{
return YES;
}
最后只要再實現(xiàn)一下- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent;這個方法,我們app的遠程控制功能就完成了。
//響應(yīng)遠程音樂播放控制消息
- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {
if (receivedEvent.type == UIEventTypeRemoteControl) {
switch (receivedEvent.subtype) {
case UIEventSubtypeRemoteControlPlay:
[self.playCenter midBtnClicked:nil];
NSLog(@"暫停播放");
break;
case UIEventSubtypeRemoteControlPause:
[self.playCenter midBtnClicked:nil];
NSLog(@"繼續(xù)播放");
break;
case UIEventSubtypeRemoteControlNextTrack:
NSLog(@"下一曲");
break;
case UIEventSubtypeRemoteControlPreviousTrack:
NSLog(@"上一曲");
break;
default:
break;
}
}
}
這里我只是簡單的舉例了其中幾個常用的枚舉值,更詳細遠程控制會在后續(xù)完善。
差點忘記,在控制臺中也是可以看到正在播放的歌曲的哦,不過如果你沒有配置的話,那就只會顯示你app的名字了,代碼如下
[[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:@{MPMediaItemPropertyTitle:self.detailModel.sampleAudioTitle,MPMediaItemPropertyAlbumTitle:self.detailModel.sampleAuther}];
監(jiān)聽RouteChange事件
監(jiān)聽RouteChange事件,我們一般是為了實現(xiàn) 拔出耳機后暫停播放 這個功能。
//首先向NSNotificationCenter添加通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(routeChange:) name:AVAudioSessionRouteChangeNotification object:nil];
-(void)routeChange:(NSNotification *)notification實現(xiàn)如下:
/**
* 一旦輸出改變則執(zhí)行此方法
*
* @param notification 輸出改變通知對象
*/
-(void)routeChange:(NSNotification *)notification{
NSDictionary *dic=notification.userInfo;
int changeReason= [dic[AVAudioSessionRouteChangeReasonKey] intValue];
//等于AVAudioSessionRouteChangeReasonOldDeviceUnavailable表示舊輸出不可用
if (changeReason==AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
AVAudioSessionRouteDescription *routeDescription=dic[AVAudioSessionRouteChangePreviousRouteKey];
AVAudioSessionPortDescription *portDescription= [routeDescription.outputs firstObject];
//原設(shè)備為耳機則暫停
if ([portDescription.portType isEqualToString:@"Headphones"]) {
if (self.playButton.selected) {
[self playButtonAction:_playButton];
}
}
}
}
一般來說如果沒有實現(xiàn) 拔出耳機后暫停播放 ,系統(tǒng)會自動的把默認輸出設(shè)備設(shè)置為揚聲器。但是有一個例外,就是當(dāng)AVAudioSession 的 Category 設(shè)置為 AVAudioSessionCategoryPlayAndRecord 的時候,音頻輸出的默認設(shè)備是聽筒而不是揚聲器。這個時候如果你既不希望播放器自動暫停播放,又希望切換到揚聲器播放的時候,你可能需要做如下處理,一樣是在方法 -(void)routeChange:(NSNotification *)notification; 中實現(xiàn)
NSError *error = nil;
AVAudioSession *session = [AVAudioSession sharedInstance];
//強制設(shè)置為揚聲器播放
[session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
好啦,我們的AVAudioSession 也就在這里告一段落了。仍然希望這篇文章能夠幫助到有需要的朋友,有疑問的歡迎在評論中提出來,大家一起探討!