media icon indicating copy to clipboard operation
media copied to clipboard

Add Compose-based (Material 3) SubtitleView for rendering CueGroup in Jetpack Compose

Open mzhsy1 opened this issue 3 months ago • 6 comments

Summary

This PR introduces a new Jetpack Compose composable, SubtitleView, designed to render subtitles provided by Media3's CueGroup. It supports both text cues (e.g., SRT, SSA/ASS converted by ExoPlayer) and bitmap cues (e.g., PNG images), respecting their layout properties such as position, size, and anchor point.

The component is built using Material 3 guidelines and provides customizable styling for text subtitles, including text style and background color. It is intended for use within Compose-based media player UIs.

Features

  • Renders both text and bitmap cues from a CueGroup.
  • Supports positioning, sizing, and anchoring based on cue metadata.
  • Customizable TextStyle and background for text cues.
  • Aligns subtitle area to the bottom center by default.
  • Includes KDoc with usage example.

Example Usage

@Composable
fun VideoPlayerScreen(exoPlayer: ExoPlayer) {
  var currentCueGroup: CueGroup? by remember { mutableStateOf(null) }

  DisposableEffect(exoPlayer) {
    val listener = object : Player.Listener {
      override fun onCues(cueGroup: CueGroup) {
        currentCueGroup = cueGroup
      }
    }
    exoPlayer.addListener(listener)
    onDispose {
      exoPlayer.removeListener(listener)
    }
  }

  Box {
    // Your video surface or PlayerView here
     PlayerView()
     ......
    SubtitleView(
      cueGroup = currentCueGroup,
      subtitleStyle = MaterialTheme.typography.bodyLarge.copy(
        color = Color.White,
        fontSize = 20.sp
      ),
      backgroundColor = Color.Black.copy(alpha = 0.5f),
      modifier = Modifier.align(Alignment.BottomCenter)
    )
  }
}

Test

SRT Rendering

Screenshot_20251010_105207

PGS Rendering

  • Versus PlayerView's SubtitleView rendering
Screenshot_20251010_183308

mzhsy1 avatar Oct 10 '25 04:10 mzhsy1

Hi @mzhsy1,

Thank you for your contribution. I appreciate you taking the time to create this composable. Subtitles and Ads are important milestones for us to match the feature parity of Composables with PlayerView and they are quite big projects in themselves.

However, this pull request does not meet the standards required for merging into the library in its current state. Things like:

  • Use of magic numbers and hardcoded offsets
  • No tests
  • A lot of unsafe null handling
  • Unhandled accessibility
  • Unhandled device rotation and configuration changes

From the design perspective, we are unsure for now about going ahead with using Image() composables. In the View world, https://github.com/androidx/media/blob/release/libraries/ui/src/main/java/androidx/media3/ui/SubtitleView.java delegated to https://github.com/androidx/media/blob/release/libraries/ui/src/main/java/androidx/media3/ui/CanvasSubtitleOutput.java and https://github.com/androidx/media/blob/release/libraries/ui/src/main/java/androidx/media3/ui/WebViewSubtitleOutput.java. You can see how thorough and detailed those cases are and we would want to match that. Perhaps with using Canvas (import androidx.compose.foundation.Canvas) and AndroidView (androidx.compose.ui.viewinterop.AndroidView) for Webview interop - this way we could build a solution without relying on Material3 at all - but this hasn't been prioritised for the upcoming 1.9.0 release.

I suggest we park this PR until next quarter and revisit it next quarter when we will have more resources to dedicate to it.

oceanjules avatar Oct 13 '25 21:10 oceanjules

Thank you for your reply. This Compose component is part of my personal project, created to replace the SubtitleView in PlayerView. The built-in SubtitleView has two issues that don’t meet my needs: first, it displays PGS subtitles from certain Blu-ray disc rips with incorrect aspect ratios; second, when multiple SRT subtitle entries share the same timestamp, it only shows one of them.

This Compose component successfully addresses both issues and is currently working well in my project. However, it may fail to fully render PGS subtitles when too many images appear on screen simultaneously.

I’ve seen your suggestion to switch from Image()-based rendering to Canvas() rendering. Regarding the offset:

Modifier.offset(x = offsetX.dp - 14.dp, y = offsetY.dp - 8.dp)

This offset is used to correct the display position of PGS subtitles. At the moment, I’m not sure why these specific offset values are necessary during normal rendering—it just happens to fix the misalignment.

I’m currently experimenting in my personal project with migrating both Text() and Image() rendering to Canvas(). So far, I’ve completed the migration from Image() to Canvas(), but the subtitle positioning offset issue mentioned above still persists. I’ll continue refining the implementation and evaluate the results.

Thank you for your suggestion! For now, I won’t submit these changes to this PR. If you develop a better subtitle rendering solution, please release it as soon as possible—I’ll prioritize adopting it.

mzhsy1 avatar Oct 14 '25 03:10 mzhsy1

Bit of a drive-by:

displays PGS subtitles from certain Blu-ray disc rips with incorrect aspect ratios

This sounds possibly similar to https://github.com/androidx/media/issues/2446 - which I think was resolved by ensuring the SubtitleView is nested inside AspectRationFrameLayout - I wonder if you're seeing a similar issue?

If it's not the same, please can you file a new issue with content we can use to repro the issue in the demo app and we can take a look.

icbaker avatar Oct 14 '25 13:10 icbaker

It seems that issue #2446 has not been resolved. The one above is the default SubtitleView, and the subtitles below are from my custom SubtitleView. Test video link: https://github.com/mzhsy1/MzDKPlayer/blob/main/demovideo/output%20(1).mkv 1min,10MB MKV videos can't be uploaded in PRs, and PGS subtitles require the MKV format, so I've placed it in my personal repository. Screenshot_20251015_000210 Screenshot_20251015_000319 Play it on PC using PotPlayer.  2025-10-15 002257

mzhsy1 avatar Oct 14 '25 16:10 mzhsy1

please can you file a new issue with content we can use to repro the issue in the demo app and we can take a look.

icbaker avatar Oct 14 '25 16:10 icbaker

please can you file a new issue with content we can use to repro the issue in the demo app and we can take a look.

@icbaker I have created an issue. https://github.com/androidx/media/issues/2849

mzhsy1 avatar Oct 15 '25 13:10 mzhsy1