AVAudioSession-Category的正確使用姿勢

最近,在開發(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,拿不到準確的值。

最后,上面的所有的經(jīng)驗和總結,都是通過各種查閱資料和不斷調試得來的,并沒有較為科學嚴謹?shù)睦碚撘罁?jù),也沒有相關的官方文檔的支持??偨Y出來,只是希望給后續(xù)如果有人遇到與我一樣的難題時,少走一些彎路,有一些啟發(fā),僅此而已。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容