Audio Session Interruption

近期处理了一个挂断电话后,莫名手机开始播放音乐的Bug。 所以顺便在这总结一下,对于IOS的AudioSession中断的几种处理情况。

一、通过C语言的init方法配置interruptionl回调。建议用这种方法,但有些细节需要注意,后续会谈到。

AudioSessionInitialize (
    NULL,                            // 1
    NULL,                            // 2
    interruptionListenerCallback,    // 3
    userData                         // 4
);

然后在回调,实现如下逻辑代码:

void interruptionListenerCallback ( void *inUserData,  UInt32  interruptionState ) {
    AudioViewController *controller = (AudioViewController *)inUserData;  
  if (interruptionState == kAudioSessionBeginInterruption) {         
        if (controller.audioRecorder) {
            [controller recordOrStop: (id) controller];                 
        } else if (controller.audioPlayer) {
            [controller pausePlayback];                                
            controller.interruptedOnPlayback = YES;                     
        }
    } else if ((interruptionState == kAudioSessionEndInterruption) && controller.interruptedOnPlayback) {
        [controller resumePlayback];
        controller.interruptedOnPlayback = NO;
    }
}

二、使用AVAudioSessionDelegate。如果你使用的是AVAudioPlayer或AVAudioRecorder,还可以使用对应的AVAudioPlayerDelegate和 AVAudioRecorderDelegate。但AVAudioSessionDelegate在6.0后被弃用,所以使用有局限性。后者没有被弃用。

- (void) beginInterruption {
    if (playing) {
        playing = NO;
        interruptedWhilePlaying = YES;
        [self updateUserInterface];
    }
}
NSError
*activationError = nil; - (void) endInterruption { if (interruptedWhilePlaying) { BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError]; if (!success) { /* handle the error in activationError */ } [player play]; playing = YES; interruptedWhilePlaying = NO; [self updateUserInterface]; } }

 

三、如上所说,6.0弃用了AVAudioSessionDelegate。所以6.0之后使用AVAudioSessionInterruptionNotification来实现类似的功能。AVAudioSessionInterruptionNotification的userInfo中包括AVAudioSessionInterruptionTypeKey和AVAudioSessionInterruptionTypeEnded。

四、使用RouteChange的回调。对于音乐播放,如果当然当前是耳机模式,拔掉耳机一般是希望音乐暂停的。类似这种拔插设备,播放语音等声音设备切换,一般通过RouteChange的回调来控制。

注册RouteChange的回调:

   AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,audioRouteChangeListenerCallback,nil);

回调处理代码:

void audioRouteChangeListenerCallback(void *inUserData, AudioSessionPropertyID inPropertyID, UInt32 inPropertyValueSize, const void *inPropertyValue) {
    if (inPropertyID != kAudioSessionProperty_AudioRouteChange)
        return;
    
    CFDictionaryRef routeChangeDictionary = inPropertyValue;
    CFNumberRef routeChangeReasonRef = CFDictionaryGetValue (routeChangeDictionary, CFSTR(kAudioSession_AudioRouteChangeKey_Reason));
    
    CFStringRef oldRouteRef = CFDictionaryGetValue (routeChangeDictionary,
                                                    CFSTR (kAudioSession_AudioRouteChangeKey_OldRoute));
    NSString *oldRouteString = (NSString *)oldRouteRef;
    
    SInt32 routeChangeReason;
    CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason);
    if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) {
        if (oldRouteStringplaying ) { //需判断不可用Route为耳机时
            playing = NO;
            interruptedWhilePlaying = NO;  //清除中断标识,如电话中拔掉耳机挂断时,不需要继续播放
        }
    }
}

对于AudioSession的Route,有如下几种模式:

/* Known values of route:
*"Headset"
* "Headphone"
* "Speaker"
* "SpeakerAndMicrophone"
* "HeadphonesAndMicrophone"
* "HeadsetInOut"
* "ReceiverAndMicrophone"
* "Lineout"
*/

//ios5以后可使用的一些类型
const CFStringRef kAudioSessionOutputRoute_LineOut;
const CFStringRef kAudioSessionOutputRoute_Headphones;
const CFStringRef kAudioSessionOutputRoute_BluetoothHFP;
const CFStringRef kAudioSessionOutputRoute_BluetoothA2DP;
const CFStringRef kAudioSessionOutputRoute_BuiltInReceiver;
const CFStringRef kAudioSessionOutputRoute_BuiltInSpeaker;
const CFStringRef kAudioSessionOutputRoute_USBAudio;
const CFStringRef kAudioSessionOutputRoute_HDMI;
const CFStringRef kAudioSessionOutputRoute_AirPlay;

 

关于其他的一些总结:

1、对于SDK6.0,AudioSession中断是一个Bug版本。不会响应AVAudioSessionDelegate,且不响应AVAudioSessionInterruptionNotification。C语言中断,当使用AVPlayer后,不响应kAudioSessionBeginInterruption,但响应kAudioSessionEndInterruption。 这是苹果的Bug。对上这种Case,有如下处理办法:

  1)当收到kAudioSessionEndInterruption时,调用暂停播放更新UI。

  2)使用RouteChange来判断电话的接通和挂断情境。但如果是耳机模式,电话的接通和挂断,经测试,使用的是同一种Route。所以,对于SDK6.0,播放过程中未接耳机时,可通过RouteChange来恢复播放。而耳机模式播放时,来电恢复播放,目前看来无完善的处理方式。

2、当后台播放歌曲时,打开游戏之类APP,声道会被其APP占用。这种Case会有kAudioSessionBeginInterruption。但返回时不会有kAudioSessionEndInterruption。所以,这种Case在回到APP时,需要interruptedWhilePlaying这个变量重置。

posted @ 2013-08-22 20:33  simalone  阅读(4170)  评论(0编辑  收藏  举报