From 4d7b23f0d117a67df33f5cab25d5971a72dc8017 Mon Sep 17 00:00:00 2001 From: jbibik Date: Wed, 24 Jan 2024 09:52:48 -0800 Subject: [PATCH] Add setters of SubtitleParser.Factory and experimental toggle The `SubtitleParser.Factory` is no longer @Nullable and the experimenting toggle is used to enable/disable the use of this factory for subtitle parsing during extraction. The three places that will hold the "truth" for the `SubtitleParser.Factory` are: BundledChunkExtractor.Factory, SsChunkSource.Factory, DefaultHlsExtractorFactory DASH: `DashMediaSource.Factory` would only propagate it to `DashChunkSource.Factory` -> `BundledChunkExtractor.Factory` SS: `SSMediaSource.Factory` -> `SsChunkSource.Factory` HLS: `HlsMediaSource.Factory` -> `HlsExtractorFactory` #minor-release PiperOrigin-RevId: 601151615 --- RELEASENOTES.md | 5 ++ .../source/chunk/BundledChunkExtractor.java | 67 +++++++--------- .../source/chunk/ChunkExtractor.java | 64 +++++++++++---- .../exoplayer/dash/DashChunkSource.java | 32 ++++++++ .../exoplayer/dash/DashMediaSource.java | 27 +------ .../dash/DefaultDashChunkSource.java | 26 +++--- .../hls/BundledHlsMediaChunkExtractor.java | 28 +++---- .../hls/DefaultHlsExtractorFactory.java | 80 ++++++++++--------- .../exoplayer/hls/HlsExtractorFactory.java | 33 ++++++++ .../media3/exoplayer/hls/HlsMediaSource.java | 29 +------ .../smoothstreaming/DefaultSsChunkSource.java | 51 +++++++----- .../smoothstreaming/SsChunkSource.java | 32 ++++++++ .../smoothstreaming/SsMediaSource.java | 28 +------ .../DefaultSsChunkSourceTest.java | 4 +- .../smoothstreaming/SsMediaSourceTest.java | 11 ++- 15 files changed, 297 insertions(+), 220 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 92ea1bcfbe..787b026ed1 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -15,6 +15,11 @@ implementing a custom `CompositeSequenceableLoaderFactory`. * Fix issue where repeating the same time causes metadata from this item to be cleared ([#1007](https://github.com/androidx/media/issues/1007)). + * Rename `experimentalSetSubtitleParserFactory` methods on + `BundledChunkExtractor.Factory` and `DefaultHlsExtractorFactory` to + `setSubtitleParserFactory` and disallow passing `null`. Use the new + `experimentalParseSubtitlesDuringExtraction(boolean)` methods to control + parsing behaviour. * Transformer: * Track Selection: * Extractors: diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/BundledChunkExtractor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/BundledChunkExtractor.java index 5305a0945c..0ec3815bfa 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/BundledChunkExtractor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/BundledChunkExtractor.java @@ -15,6 +15,7 @@ */ package androidx.media3.exoplayer.source.chunk; +import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Util.castNonNull; import android.util.SparseArray; @@ -39,9 +40,11 @@ import androidx.media3.extractor.jpeg.JpegExtractor; import androidx.media3.extractor.mkv.MatroskaExtractor; import androidx.media3.extractor.mp4.FragmentedMp4Extractor; import androidx.media3.extractor.png.PngExtractor; +import androidx.media3.extractor.text.DefaultSubtitleParserFactory; import androidx.media3.extractor.text.SubtitleExtractor; import androidx.media3.extractor.text.SubtitleParser; import androidx.media3.extractor.text.SubtitleTranscodingExtractor; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.util.List; import java.util.Objects; @@ -57,24 +60,25 @@ public final class BundledChunkExtractor implements ExtractorOutput, ChunkExtrac /** {@link ChunkExtractor.Factory} for {@link BundledChunkExtractor}. */ public static final class Factory implements ChunkExtractor.Factory { - /** Non-null if subtitles should be parsed during extraction, null otherwise. */ - @Nullable private SubtitleParser.Factory subtitleParserFactory; + private SubtitleParser.Factory subtitleParserFactory; + private boolean parseSubtitlesDuringExtraction; - /** - * Sets the {@link SubtitleParser.Factory} to use for parsing subtitles during extraction, or - * null to parse subtitles during decoding. The default is null (subtitles parsed after - * decoding). - * - *

This method is experimental. Its default value may change, or it may be renamed or removed - * in a future release. - * - * @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during - * extraction. - * @return This factory, for convenience. - */ - public Factory experimentalSetSubtitleParserFactory( - @Nullable SubtitleParser.Factory subtitleParserFactory) { - this.subtitleParserFactory = subtitleParserFactory; + 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; } @@ -85,18 +89,16 @@ public final class BundledChunkExtractor implements ExtractorOutput, ChunkExtrac * MimeTypes#APPLICATION_MEDIA3_CUES} if it is supported by {@link SubtitleParser.Factory}. * *

To modify the support behavior, you can {@linkplain - * #experimentalSetSubtitleParserFactory(SubtitleParser.Factory) set your own subtitle parser - * factory}. + * #setSubtitleParserFactory(SubtitleParser.Factory) set your own subtitle parser factory}. */ @Override public Format getOutputTextFormat(Format sourceFormat) { - if (subtitleParserFactory != null && subtitleParserFactory.supportsFormat(sourceFormat)) { - @Format.CueReplacementBehavior - int cueReplacementBehavior = subtitleParserFactory.getCueReplacementBehavior(sourceFormat); + if (parseSubtitlesDuringExtraction && subtitleParserFactory.supportsFormat(sourceFormat)) { return sourceFormat .buildUpon() .setSampleMimeType(MimeTypes.APPLICATION_MEDIA3_CUES) - .setCueReplacementBehavior(cueReplacementBehavior) + .setCueReplacementBehavior( + subtitleParserFactory.getCueReplacementBehavior(sourceFormat)) .setCodecs( sourceFormat.sampleMimeType + (sourceFormat.codecs != null ? " " + sourceFormat.codecs : "")) @@ -119,7 +121,7 @@ public final class BundledChunkExtractor implements ExtractorOutput, ChunkExtrac @Nullable String containerMimeType = representationFormat.containerMimeType; Extractor extractor; if (MimeTypes.isText(containerMimeType)) { - if (subtitleParserFactory == null) { + if (!parseSubtitlesDuringExtraction) { // Subtitles will be parsed after decoding return null; } else { @@ -129,15 +131,10 @@ public final class BundledChunkExtractor implements ExtractorOutput, ChunkExtrac } } else if (MimeTypes.isMatroska(containerMimeType)) { @MatroskaExtractor.Flags int flags = MatroskaExtractor.FLAG_DISABLE_SEEK_FOR_CUES; - if (subtitleParserFactory == null) { + if (!parseSubtitlesDuringExtraction) { flags |= MatroskaExtractor.FLAG_EMIT_RAW_SUBTITLE_DATA; } - extractor = - new MatroskaExtractor( - subtitleParserFactory != null - ? subtitleParserFactory - : SubtitleParser.Factory.UNSUPPORTED, - flags); + extractor = new MatroskaExtractor(subtitleParserFactory, flags); } else if (Objects.equals(containerMimeType, MimeTypes.IMAGE_JPEG)) { extractor = new JpegExtractor(JpegExtractor.FLAG_READ_IMAGE); } else if (Objects.equals(containerMimeType, MimeTypes.IMAGE_PNG)) { @@ -147,21 +144,19 @@ public final class BundledChunkExtractor implements ExtractorOutput, ChunkExtrac if (enableEventMessageTrack) { flags |= FragmentedMp4Extractor.FLAG_ENABLE_EMSG_TRACK; } - if (subtitleParserFactory == null) { + if (!parseSubtitlesDuringExtraction) { flags |= FragmentedMp4Extractor.FLAG_EMIT_RAW_SUBTITLE_DATA; } extractor = new FragmentedMp4Extractor( - subtitleParserFactory != null - ? subtitleParserFactory - : SubtitleParser.Factory.UNSUPPORTED, + subtitleParserFactory, flags, /* timestampAdjuster= */ null, /* sideloadedTrack= */ null, closedCaptionFormats, playerEmsgTrackOutput); } - if (subtitleParserFactory != null + if (parseSubtitlesDuringExtraction && !MimeTypes.isText(containerMimeType) && !(extractor.getUnderlyingImplementation() instanceof FragmentedMp4Extractor) && !(extractor.getUnderlyingImplementation() instanceof MatroskaExtractor)) { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkExtractor.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkExtractor.java index 35ad0caff0..b6c28fc934 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkExtractor.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/chunk/ChunkExtractor.java @@ -25,6 +25,8 @@ import androidx.media3.extractor.ChunkIndex; import androidx.media3.extractor.Extractor; import androidx.media3.extractor.ExtractorInput; import androidx.media3.extractor.TrackOutput; +import androidx.media3.extractor.text.SubtitleParser; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.util.List; @@ -41,24 +43,34 @@ public interface ChunkExtractor { interface Factory { /** - * Returns a new {@link ChunkExtractor} instance. + * Sets the {@link SubtitleParser.Factory} to use for parsing subtitles during extraction. The + * default factory value is implementation dependent. * - * @param primaryTrackType The {@link C.TrackType type} of the primary track. - * @param representationFormat The format of the representation to extract from. - * @param enableEventMessageTrack Whether to enable the event message track. - * @param closedCaptionFormats The {@link Format Formats} of the Closed-Caption tracks. - * @param playerEmsgTrackOutput The {@link TrackOutput} for extracted EMSG messages, or null. - * @param playerId The {@link PlayerId} of the player using this chunk extractor. - * @return A new {@link ChunkExtractor} instance, or null if not applicable. + * @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during + * extraction. + * @return This factory, for convenience. */ - @Nullable - ChunkExtractor createProgressiveMediaExtractor( - @C.TrackType int primaryTrackType, - Format representationFormat, - boolean enableEventMessageTrack, - List closedCaptionFormats, - @Nullable TrackOutput playerEmsgTrackOutput, - PlayerId playerId); + @CanIgnoreReturnValue + default Factory setSubtitleParserFactory(SubtitleParser.Factory subtitleParserFactory) { + return this; + } + + /** + * Sets whether subtitles should be parsed as part of extraction (before being added to the + * sample queue) or as part of rendering (when being taken from the sample queue). Defaults to + * {@code false} (i.e. subtitles will be parsed as part of rendering). + * + *

This method is experimental and will be renamed or removed in a future release. + * + * @param parseSubtitlesDuringExtraction Whether to parse subtitles during extraction or + * rendering. + * @return This factory, for convenience. + */ + @CanIgnoreReturnValue + default Factory experimentalParseSubtitlesDuringExtraction( + boolean parseSubtitlesDuringExtraction) { + return this; + } /** * Returns the output {@link Format} of emitted {@linkplain C#TRACK_TYPE_TEXT text samples} @@ -79,6 +91,26 @@ public interface ChunkExtractor { default Format getOutputTextFormat(Format sourceFormat) { return sourceFormat; } + + /** + * Returns a new {@link ChunkExtractor} instance. + * + * @param primaryTrackType The {@link C.TrackType type} of the primary track. + * @param representationFormat The format of the representation to extract from. + * @param enableEventMessageTrack Whether to enable the event message track. + * @param closedCaptionFormats The {@link Format Formats} of the Closed-Caption tracks. + * @param playerEmsgTrackOutput The {@link TrackOutput} for extracted EMSG messages, or null. + * @param playerId The {@link PlayerId} of the player using this chunk extractor. + * @return A new {@link ChunkExtractor} instance, or null if not applicable. + */ + @Nullable + ChunkExtractor createProgressiveMediaExtractor( + @C.TrackType int primaryTrackType, + Format representationFormat, + boolean enableEventMessageTrack, + List closedCaptionFormats, + @Nullable TrackOutput playerEmsgTrackOutput, + PlayerId playerId); } /** Provides {@link TrackOutput} instances to be written to during extraction. */ diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashChunkSource.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashChunkSource.java index 7e4b40401b..7f48712dfa 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashChunkSource.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashChunkSource.java @@ -30,6 +30,8 @@ import androidx.media3.exoplayer.trackselection.ExoTrackSelection; import androidx.media3.exoplayer.upstream.CmcdConfiguration; import androidx.media3.exoplayer.upstream.LoaderErrorThrower; import androidx.media3.extractor.Extractor; +import androidx.media3.extractor.text.SubtitleParser; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.util.List; /** A {@link ChunkSource} for DASH streams. */ @@ -39,6 +41,36 @@ public interface DashChunkSource extends ChunkSource { /** Factory for {@link DashChunkSource}s. */ interface Factory { + /** + * Sets the {@link SubtitleParser.Factory} to use for parsing subtitles during extraction. The + * default factory value is implementation dependent. + * + * @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during + * extraction. + * @return This factory, for convenience. + */ + @CanIgnoreReturnValue + default Factory setSubtitleParserFactory(SubtitleParser.Factory subtitleParserFactory) { + return this; + } + + /** + * Sets whether subtitles should be parsed as part of extraction (before being added to the + * sample queue) or as part of rendering (when being taken from the sample queue). Defaults to + * {@code false} (i.e. subtitles will be parsed as part of rendering). + * + *

This method is experimental and will be renamed or removed in a future release. + * + * @param parseSubtitlesDuringExtraction Whether to parse subtitles during extraction or + * rendering. + * @return This factory, for convenience. + */ + @CanIgnoreReturnValue + default Factory experimentalParseSubtitlesDuringExtraction( + boolean parseSubtitlesDuringExtraction) { + return this; + } + /** * @param manifestLoaderErrorThrower Throws errors affecting loading of manifests. * @param manifest The initial manifest. diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java index 59526b93a6..6a1527d8bc 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DashMediaSource.java @@ -77,8 +77,6 @@ import androidx.media3.exoplayer.upstream.Loader.LoadErrorAction; import androidx.media3.exoplayer.upstream.LoaderErrorThrower; import androidx.media3.exoplayer.upstream.ParsingLoadable; import androidx.media3.exoplayer.util.SntpClient; -import androidx.media3.extractor.text.DefaultSubtitleParserFactory; -import androidx.media3.extractor.text.SubtitleParser; import com.google.common.base.Charsets; import com.google.common.math.LongMath; import com.google.errorprone.annotations.CanIgnoreReturnValue; @@ -114,7 +112,6 @@ public final class DashMediaSource extends BaseMediaSource { private DrmSessionManagerProvider drmSessionManagerProvider; private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory; private LoadErrorHandlingPolicy loadErrorHandlingPolicy; - @Nullable private SubtitleParser.Factory subtitleParserFactory; private long fallbackTargetLiveOffsetMs; private long minLiveStartPositionUs; @Nullable private ParsingLoadable.Parser manifestParser; @@ -199,33 +196,11 @@ public final class DashMediaSource extends BaseMediaSource { return this; } - /** - * {@inheritDoc} - * - *

This method may only be used with {@link DefaultDashChunkSource.Factory}. - * - * @param parseSubtitlesDuringExtraction Whether to parse subtitles during extraction or - * rendering. - * @return This factory, for convenience. - */ - // TODO: b/289916598 - Flip the default of this to true. @Override @CanIgnoreReturnValue public Factory experimentalParseSubtitlesDuringExtraction( boolean parseSubtitlesDuringExtraction) { - if (parseSubtitlesDuringExtraction) { - if (subtitleParserFactory == null) { - this.subtitleParserFactory = new DefaultSubtitleParserFactory(); - } - } else { - this.subtitleParserFactory = null; - } - if (chunkSourceFactory instanceof DefaultDashChunkSource.Factory) { - ((DefaultDashChunkSource.Factory) chunkSourceFactory) - .setSubtitleParserFactory(subtitleParserFactory); - } else { - throw new IllegalStateException(); - } + chunkSourceFactory.experimentalParseSubtitlesDuringExtraction(parseSubtitlesDuringExtraction); return this; } diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java index 8d542f3566..2adcbf2046 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/DefaultDashChunkSource.java @@ -63,6 +63,7 @@ import androidx.media3.exoplayer.upstream.LoaderErrorThrower; import androidx.media3.extractor.ChunkIndex; import androidx.media3.extractor.text.SubtitleParser; import com.google.common.collect.ImmutableMap; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -113,18 +114,19 @@ public class DefaultDashChunkSource implements DashChunkSource { this.maxSegmentsPerLoad = maxSegmentsPerLoad; } - /** - * Sets the {@link SubtitleParser.Factory} to be used for parsing subtitles during extraction, - * or null to parse subtitles during decoding. - * - *

This may only be used with {@link BundledChunkExtractor.Factory}. - */ - /* package */ Factory setSubtitleParserFactory( - @Nullable SubtitleParser.Factory subtitleParserFactory) { - if (chunkExtractorFactory instanceof BundledChunkExtractor.Factory) { - ((BundledChunkExtractor.Factory) chunkExtractorFactory) - .experimentalSetSubtitleParserFactory(subtitleParserFactory); - } + @CanIgnoreReturnValue + @Override + public Factory setSubtitleParserFactory(SubtitleParser.Factory subtitleParserFactory) { + chunkExtractorFactory.setSubtitleParserFactory(subtitleParserFactory); + return this; + } + + @CanIgnoreReturnValue + @Override + public Factory experimentalParseSubtitlesDuringExtraction( + boolean parseSubtitlesDuringExtraction) { + chunkExtractorFactory.experimentalParseSubtitlesDuringExtraction( + parseSubtitlesDuringExtraction); return this; } diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/BundledHlsMediaChunkExtractor.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/BundledHlsMediaChunkExtractor.java index 9b936b65d1..9d4f4cbb25 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/BundledHlsMediaChunkExtractor.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/BundledHlsMediaChunkExtractor.java @@ -17,7 +17,6 @@ package androidx.media3.exoplayer.hls; import static androidx.media3.common.util.Assertions.checkState; -import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.media3.common.Format; import androidx.media3.common.util.TimestampAdjuster; @@ -47,7 +46,8 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract @VisibleForTesting /* package */ final Extractor extractor; private final Format multivariantPlaylistFormat; private final TimestampAdjuster timestampAdjuster; - @Nullable private final SubtitleParser.Factory subtitleParserFactory; + private final SubtitleParser.Factory subtitleParserFactory; + private final boolean parseSubtitlesDuringExtraction; /** * Creates a new instance. @@ -62,7 +62,8 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract extractor, multivariantPlaylistFormat, timestampAdjuster, - /* subtitleParserFactory= */ null); + SubtitleParser.Factory.UNSUPPORTED, + /* parseSubtitlesDuringExtraction= */ false); } /** @@ -77,16 +78,18 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract * multivariantPlaylistFormat. */ // TODO(b/289983417): Once the subtitle-parsing-during-extraction is the only available flow, make - // this constructor public and remove @Nullable from subtitleParserFactory + // this constructor public and remove parseSubtitlesDuringExtraction parameter /* package */ BundledHlsMediaChunkExtractor( Extractor extractor, Format multivariantPlaylistFormat, TimestampAdjuster timestampAdjuster, - @Nullable SubtitleParser.Factory subtitleParserFactory) { + SubtitleParser.Factory subtitleParserFactory, + boolean parseSubtitlesDuringExtraction) { this.extractor = extractor; this.multivariantPlaylistFormat = multivariantPlaylistFormat; this.timestampAdjuster = timestampAdjuster; this.subtitleParserFactory = subtitleParserFactory; + this.parseSubtitlesDuringExtraction = parseSubtitlesDuringExtraction; } @Override @@ -124,18 +127,11 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract Extractor newExtractorInstance; // LINT.IfChange(extractor_instantiation) if (extractor instanceof WebvttExtractor) { - SubtitleParser.Factory webvttSubtitleParserFactory = SubtitleParser.Factory.UNSUPPORTED; - boolean parseSubtitlesDuringExtraction = false; - if (subtitleParserFactory != null - && subtitleParserFactory.supportsFormat(multivariantPlaylistFormat)) { - webvttSubtitleParserFactory = subtitleParserFactory; - parseSubtitlesDuringExtraction = true; - } newExtractorInstance = new WebvttExtractor( multivariantPlaylistFormat.language, timestampAdjuster, - webvttSubtitleParserFactory, + subtitleParserFactory, parseSubtitlesDuringExtraction); } else if (extractor instanceof AdtsExtractor) { newExtractorInstance = new AdtsExtractor(); @@ -150,7 +146,11 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract "Unexpected extractor type for recreation: " + extractor.getClass().getSimpleName()); } return new BundledHlsMediaChunkExtractor( - newExtractorInstance, multivariantPlaylistFormat, timestampAdjuster, subtitleParserFactory); + newExtractorInstance, + multivariantPlaylistFormat, + timestampAdjuster, + subtitleParserFactory, + parseSubtitlesDuringExtraction); } @Override diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java index 8e8b56b376..67bfff3247 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/DefaultHlsExtractorFactory.java @@ -33,6 +33,7 @@ import androidx.media3.extractor.Extractor; import androidx.media3.extractor.ExtractorInput; import androidx.media3.extractor.mp3.Mp3Extractor; import androidx.media3.extractor.mp4.FragmentedMp4Extractor; +import androidx.media3.extractor.text.DefaultSubtitleParserFactory; import androidx.media3.extractor.text.SubtitleParser; import androidx.media3.extractor.ts.Ac3Extractor; import androidx.media3.extractor.ts.Ac4Extractor; @@ -41,6 +42,7 @@ import androidx.media3.extractor.ts.DefaultTsPayloadReaderFactory; import androidx.media3.extractor.ts.TsExtractor; import com.google.common.collect.ImmutableList; import com.google.common.primitives.Ints; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.EOFException; import java.io.IOException; import java.util.ArrayList; @@ -67,8 +69,8 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { private final @DefaultTsPayloadReaderFactory.Flags int payloadReaderFactoryFlags; - /** Non-null if subtitles should be parsed during extraction, null otherwise. */ - @Nullable private SubtitleParser.Factory subtitleParserFactory; + private SubtitleParser.Factory subtitleParserFactory; + private boolean parseSubtitlesDuringExtraction; private final boolean exposeCea608WhenMissingDeclarations; @@ -96,6 +98,7 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { int payloadReaderFactoryFlags, boolean exposeCea608WhenMissingDeclarations) { this.payloadReaderFactoryFlags = payloadReaderFactoryFlags; this.exposeCea608WhenMissingDeclarations = exposeCea608WhenMissingDeclarations; + subtitleParserFactory = new DefaultSubtitleParserFactory(); } @Override @@ -135,7 +138,11 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { createExtractorByFileType(fileType, format, muxedCaptionFormats, timestampAdjuster)); if (sniffQuietly(extractor, sniffingExtractorInput)) { return new BundledHlsMediaChunkExtractor( - extractor, format, timestampAdjuster, subtitleParserFactory); + extractor, + format, + timestampAdjuster, + subtitleParserFactory, + parseSubtitlesDuringExtraction); } if (fallBackExtractor == null && (fileType == formatInferredFileType @@ -149,26 +156,29 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { } return new BundledHlsMediaChunkExtractor( - checkNotNull(fallBackExtractor), format, timestampAdjuster, subtitleParserFactory); + checkNotNull(fallBackExtractor), + format, + timestampAdjuster, + subtitleParserFactory, + parseSubtitlesDuringExtraction); } - /** - * Sets the {@link SubtitleParser.Factory} to use for parsing subtitles during extraction, or null - * to parse subtitles during decoding. The default is null (subtitles parsed after decoding). - * - *

This method is experimental. Its default value may change, or it may be renamed or removed - * in a future release. - * - * @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during - * extraction. - * @return This factory, for convenience. - */ - public DefaultHlsExtractorFactory experimentalSetSubtitleParserFactory( - @Nullable SubtitleParser.Factory subtitleParserFactory) { + @CanIgnoreReturnValue + @Override + public DefaultHlsExtractorFactory setSubtitleParserFactory( + SubtitleParser.Factory subtitleParserFactory) { this.subtitleParserFactory = subtitleParserFactory; return this; } + @CanIgnoreReturnValue + @Override + public DefaultHlsExtractorFactory experimentalParseSubtitlesDuringExtraction( + boolean parseSubtitlesDuringExtraction) { + this.parseSubtitlesDuringExtraction = parseSubtitlesDuringExtraction; + return this; + } + /** * {@inheritDoc} * @@ -176,18 +186,15 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { * MimeTypes#APPLICATION_MEDIA3_CUES} if it is supported by {@link SubtitleParser.Factory}. * *

To modify the support behavior, you can {@linkplain - * #experimentalSetSubtitleParserFactory(SubtitleParser.Factory) set your own subtitle parser - * factory}. + * #setSubtitleParserFactory(SubtitleParser.Factory) set your own subtitle parser factory}. */ @Override public Format getOutputTextFormat(Format sourceFormat) { - if (subtitleParserFactory != null && subtitleParserFactory.supportsFormat(sourceFormat)) { - @Format.CueReplacementBehavior - int cueReplacementBehavior = subtitleParserFactory.getCueReplacementBehavior(sourceFormat); + if (parseSubtitlesDuringExtraction && subtitleParserFactory.supportsFormat(sourceFormat)) { return sourceFormat .buildUpon() .setSampleMimeType(MimeTypes.APPLICATION_MEDIA3_CUES) - .setCueReplacementBehavior(cueReplacementBehavior) + .setCueReplacementBehavior(subtitleParserFactory.getCueReplacementBehavior(sourceFormat)) .setCodecs( sourceFormat.sampleMimeType + (sourceFormat.codecs != null ? " " + sourceFormat.codecs : "")) @@ -216,16 +223,10 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { // LINT.IfChange(extractor_instantiation) switch (fileType) { case FileTypes.WEBVTT: - SubtitleParser.Factory webvttSubtitleParserFactory = SubtitleParser.Factory.UNSUPPORTED; - boolean parseSubtitlesDuringExtraction = false; - if (subtitleParserFactory != null && subtitleParserFactory.supportsFormat(format)) { - webvttSubtitleParserFactory = subtitleParserFactory; - parseSubtitlesDuringExtraction = true; - } return new WebvttExtractor( format.language, timestampAdjuster, - webvttSubtitleParserFactory, + subtitleParserFactory, parseSubtitlesDuringExtraction); case FileTypes.ADTS: return new AdtsExtractor(); @@ -237,7 +238,11 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { return new Mp3Extractor(/* flags= */ 0, /* forcedFirstSampleTimestampUs= */ 0); case FileTypes.MP4: return createFragmentedMp4Extractor( - subtitleParserFactory, timestampAdjuster, format, muxedCaptionFormats); + subtitleParserFactory, + parseSubtitlesDuringExtraction, + timestampAdjuster, + format, + muxedCaptionFormats); case FileTypes.TS: return createTsExtractor( payloadReaderFactoryFlags, @@ -245,7 +250,8 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { format, muxedCaptionFormats, timestampAdjuster, - subtitleParserFactory); + subtitleParserFactory, + parseSubtitlesDuringExtraction); default: return null; } @@ -257,7 +263,8 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { Format format, @Nullable List muxedCaptionFormats, TimestampAdjuster timestampAdjuster, - @Nullable SubtitleParser.Factory subtitleParserFactory) { + SubtitleParser.Factory subtitleParserFactory, + boolean parseSubtitlesDuringExtraction) { @DefaultTsPayloadReaderFactory.Flags int payloadReaderFactoryFlags = DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM @@ -287,7 +294,7 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { } } @TsExtractor.Flags int extractorFlags = 0; - if (subtitleParserFactory == null) { + if (!parseSubtitlesDuringExtraction) { subtitleParserFactory = SubtitleParser.Factory.UNSUPPORTED; extractorFlags |= TsExtractor.FLAG_EMIT_RAW_SUBTITLE_DATA; } @@ -301,7 +308,8 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { } private static FragmentedMp4Extractor createFragmentedMp4Extractor( - @Nullable SubtitleParser.Factory subtitleParserFactory, + SubtitleParser.Factory subtitleParserFactory, + boolean parseSubtitlesDuringExtraction, TimestampAdjuster timestampAdjuster, Format format, @Nullable List muxedCaptionFormats) { @@ -309,7 +317,7 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { // creating a separate EMSG track for every audio track in a video stream. @FragmentedMp4Extractor.Flags int flags = isFmp4Variant(format) ? FragmentedMp4Extractor.FLAG_ENABLE_EMSG_TRACK : 0; - if (subtitleParserFactory == null) { + if (!parseSubtitlesDuringExtraction) { subtitleParserFactory = SubtitleParser.Factory.UNSUPPORTED; flags |= FragmentedMp4Extractor.FLAG_EMIT_RAW_SUBTITLE_DATA; } diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsExtractorFactory.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsExtractorFactory.java index 75ef496522..2f4e832c75 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsExtractorFactory.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsExtractorFactory.java @@ -26,6 +26,8 @@ import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.extractor.Extractor; import androidx.media3.extractor.ExtractorInput; import androidx.media3.extractor.PositionHolder; +import androidx.media3.extractor.text.SubtitleParser; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.util.List; import java.util.Map; @@ -63,6 +65,37 @@ public interface HlsExtractorFactory { PlayerId playerId) throws IOException; + /** + * Sets the {@link SubtitleParser.Factory} to use for parsing subtitles during extraction. The + * default factory value is implementation dependent. + * + * @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during + * extraction. + * @return This factory, for convenience. + */ + @CanIgnoreReturnValue + default HlsExtractorFactory setSubtitleParserFactory( + SubtitleParser.Factory subtitleParserFactory) { + return this; + } + + /** + * Sets whether subtitles should be parsed as part of extraction (before being added to the sample + * queue) or as part of rendering (when being taken from the sample queue). Defaults to {@code + * false} (i.e. subtitles will be parsed as part of rendering). + * + *

This method is experimental and will be renamed or removed in a future release. + * + * @param parseSubtitlesDuringExtraction Whether to parse subtitles during extraction or + * rendering. + * @return This factory, for convenience. + */ + @CanIgnoreReturnValue + default HlsExtractorFactory experimentalParseSubtitlesDuringExtraction( + boolean parseSubtitlesDuringExtraction) { + return this; + } + /** * Returns the output {@link Format} of emitted {@linkplain C#TRACK_TYPE_TEXT text samples} which * were originally in {@code sourceFormat}. diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaSource.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaSource.java index 6dd1366238..4cf377982e 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaSource.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/HlsMediaSource.java @@ -58,8 +58,6 @@ import androidx.media3.exoplayer.upstream.CmcdConfiguration; import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy; import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy; import androidx.media3.extractor.Extractor; -import androidx.media3.extractor.text.DefaultSubtitleParserFactory; -import androidx.media3.extractor.text.SubtitleParser; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.lang.annotation.Documented; @@ -113,7 +111,7 @@ public final class HlsMediaSource extends BaseMediaSource @Nullable private CmcdConfiguration.Factory cmcdConfigurationFactory; private DrmSessionManagerProvider drmSessionManagerProvider; private LoadErrorHandlingPolicy loadErrorHandlingPolicy; - @Nullable private SubtitleParser.Factory subtitleParserFactory; + private boolean allowChunklessPreparation; private @MetadataType int metadataType; private boolean useSessionKeys; @@ -199,32 +197,11 @@ public final class HlsMediaSource extends BaseMediaSource return this; } - /** - * {@inheritDoc} - * - *

This method may only be used with {@link DefaultHlsExtractorFactory}. - * - * @param parseSubtitlesDuringExtraction Whether to parse subtitles during extraction or - * rendering. - * @return This factory, for convenience. - */ - // TODO: b/289916598 - Flip the default of this to true. @Override + @CanIgnoreReturnValue public Factory experimentalParseSubtitlesDuringExtraction( boolean parseSubtitlesDuringExtraction) { - if (parseSubtitlesDuringExtraction) { - if (subtitleParserFactory == null) { - this.subtitleParserFactory = new DefaultSubtitleParserFactory(); - } - } else { - this.subtitleParserFactory = null; - } - if (extractorFactory instanceof DefaultHlsExtractorFactory) { - ((DefaultHlsExtractorFactory) extractorFactory) - .experimentalSetSubtitleParserFactory(subtitleParserFactory); - } else { - throw new IllegalStateException(); - } + extractorFactory.experimentalParseSubtitlesDuringExtraction(parseSubtitlesDuringExtraction); return this; } diff --git a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSource.java b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSource.java index 40cf102473..c907ba99a6 100644 --- a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSource.java +++ b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSource.java @@ -53,8 +53,10 @@ import androidx.media3.extractor.Extractor; import androidx.media3.extractor.mp4.FragmentedMp4Extractor; import androidx.media3.extractor.mp4.Track; import androidx.media3.extractor.mp4.TrackEncryptionBox; +import androidx.media3.extractor.text.DefaultSubtitleParserFactory; import androidx.media3.extractor.text.SubtitleParser; import com.google.common.collect.ImmutableList; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; import java.util.List; @@ -65,24 +67,29 @@ public class DefaultSsChunkSource implements SsChunkSource { public static final class Factory implements SsChunkSource.Factory { private final DataSource.Factory dataSourceFactory; - @Nullable private SubtitleParser.Factory subtitleParserFactory; + private SubtitleParser.Factory subtitleParserFactory; + private boolean parseSubtitlesDuringExtraction; public Factory(DataSource.Factory dataSourceFactory) { this.dataSourceFactory = dataSourceFactory; + subtitleParserFactory = new DefaultSubtitleParserFactory(); } - /** - * Sets the {@link SubtitleParser.Factory} to be used for parsing subtitles during extraction, - * or {@code null} to parse subtitles during decoding. - * - *

This may only be used with {@link BundledChunkExtractor.Factory}. - */ - /* package */ Factory setSubtitleParserFactory( - @Nullable SubtitleParser.Factory subtitleParserFactory) { + @CanIgnoreReturnValue + @Override + public Factory setSubtitleParserFactory(SubtitleParser.Factory subtitleParserFactory) { this.subtitleParserFactory = subtitleParserFactory; return this; } + @CanIgnoreReturnValue + @Override + public Factory experimentalParseSubtitlesDuringExtraction( + boolean parseSubtitlesDuringExtraction) { + this.parseSubtitlesDuringExtraction = parseSubtitlesDuringExtraction; + return this; + } + /** * {@inheritDoc} * @@ -91,13 +98,12 @@ public class DefaultSsChunkSource implements SsChunkSource { */ @Override public Format getOutputTextFormat(Format sourceFormat) { - if (subtitleParserFactory != null && subtitleParserFactory.supportsFormat(sourceFormat)) { - @Format.CueReplacementBehavior - int cueReplacementBehavior = subtitleParserFactory.getCueReplacementBehavior(sourceFormat); + if (parseSubtitlesDuringExtraction && subtitleParserFactory.supportsFormat(sourceFormat)) { return sourceFormat .buildUpon() .setSampleMimeType(MimeTypes.APPLICATION_MEDIA3_CUES) - .setCueReplacementBehavior(cueReplacementBehavior) + .setCueReplacementBehavior( + subtitleParserFactory.getCueReplacementBehavior(sourceFormat)) .setCodecs( sourceFormat.sampleMimeType + (sourceFormat.codecs != null ? " " + sourceFormat.codecs : "")) @@ -127,7 +133,8 @@ public class DefaultSsChunkSource implements SsChunkSource { trackSelection, dataSource, cmcdConfiguration, - subtitleParserFactory); + subtitleParserFactory, + parseSubtitlesDuringExtraction); } } @@ -158,6 +165,8 @@ public class DefaultSsChunkSource implements SsChunkSource { * @param cmcdConfiguration The {@link CmcdConfiguration} for this chunk source. * @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during * extraction. + * @param parseSubtitlesDuringExtraction Whether to parse subtitles during extraction or + * rendering. */ public DefaultSsChunkSource( LoaderErrorThrower manifestLoaderErrorThrower, @@ -166,7 +175,8 @@ public class DefaultSsChunkSource implements SsChunkSource { ExoTrackSelection trackSelection, DataSource dataSource, @Nullable CmcdConfiguration cmcdConfiguration, - @Nullable SubtitleParser.Factory subtitleParserFactory) { + SubtitleParser.Factory subtitleParserFactory, + boolean parseSubtitlesDuringExtraction) { this.manifestLoaderErrorThrower = manifestLoaderErrorThrower; this.manifest = manifest; this.streamElementIndex = streamElementIndex; @@ -203,14 +213,13 @@ public class DefaultSsChunkSource implements SsChunkSource { int flags = FragmentedMp4Extractor.FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME | FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_TFDT_BOX; + if (!parseSubtitlesDuringExtraction) { + flags |= FragmentedMp4Extractor.FLAG_EMIT_RAW_SUBTITLE_DATA; + } Extractor extractor = new FragmentedMp4Extractor( - subtitleParserFactory == null - ? SubtitleParser.Factory.UNSUPPORTED - : subtitleParserFactory, - subtitleParserFactory == null - ? flags | FragmentedMp4Extractor.FLAG_EMIT_RAW_SUBTITLE_DATA - : flags, + subtitleParserFactory, + flags, /* timestampAdjuster= */ null, track, /* closedCaptionFormats= */ ImmutableList.of(), diff --git a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/SsChunkSource.java b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/SsChunkSource.java index 4592ad9c2c..e2517958bf 100644 --- a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/SsChunkSource.java +++ b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/SsChunkSource.java @@ -27,6 +27,8 @@ import androidx.media3.exoplayer.trackselection.ExoTrackSelection; import androidx.media3.exoplayer.upstream.CmcdConfiguration; import androidx.media3.exoplayer.upstream.LoaderErrorThrower; import androidx.media3.extractor.Extractor; +import androidx.media3.extractor.text.SubtitleParser; +import com.google.errorprone.annotations.CanIgnoreReturnValue; /** A {@link ChunkSource} for SmoothStreaming. */ @UnstableApi @@ -55,6 +57,36 @@ public interface SsChunkSource extends ChunkSource { @Nullable TransferListener transferListener, @Nullable CmcdConfiguration cmcdConfiguration); + /** + * Sets the {@link SubtitleParser.Factory} to use for parsing subtitles during extraction. The + * default factory value is implementation dependent. + * + * @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during + * extraction. + * @return This factory, for convenience. + */ + @CanIgnoreReturnValue + default Factory setSubtitleParserFactory(SubtitleParser.Factory subtitleParserFactory) { + return this; + } + + /** + * Sets whether subtitles should be parsed as part of extraction (before being added to the + * sample queue) or as part of rendering (when being taken from the sample queue). Defaults to + * {@code false} (i.e. subtitles will be parsed as part of rendering). + * + *

This method is experimental and will be renamed or removed in a future release. + * + * @param parseSubtitlesDuringExtraction Whether to parse subtitles during extraction or + * rendering. + * @return This factory, for convenience. + */ + @CanIgnoreReturnValue + default Factory experimentalParseSubtitlesDuringExtraction( + boolean parseSubtitlesDuringExtraction) { + return this; + } + /** * Returns the output {@link Format} of emitted {@linkplain C#TRACK_TYPE_TEXT text samples} * which were originally in {@code sourceFormat}. diff --git a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/SsMediaSource.java b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/SsMediaSource.java index 50ecb5e838..ca252621f3 100644 --- a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/SsMediaSource.java +++ b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/SsMediaSource.java @@ -65,8 +65,6 @@ import androidx.media3.exoplayer.upstream.Loader; import androidx.media3.exoplayer.upstream.Loader.LoadErrorAction; import androidx.media3.exoplayer.upstream.LoaderErrorThrower; import androidx.media3.exoplayer.upstream.ParsingLoadable; -import androidx.media3.extractor.text.DefaultSubtitleParserFactory; -import androidx.media3.extractor.text.SubtitleParser; import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.IOException; @@ -93,7 +91,6 @@ public final class SsMediaSource extends BaseMediaSource @Nullable private CmcdConfiguration.Factory cmcdConfigurationFactory; private DrmSessionManagerProvider drmSessionManagerProvider; private LoadErrorHandlingPolicy loadErrorHandlingPolicy; - @Nullable private SubtitleParser.Factory subtitleParserFactory; private long livePresentationDelayMs; @Nullable private ParsingLoadable.Parser manifestParser; @@ -155,32 +152,11 @@ public final class SsMediaSource extends BaseMediaSource return this; } - /** - * {@inheritDoc} - * - *

This method may only be used with {@link DefaultSsChunkSource.Factory}. - * - * @param parseSubtitlesDuringExtraction Whether to parse subtitles during extraction or - * rendering. - * @return This factory, for convenience. - */ - // TODO: b/289916598 - Flip the default of this to true. @Override + @CanIgnoreReturnValue public Factory experimentalParseSubtitlesDuringExtraction( boolean parseSubtitlesDuringExtraction) { - if (parseSubtitlesDuringExtraction) { - if (subtitleParserFactory == null) { - this.subtitleParserFactory = new DefaultSubtitleParserFactory(); - } - } else { - this.subtitleParserFactory = null; - } - if (chunkSourceFactory instanceof DefaultSsChunkSource.Factory) { - ((DefaultSsChunkSource.Factory) chunkSourceFactory) - .setSubtitleParserFactory(subtitleParserFactory); - } else { - throw new IllegalStateException(); - } + chunkSourceFactory.experimentalParseSubtitlesDuringExtraction(parseSubtitlesDuringExtraction); return this; } diff --git a/libraries/exoplayer_smoothstreaming/src/test/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSourceTest.java b/libraries/exoplayer_smoothstreaming/src/test/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSourceTest.java index 3d6528f9b8..d04906cf70 100644 --- a/libraries/exoplayer_smoothstreaming/src/test/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSourceTest.java +++ b/libraries/exoplayer_smoothstreaming/src/test/java/androidx/media3/exoplayer/smoothstreaming/DefaultSsChunkSourceTest.java @@ -33,6 +33,7 @@ import androidx.media3.exoplayer.trackselection.AdaptiveTrackSelection; import androidx.media3.exoplayer.upstream.CmcdConfiguration; import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter; import androidx.media3.exoplayer.upstream.LoaderErrorThrower; +import androidx.media3.extractor.text.SubtitleParser; import androidx.media3.test.utils.FakeDataSource; import androidx.media3.test.utils.TestUtil; import androidx.test.core.app.ApplicationProvider; @@ -315,6 +316,7 @@ public class DefaultSsChunkSourceTest { adaptiveTrackSelection, new FakeDataSource(), cmcdConfiguration, - /* subtitleParserFactory= */ null); + SubtitleParser.Factory.UNSUPPORTED, + /* parseSubtitlesDuringExtraction= */ false); } } diff --git a/libraries/exoplayer_smoothstreaming/src/test/java/androidx/media3/exoplayer/smoothstreaming/SsMediaSourceTest.java b/libraries/exoplayer_smoothstreaming/src/test/java/androidx/media3/exoplayer/smoothstreaming/SsMediaSourceTest.java index bfacb484ae..eba35a5dfc 100644 --- a/libraries/exoplayer_smoothstreaming/src/test/java/androidx/media3/exoplayer/smoothstreaming/SsMediaSourceTest.java +++ b/libraries/exoplayer_smoothstreaming/src/test/java/androidx/media3/exoplayer/smoothstreaming/SsMediaSourceTest.java @@ -16,7 +16,6 @@ package androidx.media3.exoplayer.smoothstreaming; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import androidx.annotation.Nullable; @@ -33,6 +32,7 @@ import androidx.media3.exoplayer.source.MediaSource; import androidx.media3.exoplayer.trackselection.ExoTrackSelection; import androidx.media3.exoplayer.upstream.CmcdConfiguration; import androidx.media3.exoplayer.upstream.LoaderErrorThrower; +import androidx.media3.extractor.text.SubtitleParser; import androidx.media3.test.utils.FakeDataSource; import androidx.media3.test.utils.TestUtil; import androidx.media3.test.utils.robolectric.RobolectricUtil; @@ -104,14 +104,12 @@ public class SsMediaSourceTest { @Test public void - setExperimentalParseSubtitlesDuringExtraction_withNonDefaultChunkSourceFactory_setThrows() { + setExperimentalParseSubtitlesDuringExtraction_withNonDefaultChunkSourceFactory_setSucceeds() { SsMediaSource.Factory ssMediaSourceFactory = new SsMediaSource.Factory( /* chunkSourceFactory= */ this::createSampleSsChunkSource, /* manifestDataSourceFactory= */ () -> createSampleDataSource(SAMPLE_MANIFEST)); - assertThrows( - IllegalStateException.class, - () -> ssMediaSourceFactory.experimentalParseSubtitlesDuringExtraction(false)); + ssMediaSourceFactory.experimentalParseSubtitlesDuringExtraction(false); } @Test @@ -219,6 +217,7 @@ public class SsMediaSourceTest { trackSelection, new FakeDataSource(), cmcdConfiguration, - /* subtitleParserFactory= */ null); + SubtitleParser.Factory.UNSUPPORTED, + /* parseSubtitlesDuringExtraction= */ false); } }