Add setting to automatically switch between ExoPlayer & MPV (#736)

## Description
The MPV backend is no longer considered experimental. This PR adds a
setting for player backend "Prefer MPV" which uses MPV for all playback
unless the video is HDR which then uses ExoPlayer.

Switching occurs per video. So if there's a playlist with both SDR &
HDR, the player will switch back and forth as needed.

### Related issues
Closes #253
Related to #235
This commit is contained in:
Ray 2026-01-23 08:50:01 -05:00 committed by GitHub
parent 16ac02a3fd
commit 0639a7a1da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 892 additions and 648 deletions

View file

@ -2,21 +2,26 @@ package com.github.damontecres.wholphin.services
import android.content.Context
import android.os.Build
import android.widget.Toast
import androidx.core.content.edit
import androidx.datastore.core.DataStore
import androidx.preference.PreferenceManager
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.WholphinApplication
import com.github.damontecres.wholphin.preferences.AppPreference
import com.github.damontecres.wholphin.preferences.AppPreferences
import com.github.damontecres.wholphin.preferences.PlayerBackend
import com.github.damontecres.wholphin.preferences.update
import com.github.damontecres.wholphin.preferences.updateAdvancedPreferences
import com.github.damontecres.wholphin.preferences.updateInterfacePreferences
import com.github.damontecres.wholphin.preferences.updateLiveTvPreferences
import com.github.damontecres.wholphin.preferences.updateMpvOptions
import com.github.damontecres.wholphin.preferences.updatePlaybackOverrides
import com.github.damontecres.wholphin.preferences.updatePlaybackPreferences
import com.github.damontecres.wholphin.preferences.updateSubtitlePreferences
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
import com.github.damontecres.wholphin.ui.preferences.subtitle.SubtitleSettings
import com.github.damontecres.wholphin.ui.showToast
import com.github.damontecres.wholphin.util.Version
import dagger.hilt.android.qualifiers.ApplicationContext
import timber.log.Timber
@ -197,4 +202,11 @@ suspend fun upgradeApp(
}
}
}
if (previous.isEqualOrBefore(Version.fromString("0.4.0-1-g0"))) {
appPreferences.updateData {
it.updatePlaybackPreferences { playerBackend = PlayerBackend.PREFER_MPV }
}
showToast(context, context.getString(R.string.upgrade_mpv_toast), Toast.LENGTH_LONG)
}
}

View file

@ -19,11 +19,14 @@ import androidx.media3.exoplayer.video.VideoRendererEventListener
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.PlaybackPreferences
import com.github.damontecres.wholphin.preferences.PlayerBackend
import com.github.damontecres.wholphin.util.mpv.MpvPlayer
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.lang.reflect.Constructor
import javax.inject.Inject
@ -53,7 +56,9 @@ class PlayerFactory
val backend = prefs?.playerBackend ?: AppPreference.PlayerBackendPref.defaultValue
val newPlayer =
when (backend) {
PlayerBackend.MPV -> {
PlayerBackend.PREFER_MPV,
PlayerBackend.MPV,
-> {
val enableHardwareDecoding =
prefs?.mpvOptions?.enableHardwareDecoding
?: AppPreference.MpvHardwareDecoding.defaultValue
@ -94,6 +99,53 @@ class PlayerFactory
currentPlayer = newPlayer
return newPlayer
}
suspend fun createVideoPlayer(
backend: PlayerBackend,
prefs: PlaybackPreferences,
): Player {
withContext(Dispatchers.Main) {
if (currentPlayer?.isReleased == false) {
Timber.w("Player was not released before trying to create a new one!")
currentPlayer?.release()
}
}
val newPlayer =
when (backend) {
PlayerBackend.PREFER_MPV,
PlayerBackend.MPV,
-> {
val enableHardwareDecoding = prefs.mpvOptions.enableHardwareDecoding
val useGpuNext = prefs.mpvOptions.useGpuNext
MpvPlayer(context, enableHardwareDecoding, useGpuNext)
}
PlayerBackend.EXO_PLAYER,
PlayerBackend.UNRECOGNIZED,
-> {
val extensions = prefs.overrides.mediaExtensionsEnabled
val decodeAv1 = prefs.overrides.decodeAv1
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(
WholphinRenderersFactory(context, decodeAv1)
.setEnableDecoderFallback(true)
.setExtensionRendererMode(rendererMode),
).build()
}
}
currentPlayer = newPlayer
return newPlayer
}
}
val Player.isReleased: Boolean