Plumb SubtitleParser.Factory into TsExtractor

PiperOrigin-RevId: 597578122
This commit is contained in:
jbibik 2024-01-11 09:32:59 -08:00 committed by Copybara-Service
parent c59711f592
commit a728ec8e67
6 changed files with 298 additions and 71 deletions

View File

@ -16,6 +16,7 @@
package androidx.media3.exoplayer.hls;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.extractor.ts.TsExtractor.DEFAULT_TIMESTAMP_SEARCH_BYTES;
import android.annotation.SuppressLint;
import android.net.Uri;
@ -205,16 +206,13 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
return createFragmentedMp4Extractor(
subtitleParserFactory, timestampAdjuster, format, muxedCaptionFormats);
case FileTypes.TS:
Extractor tsExtractor =
createTsExtractor(
payloadReaderFactoryFlags,
exposeCea608WhenMissingDeclarations,
format,
muxedCaptionFormats,
timestampAdjuster);
return subtitleParserFactory != null
? new SubtitleTranscodingExtractor(tsExtractor, subtitleParserFactory)
: tsExtractor;
return createTsExtractor(
payloadReaderFactoryFlags,
exposeCea608WhenMissingDeclarations,
format,
muxedCaptionFormats,
timestampAdjuster,
subtitleParserFactory);
default:
return null;
}
@ -225,7 +223,8 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
boolean exposeCea608WhenMissingDeclarations,
Format format,
@Nullable List<Format> muxedCaptionFormats,
TimestampAdjuster timestampAdjuster) {
TimestampAdjuster timestampAdjuster,
@Nullable SubtitleParser.Factory subtitleParserFactory) {
@DefaultTsPayloadReaderFactory.Flags
int payloadReaderFactoryFlags =
DefaultTsPayloadReaderFactory.FLAG_IGNORE_SPLICE_INFO_STREAM
@ -254,11 +253,18 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
payloadReaderFactoryFlags |= DefaultTsPayloadReaderFactory.FLAG_IGNORE_H264_STREAM;
}
}
@TsExtractor.Flags int extractorFlags = 0;
if (subtitleParserFactory == null) {
subtitleParserFactory = SubtitleParser.Factory.UNSUPPORTED;
extractorFlags |= TsExtractor.FLAG_EMIT_RAW_SUBTITLE_DATA;
}
return new TsExtractor(
TsExtractor.MODE_HLS,
extractorFlags,
subtitleParserFactory,
timestampAdjuster,
new DefaultTsPayloadReaderFactory(payloadReaderFactoryFlags, muxedCaptionFormats));
new DefaultTsPayloadReaderFactory(payloadReaderFactoryFlags, muxedCaptionFormats),
DEFAULT_TIMESTAMP_SEARCH_BYTES);
}
private static FragmentedMp4Extractor createFragmentedMp4Extractor(

View File

@ -440,6 +440,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
textTrackTranscodingEnabled
&& !(extractor.getUnderlyingImplementation() instanceof FragmentedMp4Extractor)
&& !(extractor.getUnderlyingImplementation() instanceof Mp4Extractor)
&& !(extractor.getUnderlyingImplementation() instanceof TsExtractor)
? new SubtitleTranscodingExtractor(extractor, subtitleParserFactory)
: extractor;
}
@ -530,6 +531,8 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
extractors.add(
new TsExtractor(
tsMode,
(textTrackTranscodingEnabled ? 0 : TsExtractor.FLAG_EMIT_RAW_SUBTITLE_DATA),
subtitleParserFactory,
new TimestampAdjuster(0),
new DefaultTsPayloadReaderFactory(tsFlags, tsSubtitleFormats),
tsTimestampSearchBytes));

View File

@ -24,6 +24,7 @@ import android.util.SparseIntArray;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.ParserException;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.NullableType;
@ -39,7 +40,8 @@ import androidx.media3.extractor.ExtractorsFactory;
import androidx.media3.extractor.PositionHolder;
import androidx.media3.extractor.SeekMap;
import androidx.media3.extractor.TrackOutput;
import androidx.media3.extractor.ts.DefaultTsPayloadReaderFactory.Flags;
import androidx.media3.extractor.text.SubtitleParser;
import androidx.media3.extractor.text.SubtitleTranscodingExtractorOutput;
import androidx.media3.extractor.ts.TsPayloadReader.DvbSubtitleInfo;
import androidx.media3.extractor.ts.TsPayloadReader.EsInfo;
import androidx.media3.extractor.ts.TsPayloadReader.TrackIdGenerator;
@ -58,9 +60,20 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@UnstableApi
public final class TsExtractor implements Extractor {
/** Factory for {@link TsExtractor} instances. */
/**
* @deprecated Use {@link #newFactory(SubtitleParser.Factory)} instead.
*/
@Deprecated
public static final ExtractorsFactory FACTORY = () -> new Extractor[] {new TsExtractor()};
/**
* Creates a factory for {@link TsExtractor} instances with the provided {@link
* SubtitleParser.Factory}.
*/
public static ExtractorsFactory newFactory(SubtitleParser.Factory subtitleParserFactory) {
return () -> new Extractor[] {new TsExtractor(subtitleParserFactory)};
}
/**
* Modes for the extractor. One of {@link #MODE_MULTI_PMT}, {@link #MODE_SINGLE_PMT} or {@link
* #MODE_HLS}.
@ -83,6 +96,24 @@ public final class TsExtractor implements Extractor {
*/
public static final int MODE_HLS = 2;
/**
* Flags controlling the behavior of the extractor. Possible flag value is {@link
* #FLAG_EMIT_RAW_SUBTITLE_DATA}.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@IntDef(
flag = true,
value = {FLAG_EMIT_RAW_SUBTITLE_DATA})
public @interface Flags {}
/**
* Flag to use the source subtitle formats without modification. If unset, subtitles will be
* transcoded to {@link MimeTypes#APPLICATION_MEDIA3_CUES} during extraction.
*/
public static final int FLAG_EMIT_RAW_SUBTITLE_DATA = 1;
public static final int TS_PACKET_SIZE = 188;
public static final int DEFAULT_TIMESTAMP_SEARCH_BYTES = 600 * TS_PACKET_SIZE;
@ -121,11 +152,13 @@ public final class TsExtractor implements Extractor {
private static final int SNIFF_TS_PACKET_COUNT = 5;
private final @Mode int mode;
private final @Flags int extractorFlags;
private final int timestampSearchBytes;
private final List<TimestampAdjuster> timestampAdjusters;
private final ParsableByteArray tsPacketBuffer;
private final SparseIntArray continuityCounters;
private final TsPayloadReader.Factory payloadReaderFactory;
private final SubtitleParser.Factory subtitleParserFactory;
private final SparseArray<TsPayloadReader> tsPayloadReaders; // Indexed by pid
private final SparseBooleanArray trackIds;
private final SparseBooleanArray trackPids;
@ -142,57 +175,131 @@ public final class TsExtractor implements Extractor {
private int bytesSinceLastSync;
private int pcrPid;
/**
* @deprecated Use {@link #TsExtractor(SubtitleParser.Factory)} instead.
*/
@Deprecated
public TsExtractor() {
this(/* defaultTsPayloadReaderFlags= */ 0);
this(
MODE_SINGLE_PMT,
/* extractorFlags= */ FLAG_EMIT_RAW_SUBTITLE_DATA,
SubtitleParser.Factory.UNSUPPORTED,
new TimestampAdjuster(0),
new DefaultTsPayloadReaderFactory(/* defaultTsPayloadReaderFlags= */ 0),
DEFAULT_TIMESTAMP_SEARCH_BYTES);
}
/**
* @param defaultTsPayloadReaderFlags A combination of {@link DefaultTsPayloadReaderFactory}
* {@code FLAG_*} values that control the behavior of the payload readers.
* Constructs an instance.
*
* @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during
* extraction.
*/
public TsExtractor(@Flags int defaultTsPayloadReaderFlags) {
this(MODE_SINGLE_PMT, defaultTsPayloadReaderFlags, DEFAULT_TIMESTAMP_SEARCH_BYTES);
public TsExtractor(SubtitleParser.Factory subtitleParserFactory) {
this(
MODE_SINGLE_PMT,
/* extractorFlags= */ 0,
subtitleParserFactory,
new TimestampAdjuster(0),
new DefaultTsPayloadReaderFactory(/* defaultTsPayloadReaderFlags= */ 0),
DEFAULT_TIMESTAMP_SEARCH_BYTES);
}
/**
* @param mode Mode for the extractor. One of {@link #MODE_MULTI_PMT}, {@link #MODE_SINGLE_PMT}
* and {@link #MODE_HLS}.
* @param defaultTsPayloadReaderFlags A combination of {@link DefaultTsPayloadReaderFactory}
* {@code FLAG_*} values that control the behavior of the payload readers.
* @param timestampSearchBytes The number of bytes searched from a given position in the stream to
* find a PCR timestamp. If this value is too small, the duration might be unknown and seeking
* might not be supported for high bitrate progressive streams. Setting a large value for this
* field might be inefficient though because the extractor stores a buffer of {@code
* timestampSearchBytes} bytes when determining the duration or when performing a seek
* operation. The default value is {@link #DEFAULT_TIMESTAMP_SEARCH_BYTES}. If the number of
* bytes left in the stream from the current position is less than {@code
* timestampSearchBytes}, the search is performed on the bytes left.
* Constructs an instance.
*
* @param extractorFlags Flags that control the extractor's behavior.
* @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during
* extraction.
*/
public TsExtractor(@Flags int extractorFlags, SubtitleParser.Factory subtitleParserFactory) {
this(
MODE_SINGLE_PMT,
extractorFlags,
subtitleParserFactory,
new TimestampAdjuster(0),
new DefaultTsPayloadReaderFactory(/* defaultTsPayloadReaderFlags= */ 0),
DEFAULT_TIMESTAMP_SEARCH_BYTES);
}
/**
* @deprecated Use {@link #TsExtractor(int, int, SubtitleParser.Factory, TimestampAdjuster,
* TsPayloadReader.Factory, int)} instead.
*/
@Deprecated
public TsExtractor(@DefaultTsPayloadReaderFactory.Flags int defaultTsPayloadReaderFlags) {
this(
MODE_SINGLE_PMT,
FLAG_EMIT_RAW_SUBTITLE_DATA,
SubtitleParser.Factory.UNSUPPORTED,
new TimestampAdjuster(0),
new DefaultTsPayloadReaderFactory(defaultTsPayloadReaderFlags),
DEFAULT_TIMESTAMP_SEARCH_BYTES);
}
/**
* @deprecated Use {@link #TsExtractor(int, int, SubtitleParser.Factory, TimestampAdjuster,
* TsPayloadReader.Factory, int)} instead.
*/
@Deprecated
public TsExtractor(
@Mode int mode, @Flags int defaultTsPayloadReaderFlags, int timestampSearchBytes) {
@Mode int mode,
@DefaultTsPayloadReaderFactory.Flags int defaultTsPayloadReaderFlags,
int timestampSearchBytes) {
this(
mode,
FLAG_EMIT_RAW_SUBTITLE_DATA,
SubtitleParser.Factory.UNSUPPORTED,
new TimestampAdjuster(0),
new DefaultTsPayloadReaderFactory(defaultTsPayloadReaderFlags),
timestampSearchBytes);
}
/**
* @param mode Mode for the extractor. One of {@link #MODE_MULTI_PMT}, {@link #MODE_SINGLE_PMT}
* and {@link #MODE_HLS}.
* @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps.
* @param payloadReaderFactory Factory for injecting a custom set of payload readers.
* @deprecated Use {@link #TsExtractor(int, int, SubtitleParser.Factory, TimestampAdjuster,
* TsPayloadReader.Factory, int)} instead.
*/
@Deprecated
public TsExtractor(
@Mode int mode,
TimestampAdjuster timestampAdjuster,
TsPayloadReader.Factory payloadReaderFactory) {
this(mode, timestampAdjuster, payloadReaderFactory, DEFAULT_TIMESTAMP_SEARCH_BYTES);
this(
mode,
FLAG_EMIT_RAW_SUBTITLE_DATA,
SubtitleParser.Factory.UNSUPPORTED,
timestampAdjuster,
payloadReaderFactory,
DEFAULT_TIMESTAMP_SEARCH_BYTES);
}
/**
* @deprecated Use {@link #TsExtractor(int, int, SubtitleParser.Factory, TimestampAdjuster,
* TsPayloadReader.Factory, int)} instead.
*/
@Deprecated
public TsExtractor(
@Mode int mode,
TimestampAdjuster timestampAdjuster,
TsPayloadReader.Factory payloadReaderFactory,
int timestampSearchBytes) {
this(
mode,
FLAG_EMIT_RAW_SUBTITLE_DATA,
SubtitleParser.Factory.UNSUPPORTED,
timestampAdjuster,
payloadReaderFactory,
timestampSearchBytes);
}
/**
* Constructs an instance.
*
* @param mode Mode for the extractor. One of {@link #MODE_MULTI_PMT}, {@link #MODE_SINGLE_PMT}
* and {@link #MODE_HLS}.
* @param extractorFlags Flags that control the extractor's behavior.
* @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during
* extraction.
* @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps.
* @param payloadReaderFactory Factory for injecting a custom set of payload readers.
* @param timestampSearchBytes The number of bytes searched from a given position in the stream to
@ -206,12 +313,16 @@ public final class TsExtractor implements Extractor {
*/
public TsExtractor(
@Mode int mode,
@Flags int extractorFlags,
SubtitleParser.Factory subtitleParserFactory,
TimestampAdjuster timestampAdjuster,
TsPayloadReader.Factory payloadReaderFactory,
int timestampSearchBytes) {
this.payloadReaderFactory = Assertions.checkNotNull(payloadReaderFactory);
this.timestampSearchBytes = timestampSearchBytes;
this.mode = mode;
this.extractorFlags = extractorFlags;
this.subtitleParserFactory = subtitleParserFactory;
if (mode == MODE_SINGLE_PMT || mode == MODE_HLS) {
timestampAdjusters = Collections.singletonList(timestampAdjuster);
} else {
@ -254,7 +365,10 @@ public final class TsExtractor implements Extractor {
@Override
public void init(ExtractorOutput output) {
this.output = output;
this.output =
(extractorFlags & FLAG_EMIT_RAW_SUBTITLE_DATA) == 0
? new SubtitleTranscodingExtractorOutput(output, subtitleParserFactory)
: output;
}
@Override

View File

@ -179,7 +179,7 @@ public final class DefaultExtractorsFactoryTest {
assertThat(matroskaExtractor).isInstanceOf(SubtitleTranscodingExtractor.class);
assertThat(mp4Extractor).isNotInstanceOf(SubtitleTranscodingExtractor.class);
assertThat(fragmentedMp4Extractor).isNotInstanceOf(SubtitleTranscodingExtractor.class);
assertThat(tsExtractor).isInstanceOf(SubtitleTranscodingExtractor.class);
assertThat(tsExtractor).isNotInstanceOf(SubtitleTranscodingExtractor.class);
}
private static List<Class<? extends Extractor>> getUnderlyingExtractorClasses(

View File

@ -24,6 +24,7 @@ import androidx.media3.extractor.Extractor;
import androidx.media3.extractor.ExtractorInput;
import androidx.media3.extractor.PositionHolder;
import androidx.media3.extractor.SeekMap;
import androidx.media3.extractor.text.DefaultSubtitleParserFactory;
import androidx.media3.test.utils.FakeExtractorOutput;
import androidx.media3.test.utils.FakeTrackOutput;
import androidx.media3.test.utils.TestUtil;
@ -56,7 +57,9 @@ public final class TsExtractorSeekTest {
positionHolder = new PositionHolder();
expectedTrackOutput =
TestUtil.extractAllSamplesFromFile(
new TsExtractor(), ApplicationProvider.getApplicationContext(), TEST_FILE)
new TsExtractor(new DefaultSubtitleParserFactory()),
ApplicationProvider.getApplicationContext(),
TEST_FILE)
.trackOutputs
.get(AUDIO_TRACK_ID);
@ -68,7 +71,7 @@ public final class TsExtractorSeekTest {
@Test
public void tsExtractorReads_nonSeekTableFile_returnSeekableSeekMap() throws IOException {
Uri fileUri = TestUtil.buildAssetUri(TEST_FILE);
TsExtractor extractor = new TsExtractor();
TsExtractor extractor = new TsExtractor(new DefaultSubtitleParserFactory());
SeekMap seekMap =
TestUtil.extractSeekMap(extractor, new FakeExtractorOutput(), dataSource, fileUri);
@ -81,7 +84,7 @@ public final class TsExtractorSeekTest {
@Test
public void handlePendingSeek_handlesSeekingToPositionInFile_extractsCorrectFrame()
throws IOException {
TsExtractor extractor = new TsExtractor();
TsExtractor extractor = new TsExtractor(new DefaultSubtitleParserFactory());
Uri fileUri = TestUtil.buildAssetUri(TEST_FILE);
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
@ -100,7 +103,7 @@ public final class TsExtractorSeekTest {
@Test
public void handlePendingSeek_handlesSeekToEoF_extractsLastFrame() throws IOException {
TsExtractor extractor = new TsExtractor();
TsExtractor extractor = new TsExtractor(new DefaultSubtitleParserFactory());
Uri fileUri = TestUtil.buildAssetUri(TEST_FILE);
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
@ -120,7 +123,7 @@ public final class TsExtractorSeekTest {
@Test
public void handlePendingSeek_handlesSeekingBackward_extractsCorrectFrame() throws IOException {
TsExtractor extractor = new TsExtractor();
TsExtractor extractor = new TsExtractor(new DefaultSubtitleParserFactory());
Uri fileUri = TestUtil.buildAssetUri(TEST_FILE);
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
@ -142,7 +145,7 @@ public final class TsExtractorSeekTest {
@Test
public void handlePendingSeek_handlesSeekingForward_extractsCorrectFrame() throws IOException {
TsExtractor extractor = new TsExtractor();
TsExtractor extractor = new TsExtractor(new DefaultSubtitleParserFactory());
Uri fileUri = TestUtil.buildAssetUri(TEST_FILE);
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
@ -164,7 +167,7 @@ public final class TsExtractorSeekTest {
@Test
public void handlePendingSeek_handlesRandomSeeks_extractsCorrectFrame() throws IOException {
TsExtractor extractor = new TsExtractor();
TsExtractor extractor = new TsExtractor(new DefaultSubtitleParserFactory());
Uri fileUri = TestUtil.buildAssetUri(TEST_FILE);
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
@ -187,7 +190,7 @@ public final class TsExtractorSeekTest {
@Test
public void handlePendingSeek_handlesRandomSeeksAfterReadingFileOnce_extractsCorrectFrame()
throws IOException {
TsExtractor extractor = new TsExtractor();
TsExtractor extractor = new TsExtractor(new DefaultSubtitleParserFactory());
Uri fileUri = TestUtil.buildAssetUri(TEST_FILE);
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();

View File

@ -15,7 +15,12 @@
*/
package androidx.media3.extractor.ts;
import static androidx.media3.extractor.mp4.FragmentedMp4Extractor.FLAG_EMIT_RAW_SUBTITLE_DATA;
import static androidx.media3.extractor.ts.DefaultTsPayloadReaderFactory.FLAG_DETECT_ACCESS_UNITS;
import static androidx.media3.extractor.ts.DefaultTsPayloadReaderFactory.FLAG_ENABLE_HDMV_DTS_AUDIO_STREAMS;
import static androidx.media3.extractor.ts.TsExtractor.DEFAULT_TIMESTAMP_SEARCH_BYTES;
import static androidx.media3.extractor.ts.TsExtractor.MODE_MULTI_PMT;
import static androidx.media3.extractor.ts.TsExtractor.MODE_SINGLE_PMT;
import static com.google.common.truth.Truth.assertThat;
import android.util.SparseArray;
@ -28,6 +33,8 @@ import androidx.media3.extractor.Extractor;
import androidx.media3.extractor.ExtractorOutput;
import androidx.media3.extractor.PositionHolder;
import androidx.media3.extractor.TrackOutput;
import androidx.media3.extractor.text.DefaultSubtitleParserFactory;
import androidx.media3.extractor.text.SubtitleParser;
import androidx.media3.extractor.ts.TsPayloadReader.EsInfo;
import androidx.media3.extractor.ts.TsPayloadReader.TrackIdGenerator;
import androidx.media3.test.utils.ExtractorAsserts;
@ -36,7 +43,8 @@ import androidx.media3.test.utils.FakeExtractorOutput;
import androidx.media3.test.utils.FakeTrackOutput;
import androidx.media3.test.utils.TestUtil;
import androidx.test.core.app.ApplicationProvider;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.ParameterizedRobolectricTestRunner;
@ -47,39 +55,62 @@ import org.robolectric.ParameterizedRobolectricTestRunner.Parameters;
@RunWith(ParameterizedRobolectricTestRunner.class)
public final class TsExtractorTest {
@Parameters(name = "{0}")
public static ImmutableList<ExtractorAsserts.SimulationConfig> params() {
return ExtractorAsserts.configs();
@Parameters(name = "{0},subtitlesParsedDuringExtraction={1}")
public static List<Object[]> params() {
List<Object[]> parameterList = new ArrayList<>();
for (ExtractorAsserts.SimulationConfig config : ExtractorAsserts.configs()) {
parameterList.add(new Object[] {config, /* subtitlesParsedDuringExtraction */ true});
parameterList.add(new Object[] {config, /* subtitlesParsedDuringExtraction */ false});
}
return parameterList;
}
@Parameter public ExtractorAsserts.SimulationConfig simulationConfig;
@Parameter(0)
public ExtractorAsserts.SimulationConfig simulationConfig;
@Parameter(1)
public boolean subtitlesParsedDuringExtraction;
@Test
public void sampleWithH262AndMpegAudio() throws Exception {
ExtractorAsserts.assertBehavior(
TsExtractor::new, "media/ts/sample_h262_mpeg_audio.ts", simulationConfig);
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_h262_mpeg_audio.ts",
simulationConfig);
}
@Test
public void sampleWithH263() throws Exception {
ExtractorAsserts.assertBehavior(TsExtractor::new, "media/ts/sample_h263.ts", simulationConfig);
ExtractorAsserts.assertBehavior(
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_h263.ts",
simulationConfig);
}
@Test
public void sampleWithH264() throws Exception {
ExtractorAsserts.assertBehavior(TsExtractor::new, "media/ts/sample_h264.ts", simulationConfig);
ExtractorAsserts.assertBehavior(
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_h264.ts",
simulationConfig);
}
@Test
public void sampleWithH264AndMpegAudio() throws Exception {
ExtractorAsserts.assertBehavior(
TsExtractor::new, "media/ts/sample_h264_mpeg_audio.ts", simulationConfig);
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_h264_mpeg_audio.ts",
simulationConfig);
}
@Test
public void sampleWithH264NoAccessUnitDelimiters() throws Exception {
ExtractorAsserts.assertBehavior(
() -> new TsExtractor(FLAG_DETECT_ACCESS_UNITS),
getExtractorFactory(
subtitlesParsedDuringExtraction,
MODE_SINGLE_PMT,
new TimestampAdjuster(0),
new DefaultTsPayloadReaderFactory(FLAG_DETECT_ACCESS_UNITS)),
"media/ts/sample_h264_no_access_unit_delimiters.ts",
simulationConfig);
}
@ -87,26 +118,35 @@ public final class TsExtractorTest {
@Test
public void sampleWithH264AndDtsAudio() throws Exception {
ExtractorAsserts.assertBehavior(
() -> new TsExtractor(DefaultTsPayloadReaderFactory.FLAG_ENABLE_HDMV_DTS_AUDIO_STREAMS),
getExtractorFactory(
subtitlesParsedDuringExtraction,
MODE_SINGLE_PMT,
new TimestampAdjuster(0),
new DefaultTsPayloadReaderFactory(FLAG_ENABLE_HDMV_DTS_AUDIO_STREAMS)),
"media/ts/sample_h264_dts_audio.ts",
simulationConfig);
}
@Test
public void sampleWithH265() throws Exception {
ExtractorAsserts.assertBehavior(TsExtractor::new, "media/ts/sample_h265.ts", simulationConfig);
ExtractorAsserts.assertBehavior(
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_h265.ts",
simulationConfig);
}
@Test
public void sampleWithH265RpsPred() throws Exception {
ExtractorAsserts.assertBehavior(
TsExtractor::new, "media/ts/sample_h265_rps_pred.ts", simulationConfig);
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_h265_rps_pred.ts",
simulationConfig);
}
@Test
public void sampleWithScte35() throws Exception {
ExtractorAsserts.assertBehavior(
TsExtractor::new,
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_scte35.ts",
new ExtractorAsserts.AssertionConfig.Builder()
.setDeduplicateConsecutiveFormats(true)
@ -117,7 +157,7 @@ public final class TsExtractorTest {
@Test
public void sampleWithAit() throws Exception {
ExtractorAsserts.assertBehavior(
TsExtractor::new,
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_ait.ts",
new ExtractorAsserts.AssertionConfig.Builder()
.setDeduplicateConsecutiveFormats(true)
@ -127,41 +167,63 @@ public final class TsExtractorTest {
@Test
public void sampleWithAc3() throws Exception {
ExtractorAsserts.assertBehavior(TsExtractor::new, "media/ts/sample_ac3.ts", simulationConfig);
ExtractorAsserts.assertBehavior(
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_ac3.ts",
simulationConfig);
}
@Test
public void sampleWithAc4() throws Exception {
ExtractorAsserts.assertBehavior(TsExtractor::new, "media/ts/sample_ac4.ts", simulationConfig);
ExtractorAsserts.assertBehavior(
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_ac4.ts",
simulationConfig);
}
@Test
public void sampleWithEac3() throws Exception {
ExtractorAsserts.assertBehavior(TsExtractor::new, "media/ts/sample_eac3.ts", simulationConfig);
ExtractorAsserts.assertBehavior(
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_eac3.ts",
simulationConfig);
}
@Test
public void sampleWithEac3joc() throws Exception {
ExtractorAsserts.assertBehavior(
TsExtractor::new, "media/ts/sample_eac3joc.ts", simulationConfig);
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_eac3joc.ts",
simulationConfig);
}
@Test
public void sampleWithLatm() throws Exception {
ExtractorAsserts.assertBehavior(TsExtractor::new, "media/ts/sample_latm.ts", simulationConfig);
ExtractorAsserts.assertBehavior(
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_latm.ts",
simulationConfig);
}
@Test
public void streamWithJunkData() throws Exception {
ExtractorAsserts.assertBehavior(
TsExtractor::new, "media/ts/sample_with_junk", simulationConfig);
getExtractorFactory(subtitlesParsedDuringExtraction),
"media/ts/sample_with_junk",
simulationConfig);
}
@Test
public void customPesReader() throws Exception {
CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(true, false);
TsExtractor tsExtractor =
new TsExtractor(TsExtractor.MODE_MULTI_PMT, new TimestampAdjuster(0), factory);
(TsExtractor)
getExtractorFactory(
subtitlesParsedDuringExtraction,
MODE_MULTI_PMT,
new TimestampAdjuster(0),
factory)
.create();
FakeExtractorInput input =
new FakeExtractorInput.Builder()
.setData(
@ -199,7 +261,13 @@ public final class TsExtractorTest {
public void customInitialSectionReader() throws Exception {
CustomTsPayloadReaderFactory factory = new CustomTsPayloadReaderFactory(false, true);
TsExtractor tsExtractor =
new TsExtractor(TsExtractor.MODE_MULTI_PMT, new TimestampAdjuster(0), factory);
(TsExtractor)
getExtractorFactory(
subtitlesParsedDuringExtraction,
MODE_MULTI_PMT,
new TimestampAdjuster(0),
factory)
.create();
FakeExtractorInput input =
new FakeExtractorInput.Builder()
.setData(
@ -221,6 +289,39 @@ public final class TsExtractorTest {
assertThat(factory.sdtReader.consumedSdts).isEqualTo(2);
}
private static ExtractorAsserts.ExtractorFactory getExtractorFactory(
boolean subtitlesParsedDuringExtraction) {
return getExtractorFactory(
subtitlesParsedDuringExtraction,
MODE_SINGLE_PMT,
new TimestampAdjuster(0),
new DefaultTsPayloadReaderFactory(0));
}
private static ExtractorAsserts.ExtractorFactory getExtractorFactory(
boolean subtitlesParsedDuringExtraction,
@TsExtractor.Mode int mode,
TimestampAdjuster timestampAdjuster,
TsPayloadReader.Factory payloadReaderFactory) {
SubtitleParser.Factory subtitleParserFactory;
@TsExtractor.Flags int flags;
if (subtitlesParsedDuringExtraction) {
subtitleParserFactory = new DefaultSubtitleParserFactory();
flags = 0;
} else {
subtitleParserFactory = SubtitleParser.Factory.UNSUPPORTED;
flags = FLAG_EMIT_RAW_SUBTITLE_DATA;
}
return () ->
new TsExtractor(
mode,
flags,
subtitleParserFactory,
timestampAdjuster,
payloadReaderFactory,
DEFAULT_TIMESTAMP_SEARCH_BYTES);
}
private static final class CustomTsPayloadReaderFactory implements TsPayloadReader.Factory {
private final boolean provideSdtReader;