Edited at

How to handle error which was detected by AVPlayer that get video content from HLS

More than 1 year has passed since last update.

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 and AVPlayerItem.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 and AVPlayerItem.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.


References