Search code examples
androidandroid-jetpack-composehttp-live-streamingexoplayerandroid-media3

How to display HLS subtitles with ExoPlayer 3 and Compose?


How can I display HLS embedded subtitles using AndroidX Media3 ExoPlayer and Jetpack Compose?

Below is what I tried but no subtitles show up, nor does any button to show/hide them. Thanks for your help!

@Composable
fun MyPlayer() {
  val hlsUri = Uri.parse("https://some.website/video.m3u8")
  val context = LocalContext.current
  val player: ExoPlayer = remember {
    ExoPlayer.Builder(context)
        .build()
        .apply {
            setMediaItem(MediaItem.fromUri(hlsUri))
            prepare()
        }
    }

    AndroidView(
        factory = {
            PlayerView(context).apply {
                this.player = player
                layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
            }
        }
    )
}

Dependencies:

# gradle/libs.versions.toml

androidXMedia = "1.0.2"
androidx-media-player = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "androidXMedia" }
androidx-media-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "androidXMedia" }
androidx-media-hls = { group = "androidx.media3", name = "media3-exoplayer-hls", version.ref = "androidXMedia" }
// app/build.gradle.kts

dependencies {
    // ...
    implementation(libs.androidx.media.player)
    implementation(libs.androidx.media.ui)
    implementation(libs.androidx.media.hls)
}

Here is what the HLS file looks like:

#EXTM3U
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English",LANGUAGE="en",URI="https://.../playlist.m3u8",AUTOSELECT=YES,DEFAULT=NO
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Français",LANGUAGE="fr",URI="https://.../playlist.m3u8",AUTOSELECT=YES,DEFAULT=NO

#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-medium",NAME="audio",AUTOSELECT=YES,DEFAULT=YES,CHANNELS="2",URI="https://.../playlist.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio-high",NAME="audio",AUTOSELECT=YES,DEFAULT=YES,CHANNELS="2",URI="https://.../playlist.m3u8"

#EXT-X-STREAM-INF:SUBTITLES="subs",BANDWIDTH=2255226,AVERAGE-BANDWIDTH=1578000,RESOLUTION=1280x720,FRAME-RATE=24.000,CODECS="avc1.640020,mp4a.40.2",AUDIO="audio-high"
https://.../playlist.m3u8
#EXT-X-STREAM-INF:SUBTITLES="subs",BANDWIDTH=698963,AVERAGE-BANDWIDTH=537000,RESOLUTION=640x360,FRAME-RATE=24.000,CODECS="avc1.64001E,mp4a.40.2",AUDIO="audio-medium"
https://.../playlist.m3u8
#EXT-X-STREAM-INF:SUBTITLES="subs",BANDWIDTH=463242,AVERAGE-BANDWIDTH=332000,RESOLUTION=426x240,FRAME-RATE=24.000,CODECS="avc1.640015,mp4a.40.2",AUDIO="audio-medium"
https://.../playlist.m3u8
#EXT-X-STREAM-INF:SUBTITLES="subs",BANDWIDTH=5496152,AVERAGE-BANDWIDTH=4096000,RESOLUTION=1920x1080,FRAME-RATE=24.000,CODECS="avc1.640028,mp4a.40.2",AUDIO="audio-high"
https://.../playlist.m3u8
#EXT-X-STREAM-INF:SUBTITLES="subs",BANDWIDTH=1440054,AVERAGE-BANDWIDTH=965000,RESOLUTION=960x540,FRAME-RATE=24.000,CODECS="avc1.64001F,mp4a.40.2",AUDIO="audio-medium"
https://.../playlist.m3u8

Solution

  • I had the same problem, the solution I found was to use the setShowSubtitleButton function:

    AndroidView(
            factory = { context ->
                PlayerView(context).apply {
                    player = [email protected]
                    setShowSubtitleButton(true)
                }
            },
            modifier = modifier
    )
    

    The only problem is that it is an UnstableApi, in the function you need to add this annotation:

    @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
    

    The full code:

    @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class)
        @Composable
        private fun VideoPlayer(
            modifier: Modifier = Modifier
        ) {
            AndroidView(
                factory = { context ->
                    PlayerView(context).apply {
                        player = [email protected]
                        setShowSubtitleButton(true)
                    }
                },
                modifier = modifier
            )
        }