当包裹在具有延迟的 DispatchWorkItem 中时,AVPlayer.status 不会运行

问题描述

因为我在单元格中播放视频,所以我有一个 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()
    }
}

解决方法

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

,

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