Avoid blocking the AVAudioPlayer headset in iOS

In my iPhone app, I use AVAudioPlayer to play songs ... But when I plug in or plug in the headset while playing a song, it automatically stops AVAudioPlayer ... I need to start the audio player even if these changes occur .. any ideas will be appreciated. Thanks in advance.

+4
source share
6 answers

I have found the answer.

We just need to import the following files

#import <AudioToolbox/AudioToolbox.h> #import <AVFoundation/AVAudioPlayer.h> 

and write this code

 //Play the Event in Background NSError *setCategoryErr = nil; NSError *activationErr = nil; [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: &setCategoryErr]; [[AVAudioSession sharedInstance] setActive: YES error: &activationErr]; [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid; newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL]; 

Now he constantly plays, even I'm connected to the headphone.

-1
source

First, you must specify the AVAudioSession sound behavior of your application. Apple called this category an audio session; it can be set using

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

For example, AVAudioSessionCategoryPlayback :

When using this category, the sound of your application continues with the Silent switch turned off or the screen turned off. (The switch is called the Ring / Silent switch on the iPhone.)

This category generally prohibits mixing sound from other applications using an audio application. To enable mixing for this category, use the kAudioSessionProperty_OverrideCategoryMixWithOthers property.

Then, as soon as the audio recording is installed, the application will respond to some sound notifications, such as AVAudioSessionInterruptionNotification or AVAudioSessionRouteChangeNotification

To answer the original question, AVAudioSessionRouteChangeNotification is called when the audio route has been changed (for example, the plug-in / plug-in for the headset, and the Bluetooth device is also turned off). With a little code, we can find the reason for the route change. And, in our case, start the player again, and the headset is turned off.

 - (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]; } } 

In conclusion, also think about the user in a boring meeting who accidentally connected his headset. He did not have such behavior that would make the device scream loudly in the room!

+7
source

Swift 3

Set up your player - play audio (even in silent mode) and turn off 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 disconnecting 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() } } } } 
+3
source

I know this is an old post, but I did some research about it. @Martin's answer was correct and I am using NSNotificationCenter, but I am using Swift3, so these are the 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 the 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)") } } } 
+1
source

@Martin almost immediately except when we receive AVAudioSessionRouteChangeNotification notifications, the sound can still play, you should check the player's property rate . If it is equal to zero, play it, otherwise you should observe the rate , when it changes to zero, play it. Check link

One more note: AVAudioSessionRouteChangeNotification located in the secondary stream (not in the main stream), if necessary, you should send it to the main stream.

0
source

Source: https://habr.com/ru/post/1486184/


All Articles