AVplayer resuming after incoming call
Asked Answered
G

6

31

I am using AVPlayer for music playback. My problem is that after an incoming call, the player won't resume. How do I handle this when an incoming call comes?

Graben answered 23/12, 2013 at 4:3 Comment(0)
S
59

Starting from iOS 6 you must handle AVAudioSessionInterruptionNotification and AVAudioSessionMediaServicesWereResetNotification, before this you had to use delegate methods.

First you should call the AVAudioSession singleton and configure it for your desired use.

For example:

AVAudioSession *aSession = [AVAudioSession sharedInstance];
[aSession setCategory:AVAudioSessionCategoryPlayback
          withOptions:AVAudioSessionCategoryOptionAllowBluetooth 
                error:&error];
[aSession setMode:AVAudioSessionModeDefault error:&error];
[aSession setActive: YES error: &error];

Then you should implement two methods, for the notifications which the AVAudioSession would call:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(handleAudioSessionInterruption:)
                                             name:AVAudioSessionInterruptionNotification
                                           object:aSession];

First one is for any interruption which would be called because of an incoming call, alarm clock, etc.

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(handleMediaServicesReset)
                                             name:AVAudioSessionMediaServicesWereResetNotification
                                           object:aSession];

The second one if the media server resets for any reason, you should handle this notification to reconfigure audio or do any housekeeping. By the way the notification dictionary won't contain any object.

Here is an example for handling the playback interruption:

- (void)handleAudioSessionInterruption:(NSNotification*)notification {

    NSNumber *interruptionType = [[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey];
    NSNumber *interruptionOption = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey];

    switch (interruptionType.unsignedIntegerValue) {
        case AVAudioSessionInterruptionTypeBegan:{
            // • Audio has stopped, already inactive
            // • Change state of UI, etc., to reflect non-playing state
        } break;
        case AVAudioSessionInterruptionTypeEnded:{
            // • Make session active
            // • Update user interface
            // • AVAudioSessionInterruptionOptionShouldResume option
            if (interruptionOption.unsignedIntegerValue == AVAudioSessionInterruptionOptionShouldResume) {
                // Here you should continue playback.
                [player play];
            }
        } break;
        default:
            break;
    }
}

Notice that you should resume playback when the optional value is AVAudioSessionInterruptionOptionShouldResume

And for the other notification you should take care of the following:

- (void)handleMediaServicesReset {
// • No userInfo dictionary for this notification
// • Audio streaming objects are invalidated (zombies)
// • Handle this notification by fully reconfiguring audio
}

Regards.

Sakti answered 26/12, 2013 at 21:9 Comment(2)
In the documentation it says "take the appropriate steps to re-initialize any audio objects used by your application" when handling MediaServicesWereReset.. is it just reconfigure the AudioSession and set it back to Active. anything else I should do?Selfwinding
Awesome!! it's really working for me! Thanks for this piece of save my timeBiogeography
C
9

AVAudioSession will send a notification when an interruption starts and ends. See Handling Audio Interruptions

- (id)init
{
    if (self = [super init]) {
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

        NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
        [center addObserver:self selector:@selector(audioSessionInterrupted:) name:AVAudioSessionInterruptionNotification object:nil];
    }
}

- (void)audioSessionInterrupted:(NSNotification *)notification
{
    int interruptionType = [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue];
    if (interruptionType == AVAudioSessionInterruptionTypeBegan) {
        if (_state == GBPlayerStateBuffering || _state == GBPlayerStatePlaying) {
            NSLog(@"Pausing for audio session interruption");
            pausedForAudioSessionInterruption = YES;
            [self pause];
        }
    } else if (interruptionType == AVAudioSessionInterruptionTypeEnded) {
        if ([notification.userInfo[AVAudioSessionInterruptionOptionKey] intValue] == AVAudioSessionInterruptionOptionShouldResume) {
            if (pausedForAudioSessionInterruption) {
                NSLog(@"Resuming after audio session interruption");
                [self play];
            }
        }
        pausedForAudioSessionInterruption = NO;
    }
}
Cohune answered 23/12, 2013 at 4:10 Comment(2)
can we use CTCallCenter for thisGraben
I wouldn't suggest CTCallCenter because AVAudioSession provides a more detailed interface. I updated my answer to remove deprecated methods.Cohune
L
2

In some cases my AVPlayer don't resume play even if I call play(). Only reload player helps me to resolve the problem:

    func interruptionNotification(_ notification: Notification) {
    guard let type = notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? UInt,
      let interruption = AVAudioSessionInterruptionType(rawValue: type) else {
        return
    }
    if interruption == .ended && playerWasPlayingBeforeInterruption {
      player.replaceCurrentItem(with: AVPlayerItem(url: radioStation.url))
      play()
    }
  }
Lole answered 19/4, 2017 at 18:46 Comment(0)
B
2

Hi everyone who read this. Want to share my experience with AVAudioSessionInterruptionNotification. It drove me crazy. I spent a day to find a solution. I tried everything written above and nothing helped. iOS 13.2.3. So for some reason this notification is not working properly with AVAudioSessionCategoryPlayback. And the fix for me was just to replace it with AVAudioSessionCategoryPlayAndRecord and I started to receive the AVAudioSessionInterruptionTypeEnded See example below:

-(void)setupAudioSession {

    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
    [[AVAudioSession sharedInstance] setMode:AVAudioSessionModeDefault error:nil];
    [[AVAudioSession sharedInstance] setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];    
}

After that register your observer, like this:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioSessionHandleIntrruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];

And actually implement the interruption method:

- (void)audioSessionHandleIntrruption:(NSNotification *)notification {

    NSDictionary *userInfo = notification.userInfo;
    int interruptionType = [userInfo[AVAudioSessionInterruptionTypeKey] intValue];

    if (interruptionType == AVAudioSessionInterruptionTypeBegan) {
        //Interruption begun

    } else if (interruptionType == AVAudioSessionInterruptionTypeEnded) {
        int interruptionOption = [userInfo[AVAudioSessionInterruptionOptionKey] intValue];

        BOOL shouldResumePlayback = interruptionOption == AVAudioSessionInterruptionOptionShouldResume;

        if (shouldResumePlayback) {
            //Resume playback if needed
        }
    }
}

Hope this could save to someone a lot of time.

Beamon answered 6/4, 2020 at 14:36 Comment(0)
C
1

I have to wait for 2 seconds to make it work.

        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {

            self.player.play()
        }

The whole func:

func playerInterruption(notification: NSNotification) {

    guard let userInfo = notification.userInfo,
        let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
        let type = AVAudioSessionInterruptionType(rawValue: typeValue) else {
            return
    }
    if type == .began {
        // Interruption began, take appropriate actions (save state, update user interface)
        player.pause()
    }
    else if type == .ended {

        guard let optionsValue =
            userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else {

                return
        }
        let options = AVAudioSessionInterruptionOptions(rawValue: optionsValue)
        if options.contains(.shouldResume) {
            // Interruption Ended - playback should resume

            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {

                self.player.play()
            }

        }
    }
}
Culpable answered 26/9, 2018 at 8:2 Comment(0)
P
1

I was getting the same problem in "AVAudioPlayer" controller. Using "AVAudioSession.interruptionNotification" we can resume audio playing once interruption ended even though audio playing in the background.

    @objc func interruptionNotification(notification: Notification) {
    guard let type = notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? UInt,
        let interruption = AVAudioSession.InterruptionType(rawValue: type) else {
            return
    }

    if interruption == .began{
        print("Pause audio....")
        self.player?.pause()
        do {
            try AVAudioSession.sharedInstance().setActive(false)
            print("AVAudioSession is inactive")
        } catch let error as NSError {
            print(error.localizedDescription)
        }
    }

    if interruption == .ended{
        print("Play audio....")
        do {
            try AVAudioSession.sharedInstance().setActive(true)
            print("AVAudioSession is Active again")
            self.player?.play()
        } catch let error as NSError {
            print(error.localizedDescription)
        }
    }
}

Here first you need to InActive AVAudioSession when it's interrupted and Active AVAudioSession once interruption ended. It's working perfectly please try it!

Proletarian answered 31/7, 2019 at 3:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.