如何处理 AVPlayer .AVPlayerItemPlaybackStalled 通知

How to handle AVPlayer .AVPlayerItemPlaybackStalled Notification

我正在使用 AVPlayer,有时播放器会随机暂停。我正在观察 \.timeControlStatus,但我得到的唯一回应是 .paused。我也在观察 \.isPlaybackLikelyToKeepUp\.isPlaybackBufferEmpty\.isPlaybackBufferFull,但不会触发这些。但是使用 Notification.Name.AVPlayerItemPlaybackStalled 我确实得到了一个打印语句,上面写着“停滞”。

即使 \.rate 在玩家暂停时触发,但在 .AVPlayerItemPlaybackStalled 触发之前触发,有时 \.timeControlStatus .paused.AVPlayerItemPlaybackStalled 之后触发,玩家只是坐在那里。

.AVPlayerItemPlaybackStalled 运行且播放器暂停时我应该做什么?

NotificationCenter.default.addObserver(self, selector: #selector(self.playerItemPlaybackStalled(_:)),
                                               name: NSNotification.Name.AVPlayerItemPlaybackStalled,
                                               object: playerItem)

@objc fileprivate func playerItemPlaybackStalled(_ notification: Notification) {
    print("playerItemPlaybackStalled")
}

这是我的完整代码:

let player = AVPlayer()
player.automaticallyWaitsToMinimizeStalling = false
playerLayer = AVPlayerLayer(player: player)
playerLayer?.videoGravity = AVLayerVideoGravity.resizeAspect
// add KVO observers and NotificationCenter observers
// playerItem keys are already loaded
playerItem.preferredForwardBufferDuration = TimeInterval(1.0)
player.replaceCurrentItem(with: playerItem)


playerStatusObserver = player?.observe(\.currentItem?.status, options: [.new, .old]) { [weak self] (player, change) in
        
    switch (player.status) {
    case .readyToPlay:
        DispatchQueue.main.async {
            // play video
        }
    case .failed, .unknown:
        print("Video Failed to Play")
    @unknown default:
        break
    }
}
    
playerRateObserver = player?.observe(\.rate, options:  [.new, .old], changeHandler: { [weak self](player, change) in

    if player.rate == 1  {
        DispatchQueue.main.async {
            // if player isn't playing play it
        }
    } else {
        DispatchQueue.main.async {
           // is player is playing pause it
        }
    }
})
    
playerTimeControlStatusObserver = player?.observe(\.timeControlStatus, options: [.new, .old]) { [weak self](player, change) in
        
    switch (player.timeControlStatus) {
    case .playing:
            DispatchQueue.main.async { [weak self] in
                // if player isn't playing pay it
            }
    case .paused:
            print("timeControlStatus is paused") // *** SOMETIMES PRINTS after .AVPlayerItemPlaybackStalled runs***

    case .waitingToPlayAtSpecifiedRate:
            print("timeControlStatus- .waitingToPlayAtSpecifiedRate")

    if let reason = player.reasonForWaitingToPlay {
                
            switch reason {
            case .evaluatingBufferingRate:
                    print("timeControlStatus- .evaluatingBufferingRate") // never prints
                    
            case .toMinimizeStalls:
                    print("timeControlStatus- .toMinimizeStalls") // never prints
                    
            case .noItemToPlay:
                    print("timeControlStatus- .noItemToPlay") // never prints
                    
            default:
                    print("Unknown \(reason)")
            }
        }
    @unknown default:
        break
    }
}
    
playbackLikelyToKeepUpObserver = player?.currentItem?.observe(\.isPlaybackLikelyToKeepUp, options: [.old, .new]) {  (playerItem, change) in
    print("isPlaybackLikelyToKeepUp") // never prints
}
    
playbackBufferEmptyObserver = player?.currentItem?.observe(\.isPlaybackBufferEmpty, options: [.old, .new]) { (playerItem, change) in
    print("isPlaybackBufferEmpty") // never prints
}
    
playbackBufferFullObserver = player?.currentItem?.observe(\.isPlaybackBufferFull, options: [.old, .new]) { (playerItem, change) in
    print("isPlaybackBufferFull") // never prints
}
    
NotificationCenter.default.addObserver(self, selector: #selector(self.playerItemDidReachEnd(_:)),
                                               name: NSNotification.Name.AVPlayerItemDidPlayToEndTime,
                                               object: playerItem)

NotificationCenter.default.addObserver(self, selector: #selector(self.playerItemFailedToPlayToEndTime(_:)),
                                           name: .AVPlayerItemFailedToPlayToEndTime,
                                           object: playerItem)
    
    
NotificationCenter.default.addObserver(self, selector: #selector(playerItemNewError(_:)),
                                           name: .AVPlayerItemNewErrorLogEntry,
                                           object: playerItem)

NotificationCenter.default.addObserver(self, selector: #selector(self.playerItemPlaybackStalled(_:)),
                                           name: NSNotification.Name.AVPlayerItemPlaybackStalled,
                                           object: playerItem)

@objc func playerItemDidReachEnd(_ notification: Notification) {
    // show replay button
}

@objc func playerItemFailedToPlayToEndTime(_ notification: Notification) {
    print("playerItemFailedToPlayToEndTime") // never prints

    if let error = notification.userInfo?["AVPlayerItemFailedToPlayToEndTime"] as? Error {
        print(error.localizedDescription) // never prints
    }
}

@objc func playerItemNewError(_ notification: Notification) {
    print("playerItemNewErrorLogEntry") // never prints
}

@objc func playerItemPlaybackStalled(_ notification: Notification) {
    print("playerItemPlaybackStalled") // *** PRINTS ***
}

我找到了 answer here。基本上在停滞的通知中,我检查缓冲区是否已满。如果不是我 运行 来自 link 的代码。