Compare commits

...

1 Commits

Author SHA1 Message Date
Gauvain
e660b98871 fix(mpv): force software decoding on Android emulator (#1752)
Some checks are pending
🏗️ Build Apps / 🤖 Build Android APK (Phone) (push) Waiting to run
🏗️ Build Apps / 🤖 Build Android APK (TV) (push) Waiting to run
🏗️ Build Apps / 🍎 Build iOS IPA (Phone) (push) Waiting to run
🏗️ Build Apps / 🍎 Build iOS IPA (Phone - Unsigned) (push) Waiting to run
🏗️ Build Apps / 🍎 Build tvOS IPA (push) Waiting to run
🏗️ Build Apps / 🍎 Build tvOS IPA (Unsigned) (push) Waiting to run
🔒 Lockfile Consistency Check / 🔍 Check bun.lock and package.json consistency (push) Waiting to run
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (actions) (push) Waiting to run
🛡️ CodeQL Analysis / 🔎 Analyze with CodeQL (javascript-typescript) (push) Waiting to run
🏷️🔀Merge Conflict Labeler / 🏷️ Labeling Merge Conflicts (push) Waiting to run
🚦 Security & Quality Gate / 🔍 Vulnerable Dependencies (push) Waiting to run
🚦 Security & Quality Gate / 🚑 Expo Doctor Check (push) Waiting to run
🚦 Security & Quality Gate / 🔍 Lint & Test (check) (push) Waiting to run
🚦 Security & Quality Gate / 🔍 Lint & Test (typecheck) (push) Waiting to run
🛡️ Trivy Security Scan / 🔎 Filesystem scan (push) Waiting to run
🚦 Security & Quality Gate / 📝 Validate PR Title (push) Waiting to run
🚦 Security & Quality Gate / 🔍 Lint & Test (format) (push) Waiting to run
🚦 Security & Quality Gate / 🔍 Lint & Test (i18n:check) (push) Waiting to run
🚦 Security & Quality Gate / 🔍 Lint & Test (lint) (push) Waiting to run
2026-06-19 00:12:40 +02:00

View File

@@ -4,6 +4,7 @@ import android.app.UiModeManager
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.AssetManager import android.content.res.AssetManager
import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.Log import android.util.Log
@@ -35,6 +36,30 @@ class MPVLayerRenderer(private val context: Context) : MPVLib.EventObserver {
return uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION return uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION
} }
/**
* True only on the Android emulator. Its goldfish/ranchu MediaCodec can't bind a
* decode output surface (decode opens with surface 0x0): HEVC then fails cleanly and
* mpv auto-falls-back to software, but H.264 "opens" deceptively and wedges the core
* (no fallback) — black video, then any command (seek/pause) deadlocks the UI thread
* → ANR. We force software decoding here.
*
* Only QEMU/SDK-exclusive signals are checked so a real device can never match — a
* false positive would needlessly drop shipping hardware to software decoding. The
* emulator reports ro.hardware=goldfish|ranchu, an sdk_* product, or a generic/
* emulator build fingerprint, none of which appear on real devices.
*/
private fun isEmulator(): Boolean {
val hardware = Build.HARDWARE.lowercase()
if (hardware == "goldfish" || hardware == "ranchu") return true
val product = Build.PRODUCT
if (product == "sdk" || product.startsWith("sdk_")) return true
val fingerprint = Build.FINGERPRINT
return fingerprint.startsWith("generic") ||
fingerprint.contains("emulator", ignoreCase = true)
}
interface Delegate { interface Delegate {
fun onPositionChanged(position: Double, duration: Double, cacheSeconds: Double) fun onPositionChanged(position: Double, duration: Double, cacheSeconds: Double)
fun onPauseChanged(isPaused: Boolean) fun onPauseChanged(isPaused: Boolean)
@@ -169,15 +194,21 @@ class MPVLayerRenderer(private val context: Context) : MPVLib.EventObserver {
MPVLib.setOptionString("gpu-context", "android") MPVLib.setOptionString("gpu-context", "android")
MPVLib.setOptionString("opengl-es", "yes") MPVLib.setOptionString("opengl-es", "yes")
// Hardware video decoding // Hardware decode path:
// TV: zero-copy (mediacodec) for better performance on low-power devices // - Real TV hardware: zero-copy `mediacodec` (fastest on low-power devices).
// Mobile: copy mode (mediacodec-copy) for better compatibility // - Real phone: `mediacodec-copy` (broadest compatibility).
val isTV = isTvDevice() // - Emulator: software decode. Its MediaCodec can't bind an output surface
if (isTV) { // (surface 0x0); HEVC then fails cleanly and mpv auto-falls-back to software,
// but H.264 "opens" deceptively and wedges the core with no fallback (black
// video, then any command — seek/pause — deadlocks the UI thread → ANR).
// hwdec=no makes every codec render via the gpu-next VO. Real devices unaffected.
when {
isEmulator() -> MPVLib.setOptionString("hwdec", "no")
isTvDevice() -> {
MPVLib.setOptionString("hwdec", "mediacodec") MPVLib.setOptionString("hwdec", "mediacodec")
MPVLib.setOptionString("profile", "fast") MPVLib.setOptionString("profile", "fast")
} else { }
MPVLib.setOptionString("hwdec", "mediacodec-copy") else -> MPVLib.setOptionString("hwdec", "mediacodec-copy")
} }
MPVLib.setOptionString("hwdec-codecs", "h264,hevc,mpeg4,mpeg2video,vp8,vp9,av1") MPVLib.setOptionString("hwdec-codecs", "h264,hevc,mpeg4,mpeg2video,vp8,vp9,av1")