Retry or fallback logic for Segment not found and other errors - devrath/MediaAlchemySuite GitHub Wiki
Brief description
- Define your logic for
DefaultLoadErrorHandlingPolicy by overriding it
- We can intercept certain events when certain errors occur during the playback and react in a specific way defined in the UI.
- Say, for a particular error, we can show a specific UI, and we can also log anything if needed in analytics.
- With this, we can provide more robust and user-friendly behaviour.
Why it's used
- Say, when you are playing the player, and suddenly you lose connectivity. Now, instead of loading indefinitely we can show a user-friendly message
no-connectivity
- Smarter retry logic. We will be able to distinguish the type of errors based on the error code we receive. Say for
503, which is service unavailable, it makes sense to retry, but for `404 ', which is a file not found, where exponential backoff is applied for each retry scenario.
- We can define a fallback mechanism where we can switch to a lower resolution or switch the source to a different one if needed.
code
@UnstableApi
class CustomLoadErrorHandlingPolicy(
private val errorHandler: PlaybackErrorHandler
) : DefaultLoadErrorHandlingPolicy() {
override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorHandlingPolicy.LoadErrorInfo): Long {
when (val exception = loadErrorInfo.exception) {
is HttpDataSource.InvalidResponseCodeException -> {
val responseCode = exception.responseCode
return when (responseCode) {
404 -> retryWithBackoff(loadErrorInfo)
in 500..599 -> retryWithBackoff(loadErrorInfo)
else -> triggerFallback()
}
}
is HttpDataSource.HttpDataSourceException -> {
if (isNetworkUnavailable(exception.cause)) {
Log.e(APP_TAG, "No connectivity detected.")
return noNetworkConnectivity()
}
return retryWithBackoff(loadErrorInfo)
}
else -> {
if (isNetworkUnavailable(exception)) {
Log.e(APP_TAG, "No internet connection.")
return noNetworkConnectivity()
}
return retryWithBackoff(loadErrorInfo)
}
}
}
override fun getMinimumLoadableRetryCount(dataType: Int): Int = 3
private fun retryWithBackoff(loadErrorInfo: LoadErrorHandlingPolicy.LoadErrorInfo): Long {
val delay = when (loadErrorInfo.errorCount) {
0 -> 1000L
1 -> 2000L
2 -> 4000L
else -> return triggerFallback()
}
Log.w(APP_TAG, "Retrying load. Attempt=${loadErrorInfo.errorCount + 1}, delay=${delay}ms")
return delay
}
private fun noNetworkConnectivity(): Long {
errorHandler.noConnectivity?.invoke()
return C.TIME_UNSET
}
private fun triggerFallback(): Long {
Log.e(APP_TAG, "Triggering fallback after max retries or unrecoverable error.")
errorHandler.onMaxRetryReached?.invoke()
return C.TIME_UNSET
}
private fun isNetworkUnavailable(exception: Throwable?): Boolean {
return exception is java.net.UnknownHostException ||
exception is java.net.SocketTimeoutException ||
exception is java.net.ConnectException ||
exception is java.net.NoRouteToHostException
}
}