I have an app that plays back videos. It's compatible with iOS 11, 12 and iOS 13. On iOS 11 and 12, video playback works properly as expected using either AVPlayerViewController
or even just AVPlayerLayer
.
However, on iOS 13 I started getting reports that suddenly video wasn't loading (or would only load audio, or only the first frame) for quite a few users when they updated iOS. I had a really hard time replicating it, but some mentioned it occurred mostly with poor network connections, and sure enough with Network Link Conditioner I was able to reproduce it.
It specifically affects HLS video (the fancy livestream compatible one that Reddit uses, for instance). It continues to work fine with MP4. Here's an example URL that fails: https://v.redd.it/gl3chx2kd4v31/HLSPlaylist.m3u8
Here's a Network Link Conditioner profile that triggers it: https://i.imgur.com/XWsKUeM.jpg
Here's a sample project that triggers it, showing both AVPlayerViewController and AVPlayer (hit Download, Google is being weird): https://drive.google.com/file/d/1RS5DvUypdOLFCYJe1Pt2Ig0fQljZDLs2/view
Here's sample code showing it off with AVPlayerViewController:
let assetURL = URL(string: "https://v.redd.it/gl3chx2kd4v31/HLSPlaylist.m3u8")!
// The following MP4 URL *does* work, for instance
// let assetURL = URL(string: "https://giant.gfycat.com/DependentFreshKissingbug.mp4")!
let player = AVPlayer(url: assetURL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
If I try that exact same code on an iOS 12 device instead it works perfectly.
Does anyone have any suggestions on how to fix it? If you scrub back to the beginning sometimes you can get the video to play back properly, but not reliably enough so to seemingly build a solution off that. Video stuff definitely isn't my forte in iOS development so I'm starting to scratch my head a little, any help would be super appreciated.
Note: I'm quite aware this is (likely) an iOS bug and I will file a Radar, but I still have to deal with it now.
You need to kvo on the playerItems status in order to know when it is ready to play. Then in the playerItem status change to ready to play call your player.play.
playerItem.addObserver(self,
forKeyPath: #keyPath(AVPlayerItem.status),
options: [.old, .new],
context: &playerItemContext)
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
// Only handle observations for the playerItemContext
guard context == &playerItemContext else {
super.observeValue(forKeyPath: keyPath,
of: object,
change: change,
context: context)
return
}
if keyPath == #keyPath(AVPlayerItem.status) {
let status: AVPlayerItemStatus
if let statusNumber = change?[.newKey] as? NSNumber {
status = AVPlayerItemStatus(rawValue: statusNumber.intValue)!
} else {
status = .unknown
}
// Switch over status value
switch status {
case .readyToPlay:
// Player item is ready to play.
case .failed:
// Player item failed. See error.
case .unknown:
// Player item is not yet ready.
}
}
}
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