Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ class LaunchTestSourceContentModeTest {
rawPreset = "punchy",
)

assertEquals(PlaybackRenderBackend.EXPERIMENTAL_GL, overrides.backend)
assertEquals(PlaybackRenderBackend.STANDARD_EXO, overrides.backend)
assertEquals(VideoRenderRuleKey.HDR10_PLUS, overrides.ruleKey)
assertEquals(ToneMappingProfilePreset.PUNCHY, overrides.preset)
}
Expand Down Expand Up @@ -461,7 +461,7 @@ class LaunchTestSourceContentModeTest {
hasBaseUrlExtra = false,
),
playbackOverrides = LaunchPlaybackOverrides(
backend = PlaybackRenderBackend.EXPERIMENTAL_GL,
backend = PlaybackRenderBackend.STANDARD_EXO,
ruleKey = null,
preset = null,
),
Expand Down Expand Up @@ -513,7 +513,7 @@ class LaunchTestSourceContentModeTest {
hasBaseUrlExtra = false,
),
playbackOverrides = LaunchPlaybackOverrides(
backend = PlaybackRenderBackend.EXPERIMENTAL_GL,
backend = PlaybackRenderBackend.STANDARD_EXO,
ruleKey = null,
preset = null,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ fun pictureSessionOverrideLabel(): String =
fun playbackBackendLabel(backend: PlaybackRenderBackend): String =
when (backend.normalizeSupportedBackend()) {
PlaybackRenderBackend.STANDARD_EXO -> "标准 Exo"
PlaybackRenderBackend.EXPERIMENTAL_GL -> "旧实验 GL"
PlaybackRenderBackend.EXPERIMENTAL_GL -> "标准 Exo"
PlaybackRenderBackend.EXPERIMENTAL_MPV_ANDROID -> "实验 mpv 内嵌"
PlaybackRenderBackend.EXPERIMENTAL_MPV_EMBEDDED -> "实验 mpv 内嵌"
PlaybackRenderBackend.EXPERIMENTAL_IJKPLAYER -> "实验 ijkplayer"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import kotlinx.serialization.Serializable
@Serializable
enum class PlaybackRenderBackend {
STANDARD_EXO,
// Serialized migration token only; normalizeSupportedBackend maps it to STANDARD_EXO.
EXPERIMENTAL_GL,
EXPERIMENTAL_MPV_ANDROID,
EXPERIMENTAL_MPV_EMBEDDED,
Expand All @@ -15,7 +16,7 @@ enum class PlaybackRenderBackend {
fun PlaybackRenderBackend.normalizeSupportedBackend(): PlaybackRenderBackend =
when (this) {
PlaybackRenderBackend.STANDARD_EXO -> PlaybackRenderBackend.STANDARD_EXO
PlaybackRenderBackend.EXPERIMENTAL_GL -> PlaybackRenderBackend.EXPERIMENTAL_GL
PlaybackRenderBackend.EXPERIMENTAL_GL -> PlaybackRenderBackend.STANDARD_EXO
PlaybackRenderBackend.EXPERIMENTAL_MPV_ANDROID -> PlaybackRenderBackend.EXPERIMENTAL_MPV_EMBEDDED
PlaybackRenderBackend.EXPERIMENTAL_MPV_EMBEDDED -> PlaybackRenderBackend.EXPERIMENTAL_MPV_EMBEDDED
PlaybackRenderBackend.EXPERIMENTAL_IJKPLAYER -> PlaybackRenderBackend.EXPERIMENTAL_IJKPLAYER
Expand All @@ -25,7 +26,6 @@ fun PlaybackRenderBackend.normalizeSupportedBackend(): PlaybackRenderBackend =
fun supportedPlaybackRenderBackends(): List<PlaybackRenderBackend> =
listOf(
PlaybackRenderBackend.STANDARD_EXO,
PlaybackRenderBackend.EXPERIMENTAL_GL,
PlaybackRenderBackend.EXPERIMENTAL_MPV_EMBEDDED,
PlaybackRenderBackend.EXPERIMENTAL_IJKPLAYER,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class PlaybackPreferencesManagerTest {
}

@Test
fun `manager persists and reloads customized format-aware tone mapping preferences`() = runBlocking {
fun `manager migrates legacy gl backend while preserving tone mapping preferences`() = runBlocking {
val updated = FormatAwareToneMappingPreferences(
defaultBackend = PlaybackRenderBackend.EXPERIMENTAL_GL,
rules = FormatAwareToneMappingPreferences().rules + (
Expand All @@ -119,7 +119,7 @@ class PlaybackPreferencesManagerTest {

val restored = manager.getFormatAwareToneMappingPreferences()

assertEquals(PlaybackRenderBackend.EXPERIMENTAL_GL, restored.defaultBackend)
assertEquals(PlaybackRenderBackend.STANDARD_EXO, restored.defaultBackend)
assertEquals(
ToneMappingCurvePreset.REINHARD,
restored.rules.getValue(VideoRenderRuleKey.HDR10).curvePreset,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ private fun buildLinearPhaseFilter(preset: com.miruplay.tv.model.AudioDspPreset)
}
val response = FrequencyResponse.sample(plan, frequencies)
val entries = response.frequenciesHz.indices.joinToString(";") { index ->
"${number(response.frequenciesHz[index])}:${number(response.magnitudeDb[index])}"
"entry(${number(response.frequenciesHz[index])},${number(response.magnitudeDb[index])})"
}
val delaySeconds = plan.groupDelayFrames.toDouble() / DSP_SAMPLE_RATE_HZ
return "firequalizer=gain_entry='$entries':delay=${number(delaySeconds)}:multi=true:zero_phase=false"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.miruplay.tv.player

import android.app.ActivityManager
import android.content.Context
import android.content.pm.ConfigurationInfo

fun resolveGlEsMajorVersion(reqGlEsVersion: Int): Int =
when {
reqGlEsVersion == ConfigurationInfo.GL_ES_VERSION_UNDEFINED -> 2
reqGlEsVersion <= 0 -> 2
else -> reqGlEsVersion shr 16
}
Comment on lines +7 to +12

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
file=$(git ls-files | grep -F 'player-core/src/main/kotlin/com/miruplay/tv/player/DeviceGlCapabilities.kt' | head -n1)
printf '%s\n' "$file"
wc -l "$file"
cat -n "$file"
printf '\nTest references:\n'
rg -n -C 4 'resolveGlEsMajorVersion|EmbeddedMpvSessionOptionsTest|reqGlEsVersion|GL_ES_VERSION_UNDEFINED' .

Repository: ModerRAS/MiruPlay

Length of output: 6816


🌐 Web query:

Android SDK ConfigurationInfo reqGlEsVersion "major" "minor" 16 bits GL_ES_VERSION_UNDEFINED

💡 Result:

In the Android SDK, the property reqGlEsVersion represents the OpenGL ES version required by an application, using a 32-bit integer format where the upper 16 bits define the major version and the lower 16 bits define the minor version [1][2][3]. GL_ES_VERSION_UNDEFINED is a constant defined as 0, which serves as the default value for reqGlEsVersion when no specific version has been set [1][2][3]. Key details regarding this configuration include: Bitwise Representation: The value is encoded such that the major and minor versions are separated into the high and low 16-bit segments, respectively [1][3]. For example, a value of 0x00010002 corresponds to OpenGL ES version 1.2 [2][3]. You can extract these values using the getGlEsVersion method, which parses this integer into a readable "major.minor" string [1][2][3]. Usage in Manifest: Developers declare these requirements in their AndroidManifest.xml using the uses-feature element with the android:glEsVersion attribute [4][5]. If an application does not specify this attribute, the system defaults to assuming the app requires OpenGL ES 1.0, which is supported by all Android devices [4]. Context in Data Structures: This field appears in both the ConfigurationInfo and FeatureInfo classes [1][6]. In FeatureInfo, the reqGlEsVersion is only considered valid if the name field of the object is null, indicating that the feature represents an OpenGL ES version requirement [6][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' 'Relevant test section:'
cat -n player-core/src/test/kotlin/com/miruplay/tv/player/EmbeddedMpvSessionOptionsTest.kt | sed -n '145,170p'
printf '\nCall sites and related GL-version logic:\n'
rg -n -C 3 'resolveDeviceGlEsMajorVersion|resolveGlEsMajorVersion|glEsMajor|GLES' player-core/src/main player-core/src/test
printf '\nBehavioral probe for packed values:\n'
python3 - <<'PY'
values = [0, -1, 0x0000FFFF, 0x00010002, 0x00030000, 0x00030002]
for value in values:
    signed = value if value < 2**31 else value - 2**32
    major = signed >> 16
    fallback = 2 if signed == 0 or signed <= 0 else major
    corrected = 2 if signed <= 0 else (major if major > 0 else 2)
    print(f"{value:`#010x`}: encoded major={(value & 0xffffffff) >> 16}, current={fallback}, corrected={corrected}")
PY

Repository: ModerRAS/MiruPlay

Length of output: 11017


Fallback to ES2 when the packed major version is zero.

ConfigurationInfo.reqGlEsVersion stores the major version in the upper 16 bits. A positive value such as 0x0000FFFF currently resolves to 0, but invalid values must fall back to ES2. Coerce a zero decoded major version to 2 and add this case to EmbeddedMpvSessionOptionsTest.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@player-core/src/main/kotlin/com/miruplay/tv/player/DeviceGlCapabilities.kt`
around lines 7 - 12, Update resolveGlEsMajorVersion so the decoded upper-16-bit
major version cannot return zero: treat a zero result from reqGlEsVersion shr as
ES2 while preserving the existing undefined and non-positive fallbacks. Add a
test case to EmbeddedMpvSessionOptionsTest covering a positive packed value
whose decoded major version is zero.


fun resolveDeviceGlEsMajorVersion(context: Context): Int {
val activityManager = context.getSystemService(ActivityManager::class.java)
val reqGlEsVersion = activityManager?.deviceConfigurationInfo?.reqGlEsVersion
?: ConfigurationInfo.GL_ES_VERSION_UNDEFINED
return resolveGlEsMajorVersion(reqGlEsVersion)
}
37 changes: 0 additions & 37 deletions player-core/src/main/kotlin/com/miruplay/tv/player/DiModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,40 +54,6 @@ object PlayerModule {
}
}

@Provides
@Singleton
@ExperimentalPlaybackPlayer
fun provideExperimentalExoPlayer(
@ApplicationContext context: Context,
dataSourceFactory: PlaybackDataSourceFactory,
audioDspRuntimeConfig: AudioDspRuntimeConfig,
): ExoPlayer {
val libassSession = LibassSubtitleSession()
val nativeAssAvailable = NativeAssRenderer.isAvailable()
val renderersFactory = ExperimentalRenderersFactory(context, audioDspRuntimeConfig, libassSession)
// The experimental HDR backend relies on stable HEVC surface attachment across
// vendor codecs, so we bias toward compatibility over async throughput here.
.forceDisableMediaCodecAsynchronousQueueing()
.setEnableDecoderFallback(true)
.setMediaCodecSelector(PlaybackMediaCodecSelector)

return ExoPlayer.Builder(context, renderersFactory)
.setMediaSourceFactory(
DefaultMediaSourceFactory(
ZlibSubtitleProtectingDataSourceFactory(dataSourceFactory),
ZlibSubtitleExtractorsFactory(
session = libassSession,
nativeAvailable = { nativeAssAvailable },
),
),
)
.build()
.also { player ->
LibassSubtitleRegistry.register(player, libassSession)
if (nativeAssAvailable) player.setVideoFrameMetadataListener(libassSession)
}
}

@Provides
@Singleton
fun providePlaybackConfig(): PlaybackConfig = PlaybackConfig()
Expand All @@ -98,8 +64,6 @@ object PlayerModule {
@ApplicationContext context: Context,
@StandardPlaybackPlayer
standardPlayerProvider: javax.inject.Provider<ExoPlayer>,
@ExperimentalPlaybackPlayer
experimentalPlayerProvider: javax.inject.Provider<ExoPlayer>,
dataSourceFactory: PlaybackDataSourceFactory,
httpRequestResolver: PlaybackHttpRequestResolver,
playbackPreferencesRepository: PlaybackPreferencesRepository,
Expand All @@ -111,7 +75,6 @@ object PlayerModule {
return ExoPlaybackController(
context = context,
standardExoPlayerProvider = standardPlayerProvider,
experimentalExoPlayerProvider = experimentalPlayerProvider,
dataSourceFactory = dataSourceFactory,
httpRequestResolver = httpRequestResolver,
playbackPreferencesRepository = playbackPreferencesRepository,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ fun buildEmbeddedMpvSessionOptions(
shaderPaths: List<String>,
speed: Float = 1.0f,
runtimeAbiIs32Bit: Boolean = isEmbeddedMpvRuntime32Bit(),
glEsMajorVersion: Int = 3,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 8 --glob '*.kt' \
  'buildEmbeddedMpvSessionOptions\s*\(|effectiveEmbeddedMpvVideoOutput\s*\(' \
  player-core player-mpv-android ui-tv

Repository: ModerRAS/MiruPlay

Length of output: 26245


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- all references ---'
rg -n -C 5 --hidden --glob '!build/**' --glob '!node_modules/**' \
  'buildEmbeddedMpvSessionOptions|effectiveEmbeddedMpvVideoOutput|deviceGlEsMajorVersion|resolveGlEsMajorVersion' .

printf '%s\n' '--- candidate source files ---'
git ls-files | rg 'EmbeddedMpvSessionOptions|ExoPlaybackController|\.kt$|\.java$' | head -200

Repository: ModerRAS/MiruPlay

Length of output: 41830


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- WebControlService production call ---'
sed -n '1075,1145p' web-control/src/main/kotlin/com/miruplay/tv/webcontrol/WebControlService.kt

printf '%s\n' '--- Embedded MPV option functions ---'
sed -n '1,45p;125,145p' player-core/src/main/kotlin/com/miruplay/tv/player/EmbeddedMpvSessionOptions.kt

printf '%s\n' '--- GLES capability resolution ---'
cat -n player-core/src/main/kotlin/com/miruplay/tv/player/DeviceGlCapabilities.kt

printf '%s\n' '--- all production uses of the diagnostic value ---'
rg -n -C 4 --glob 'src/main/**/*.kt' \
  'effectiveEmbeddedMpvVo|embeddedMpvVo|effectiveEmbeddedMpvVideoOutput' web-control player-core app

Repository: ModerRAS/MiruPlay

Length of output: 7856


Pass the resolved GLES version to effectiveEmbeddedMpvVideoOutput. The production debug snapshot omits glEsMajorVersion, so ES2 devices report gpu-next instead of mediacodec_embed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@player-core/src/main/kotlin/com/miruplay/tv/player/EmbeddedMpvSessionOptions.kt`
at line 16, Update the call to effectiveEmbeddedMpvVideoOutput to pass the
resolved glEsMajorVersion from EmbeddedMpvSessionOptions, ensuring ES2 devices
select mediacodec_embed instead of gpu-next.

debugConfig: EmbeddedMpvDebugConfig = EmbeddedMpvDebugConfig(),
audioDspConfig: AudioDspConfig = AudioDspConfig.neutral(),
): MiruMpvSurfaceView.SessionOptions {
Expand All @@ -26,6 +27,7 @@ fun buildEmbeddedMpvSessionOptions(
return MiruMpvSurfaceView.SessionOptions(
vo = effectiveEmbeddedMpvVideoOutput(
runtimeAbiIs32Bit = runtimeAbiIs32Bit,
glEsMajorVersion = glEsMajorVersion,
debugConfig = debugConfig,
),
hwdec = effectiveEmbeddedMpvHwdec(debugConfig),
Expand Down Expand Up @@ -130,8 +132,13 @@ private fun resolveEmbeddedMpvPeakDetection(strategy: PeakDetectionStrategy): Em

fun effectiveEmbeddedMpvVideoOutput(
runtimeAbiIs32Bit: Boolean = isEmbeddedMpvRuntime32Bit(),
glEsMajorVersion: Int = 3,
debugConfig: EmbeddedMpvDebugConfig = EmbeddedMpvDebugConfig(),
): String = debugConfig.vo ?: if (runtimeAbiIs32Bit) "gpu-hq" else "gpu-next"
): String = debugConfig.vo ?: when {
glEsMajorVersion < 3 -> "mediacodec_embed"
runtimeAbiIs32Bit -> "gpu-hq"
else -> "gpu-next"
}

fun effectiveEmbeddedMpvHwdec(
debugConfig: EmbeddedMpvDebugConfig = EmbeddedMpvDebugConfig(),
Expand Down
Loading
Loading