mirror of
https://github.com/tapframe/NuvioStreaming.git
synced 2026-05-18 16:01:44 +00:00
fix: decoder fallback handling in ExoPlayer to try app decoders if a decoder failure occurs
This commit is contained in:
parent
bcf1e65903
commit
3c61e0d39e
1 changed files with 47 additions and 2 deletions
|
|
@ -98,10 +98,28 @@ actual fun PlatformPlayerSurface(
|
||||||
val libassRenderType = runCatching {
|
val libassRenderType = runCatching {
|
||||||
LibassRenderType.valueOf(playerSettings.libassRenderType)
|
LibassRenderType.valueOf(playerSettings.libassRenderType)
|
||||||
}.getOrDefault(LibassRenderType.CUES)
|
}.getOrDefault(LibassRenderType.CUES)
|
||||||
|
val playerSourceKey = listOf(
|
||||||
|
sourceUrl,
|
||||||
|
sourceAudioUrl.orEmpty(),
|
||||||
|
sanitizedSourceHeaders,
|
||||||
|
sanitizedSourceResponseHeaders,
|
||||||
|
useYoutubeChunkedPlayback,
|
||||||
|
)
|
||||||
|
var decoderPriorityOverride by remember(playerSourceKey) { mutableStateOf<Int?>(null) }
|
||||||
|
var fallbackStartPositionMs by remember(playerSourceKey) { mutableStateOf<Long?>(null) }
|
||||||
|
val effectiveDecoderPriority = decoderPriorityOverride ?: playerSettings.decoderPriority
|
||||||
|
|
||||||
val exoPlayer = remember(sourceUrl, sourceAudioUrl, sanitizedSourceHeaders, sanitizedSourceResponseHeaders) {
|
val exoPlayer = remember(
|
||||||
|
sourceUrl,
|
||||||
|
sourceAudioUrl,
|
||||||
|
sanitizedSourceHeaders,
|
||||||
|
sanitizedSourceResponseHeaders,
|
||||||
|
useYoutubeChunkedPlayback,
|
||||||
|
effectiveDecoderPriority,
|
||||||
|
) {
|
||||||
val renderersFactory = DefaultRenderersFactory(context)
|
val renderersFactory = DefaultRenderersFactory(context)
|
||||||
.setExtensionRendererMode(playerSettings.decoderPriority)
|
.setExtensionRendererMode(effectiveDecoderPriority)
|
||||||
|
.setEnableDecoderFallback(true)
|
||||||
.setMapDV7ToHevc(playerSettings.mapDV7ToHevc)
|
.setMapDV7ToHevc(playerSettings.mapDV7ToHevc)
|
||||||
|
|
||||||
val trackSelector = DefaultTrackSelector(context).apply {
|
val trackSelector = DefaultTrackSelector(context).apply {
|
||||||
|
|
@ -169,6 +187,7 @@ actual fun PlatformPlayerSurface(
|
||||||
} else {
|
} else {
|
||||||
setMediaItem(MediaItem.fromUri(sourceUrl))
|
setMediaItem(MediaItem.fromUri(sourceUrl))
|
||||||
}
|
}
|
||||||
|
fallbackStartPositionMs?.let { seekTo(it.coerceAtLeast(0L)) }
|
||||||
prepare()
|
prepare()
|
||||||
this.playWhenReady = playWhenReady
|
this.playWhenReady = playWhenReady
|
||||||
}
|
}
|
||||||
|
|
@ -191,6 +210,21 @@ actual fun PlatformPlayerSurface(
|
||||||
val listener = object : Player.Listener {
|
val listener = object : Player.Listener {
|
||||||
override fun onPlayerError(error: PlaybackException) {
|
override fun onPlayerError(error: PlaybackException) {
|
||||||
syncPlayerViewKeepScreenOn()
|
syncPlayerViewKeepScreenOn()
|
||||||
|
if (
|
||||||
|
playerSettings.decoderPriority == DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON &&
|
||||||
|
effectiveDecoderPriority != DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER &&
|
||||||
|
error.isDecoderFailure()
|
||||||
|
) {
|
||||||
|
Log.w(
|
||||||
|
TAG,
|
||||||
|
"Decoder failure (${error.errorCodeName}); retrying with app decoders",
|
||||||
|
error,
|
||||||
|
)
|
||||||
|
fallbackStartPositionMs = exoPlayer.currentPosition.coerceAtLeast(0L)
|
||||||
|
decoderPriorityOverride = DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
|
||||||
|
latestOnError.value(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
latestOnError.value(error.localizedMessage ?: runBlocking { getString(Res.string.player_unable_to_play_stream) })
|
latestOnError.value(error.localizedMessage ?: runBlocking { getString(Res.string.player_unable_to_play_stream) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,6 +238,7 @@ actual fun PlatformPlayerSurface(
|
||||||
}
|
}
|
||||||
Log.d(TAG, "onPlaybackStateChanged: $stateName")
|
Log.d(TAG, "onPlaybackStateChanged: $stateName")
|
||||||
if (playbackState == Player.STATE_READY) {
|
if (playbackState == Player.STATE_READY) {
|
||||||
|
fallbackStartPositionMs = null
|
||||||
latestOnError.value(null)
|
latestOnError.value(null)
|
||||||
exoPlayer.logCurrentTracks("STATE_READY")
|
exoPlayer.logCurrentTracks("STATE_READY")
|
||||||
}
|
}
|
||||||
|
|
@ -484,6 +519,16 @@ private fun ExoPlayer.shouldKeepPlayerScreenOn(): Boolean =
|
||||||
playWhenReady &&
|
playWhenReady &&
|
||||||
playbackState in setOf(Player.STATE_BUFFERING, Player.STATE_READY)
|
playbackState in setOf(Player.STATE_BUFFERING, Player.STATE_READY)
|
||||||
|
|
||||||
|
private fun PlaybackException.isDecoderFailure(): Boolean =
|
||||||
|
errorCode in setOf(
|
||||||
|
PlaybackException.ERROR_CODE_DECODER_INIT_FAILED,
|
||||||
|
PlaybackException.ERROR_CODE_DECODER_QUERY_FAILED,
|
||||||
|
PlaybackException.ERROR_CODE_DECODING_FAILED,
|
||||||
|
PlaybackException.ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES,
|
||||||
|
PlaybackException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED,
|
||||||
|
PlaybackException.ERROR_CODE_DECODING_RESOURCES_RECLAIMED,
|
||||||
|
)
|
||||||
|
|
||||||
private fun PlayerResizeMode.toExoResizeMode(): Int =
|
private fun PlayerResizeMode.toExoResizeMode(): Int =
|
||||||
when (this) {
|
when (this) {
|
||||||
PlayerResizeMode.Fit -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
PlayerResizeMode.Fit -> AspectRatioFrameLayout.RESIZE_MODE_FIT
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue