From 5563935a514888f9ea4cdbe0072d1acd0b36ed42 Mon Sep 17 00:00:00 2001 From: Damontecres Date: Wed, 5 Nov 2025 16:04:10 -0500 Subject: [PATCH] Add settings to switch player backends --- .../wholphin/preferences/AppPreference.kt | 26 ++++++++ .../preferences/AppPreferencesSerializer.kt | 14 +++++ .../wholphin/ui/playback/PlaybackOverlay.kt | 6 ++ .../wholphin/ui/playback/PlaybackViewModel.kt | 59 ++++++++++++------- .../wholphin/util/mpv/MpvPlayer.kt | 4 +- app/src/main/proto/WholphinDataStore.proto | 11 ++++ app/src/main/res/values/preferences.xml | 5 ++ app/src/main/res/values/strings.xml | 3 + 8 files changed, 106 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt index aa609f40..3541633d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt @@ -654,6 +654,30 @@ sealed interface AppPreference { summaryOn = R.string.enabled, summaryOff = R.string.disabled, ) + + val PlayerBackendPref = + AppChoicePreference( + title = R.string.player_backend, + defaultValue = PlayerBackend.EXO_PLAYER, + getter = { it.playbackPreferences.playerBackend }, + setter = { prefs, value -> + prefs.updatePlaybackPreferences { playerBackend = value } + }, + displayValues = R.array.player_backend_options, + indexToValue = { PlayerBackend.forNumber(it) }, + valueToIndex = { it.number }, + ) + + val MpvHardwareDecoding = + AppSwitchPreference( + title = R.string.mpv_hardware_decoding, + defaultValue = true, + getter = { it.playbackPreferences.mpvOptions.enableHardwareDecoding }, + setter = { prefs, value -> + prefs.updateMpvOptions { enableHardwareDecoding = value } + }, + summary = R.string.disable_if_crash, + ) } } @@ -739,6 +763,8 @@ val advancedPreferences = AppPreference.SkipRecaps, AppPreference.MaxBitrate, AppPreference.PlaybackDebugInfo, + AppPreference.PlayerBackendPref, + AppPreference.MpvHardwareDecoding, ), ), PreferenceGroup( diff --git a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreferencesSerializer.kt b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreferencesSerializer.kt index b76b6423..f5f423c1 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreferencesSerializer.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreferencesSerializer.kt @@ -44,6 +44,7 @@ class AppPreferencesSerializer passOutProtectionMs = AppPreference.PassOutProtection.defaultValue.hours.inWholeMilliseconds showNextUpWhen = AppPreference.ShowNextUpTiming.defaultValue + playerBackend = AppPreference.PlayerBackendPref.defaultValue overrides = PlaybackOverrides @@ -56,6 +57,14 @@ class AppPreferencesSerializer mediaExtensionsEnabled = AppPreference.FfmpegPreference.defaultValue }.build() + + mpvOptions = + MpvOptions + .newBuilder() + .apply { + enableHardwareDecoding = + AppPreference.MpvHardwareDecoding.defaultValue + }.build() }.build() homePagePreferences = HomePagePreferences @@ -103,6 +112,11 @@ inline fun AppPreferences.updatePlaybackOverrides(block: PlaybackOverrides.Build overrides = overrides.toBuilder().apply(block).build() } +inline fun AppPreferences.updateMpvOptions(block: MpvOptions.Builder.() -> Unit): AppPreferences = + updatePlaybackPreferences { + mpvOptions = mpvOptions.toBuilder().apply(block).build() + } + inline fun AppPreferences.updateHomePagePreferences(block: HomePagePreferences.Builder.() -> Unit): AppPreferences = update { homePagePreferences = homePagePreferences.toBuilder().apply(block).build() diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackOverlay.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackOverlay.kt index 9d050b02..b96c613a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackOverlay.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackOverlay.kt @@ -390,6 +390,12 @@ fun PlaybackOverlay( .padding(16.dp) .background(AppColors.TransparentBlack50), ) { + Text( + text = "Backend: ${currentPlayback?.backend}", + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier.padding(start = 8.dp), + ) Text( text = "Play method: ${currentPlayback?.playMethod?.serialName}", style = MaterialTheme.typography.bodySmall, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt index ba746632..7d25e7aa 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt @@ -18,6 +18,7 @@ import androidx.media3.common.TrackSelectionOverride import androidx.media3.common.Tracks import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.DefaultRenderersFactory +import androidx.media3.exoplayer.ExoPlayer import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.ItemPlaybackDao import com.github.damontecres.wholphin.data.ItemPlaybackRepository @@ -33,6 +34,7 @@ import com.github.damontecres.wholphin.data.model.chooseStream import com.github.damontecres.wholphin.preferences.AppPreference import com.github.damontecres.wholphin.preferences.AppPreferences import com.github.damontecres.wholphin.preferences.MediaExtensionStatus +import com.github.damontecres.wholphin.preferences.PlayerBackend import com.github.damontecres.wholphin.preferences.ShowNextUpWhen import com.github.damontecres.wholphin.preferences.SkipSegmentBehavior import com.github.damontecres.wholphin.preferences.UserPreferences @@ -121,28 +123,41 @@ class PlaybackViewModel ) : ViewModel(), Player.Listener { val player by lazy { - val extensions = - runBlocking { appPreferences.data.firstOrNull() }?.playbackPreferences?.overrides?.mediaExtensionsEnabled - Timber.v("extensions=$extensions") - val rendererMode = - when (extensions) { - MediaExtensionStatus.MES_FALLBACK -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON - MediaExtensionStatus.MES_PREFERRED -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER - MediaExtensionStatus.MES_DISABLED -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF - else -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON + val prefs = runBlocking { appPreferences.data.firstOrNull()?.playbackPreferences } + + val backend = prefs?.playerBackend ?: AppPreference.PlayerBackendPref.defaultValue + when (backend) { + PlayerBackend.MPV -> { + val enableHardwareDecoding = + prefs?.mpvOptions?.enableHardwareDecoding + ?: AppPreference.MpvHardwareDecoding.defaultValue + MpvPlayer(context, enableHardwareDecoding) + .apply { + playWhenReady = true + } } -// ExoPlayer -// .Builder(context) -// .setRenderersFactory( -// DefaultRenderersFactory(context) -// .setEnableDecoderFallback(true) -// .setExtensionRendererMode(rendererMode), -// ).build() - // TODO add settings for enabling HW decoding - MpvPlayer(context, false) - .apply { - playWhenReady = true + + PlayerBackend.EXO_PLAYER, + PlayerBackend.UNRECOGNIZED, + -> { + val extensions = prefs?.overrides?.mediaExtensionsEnabled + Timber.v("extensions=$extensions") + val rendererMode = + when (extensions) { + MediaExtensionStatus.MES_FALLBACK -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON + MediaExtensionStatus.MES_PREFERRED -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER + MediaExtensionStatus.MES_DISABLED -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF + else -> DefaultRenderersFactory.EXTENSION_RENDERER_MODE_ON + } + ExoPlayer + .Builder(context) + .setRenderersFactory( + DefaultRenderersFactory(context) + .setEnableDecoderFallback(true) + .setExtensionRendererMode(rendererMode), + ).build() } + } } val loading = MutableLiveData(LoadingState.Loading) @@ -518,7 +533,8 @@ class PlaybackViewModel val playback = CurrentPlayback( - listOf(), + backend = preferences.appPreferences.playbackPreferences.playerBackend, + tracks = listOf(), playMethod = transcodeType, playSessionId = response.playSessionId, liveStreamId = source.liveStreamId, @@ -1029,6 +1045,7 @@ class PlaybackViewModel } data class CurrentPlayback( + val backend: PlayerBackend, val tracks: List, val playMethod: PlayMethod, val playSessionId: String?, diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/mpv/MpvPlayer.kt b/app/src/main/java/com/github/damontecres/wholphin/util/mpv/MpvPlayer.kt index 94cbece3..e562b8ff 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/mpv/MpvPlayer.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/mpv/MpvPlayer.kt @@ -293,8 +293,10 @@ class MpvPlayer( } override fun getDuration(): Long { - throwIfReleased() if (DEBUG) Timber.v("getDuration") + if (isReleased) { + return durationMs + } val duration = MPVLib.getPropertyDouble("duration/full")?.seconds?.inWholeMilliseconds ?: durationMs diff --git a/app/src/main/proto/WholphinDataStore.proto b/app/src/main/proto/WholphinDataStore.proto index bdfd92a4..e0db13ab 100644 --- a/app/src/main/proto/WholphinDataStore.proto +++ b/app/src/main/proto/WholphinDataStore.proto @@ -29,6 +29,15 @@ enum MediaExtensionStatus{ MES_DISABLED = 2; } +enum PlayerBackend{ + EXO_PLAYER = 0; + MPV = 1; +} + +message MpvOptions{ + bool enable_hardware_decoding = 1; +} + message PlaybackOverrides{ bool ac3_supported = 1; bool downmix_stereo = 2; @@ -58,6 +67,8 @@ message PlaybackPreferences { int64 pass_out_protection_ms = 16; PrefContentScale global_content_scale = 17; ShowNextUpWhen show_next_up_when = 18; + PlayerBackend player_backend = 19; + MpvOptions mpv_options = 20; } message HomePagePreferences{ diff --git a/app/src/main/res/values/preferences.xml b/app/src/main/res/values/preferences.xml index 1f7ba4d4..be288e01 100644 --- a/app/src/main/res/values/preferences.xml +++ b/app/src/main/res/values/preferences.xml @@ -90,4 +90,9 @@ During end credits/outro + + ExoPlayer (default) + MPV (Experimental) + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a6a3287e..c94342d7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -142,6 +142,9 @@ Aired Episode Order Create new playlist Add to playlist + Playback Backend + MPV: Use hardware decoding + Disable if you experience crashes %d downloads