From 72ee0b4292c50d205583c3932abd4b01d2a6a286 Mon Sep 17 00:00:00 2001 From: Christophe Beyls Date: Wed, 17 Apr 2024 21:29:21 +0200 Subject: [PATCH] Enable support for WebVTT and TTML subtitles for the player (#4377) All subtitle formats used to be enabled by default in older versions of media3. After the media3 library was upgraded to 1.3.0, Extractors should specify a `SubtitleParser.Factory` explicitly. By default, no subtitle formats are supported when using the now deprecated empty constructor of Extractors. Limit support to **WebVTT** and **TTML** which are the only true web standards amongst all the subtitle formats supported by ExoPlayer. Note that only subtitles embedded in MP4 and WebM files are supported until Mastodon provides a way to upload subtitles separately. --- .../keylesspalace/tusky/di/PlayerModule.kt | 47 +++++++++++++++++-- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/di/PlayerModule.kt b/app/src/main/java/com/keylesspalace/tusky/di/PlayerModule.kt index 1156f4131..f12587fc5 100644 --- a/app/src/main/java/com/keylesspalace/tusky/di/PlayerModule.kt +++ b/app/src/main/java/com/keylesspalace/tusky/di/PlayerModule.kt @@ -21,6 +21,8 @@ import android.content.Context import android.os.Looper import androidx.annotation.OptIn import androidx.media3.common.C +import androidx.media3.common.Format +import androidx.media3.common.MimeTypes import androidx.media3.common.util.UnstableApi import androidx.media3.datasource.DataSource import androidx.media3.datasource.DefaultDataSource @@ -44,6 +46,10 @@ import androidx.media3.extractor.mp3.Mp3Extractor import androidx.media3.extractor.mp4.FragmentedMp4Extractor import androidx.media3.extractor.mp4.Mp4Extractor import androidx.media3.extractor.ogg.OggExtractor +import androidx.media3.extractor.text.SubtitleParser +import androidx.media3.extractor.text.ttml.TtmlParser +import androidx.media3.extractor.text.webvtt.Mp4WebvttParser +import androidx.media3.extractor.text.webvtt.WebvttParser import androidx.media3.extractor.wav.WavExtractor import dagger.Module import dagger.Provides @@ -98,17 +104,50 @@ object PlayerModule { } @Provides - fun provideExtractorsFactory(): ExtractorsFactory { + fun providesSubtitleParserFactory(): SubtitleParser.Factory { + return object : SubtitleParser.Factory { + override fun supportsFormat(format: Format): Boolean { + return when (format.sampleMimeType) { + MimeTypes.TEXT_VTT, + MimeTypes.APPLICATION_MP4VTT, + MimeTypes.APPLICATION_TTML -> true + + else -> false + } + } + + override fun getCueReplacementBehavior(format: Format): Int { + return when (val mimeType = format.sampleMimeType) { + MimeTypes.TEXT_VTT -> WebvttParser.CUE_REPLACEMENT_BEHAVIOR + MimeTypes.APPLICATION_MP4VTT -> Mp4WebvttParser.CUE_REPLACEMENT_BEHAVIOR + MimeTypes.APPLICATION_TTML -> TtmlParser.CUE_REPLACEMENT_BEHAVIOR + else -> throw IllegalArgumentException("Unsupported MIME type: $mimeType") + } + } + + override fun create(format: Format): SubtitleParser { + return when (val mimeType = format.sampleMimeType) { + MimeTypes.TEXT_VTT -> WebvttParser() + MimeTypes.APPLICATION_MP4VTT -> Mp4WebvttParser() + MimeTypes.APPLICATION_TTML -> TtmlParser() + else -> throw IllegalArgumentException("Unsupported MIME type: $mimeType") + } + } + } + } + + @Provides + fun provideExtractorsFactory(subtitleParserFactory: SubtitleParser.Factory): ExtractorsFactory { // Extractors order is optimized according to // https://docs.google.com/document/d/1w2mKaWMxfz2Ei8-LdxqbPs1VLe_oudB-eryXXw9OvQQ return ExtractorsFactory { arrayOf( FlacExtractor(), WavExtractor(), - Mp4Extractor(), - FragmentedMp4Extractor(), + Mp4Extractor(subtitleParserFactory), + FragmentedMp4Extractor(subtitleParserFactory), OggExtractor(), - MatroskaExtractor(), + MatroskaExtractor(subtitleParserFactory), Mp3Extractor() ) }