3
votes

Apple indicates that when headphones are unplugged, iOS automatically stops playing.

While this is true when using AVPlayer, it actually does NOT work as expected when using AVAudioPlayer, instead audio keeps continuing to play from the built in speakers.

Link to Apple's site: https://developer.apple.com/library/content/documentation/Audio/Conceptual/AudioSessionProgrammingGuide/HandlingAudioHardwareRouteChanges/HandlingAudioHardwareRouteChanges.html

3

3 Answers

2
votes

I'm not sure but Try this:

- (void)viewDidLoad {
    [super viewDidLoad];

    NSError *setCategoryErr;
    [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:&setCategoryErr];

    // Detects when the audio route changes (ex: headphones unplugged)
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioHardwareRouteChanged:) name:AVAudioSessionRouteChangeNotification object:nil];
    // Don't forget to remove notification in dealloc method!!
}

- (void)audioHardwareRouteChanged:(NSNotification *)notification {
    NSInteger routeChangeReason = [notification.userInfo[AVAudioSessionRouteChangeReasonKey] integerValue];
    if (routeChangeReason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
        // if we're here, The old device is unavailable == headphones have been unplugged, so stop manually!
        [self.player stop];
    }
}
2
votes

You need to observe the hardware rout change observer and based on callback, You can just stop playing.

Setup your player - play audio (even on silent mode) and silence other music:

let audioSession = AVAudioSession.sharedInstance()
_ = try? audioSession.setCategory(AVAudioSessionCategoryPlayback, with: .duckOthers)
_ = try? audioSession.setActive(true)
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(audioRouteChanged), name: .AVAudioSessionRouteChange, object: nil)


func audioRouteChanged(note: Notification) {
   if let userInfo = note.userInfo {
       if let reason = userInfo[AVAudioSessionRouteChangeReasonKey] as? Int  {
       if reason == AVAudioSessionRouteChangeReason.oldDeviceUnavailable.hashValue {
       // headphones plugged out
       player.stop()
      }
    }
  }
}

Important: Media playback apps should pause playback if the route change reason is AVAudioSessionRouteChangeReasonOldDeviceUnavailable, but should not if the reason is AVAudioSessionRouteChangeReasonOverride.

1
votes

From the document you linked to

Important: Media playback apps should pause playback if the route change reason is AVAudioSessionRouteChangeReasonOldDeviceUnavailable, but should not if the reason is AVAudioSessionRouteChangeReasonOverride.

"Should" can indicate either duty or the likelihood of an event. In this case it means it is your duty to implement pausing when headphones, etc, are unplugged and not that it is likely to happen without any intervention on your part (as you've seen, it doesn't).

Looking at it with fresh eyes, the documentation is ambiguous. Should was not a good word choice.