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.
This commit is contained in:
edde746
2026-08-17 01:41:46 +02:00
parent 096e285ab0
commit 06ecba5fb5
2 changed files with 11 additions and 3 deletions
@@ -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) {
@@ -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"),