From f18373eeb23b67e46cbc84e1467beba79eebd67b Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Wed, 12 Oct 2016 06:18:06 -0700 Subject: [PATCH] Decouple TsExtractor's readers from TrackOutputs This allows the injectable reader factory to be a stateless factory, allows the seeking to be consistent and will allow multiple CC channel support later on. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=135909712 --- .../extractor/ts/AdtsReaderTest.java | 11 ++- .../extractor/ts/TsExtractorTest.java | 28 ++++--- .../exoplayer2/extractor/ts/Ac3Extractor.java | 4 +- .../exoplayer2/extractor/ts/Ac3Reader.java | 18 +++-- .../extractor/ts/AdtsExtractor.java | 4 +- .../exoplayer2/extractor/ts/AdtsReader.java | 36 ++++++--- .../ts/DefaultStreamReaderFactory.java | 78 ++++++------------- .../exoplayer2/extractor/ts/DtsReader.java | 21 +++-- .../extractor/ts/ElementaryStreamReader.java | 41 ++++++---- .../exoplayer2/extractor/ts/H262Reader.java | 11 ++- .../exoplayer2/extractor/ts/H264Reader.java | 35 +++++---- .../exoplayer2/extractor/ts/H265Reader.java | 19 +++-- .../exoplayer2/extractor/ts/Id3Reader.java | 15 +++- .../extractor/ts/MpegAudioReader.java | 15 +++- .../exoplayer2/extractor/ts/PsExtractor.java | 10 ++- .../exoplayer2/extractor/ts/TsExtractor.java | 61 +++++++++++---- .../exoplayer2/source/hls/HlsChunkSource.java | 32 ++++---- 17 files changed, 259 insertions(+), 180 deletions(-) diff --git a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/AdtsReaderTest.java b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/AdtsReaderTest.java index 7faea926e0..d1b5ce600d 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/AdtsReaderTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/AdtsReaderTest.java @@ -16,6 +16,8 @@ package com.google.android.exoplayer2.extractor.ts; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator; +import com.google.android.exoplayer2.testutil.FakeExtractorOutput; import com.google.android.exoplayer2.testutil.FakeTrackOutput; import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.util.ParsableByteArray; @@ -66,9 +68,12 @@ public class AdtsReaderTest extends TestCase { @Override protected void setUp() throws Exception { - adtsOutput = new FakeTrackOutput(); - id3Output = new FakeTrackOutput(); - adtsReader = new AdtsReader(adtsOutput, id3Output); + FakeExtractorOutput fakeExtractorOutput = new FakeExtractorOutput(true); + adtsOutput = fakeExtractorOutput.track(0); + id3Output = fakeExtractorOutput.track(1); + adtsReader = new AdtsReader(true); + TrackIdGenerator idGenerator = new TrackIdGenerator(0, 1); + adtsReader.init(fakeExtractorOutput, idGenerator); data = new ParsableByteArray(TEST_DATA); firstFeed = true; } diff --git a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java index dfbc9120c6..1f08507599 100644 --- a/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java +++ b/library/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java @@ -22,6 +22,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.TimestampAdjuster; import com.google.android.exoplayer2.extractor.TrackOutput; +import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.EsInfo; import com.google.android.exoplayer2.testutil.FakeExtractorInput; import com.google.android.exoplayer2.testutil.FakeExtractorOutput; import com.google.android.exoplayer2.testutil.FakeTrackOutput; @@ -72,7 +73,7 @@ public final class TsExtractorTest extends InstrumentationTestCase { public void testCustomPesReader() throws Exception { CustomEsReaderFactory factory = new CustomEsReaderFactory(); - TsExtractor tsExtractor = new TsExtractor(new TimestampAdjuster(0), factory); + TsExtractor tsExtractor = new TsExtractor(new TimestampAdjuster(0), factory, false); FakeExtractorInput input = new FakeExtractorInput.Builder() .setData(TestUtil.getByteArray(getInstrumentation(), "ts/sample.ts")) .setSimulateIOErrors(false) @@ -107,18 +108,25 @@ public final class TsExtractorTest extends InstrumentationTestCase { private static final class CustomEsReader extends ElementaryStreamReader { + private final String language; + private TrackOutput output; public int packetsRead = 0; - public CustomEsReader(TrackOutput output, String language) { - super(output); - output.format(Format.createTextSampleFormat("Overriding format", "mime", null, 0, 0, - language, null, 0)); + public CustomEsReader(String language) { + this.language = language; } @Override public void seek() { } + @Override + public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { + output = extractorOutput.track(idGenerator.getNextId()); + output.format(Format.createTextSampleFormat("Overriding format", "mime", null, 0, 0, + language, null, 0)); + } + @Override public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { } @@ -148,16 +156,12 @@ public final class TsExtractorTest extends InstrumentationTestCase { } @Override - public ElementaryStreamReader onPmtEntry(int pid, int streamType, - ElementaryStreamReader.EsInfo esInfo, ExtractorOutput output) { + public ElementaryStreamReader createStreamReader(int streamType, EsInfo esInfo) { if (streamType == 3) { - // We need to manually avoid a duplicate custom reader creation. - if (reader == null) { - reader = new CustomEsReader(output.track(pid), esInfo.language); - } + reader = new CustomEsReader(esInfo.language); return reader; } else { - return defaultFactory.onPmtEntry(pid, streamType, esInfo, output); + return defaultFactory.createStreamReader(streamType, esInfo); } } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java index 979c7244a8..7fc8b429a8 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java @@ -23,6 +23,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; +import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; @@ -117,7 +118,8 @@ public final class Ac3Extractor implements Extractor { @Override public void init(ExtractorOutput output) { - reader = new Ac3Reader(output.track(0)); // TODO: Add support for embedded ID3. + reader = new Ac3Reader(); // TODO: Add support for embedded ID3. + reader.init(output, new TrackIdGenerator(0, 1)); output.endTracks(); output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET)); } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java index cbe6d2e9c8..a9d3319f87 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ts; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.audio.Ac3Util; +import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableByteArray; @@ -37,6 +38,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray; private final ParsableByteArray headerScratchBytes; private final String language; + private TrackOutput output; + private int state; private int bytesRead; @@ -54,21 +57,17 @@ import com.google.android.exoplayer2.util.ParsableByteArray; /** * Constructs a new reader for (E-)AC-3 elementary streams. - * - * @param output Track output for extracted samples. */ - public Ac3Reader(TrackOutput output) { - this(output, null); + public Ac3Reader() { + this(null); } /** * Constructs a new reader for (E-)AC-3 elementary streams. * - * @param output Track output for extracted samples. * @param language Track language. */ - public Ac3Reader(TrackOutput output, String language) { - super(output); + public Ac3Reader(String language) { headerScratchBits = new ParsableBitArray(new byte[HEADER_SIZE]); headerScratchBytes = new ParsableByteArray(headerScratchBits.data); state = STATE_FINDING_SYNC; @@ -82,6 +81,11 @@ import com.google.android.exoplayer2.util.ParsableByteArray; lastByteWas0B = false; } + @Override + public void init(ExtractorOutput extractorOutput, TrackIdGenerator generator) { + output = extractorOutput.track(generator.getNextId()); + } + @Override public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { timeUs = pesTimeUs; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java index f131d8997b..7a9cbd4bb1 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java @@ -22,6 +22,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; +import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator; import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.Util; @@ -126,7 +127,8 @@ public final class AdtsExtractor implements Extractor { @Override public void init(ExtractorOutput output) { - reader = new AdtsReader(output.track(0), output.track(1)); + reader = new AdtsReader(true); + reader.init(output, new TrackIdGenerator(0, 1)); output.endTracks(); output.seekMap(new SeekMap.Unseekable(C.TIME_UNSET)); } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsReader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsReader.java index ac493c7d32..d0474f7e44 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsReader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsReader.java @@ -19,6 +19,8 @@ import android.util.Log; import android.util.Pair; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.extractor.DummyTrackOutput; +import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.MimeTypes; @@ -53,11 +55,14 @@ import java.util.Collections; private static final int ID3_SIZE_OFFSET = 6; private static final byte[] ID3_IDENTIFIER = {'I', 'D', '3'}; + private final boolean exposeId3; private final ParsableBitArray adtsScratch; private final ParsableByteArray id3HeaderBuffer; - private final TrackOutput id3Output; private final String language; + private TrackOutput output; + private TrackOutput id3Output; + private int state; private int bytesRead; @@ -77,26 +82,21 @@ import java.util.Collections; private long currentSampleDuration; /** - * @param output A {@link TrackOutput} to which AAC samples should be written. - * @param id3Output A {@link TrackOutput} to which ID3 samples should be written. + * @param exposeId3 True if the reader should expose ID3 information. */ - public AdtsReader(TrackOutput output, TrackOutput id3Output) { - this(output, id3Output, null); + public AdtsReader(boolean exposeId3) { + this(exposeId3, null); } /** - * @param output A {@link TrackOutput} to which AAC samples should be written. - * @param id3Output A {@link TrackOutput} to which ID3 samples should be written. + * @param exposeId3 True if the reader should expose ID3 information. * @param language Track language. */ - public AdtsReader(TrackOutput output, TrackOutput id3Output, String language) { - super(output); - this.id3Output = id3Output; - id3Output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, null, - Format.NO_VALUE, null)); + public AdtsReader(boolean exposeId3, String language) { adtsScratch = new ParsableBitArray(new byte[HEADER_SIZE + CRC_SIZE]); id3HeaderBuffer = new ParsableByteArray(Arrays.copyOf(ID3_IDENTIFIER, ID3_HEADER_SIZE)); setFindingSampleState(); + this.exposeId3 = exposeId3; this.language = language; } @@ -105,6 +105,18 @@ import java.util.Collections; setFindingSampleState(); } + @Override + public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { + output = extractorOutput.track(idGenerator.getNextId()); + if (exposeId3) { + id3Output = extractorOutput.track(idGenerator.getNextId()); + id3Output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, null, + Format.NO_VALUE, null)); + } else { + id3Output = new DummyTrackOutput(); + } + } + @Override public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { timeUs = pesTimeUs; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultStreamReaderFactory.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultStreamReaderFactory.java index d5e3b78cfd..58a0e55f02 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultStreamReaderFactory.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultStreamReaderFactory.java @@ -16,9 +16,7 @@ package com.google.android.exoplayer2.extractor.ts; import android.support.annotation.IntDef; -import android.util.SparseBooleanArray; -import com.google.android.exoplayer2.extractor.DummyTrackOutput; -import com.google.android.exoplayer2.extractor.ExtractorOutput; +import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.EsInfo; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -28,80 +26,54 @@ import java.lang.annotation.RetentionPolicy; public final class DefaultStreamReaderFactory implements ElementaryStreamReader.Factory { /** - * Flags controlling what workarounds are enabled for elementary stream readers. + * Flags controlling elementary stream readers behaviour. */ @Retention(RetentionPolicy.SOURCE) - @IntDef(flag = true, value = {WORKAROUND_ALLOW_NON_IDR_KEYFRAMES, WORKAROUND_IGNORE_AAC_STREAM, - WORKAROUND_IGNORE_H264_STREAM, WORKAROUND_DETECT_ACCESS_UNITS, WORKAROUND_MAP_BY_TYPE}) - public @interface WorkaroundFlags { + @IntDef(flag = true, value = {FLAG_ALLOW_NON_IDR_KEYFRAMES, FLAG_IGNORE_AAC_STREAM, + FLAG_IGNORE_H264_STREAM, FLAG_DETECT_ACCESS_UNITS}) + public @interface Flags { } - public static final int WORKAROUND_ALLOW_NON_IDR_KEYFRAMES = 1; - public static final int WORKAROUND_IGNORE_AAC_STREAM = 2; - public static final int WORKAROUND_IGNORE_H264_STREAM = 4; - public static final int WORKAROUND_DETECT_ACCESS_UNITS = 8; - public static final int WORKAROUND_MAP_BY_TYPE = 16; + public static final int FLAG_ALLOW_NON_IDR_KEYFRAMES = 1; + public static final int FLAG_IGNORE_AAC_STREAM = 2; + public static final int FLAG_IGNORE_H264_STREAM = 4; + public static final int FLAG_DETECT_ACCESS_UNITS = 8; - private static final int BASE_EMBEDDED_TRACK_ID = 0x2000; // 0xFF + 1. - - private final SparseBooleanArray trackIds; - @WorkaroundFlags - private final int workaroundFlags; - private Id3Reader id3Reader; - private int nextEmbeddedTrackId = BASE_EMBEDDED_TRACK_ID; + @Flags + private final int flags; public DefaultStreamReaderFactory() { this(0); } - public DefaultStreamReaderFactory(int workaroundFlags) { - trackIds = new SparseBooleanArray(); - this.workaroundFlags = workaroundFlags; + public DefaultStreamReaderFactory(@Flags int flags) { + this.flags = flags; } @Override - public ElementaryStreamReader onPmtEntry(int pid, int streamType, - ElementaryStreamReader.EsInfo esInfo, ExtractorOutput output) { - - if ((workaroundFlags & WORKAROUND_MAP_BY_TYPE) != 0 && id3Reader == null) { - // Setup an ID3 track regardless of whether there's a corresponding entry, in case one - // appears intermittently during playback. See b/20261500. - id3Reader = new Id3Reader(output.track(TsExtractor.TS_STREAM_TYPE_ID3)); - } - int trackId = (workaroundFlags & WORKAROUND_MAP_BY_TYPE) != 0 ? streamType : pid; - if (trackIds.get(trackId)) { - return null; - } - trackIds.put(trackId, true); + public ElementaryStreamReader createStreamReader(int streamType, EsInfo esInfo) { switch (streamType) { case TsExtractor.TS_STREAM_TYPE_MPA: case TsExtractor.TS_STREAM_TYPE_MPA_LSF: - return new MpegAudioReader(output.track(trackId), esInfo.language); + return new MpegAudioReader(esInfo.language); case TsExtractor.TS_STREAM_TYPE_AAC: - return (workaroundFlags & WORKAROUND_IGNORE_AAC_STREAM) != 0 ? null - : new AdtsReader(output.track(trackId), new DummyTrackOutput(), esInfo.language); + return (flags & FLAG_IGNORE_AAC_STREAM) != 0 ? null + : new AdtsReader(false, esInfo.language); case TsExtractor.TS_STREAM_TYPE_AC3: case TsExtractor.TS_STREAM_TYPE_E_AC3: - return new Ac3Reader(output.track(trackId), esInfo.language); + return new Ac3Reader(esInfo.language); case TsExtractor.TS_STREAM_TYPE_DTS: case TsExtractor.TS_STREAM_TYPE_HDMV_DTS: - return new DtsReader(output.track(trackId), esInfo.language); + return new DtsReader(esInfo.language); case TsExtractor.TS_STREAM_TYPE_H262: - return new H262Reader(output.track(trackId)); + return new H262Reader(); case TsExtractor.TS_STREAM_TYPE_H264: - return (workaroundFlags & WORKAROUND_IGNORE_H264_STREAM) != 0 - ? null : new H264Reader(output.track(trackId), - new SeiReader(output.track(nextEmbeddedTrackId++)), - (workaroundFlags & WORKAROUND_ALLOW_NON_IDR_KEYFRAMES) != 0, - (workaroundFlags & WORKAROUND_DETECT_ACCESS_UNITS) != 0); + return (flags & FLAG_IGNORE_H264_STREAM) != 0 ? null + : new H264Reader((flags & FLAG_ALLOW_NON_IDR_KEYFRAMES) != 0, + (flags & FLAG_DETECT_ACCESS_UNITS) != 0); case TsExtractor.TS_STREAM_TYPE_H265: - return new H265Reader(output.track(trackId), - new SeiReader(output.track(nextEmbeddedTrackId++))); + return new H265Reader(); case TsExtractor.TS_STREAM_TYPE_ID3: - if ((workaroundFlags & WORKAROUND_MAP_BY_TYPE) != 0) { - return id3Reader; - } else { - return new Id3Reader(output.track(nextEmbeddedTrackId++)); - } + return new Id3Reader(); default: return null; } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DtsReader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DtsReader.java index e2112df755..42223ef285 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DtsReader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/DtsReader.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ts; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.audio.DtsUtil; +import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.ParsableByteArray; @@ -37,6 +38,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray; private final ParsableByteArray headerScratchBytes; private final String language; + private TrackOutput output; + private int state; private int bytesRead; @@ -54,20 +57,9 @@ import com.google.android.exoplayer2.util.ParsableByteArray; /** * Constructs a new reader for DTS elementary streams. * - * @param output Track output for extracted samples. - */ - public DtsReader(TrackOutput output) { - this(output, null); - } - - /** - * Constructs a new reader for DTS elementary streams. - * - * @param output Track output for extracted samples. * @param language Track language. */ - public DtsReader(TrackOutput output, String language) { - super(output); + public DtsReader(String language) { headerScratchBytes = new ParsableByteArray(new byte[HEADER_SIZE]); headerScratchBytes.data[0] = (byte) ((SYNC_VALUE >> 24) & 0xFF); headerScratchBytes.data[1] = (byte) ((SYNC_VALUE >> 16) & 0xFF); @@ -84,6 +76,11 @@ import com.google.android.exoplayer2.util.ParsableByteArray; syncBytes = 0; } + @Override + public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { + output = extractorOutput.track(idGenerator.getNextId()); + } + @Override public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { timeUs = pesTimeUs; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java index 7a220c98b3..e2efbebb43 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java @@ -33,17 +33,12 @@ public abstract class ElementaryStreamReader { * Returns an {@link ElementaryStreamReader} for a given PMT entry. May return null if the * stream type is not supported or if the stream already has a reader assigned to it. * - * @param pid The pid for the PMT entry. - * @param streamType One of the {@link TsExtractor}{@code .TS_STREAM_TYPE_*} constants defining - * the type of the stream. - * @param esInfo The descriptor information linked to the elementary stream. - * @param output The {@link ExtractorOutput} that provides the {@link TrackOutput}s for the - * created readers. + * @param streamType Stream type value as defined in the PMT entry or associated descriptors. + * @param esInfo Information associated to the elementary stream provided in the PMT. * @return An {@link ElementaryStreamReader} for the elementary streams carried by the provided * pid. {@code null} if the stream is not supported or if it should be ignored. */ - ElementaryStreamReader onPmtEntry(int pid, int streamType, EsInfo esInfo, - ExtractorOutput output); + ElementaryStreamReader createStreamReader(int streamType, EsInfo esInfo); } @@ -70,13 +65,24 @@ public abstract class ElementaryStreamReader { } - protected final TrackOutput output; - /** - * @param output A {@link TrackOutput} to which samples should be written. + * Generates track ids for initializing {@link ElementaryStreamReader}s' {@link TrackOutput}s. */ - protected ElementaryStreamReader(TrackOutput output) { - this.output = output; + public static final class TrackIdGenerator { + + private final int firstId; + private final int idIncrement; + private int generatedIdCount; + + public TrackIdGenerator(int firstId, int idIncrement) { + this.firstId = firstId; + this.idIncrement = idIncrement; + } + + public int getNextId() { + return firstId + idIncrement * generatedIdCount++; + } + } /** @@ -84,6 +90,15 @@ public abstract class ElementaryStreamReader { */ public abstract void seek(); + /** + * Initializes the reader by providing outputs and ids for the tracks. + * + * @param extractorOutput The {@link ExtractorOutput} that receives the extracted data. + * @param idGenerator A {@link TrackIdGenerator} that generates unique track ids for the + * {@link TrackOutput}s. + */ + public abstract void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator); + /** * Called when a packet starts. * diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java index cdbd8e391d..fbfe7e1209 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ts; import android.util.Pair; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.NalUnitUtil; @@ -35,6 +36,8 @@ import java.util.Collections; private static final int START_EXTENSION = 0xB5; private static final int START_GROUP = 0xB8; + private TrackOutput output; + // Maps (frame_rate_code - 1) indices to values, as defined in ITU-T H.262 Table 6-4. private static final double[] FRAME_RATE_VALUES = new double[] { 24000d / 1001, 24, 25, 30000d / 1001, 30, 50, 60000d / 1001, 60}; @@ -58,8 +61,7 @@ import java.util.Collections; private long framePosition; private long frameTimeUs; - public H262Reader(TrackOutput output) { - super(output); + public H262Reader() { prefixFlags = new boolean[4]; csdBuffer = new CsdBuffer(128); } @@ -73,6 +75,11 @@ import java.util.Collections; totalBytesWritten = 0; } + @Override + public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { + output = extractorOutput.track(idGenerator.getNextId()); + } + @Override public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { pesPtsUsAvailable = pesTimeUs != C.TIME_UNSET; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java index ce7b7e6383..6fee9ea6d7 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ts; import android.util.SparseArray; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.NalUnitUtil; @@ -37,17 +38,20 @@ import java.util.List; private static final int NAL_UNIT_TYPE_SPS = 7; // Sequence parameter set private static final int NAL_UNIT_TYPE_PPS = 8; // Picture parameter set - // State that should not be reset on seek. - private boolean hasOutputFormat; - - // State that should be reset on seek. - private final SeiReader seiReader; - private final boolean[] prefixFlags; - private final SampleReader sampleReader; + private final boolean allowNonIdrKeyframes; + private final boolean detectAccessUnits; private final NalUnitTargetBuffer sps; private final NalUnitTargetBuffer pps; private final NalUnitTargetBuffer sei; private long totalBytesWritten; + private final boolean[] prefixFlags; + + private TrackOutput output; + private SeiReader seiReader; + private SampleReader sampleReader; + + // State that should not be reset on seek. + private boolean hasOutputFormat; // Per packet state that gets reset at the start of each packet. private long pesTimeUs; @@ -56,19 +60,15 @@ import java.util.List; private final ParsableByteArray seiWrapper; /** - * @param output A {@link TrackOutput} to which H.264 samples should be written. - * @param seiReader A reader for CEA-608 samples in SEI NAL units. * @param allowNonIdrKeyframes Whether to treat samples consisting of non-IDR I slices as * synchronization samples (key-frames). * @param detectAccessUnits Whether to split the input stream into access units (samples) based on * slice headers. Pass {@code false} if the stream contains access unit delimiters (AUDs). */ - public H264Reader(TrackOutput output, SeiReader seiReader, boolean allowNonIdrKeyframes, - boolean detectAccessUnits) { - super(output); - this.seiReader = seiReader; + public H264Reader(boolean allowNonIdrKeyframes, boolean detectAccessUnits) { prefixFlags = new boolean[3]; - sampleReader = new SampleReader(output, allowNonIdrKeyframes, detectAccessUnits); + this.allowNonIdrKeyframes = allowNonIdrKeyframes; + this.detectAccessUnits = detectAccessUnits; sps = new NalUnitTargetBuffer(NAL_UNIT_TYPE_SPS, 128); pps = new NalUnitTargetBuffer(NAL_UNIT_TYPE_PPS, 128); sei = new NalUnitTargetBuffer(NAL_UNIT_TYPE_SEI, 128); @@ -85,6 +85,13 @@ import java.util.List; totalBytesWritten = 0; } + @Override + public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { + output = extractorOutput.track(idGenerator.getNextId()); + sampleReader = new SampleReader(output, allowNonIdrKeyframes, detectAccessUnits); + seiReader = new SeiReader(extractorOutput.track(idGenerator.getNextId())); + } + @Override public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { this.pesTimeUs = pesTimeUs; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java index c8828cefa6..6283371a19 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java @@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ts; import android.util.Log; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.NalUnitUtil; @@ -42,11 +43,13 @@ import java.util.Collections; private static final int PREFIX_SEI_NUT = 39; private static final int SUFFIX_SEI_NUT = 40; + private TrackOutput output; + private SeiReader seiReader; + // State that should not be reset on seek. private boolean hasOutputFormat; // State that should be reset on seek. - private final SeiReader seiReader; private final boolean[] prefixFlags; private final NalUnitTargetBuffer vps; private final NalUnitTargetBuffer sps; @@ -62,13 +65,7 @@ import java.util.Collections; // Scratch variables to avoid allocations. private final ParsableByteArray seiWrapper; - /** - * @param output A {@link TrackOutput} to which H.265 samples should be written. - * @param seiReader A reader for CEA-608 samples in SEI NAL units. - */ - public H265Reader(TrackOutput output, SeiReader seiReader) { - super(output); - this.seiReader = seiReader; + public H265Reader() { prefixFlags = new boolean[3]; vps = new NalUnitTargetBuffer(VPS_NUT, 128); sps = new NalUnitTargetBuffer(SPS_NUT, 128); @@ -91,6 +88,12 @@ import java.util.Collections; totalBytesWritten = 0; } + @Override + public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { + output = extractorOutput.track(idGenerator.getNextId()); + seiReader = new SeiReader(extractorOutput.track(idGenerator.getNextId())); + } + @Override public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { this.pesTimeUs = pesTimeUs; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Id3Reader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Id3Reader.java index 1001f1a1ae..2c657d4aca 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Id3Reader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/Id3Reader.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.ts; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.ParsableByteArray; @@ -30,6 +31,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray; private final ParsableByteArray id3Header; + private TrackOutput output; + // State that should be reset on seek. private boolean writingSample; @@ -38,10 +41,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray; private int sampleSize; private int sampleBytesRead; - public Id3Reader(TrackOutput output) { - super(output); - output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, null, Format.NO_VALUE, - null)); + public Id3Reader() { id3Header = new ParsableByteArray(ID3_HEADER_SIZE); } @@ -50,6 +50,13 @@ import com.google.android.exoplayer2.util.ParsableByteArray; writingSample = false; } + @Override + public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { + output = extractorOutput.track(idGenerator.getNextId()); + output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, null, Format.NO_VALUE, + null)); + } + @Override public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { if (!dataAlignmentIndicator) { diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/MpegAudioReader.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/MpegAudioReader.java index c78882c2c9..d25d0703ae 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/MpegAudioReader.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/MpegAudioReader.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.ts; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.MpegAudioHeader; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.ParsableByteArray; @@ -36,6 +37,8 @@ import com.google.android.exoplayer2.util.ParsableByteArray; private final MpegAudioHeader header; private final String language; + private TrackOutput output; + private int state; private int frameBytesRead; private boolean hasOutputFormat; @@ -50,12 +53,11 @@ import com.google.android.exoplayer2.util.ParsableByteArray; // The timestamp to attach to the next sample in the current packet. private long timeUs; - public MpegAudioReader(TrackOutput output) { - this(output, null); + public MpegAudioReader() { + this(null); } - public MpegAudioReader(TrackOutput output, String language) { - super(output); + public MpegAudioReader(String language) { state = STATE_FINDING_HEADER; // The first byte of an MPEG Audio frame header is always 0xFF. headerScratch = new ParsableByteArray(4); @@ -71,6 +73,11 @@ import com.google.android.exoplayer2.util.ParsableByteArray; lastByteWasFF = false; } + @Override + public void init(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) { + output = extractorOutput.track(idGenerator.getNextId()); + } + @Override public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { timeUs = pesTimeUs; diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java index 35eb519f09..b615a3e8ee 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java @@ -24,6 +24,7 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.TimestampAdjuster; +import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator; import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableByteArray; import java.io.IOException; @@ -49,6 +50,7 @@ public final class PsExtractor implements Extractor { private static final int SYSTEM_HEADER_START_CODE = 0x000001BB; private static final int PACKET_START_CODE_PREFIX = 0x000001; private static final int MPEG_PROGRAM_END_CODE = 0x000001B9; + private static final int MAX_STREAM_ID_PLUS_ONE = 0x100; private static final long MAX_SEARCH_LENGTH = 1024 * 1024; public static final int PRIVATE_STREAM_1 = 0xBD; @@ -189,16 +191,18 @@ public final class PsExtractor implements Extractor { // Private stream, used for AC3 audio. // NOTE: This may need further parsing to determine if its DTS, but that's likely only // valid for DVDs. - elementaryStreamReader = new Ac3Reader(output.track(streamId)); + elementaryStreamReader = new Ac3Reader(); foundAudioTrack = true; } else if (!foundAudioTrack && (streamId & AUDIO_STREAM_MASK) == AUDIO_STREAM) { - elementaryStreamReader = new MpegAudioReader(output.track(streamId)); + elementaryStreamReader = new MpegAudioReader(); foundAudioTrack = true; } else if (!foundVideoTrack && (streamId & VIDEO_STREAM_MASK) == VIDEO_STREAM) { - elementaryStreamReader = new H262Reader(output.track(streamId)); + elementaryStreamReader = new H262Reader(); foundVideoTrack = true; } if (elementaryStreamReader != null) { + TrackIdGenerator idGenerator = new TrackIdGenerator(streamId, MAX_STREAM_ID_PLUS_ONE); + elementaryStreamReader.init(output, idGenerator); payloadReader = new PesReader(elementaryStreamReader, timestampAdjuster); psPayloadReaders.put(streamId, payloadReader); } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java index 0248db9650..f5a7d090e1 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.ts; import android.util.Log; import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.util.SparseIntArray; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.extractor.Extractor; @@ -26,6 +27,9 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory; import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.TimestampAdjuster; +import com.google.android.exoplayer2.extractor.TrackOutput; +import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.EsInfo; +import com.google.android.exoplayer2.extractor.ts.ElementaryStreamReader.TrackIdGenerator; import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableByteArray; @@ -50,11 +54,6 @@ public final class TsExtractor implements Extractor { }; - private static final String TAG = "TsExtractor"; - - private static final int TS_PACKET_SIZE = 188; - private static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet. - private static final int TS_PAT_PID = 0; public static final int TS_STREAM_TYPE_MPA = 0x03; public static final int TS_STREAM_TYPE_MPA_LSF = 0x04; @@ -68,6 +67,12 @@ public final class TsExtractor implements Extractor { public static final int TS_STREAM_TYPE_H265 = 0x24; public static final int TS_STREAM_TYPE_ID3 = 0x15; + private static final String TAG = "TsExtractor"; + + private static final int TS_PACKET_SIZE = 188; + private static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet. + private static final int TS_PAT_PID = 0; + private static final int MAX_PID_PLUS_ONE = 0x2000; private static final long AC3_FORMAT_IDENTIFIER = Util.getIntegerCodeForString("AC-3"); private static final long E_AC3_FORMAT_IDENTIFIER = Util.getIntegerCodeForString("EAC3"); @@ -76,15 +81,18 @@ public final class TsExtractor implements Extractor { private static final int BUFFER_PACKET_COUNT = 5; // Should be at least 2 private static final int BUFFER_SIZE = TS_PACKET_SIZE * BUFFER_PACKET_COUNT; + private final boolean mapByType; private final TimestampAdjuster timestampAdjuster; private final ParsableByteArray tsPacketBuffer; private final ParsableBitArray tsScratch; private final SparseIntArray continuityCounters; private final ElementaryStreamReader.Factory streamReaderFactory; - /* package */ final SparseArray tsPayloadReaders; // Indexed by pid + private final SparseArray tsPayloadReaders; // Indexed by pid + private final SparseBooleanArray trackIds; // Accessed only by the loading thread. private ExtractorOutput output; + private ElementaryStreamReader id3Reader; public TsExtractor() { this(new TimestampAdjuster(0)); @@ -94,19 +102,23 @@ public final class TsExtractor implements Extractor { * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. */ public TsExtractor(TimestampAdjuster timestampAdjuster) { - this(timestampAdjuster, new DefaultStreamReaderFactory()); + this(timestampAdjuster, new DefaultStreamReaderFactory(), false); } /** * @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps. * @param customReaderFactory Factory for injecting a custom set of elementary stream readers. + * @param mapByType True if {@link TrackOutput}s should be mapped by their type, false to map them + * by their PID. */ public TsExtractor(TimestampAdjuster timestampAdjuster, - ElementaryStreamReader.Factory customReaderFactory) { + ElementaryStreamReader.Factory customReaderFactory, boolean mapByType) { this.timestampAdjuster = timestampAdjuster; this.streamReaderFactory = Assertions.checkNotNull(customReaderFactory); + this.mapByType = mapByType; tsPacketBuffer = new ParsableByteArray(BUFFER_SIZE); tsScratch = new ParsableBitArray(new byte[3]); + trackIds = new SparseBooleanArray(); tsPayloadReaders = new SparseArray<>(); tsPayloadReaders.put(TS_PAT_PID, new PatReader()); continuityCounters = new SparseIntArray(); @@ -413,6 +425,14 @@ public final class TsExtractor implements Extractor { // Skip the descriptors. sectionData.skipBytes(programInfoLength); + if (mapByType && id3Reader == null) { + // Setup an ID3 track regardless of whether there's a corresponding entry, in case one + // appears intermittently during playback. See [Internal: b/20261500]. + EsInfo dummyEsInfo = new EsInfo(TS_STREAM_TYPE_ID3, null, new byte[0]); + id3Reader = streamReaderFactory.createStreamReader(TS_STREAM_TYPE_ID3, dummyEsInfo); + id3Reader.init(output, new TrackIdGenerator(TS_STREAM_TYPE_ID3, MAX_PID_PLUS_ONE)); + } + int remainingEntriesLength = sectionLength - 9 /* Length of fields before descriptors */ - programInfoLength - 4 /* CRC length */; while (remainingEntriesLength > 0) { @@ -422,17 +442,28 @@ public final class TsExtractor implements Extractor { int elementaryPid = pmtScratch.readBits(13); pmtScratch.skipBits(4); // reserved int esInfoLength = pmtScratch.readBits(12); // ES_info_length. - ElementaryStreamReader.EsInfo esInfo = readEsInfo(sectionData, esInfoLength); + EsInfo esInfo = readEsInfo(sectionData, esInfoLength); if (streamType == 0x06) { streamType = esInfo.streamType; } remainingEntriesLength -= esInfoLength + 5; - ElementaryStreamReader pesPayloadReader = streamReaderFactory.onPmtEntry(elementaryPid, - streamType, esInfo, output); + + int trackId = mapByType ? streamType : elementaryPid; + if (trackIds.get(trackId)) { + continue; + } + trackIds.put(trackId, true); + + ElementaryStreamReader pesPayloadReader; + if (mapByType && streamType == TS_STREAM_TYPE_ID3) { + pesPayloadReader = id3Reader; + } else { + pesPayloadReader = streamReaderFactory.createStreamReader(streamType, esInfo); + pesPayloadReader.init(output, new TrackIdGenerator(trackId, MAX_PID_PLUS_ONE)); + } if (pesPayloadReader != null) { - tsPayloadReaders.put(elementaryPid, - new PesReader(pesPayloadReader, timestampAdjuster)); + tsPayloadReaders.put(elementaryPid, new PesReader(pesPayloadReader, timestampAdjuster)); } } @@ -447,7 +478,7 @@ public final class TsExtractor implements Extractor { * @param length The length of descriptors to read from the current position in {@code data}. * @return The stream info read from the available descriptors. */ - private ElementaryStreamReader.EsInfo readEsInfo(ParsableByteArray data, int length) { + private EsInfo readEsInfo(ParsableByteArray data, int length) { int descriptorsStartPosition = data.getPosition(); int descriptorsEndPosition = descriptorsStartPosition + length; int streamType = -1; @@ -479,7 +510,7 @@ public final class TsExtractor implements Extractor { data.skipBytes(positionOfNextDescriptor - data.getPosition()); } data.setPosition(descriptorsEndPosition); - return new ElementaryStreamReader.EsInfo(streamType, language, + return new EsInfo(streamType, language, Arrays.copyOfRange(sectionData.data, descriptorsStartPosition, descriptorsEndPosition)); } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java index caca5d9b83..6dade6a02f 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java @@ -369,29 +369,29 @@ import java.util.Locale; } } else if (needNewExtractor) { // MPEG-2 TS segments, but we need a new extractor. - // This flag ensures the change of pid between streams does not affect the sample queues. - @DefaultStreamReaderFactory.WorkaroundFlags - int workaroundFlags = DefaultStreamReaderFactory.WORKAROUND_MAP_BY_TYPE; - String codecs = variants[newVariantIndex].format.codecs; - if (!TextUtils.isEmpty(codecs)) { - // Sometimes AAC and H264 streams are declared in TS chunks even though they don't really - // exist. If we know from the codec attribute that they don't exist, then we can explicitly - // ignore them even if they're declared. - if (!MimeTypes.AUDIO_AAC.equals(MimeTypes.getAudioMediaMimeType(codecs))) { - workaroundFlags |= DefaultStreamReaderFactory.WORKAROUND_IGNORE_AAC_STREAM; - } - if (!MimeTypes.VIDEO_H264.equals(MimeTypes.getVideoMediaMimeType(codecs))) { - workaroundFlags |= DefaultStreamReaderFactory.WORKAROUND_IGNORE_H264_STREAM; - } - } isTimestampMaster = true; if (useInitializedExtractor) { extractor = lastLoadedInitializationChunk.extractor; } else { timestampAdjuster = timestampAdjusterProvider.getAdjuster( segment.discontinuitySequenceNumber, startTimeUs); + // This flag ensures the change of pid between streams does not affect the sample queues. + @DefaultStreamReaderFactory.Flags + int esReaderFactoryFlags = 0; + String codecs = variants[newVariantIndex].format.codecs; + if (!TextUtils.isEmpty(codecs)) { + // Sometimes AAC and H264 streams are declared in TS chunks even though they don't really + // exist. If we know from the codec attribute that they don't exist, then we can + // explicitly ignore them even if they're declared. + if (!MimeTypes.AUDIO_AAC.equals(MimeTypes.getAudioMediaMimeType(codecs))) { + esReaderFactoryFlags |= DefaultStreamReaderFactory.FLAG_IGNORE_AAC_STREAM; + } + if (!MimeTypes.VIDEO_H264.equals(MimeTypes.getVideoMediaMimeType(codecs))) { + esReaderFactoryFlags |= DefaultStreamReaderFactory.FLAG_IGNORE_H264_STREAM; + } + } extractor = new TsExtractor(timestampAdjuster, - new DefaultStreamReaderFactory(workaroundFlags)); + new DefaultStreamReaderFactory(esReaderFactoryFlags), true); } } else { // MPEG-2 TS segments, and we need to continue using the same extractor.