I am using an AVPlayer
to playback audio in my iOS app (because the audio files I'm playing are not local), and when my track finishes, I want to hide the InfoCenter controls, but I get this error:
AVAudioSession.mm:1079:-[AVAudioSession setActive:withOptions:error:]: Deactivating an audio session that has running I/O. All I/O should be stopped or paused prior to deactivating the audio session.
I am however pausing my AVPlayer
before.
This is my code:
player.pause()
player.replaceCurrentItem(with: nil)
MPNowPlayingInfoCenter.default().nowPlayingInfo = nil
try? audioSession.setActive(false, options: .notifyOthersOnDeactivation)
Due to this error, the native controls remain visible, but don't work anymore.
A quick way to lose the lock screen controls is to make your audio session mixable (without deactivating your audio session):
try! AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: .mixWithOthers)
Remove the .mixWithOthers
when you want them back.
Another, perhaps more palatable way is to remove your MPRemoteCommandCenter
targets once you pause playback. e.g. if you set it up like this
var playTarget: Any? = nil
var pauseTarget: Any? = nil
func addRemoteTargets() {
let commandCenter = MPRemoteCommandCenter.shared()
playTarget = commandCenter.playCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
print("play \(event)")
return .success
}
pauseTarget = commandCenter.pauseCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
print("pause \(event)")
return .success
}
}
Then remove the targets like so:
func removeRemoteTargets() {
let commandCenter = MPRemoteCommandCenter.shared()
commandCenter.playCommand.removeTarget(playTarget)
playTarget = nil
commandCenter.pauseCommand.removeTarget(pauseTarget)
pauseTarget = nil
}
p.s. these are "seems to work!" answers. I don't know how you are supposed to determine when an AVPlayer
has finished its audio I/O (apart from polling via setActive:false
failing). My reasoning is that becoming the lock screen/"Now Playing App" has 3 pieces (this is sort of documented somewhere, this is the closest thing I can find to that right now, although the most explicit documentation of how this works is in the system logs):
MPRemoteCommandCenter
integrationso if you can remove any one of these pieces, the lock screen controls should go away.
AVPlayer
doesn't stop instantly, so you need to subscribe for its status change to catch when it got paused. Only then you can deactivate the AVAudioSession
safely.
var timeControlObservation: NSKeyValueObservation?
timeControlObservation = player.observe(\.timeControlStatus, changeHandler: { [weak player] (_, _) in
guard let player = player else {
return
}
if player.timeControlStatus != .playing {
// Finish your work here
AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
timeControlObservation = nil
}
}
player.pause()
You getting that error because you are deactivating the audio session before deallocating the player: you can deallocate it with:
play.pause()
player = nil
but you should declare your player as optional first. You question is related to: Impossible to stop AVPlayer
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