如何处理 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 通知的主要内容,如果未能解决你的问题,请参考以下文章
在像 instagram 这样的 tableview 中使用多个 AVPlayer