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 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 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 extends DashManifest> 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 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 extends SsManifest> 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);
}
}