How to Resume Audio After Interruption in Swift

How to resume audio after interruption in Swift?

(Posted on behalf of the question author, after it was posted in the question).

Solution found! Following discussion here, inserted this in viewDidLoad()

do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, withOptions: AVAudioSessionCategoryOptions.MixWithOthers)
} catch {
}

After clicking "ok" on the alarm interruption, the audio play continued. Unlike previously noted, the solution does NOT require an interruption handler (which @Leo Dabus has since removed).

However if you are using an interruption handler, .play() must NOT be invoked within handleInterruption() as doing so does NOT guarantee play to resume & seems to prevent audioPlayerEndInterruption() to be called (see docs). Instead .play() must be invoked within audioPlayerEndInterruption() (any of its 3 versions) to guarantee resumption.

Furthermore, AVAudioSession must be give option .MixWithOthers noted by @Simon Newstead if you want your app to resume play after interruption when your app is in the background. It seems that if a user wants the app to continue playing when it goes into the background, it is logical to assume the user also wants the app to resume playing after an interruption while the app is in the background. Indeed that is the behaviour exhibited by the Apple Music app.

Background audio doesn't resume after a call

We are resuming our audio after an outbound call and inbound call while the app is in the background.

We play audio with AVAudioPlayer and listen to the AVAudioSessionInterruptionNotification. Apple automatically pauses the AVAudioPlayer for you on an interruption and when you tell it to resume after you receive the interruption is over, Apple will set your session active again. See Table 3-2 from Handling Audio Interruptions on recommendations if you are using other types of audio technologies.

Subscribe to the notification:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAudioSessionEvent:) name:AVAudioSessionInterruptionNotification object:nil];

Handle the notification:

- (void) onAudioSessionEvent: (NSNotification *) notification
{
//Check the type of notification, especially if you are sending multiple AVAudioSession events here
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
NSLog(@"Interruption notification received!");

//Check to see if it was a Begin interruption
if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
NSLog(@"Interruption began!");

} else {
NSLog(@"Interruption ended!");
//Resume your audio
}
}
}

How to resume background audio in Swift 2 / AVPlayer?

Finally got it!

Solution: added the mixable option by changing the setCategory line to be:

AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback,
withOptions: .mixWithOthers )

Resuming AVPlayer after phone call

I solved this but creating a shared VideoPlayer class that contained references to all the screen that had animations.

import Foundation
import UIKit
import AVKit

class VideoPlayer: NSObject {

static var shared: VideoPlayer = VideoPlayer()

var avPlayer: AVPlayer!
var avPlayerLayer: AVPlayerLayer!

weak var vcForConnect:ConnectVC?
weak var vcForList:ListVC?

override init() {
super.init()
guard let path = Bundle.main.path(forResource: "animation", ofType:"mp4") else {
print("video not found")
return
}
avPlayer = AVPlayer(url: URL(fileURLWithPath: path))
avPlayerLayer = AVPlayerLayer(player: avPlayer)
avPlayerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
avPlayer.volume = 0
avPlayer.actionAtItemEnd = .none
loopVideo(videoPlayer: avPlayer)
avPlayer.play()
NotificationCenter.default.addObserver(self, selector: #selector(handleInterruption(notification:)), name: AVAudioSession.interruptionNotification, object: nil)

}

deinit {
avPlayer.pause()
}

@objc func handleInterruption(notification: Notification) {
guard let info = notification.userInfo,
let typeValue = info[AVAudioSessionInterruptionTypeKey] as? UInt,
let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
return
}
if type == .began {
// Interruption began, take appropriate actions (save state, update user interface)
self.avPlayer.pause()
} else if type == .ended {
guard let optionsValue =
info[AVAudioSessionInterruptionOptionKey] as? UInt else {
return
}
let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
if options.contains(.shouldResume) {
// Interruption Ended - playback should resume
self.avPlayer.play()
}
}
}

func resumeAllAnimations() {
self.avPlayer.play()
if vcForList?.avPlayer != nil {
vcForList?.avPlayer.play()
}
if vcForConnect?.avPlayer != nil {
vcForConnect?.avPlayer.play()
}
if vcForConnect?.avPlayerBG != nil {
vcForConnect?.avPlayerBG.play()
}
}
...
}

I then resume the animations by calling resumeAllAnimations() in applicationDidBecomeActive(_:) in AppDelegate.swift like so:

func applicationDidBecomeActive(_ application: UIApplication) {
VideoPlayer.shared.resumeAllAnimations()
...
}


Related Topics



Leave a reply



Submit