如何处理 AVPlayer .AVPlayerItemPlaybackStalled 通知

Posted

技术标签:

【中文标题】如何处理 AVPlayer .AVPlayerItemPlaybackStalled 通知【英文标题】:How to handle AVPlayer .AVPlayerItemPlaybackStalled Notification 【发布时间】:2020-06-20 07:16:32 【问题描述】:

我正在使用 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 ***

【问题讨论】:

@matt btw 感谢您的建议,我会设置一个活动指示器并且不会触摸播放器。 @matt 它一直在发生,问题是之后什么都没有发生,这意味着没有其他观察者或通知被调用。视频只有 15 秒,大约 5 秒后冻结,我让它静置 5 分钟,期待会发生一些事情,但 nada。如果它试图填充缓冲区,它不应该在 5 分钟内被填充并且其他方法之一被触发吗? 好吧,我想我错了。我不知道为什么视频会在五秒钟后停止,可能需要更多信息。 另外我不太明白你为什么将automaticallyWaitsToMinimizeStalling 设置为false,这样就没有达到目的。 我添加了 automaticallyWaitsToMinimizeStalling = false 想也许它会帮助它玩得更快。你是什​​么意思它违背了目的?我会注释掉它,看看是否仍然出现失速 【参考方案1】:

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

【讨论】:

以上是关于如何处理 AVPlayer .AVPlayerItemPlaybackStalled 通知的主要内容,如果未能解决你的问题,请参考以下文章

来电后AVplayer恢复

如何正确处理 AVPlayer HTTP 错误?

AVPlayer 播放错误的视频文件

在像 instagram 这样的 tableview 中使用多个 AVPlayer

m3u8 音频流在 MPMoviePlayerController 中播放但不是 AVPlayer?

c# FileSystemWatcher如何处理多个文件?