這就要說到
AVAudioSession-Category的正確使用姿勢
解決問題代碼如下
// 使用這個category的應用不會隨著手機靜音鍵打開而靜音,可在手機靜音下播放聲音
NSError *setCategoryError = nil;
BOOL success = [[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategoryPlayback
error: &setCategoryError];
if (!success) { /* handle the error in setCategoryError */ }
最近,在開發(fā)一款音樂播放器類型項目中遇到的一些與AVAudioSession-Category設置的一些坑,以下是整個過程的一些經(jīng)驗總結。
1.常規(guī)播放
一般如果應用只有簡單音樂播放功能梦谜,那么我們的AVAudioSession-Category只用像如下一樣設置即可:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; [[AVAudioSession sharedInstance] setActive:YES error:nil];
此時如果我們只是播放音樂,而不需要獨占鎖屏界面時袭景,還可以設置:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
withOptions:AVAudioSessionCategoryOptionMixWithOthers
error:nil];
這樣我們兼容其他后臺播放的音樂一起進行播放唁桩,不過大部分場景下,我們是需要獨占式后臺播放耸棒。
2.常規(guī)錄音
在錄音的時候荒澡,我們一般如以下設置:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
3.如果將錄音和播放同時進行時,我們改選擇何種Category与殃?
同時進行播放和錄音時单山,我們需要這樣設置:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
需要注意的是,設置成這樣的情況下幅疼,如果米奸,在錄音未開啟的情況下,直接進行播放爽篷,則會出現(xiàn)悴晰,播放音量特別小的情況,我們需要在播放之前狼忱,將錄音打開膨疏。
4.前后臺切換
上述的模式,在iOS系統(tǒng)下钻弄,是不允許錄音和播放在后臺狀態(tài)下同時進行的(PS:語音視頻通話是通過CallKit實現(xiàn)的,不用于常規(guī)的播放和錄音功能)者吁。由此窘俺,我們在應用進入后臺時就需要關掉其中一個功能。
以后臺支持播放為例,在應用將要失活時瘤泪,先切換模式灶泵,再關掉錄音功能:
// stopRecording...
// 切換模式
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
應用即將進入前臺時,切換模式对途,再開啟錄音功能:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
// 延遲恢復赦邻,否則會導致AVAudioSession的i/o錯誤
[self performSelectorOnMainThread:@selector(startRecording) withObject:nil waitUntilDone:NO];
5.電話中斷
電話鬧鐘的中斷也會對,[AVAudioSession sharedInstance] 產(chǎn)生影響实檀。
我們一般場景下會用 下面這個通知進行監(jiān)控并處理暫停和恢復的工作:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:nil];
- (void)handleInterruption:(NSNotification*)notification { NSLog(@"interruption info:%@",notification.userInfo); }
但是惶洲,當我們在處理第四個場景前后臺的情況下,這個通知膳犹,在中斷的時候會進入恬吕,但是電話結束后,不會再接收到中斷結束的通知须床。
原因:
有的app使用了AVCaptureDevice和AVCaptureSession铐料,以進行錄音錄像操作。為了調優(yōu)app設置豺旬,以更好的進行錄音錄像钠惩,從iOS7開始,在默認情況下族阅,AVCaptureSession會使用app的AVAudioSession篓跛,并對其進行修改。這樣耘分,設置的中斷監(jiān)聽方法會失效举塔。
而電話來電也會使我們的應用接收到 失活的通知,在失活的時候處理了AVAudioSession求泰,就會導致上述通知失效央渣。
解決方案:我這里采用了比較折中的方案,因為我們的需求渴频,對于第四條的處理是必要的芽丹。使用的是 CoreTelephony框架下的CTCallCenter對象,來監(jiān)控電話的 撥入接通卜朗、掛斷等狀態(tài)拔第。代碼如下:
self.center = [[CTCallCenter alloc] init];
// TODO: 檢測到來電后的處理
self.center.callEventHandler = ^(CTCall * call){
if (call.callState == CTCallStateIncoming ||
call.callState == CTCallStateConnected ||
call.callState == CTCallStateDialing)
{
}
else if (call.callState == CTCallStateDisconnected)
{
}
};
通過各種打電話的場景測試后,可以實現(xiàn)電話中斷恢復功能场钉。
ps:至于鬧鐘的中斷以及siri等其他中斷蚊俺,暫時沒有調研和實現(xiàn)。
6.藍牙車載
終于來到了本文的最后一個部分了逛万,也是最為曲折的一部分泳猬。
本來以為車載的車機連接后對于iPhone的播放控制與鎖屏控制類似,直接在系統(tǒng)媒體遠程控制監(jiān)控中就能夠拿到相應的控制方法回調。
在APPDelegate中加上如下代碼:
//監(jiān)聽遠程交互方法
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
{
switch (event.subtype)
{
//播放
case UIEventSubtypeRemoteControlPlay:
break;
//停止
case UIEventSubtypeRemoteControlPause:
break;
//下一首
case UIEventSubtypeRemoteControlNextTrack:
break;
//上一首
case UIEventSubtypeRemoteControlPreviousTrack:
break;
default:
break;
}
}
事實上得封,當我們的應用只有簡單的播放功能的時候埋心,上述代碼的確可以完美的實現(xiàn)車機對于播放的控制功能。但是當應用出于前臺的情況下忙上,我們添加上了一直錄音的功能的時候拷呆,用車機控制播放,就完全沒有任何響應了疫粥〔绺可以注意到的是,我們看到車機的屏幕上手形,會顯示通話中啥供。查閱了各種資料和文章,都沒有找到相關的解決辦法和原理解釋库糠。
最后伙狐,想到了看看有沒有其他類似的語音識別及播放功能的應用(iOS)有沒有類似的處理,結果調研到百度地圖 中的小度 有相關的處理瞬欧。在它的設置中贷屎,找到 語音設置有一個藍牙連接設置 。兩個模式設置 如下:
a.藍牙設備播報艘虎,小度無法喚醒使用(播放體驗最佳)
b.藍牙設備播報唉侄,小度喚醒正常使用(車機顯示通話中,播報音量可能變小)
由此可以看出野建,a場景下 錄音功能關閉属划,只有語音播報功能,b場景下候生,錄音功能開啟同眯,車機就是會識別到手機設備在錄音和播放中,認為就是在通話中唯鸭,這個是車機本身的限制须蜗,無法從應用層進行優(yōu)化。而且目溉,百度地圖的給用的默認選擇就是明肮,連接藍牙的情況下,小度不能喚醒缭付。
綜合上面我們協(xié)同產(chǎn)品柿估,從交互層面上更改,保證陷猫,在連接車機的情況下官份,能夠控制播放只厘。具體處理交互如下:
在應用進入到前臺時烙丛,檢測到連接了藍牙設備舅巷,彈出彈框,讓用戶選擇河咽,繼續(xù)開啟喚醒功能開始钠右,關閉喚醒功能(保證播放控制功能)。繼續(xù)開啟的情況下忘蟹,車機無法控制播放飒房。
下面是檢測是否有輸出設備連接的代碼(并未找到檢查當前是否有連接藍牙設備的方法):
+ (BOOL)checkIsConnectToBluetooth
{
BOOL isBluetooth = NO;
// 找出當前所有支持輸入的設備 availableInputs 這里面會出現(xiàn) iPhone麥克風, 藍牙耳機1, 藍牙耳機2 , 三個對象, 在一個數(shù)組里.
NSArray* inputArray = [[AVAudioSession sharedInstance] availableInputs];
for (AVAudioSessionPortDescription* desc in inputArray)
{
if ([desc.portType isEqualToString:AVAudioSessionPortBluetoothLE] ||
[desc.portType isEqualToString:AVAudioSessionPortBluetoothHFP] ||
[desc.portType isEqualToString:AVAudioSessionPortBluetoothA2DP])
{
isBluetooth = YES;
}
}
return isBluetooth;
}
同時,還需要配合Category的設置:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:&error];
[[AVAudioSession sharedInstance] setActive:YES error:&error1];
AVAudioSessionCategoryOptionAllowBluetooth這是必須要添加的媚值,否則上面的方法狠毯,連接藍牙后,在應用即將活躍的監(jiān)控的時候褥芒,是會返回NO嚼松,拿不到準確的值。