AVPlayer.status 不 运行 包裹在带有延迟的 DispatchWorkItem 中

AVPlayer.status doesn't run when wrapped in a DispatchWorkItem with a Delay

因为我在单元格中播放视频,所以我有一个 AVPlayer 可以在某些情况下立即播放视频,而在其他情况下它会在几秒钟后播放。当它立即运行时 .status 工作正常。但是,当我将它包装在 DispatchWorkItem 中并带有 .asyncAfter 延迟时,永远不会调用完全相同的 .status 。我也尝试使用 perform(_:, with:, afterDelay:)Timer 但这也不起作用。

var player: AVPlayer?
var playerItem: AVPlayerItem?
var observer: NSKeyValueObservation? = nil
var workItem: DispatchWorkItem? = nil
var startImmediately = false
var timer: Timer?

viewDidLoad() {
    // add asset to playerItem, add playerItem to player ... 
}

func someCircumstance() {

    if startImmediately {

        setNSKeyValueObserver() // this works fine and .status is called

    } else {

        createWorkItem()

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.33, execute: executeWorkItem) // this delay runs but .status is never called

        // perform(#selector(executeWorkItem), with: nil, afterDelay: 0.33) // same issue

        // timer = Timer.scheduledTimer(timeInterval: 0.33, target: self, selector: #selector(executeWorkItem), userInfo: nil, repeats: false) // same issue
        // RunLoop.current.add(timer!, forMode: .common)
    }
}

func createWorkItem() {

    workItem = DispatchWorkItem {
        DispatchQueue.main.async { [weak self] in
            self?.setNSKeyValueObserver()
        }
    }
}

@objc func executeWorkItem() {

    guard let workItem = workItem else { return }

    workItem.perform()
}

func setNSKeyValueObserver() {

    // without a long explanation this sometimes has to start with a delay because of scrolling reason I also might have to cancel it
    observer = player?.observe(\.status, options: [.new, .old]) { [weak self] (player, change) in
                
        switch (player.status) {
        case .readyToPlay:
            print("Media Ready to Play")
                    
        case .failed, .unknown:
            print("Media Failed to Play")
        @unknown default:
            print("Unknown Error")
        }
    }
}

我也尝试使用较旧的 KVO API .status 观察器,但是在使用延迟时出现了同样的问题

private var keepUpContext = 0

viewDidLoad() {

    // add asset to playerItem, add playerItem to player ...
    player?.addObserver(self, forKeyPath: "currentItem.playbackLikelyToKeepUp", options: [.old, .new], context: &keepUpContext)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

    if context == &keepUpContext {

        if let player = player, let item = player.currentItem, item.isPlaybackLikelyToKeepUp {
            print("Media Ready to Play")
        }
    }
}

问题似乎是延迟。

更新

我刚刚尝试了以下代码,但它也不起作用:

if startImmediately {

    setNSKeyValueObserver()

} else {

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { [weak self] in
        self?.setNSKeyValueObserver()
    }
}

能否尝试将 forKeyPath 参数值更改为 #keyPath(AVPlayerItem.status)

您似乎是在播放器加载内容后添加观察者。它可能在 viewDidLoad 和延迟之间加载。如果在添加观察者时将 .initial 添加到选项列表,即使玩家已经准备好,您也一定会收到状态通知。