繁体   English   中英

AVAudioSession 错误:停用正在运行 I/O 的音频会话

[英]AVAudioSession error: Deactivating an audio session that has running I/O

2017-02-24 14:56:44.280 PropertyManager[10172:5336578] 14:56:44.280 ERROR:    [0x1a0a24000] AVAudioSession.mm:692: -[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.

2017-02-24 14:56:44.281 PropertyManager[10172:5336578] error === Error Domain=NSOSStatusErrorDomain Code=560030580 "(null)"
PropertyManager was compiled with optimization - stepping may behave oddly; variables may not be available.

您的错误日志非常简洁地自我表达:

Deactivating an audio session that has running I/O. All I/O should be stopped or paused prior to deactivating the audio session

它会告诉您问题以及解决方案。

现在你正在做这样的事情:

[[AVAudioSession sharedInstance] setActive:NO
               withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
                                             error:nil];

但是,您应该首先停止音频播放器实例,然后将激活状态设置为是或否。

[yourAudioPlayer stop];
[[AVAudioSession sharedInstance] setActive:NO
                   withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
                                                 error:nil];

请参阅 Apple 文档以查看enum AudioSessionSetActiveOption 的值。

另请参阅:关于setActive:withOptions方法的 Apple 文档

至于你的第二个错误

PropertyManager was compiled with optimization - stepping may behave oddly; variables may not be available.

看到这个优秀的答案

让音频播放器在录制会话假之前停止..如果你想让音频播放器为零..删除那条线..它对我有用..

@NSNoob是 100% 正确的。 播放器(或其他东西)仍处于活动状态。

这就是我使用dani-mp的解决方案做到的。 他说:

我会说暂停播放器不是同步操作,因此我们不应该在知道播放器已暂停之前停用会话(请参阅此线程中的响应)。

此问题的解决方案可能是我们监听 timeControlStatus 的更改并在播放器真正暂停后停用音频会话。

线程中的答案说

此错误表示在调用 AVAudioSession setActive:NO 时,您的应用程序中的某些内容仍在使用音频 I/O。 在没有更多信息的情况下无法说出哪个对象,但有时这是对某种播放器调用异步停止方法的结果,而不是在停用音频会话之前等待播放器已停止的通知。

要在后台播放音频并使用MPRemoteCommandCenter我必须从.mixWithOthers切换到.playback

阅读注释掉的代码中的 10 个步骤。

var player: AVPlayer?
var playerTimeControlStatusObserver: NSKeyValueObservation?
var wasPlayerPlayingBeforeItWentToTheBackground = false // 0. this is needed in steps 2 and 6 

override func viewDidLoad() {
     super.viewDidLoad()

     NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
}

func checkIfPlayerWasPlayingWhenAppEntersBackground() { // 1. this gets called in the UIApplication.didEnterBackgroundNotification selector method

    if let player = player, player.isPlaying  {
                
        wasPlayerPlayingBeforeItWentToTheBackground = true // 2.  this is needed in step 6

        player.pause() // 3. once the player is paused the `.timeControlStatus` will fire when it's 100% stopped
    }
}

playerTimeControlStatusObserver = player?.observe(\.timeControlStatus, options: [.new, .old]) { [weak self](player, change) in

    switch (player.timeControlStatus) {
        // ... other cases

        case .paused: // 4. read the very long function name to see what happens next. Once this .paused case runs the player is Definitely stopped.
            DispatchQueue.main.async { [weak self] in
                self?.checkTimeStatusAndApplicationStateToMakeSureThePlayerIsDefinitelyStoppedAndInTheBackground()
            }

        // ... other cases
    }
}

func checkTimeStatusAndApplicationStateToMakeSureThePlayerIsDefinitelyStoppedAndInTheBackground() {

    let state = UIApplication.shared.applicationState
    if state == .background { // 5. this code only runs if it's in the background

        if wasPlayerPlayingBeforeItWentToTheBackground { // 6. this has to be true

            wasPlayerPlayingBeforeItWentToTheBackground = false // 7. set it back to false

            do { // 8. deactivate the AVAudioSession
                try AVAudioSession.sharedInstance().setActive(false)
                try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
                try AVAudioSession.sharedInstance().setActive(true)

                player?.play() // 9. resume play

            } catch let err as NSError {
                print(err.localizedDescription)
            }
        }
    }
}

@objc func appDidEnterBackground() {

    checkIfPlayerWasPlayingWhenAppEntersBackground() // 10. when the app is backgrounded run the code from step 1.
}

当然,当应用程序返回前台以暂停播放器时,我必须做同样的检查,检查.timeControlStatus观察器,然后切换回.mixWithOthers

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM