Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

avoid Headset plugout stops AVAudioPlayer in iOS

In my iPhone app I am using AVAudioPlayer to play the songs...But when I plug out or plugin the headset during song playing, It automatically stops the AVAudioPlayer... I need to run audio player even though these changes occur.. any ideas will be appreciated.Thanks in advance.

like image 580
Palani Jacob Avatar asked Jun 14 '13 07:06

Palani Jacob


Video Answer


3 Answers

First, you have to tell AVAudioSession the audio behaviour of your app. Apple name this the audio session category, an can be set by

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

For example, AVAudioSessionCategoryPlayback :

When using this category, your app audio continues with the Silent switch set to silent or when the screen locks. (The switch is called the Ring/Silent switch on iPhone.)

This category normally prevents audio from other apps from mixing with your app's audio. To allow mixing for this category, use the kAudioSessionProperty_OverrideCategoryMixWithOthers property.

Then, once the audio session set, the app will respond to some audio notifications, like AVAudioSessionInterruptionNotification or AVAudioSessionRouteChangeNotification

To answer, the original question, AVAudioSessionRouteChangeNotification is called when the audio route has been changed (ex: headset plug-out/plug-in, but also bluetooth device turning off, ...). With a bit of code, we can find the route change reason. And, in our case, start the player again il the headset has been unplugged.

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSError *setCategoryErr;
    [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:&setCategoryErr];
    
    // Detects when the audio route changes (ex: jack 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 player has been stopped, so play again!
        [self.player play];
    }
}

To conclude, also think about a user, in a boring meeting, who accidentaly plug-out his headset. He would not have this kind of behaviour, whose would make the device suddently scream in the room!

like image 199
Martin Avatar answered Sep 28 '22 11:09

Martin


Swift 3

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

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)

Route change observer (fix for unplugging headphones during playback):

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

Swift 2

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

Route change observer:

func audioRouteChanged(note: NSNotification) {
  if let userInfo = note.userInfo {
    if let reason = userInfo[AVAudioSessionRouteChangeReasonKey] as? Int {
      if reason == AVAudioSessionRouteChangeReason.OldDeviceUnavailable.rawValue {
        // headphones plugged out -> continue playback
        player.play()
      }
    }
  }
}
like image 34
budiDino Avatar answered Sep 28 '22 12:09

budiDino


I know this is old post but i did some research about this. @Martin answer was correct and i am using NSNotificationCenter but i am using Swift3 so these are things you can get from notification.userInfo

case AVAudioSessionInterruptionNotificationKey
/* value is an NSNumber representing an AVAudioSessionInterruptionType */

case AVAudioSessionInterruptionOptionsKey */
/* Only present for end interruption events.  Value is of type AVAudioSessionInterruptionOptions.*/

case AVAudioSessionRouteChangeReasonKey */
/* value is an NSNumber representing an AVAudioSessionRouteChangeReason */
    case unknown
    case newDeviceAvailable
    case oldDeviceUnavailable
    case categoryChange
    case override 
    case wakeFromSleep
    case noSuitableRouteForCategory
    case routeConfigurationChange

case AVAudioSessionRouteChangePreviousRouteKey * */
/* value is AVAudioSessionRouteDescription * */
    case input
    case output

case AVAudioSessionSilenceSecondaryAudioHintTypeKey */
/* value is an NSNumber representing an AVAudioSessionSilenceSecondaryAudioHintType */

Here is method in swift3

func audioSessionRouteChange(notification: NSNotification) {

    if let userInfo = notification.userInfo {

        print("Notification: AVAudioSessionInterruptionTypeKey = \(userInfo[AVAudioSessionInterruptionTypeKey])")
        print("Notification: AVAudioSessionInterruptionOptionKey = \(userInfo[AVAudioSessionInterruptionOptionKey])")
        print("Notification: AVAudioSessionSilenceSecondaryAudioHintTypeKey = \(userInfo[AVAudioSessionSilenceSecondaryAudioHintTypeKey])")

        if let reason = userInfo[AVAudioSessionRouteChangeReasonKey] as? Int {

            print("Notification: AVAudioSessionRouteChangeReasonOldDeviceUnavailable")

            if reason == AVAudioSessionRouteChangeReason.oldDeviceUnavailable.hashValue {

                print("Notification: Headphones out")
            }

            if reason == AVAudioSessionRouteChangeReason.newDeviceAvailable.hashValue {

                print("Notification: Headphones in")
            }
        }

        if let description = userInfo[AVAudioSessionRouteChangePreviousRouteKey] as? AVAudioSessionRouteDescription {

            // here you can check previous input and output 
            // po description.outputs[0].portType == AVAudioSessionPortBuiltInSpeaker

            print("Notification: AVAudioSessionRouteChangePreviousRouteKey Inputs: \(description.inputs)")
            print("Notification: AVAudioSessionRouteChangePreviousRouteKey Outputs: \(description.outputs)")
        }
    }
}
like image 36
Markicevic Avatar answered Sep 28 '22 12:09

Markicevic