Skip to content

Commit

Permalink
Merge pull request #49 from scribd/kabliz/APT-10467-retry
Browse files Browse the repository at this point in the history
[APT-10467] Retry with Exponential Backoff
  • Loading branch information
kabliz authored Sep 23, 2024
2 parents 4e07c3a + bfa6087 commit 428dd66
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.scribd.armadillo.actions.LicenseDrmErrorAction
import com.scribd.armadillo.actions.LicenseExpirationDetermined
import com.scribd.armadillo.actions.LicenseExpiredAction
import com.scribd.armadillo.actions.LicenseKeyIsUsableAction
import com.scribd.armadillo.playback.error.ArmadilloHttpErrorHandlingPolicy
import com.scribd.armadillo.time.milliseconds
import java.util.UUID
import javax.inject.Inject
Expand Down Expand Up @@ -67,7 +68,7 @@ internal class ArmadilloDrmSessionManagerProvider @Inject constructor(private va
}
}

/** Near identical to DefaultDrmSessionManagerProvider method, except for the indicated line */
/** Near identical to DefaultDrmSessionManagerProvider method, except for the indicated lines */
private fun createManager(drmConfiguration: DrmConfiguration): DrmSessionManager {
val dataSourceFactory = this.drmHttpDataSourceFactory
?: DefaultHttpDataSource.Factory().setUserAgent(this.userAgent)
Expand All @@ -84,6 +85,8 @@ internal class ArmadilloDrmSessionManagerProvider @Inject constructor(private va
.setMultiSession(drmConfiguration.multiSession)
.setPlayClearSamplesWithoutKeys(drmConfiguration.playClearContentWithoutKey)
.setUseDrmSessionsForClearContent(*Ints.toArray(drmConfiguration.forcedSessionTrackTypes))
//this line is also different, adding custom error handling
.setLoadErrorHandlingPolicy(ArmadilloHttpErrorHandlingPolicy())
.build(httpDrmCallback)
drmSessionManager.setMode(DefaultDrmSessionManager.MODE_PLAYBACK, drmConfiguration.keySetId)
return drmSessionManager
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.scribd.armadillo.playback.error

import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.ParserException
import com.google.android.exoplayer2.upstream.DefaultLoadErrorHandlingPolicy
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy
import java.net.SocketTimeoutException
import java.net.UnknownHostException

class ArmadilloHttpErrorHandlingPolicy : DefaultLoadErrorHandlingPolicy(DEFAULT_MIN_LOADABLE_RETRY_COUNT) {
override fun getRetryDelayMsFor(loadErrorInfo: LoadErrorHandlingPolicy.LoadErrorInfo): Long {
return if (loadErrorInfo.exception is UnknownHostException || loadErrorInfo.exception is SocketTimeoutException) {
//retry every 10 seconds for potential loss of internet -keep buffering - internet may later succeed.
if (loadErrorInfo.errorCount > 6) {
C.TIME_UNSET //stop retrying after around 10 minutes
} else {
//exponential backoff based on a 10 second interval
(1 shl (loadErrorInfo.errorCount - 1)) * 10 * 1000L
}
} else if (loadErrorInfo.exception is ParserException) {
/*
Exoplayer by default assumes ParserExceptions only occur because source content is malformed,
so Exoplayer will never retry ParserExceptions.
We care about content failing to checksum correctly over the internet, so we wish to retry these.
*/
if (loadErrorInfo.errorCount > 3) {
C.TIME_UNSET //stop retrying, the content is likely malformed
} else {
//This is exponential backoff based on a 1 second interval
(1 shl (loadErrorInfo.errorCount - 1)) * 1000L
}
} else {
super.getRetryDelayMsFor(loadErrorInfo)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.scribd.armadillo.download.DownloadTracker
import com.scribd.armadillo.download.drm.events.WidevineSessionEventListener
import com.scribd.armadillo.models.AudioPlayable
import com.scribd.armadillo.models.DrmType
import com.scribd.armadillo.playback.error.ArmadilloHttpErrorHandlingPolicy
import javax.inject.Inject

/** For playback, both streaming and downloaded */
Expand Down Expand Up @@ -55,6 +56,7 @@ internal class DashMediaSourceGenerator @Inject constructor(
DownloadHelper.createMediaSource(download!!.request, dataSourceFactory, drmManager)
} else {
var factory = DashMediaSource.Factory(dataSourceFactory)
.setLoadErrorHandlingPolicy(ArmadilloHttpErrorHandlingPolicy())
if (request.drmInfo != null) {
factory = factory.setDrmSessionManagerProvider(drmSessionManagerProvider)
}
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ Welcome to Project Armadillo, a delightful audio player for Android.

## What is Armadillo?

Armadillo is a fully featured audio player. Armadillo leverages [Google's Exoplayer](https://github.com/google/ExoPlayer/) library for its audio engine. Exoplayer wraps a variety of low level audio and video apis but has few opinions of its own for actually using audio in an Android app. The leap required from Exoplayer to audio player is enormous both in terms of the amount of code needed as well as the amount of domain knowledge required about complex audio related subjects. Armadillo provides a turn key solution for powering an audio player and providing the information to update a UI.
Armadillo is a fully featured audio player. Armadillo leverages [Google's Exoplayer](https://github.com/google/ExoPlayer/) library for
its audio engine. Exoplayer wraps a variety of low level audio and video apis but has few opinions of its own for actually using
internet based audio in an Android app. The leap required from Exoplayer to audio player is enormous both in terms of the amount of code
needed as well as the amount of domain knowledge required about complex audio related subjects. Armadillo provides a turn key solution for powering an audio player and providing the information to update a UI.

- **Easy-to-use** because it outputs state updates with everything needed for a UI or analytics. Works in the background state.
- **Effective** because it uses [Google's Exoplayer](https://github.com/google/ExoPlayer/) as the playback engine.
Expand Down
4 changes: 4 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Project Armadillo Release Notes

## 1.6.6
- Adds graceful exponential backoff toward internet connectivity errors.
- Retries streaming parsing exceptions, in case the network has not succeeded in retreiving valid data.

## 1.6.5
- ArmadilloPlayer handles client calls from any thread appropriately, without blocking. For those recently updating since 1.5.1, this
should resolve any strange bugs from client behavior.
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ org.gradle.jvmargs=-Xmx1536m
# org.gradle.parallel=true
PACKAGE_NAME=com.scribd.armadillo
GRADLE_PLUGIN_VERSION=7.2.0
LIBRARY_VERSION=1.6.5
LIBRARY_VERSION=1.6.6
EXOPLAYER_VERSION=2.19.1
RXJAVA_VERSION=2.2.4
RXANDROID_VERSION=2.0.1
Expand Down

0 comments on commit 428dd66

Please sign in to comment.