mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Mitigate the need for FLAG_ALLOW_NON_IDR_KEYFRAMES
Use random access indicator in transport streams Issue:#1967 Issue:#2020 Issue:#2182 Issue:#2469 Issue:#2581 Issue:#2748 Issue:#2939 Issue:#2979 Issue:#3316 Issue:#3574 Issue:#3709 Issue:#3747 Issue:#4103 Issue:#4184 Issue:#4355 Issue:#4538 Issue:#4719 Issue:#4861 Issue:#4925 Issue:#4951 Issue:#5108 Issue:#5186 PiperOrigin-RevId: 225798044
This commit is contained in:
parent
d5e53d11f7
commit
8202cb2d2a
@ -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)).
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
*
|
||||
* <p>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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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) {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user