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
This commit is contained in:
parent
0acf6902e5
commit
4d7b23f0d1
@ -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:
|
||||
|
@ -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).
|
||||
*
|
||||
* <p>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}.
|
||||
*
|
||||
* <p>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)) {
|
||||
|
@ -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<Format> 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).
|
||||
*
|
||||
* <p>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<Format> closedCaptionFormats,
|
||||
@Nullable TrackOutput playerEmsgTrackOutput,
|
||||
PlayerId playerId);
|
||||
}
|
||||
|
||||
/** Provides {@link TrackOutput} instances to be written to during extraction. */
|
||||
|
@ -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).
|
||||
*
|
||||
* <p>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.
|
||||
|
@ -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}
|
||||
*
|
||||
* <p>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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
* <p>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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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).
|
||||
*
|
||||
* <p>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}.
|
||||
*
|
||||
* <p>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<Format> 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<Format> 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;
|
||||
}
|
||||
|
@ -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).
|
||||
*
|
||||
* <p>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}.
|
||||
|
@ -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}
|
||||
*
|
||||
* <p>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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
* <p>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(),
|
||||
|
@ -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).
|
||||
*
|
||||
* <p>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}.
|
||||
|
@ -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}
|
||||
*
|
||||
* <p>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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user