Skip to content

Commit

Permalink
Fallback to prefer extension renderers if decoder fails
Browse files Browse the repository at this point in the history
Necessary to support e.g. AAC-LTP on some devices, without unnecessarily impacting battery life for codecs supported by the hardware.
  • Loading branch information
Maxr1998 authored and nielsvanvelzen committed Nov 1, 2021
1 parent 8450736 commit 17376cc
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 1 deletion.
23 changes: 22 additions & 1 deletion app/src/main/java/org/jellyfin/mobile/player/PlayerViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import androidx.lifecycle.viewModelScope
import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.DefaultRenderersFactory
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.PlaybackException
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.analytics.AnalyticsCollector
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory
import com.google.android.exoplayer2.extractor.ts.TsExtractor
import com.google.android.exoplayer2.mediacodec.MediaCodecDecoderException
import com.google.android.exoplayer2.util.Clock
import com.google.android.exoplayer2.util.EventLogger
import kotlinx.coroutines.CoroutineScope
Expand Down Expand Up @@ -91,6 +93,7 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application),
addListener(eventLogger)
}
private val initialTracksSelected = AtomicBoolean(false)
private var fallbackPreferExtensionRenderers = false

private var progressUpdateJob: Job? = null

Expand Down Expand Up @@ -159,7 +162,12 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application),
*/
fun setupPlayer() {
val renderersFactory = DefaultRenderersFactory(getApplication()).apply {
setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON)
setEnableDecoderFallback(true) // Fallback only works if initialization fails, not decoding at playback time
val rendererMode = when {
fallbackPreferExtensionRenderers -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
else -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON
}
setExtensionRendererMode(rendererMode)
}
val extractorsFactory = DefaultExtractorsFactory().apply {
// https://github.com/google/ExoPlayer/issues/8571
Expand Down Expand Up @@ -450,6 +458,19 @@ class PlayerViewModel(application: Application) : AndroidViewModel(application),
}
}

override fun onPlayerError(error: PlaybackException) {
if (error.cause is MediaCodecDecoderException && !fallbackPreferExtensionRenderers) {
Timber.e(error.cause, "Decoder failed, attempting to restart playback with decoder extensions preferred")
playerOrNull?.run {
removeListener(this@PlayerViewModel)
release()
}
fallbackPreferExtensionRenderers = true
setupPlayer()
mediaQueueManager.tryRestartPlayback()
}
}

override fun onCleared() {
reportPlaybackStop()
ProcessLifecycleOwner.get().lifecycle.removeObserver(lifecycleObserver)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ class MediaQueueManager(
return null
}

fun tryRestartPlayback() {
_mediaQueue.value?.play()
}

@CheckResult
private fun createQueueItem(jellyfinMediaSource: JellyfinMediaSource, previous: QueueItem, next: QueueItem): QueueItem.Loaded {
val exoMediaSource = prepareStreams(jellyfinMediaSource)
Expand Down

0 comments on commit 17376cc

Please sign in to comment.