How to handle AVPlayer .AVPlayerItemPlaybackStalled Notification

杀马特。学长 韩版系。学妹 提交于 2020-12-15 09:11:09

问题


I'm using AVPlayer and sometimes the player randomly pauses. I'm observing \.timeControlStatus but the only response I get .paused. I'm also observing \.isPlaybackLikelyToKeepUp, \.isPlaybackBufferEmpty, and \.isPlaybackBufferFull but nothing fires for those. However using Notification.Name.AVPlayerItemPlaybackStalled I do get a print statement that says "stalled".

Even though \.rate fires when the the player pauses, that fires before .AVPlayerItemPlaybackStalled fires, sometimes \.timeControlStatus .paused fires after .AVPlayerItemPlaybackStalled and the player just sits there.

What should I do once .AVPlayerItemPlaybackStalled runs and the player is sitting paused?

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

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

Here is my full code:

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

回答1:


I found the answer here. Basically inside the stalled notification I check to see if the buffer is full or not. If not I run the code from the link.



来源:https://stackoverflow.com/questions/62482826/how-to-handle-avplayer-avplayeritemplaybackstalled-notification

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!