I use AVPlayer to play an audio livestream via internet. I like to recover playback if it was paused for longer than 1 minute.
I call player.rate = 1.0
to resume. However if the stream was paused for >1 minute it does not play any more. I need to recreate AVPlayerItem in this case to make it work again.
So how can I catch this case, so I know the playback did not recover?
player.rate
. It stays at 1.0
though. The player is not playing!currentItem.playbackBufferEmpty
. It is not called in this case though.currentItem.status
does not switch to .Failed
. It does not change at all.The AVPlayer just seems to do nothing in this case. Any ideas?
I build a Playground code to demonstrate the issue:
import UIKit
import AVFoundation
// keep it running forever so it plays audio
import XCPlayground
XCPSetExecutionShouldContinueIndefinitely(true)
class AVPlayerTest {
let player = AVPlayer()
let streamurl = NSURL(string: "http://detektor.fm/stream/mp3/musik/")!
func startTest() {
let item = AVPlayerItem(URL: streamurl)
player.replaceCurrentItemWithPlayerItem(item)
player.play()
// give it some start time to build a buffer
NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: #selector(timerTickedToPause), userInfo: nil, repeats: false)
}
@objc func timerTickedToPause(timer: NSTimer) {
player.pause()
// pause now for some time. 90s is not enough.
NSTimer.scheduledTimerWithTimeInterval(120, target: self, selector: #selector(timerTickedToPlay), userInfo: nil, repeats: false)
}
@objc func timerTickedToPlay(timer: NSTimer) {
// try to resume playback
print(player.rate)
player.play()
print(player.rate)
// check in some seconds if it recovered
NSTimer.scheduledTimerWithTimeInterval(10, target: self, selector: #selector(timerTickedCheck), userInfo: nil, repeats: false)
}
@objc func timerTickedCheck(timer: NSTimer) {
// it reports rate = 1.0 but is not playing here though!
// there is no way to know for me it did not recover here!?
print(player.rate)
// recover by creating a new item works
let item = AVPlayerItem(URL: streamurl)
player.replaceCurrentItemWithPlayerItem(item)
player.play()
}
}
let test = AVPlayerTest()
test.startTest()
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)")
}
}
}
I have the same issue but in my case, it occurs if I go into background while in the paused state. When I come back to forground .play() does not work. 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