test: libass overlay test

This commit is contained in:
tapframe 2026-04-10 14:31:36 +05:30
parent 10b0f634f0
commit 88226ba127
3 changed files with 79 additions and 5 deletions

View file

@ -45,6 +45,9 @@ import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
import androidx.media3.ui.SubtitleView import androidx.media3.ui.SubtitleView
import androidx.media3.ui.CaptionStyleCompat import androidx.media3.ui.CaptionStyleCompat
import com.nuvio.app.R
import io.github.peerless2012.ass.media.kt.withAssSupport
import io.github.peerless2012.ass.media.widget.AssSubtitleView
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -90,6 +93,10 @@ actual fun PlatformPlayerSurface(
val sanitizedSourceResponseHeaders = remember(sourceResponseHeaders) { val sanitizedSourceResponseHeaders = remember(sourceResponseHeaders) {
sanitizePlaybackResponseHeaders(sourceResponseHeaders) sanitizePlaybackResponseHeaders(sourceResponseHeaders)
} }
val useLibass = playerSettings.useLibass
val libassRenderType = runCatching {
LibassRenderType.valueOf(playerSettings.libassRenderType)
}.getOrDefault(LibassRenderType.CUES)
val exoPlayer = remember(sourceUrl, sourceAudioUrl, sanitizedSourceHeaders, sanitizedSourceResponseHeaders) { val exoPlayer = remember(sourceUrl, sourceAudioUrl, sanitizedSourceHeaders, sanitizedSourceResponseHeaders) {
val renderersFactory = DefaultRenderersFactory(context) val renderersFactory = DefaultRenderersFactory(context)
@ -126,11 +133,6 @@ actual fun PlatformPlayerSurface(
useYoutubeChunkedPlayback = useYoutubeChunkedPlayback, useYoutubeChunkedPlayback = useYoutubeChunkedPlayback,
) )
val useLibass = playerSettings.useLibass
val libassRenderType = runCatching {
LibassRenderType.valueOf(playerSettings.libassRenderType)
}.getOrDefault(LibassRenderType.CUES)
val player = if (useLibass) { val player = if (useLibass) {
ExoPlayer.Builder(context) ExoPlayer.Builder(context)
.setTrackSelector(trackSelector) .setTrackSelector(trackSelector)
@ -418,6 +420,11 @@ actual fun PlatformPlayerSurface(
this.resizeMode = resizeMode.toExoResizeMode() this.resizeMode = resizeMode.toExoResizeMode()
setShutterBackgroundColor(android.graphics.Color.BLACK) setShutterBackgroundColor(android.graphics.Color.BLACK)
playerViewRef = this playerViewRef = this
syncLibassOverlay(
player = exoPlayer,
enabled = useLibass,
renderType = libassRenderType,
)
applySubtitleStyle(currentSubtitleStyle) applySubtitleStyle(currentSubtitleStyle)
} }
}, },
@ -426,6 +433,11 @@ actual fun PlatformPlayerSurface(
playerView.useController = useNativeController playerView.useController = useNativeController
playerView.resizeMode = resizeMode.toExoResizeMode() playerView.resizeMode = resizeMode.toExoResizeMode()
playerViewRef = playerView playerViewRef = playerView
playerView.syncLibassOverlay(
player = exoPlayer,
enabled = useLibass,
renderType = libassRenderType,
)
playerView.applySubtitleStyle(currentSubtitleStyle) playerView.applySubtitleStyle(currentSubtitleStyle)
}, },
) )
@ -456,6 +468,56 @@ private fun PlayerResizeMode.toExoResizeMode(): Int =
PlayerResizeMode.Zoom -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM PlayerResizeMode.Zoom -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
} }
private fun PlayerView.syncLibassOverlay(
player: ExoPlayer,
enabled: Boolean,
renderType: LibassRenderType,
) {
val subtitleView = subtitleView ?: return
val needsOverlay = enabled && renderType.usesOverlaySubtitleView()
val boundPlayer = getTag(R.id.libass_overlay_bound_player) as? ExoPlayer
val hasOverlayChild = subtitleView.hasAssOverlayChild()
if (!needsOverlay) {
if (hasOverlayChild) {
subtitleView.removeAssOverlayChildren()
}
if (boundPlayer != null) {
setTag(R.id.libass_overlay_bound_player, null)
}
return
}
val assHandler = player.getAssHandlerCompat() ?: return
if (boundPlayer === player && hasOverlayChild) {
return
}
subtitleView.removeAssOverlayChildren()
subtitleView.withAssSupport(assHandler)
setTag(R.id.libass_overlay_bound_player, player)
}
private fun LibassRenderType.usesOverlaySubtitleView(): Boolean =
this == LibassRenderType.OVERLAY_CANVAS || this == LibassRenderType.OVERLAY_OPEN_GL
private fun SubtitleView.hasAssOverlayChild(): Boolean {
for (index in 0 until childCount) {
if (getChildAt(index) is AssSubtitleView) {
return true
}
}
return false
}
private fun SubtitleView.removeAssOverlayChildren() {
for (index in childCount - 1 downTo 0) {
if (getChildAt(index) is AssSubtitleView) {
removeViewAt(index)
}
}
}
private fun PlayerView.applySubtitleStyle(style: SubtitleStyleState) { private fun PlayerView.applySubtitleStyle(style: SubtitleStyleState) {
subtitleView?.apply { subtitleView?.apply {
val baseBottomPaddingFraction = SubtitleView.DEFAULT_BOTTOM_PADDING_FRACTION * 2f / 3f val baseBottomPaddingFraction = SubtitleView.DEFAULT_BOTTOM_PADDING_FRACTION * 2f / 3f

View file

@ -20,6 +20,10 @@ import io.github.peerless2012.ass.media.extractor.AssMatroskaExtractor
import io.github.peerless2012.ass.media.kt.withAssSupport import io.github.peerless2012.ass.media.kt.withAssSupport
import io.github.peerless2012.ass.media.parser.AssSubtitleParserFactory import io.github.peerless2012.ass.media.parser.AssSubtitleParserFactory
import io.github.peerless2012.ass.media.type.AssRenderType import io.github.peerless2012.ass.media.type.AssRenderType
import java.util.Collections
import java.util.WeakHashMap
private val assHandlersByPlayer = Collections.synchronizedMap(WeakHashMap<ExoPlayer, AssHandler>())
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
internal fun ExoPlayer.Builder.buildWithAssSupportCompat( internal fun ExoPlayer.Builder.buildWithAssSupportCompat(
@ -47,10 +51,14 @@ internal fun ExoPlayer.Builder.buildWithAssSupportCompat(
.setRenderersFactory(renderersFactory.withAssSupport(assHandler)) .setRenderersFactory(renderersFactory.withAssSupport(assHandler))
.build() .build()
assHandlersByPlayer[player] = assHandler
assHandler.init(player) assHandler.init(player)
return player return player
} }
internal fun ExoPlayer.getAssHandlerCompat(): AssHandler? = assHandlersByPlayer[this]
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
private class CompatAssSubtitleParserFactory( private class CompatAssSubtitleParserFactory(
private val assHandler: AssHandler private val assHandler: AssHandler

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="libass_overlay_bound_player" type="id" />
</resources>