From 67daab31b41304ea77dba0077f1e97d80a5aede6 Mon Sep 17 00:00:00 2001 From: ModerRAS Date: Fri, 31 Jul 2026 21:10:53 +0800 Subject: [PATCH] fix(player): render layered bilingual ASS subtitles --- gradle/libs.versions.toml | 2 +- .../kotlin/com/miruplay/tv/player/DiModule.kt | 12 +---- .../tv/player/ExoPlaybackController.kt | 1 + .../ExperimentalHdrSdrVideoGraphFactory.kt | 47 ------------------- .../tv/player/ExperimentalRenderersFactory.kt | 46 +++++++----------- ...ExperimentalHdrSdrVideoGraphFactoryTest.kt | 35 -------------- 6 files changed, 20 insertions(+), 123 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ccdb535f..5a317acd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ kotlin = "2.0.0" compose-version = "1.2.1" tv-material = "1.0.0-beta01" tv-foundation = "1.0.0-beta01" -media3 = "1.3.1" +media3 = "1.8.0" hilt = "2.52" room = "2.6.1" ksp = "2.0.0-1.0.21" diff --git a/player-core/src/main/kotlin/com/miruplay/tv/player/DiModule.kt b/player-core/src/main/kotlin/com/miruplay/tv/player/DiModule.kt index 21b53df8..949cd961 100644 --- a/player-core/src/main/kotlin/com/miruplay/tv/player/DiModule.kt +++ b/player-core/src/main/kotlin/com/miruplay/tv/player/DiModule.kt @@ -1,7 +1,6 @@ package com.miruplay.tv.player import android.content.Context -import androidx.media3.common.PreviewingVideoGraph import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer @@ -41,12 +40,8 @@ object PlayerModule { fun provideExperimentalExoPlayer( @ApplicationContext context: Context, dataSourceFactory: PlaybackDataSourceFactory, - experimentalPreviewingVideoGraphFactory: PreviewingVideoGraph.Factory, ): ExoPlayer { - val renderersFactory = ExperimentalRenderersFactory( - context = context, - previewingVideoGraphFactory = experimentalPreviewingVideoGraphFactory, - ) + val renderersFactory = ExperimentalRenderersFactory(context) // The experimental HDR backend relies on stable HEVC surface attachment across // vendor codecs, so we bias toward compatibility over async throughput here. .forceDisableMediaCodecAsynchronousQueueing() @@ -58,11 +53,6 @@ object PlayerModule { .build() } - @Provides - @Singleton - fun provideExperimentalPreviewingVideoGraphFactory(): PreviewingVideoGraph.Factory = - ExperimentalHdrSdrPreviewingVideoGraphFactory() - @Provides @Singleton fun providePlaybackConfig(): PlaybackConfig = PlaybackConfig() diff --git a/player-core/src/main/kotlin/com/miruplay/tv/player/ExoPlaybackController.kt b/player-core/src/main/kotlin/com/miruplay/tv/player/ExoPlaybackController.kt index 1d78c332..37a95501 100644 --- a/player-core/src/main/kotlin/com/miruplay/tv/player/ExoPlaybackController.kt +++ b/player-core/src/main/kotlin/com/miruplay/tv/player/ExoPlaybackController.kt @@ -901,6 +901,7 @@ class ExoPlaybackController @Inject constructor( override fun onVideoInputFormatChanged( eventTime: AnalyticsListener.EventTime, format: Format, + decoderReuseEvaluation: androidx.media3.exoplayer.DecoderReuseEvaluation?, ) { if (!isCurrentPlayer(player)) return refreshVideoSignalDescriptor(format) diff --git a/player-core/src/main/kotlin/com/miruplay/tv/player/ExperimentalHdrSdrVideoGraphFactory.kt b/player-core/src/main/kotlin/com/miruplay/tv/player/ExperimentalHdrSdrVideoGraphFactory.kt index fa9a2027..588eea15 100644 --- a/player-core/src/main/kotlin/com/miruplay/tv/player/ExperimentalHdrSdrVideoGraphFactory.kt +++ b/player-core/src/main/kotlin/com/miruplay/tv/player/ExperimentalHdrSdrVideoGraphFactory.kt @@ -2,25 +2,6 @@ package com.miruplay.tv.player -import android.content.Context -import androidx.media3.common.ColorInfo -import androidx.media3.common.DebugViewProvider -import androidx.media3.common.Effect -import androidx.media3.common.PreviewingVideoGraph -import androidx.media3.common.VideoGraph -import androidx.media3.effect.PreviewingSingleInputVideoGraph -import java.util.concurrent.Executor - -internal fun resolveExperimentalGraphOutputColorInfo( - inputColorInfo: ColorInfo, - requestedOutputColorInfo: ColorInfo, -): ColorInfo = - if (ColorInfo.isTransferHdr(inputColorInfo)) { - ColorInfo.SDR_BT709_LIMITED - } else { - requestedOutputColorInfo - } - internal fun shouldUseExoVideoEffectsPipeline( effectPipelineEnabled: Boolean, activeBackend: com.miruplay.tv.model.PlaybackRenderBackend, @@ -29,31 +10,3 @@ internal fun shouldUseExoVideoEffectsPipeline( effectPipelineEnabled && activeBackend == com.miruplay.tv.model.PlaybackRenderBackend.EXPERIMENTAL_GL && usesExperimentalEffectsPlayer - -internal class ExperimentalHdrSdrPreviewingVideoGraphFactory( - private val delegate: PreviewingVideoGraph.Factory = PreviewingSingleInputVideoGraph.Factory(), -) : PreviewingVideoGraph.Factory { - override fun create( - context: Context, - inputColorInfo: ColorInfo, - outputColorInfo: ColorInfo, - debugViewProvider: DebugViewProvider, - listener: VideoGraph.Listener, - listenerExecutor: Executor, - compositionEffects: List, - initialTimestampOffsetUs: Long, - ): PreviewingVideoGraph = - delegate.create( - context, - inputColorInfo, - resolveExperimentalGraphOutputColorInfo( - inputColorInfo = inputColorInfo, - requestedOutputColorInfo = outputColorInfo, - ), - debugViewProvider, - listener, - listenerExecutor, - compositionEffects, - initialTimestampOffsetUs, - ) -} diff --git a/player-core/src/main/kotlin/com/miruplay/tv/player/ExperimentalRenderersFactory.kt b/player-core/src/main/kotlin/com/miruplay/tv/player/ExperimentalRenderersFactory.kt index 1afe5a25..f907d673 100644 --- a/player-core/src/main/kotlin/com/miruplay/tv/player/ExperimentalRenderersFactory.kt +++ b/player-core/src/main/kotlin/com/miruplay/tv/player/ExperimentalRenderersFactory.kt @@ -5,19 +5,16 @@ package com.miruplay.tv.player import android.content.Context import android.os.Handler import android.util.Log -import androidx.media3.common.PreviewingVideoGraph import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.exoplayer.Renderer import androidx.media3.exoplayer.mediacodec.MediaCodecSelector -import androidx.media3.exoplayer.video.CompositingVideoSinkProvider import androidx.media3.exoplayer.video.MediaCodecVideoRenderer import androidx.media3.exoplayer.video.VideoRendererEventListener @UnstableApi class ExperimentalRenderersFactory( context: Context, - private val previewingVideoGraphFactory: PreviewingVideoGraph.Factory, ) : DefaultRenderersFactory(context) { private val experimentalVideoPipelineMode = resolveExperimentalVideoPipelineMode(resolveDeviceGlEsMajorVersion(context)) @@ -48,34 +45,25 @@ class ExperimentalRenderersFactory( return } - val replacementRenderer = when (experimentalVideoPipelineMode) { - ExperimentalVideoPipelineMode.MEDIA3_EFFECTS -> MediaCodecVideoRenderer( - context, - getCodecAdapterFactory(), - mediaCodecSelector, - allowedVideoJoiningTimeMs, - enableDecoderFallback, - eventHandler, - eventListener, - MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY, - 30f, - CompositingVideoSinkProvider.Builder(context) - .setPreviewingVideoGraphFactory(previewingVideoGraphFactory) - .build(), - ) - - ExperimentalVideoPipelineMode.DEDICATED_GL_SURFACE -> ExperimentalHdrSurfaceMediaCodecVideoRenderer( - context = context, - mediaCodecAdapterFactory = getCodecAdapterFactory(), - mediaCodecSelector = mediaCodecSelector, - allowedVideoJoiningTimeMs = allowedVideoJoiningTimeMs, - enableDecoderFallback = enableDecoderFallback, - eventHandler = eventHandler, - eventListener = eventListener, - maxDroppedFramesToNotify = MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY, - experimentalVideoPipelineMode = experimentalVideoPipelineMode, + if (experimentalVideoPipelineMode == ExperimentalVideoPipelineMode.MEDIA3_EFFECTS) { + Log.i( + "ExperimentalRenderersFactory", + "Using Media3 native effects renderer", ) + return } + + val replacementRenderer = ExperimentalHdrSurfaceMediaCodecVideoRenderer( + context = context, + mediaCodecAdapterFactory = getCodecAdapterFactory(), + mediaCodecSelector = mediaCodecSelector, + allowedVideoJoiningTimeMs = allowedVideoJoiningTimeMs, + enableDecoderFallback = enableDecoderFallback, + eventHandler = eventHandler, + eventListener = eventListener, + maxDroppedFramesToNotify = MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY, + experimentalVideoPipelineMode = experimentalVideoPipelineMode, + ) out[rendererIndex] = replacementRenderer Log.i( "ExperimentalRenderersFactory", diff --git a/player-core/src/test/kotlin/com/miruplay/tv/player/ExperimentalHdrSdrVideoGraphFactoryTest.kt b/player-core/src/test/kotlin/com/miruplay/tv/player/ExperimentalHdrSdrVideoGraphFactoryTest.kt index a7f23998..5f291608 100644 --- a/player-core/src/test/kotlin/com/miruplay/tv/player/ExperimentalHdrSdrVideoGraphFactoryTest.kt +++ b/player-core/src/test/kotlin/com/miruplay/tv/player/ExperimentalHdrSdrVideoGraphFactoryTest.kt @@ -1,46 +1,11 @@ package com.miruplay.tv.player -import androidx.media3.common.C -import androidx.media3.common.ColorInfo import com.miruplay.tv.model.PlaybackRenderBackend -import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse -import org.junit.Assert.assertSame import org.junit.Assert.assertTrue import org.junit.Test class ExperimentalHdrSdrVideoGraphFactoryTest { - - @Test - fun `hdr input is forced to sdr output for experimental graph`() { - val hdrInput = ColorInfo.Builder() - .setColorSpace(C.COLOR_SPACE_BT2020) - .setColorRange(C.COLOR_RANGE_LIMITED) - .setColorTransfer(C.COLOR_TRANSFER_ST2084) - .setLumaBitdepth(10) - .setChromaBitdepth(10) - .build() - - val resolved = resolveExperimentalGraphOutputColorInfo( - inputColorInfo = hdrInput, - requestedOutputColorInfo = hdrInput, - ) - - assertEquals(ColorInfo.SDR_BT709_LIMITED, resolved) - } - - @Test - fun `sdr input keeps requested output color info`() { - val requestedOutput = ColorInfo.SRGB_BT709_FULL - - val resolved = resolveExperimentalGraphOutputColorInfo( - inputColorInfo = ColorInfo.SDR_BT709_LIMITED, - requestedOutputColorInfo = requestedOutput, - ) - - assertSame(requestedOutput, resolved) - } - @Test fun `only experimental backend enables exo effect pipeline`() { assertTrue(