From 06ecba5fb5175da5ccbfa900dead01fe3c7bfe43 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Mon, 17 Aug 2026 00:10:39 +0200 Subject: [PATCH] fix(player): render Dolby Vision via gpu-next on the Android mpv backend Dolby Vision Profile 5 files play with pink and purple colors on Android devices without DV support (#1902): vo=gpu cannot apply DV RPU reshaping, so the raw IPTPQc2 base layer reaches the screen. The shipped libmpv AAR builds mpv 0.41 with libplacebo, where gpu-next is the upstream default VO and reshapes DV correctly. Prefer vo=gpu-next with an explicit vo=gpu fallback on both the primary mpv backend and the ExoPlayer failure-fallback path, drop the vd-lavc-film-grain=cpu override so film grain applies on the GPU under gpu-next, and report current-vo in player stats. Verified on a Pixel 7 (no DV display or decoder): gpu-next initializes on Mali-G710/GLES 3.2, SDR hardware decode is unchanged, and a 4K DV stream decodes to dolbyvision/bt.2020/pq with correct colors under software decode. Reshaping still needs software decode: FFmpeg 8.0's mediacodec wrapper exports no DOVI side data, so hardware-decoded DV keeps playing the base layer untouched. --- .../com/edde746/plezy/exoplayer/ExoPlayerPlugin.kt | 2 +- .../kotlin/com/edde746/plezy/mpv/MpvPlayerCore.kt | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerPlugin.kt b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerPlugin.kt index 4d47155c7..6e76677b2 100644 --- a/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerPlugin.kt +++ b/android/app/src/main/kotlin/com/edde746/plezy/exoplayer/ExoPlayerPlugin.kt @@ -1313,7 +1313,7 @@ class ExoPlayerPlugin : val bufferSize = configuredBufferSizeBytes core.setProperty("hwdec", "mediacodec,mediacodec-copy") - core.setProperty("vo", "gpu") + core.setProperty("vo", "gpu-next,gpu") core.setProperty("ao", "audiotrack") if (bufferSize != null && bufferSize > 0) { diff --git a/android/app/src/main/kotlin/com/edde746/plezy/mpv/MpvPlayerCore.kt b/android/app/src/main/kotlin/com/edde746/plezy/mpv/MpvPlayerCore.kt index 02f9560fb..6538b0d79 100644 --- a/android/app/src/main/kotlin/com/edde746/plezy/mpv/MpvPlayerCore.kt +++ b/android/app/src/main/kotlin/com/edde746/plezy/mpv/MpvPlayerCore.kt @@ -293,10 +293,17 @@ class MpvPlayerCore private constructor( setOption("audio-display", "no") setOption("gapless-audio", "weak") } else { - setOption("vo", "gpu") + // gpu-next (libplacebo) is mpv 0.41's default VO and the only + // Android path that applies Dolby Vision RPU reshaping — vo=gpu + // renders the raw base layer, which for DV Profile 5 means + // pink/purple IPTPQc2 output (#1902). Keep the explicit chain so + // a device where gpu-next cannot initialize still gets the + // legacy renderer instead of no video output. Film grain is left + // on its `auto` default: applied by the VO under gpu-next, by + // the decoder on the fallback path. + setOption("vo", "gpu-next,gpu") setOption("gpu-context", "android") setOption("opengl-es", "yes") - setOption("vd-lavc-film-grain", "cpu") if (displayFpsOverride != null) { setOption("display-fps-override", displayFpsOverride) } @@ -1038,6 +1045,7 @@ class MpvPlayerCore private constructor( "estimated-vf-fps" to getProperty("estimated-vf-fps"), "video-bitrate" to getProperty("video-bitrate"), "hwdec-current" to getProperty("hwdec-current"), + "current-vo" to getProperty("current-vo"), "audio-codec-name" to getProperty("audio-codec-name"), "audio-params/samplerate" to getProperty("audio-params/samplerate"), "audio-params/hr-channels" to getProperty("audio-params/hr-channels"),