Как обрабатывать уведомление AVPlayer .AVPlayerItemPlaybackStalled - PullRequest
0 голосов
/ 20 июня 2020

Я использую 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 ***
}

1 Ответ

0 голосов
/ 22 июня 2020

Я нашел ответ здесь . В основном внутри зависшего уведомления я проверяю, заполнен ли буфер или нет. Если нет, я запускаю код по ссылке.

...