Add experimental opt-in to parse HLS subtitles during extraction

PiperOrigin-RevId: 578524318
This commit is contained in:
jbibik 2023-11-01 08:23:33 -07:00 committed by Copybara-Service
parent 72b7019578
commit 7b762642db
7 changed files with 152 additions and 14 deletions

View file

@ -46,6 +46,9 @@
* HLS Extension: * HLS Extension:
* Reduce `HlsMediaPeriod` to package-private visibility. This type * Reduce `HlsMediaPeriod` to package-private visibility. This type
shouldn't be directly depended on from outside the HLS package. shouldn't be directly depended on from outside the HLS package.
* Add experimental support for parsing subtitles during extraction. You
can enable this using
`HlsMediaSource.Factory.experimentalParseSubtitlesDuringExtraction()`.
* DASH Extension: * DASH Extension:
* Extend experimental support for parsing subtitles during extraction to * Extend experimental support for parsing subtitles during extraction to
work with standalone text files (previously it only worked with work with standalone text files (previously it only worked with

View file

@ -17,6 +17,7 @@ package androidx.media3.exoplayer.hls;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.media3.common.Format; import androidx.media3.common.Format;
import androidx.media3.common.util.TimestampAdjuster; import androidx.media3.common.util.TimestampAdjuster;
@ -27,6 +28,8 @@ import androidx.media3.extractor.ExtractorOutput;
import androidx.media3.extractor.PositionHolder; import androidx.media3.extractor.PositionHolder;
import androidx.media3.extractor.mp3.Mp3Extractor; import androidx.media3.extractor.mp3.Mp3Extractor;
import androidx.media3.extractor.mp4.FragmentedMp4Extractor; import androidx.media3.extractor.mp4.FragmentedMp4Extractor;
import androidx.media3.extractor.text.SubtitleParser;
import androidx.media3.extractor.text.SubtitleTranscodingExtractor;
import androidx.media3.extractor.ts.Ac3Extractor; import androidx.media3.extractor.ts.Ac3Extractor;
import androidx.media3.extractor.ts.Ac4Extractor; import androidx.media3.extractor.ts.Ac4Extractor;
import androidx.media3.extractor.ts.AdtsExtractor; import androidx.media3.extractor.ts.AdtsExtractor;
@ -45,6 +48,7 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract
@VisibleForTesting /* package */ final Extractor extractor; @VisibleForTesting /* package */ final Extractor extractor;
private final Format multivariantPlaylistFormat; private final Format multivariantPlaylistFormat;
private final TimestampAdjuster timestampAdjuster; private final TimestampAdjuster timestampAdjuster;
@Nullable private final SubtitleParser.Factory subtitleParserFactory;
/** /**
* Creates a new instance. * Creates a new instance.
@ -55,9 +59,35 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract
*/ */
public BundledHlsMediaChunkExtractor( public BundledHlsMediaChunkExtractor(
Extractor extractor, Format multivariantPlaylistFormat, TimestampAdjuster timestampAdjuster) { Extractor extractor, Format multivariantPlaylistFormat, TimestampAdjuster timestampAdjuster) {
this(
extractor,
multivariantPlaylistFormat,
timestampAdjuster,
/* subtitleParserFactory= */ null);
}
/**
* Creates a new instance.
*
* @param extractor The underlying {@link Extractor}.
* @param multivariantPlaylistFormat The {@link Format} obtained from the multivariant playlist.
* @param timestampAdjuster A {@link TimestampAdjuster} to adjust sample timestamps.
* @param subtitleParserFactory A {@link SubtitleParser.Factory} to be used with WebVTT subtitles.
* If the value is null, subtitles will be parsed during decoding, otherwise - during
* extraction. Decoding will only work if this subtitleParserFactory supports the provided
* multivariantPlaylistFormat.
*/
// TODO(b/289983417): Once the subtitle-parsing-during-extraction is the only available flow, make
// this constructor public and remove @Nullable from subtitleParserFactory
/* package */ BundledHlsMediaChunkExtractor(
Extractor extractor,
Format multivariantPlaylistFormat,
TimestampAdjuster timestampAdjuster,
@Nullable SubtitleParser.Factory subtitleParserFactory) {
this.extractor = extractor; this.extractor = extractor;
this.multivariantPlaylistFormat = multivariantPlaylistFormat; this.multivariantPlaylistFormat = multivariantPlaylistFormat;
this.timestampAdjuster = timestampAdjuster; this.timestampAdjuster = timestampAdjuster;
this.subtitleParserFactory = subtitleParserFactory;
} }
@Override @Override
@ -97,6 +127,11 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract
if (extractor instanceof WebvttExtractor) { if (extractor instanceof WebvttExtractor) {
newExtractorInstance = newExtractorInstance =
new WebvttExtractor(multivariantPlaylistFormat.language, timestampAdjuster); new WebvttExtractor(multivariantPlaylistFormat.language, timestampAdjuster);
if (subtitleParserFactory != null
&& subtitleParserFactory.supportsFormat(multivariantPlaylistFormat)) {
newExtractorInstance =
new SubtitleTranscodingExtractor(newExtractorInstance, subtitleParserFactory);
}
} else if (extractor instanceof AdtsExtractor) { } else if (extractor instanceof AdtsExtractor) {
newExtractorInstance = new AdtsExtractor(); newExtractorInstance = new AdtsExtractor();
} else if (extractor instanceof Ac3Extractor) { } else if (extractor instanceof Ac3Extractor) {
@ -110,7 +145,7 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract
"Unexpected extractor type for recreation: " + extractor.getClass().getSimpleName()); "Unexpected extractor type for recreation: " + extractor.getClass().getSimpleName());
} }
return new BundledHlsMediaChunkExtractor( return new BundledHlsMediaChunkExtractor(
newExtractorInstance, multivariantPlaylistFormat, timestampAdjuster); newExtractorInstance, multivariantPlaylistFormat, timestampAdjuster, subtitleParserFactory);
} }
@Override @Override

View file

@ -32,6 +32,8 @@ import androidx.media3.extractor.Extractor;
import androidx.media3.extractor.ExtractorInput; import androidx.media3.extractor.ExtractorInput;
import androidx.media3.extractor.mp3.Mp3Extractor; import androidx.media3.extractor.mp3.Mp3Extractor;
import androidx.media3.extractor.mp4.FragmentedMp4Extractor; import androidx.media3.extractor.mp4.FragmentedMp4Extractor;
import androidx.media3.extractor.text.SubtitleParser;
import androidx.media3.extractor.text.SubtitleTranscodingExtractor;
import androidx.media3.extractor.ts.Ac3Extractor; import androidx.media3.extractor.ts.Ac3Extractor;
import androidx.media3.extractor.ts.Ac4Extractor; import androidx.media3.extractor.ts.Ac4Extractor;
import androidx.media3.extractor.ts.AdtsExtractor; import androidx.media3.extractor.ts.AdtsExtractor;
@ -63,6 +65,10 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
}; };
private final @DefaultTsPayloadReaderFactory.Flags int payloadReaderFactoryFlags; private final @DefaultTsPayloadReaderFactory.Flags int payloadReaderFactoryFlags;
/** Non-null if subtitles should be parsed during extraction, null otherwise. */
@Nullable private SubtitleParser.Factory subtitleParserFactory;
private final boolean exposeCea608WhenMissingDeclarations; private final boolean exposeCea608WhenMissingDeclarations;
/** /**
@ -127,7 +133,8 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
checkNotNull( checkNotNull(
createExtractorByFileType(fileType, format, muxedCaptionFormats, timestampAdjuster)); createExtractorByFileType(fileType, format, muxedCaptionFormats, timestampAdjuster));
if (sniffQuietly(extractor, sniffingExtractorInput)) { if (sniffQuietly(extractor, sniffingExtractorInput)) {
return new BundledHlsMediaChunkExtractor(extractor, format, timestampAdjuster); return new BundledHlsMediaChunkExtractor(
extractor, format, timestampAdjuster, subtitleParserFactory);
} }
if (fallBackExtractor == null if (fallBackExtractor == null
&& (fileType == formatInferredFileType && (fileType == formatInferredFileType
@ -141,7 +148,24 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
} }
return new BundledHlsMediaChunkExtractor( return new BundledHlsMediaChunkExtractor(
checkNotNull(fallBackExtractor), format, timestampAdjuster); checkNotNull(fallBackExtractor), format, timestampAdjuster, subtitleParserFactory);
}
/**
* 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) {
this.subtitleParserFactory = subtitleParserFactory;
return this;
} }
private static void addFileTypeIfValidAndNotPresent( private static void addFileTypeIfValidAndNotPresent(
@ -162,7 +186,12 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
// LINT.IfChange(extractor_instantiation) // LINT.IfChange(extractor_instantiation)
switch (fileType) { switch (fileType) {
case FileTypes.WEBVTT: case FileTypes.WEBVTT:
return new WebvttExtractor(format.language, timestampAdjuster); if (subtitleParserFactory != null && subtitleParserFactory.supportsFormat(format)) {
return new SubtitleTranscodingExtractor(
new WebvttExtractor(format.language, timestampAdjuster), subtitleParserFactory);
} else {
return new WebvttExtractor(format.language, timestampAdjuster);
}
case FileTypes.ADTS: case FileTypes.ADTS:
return new AdtsExtractor(); return new AdtsExtractor();
case FileTypes.AC3: case FileTypes.AC3:

View file

@ -51,6 +51,7 @@ import androidx.media3.exoplayer.upstream.Allocator;
import androidx.media3.exoplayer.upstream.CmcdConfiguration; import androidx.media3.exoplayer.upstream.CmcdConfiguration;
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy; import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
import androidx.media3.extractor.Extractor; import androidx.media3.extractor.Extractor;
import androidx.media3.extractor.text.SubtitleParser;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -85,6 +86,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private final PlayerId playerId; private final PlayerId playerId;
private final HlsSampleStreamWrapper.Callback sampleStreamWrapperCallback; private final HlsSampleStreamWrapper.Callback sampleStreamWrapperCallback;
private final long timestampAdjusterInitializationTimeoutMs; private final long timestampAdjusterInitializationTimeoutMs;
@Nullable private final SubtitleParser.Factory subtitleParserFactory;
@Nullable private MediaPeriod.Callback mediaPeriodCallback; @Nullable private MediaPeriod.Callback mediaPeriodCallback;
private int pendingPrepareCount; private int pendingPrepareCount;
@ -139,7 +141,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@HlsMediaSource.MetadataType int metadataType, @HlsMediaSource.MetadataType int metadataType,
boolean useSessionKeys, boolean useSessionKeys,
PlayerId playerId, PlayerId playerId,
long timestampAdjusterInitializationTimeoutMs) { long timestampAdjusterInitializationTimeoutMs,
@Nullable SubtitleParser.Factory subtitleParserFactory) {
this.extractorFactory = extractorFactory; this.extractorFactory = extractorFactory;
this.playlistTracker = playlistTracker; this.playlistTracker = playlistTracker;
this.dataSourceFactory = dataSourceFactory; this.dataSourceFactory = dataSourceFactory;
@ -164,6 +167,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
sampleStreamWrappers = new HlsSampleStreamWrapper[0]; sampleStreamWrappers = new HlsSampleStreamWrapper[0];
enabledSampleStreamWrappers = new HlsSampleStreamWrapper[0]; enabledSampleStreamWrappers = new HlsSampleStreamWrapper[0];
manifestUrlIndicesPerWrapper = new int[0][]; manifestUrlIndicesPerWrapper = new int[0][];
this.subtitleParserFactory = subtitleParserFactory;
} }
public void release() { public void release() {
@ -517,21 +521,43 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
for (int i = 0; i < subtitleRenditions.size(); i++) { for (int i = 0; i < subtitleRenditions.size(); i++) {
Rendition subtitleRendition = subtitleRenditions.get(i); Rendition subtitleRendition = subtitleRenditions.get(i);
String sampleStreamWrapperUid = "subtitle:" + i + ":" + subtitleRendition.name; String sampleStreamWrapperUid = "subtitle:" + i + ":" + subtitleRendition.name;
// Format for HlsChunkSource to createExtractor with
Format originalSubtitleFormat = subtitleRendition.format;
HlsSampleStreamWrapper sampleStreamWrapper = HlsSampleStreamWrapper sampleStreamWrapper =
buildSampleStreamWrapper( buildSampleStreamWrapper(
sampleStreamWrapperUid, sampleStreamWrapperUid,
C.TRACK_TYPE_TEXT, C.TRACK_TYPE_TEXT,
new Uri[] {subtitleRendition.url}, new Uri[] {subtitleRendition.url},
new Format[] {subtitleRendition.format}, new Format[] {originalSubtitleFormat},
null, null,
Collections.emptyList(), Collections.emptyList(),
overridingDrmInitData, overridingDrmInitData,
positionUs); positionUs);
manifestUrlIndicesPerWrapper.add(new int[] {i}); manifestUrlIndicesPerWrapper.add(new int[] {i});
sampleStreamWrappers.add(sampleStreamWrapper); sampleStreamWrappers.add(sampleStreamWrapper);
sampleStreamWrapper.prepareWithMultivariantPlaylistInfo( if (subtitleParserFactory != null
new TrackGroup[] {new TrackGroup(sampleStreamWrapperUid, subtitleRendition.format)}, && subtitleParserFactory.supportsFormat(originalSubtitleFormat)) {
/* primaryTrackGroupIndex= */ 0); Format updatedSubtitleFormat =
originalSubtitleFormat
.buildUpon()
.setSampleMimeType(MimeTypes.APPLICATION_MEDIA3_CUES)
.setCueReplacementBehavior(
subtitleParserFactory.getCueReplacementBehavior(originalSubtitleFormat))
.setCodecs(
originalSubtitleFormat.sampleMimeType
+ (originalSubtitleFormat.codecs != null
? " " + originalSubtitleFormat.codecs
: ""))
.setSubsampleOffsetUs(Format.OFFSET_SAMPLE_RELATIVE)
.build();
sampleStreamWrapper.prepareWithMultivariantPlaylistInfo(
new TrackGroup[] {new TrackGroup(sampleStreamWrapperUid, updatedSubtitleFormat)},
/* primaryTrackGroupIndex= */ 0);
} else {
sampleStreamWrapper.prepareWithMultivariantPlaylistInfo(
new TrackGroup[] {new TrackGroup(sampleStreamWrapperUid, originalSubtitleFormat)},
/* primaryTrackGroupIndex= */ 0);
}
} }
this.sampleStreamWrappers = sampleStreamWrappers.toArray(new HlsSampleStreamWrapper[0]); this.sampleStreamWrappers = sampleStreamWrappers.toArray(new HlsSampleStreamWrapper[0]);

View file

@ -58,6 +58,8 @@ import androidx.media3.exoplayer.upstream.CmcdConfiguration;
import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy; import androidx.media3.exoplayer.upstream.DefaultLoadErrorHandlingPolicy;
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy; import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
import androidx.media3.extractor.Extractor; import androidx.media3.extractor.Extractor;
import androidx.media3.extractor.text.DefaultSubtitleParserFactory;
import androidx.media3.extractor.text.SubtitleParser;
import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
@ -111,6 +113,7 @@ public final class HlsMediaSource extends BaseMediaSource
@Nullable private CmcdConfiguration.Factory cmcdConfigurationFactory; @Nullable private CmcdConfiguration.Factory cmcdConfigurationFactory;
private DrmSessionManagerProvider drmSessionManagerProvider; private DrmSessionManagerProvider drmSessionManagerProvider;
private LoadErrorHandlingPolicy loadErrorHandlingPolicy; private LoadErrorHandlingPolicy loadErrorHandlingPolicy;
@Nullable private SubtitleParser.Factory subtitleParserFactory;
private boolean allowChunklessPreparation; private boolean allowChunklessPreparation;
private @MetadataType int metadataType; private @MetadataType int metadataType;
private boolean useSessionKeys; private boolean useSessionKeys;
@ -196,6 +199,40 @@ public final class HlsMediaSource extends BaseMediaSource
return this; return this;
} }
/**
* Sets whether subtitles should be parsed as part of extraction (before the sample queue) or as
* part of rendering (after the sample queue). Defaults to false (i.e. subtitles will be parsed
* as part of rendering).
*
* <p>This method is experimental. Its default value may change, or it may be renamed or removed
* in a future release.
*
* <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 (probably wired up to a single method on
// DefaultMediaSourceFactory via the MediaSource.Factory interface).
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();
}
return this;
}
/** /**
* Sets the factory from which playlist parsers will be obtained. * Sets the factory from which playlist parsers will be obtained.
* *
@ -381,6 +418,7 @@ public final class HlsMediaSource extends BaseMediaSource
mediaItem, mediaItem,
hlsDataSourceFactory, hlsDataSourceFactory,
extractorFactory, extractorFactory,
subtitleParserFactory,
compositeSequenceableLoaderFactory, compositeSequenceableLoaderFactory,
cmcdConfiguration, cmcdConfiguration,
drmSessionManagerProvider.get(mediaItem), drmSessionManagerProvider.get(mediaItem),
@ -412,6 +450,7 @@ public final class HlsMediaSource extends BaseMediaSource
private final HlsPlaylistTracker playlistTracker; private final HlsPlaylistTracker playlistTracker;
private final long elapsedRealTimeOffsetMs; private final long elapsedRealTimeOffsetMs;
private final long timestampAdjusterInitializationTimeoutMs; private final long timestampAdjusterInitializationTimeoutMs;
@Nullable private final SubtitleParser.Factory subtitleParserFactory;
private MediaItem.LiveConfiguration liveConfiguration; private MediaItem.LiveConfiguration liveConfiguration;
@Nullable private TransferListener mediaTransferListener; @Nullable private TransferListener mediaTransferListener;
@ -423,6 +462,7 @@ public final class HlsMediaSource extends BaseMediaSource
MediaItem mediaItem, MediaItem mediaItem,
HlsDataSourceFactory dataSourceFactory, HlsDataSourceFactory dataSourceFactory,
HlsExtractorFactory extractorFactory, HlsExtractorFactory extractorFactory,
@Nullable SubtitleParser.Factory subtitleParserFactory,
CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory, CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory,
@Nullable CmcdConfiguration cmcdConfiguration, @Nullable CmcdConfiguration cmcdConfiguration,
DrmSessionManager drmSessionManager, DrmSessionManager drmSessionManager,
@ -437,6 +477,7 @@ public final class HlsMediaSource extends BaseMediaSource
this.liveConfiguration = mediaItem.liveConfiguration; this.liveConfiguration = mediaItem.liveConfiguration;
this.dataSourceFactory = dataSourceFactory; this.dataSourceFactory = dataSourceFactory;
this.extractorFactory = extractorFactory; this.extractorFactory = extractorFactory;
this.subtitleParserFactory = subtitleParserFactory;
this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory; this.compositeSequenceableLoaderFactory = compositeSequenceableLoaderFactory;
this.cmcdConfiguration = cmcdConfiguration; this.cmcdConfiguration = cmcdConfiguration;
this.drmSessionManager = drmSessionManager; this.drmSessionManager = drmSessionManager;
@ -511,7 +552,8 @@ public final class HlsMediaSource extends BaseMediaSource
metadataType, metadataType,
useSessionKeys, useSessionKeys,
getPlayerId(), getPlayerId(),
timestampAdjusterInitializationTimeoutMs); timestampAdjusterInitializationTimeoutMs,
subtitleParserFactory);
} }
@Override @Override

View file

@ -97,7 +97,8 @@ public final class HlsMediaPeriodTest {
HlsMediaSource.METADATA_TYPE_ID3, HlsMediaSource.METADATA_TYPE_ID3,
/* useSessionKeys= */ false, /* useSessionKeys= */ false,
PlayerId.UNSET, PlayerId.UNSET,
/* timestampAdjusterInitializationTimeoutMs= */ 0); /* timestampAdjusterInitializationTimeoutMs= */ 0,
/* subtitleParserFactory= */ null);
}; };
MediaPeriodAsserts.assertGetStreamKeysAndManifestFilterIntegration( MediaPeriodAsserts.assertGetStreamKeysAndManifestFilterIntegration(

View file

@ -21,7 +21,9 @@ import android.graphics.SurfaceTexture;
import android.view.Surface; import android.view.Surface;
import androidx.media3.common.MediaItem; import androidx.media3.common.MediaItem;
import androidx.media3.common.Player; import androidx.media3.common.Player;
import androidx.media3.datasource.DefaultDataSource;
import androidx.media3.exoplayer.ExoPlayer; import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.hls.HlsMediaSource;
import androidx.media3.test.utils.CapturingRenderersFactory; import androidx.media3.test.utils.CapturingRenderersFactory;
import androidx.media3.test.utils.DumpFileAsserts; import androidx.media3.test.utils.DumpFileAsserts;
import androidx.media3.test.utils.FakeClock; import androidx.media3.test.utils.FakeClock;
@ -30,7 +32,6 @@ import androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig;
import androidx.media3.test.utils.robolectric.TestPlayerRunHelper; import androidx.media3.test.utils.robolectric.TestPlayerRunHelper;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Ignore;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -44,14 +45,15 @@ public final class HlsPlaybackTest {
ShadowMediaCodecConfig.forAllSupportedMimeTypes(); ShadowMediaCodecConfig.forAllSupportedMimeTypes();
@Test @Test
@Ignore(
"Disabled until subtitles are reliably asserted in robolectric tests [internal b/174661563].")
public void webvttSubtitles() throws Exception { public void webvttSubtitles() throws Exception {
Context applicationContext = ApplicationProvider.getApplicationContext(); Context applicationContext = ApplicationProvider.getApplicationContext();
CapturingRenderersFactory capturingRenderersFactory = CapturingRenderersFactory capturingRenderersFactory =
new CapturingRenderersFactory(applicationContext); new CapturingRenderersFactory(applicationContext);
ExoPlayer player = ExoPlayer player =
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory) new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
.setMediaSourceFactory(
new HlsMediaSource.Factory(new DefaultDataSource.Factory(applicationContext))
.experimentalParseSubtitlesDuringExtraction(true))
.setClock(new FakeClock(/* isAutoAdvancing= */ true)) .setClock(new FakeClock(/* isAutoAdvancing= */ true))
.build(); .build();
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1))); player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));