I have an app that can show video and I would like the following behavior.
When a video first appears onscreen and starts playing, it's audio should mix with audio from other apps that may already be playing. For example, if the user is listening to Spotify, they should hear audio from both Spotify and the playing video.
If the user taps a specific button, audio from other apps should be muted so that only audio from the video plays.
If the user taps the button again, audio from other apps should resume and the video's audio along with the audio from other apps should mix again.
Essentially, I want to be able to toggle my app's audio mode between "mixing" and "non-mixing".
Here's some code that show's how I'm modifying the AVAudioSession
to try to get this functionality.
Turning on Mixing Mode
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryAmbient
withOptions:AVAudioSessionCategoryOptionMixWithOthers
error:nil];
[session setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
Turning off Mixing Mode
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback
withOptions:0
error:nil];
[session setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:nil];
The problem that I'm having is that turning on audio mixing after previously turning it off, doesn't notify other apps that they can resume playing audio again. I really, really want that behavior. Is that something that's just not possible.
It seems like the only way to notify other apps that they can resume is to set my app's audio session to be "inactive". Unfortunately doing this while a video is playing causes a warning to be printed out in the console saying that I should stop playing audio before deactivating the audio session. This also has the effect of basically breaking the AVPlayer, as it can no longer play any audio.
Anyone know how to work around these problems? Any solution would be welcome, even ones that are pretty complicated or that make use of a private API.
So as other answers have pointed out you can "pause" the audio of another app. However, you can turn it down by using the .duckWithOthers
. Once your audio is finished you can reset that option back to .mixWithOthers
and notify other apps that they volume can be restored to the full level.
Let's assume your audio starts when your ViewController appears and stops when your ViewController disappears. Then you can use the following code:
override func viewWillAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Use duckOthers so the audio in your VC is slightly louder than the background audio such as Spotify
updateAVAudioSessionOptions(to: .duckOthers)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Reset to mixWithOthers since we are no longer playing audio
updateAVAudioSessionOptions(to: .mixWithOthers)
// probably a good idea to stop the audio as well
player.pause()
}
private func updateAVAudioSessionOptions(to options: AVAudioSession.CategoryOptions) {
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback, mode: AVAudioSession.Mode.default, options: options)
if options == .mixWithOthers {
// Notify other apps that they volume can be restored to the full level
try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
}
} catch let error {
print("Error: \(error)")
}
}
I think you're out of luck. Even AVAudioSessionCategoryOptionDuckOthers
, which sounds like it should fit your use case, requires you to deactivate your session to return other apps' volume to normal, but as you've seen this causes errors.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With