Hi there. How are you doing?
I'm hiroakit that is iOS/tvOS Engineer.
This letter introduces you to the basic error handling of AVPlayer.
Introduction
AVPlayer will provide a video experience to your end-user.
And it may get some error from server interaction.
So it give us to issues below:
- What kind of errors do AVPlayer catch?
- What should app do to handle error which was catched by AVPlayer?
Then I looked up these issues on Apple's documents.
What kind of errors do AVPlayer catch?
AVPlayer will get these errors:
- AVFoundationErrorDomain
- CoreMediaErrorDomain
AVFoundationErrorDomain
AVPlayer will get AVFoundationErrorDomain errors when failures occur.
via: https://developer.apple.com/videos/play/wwdc2017/514/?time=875
Type | Description | AVError |
---|---|---|
Network errors | HTTP 4xx errors, HTTP 5xx errors, TCP/IP, DNS errors | AVErrorContentIsUnavailable, AVErrorNoLongerPlayable |
Timeouts | Master playlist, media playlist, media file, keys timeout | AVErrorContentIsUnavailable, AVErrorNoLongerPlayable |
Format errors | Playlist format error, Key format error, Session data format error | AVErrorFailedToParse |
Live playlist update errors | Must update live playlist in time as per HLS Spec | AVErrorContentNotUpdated |
The above AVFoundationErrorDomain error has assigned error code by Apple.
It is below:
Error | Code |
---|---|
AVErrorContentIsUnavailable | -11863 |
AVErrorNoLongerPlayable | -11867 |
AVErrorFailedToParse | -11853 |
AVErrorContentNotUpdated | -11866 |
Example is here.
Error Domain=AVFoundationErrorDomain Code=-11867 "See -[AVPlayerItem errorLog] for 2 events" UserInfo={NSLocalizedDescription=Playback Stopped, NSUnderlyingError=0x60c000444b90 {Error Domain=CoreMediaErrorDomain Code=-12880 "Can not proceed after removing variants" UserInfo={NSDescription=Can not proceed after removing variants}}, NSDebugDescription=See -[AVPlayerItem errorLog] for 2 events, NSLocalizedFailureReason=Could not download required resources.})
AVPlayer will get to detect these errors at the start of playback.
If you handle AVFoundationErrorDomain errors, Your app should observe AVPlayer.status
and AVPlayer.currentItem.status
CoreMediaErrorDomain
Your app receive errors from AVPlayerItemNewErrorLogEntry notification.
The notification has CoreMediaErrorDomain error.
Example is here.
Error Domain=CoreMediaErrorDomain Code=-12642 "Playlist parse error" (See -[AVPlayerItem errorLog] for 2 events) UserInfo={NSDescription=Playlist parse error, NSDebugDescription=See -[AVPlayerItem errorLog] for 2 events})
AVPlayer will catch CoreMediaErrorDomain errors while playback video.
Expected Failures and Error Codes
AVPlayer expect these error code when a failure occurs on HTTP.
via: https://developer.apple.com/videos/play/wwdc2017/514/?time=218
Failure | Error Code |
---|---|
Authentication failures | 401 Unauthorized |
Client doesn’t have permissions | 403 Forbidden |
Resource unavailable but may become available in the future | 404 Not Found |
Resource unavailable and will never become available in the future | 410 Gone |
Internal server errors | 500 Internal Server Error |
Invalid response from gateway | 502 Bad Gateway |
Server unavailable | 503 Service Unavailable |
Gateway timeouts | 504 Gateway Time-Out |
When AVPlayer get these error codes, it exec the recovery below:
- 401, 403, 404: Retry
- 410, 500, 502, 503, 504: Switch other variants.
iOS 11 catch temporary resource/server unavailability with EXT-X-GAP tag in Playlist.
It is unverified I wish to Write this feature on next letter.
What should app do to handle error which was catched by AVPlayer?
Get error via these methods below:
- Watch
AVPlayer.status
andAVPlayerItem.status
- Listen to these notifications
NSNotification.Name.AVPlayerItemFailedToPlayToEndTime
NSNotification.Name.AVPlayerItemNewErrorLogEntry
Example is here.
func foo() {
// Get AVPlayerItem
let url: URL // something url
let asset = AVURLAsset(URL: url)
let item = AVPlayerItem(asset: asset)
// Get AVPlayer
self.player = AVPlayer(playerItem: item)
// Add observer for AVPlayer status and AVPlayerItem status
self.player.addObserver(self, forKeyPath: #keyPath(AVPlayer.status), options: [.new, .initial], context: nil)
self.player.addObserver(self, forKeyPath: #keyPath(AVPlayer.currentItem.status), options:[.new, .initial], context: nil)
// Watch notifications
let center = NotificationCenter.default
center.addObserver(self, selector:"newErrorLogEntry:", name: .AVPlayerItemNewErrorLogEntry, object: player.currentItem)
center.addObserver(self, selector:"failedToPlayToEndTime:", name: .AVPlayerItemFailedToPlayToEndTime, object: player.currentItem)
}
// Observe If AVPlayerItem.status Changed to Fail
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if let player: AVPlayer = object as? AVPlayer && keyPath == #keyPath(AVPlayer.currentItem.status) {
let newStatus: AVPlayerItemStatus
if let newStatusAsNumber = change?[NSKeyValueChangeKey.newKey] as? NSNumber {
newStatus = AVPlayerItemStatus(rawValue: newStatusAsNumber.intValue)!
} else {
newStatus = .unknown
}
if newStatus == .failed {
NSLog("Error: \(String(describing: self.player?.currentItem?.error?.localizedDescription)), error: \(String(describing: self.player?.currentItem?.error))")
}
}
}
// Getting error from Notification payload
func newErrorLogEntry(_ notification: Notification) {
guard let object = notification.object, let playerItem = object as? AVPlayerItem else {
return
}
guard let errorLog: AVPlayerItemErrorLog = playerItem.errorLog() else {
return
}
NSLog("Error: \(errorLog)")
}
func failedToPlayToEndTime(_ notification: Notification) {
let error = notification.userInfo![”AVPlayerItemFailedToPlayToEndTimeErrorKey”]
NSLog("Error: \(error?.localizedDescription), error: \(error)")
}
Conclusion
In this letter, I described how to handle errors by the approach below:
- Watch
AVPlayer.status
andAVPlayerItem.status
- Listen to these notifications
NSNotification.Name.AVPlayerItemFailedToPlayToEndTime
NSNotification.Name.AVPlayerItemNewErrorLogEntry
Perhaps we can get how to reduce errors or more good error handling by taking part in the discussion from the server side (e.g, EXT-X-GAP, Fail. So error handling is debatable. It will take good for client side too.
Finally, I think that i/we can still improve the video user experience, so any ideas are welcome. Then we can do better together.