diff --git a/RELEASENOTES.md b/RELEASENOTES.md index aca11cb403..cc604b5a5a 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,8 @@ ### dev-v2 (not yet released) ### +* Use Transport Stream's random access indicator to minimize the need for + FLAG_ALLOW_NON_IDR_KEYFRAMES. * Support for playing spherical videos on Daydream. * Improve decoder re-use between playbacks. TODO: Write and link a blog post here ([#2826](https://github.com/google/ExoPlayer/issues/2826)). diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java index 93ce15a7ab..3741d52294 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Extractor.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.extractor.ts; +import static com.google.android.exoplayer2.extractor.ts.TsPayloadReader.FLAG_DATA_ALIGNMENT_INDICATOR; + import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.audio.Ac3Util; import com.google.android.exoplayer2.extractor.Extractor; @@ -140,7 +142,7 @@ public final class Ac3Extractor implements Extractor { if (!startedPacket) { // Pass data to the reader as though it's contained within a single infinitely long packet. - reader.packetStarted(firstSampleTimestampUs, true); + reader.packetStarted(firstSampleTimestampUs, FLAG_DATA_ALIGNMENT_INDICATOR); startedPacket = true; } // TODO: Make it possible for the reader to consume the dataSource directly, so that it becomes diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java index 2ef9704a7a..93724be92d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Ac3Reader.java @@ -100,7 +100,7 @@ public final class Ac3Reader implements ElementaryStreamReader { } @Override - public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { + public void packetStarted(long pesTimeUs, @TsPayloadReader.Flags int flags) { timeUs = pesTimeUs; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java index 04a6b571bd..77b79fa19f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsExtractor.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.extractor.ts; +import static com.google.android.exoplayer2.extractor.ts.TsPayloadReader.FLAG_DATA_ALIGNMENT_INDICATOR; + import android.support.annotation.IntDef; import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; @@ -202,7 +204,7 @@ public final class AdtsExtractor implements Extractor { if (!startedPacket) { // Pass data to the reader as though it's contained within a single infinitely long packet. - reader.packetStarted(firstSampleTimestampUs, true); + reader.packetStarted(firstSampleTimestampUs, FLAG_DATA_ALIGNMENT_INDICATOR); startedPacket = true; } // TODO: Make it possible for reader to consume the dataSource directly, so that it becomes diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsReader.java index e31f67c77c..589b543170 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/AdtsReader.java @@ -141,7 +141,7 @@ public final class AdtsReader implements ElementaryStreamReader { } @Override - public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { + public void packetStarted(long pesTimeUs, @TsPayloadReader.Flags int flags) { timeUs = pesTimeUs; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DtsReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DtsReader.java index 2e45853951..1f9b0e79d4 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DtsReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DtsReader.java @@ -80,7 +80,7 @@ public final class DtsReader implements ElementaryStreamReader { } @Override - public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { + public void packetStarted(long pesTimeUs, @TsPayloadReader.Flags int flags) { timeUs = pesTimeUs; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DvbSubtitleReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DvbSubtitleReader.java index 0944d1810e..3f0a772b1c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DvbSubtitleReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DvbSubtitleReader.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.extractor.ts; +import static com.google.android.exoplayer2.extractor.ts.TsPayloadReader.FLAG_DATA_ALIGNMENT_INDICATOR; + import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.extractor.ExtractorOutput; @@ -73,8 +75,8 @@ public final class DvbSubtitleReader implements ElementaryStreamReader { } @Override - public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { - if (!dataAlignmentIndicator) { + public void packetStarted(long pesTimeUs, @TsPayloadReader.Flags int flags) { + if ((flags & FLAG_DATA_ALIGNMENT_INDICATOR) == 0) { return; } writingSample = true; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java index fa7f78c8c0..e022fc237b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/ElementaryStreamReader.java @@ -43,9 +43,9 @@ public interface ElementaryStreamReader { * Called when a packet starts. * * @param pesTimeUs The timestamp associated with the packet. - * @param dataAlignmentIndicator The data alignment indicator associated with the packet. + * @param flags See {@link TsPayloadReader.Flags}. */ - void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator); + void packetStarted(long pesTimeUs, @TsPayloadReader.Flags int flags); /** * Consumes (possibly partial) data from the current packet. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java index e9827893ee..1564157d44 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H262Reader.java @@ -107,7 +107,8 @@ public final class H262Reader implements ElementaryStreamReader { } @Override - public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { + public void packetStarted(long pesTimeUs, @TsPayloadReader.Flags int flags) { + // TODO (Internal b/32267012): Consider using random access indicator. this.pesTimeUs = pesTimeUs; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java index 45e094f69d..d249c1b9da 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H264Reader.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.extractor.ts; +import static com.google.android.exoplayer2.extractor.ts.TsPayloadReader.FLAG_RANDOM_ACCESS_INDICATOR; + import android.util.SparseArray; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; @@ -56,9 +58,12 @@ public final class H264Reader implements ElementaryStreamReader { // State that should not be reset on seek. private boolean hasOutputFormat; - // Per packet state that gets reset at the start of each packet. + // Per PES packet state that gets reset at the start of each PES packet. private long pesTimeUs; + // State inherited from the TS packet header. + private boolean randomAccessIndicator; + // Scratch variables to avoid allocations. private final ParsableByteArray seiWrapper; @@ -88,6 +93,7 @@ public final class H264Reader implements ElementaryStreamReader { sei.reset(); sampleReader.reset(); totalBytesWritten = 0; + randomAccessIndicator = false; } @Override @@ -100,8 +106,9 @@ public final class H264Reader implements ElementaryStreamReader { } @Override - public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { + public void packetStarted(long pesTimeUs, @TsPayloadReader.Flags int flags) { this.pesTimeUs = pesTimeUs; + randomAccessIndicator |= (flags & FLAG_RANDOM_ACCESS_INDICATOR) != 0; } @Override @@ -220,12 +227,17 @@ public final class H264Reader implements ElementaryStreamReader { seiWrapper.setPosition(4); // NAL prefix and nal_unit() header. seiReader.consume(pesTimeUs, seiWrapper); } - sampleReader.endNalUnit(position, offset); + boolean sampleIsKeyFrame = + sampleReader.endNalUnit(position, offset, hasOutputFormat, randomAccessIndicator); + if (sampleIsKeyFrame) { + // This is either an IDR frame or the first I-frame since the random access indicator, so mark + // it as a keyframe. Clear the flag so that subsequent non-IDR I-frames are not marked as + // keyframes until we see another random access indicator. + randomAccessIndicator = false; + } } - /** - * Consumes a stream of NAL units and outputs samples. - */ + /** Consumes a stream of NAL units and outputs samples. */ private static final class SampleReader { private static final int DEFAULT_BUFFER_SIZE = 128; @@ -430,11 +442,12 @@ public final class H264Reader implements ElementaryStreamReader { isFilling = false; } - public void endNalUnit(long position, int offset) { + public boolean endNalUnit( + long position, int offset, boolean hasOutputFormat, boolean randomAccessIndicator) { if (nalUnitType == NAL_UNIT_TYPE_AUD || (detectAccessUnits && sliceHeader.isFirstVclNalUnitOfPicture(previousSliceHeader))) { // If the NAL unit ending is the start of a new sample, output the previous one. - if (readingSample) { + if (hasOutputFormat && readingSample) { int nalUnitLength = (int) (position - nalUnitStartPosition); outputSample(offset + nalUnitLength); } @@ -443,8 +456,12 @@ public final class H264Reader implements ElementaryStreamReader { sampleIsKeyframe = false; readingSample = true; } - sampleIsKeyframe |= nalUnitType == NAL_UNIT_TYPE_IDR || (allowNonIdrKeyframes - && nalUnitType == NAL_UNIT_TYPE_NON_IDR && sliceHeader.isISlice()); + boolean treatIFrameAsKeyframe = + allowNonIdrKeyframes ? sliceHeader.isISlice() : randomAccessIndicator; + sampleIsKeyframe |= + nalUnitType == NAL_UNIT_TYPE_IDR + || (treatIFrameAsKeyframe && nalUnitType == NAL_UNIT_TYPE_NON_IDR); + return sampleIsKeyframe; } private void outputSample(int offset) { @@ -486,10 +503,21 @@ public final class H264Reader implements ElementaryStreamReader { hasSliceType = true; } - public void setAll(SpsData spsData, int nalRefIdc, int sliceType, int frameNum, - int picParameterSetId, boolean fieldPicFlag, boolean bottomFieldFlagPresent, - boolean bottomFieldFlag, boolean idrPicFlag, int idrPicId, int picOrderCntLsb, - int deltaPicOrderCntBottom, int deltaPicOrderCnt0, int deltaPicOrderCnt1) { + public void setAll( + SpsData spsData, + int nalRefIdc, + int sliceType, + int frameNum, + int picParameterSetId, + boolean fieldPicFlag, + boolean bottomFieldFlagPresent, + boolean bottomFieldFlag, + boolean idrPicFlag, + int idrPicId, + int picOrderCntLsb, + int deltaPicOrderCntBottom, + int deltaPicOrderCnt0, + int deltaPicOrderCnt1) { this.spsData = spsData; this.nalRefIdc = nalRefIdc; this.sliceType = sliceType; @@ -514,23 +542,26 @@ public final class H264Reader implements ElementaryStreamReader { private boolean isFirstVclNalUnitOfPicture(SliceHeaderData other) { // See ISO 14496-10 subsection 7.4.1.2.4. - return isComplete && (!other.isComplete || frameNum != other.frameNum - || picParameterSetId != other.picParameterSetId || fieldPicFlag != other.fieldPicFlag - || (bottomFieldFlagPresent && other.bottomFieldFlagPresent - && bottomFieldFlag != other.bottomFieldFlag) - || (nalRefIdc != other.nalRefIdc && (nalRefIdc == 0 || other.nalRefIdc == 0)) - || (spsData.picOrderCountType == 0 && other.spsData.picOrderCountType == 0 - && (picOrderCntLsb != other.picOrderCntLsb - || deltaPicOrderCntBottom != other.deltaPicOrderCntBottom)) - || (spsData.picOrderCountType == 1 && other.spsData.picOrderCountType == 1 - && (deltaPicOrderCnt0 != other.deltaPicOrderCnt0 - || deltaPicOrderCnt1 != other.deltaPicOrderCnt1)) - || idrPicFlag != other.idrPicFlag - || (idrPicFlag && other.idrPicFlag && idrPicId != other.idrPicId)); + return isComplete + && (!other.isComplete + || frameNum != other.frameNum + || picParameterSetId != other.picParameterSetId + || fieldPicFlag != other.fieldPicFlag + || (bottomFieldFlagPresent + && other.bottomFieldFlagPresent + && bottomFieldFlag != other.bottomFieldFlag) + || (nalRefIdc != other.nalRefIdc && (nalRefIdc == 0 || other.nalRefIdc == 0)) + || (spsData.picOrderCountType == 0 + && other.spsData.picOrderCountType == 0 + && (picOrderCntLsb != other.picOrderCntLsb + || deltaPicOrderCntBottom != other.deltaPicOrderCntBottom)) + || (spsData.picOrderCountType == 1 + && other.spsData.picOrderCountType == 1 + && (deltaPicOrderCnt0 != other.deltaPicOrderCnt0 + || deltaPicOrderCnt1 != other.deltaPicOrderCnt1)) + || idrPicFlag != other.idrPicFlag + || (idrPicFlag && other.idrPicFlag && idrPicId != other.idrPicId)); } - } - } - } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java index 13d679c47c..88bde53746 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/H265Reader.java @@ -104,7 +104,8 @@ public final class H265Reader implements ElementaryStreamReader { } @Override - public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { + public void packetStarted(long pesTimeUs, @TsPayloadReader.Flags int flags) { + // TODO (Internal b/32267012): Consider using random access indicator. this.pesTimeUs = pesTimeUs; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Id3Reader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Id3Reader.java index 0f0f2ad981..f936fb9e43 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Id3Reader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/Id3Reader.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.extractor.ts; +import static com.google.android.exoplayer2.extractor.ts.TsPayloadReader.FLAG_DATA_ALIGNMENT_INDICATOR; + import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.extractor.ExtractorOutput; @@ -63,8 +65,8 @@ public final class Id3Reader implements ElementaryStreamReader { } @Override - public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { - if (!dataAlignmentIndicator) { + public void packetStarted(long pesTimeUs, @TsPayloadReader.Flags int flags) { + if ((flags & FLAG_DATA_ALIGNMENT_INDICATOR) == 0) { return; } writingSample = true; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/LatmReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/LatmReader.java index f401a6e736..2a633c191d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/LatmReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/LatmReader.java @@ -93,7 +93,7 @@ public final class LatmReader implements ElementaryStreamReader { } @Override - public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { + public void packetStarted(long pesTimeUs, @TsPayloadReader.Flags int flags) { timeUs = pesTimeUs; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/MpegAudioReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/MpegAudioReader.java index effa7d7c96..393e297818 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/MpegAudioReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/MpegAudioReader.java @@ -83,7 +83,7 @@ public final class MpegAudioReader implements ElementaryStreamReader { } @Override - public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) { + public void packetStarted(long pesTimeUs, @TsPayloadReader.Flags int flags) { timeUs = pesTimeUs; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PesReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PesReader.java index 91cd548367..ff755f4ece 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PesReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PesReader.java @@ -78,9 +78,8 @@ public final class PesReader implements TsPayloadReader { } @Override - public final void consume(ParsableByteArray data, boolean payloadUnitStartIndicator) - throws ParserException { - if (payloadUnitStartIndicator) { + public final void consume(ParsableByteArray data, @Flags int flags) throws ParserException { + if ((flags & FLAG_PAYLOAD_UNIT_START_INDICATOR) != 0) { switch (state) { case STATE_FINDING_HEADER: case STATE_READING_HEADER: @@ -122,7 +121,8 @@ public final class PesReader implements TsPayloadReader { if (continueRead(data, pesScratch.data, readLength) && continueRead(data, null, extendedHeaderLength)) { parseHeaderExtension(); - reader.packetStarted(timeUs, dataAlignmentIndicator); + flags |= dataAlignmentIndicator ? FLAG_DATA_ALIGNMENT_INDICATOR : 0; + reader.packetStarted(timeUs, flags); setState(STATE_READING_BODY); } break; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java index c7a082aeac..f453a9cc43 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java @@ -343,7 +343,7 @@ public final class PsExtractor implements Extractor { data.readBytes(pesScratch.data, 0, extendedHeaderLength); pesScratch.setPosition(0); parseHeaderExtension(); - pesPayloadReader.packetStarted(timeUs, true); + pesPayloadReader.packetStarted(timeUs, TsPayloadReader.FLAG_DATA_ALIGNMENT_INDICATOR); pesPayloadReader.consume(data); // We always have complete PES packets with program stream. pesPayloadReader.packetFinished(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/SectionReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/SectionReader.java index d217cfcb7a..101a1f74d9 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/SectionReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/SectionReader.java @@ -57,7 +57,8 @@ public final class SectionReader implements TsPayloadReader { } @Override - public void consume(ParsableByteArray data, boolean payloadUnitStartIndicator) { + public void consume(ParsableByteArray data, @Flags int flags) { + boolean payloadUnitStartIndicator = (flags & FLAG_PAYLOAD_UNIT_START_INDICATOR) != 0; int payloadStartPosition = C.POSITION_UNSET; if (payloadUnitStartIndicator) { int payloadStartOffset = data.readUnsignedByte(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java index f47a481d7e..d91842423d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.extractor.ts; +import static com.google.android.exoplayer2.extractor.ts.TsPayloadReader.FLAG_PAYLOAD_UNIT_START_INDICATOR; + import android.support.annotation.IntDef; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -279,6 +281,8 @@ public final class TsExtractor implements Extractor { return RESULT_CONTINUE; } + @TsPayloadReader.Flags int packetHeaderFlags = 0; + // Note: See ISO/IEC 13818-1, section 2.4.3.2 for details of the header format. int tsPacketHeader = tsPacketBuffer.readInt(); if ((tsPacketHeader & 0x800000) != 0) { // transport_error_indicator @@ -286,7 +290,7 @@ public final class TsExtractor implements Extractor { tsPacketBuffer.setPosition(endOfPacket); return RESULT_CONTINUE; } - boolean payloadUnitStartIndicator = (tsPacketHeader & 0x400000) != 0; + packetHeaderFlags |= (tsPacketHeader & 0x400000) != 0 ? FLAG_PAYLOAD_UNIT_START_INDICATOR : 0; // Ignoring transport_priority (tsPacketHeader & 0x200000) int pid = (tsPacketHeader & 0x1FFF00) >> 8; // Ignoring transport_scrambling_control (tsPacketHeader & 0xC0) @@ -317,14 +321,20 @@ public final class TsExtractor implements Extractor { // Skip the adaptation field. if (adaptationFieldExists) { int adaptationFieldLength = tsPacketBuffer.readUnsignedByte(); - tsPacketBuffer.skipBytes(adaptationFieldLength); + int adaptationFieldFlags = tsPacketBuffer.readUnsignedByte(); + + packetHeaderFlags |= + (adaptationFieldFlags & 0x40) != 0 // random_access_indicator. + ? TsPayloadReader.FLAG_RANDOM_ACCESS_INDICATOR + : 0; + tsPacketBuffer.skipBytes(adaptationFieldLength - 1 /* flags */); } // Read the payload. boolean wereTracksEnded = tracksEnded; if (shouldConsumePacketPayload(pid)) { tsPacketBuffer.setLimit(endOfPacket); - payloadReader.consume(tsPacketBuffer, payloadUnitStartIndicator); + payloadReader.consume(tsPacketBuffer, packetHeaderFlags); tsPacketBuffer.setLimit(limit); } if (mode != MODE_HLS && !wereTracksEnded && tracksEnded && inputLength != C.LENGTH_UNSET) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsPayloadReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsPayloadReader.java index 2ea25bb2e0..a034b05696 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsPayloadReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsPayloadReader.java @@ -15,12 +15,16 @@ */ package com.google.android.exoplayer2.extractor.ts; +import android.support.annotation.IntDef; import android.util.SparseArray; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.TimestampAdjuster; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Collections; import java.util.List; @@ -174,6 +178,29 @@ public interface TsPayloadReader { } + /** + * Contextual flags indicating the presence of indicators in the TS packet or PES packet headers. + */ + @Documented + @Retention(RetentionPolicy.SOURCE) + @IntDef( + flag = true, + value = { + FLAG_PAYLOAD_UNIT_START_INDICATOR, + FLAG_RANDOM_ACCESS_INDICATOR, + FLAG_DATA_ALIGNMENT_INDICATOR + }) + @interface Flags {} + + /** Indicates the presence of the payload_unit_start_indicator in the TS packet header. */ + int FLAG_PAYLOAD_UNIT_START_INDICATOR = 1; + /** + * Indicates the presence of the random_access_indicator in the TS packet header adaptation field. + */ + int FLAG_RANDOM_ACCESS_INDICATOR = 1 << 1; + /** Indicates the presence of the data_alignment_indicator in the PES header. */ + int FLAG_DATA_ALIGNMENT_INDICATOR = 1 << 2; + /** * Initializes the payload reader. * @@ -187,10 +214,10 @@ public interface TsPayloadReader { /** * Notifies the reader that a seek has occurred. - *
- * Following a call to this method, the data passed to the next invocation of - * {@link #consume(ParsableByteArray, boolean)} will not be a continuation of the data that was - * previously passed. Hence the reader should reset any internal state. + * + *
Following a call to this method, the data passed to the next invocation of {@link #consume} + * will not be a continuation of the data that was previously passed. Hence the reader should + * reset any internal state. */ void seek(); @@ -198,9 +225,8 @@ public interface TsPayloadReader { * Consumes the payload of a TS packet. * * @param data The TS packet. The position will be set to the start of the payload. - * @param payloadUnitStartIndicator Whether payloadUnitStartIndicator was set on the TS packet. + * @param flags See {@link Flags}. * @throws ParserException If the payload could not be parsed. */ - void consume(ParsableByteArray data, boolean payloadUnitStartIndicator) throws ParserException; - + void consume(ParsableByteArray data, @Flags int flags) throws ParserException; } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/AdtsReaderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/AdtsReaderTest.java index f7cfd6ccaf..62da914f5b 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/AdtsReaderTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/AdtsReaderTest.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.extractor.ts; +import static com.google.android.exoplayer2.extractor.ts.TsPayloadReader.FLAG_DATA_ALIGNMENT_INDICATOR; + import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; @@ -198,7 +200,7 @@ public class AdtsReaderTest { private void maybeStartPacket() { if (firstFeed) { - adtsReader.packetStarted(0, true); + adtsReader.packetStarted(0, FLAG_DATA_ALIGNMENT_INDICATOR); firstFeed = false; } } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/SectionReaderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/SectionReaderTest.java index 713d986d21..7bff736b4a 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/SectionReaderTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/SectionReaderTest.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.extractor.ts; +import static com.google.android.exoplayer2.extractor.ts.TsPayloadReader.FLAG_PAYLOAD_UNIT_START_INDICATOR; import static com.google.common.truth.Truth.assertThat; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; @@ -55,7 +56,7 @@ public final class SectionReaderTest { public void testSingleOnePacketSection() { packetPayload[0] = 3; insertTableSection(4, (byte) 99, 3); - reader.consume(new ParsableByteArray(packetPayload), true); + reader.consume(new ParsableByteArray(packetPayload), FLAG_PAYLOAD_UNIT_START_INDICATOR); assertThat(payloadReader.parsedTableIds).isEqualTo(singletonList(99)); } @@ -65,12 +66,12 @@ public final class SectionReaderTest { insertTableSection(4, (byte) 100, 3); // This section header spreads across both packets. ParsableByteArray firstPacket = new ParsableByteArray(packetPayload, 5); - reader.consume(firstPacket, true); + reader.consume(firstPacket, FLAG_PAYLOAD_UNIT_START_INDICATOR); assertThat(payloadReader.parsedTableIds).isEmpty(); ParsableByteArray secondPacket = new ParsableByteArray(packetPayload); secondPacket.setPosition(5); - reader.consume(secondPacket, false); + reader.consume(secondPacket, /* flags= */ 0); assertThat(payloadReader.parsedTableIds).isEqualTo(singletonList(100)); } @@ -85,12 +86,12 @@ public final class SectionReaderTest { insertTableSection(54, (byte) 105, 10); ParsableByteArray firstPacket = new ParsableByteArray(packetPayload, 40); - reader.consume(firstPacket, true); + reader.consume(firstPacket, FLAG_PAYLOAD_UNIT_START_INDICATOR); assertThat(payloadReader.parsedTableIds).isEqualTo(asList(101, 102, 103)); ParsableByteArray secondPacket = new ParsableByteArray(packetPayload); secondPacket.setPosition(40); - reader.consume(secondPacket, true); + reader.consume(secondPacket, FLAG_PAYLOAD_UNIT_START_INDICATOR); assertThat(payloadReader.parsedTableIds).isEqualTo(asList(101, 102, 103, 104, 105)); } @@ -105,22 +106,22 @@ public final class SectionReaderTest { insertTableSection(318, (byte) 108, 10); ParsableByteArray firstPacket = new ParsableByteArray(packetPayload, 100); - reader.consume(firstPacket, true); + reader.consume(firstPacket, FLAG_PAYLOAD_UNIT_START_INDICATOR); assertThat(payloadReader.parsedTableIds).isEmpty(); ParsableByteArray secondPacket = new ParsableByteArray(packetPayload, 200); secondPacket.setPosition(100); - reader.consume(secondPacket, false); + reader.consume(secondPacket, /* flags= */ 0); assertThat(payloadReader.parsedTableIds).isEmpty(); ParsableByteArray thirdPacket = new ParsableByteArray(packetPayload, 300); thirdPacket.setPosition(200); - reader.consume(thirdPacket, false); + reader.consume(thirdPacket, /* flags= */ 0); assertThat(payloadReader.parsedTableIds).isEmpty(); ParsableByteArray fourthPacket = new ParsableByteArray(packetPayload); fourthPacket.setPosition(300); - reader.consume(fourthPacket, true); + reader.consume(fourthPacket, FLAG_PAYLOAD_UNIT_START_INDICATOR); assertThat(payloadReader.parsedTableIds).isEqualTo(asList(107, 108)); } @@ -135,24 +136,24 @@ public final class SectionReaderTest { insertTableSection(318, (byte) 111, 10); ParsableByteArray firstPacket = new ParsableByteArray(packetPayload, 100); - reader.consume(firstPacket, true); + reader.consume(firstPacket, FLAG_PAYLOAD_UNIT_START_INDICATOR); assertThat(payloadReader.parsedTableIds).isEmpty(); ParsableByteArray secondPacket = new ParsableByteArray(packetPayload, 200); secondPacket.setPosition(100); - reader.consume(secondPacket, false); + reader.consume(secondPacket, /* flags= */ 0); assertThat(payloadReader.parsedTableIds).isEmpty(); ParsableByteArray thirdPacket = new ParsableByteArray(packetPayload, 300); thirdPacket.setPosition(200); - reader.consume(thirdPacket, false); + reader.consume(thirdPacket, /* flags= */ 0); assertThat(payloadReader.parsedTableIds).isEmpty(); reader.seek(); ParsableByteArray fourthPacket = new ParsableByteArray(packetPayload); fourthPacket.setPosition(300); - reader.consume(fourthPacket, true); + reader.consume(fourthPacket, FLAG_PAYLOAD_UNIT_START_INDICATOR); assertThat(payloadReader.parsedTableIds).isEqualTo(singletonList(111)); } @@ -165,9 +166,9 @@ public final class SectionReaderTest { byte[] incorrectCrcPat = Arrays.copyOf(correctCrcPat, correctCrcPat.length); // Crc field is incorrect, and should not be passed to the payload reader. incorrectCrcPat[16]--; - reader.consume(new ParsableByteArray(correctCrcPat), true); + reader.consume(new ParsableByteArray(correctCrcPat), FLAG_PAYLOAD_UNIT_START_INDICATOR); assertThat(payloadReader.parsedTableIds).isEqualTo(singletonList(0)); - reader.consume(new ParsableByteArray(incorrectCrcPat), true); + reader.consume(new ParsableByteArray(incorrectCrcPat), FLAG_PAYLOAD_UNIT_START_INDICATOR); assertThat(payloadReader.parsedTableIds).isEqualTo(singletonList(0)); } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java index 332fbe384a..beaa5ffa83 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java @@ -202,7 +202,7 @@ public final class TsExtractorTest { } @Override - public void packetStarted(long pesTimeUs, boolean dataAlignmentIndicator) {} + public void packetStarted(long pesTimeUs, @TsPayloadReader.Flags int flags) {} @Override public void consume(ParsableByteArray data) {}