After .pause() if I call .play() ok it continues but if I wait 30-60 sec after .pause() and try to .play() it sometimes fail to play,
AVPlayerStatus.Failed returns false
AVPlayerStatus.ReadyToPlay returns true
I should re-initialize player with url to make it work. Now I want to do that, if player can be played, I want to just call .play() but if not, I want to re-initialize it, my question is how to detect if player is playable? By the way it is a radio link with .pls extension
Indeed theres no indication that when resuming after a large period of time the player is in limbo. (Except that in my tests I saw that in this situation the meta received from AVPlayerItem is null)
Anyway.. From what I've gathered from the internet (there's no proper documentation on this), when you pause, the player will keep buffering in the background and .. if you try to resume after 50-60 seconds it just cannot. A stop function would be good here.
My solution: A simple timer to check if that 50 seconds passed and if so update a flag to know that when the resume method is called I want to start on a new player.
func pausePlayer() {
..
player.pause()
..
// Will count to default 50 seconds or the indicated interval and only then set the bufferedInExcess flag to true
startCountingPlayerBufferingSeconds()
bufferedInExcess = false
}
func startCountingPlayerBufferingSeconds(interval: Double = 50) {
timer = NSTimer.scheduledTimerWithTimeInterval(interval, target: self, selector: Selector("setExcessiveBufferedFlag"), userInfo: nil, repeats: false)
}
func setExcessiveBufferedFlag() {
if DEBUG_LOG {
print("Maximum player buffering interval reached.")
}
bufferedInExcess = true
}
func stopCountingPlayerBufferingSeconds() {
timer.invalidate()
}
func resumePlayer() {
if haveConnectivity() {
if (.. || bufferedInExcess) {
startPlaying(true)
} else {
..
player.play
}
..
}
}
func startPlaying(withNewPlayer: Bool = false) {
if (withNewPlayer) {
if DEBUG_LOG {
print("Starting to play on a fresh new player")
}
// If we need another player is very important to fist remove any observers for
// the current AVPlayer/AVPlayerItem before reinitializing them and add again the needed observers
initPlayer()
player.play()
...
}
...
}
Assuming you have checked the AVPlayerItem.status == .playable before issuing .play()
Then use the timeControlStatus of the AVPlayer instance which "indicates whether playback is currently in progress, paused indefinitely, or suspended while waiting for appropriate network conditions."
let status = player.timeControlStatus
switch status {
case .paused:
print("timeControlStatus.paused")
case .playing:
print("timeControlStatus.playing")
case .waitingToPlayAtSpecifiedRate:
print("timeControlStatus.waitingToPlayAtSpecifiedRate")
if let reason = player.reasonForWaitingToPlay {
switch reason {
case .evaluatingBufferingRate:
print("reasonForWaitingToPlay.evaluatingBufferingRate")
case .toMinimizeStalls:
print("reasonForWaitingToPlay.toMinimizeStalls")
case .noItemToPlay:
print("reasonForWaitingToPlay.noItemToPlay")
default:
print("Unknown \(reason)")
}
}
}
In my case it gets stuck the waitingToPlayAtSpecifiedRate.evaluatingBufferingRate mode. At that point the AVPlayerItem.status instance is readToPlay.
At this time of writing whenever a startCommand is received I reset the AVplayer to be sure. But this seems clunky. Looking for a smoother solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With