Skip to content

fix(player): stack bilingual subtitle cues so they don't overlap - #59

Open
ModerRAS wants to merge 2 commits into
masterfrom
fix/subtitle-bilingual-overlap
Open

fix(player): stack bilingual subtitle cues so they don't overlap#59
ModerRAS wants to merge 2 commits into
masterfrom
fix/subtitle-bilingual-overlap

Conversation

@ModerRAS

@ModerRAS ModerRAS commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Problem

Bilingual (CN+JP) ASS subtitles rendered as two overlapping layers because every dialogue cue shared the bottom-centre position and the renderers drew them on top of each other. With many simultaneous lines the stack also overflowed the screen and clipped every line to half-height.

Fix — both backends

ExoPlayer / media3 (default backend)

Subtitles were rendered by the PlayerView's built-in SubtitleView with no cue processing, so cues kept their ASS positions and collided.

  • Intercept Player.Listener.onCues(CueGroup) in PlayerScreen to collect current cues.
  • restackSubtitleCues(cues) (pure fn): assign explicit line numbers counting up from the bottom (-1, -2, … with LINE_TYPE_NUMBER + ANCHOR_TYPE_END) so cues stack without overlapping; clear embedded horizontal position/size (DIMEN_UNSET) so nothing drifts off-screen → fixes overflow; cap the stack at 4 (keeping most recent) so it never overflows the top → fixes clipping; preserves ASS embedded styling (colours/typeface) via buildUpon().
  • Hide PlayerView.subtitleView; render a controlled SubtitleView overlay with the restacked cues, applying the existing transparent-background preference.

mpv / libass (experimental embedded backend)

  • Set sub-ass-override=force in MiruMpvSurfaceView.initOptions() so libass ignores embedded ASS \pos/styles and renders plain, wrapped, bottom-stacked subtitles via its default collision avoidance → bilingual lines stack instead of overlapping.
  • Declarative mpvSubtitleLayoutNormalisationOptions config (applied in initOptions).
  • Note: this discards ASS styling (colours/fonts) on the mpv backend, trading fidelity for readability — consistent with normalising layout. Can be made a preference later if styled ASS is desired.

Tests

  • SubtitleCueLayoutTest (6 tests, ExoPlayer restack logic) — all passing.
  • MpvSubtitleLayoutTest (2 tests, mpv normalisation option) — all passing.
  • Local: :ui-tv:testDebugUnitTest and :player-mpv-android:testDebugUnitTest both BUILD SUCCESSFUL (no regressions).

Notes

  • Needs on-device verification of actual rendering (line spacing for multi-line cues is approximate; single-line-per-cue stacking is exact).
  • ExoPlayer path keeps ASS colours; mpv path uses plain style (libass force).

Bilingual (CN+JP) ASS subtitles rendered as overlapping layers because every
dialogue cue shared the bottom-centre position and media3's SubtitleView drew
them on top of each other. With many simultaneous lines the stack also overflowed
the screen and clipped every line to half-height.

Intercept onCues from the ExoPlayer, restack simultaneous cues into explicit
vertical line positions counting up from the bottom, clear embedded horizontal
position/size so nothing drifts off-screen, and cap the stack so it never
overflows the top. Render on a controlled SubtitleView overlay (the PlayerView's
built-in SubtitleView is hidden) so the restacked layout actually takes effect.

- Add restackSubtitleCues() pure function (ui-tv) with unit tests
- Wire Player.Listener.onCues in PlayerScreen to collect current cues
- Hide PlayerView.subtitleView; render a restacked SubtitleView overlay
- Preserve ASS embedded styling (colours/typeface); only positioning is normalised
- Apply the existing transparent-background preference to the overlay
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@ModerRAS, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a78fef62-a15d-4dc0-93f4-21d1fda4f255

📥 Commits

Reviewing files that changed from the base of the PR and between 4fc2fb6 and 66e956d.

📒 Files selected for processing (6)
  • player-mpv-android/src/main/kotlin/is/xyz/mpv/MiruMpvSurfaceView.kt
  • player-mpv-android/src/main/kotlin/is/xyz/mpv/MpvSubtitleLayout.kt
  • player-mpv-android/src/test/kotlin/is/xyz/mpv/MpvSubtitleLayoutTest.kt
  • ui-tv/src/main/kotlin/com/miruplay/tv/ui/player/PlayerScreen.kt
  • ui-tv/src/main/kotlin/com/miruplay/tv/ui/player/SubtitleCueLayout.kt
  • ui-tv/src/test/kotlin/com/miruplay/tv/ui/player/SubtitleCueLayoutTest.kt

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Set sub-ass-override=force on the embedded mpv backend so bilingual (CN+JP)
ASS dialogue lines stack vertically instead of overlapping at the same \pos,
mirroring the ExoPlayer cue-restack fix. libass then renders plain, wrapped,
bottom-stacked subtitles via its default collision avoidance.

- Add mpvSubtitleLayoutNormalisationOptions declarative config + unit test
- Apply the options in MiruMpvSurfaceView.initOptions()

Note: this discards ASS styling (colours/fonts) on the mpv backend, trading
fidelity for readability — consistent with normalising layout on ExoPlayer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant