问题
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