无法使用 AVPlayer 将 AudioSession 设置为非活动状态

Unable to set AudioSession inactive with AVPlayer

我正在使用 AVPlayer 在我的 iOS 应用程序中播放音频(因为我正在播放的音频文件不是本地的),当我的曲目结束时,我想隐藏信息中心控件,但出现此错误:

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.

但是我之前暂停了我的AVPlayer

这是我的代码:

player.pause()
player.replaceCurrentItem(with: nil)

MPNowPlayingInfoCenter.default().nowPlayingInfo = nil
try? audioSession.setActive(false, options: .notifyOthersOnDeactivation)

由于这个错误,本机控件仍然可见,但不再工作。

您收到该错误是因为您在取消分配播放器之前停用了音频会话: 您可以通过以下方式解除分配:

play.pause()
player = nil

但您应该先声明您的播放器为可选播放器。 你的问题是关于:

您必须删除

player.replaceCurrentItem(with: nil)
MPNowPlayingInfoCenter.default().nowPlayingInfo = nil

一个只保留这个

player.pause()
try? audioSession.setActive(true, options: .notifyOthersOnDeactivation)

失去锁定屏幕控件的快速方法是使您的音频会话可混合(无需停用您的音频会话):

try! AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: .mixWithOthers)

当你想要它们时,删除 .mixWithOthers

另一种可能更可口的方法是在暂停播放后删除 MPRemoteCommandCenter 目标。例如如果你这样设置

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
    }
}

然后像这样删除目标:

func removeRemoteTargets() {
    let commandCenter = MPRemoteCommandCenter.shared()
    commandCenter.playCommand.removeTarget(playTarget)
    playTarget = nil
    commandCenter.pauseCommand.removeTarget(pauseTarget)
    pauseTarget = nil
}

p.s。这些是 "seems to work!" 个答案。我不知道您应该如何确定 AVPlayer 何时完成其音频 I/O(除了通过 setActive:false 失败的轮询)。我的理由是成为锁定屏幕/"Now Playing App" 有 3 个部分(这在某处有记录,this 是我现在能找到的最接近的东西,尽管最明确的文档是如何做到这一点的作品在系统日志中):

  1. 不可混合的音频会话类别
  2. MPRemoteCommandCenter 整合
  3. 当前或"recently" 完成的音频 IO

因此,如果您可以删除其中任何一个,锁定屏幕控件应该会消失。

试试这个

player.pause()
player.replaceCurrentItem(with: nil)
player = nil

AVPlayer 不会立即停止,因此您需要订阅其状态更改以在暂停时捕获。只有这样你才能安全地停用 AVAudioSession

Swift 5 个例子

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()