diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/MediaParserChunkExtractor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/MediaParserChunkExtractor.java index 8aed8adb9c..8467fdb3ac 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/MediaParserChunkExtractor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/MediaParserChunkExtractor.java @@ -15,6 +15,7 @@ */ package androidx.media3.exoplayer.source.chunk; +import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.exoplayer.source.mediaparser.MediaParserUtil.PARAMETER_EAGERLY_EXPOSE_TRACK_TYPE; import static androidx.media3.exoplayer.source.mediaparser.MediaParserUtil.PARAMETER_EXPOSE_CAPTION_FORMATS; import static androidx.media3.exoplayer.source.mediaparser.MediaParserUtil.PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT; @@ -44,6 +45,10 @@ import androidx.media3.extractor.ExtractorInput; import androidx.media3.extractor.ExtractorOutput; import androidx.media3.extractor.SeekMap; import androidx.media3.extractor.TrackOutput; +import androidx.media3.extractor.text.DefaultSubtitleParserFactory; +import androidx.media3.extractor.text.SubtitleExtractor; +import androidx.media3.extractor.text.SubtitleParser; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -56,22 +61,87 @@ public final class MediaParserChunkExtractor implements ChunkExtractor { // Maximum TAG length is 23 characters. private static final String TAG = "MediaPrsrChunkExtractor"; - public static final ChunkExtractor.Factory FACTORY = - (primaryTrackType, - format, - enableEventMessageTrack, - closedCaptionFormats, - playerEmsgTrackOutput, - playerId) -> { - if (!MimeTypes.isText(format.containerMimeType)) { - // Container is either Matroska or Fragmented MP4. - return new MediaParserChunkExtractor( - primaryTrackType, format, closedCaptionFormats, playerId); - } else { - // This is a text track that does not require an extractor. + /** A {@link ChunkExtractor.Factory} for {@link MediaParserChunkExtractor} instances. */ + public static class Factory implements ChunkExtractor.Factory { + + private SubtitleParser.Factory subtitleParserFactory; + private boolean parseSubtitlesDuringExtraction; + + public Factory() { + subtitleParserFactory = new DefaultSubtitleParserFactory(); + } + + @CanIgnoreReturnValue + @Override + public Factory setSubtitleParserFactory(SubtitleParser.Factory subtitleParserFactory) { + this.subtitleParserFactory = checkNotNull(subtitleParserFactory); + return this; + } + + @CanIgnoreReturnValue + @Override + public Factory experimentalParseSubtitlesDuringExtraction( + boolean parseSubtitlesDuringExtraction) { + this.parseSubtitlesDuringExtraction = parseSubtitlesDuringExtraction; + return this; + } + + /** + * {@inheritDoc} + * + *
This implementation performs transcoding of the original format to {@link + * MimeTypes#APPLICATION_MEDIA3_CUES} if it is supported by {@link SubtitleParser.Factory}. + * + *
To modify the support behavior, you can {@linkplain
+ * #setSubtitleParserFactory(SubtitleParser.Factory) set your own subtitle parser factory}.
+ */
+ @Override
+ public Format getOutputTextFormat(Format sourceFormat) {
+ if (parseSubtitlesDuringExtraction && subtitleParserFactory.supportsFormat(sourceFormat)) {
+ return sourceFormat
+ .buildUpon()
+ .setSampleMimeType(MimeTypes.APPLICATION_MEDIA3_CUES)
+ .setCueReplacementBehavior(
+ subtitleParserFactory.getCueReplacementBehavior(sourceFormat))
+ .setCodecs(
+ sourceFormat.sampleMimeType
+ + (sourceFormat.codecs != null ? " " + sourceFormat.codecs : ""))
+ .setSubsampleOffsetUs(Format.OFFSET_SAMPLE_RELATIVE)
+ .build();
+ } else {
+ return sourceFormat;
+ }
+ }
+
+ @Nullable
+ @Override
+ public ChunkExtractor createProgressiveMediaExtractor(
+ @C.TrackType int primaryTrackType,
+ Format representationFormat,
+ boolean enableEventMessageTrack,
+ List