Commit 63f90ade by olly Committed by Oliver Woodman

Add package level NonNull to extractor.ts

Also remove most classes from the nullness blacklist

PiperOrigin-RevId: 288494712
parent fb42f818
Showing with 304 additions and 136 deletions
...@@ -81,7 +81,10 @@ public final class DtsUtil { ...@@ -81,7 +81,10 @@ public final class DtsUtil {
* @return The DTS format parsed from data in the header. * @return The DTS format parsed from data in the header.
*/ */
public static Format parseDtsFormat( public static Format parseDtsFormat(
byte[] frame, String trackId, @Nullable String language, @Nullable DrmInitData drmInitData) { byte[] frame,
@Nullable String trackId,
@Nullable String language,
@Nullable DrmInitData drmInitData) {
ParsableBitArray frameBits = getNormalizedFrameHeader(frame); ParsableBitArray frameBits = getNormalizedFrameHeader(frame);
frameBits.skipBits(32 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE frameBits.skipBits(32 + 1 + 5 + 1 + 7 + 14); // SYNC, FTYPE, SHORT, CPF, NBLKS, FSIZE
int amode = frameBits.readBits(6); int amode = frameBits.readBits(6);
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.Ac3Util; import com.google.android.exoplayer2.audio.Ac3Util;
...@@ -23,11 +24,15 @@ import com.google.android.exoplayer2.audio.Ac3Util.SyncFrameInfo; ...@@ -23,11 +24,15 @@ import com.google.android.exoplayer2.audio.Ac3Util.SyncFrameInfo;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** /**
* Parses a continuous (E-)AC-3 byte stream and extracts individual samples. * Parses a continuous (E-)AC-3 byte stream and extracts individual samples.
...@@ -47,10 +52,10 @@ public final class Ac3Reader implements ElementaryStreamReader { ...@@ -47,10 +52,10 @@ public final class Ac3Reader implements ElementaryStreamReader {
private final ParsableBitArray headerScratchBits; private final ParsableBitArray headerScratchBits;
private final ParsableByteArray headerScratchBytes; private final ParsableByteArray headerScratchBytes;
private final String language; @Nullable private final String language;
private String trackFormatId; @MonotonicNonNull private String formatId;
private TrackOutput output; @MonotonicNonNull private TrackOutput output;
@State private int state; @State private int state;
private int bytesRead; private int bytesRead;
...@@ -60,7 +65,7 @@ public final class Ac3Reader implements ElementaryStreamReader { ...@@ -60,7 +65,7 @@ public final class Ac3Reader implements ElementaryStreamReader {
// Used when parsing the header. // Used when parsing the header.
private long sampleDurationUs; private long sampleDurationUs;
private Format format; @MonotonicNonNull private Format format;
private int sampleSize; private int sampleSize;
// Used when reading the samples. // Used when reading the samples.
...@@ -78,7 +83,7 @@ public final class Ac3Reader implements ElementaryStreamReader { ...@@ -78,7 +83,7 @@ public final class Ac3Reader implements ElementaryStreamReader {
* *
* @param language Track language. * @param language Track language.
*/ */
public Ac3Reader(String language) { public Ac3Reader(@Nullable String language) {
headerScratchBits = new ParsableBitArray(new byte[HEADER_SIZE]); headerScratchBits = new ParsableBitArray(new byte[HEADER_SIZE]);
headerScratchBytes = new ParsableByteArray(headerScratchBits.data); headerScratchBytes = new ParsableByteArray(headerScratchBits.data);
state = STATE_FINDING_SYNC; state = STATE_FINDING_SYNC;
...@@ -95,7 +100,7 @@ public final class Ac3Reader implements ElementaryStreamReader { ...@@ -95,7 +100,7 @@ public final class Ac3Reader implements ElementaryStreamReader {
@Override @Override
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator generator) { public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator generator) {
generator.generateNewId(); generator.generateNewId();
trackFormatId = generator.getFormatId(); formatId = generator.getFormatId();
output = extractorOutput.track(generator.getTrackId(), C.TRACK_TYPE_AUDIO); output = extractorOutput.track(generator.getTrackId(), C.TRACK_TYPE_AUDIO);
} }
...@@ -106,6 +111,7 @@ public final class Ac3Reader implements ElementaryStreamReader { ...@@ -106,6 +111,7 @@ public final class Ac3Reader implements ElementaryStreamReader {
@Override @Override
public void consume(ParsableByteArray data) { public void consume(ParsableByteArray data) {
Assertions.checkStateNotNull(output); // Asserts that createTracks has been called.
while (data.bytesLeft() > 0) { while (data.bytesLeft() > 0) {
switch (state) { switch (state) {
case STATE_FINDING_SYNC: case STATE_FINDING_SYNC:
...@@ -185,19 +191,28 @@ public final class Ac3Reader implements ElementaryStreamReader { ...@@ -185,19 +191,28 @@ public final class Ac3Reader implements ElementaryStreamReader {
return false; return false;
} }
/** /** Parses the sample header. */
* Parses the sample header. @RequiresNonNull("output")
*/
@SuppressWarnings("ReferenceEquality")
private void parseHeader() { private void parseHeader() {
headerScratchBits.setPosition(0); headerScratchBits.setPosition(0);
SyncFrameInfo frameInfo = Ac3Util.parseAc3SyncframeInfo(headerScratchBits); SyncFrameInfo frameInfo = Ac3Util.parseAc3SyncframeInfo(headerScratchBits);
if (format == null || frameInfo.channelCount != format.channelCount if (format == null
|| frameInfo.channelCount != format.channelCount
|| frameInfo.sampleRate != format.sampleRate || frameInfo.sampleRate != format.sampleRate
|| frameInfo.mimeType != format.sampleMimeType) { || Util.areEqual(frameInfo.mimeType, format.sampleMimeType)) {
format = Format.createAudioSampleFormat(trackFormatId, frameInfo.mimeType, null, format =
Format.NO_VALUE, Format.NO_VALUE, frameInfo.channelCount, frameInfo.sampleRate, null, Format.createAudioSampleFormat(
null, 0, language); formatId,
frameInfo.mimeType,
null,
Format.NO_VALUE,
Format.NO_VALUE,
frameInfo.channelCount,
frameInfo.sampleRate,
null,
null,
0,
language);
output.format(format); output.format(format);
} }
sampleSize = frameInfo.frameSize; sampleSize = frameInfo.frameSize;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.Ac4Util; import com.google.android.exoplayer2.audio.Ac4Util;
...@@ -23,12 +24,15 @@ import com.google.android.exoplayer2.audio.Ac4Util.SyncFrameInfo; ...@@ -23,12 +24,15 @@ import com.google.android.exoplayer2.audio.Ac4Util.SyncFrameInfo;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** Parses a continuous AC-4 byte stream and extracts individual samples. */ /** Parses a continuous AC-4 byte stream and extracts individual samples. */
public final class Ac4Reader implements ElementaryStreamReader { public final class Ac4Reader implements ElementaryStreamReader {
...@@ -44,10 +48,10 @@ public final class Ac4Reader implements ElementaryStreamReader { ...@@ -44,10 +48,10 @@ public final class Ac4Reader implements ElementaryStreamReader {
private final ParsableBitArray headerScratchBits; private final ParsableBitArray headerScratchBits;
private final ParsableByteArray headerScratchBytes; private final ParsableByteArray headerScratchBytes;
private final String language; @Nullable private final String language;
private String trackFormatId; @MonotonicNonNull private String formatId;
private TrackOutput output; @MonotonicNonNull private TrackOutput output;
@State private int state; @State private int state;
private int bytesRead; private int bytesRead;
...@@ -58,7 +62,7 @@ public final class Ac4Reader implements ElementaryStreamReader { ...@@ -58,7 +62,7 @@ public final class Ac4Reader implements ElementaryStreamReader {
// Used when parsing the header. // Used when parsing the header.
private long sampleDurationUs; private long sampleDurationUs;
private Format format; @MonotonicNonNull private Format format;
private int sampleSize; private int sampleSize;
// Used when reading the samples. // Used when reading the samples.
...@@ -74,7 +78,7 @@ public final class Ac4Reader implements ElementaryStreamReader { ...@@ -74,7 +78,7 @@ public final class Ac4Reader implements ElementaryStreamReader {
* *
* @param language Track language. * @param language Track language.
*/ */
public Ac4Reader(String language) { public Ac4Reader(@Nullable String language) {
headerScratchBits = new ParsableBitArray(new byte[Ac4Util.HEADER_SIZE_FOR_PARSER]); headerScratchBits = new ParsableBitArray(new byte[Ac4Util.HEADER_SIZE_FOR_PARSER]);
headerScratchBytes = new ParsableByteArray(headerScratchBits.data); headerScratchBytes = new ParsableByteArray(headerScratchBits.data);
state = STATE_FINDING_SYNC; state = STATE_FINDING_SYNC;
...@@ -95,7 +99,7 @@ public final class Ac4Reader implements ElementaryStreamReader { ...@@ -95,7 +99,7 @@ public final class Ac4Reader implements ElementaryStreamReader {
@Override @Override
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator generator) { public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator generator) {
generator.generateNewId(); generator.generateNewId();
trackFormatId = generator.getFormatId(); formatId = generator.getFormatId();
output = extractorOutput.track(generator.getTrackId(), C.TRACK_TYPE_AUDIO); output = extractorOutput.track(generator.getTrackId(), C.TRACK_TYPE_AUDIO);
} }
...@@ -106,6 +110,7 @@ public final class Ac4Reader implements ElementaryStreamReader { ...@@ -106,6 +110,7 @@ public final class Ac4Reader implements ElementaryStreamReader {
@Override @Override
public void consume(ParsableByteArray data) { public void consume(ParsableByteArray data) {
Assertions.checkStateNotNull(output); // Asserts that createTracks has been called.
while (data.bytesLeft() > 0) { while (data.bytesLeft() > 0) {
switch (state) { switch (state) {
case STATE_FINDING_SYNC: case STATE_FINDING_SYNC:
...@@ -185,7 +190,7 @@ public final class Ac4Reader implements ElementaryStreamReader { ...@@ -185,7 +190,7 @@ public final class Ac4Reader implements ElementaryStreamReader {
} }
/** Parses the sample header. */ /** Parses the sample header. */
@SuppressWarnings("ReferenceEquality") @RequiresNonNull("output")
private void parseHeader() { private void parseHeader() {
headerScratchBits.setPosition(0); headerScratchBits.setPosition(0);
SyncFrameInfo frameInfo = Ac4Util.parseAc4SyncframeInfo(headerScratchBits); SyncFrameInfo frameInfo = Ac4Util.parseAc4SyncframeInfo(headerScratchBits);
...@@ -195,7 +200,7 @@ public final class Ac4Reader implements ElementaryStreamReader { ...@@ -195,7 +200,7 @@ public final class Ac4Reader implements ElementaryStreamReader {
|| !MimeTypes.AUDIO_AC4.equals(format.sampleMimeType)) { || !MimeTypes.AUDIO_AC4.equals(format.sampleMimeType)) {
format = format =
Format.createAudioSampleFormat( Format.createAudioSampleFormat(
trackFormatId, formatId,
MimeTypes.AUDIO_AC4, MimeTypes.AUDIO_AC4,
/* codecs= */ null, /* codecs= */ null,
/* bitrate= */ Format.NO_VALUE, /* bitrate= */ Format.NO_VALUE,
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import android.util.Pair; import android.util.Pair;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
...@@ -23,13 +24,18 @@ import com.google.android.exoplayer2.extractor.DummyTrackOutput; ...@@ -23,13 +24,18 @@ import com.google.android.exoplayer2.extractor.DummyTrackOutput;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** /**
* Parses a continuous ADTS byte stream and extracts individual frames. * Parses a continuous ADTS byte stream and extracts individual frames.
...@@ -62,11 +68,11 @@ public final class AdtsReader implements ElementaryStreamReader { ...@@ -62,11 +68,11 @@ public final class AdtsReader implements ElementaryStreamReader {
private final boolean exposeId3; private final boolean exposeId3;
private final ParsableBitArray adtsScratch; private final ParsableBitArray adtsScratch;
private final ParsableByteArray id3HeaderBuffer; private final ParsableByteArray id3HeaderBuffer;
private final String language; @Nullable private final String language;
private String formatId; @MonotonicNonNull private String formatId;
private TrackOutput output; @MonotonicNonNull private TrackOutput output;
private TrackOutput id3Output; @MonotonicNonNull private TrackOutput id3Output;
private int state; private int state;
private int bytesRead; private int bytesRead;
...@@ -90,7 +96,7 @@ public final class AdtsReader implements ElementaryStreamReader { ...@@ -90,7 +96,7 @@ public final class AdtsReader implements ElementaryStreamReader {
// Used when reading the samples. // Used when reading the samples.
private long timeUs; private long timeUs;
private TrackOutput currentOutput; @MonotonicNonNull private TrackOutput currentOutput;
private long currentSampleDuration; private long currentSampleDuration;
/** /**
...@@ -104,7 +110,7 @@ public final class AdtsReader implements ElementaryStreamReader { ...@@ -104,7 +110,7 @@ public final class AdtsReader implements ElementaryStreamReader {
* @param exposeId3 True if the reader should expose ID3 information. * @param exposeId3 True if the reader should expose ID3 information.
* @param language Track language. * @param language Track language.
*/ */
public AdtsReader(boolean exposeId3, String language) { public AdtsReader(boolean exposeId3, @Nullable String language) {
adtsScratch = new ParsableBitArray(new byte[HEADER_SIZE + CRC_SIZE]); adtsScratch = new ParsableBitArray(new byte[HEADER_SIZE + CRC_SIZE]);
id3HeaderBuffer = new ParsableByteArray(Arrays.copyOf(ID3_IDENTIFIER, ID3_HEADER_SIZE)); id3HeaderBuffer = new ParsableByteArray(Arrays.copyOf(ID3_IDENTIFIER, ID3_HEADER_SIZE));
setFindingSampleState(); setFindingSampleState();
...@@ -130,6 +136,7 @@ public final class AdtsReader implements ElementaryStreamReader { ...@@ -130,6 +136,7 @@ public final class AdtsReader implements ElementaryStreamReader {
idGenerator.generateNewId(); idGenerator.generateNewId();
formatId = idGenerator.getFormatId(); formatId = idGenerator.getFormatId();
output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_AUDIO); output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_AUDIO);
currentOutput = output;
if (exposeId3) { if (exposeId3) {
idGenerator.generateNewId(); idGenerator.generateNewId();
id3Output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_METADATA); id3Output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_METADATA);
...@@ -147,6 +154,7 @@ public final class AdtsReader implements ElementaryStreamReader { ...@@ -147,6 +154,7 @@ public final class AdtsReader implements ElementaryStreamReader {
@Override @Override
public void consume(ParsableByteArray data) throws ParserException { public void consume(ParsableByteArray data) throws ParserException {
assertTracksCreated();
while (data.bytesLeft() > 0) { while (data.bytesLeft() > 0) {
switch (state) { switch (state) {
case STATE_FINDING_SAMPLE: case STATE_FINDING_SAMPLE:
...@@ -425,9 +433,8 @@ public final class AdtsReader implements ElementaryStreamReader { ...@@ -425,9 +433,8 @@ public final class AdtsReader implements ElementaryStreamReader {
return true; return true;
} }
/** /** Parses the Id3 header. */
* Parses the Id3 header. @RequiresNonNull("id3Output")
*/
private void parseId3Header() { private void parseId3Header() {
id3Output.sampleData(id3HeaderBuffer, ID3_HEADER_SIZE); id3Output.sampleData(id3HeaderBuffer, ID3_HEADER_SIZE);
id3HeaderBuffer.setPosition(ID3_SIZE_OFFSET); id3HeaderBuffer.setPosition(ID3_SIZE_OFFSET);
...@@ -435,9 +442,8 @@ public final class AdtsReader implements ElementaryStreamReader { ...@@ -435,9 +442,8 @@ public final class AdtsReader implements ElementaryStreamReader {
id3HeaderBuffer.readSynchSafeInt() + ID3_HEADER_SIZE); id3HeaderBuffer.readSynchSafeInt() + ID3_HEADER_SIZE);
} }
/** /** Parses the sample header. */
* Parses the sample header. @RequiresNonNull("output")
*/
private void parseAdtsHeader() throws ParserException { private void parseAdtsHeader() throws ParserException {
adtsScratch.setPosition(0); adtsScratch.setPosition(0);
...@@ -487,9 +493,8 @@ public final class AdtsReader implements ElementaryStreamReader { ...@@ -487,9 +493,8 @@ public final class AdtsReader implements ElementaryStreamReader {
setReadingSampleState(output, sampleDurationUs, 0, sampleSize); setReadingSampleState(output, sampleDurationUs, 0, sampleSize);
} }
/** /** Reads the rest of the sample */
* Reads the rest of the sample @RequiresNonNull("currentOutput")
*/
private void readSample(ParsableByteArray data) { private void readSample(ParsableByteArray data) {
int bytesToRead = Math.min(data.bytesLeft(), sampleSize - bytesRead); int bytesToRead = Math.min(data.bytesLeft(), sampleSize - bytesRead);
currentOutput.sampleData(data, bytesToRead); currentOutput.sampleData(data, bytesToRead);
...@@ -501,4 +506,10 @@ public final class AdtsReader implements ElementaryStreamReader { ...@@ -501,4 +506,10 @@ public final class AdtsReader implements ElementaryStreamReader {
} }
} }
@EnsuresNonNull({"output", "currentOutput", "id3Output"})
private void assertTracksCreated() {
Assertions.checkNotNull(output);
Util.castNonNull(currentOutput);
Util.castNonNull(id3Output);
}
} }
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.ts; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.ts;
import android.util.SparseArray; import android.util.SparseArray;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo;
import com.google.android.exoplayer2.text.cea.Cea708InitializationData; import com.google.android.exoplayer2.text.cea.Cea708InitializationData;
...@@ -134,6 +135,7 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact ...@@ -134,6 +135,7 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
return new SparseArray<>(); return new SparseArray<>();
} }
@Nullable
@Override @Override
public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) { public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) {
switch (streamType) { switch (streamType) {
...@@ -247,7 +249,7 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact ...@@ -247,7 +249,7 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
// Skip reserved (8). // Skip reserved (8).
scratchDescriptorData.skipBytes(1); scratchDescriptorData.skipBytes(1);
List<byte[]> initializationData = null; @Nullable List<byte[]> initializationData = null;
// The wide_aspect_ratio flag only has meaning for CEA-708. // The wide_aspect_ratio flag only has meaning for CEA-708.
if (isDigital) { if (isDigital) {
boolean isWideAspectRatio = (flags & 0x40) != 0; boolean isWideAspectRatio = (flags & 0x40) != 0;
......
...@@ -15,13 +15,17 @@ ...@@ -15,13 +15,17 @@
*/ */
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.DtsUtil; import com.google.android.exoplayer2.audio.DtsUtil;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** /**
* Parses a continuous DTS byte stream and extracts individual samples. * Parses a continuous DTS byte stream and extracts individual samples.
...@@ -35,10 +39,10 @@ public final class DtsReader implements ElementaryStreamReader { ...@@ -35,10 +39,10 @@ public final class DtsReader implements ElementaryStreamReader {
private static final int HEADER_SIZE = 18; private static final int HEADER_SIZE = 18;
private final ParsableByteArray headerScratchBytes; private final ParsableByteArray headerScratchBytes;
private final String language; @Nullable private final String language;
private String formatId; @MonotonicNonNull private String formatId;
private TrackOutput output; @MonotonicNonNull private TrackOutput output;
private int state; private int state;
private int bytesRead; private int bytesRead;
...@@ -48,7 +52,7 @@ public final class DtsReader implements ElementaryStreamReader { ...@@ -48,7 +52,7 @@ public final class DtsReader implements ElementaryStreamReader {
// Used when parsing the header. // Used when parsing the header.
private long sampleDurationUs; private long sampleDurationUs;
private Format format; @MonotonicNonNull private Format format;
private int sampleSize; private int sampleSize;
// Used when reading the samples. // Used when reading the samples.
...@@ -59,7 +63,7 @@ public final class DtsReader implements ElementaryStreamReader { ...@@ -59,7 +63,7 @@ public final class DtsReader implements ElementaryStreamReader {
* *
* @param language Track language. * @param language Track language.
*/ */
public DtsReader(String language) { public DtsReader(@Nullable String language) {
headerScratchBytes = new ParsableByteArray(new byte[HEADER_SIZE]); headerScratchBytes = new ParsableByteArray(new byte[HEADER_SIZE]);
state = STATE_FINDING_SYNC; state = STATE_FINDING_SYNC;
this.language = language; this.language = language;
...@@ -86,6 +90,7 @@ public final class DtsReader implements ElementaryStreamReader { ...@@ -86,6 +90,7 @@ public final class DtsReader implements ElementaryStreamReader {
@Override @Override
public void consume(ParsableByteArray data) { public void consume(ParsableByteArray data) {
Assertions.checkStateNotNull(output); // Asserts that createTracks has been called.
while (data.bytesLeft() > 0) { while (data.bytesLeft() > 0) {
switch (state) { switch (state) {
case STATE_FINDING_SYNC: case STATE_FINDING_SYNC:
...@@ -162,9 +167,8 @@ public final class DtsReader implements ElementaryStreamReader { ...@@ -162,9 +167,8 @@ public final class DtsReader implements ElementaryStreamReader {
return false; return false;
} }
/** /** Parses the sample header. */
* Parses the sample header. @RequiresNonNull("output")
*/
private void parseHeader() { private void parseHeader() {
byte[] frameData = headerScratchBytes.data; byte[] frameData = headerScratchBytes.data;
if (format == null) { if (format == null) {
......
...@@ -64,12 +64,12 @@ public final class DvbSubtitleReader implements ElementaryStreamReader { ...@@ -64,12 +64,12 @@ public final class DvbSubtitleReader implements ElementaryStreamReader {
Format.createImageSampleFormat( Format.createImageSampleFormat(
idGenerator.getFormatId(), idGenerator.getFormatId(),
MimeTypes.APPLICATION_DVBSUBS, MimeTypes.APPLICATION_DVBSUBS,
null, /* codecs= */ null,
Format.NO_VALUE, Format.NO_VALUE,
0, /* selectionFlags= */ 0,
Collections.singletonList(subtitleInfo.initializationData), Collections.singletonList(subtitleInfo.initializationData),
subtitleInfo.language, subtitleInfo.language,
null)); /* drmInitData= */ null));
outputs[i] = output; outputs[i] = output;
} }
} }
......
...@@ -16,16 +16,20 @@ ...@@ -16,16 +16,20 @@
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import android.util.Pair; import android.util.Pair;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.NalUnitUtil; import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* Parses a continuous H262 byte stream and extracts individual frames. * Parses a continuous H262 byte stream and extracts individual frames.
...@@ -38,27 +42,27 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -38,27 +42,27 @@ public final class H262Reader implements ElementaryStreamReader {
private static final int START_GROUP = 0xB8; private static final int START_GROUP = 0xB8;
private static final int START_USER_DATA = 0xB2; private static final int START_USER_DATA = 0xB2;
private String formatId; @MonotonicNonNull private String formatId;
private TrackOutput output; @MonotonicNonNull private TrackOutput output;
// Maps (frame_rate_code - 1) indices to values, as defined in ITU-T H.262 Table 6-4. // 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[] { private static final double[] FRAME_RATE_VALUES = new double[] {
24000d / 1001, 24, 25, 30000d / 1001, 30, 50, 60000d / 1001, 60}; 24000d / 1001, 24, 25, 30000d / 1001, 30, 50, 60000d / 1001, 60};
// State that should not be reset on seek. @Nullable private final UserDataReader userDataReader;
private boolean hasOutputFormat; @Nullable private final ParsableByteArray userDataParsable;
private long frameDurationUs;
private final UserDataReader userDataReader;
private final ParsableByteArray userDataParsable;
// State that should be reset on seek. // State that should be reset on seek.
@Nullable private final NalUnitTargetBuffer userData;
private final boolean[] prefixFlags; private final boolean[] prefixFlags;
private final CsdBuffer csdBuffer; private final CsdBuffer csdBuffer;
private final NalUnitTargetBuffer userData;
private long totalBytesWritten; private long totalBytesWritten;
private boolean startedFirstSample; private boolean startedFirstSample;
// State that should not be reset on seek.
private boolean hasOutputFormat;
private long frameDurationUs;
// Per packet state that gets reset at the start of each packet. // Per packet state that gets reset at the start of each packet.
private long pesTimeUs; private long pesTimeUs;
...@@ -72,7 +76,7 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -72,7 +76,7 @@ public final class H262Reader implements ElementaryStreamReader {
this(null); this(null);
} }
/* package */ H262Reader(UserDataReader userDataReader) { /* package */ H262Reader(@Nullable UserDataReader userDataReader) {
this.userDataReader = userDataReader; this.userDataReader = userDataReader;
prefixFlags = new boolean[4]; prefixFlags = new boolean[4];
csdBuffer = new CsdBuffer(128); csdBuffer = new CsdBuffer(128);
...@@ -89,7 +93,7 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -89,7 +93,7 @@ public final class H262Reader implements ElementaryStreamReader {
public void seek() { public void seek() {
NalUnitUtil.clearPrefixFlags(prefixFlags); NalUnitUtil.clearPrefixFlags(prefixFlags);
csdBuffer.reset(); csdBuffer.reset();
if (userDataReader != null) { if (userData != null) {
userData.reset(); userData.reset();
} }
totalBytesWritten = 0; totalBytesWritten = 0;
...@@ -114,6 +118,7 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -114,6 +118,7 @@ public final class H262Reader implements ElementaryStreamReader {
@Override @Override
public void consume(ParsableByteArray data) { public void consume(ParsableByteArray data) {
Assertions.checkStateNotNull(output); // Asserts that createTracks has been called.
int offset = data.getPosition(); int offset = data.getPosition();
int limit = data.limit(); int limit = data.limit();
byte[] dataArray = data.data; byte[] dataArray = data.data;
...@@ -130,7 +135,7 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -130,7 +135,7 @@ public final class H262Reader implements ElementaryStreamReader {
if (!hasOutputFormat) { if (!hasOutputFormat) {
csdBuffer.onData(dataArray, offset, limit); csdBuffer.onData(dataArray, offset, limit);
} }
if (userDataReader != null) { if (userData != null) {
userData.appendToNalUnit(dataArray, offset, limit); userData.appendToNalUnit(dataArray, offset, limit);
} }
return; return;
...@@ -157,7 +162,7 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -157,7 +162,7 @@ public final class H262Reader implements ElementaryStreamReader {
hasOutputFormat = true; hasOutputFormat = true;
} }
} }
if (userDataReader != null) { if (userData != null) {
int bytesAlreadyPassed = 0; int bytesAlreadyPassed = 0;
if (lengthToStartCode > 0) { if (lengthToStartCode > 0) {
userData.appendToNalUnit(dataArray, offset, startCodeOffset); userData.appendToNalUnit(dataArray, offset, startCodeOffset);
...@@ -167,8 +172,8 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -167,8 +172,8 @@ public final class H262Reader implements ElementaryStreamReader {
if (userData.endNalUnit(bytesAlreadyPassed)) { if (userData.endNalUnit(bytesAlreadyPassed)) {
int unescapedLength = NalUnitUtil.unescapeStream(userData.nalData, userData.nalLength); int unescapedLength = NalUnitUtil.unescapeStream(userData.nalData, userData.nalLength);
userDataParsable.reset(userData.nalData, unescapedLength); Util.castNonNull(userDataParsable).reset(userData.nalData, unescapedLength);
userDataReader.consume(sampleTimeUs, userDataParsable); Util.castNonNull(userDataReader).consume(sampleTimeUs, userDataParsable);
} }
if (startCodeValue == START_USER_DATA && data.data[startCodeOffset + 2] == 0x1) { if (startCodeValue == START_USER_DATA && data.data[startCodeOffset + 2] == 0x1) {
...@@ -211,10 +216,10 @@ public final class H262Reader implements ElementaryStreamReader { ...@@ -211,10 +216,10 @@ public final class H262Reader implements ElementaryStreamReader {
* *
* @param csdBuffer The csd buffer. * @param csdBuffer The csd buffer.
* @param formatId The id for the generated format. May be null. * @param formatId The id for the generated format. May be null.
* @return A pair consisting of the {@link Format} and the frame duration in microseconds, or * @return A pair consisting of the {@link Format} and the frame duration in microseconds, or 0 if
* 0 if the duration could not be determined. * the duration could not be determined.
*/ */
private static Pair<Format, Long> parseCsdBuffer(CsdBuffer csdBuffer, String formatId) { private static Pair<Format, Long> parseCsdBuffer(CsdBuffer csdBuffer, @Nullable String formatId) {
byte[] csdData = Arrays.copyOf(csdBuffer.data, csdBuffer.length); byte[] csdData = Arrays.copyOf(csdBuffer.data, csdBuffer.length);
int firstByte = csdData[4] & 0xFF; int firstByte = csdData[4] & 0xFF;
......
...@@ -23,15 +23,21 @@ import com.google.android.exoplayer2.Format; ...@@ -23,15 +23,21 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.NalUnitUtil; import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.NalUnitUtil.SpsData; import com.google.android.exoplayer2.util.NalUnitUtil.SpsData;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.ParsableNalUnitBitArray; import com.google.android.exoplayer2.util.ParsableNalUnitBitArray;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** /**
* Parses a continuous H264 byte stream and extracts individual frames. * Parses a continuous H264 byte stream and extracts individual frames.
...@@ -51,9 +57,9 @@ public final class H264Reader implements ElementaryStreamReader { ...@@ -51,9 +57,9 @@ public final class H264Reader implements ElementaryStreamReader {
private long totalBytesWritten; private long totalBytesWritten;
private final boolean[] prefixFlags; private final boolean[] prefixFlags;
private String formatId; @MonotonicNonNull private String formatId;
private TrackOutput output; @MonotonicNonNull private TrackOutput output;
private SampleReader sampleReader; @MonotonicNonNull private SampleReader sampleReader;
// State that should not be reset on seek. // State that should not be reset on seek.
private boolean hasOutputFormat; private boolean hasOutputFormat;
...@@ -87,13 +93,15 @@ public final class H264Reader implements ElementaryStreamReader { ...@@ -87,13 +93,15 @@ public final class H264Reader implements ElementaryStreamReader {
@Override @Override
public void seek() { public void seek() {
totalBytesWritten = 0;
randomAccessIndicator = false;
NalUnitUtil.clearPrefixFlags(prefixFlags); NalUnitUtil.clearPrefixFlags(prefixFlags);
sps.reset(); sps.reset();
pps.reset(); pps.reset();
sei.reset(); sei.reset();
sampleReader.reset(); if (sampleReader != null) {
totalBytesWritten = 0; sampleReader.reset();
randomAccessIndicator = false; }
} }
@Override @Override
...@@ -113,6 +121,8 @@ public final class H264Reader implements ElementaryStreamReader { ...@@ -113,6 +121,8 @@ public final class H264Reader implements ElementaryStreamReader {
@Override @Override
public void consume(ParsableByteArray data) { public void consume(ParsableByteArray data) {
assertTracksCreated();
int offset = data.getPosition(); int offset = data.getPosition();
int limit = data.limit(); int limit = data.limit();
byte[] dataArray = data.data; byte[] dataArray = data.data;
...@@ -159,6 +169,7 @@ public final class H264Reader implements ElementaryStreamReader { ...@@ -159,6 +169,7 @@ public final class H264Reader implements ElementaryStreamReader {
// Do nothing. // Do nothing.
} }
@RequiresNonNull("sampleReader")
private void startNalUnit(long position, int nalUnitType, long pesTimeUs) { private void startNalUnit(long position, int nalUnitType, long pesTimeUs) {
if (!hasOutputFormat || sampleReader.needsSpsPps()) { if (!hasOutputFormat || sampleReader.needsSpsPps()) {
sps.startNalUnit(nalUnitType); sps.startNalUnit(nalUnitType);
...@@ -168,6 +179,7 @@ public final class H264Reader implements ElementaryStreamReader { ...@@ -168,6 +179,7 @@ public final class H264Reader implements ElementaryStreamReader {
sampleReader.startNalUnit(position, nalUnitType, pesTimeUs); sampleReader.startNalUnit(position, nalUnitType, pesTimeUs);
} }
@RequiresNonNull("sampleReader")
private void nalUnitData(byte[] dataArray, int offset, int limit) { private void nalUnitData(byte[] dataArray, int offset, int limit) {
if (!hasOutputFormat || sampleReader.needsSpsPps()) { if (!hasOutputFormat || sampleReader.needsSpsPps()) {
sps.appendToNalUnit(dataArray, offset, limit); sps.appendToNalUnit(dataArray, offset, limit);
...@@ -177,6 +189,7 @@ public final class H264Reader implements ElementaryStreamReader { ...@@ -177,6 +189,7 @@ public final class H264Reader implements ElementaryStreamReader {
sampleReader.appendToNalUnit(dataArray, offset, limit); sampleReader.appendToNalUnit(dataArray, offset, limit);
} }
@RequiresNonNull({"output", "sampleReader"})
private void endNalUnit(long position, int offset, int discardPadding, long pesTimeUs) { private void endNalUnit(long position, int offset, int discardPadding, long pesTimeUs) {
if (!hasOutputFormat || sampleReader.needsSpsPps()) { if (!hasOutputFormat || sampleReader.needsSpsPps()) {
sps.endNalUnit(discardPadding); sps.endNalUnit(discardPadding);
...@@ -237,6 +250,12 @@ public final class H264Reader implements ElementaryStreamReader { ...@@ -237,6 +250,12 @@ public final class H264Reader implements ElementaryStreamReader {
} }
} }
@EnsuresNonNull({"output", "sampleReader"})
private void assertTracksCreated() {
Assertions.checkStateNotNull(output);
Util.castNonNull(sampleReader);
}
/** 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 class SampleReader {
...@@ -478,7 +497,7 @@ public final class H264Reader implements ElementaryStreamReader { ...@@ -478,7 +497,7 @@ public final class H264Reader implements ElementaryStreamReader {
private boolean isComplete; private boolean isComplete;
private boolean hasSliceType; private boolean hasSliceType;
private SpsData spsData; @Nullable private SpsData spsData;
private int nalRefIdc; private int nalRefIdc;
private int sliceType; private int sliceType;
private int frameNum; private int frameNum;
...@@ -542,6 +561,8 @@ public final class H264Reader implements ElementaryStreamReader { ...@@ -542,6 +561,8 @@ public final class H264Reader implements ElementaryStreamReader {
private boolean isFirstVclNalUnitOfPicture(SliceHeaderData other) { private boolean isFirstVclNalUnitOfPicture(SliceHeaderData other) {
// See ISO 14496-10 subsection 7.4.1.2.4. // See ISO 14496-10 subsection 7.4.1.2.4.
SpsData spsData = Assertions.checkStateNotNull(this.spsData);
SpsData otherSpsData = Assertions.checkStateNotNull(other.spsData);
return isComplete return isComplete
&& (!other.isComplete && (!other.isComplete
|| frameNum != other.frameNum || frameNum != other.frameNum
...@@ -552,15 +573,15 @@ public final class H264Reader implements ElementaryStreamReader { ...@@ -552,15 +573,15 @@ public final class H264Reader implements ElementaryStreamReader {
&& bottomFieldFlag != other.bottomFieldFlag) && bottomFieldFlag != other.bottomFieldFlag)
|| (nalRefIdc != other.nalRefIdc && (nalRefIdc == 0 || other.nalRefIdc == 0)) || (nalRefIdc != other.nalRefIdc && (nalRefIdc == 0 || other.nalRefIdc == 0))
|| (spsData.picOrderCountType == 0 || (spsData.picOrderCountType == 0
&& other.spsData.picOrderCountType == 0 && otherSpsData.picOrderCountType == 0
&& (picOrderCntLsb != other.picOrderCntLsb && (picOrderCntLsb != other.picOrderCntLsb
|| deltaPicOrderCntBottom != other.deltaPicOrderCntBottom)) || deltaPicOrderCntBottom != other.deltaPicOrderCntBottom))
|| (spsData.picOrderCountType == 1 || (spsData.picOrderCountType == 1
&& other.spsData.picOrderCountType == 1 && otherSpsData.picOrderCountType == 1
&& (deltaPicOrderCnt0 != other.deltaPicOrderCnt0 && (deltaPicOrderCnt0 != other.deltaPicOrderCnt0
|| deltaPicOrderCnt1 != other.deltaPicOrderCnt1)) || deltaPicOrderCnt1 != other.deltaPicOrderCnt1))
|| idrPicFlag != other.idrPicFlag || idrPicFlag != other.idrPicFlag
|| (idrPicFlag && other.idrPicFlag && idrPicId != other.idrPicId)); || (idrPicFlag && idrPicId != other.idrPicId));
} }
} }
} }
......
...@@ -20,12 +20,18 @@ import com.google.android.exoplayer2.Format; ...@@ -20,12 +20,18 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.NalUnitUtil; import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.ParsableNalUnitBitArray; import com.google.android.exoplayer2.util.ParsableNalUnitBitArray;
import com.google.android.exoplayer2.util.Util;
import java.util.Collections; import java.util.Collections;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** /**
* Parses a continuous H.265 byte stream and extracts individual frames. * Parses a continuous H.265 byte stream and extracts individual frames.
...@@ -46,9 +52,9 @@ public final class H265Reader implements ElementaryStreamReader { ...@@ -46,9 +52,9 @@ public final class H265Reader implements ElementaryStreamReader {
private final SeiReader seiReader; private final SeiReader seiReader;
private String formatId; @MonotonicNonNull private String formatId;
private TrackOutput output; @MonotonicNonNull private TrackOutput output;
private SampleReader sampleReader; @MonotonicNonNull private SampleReader sampleReader;
// State that should not be reset on seek. // State that should not be reset on seek.
private boolean hasOutputFormat; private boolean hasOutputFormat;
...@@ -84,14 +90,16 @@ public final class H265Reader implements ElementaryStreamReader { ...@@ -84,14 +90,16 @@ public final class H265Reader implements ElementaryStreamReader {
@Override @Override
public void seek() { public void seek() {
totalBytesWritten = 0;
NalUnitUtil.clearPrefixFlags(prefixFlags); NalUnitUtil.clearPrefixFlags(prefixFlags);
vps.reset(); vps.reset();
sps.reset(); sps.reset();
pps.reset(); pps.reset();
prefixSei.reset(); prefixSei.reset();
suffixSei.reset(); suffixSei.reset();
sampleReader.reset(); if (sampleReader != null) {
totalBytesWritten = 0; sampleReader.reset();
}
} }
@Override @Override
...@@ -111,6 +119,8 @@ public final class H265Reader implements ElementaryStreamReader { ...@@ -111,6 +119,8 @@ public final class H265Reader implements ElementaryStreamReader {
@Override @Override
public void consume(ParsableByteArray data) { public void consume(ParsableByteArray data) {
assertTracksCreated();
while (data.bytesLeft() > 0) { while (data.bytesLeft() > 0) {
int offset = data.getPosition(); int offset = data.getPosition();
int limit = data.limit(); int limit = data.limit();
...@@ -160,6 +170,7 @@ public final class H265Reader implements ElementaryStreamReader { ...@@ -160,6 +170,7 @@ public final class H265Reader implements ElementaryStreamReader {
// Do nothing. // Do nothing.
} }
@RequiresNonNull("sampleReader")
private void startNalUnit(long position, int offset, int nalUnitType, long pesTimeUs) { private void startNalUnit(long position, int offset, int nalUnitType, long pesTimeUs) {
if (hasOutputFormat) { if (hasOutputFormat) {
sampleReader.startNalUnit(position, offset, nalUnitType, pesTimeUs); sampleReader.startNalUnit(position, offset, nalUnitType, pesTimeUs);
...@@ -172,6 +183,7 @@ public final class H265Reader implements ElementaryStreamReader { ...@@ -172,6 +183,7 @@ public final class H265Reader implements ElementaryStreamReader {
suffixSei.startNalUnit(nalUnitType); suffixSei.startNalUnit(nalUnitType);
} }
@RequiresNonNull("sampleReader")
private void nalUnitData(byte[] dataArray, int offset, int limit) { private void nalUnitData(byte[] dataArray, int offset, int limit) {
if (hasOutputFormat) { if (hasOutputFormat) {
sampleReader.readNalUnitData(dataArray, offset, limit); sampleReader.readNalUnitData(dataArray, offset, limit);
...@@ -184,6 +196,7 @@ public final class H265Reader implements ElementaryStreamReader { ...@@ -184,6 +196,7 @@ public final class H265Reader implements ElementaryStreamReader {
suffixSei.appendToNalUnit(dataArray, offset, limit); suffixSei.appendToNalUnit(dataArray, offset, limit);
} }
@RequiresNonNull({"output", "sampleReader"})
private void endNalUnit(long position, int offset, int discardPadding, long pesTimeUs) { private void endNalUnit(long position, int offset, int discardPadding, long pesTimeUs) {
if (hasOutputFormat) { if (hasOutputFormat) {
sampleReader.endNalUnit(position, offset); sampleReader.endNalUnit(position, offset);
...@@ -214,8 +227,11 @@ public final class H265Reader implements ElementaryStreamReader { ...@@ -214,8 +227,11 @@ public final class H265Reader implements ElementaryStreamReader {
} }
} }
private static Format parseMediaFormat(String formatId, NalUnitTargetBuffer vps, private static Format parseMediaFormat(
NalUnitTargetBuffer sps, NalUnitTargetBuffer pps) { @Nullable String formatId,
NalUnitTargetBuffer vps,
NalUnitTargetBuffer sps,
NalUnitTargetBuffer pps) {
// Build codec-specific data. // Build codec-specific data.
byte[] csd = new byte[vps.nalLength + sps.nalLength + pps.nalLength]; byte[] csd = new byte[vps.nalLength + sps.nalLength + pps.nalLength];
System.arraycopy(vps.nalData, 0, csd, 0, vps.nalLength); System.arraycopy(vps.nalData, 0, csd, 0, vps.nalLength);
...@@ -389,6 +405,12 @@ public final class H265Reader implements ElementaryStreamReader { ...@@ -389,6 +405,12 @@ public final class H265Reader implements ElementaryStreamReader {
} }
} }
@EnsuresNonNull({"output", "sampleReader"})
private void assertTracksCreated() {
Assertions.checkStateNotNull(output);
Util.castNonNull(sampleReader);
}
private static final class SampleReader { private static final class SampleReader {
/** /**
......
...@@ -23,9 +23,11 @@ import com.google.android.exoplayer2.Format; ...@@ -23,9 +23,11 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* Parses ID3 data and extracts individual text information frames. * Parses ID3 data and extracts individual text information frames.
...@@ -36,7 +38,7 @@ public final class Id3Reader implements ElementaryStreamReader { ...@@ -36,7 +38,7 @@ public final class Id3Reader implements ElementaryStreamReader {
private final ParsableByteArray id3Header; private final ParsableByteArray id3Header;
private TrackOutput output; @MonotonicNonNull private TrackOutput output;
// State that should be reset on seek. // State that should be reset on seek.
private boolean writingSample; private boolean writingSample;
...@@ -76,6 +78,7 @@ public final class Id3Reader implements ElementaryStreamReader { ...@@ -76,6 +78,7 @@ public final class Id3Reader implements ElementaryStreamReader {
@Override @Override
public void consume(ParsableByteArray data) { public void consume(ParsableByteArray data) {
Assertions.checkStateNotNull(output); // Asserts that createTracks has been called.
if (!writingSample) { if (!writingSample) {
return; return;
} }
...@@ -106,6 +109,7 @@ public final class Id3Reader implements ElementaryStreamReader { ...@@ -106,6 +109,7 @@ public final class Id3Reader implements ElementaryStreamReader {
@Override @Override
public void packetFinished() { public void packetFinished() {
Assertions.checkStateNotNull(output); // Asserts that createTracks has been called.
if (!writingSample || sampleSize == 0 || sampleBytesRead != sampleSize) { if (!writingSample || sampleSize == 0 || sampleBytesRead != sampleSize) {
return; return;
} }
......
...@@ -23,11 +23,14 @@ import com.google.android.exoplayer2.ParserException; ...@@ -23,11 +23,14 @@ import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.Collections; import java.util.Collections;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** /**
* Parses and extracts samples from an AAC/LATM elementary stream. * Parses and extracts samples from an AAC/LATM elementary stream.
...@@ -43,14 +46,14 @@ public final class LatmReader implements ElementaryStreamReader { ...@@ -43,14 +46,14 @@ public final class LatmReader implements ElementaryStreamReader {
private static final int SYNC_BYTE_FIRST = 0x56; private static final int SYNC_BYTE_FIRST = 0x56;
private static final int SYNC_BYTE_SECOND = 0xE0; private static final int SYNC_BYTE_SECOND = 0xE0;
private final String language; @Nullable private final String language;
private final ParsableByteArray sampleDataBuffer; private final ParsableByteArray sampleDataBuffer;
private final ParsableBitArray sampleBitArray; private final ParsableBitArray sampleBitArray;
// Track output info. // Track output info.
private TrackOutput output; @MonotonicNonNull private TrackOutput output;
private Format format; @MonotonicNonNull private String formatId;
private String formatId; @MonotonicNonNull private Format format;
// Parser state info. // Parser state info.
private int state; private int state;
...@@ -99,6 +102,7 @@ public final class LatmReader implements ElementaryStreamReader { ...@@ -99,6 +102,7 @@ public final class LatmReader implements ElementaryStreamReader {
@Override @Override
public void consume(ParsableByteArray data) throws ParserException { public void consume(ParsableByteArray data) throws ParserException {
Assertions.checkStateNotNull(output); // Asserts that createTracks has been called.
int bytesToRead; int bytesToRead;
while (data.bytesLeft() > 0) { while (data.bytesLeft() > 0) {
switch (state) { switch (state) {
...@@ -150,6 +154,7 @@ public final class LatmReader implements ElementaryStreamReader { ...@@ -150,6 +154,7 @@ public final class LatmReader implements ElementaryStreamReader {
* *
* @param data A {@link ParsableBitArray} containing the AudioMuxElement's bytes. * @param data A {@link ParsableBitArray} containing the AudioMuxElement's bytes.
*/ */
@RequiresNonNull("output")
private void parseAudioMuxElement(ParsableBitArray data) throws ParserException { private void parseAudioMuxElement(ParsableBitArray data) throws ParserException {
boolean useSameStreamMux = data.readBit(); boolean useSameStreamMux = data.readBit();
if (!useSameStreamMux) { if (!useSameStreamMux) {
...@@ -173,9 +178,8 @@ public final class LatmReader implements ElementaryStreamReader { ...@@ -173,9 +178,8 @@ public final class LatmReader implements ElementaryStreamReader {
} }
} }
/** /** Parses a StreamMuxConfig as defined in ISO/IEC 14496-3:2009 Section 1.7.3.1, Table 1.42. */
* Parses a StreamMuxConfig as defined in ISO/IEC 14496-3:2009 Section 1.7.3.1, Table 1.42. @RequiresNonNull("output")
*/
private void parseStreamMuxConfig(ParsableBitArray data) throws ParserException { private void parseStreamMuxConfig(ParsableBitArray data) throws ParserException {
int audioMuxVersion = data.readBits(1); int audioMuxVersion = data.readBits(1);
audioMuxVersionA = audioMuxVersion == 1 ? data.readBits(1) : 0; audioMuxVersionA = audioMuxVersion == 1 ? data.readBits(1) : 0;
...@@ -198,9 +202,19 @@ public final class LatmReader implements ElementaryStreamReader { ...@@ -198,9 +202,19 @@ public final class LatmReader implements ElementaryStreamReader {
data.setPosition(startPosition); data.setPosition(startPosition);
byte[] initData = new byte[(readBits + 7) / 8]; byte[] initData = new byte[(readBits + 7) / 8];
data.readBits(initData, 0, readBits); data.readBits(initData, 0, readBits);
Format format = Format.createAudioSampleFormat(formatId, MimeTypes.AUDIO_AAC, null, Format format =
Format.NO_VALUE, Format.NO_VALUE, channelCount, sampleRateHz, Format.createAudioSampleFormat(
Collections.singletonList(initData), null, 0, language); formatId,
MimeTypes.AUDIO_AAC,
/* codecs= */ null,
Format.NO_VALUE,
Format.NO_VALUE,
channelCount,
sampleRateHz,
Collections.singletonList(initData),
/* drmInitData= */ null,
/* selectionFlags= */ 0,
language);
if (!format.equals(this.format)) { if (!format.equals(this.format)) {
this.format = format; this.format = format;
sampleDurationUs = (C.MICROS_PER_SECOND * 1024) / format.sampleRate; sampleDurationUs = (C.MICROS_PER_SECOND * 1024) / format.sampleRate;
...@@ -280,6 +294,7 @@ public final class LatmReader implements ElementaryStreamReader { ...@@ -280,6 +294,7 @@ public final class LatmReader implements ElementaryStreamReader {
} }
} }
@RequiresNonNull("output")
private void parsePayloadMux(ParsableBitArray data, int muxLengthBytes) { private void parsePayloadMux(ParsableBitArray data, int muxLengthBytes) {
// The start of sample data in // The start of sample data in
int bitPosition = data.getPosition(); int bitPosition = data.getPosition();
......
...@@ -21,7 +21,11 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput; ...@@ -21,7 +21,11 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.MpegAudioHeader; import com.google.android.exoplayer2.extractor.MpegAudioHeader;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** /**
* Parses a continuous MPEG Audio byte stream and extracts individual frames. * Parses a continuous MPEG Audio byte stream and extracts individual frames.
...@@ -36,10 +40,10 @@ public final class MpegAudioReader implements ElementaryStreamReader { ...@@ -36,10 +40,10 @@ public final class MpegAudioReader implements ElementaryStreamReader {
private final ParsableByteArray headerScratch; private final ParsableByteArray headerScratch;
private final MpegAudioHeader header; private final MpegAudioHeader header;
private final String language; @Nullable private final String language;
private String formatId; @MonotonicNonNull private TrackOutput output;
private TrackOutput output; @MonotonicNonNull private String formatId;
private int state; private int state;
private int frameBytesRead; private int frameBytesRead;
...@@ -59,7 +63,7 @@ public final class MpegAudioReader implements ElementaryStreamReader { ...@@ -59,7 +63,7 @@ public final class MpegAudioReader implements ElementaryStreamReader {
this(null); this(null);
} }
public MpegAudioReader(String language) { public MpegAudioReader(@Nullable String language) {
state = STATE_FINDING_HEADER; state = STATE_FINDING_HEADER;
// The first byte of an MPEG Audio frame header is always 0xFF. // The first byte of an MPEG Audio frame header is always 0xFF.
headerScratch = new ParsableByteArray(4); headerScratch = new ParsableByteArray(4);
...@@ -89,6 +93,7 @@ public final class MpegAudioReader implements ElementaryStreamReader { ...@@ -89,6 +93,7 @@ public final class MpegAudioReader implements ElementaryStreamReader {
@Override @Override
public void consume(ParsableByteArray data) { public void consume(ParsableByteArray data) {
Assertions.checkStateNotNull(output); // Asserts that createTracks has been called.
while (data.bytesLeft() > 0) { while (data.bytesLeft() > 0) {
switch (state) { switch (state) {
case STATE_FINDING_HEADER: case STATE_FINDING_HEADER:
...@@ -146,20 +151,21 @@ public final class MpegAudioReader implements ElementaryStreamReader { ...@@ -146,20 +151,21 @@ public final class MpegAudioReader implements ElementaryStreamReader {
/** /**
* Attempts to read the remaining two bytes of the frame header. * Attempts to read the remaining two bytes of the frame header.
* <p> *
* If a frame header is read in full then the state is changed to {@link #STATE_READING_FRAME}, * <p>If a frame header is read in full then the state is changed to {@link #STATE_READING_FRAME},
* the media format is output if this has not previously occurred, the four header bytes are * the media format is output if this has not previously occurred, the four header bytes are
* output as sample data, and the position of the source is advanced to the byte that immediately * output as sample data, and the position of the source is advanced to the byte that immediately
* follows the header. * follows the header.
* <p> *
* If a frame header is read in full but cannot be parsed then the state is changed to * <p>If a frame header is read in full but cannot be parsed then the state is changed to {@link
* {@link #STATE_READING_HEADER}. * #STATE_READING_HEADER}.
* <p> *
* If a frame header is not read in full then the position of the source is advanced to the limit, * <p>If a frame header is not read in full then the position of the source is advanced to the
* and the method should be called again with the next source to continue the read. * limit, and the method should be called again with the next source to continue the read.
* *
* @param source The source from which to read. * @param source The source from which to read.
*/ */
@RequiresNonNull("output")
private void readHeaderRemainder(ParsableByteArray source) { private void readHeaderRemainder(ParsableByteArray source) {
int bytesToRead = Math.min(source.bytesLeft(), HEADER_SIZE - frameBytesRead); int bytesToRead = Math.min(source.bytesLeft(), HEADER_SIZE - frameBytesRead);
source.readBytes(headerScratch.data, frameBytesRead, bytesToRead); source.readBytes(headerScratch.data, frameBytesRead, bytesToRead);
...@@ -195,16 +201,17 @@ public final class MpegAudioReader implements ElementaryStreamReader { ...@@ -195,16 +201,17 @@ public final class MpegAudioReader implements ElementaryStreamReader {
/** /**
* Attempts to read the remainder of the frame. * Attempts to read the remainder of the frame.
* <p> *
* If a frame is read in full then true is returned. The frame will have been output, and the * <p>If a frame is read in full then true is returned. The frame will have been output, and the
* position of the source will have been advanced to the byte that immediately follows the end of * position of the source will have been advanced to the byte that immediately follows the end of
* the frame. * the frame.
* <p> *
* If a frame is not read in full then the position of the source will have been advanced to the * <p>If a frame is not read in full then the position of the source will have been advanced to
* limit, and the method should be called again with the next source to continue the read. * the limit, and the method should be called again with the next source to continue the read.
* *
* @param source The source from which to read. * @param source The source from which to read.
*/ */
@RequiresNonNull("output")
private void readFrameRemainder(ParsableByteArray source) { private void readFrameRemainder(ParsableByteArray source) {
int bytesToRead = Math.min(source.bytesLeft(), frameSize - frameBytesRead); int bytesToRead = Math.min(source.bytesLeft(), frameSize - frameBytesRead);
output.sampleData(source, bytesToRead); output.sampleData(source, bytesToRead);
...@@ -219,5 +226,4 @@ public final class MpegAudioReader implements ElementaryStreamReader { ...@@ -219,5 +226,4 @@ public final class MpegAudioReader implements ElementaryStreamReader {
frameBytesRead = 0; frameBytesRead = 0;
state = STATE_FINDING_HEADER; state = STATE_FINDING_HEADER;
} }
} }
...@@ -15,13 +15,17 @@ ...@@ -15,13 +15,17 @@
*/ */
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.TimestampAdjuster;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** /**
* Parses PES packet data and extracts samples. * Parses PES packet data and extracts samples.
...@@ -45,7 +49,7 @@ public final class PesReader implements TsPayloadReader { ...@@ -45,7 +49,7 @@ public final class PesReader implements TsPayloadReader {
private int state; private int state;
private int bytesRead; private int bytesRead;
private TimestampAdjuster timestampAdjuster; @MonotonicNonNull private TimestampAdjuster timestampAdjuster;
private boolean ptsFlag; private boolean ptsFlag;
private boolean dtsFlag; private boolean dtsFlag;
private boolean seenFirstDts; private boolean seenFirstDts;
...@@ -79,6 +83,8 @@ public final class PesReader implements TsPayloadReader { ...@@ -79,6 +83,8 @@ public final class PesReader implements TsPayloadReader {
@Override @Override
public final void consume(ParsableByteArray data, @Flags int flags) throws ParserException { public final void consume(ParsableByteArray data, @Flags int flags) throws ParserException {
Assertions.checkStateNotNull(timestampAdjuster); // Asserts init has been called.
if ((flags & FLAG_PAYLOAD_UNIT_START_INDICATOR) != 0) { if ((flags & FLAG_PAYLOAD_UNIT_START_INDICATOR) != 0) {
switch (state) { switch (state) {
case STATE_FINDING_HEADER: case STATE_FINDING_HEADER:
...@@ -119,7 +125,7 @@ public final class PesReader implements TsPayloadReader { ...@@ -119,7 +125,7 @@ public final class PesReader implements TsPayloadReader {
int readLength = Math.min(MAX_HEADER_EXTENSION_SIZE, extendedHeaderLength); int readLength = Math.min(MAX_HEADER_EXTENSION_SIZE, extendedHeaderLength);
// Read as much of the extended header as we're interested in, and skip the rest. // Read as much of the extended header as we're interested in, and skip the rest.
if (continueRead(data, pesScratch.data, readLength) if (continueRead(data, pesScratch.data, readLength)
&& continueRead(data, null, extendedHeaderLength)) { && continueRead(data, /* target= */ null, extendedHeaderLength)) {
parseHeaderExtension(); parseHeaderExtension();
flags |= dataAlignmentIndicator ? FLAG_DATA_ALIGNMENT_INDICATOR : 0; flags |= dataAlignmentIndicator ? FLAG_DATA_ALIGNMENT_INDICATOR : 0;
reader.packetStarted(timeUs, flags); reader.packetStarted(timeUs, flags);
...@@ -162,7 +168,8 @@ public final class PesReader implements TsPayloadReader { ...@@ -162,7 +168,8 @@ public final class PesReader implements TsPayloadReader {
* @param targetLength The target length of the read. * @param targetLength The target length of the read.
* @return Whether the target length has been reached. * @return Whether the target length has been reached.
*/ */
private boolean continueRead(ParsableByteArray source, byte[] target, int targetLength) { private boolean continueRead(
ParsableByteArray source, @Nullable byte[] target, int targetLength) {
int bytesToRead = Math.min(source.bytesLeft(), targetLength - bytesRead); int bytesToRead = Math.min(source.bytesLeft(), targetLength - bytesRead);
if (bytesToRead <= 0) { if (bytesToRead <= 0) {
return true; return true;
...@@ -207,6 +214,7 @@ public final class PesReader implements TsPayloadReader { ...@@ -207,6 +214,7 @@ public final class PesReader implements TsPayloadReader {
return true; return true;
} }
@RequiresNonNull("timestampAdjuster")
private void parseHeaderExtension() { private void parseHeaderExtension() {
pesScratch.setPosition(0); pesScratch.setPosition(0);
timeUs = C.TIME_UNSET; timeUs = C.TIME_UNSET;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import android.util.SparseArray; import android.util.SparseArray;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.Extractor;
...@@ -25,10 +26,13 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory; ...@@ -25,10 +26,13 @@ import com.google.android.exoplayer2.extractor.ExtractorsFactory;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator; import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.TrackIdGenerator;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.ParsableBitArray; import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.TimestampAdjuster;
import java.io.IOException; import java.io.IOException;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** /**
* Extracts data from the MPEG-2 PS container format. * Extracts data from the MPEG-2 PS container format.
...@@ -67,8 +71,8 @@ public final class PsExtractor implements Extractor { ...@@ -67,8 +71,8 @@ public final class PsExtractor implements Extractor {
private long lastTrackPosition; private long lastTrackPosition;
// Accessed only by the loading thread. // Accessed only by the loading thread.
private PsBinarySearchSeeker psBinarySearchSeeker; @Nullable private PsBinarySearchSeeker psBinarySearchSeeker;
private ExtractorOutput output; @MonotonicNonNull private ExtractorOutput output;
private boolean hasOutputSeekMap; private boolean hasOutputSeekMap;
public PsExtractor() { public PsExtractor() {
...@@ -160,6 +164,7 @@ public final class PsExtractor implements Extractor { ...@@ -160,6 +164,7 @@ public final class PsExtractor implements Extractor {
@Override @Override
public int read(ExtractorInput input, PositionHolder seekPosition) public int read(ExtractorInput input, PositionHolder seekPosition)
throws IOException, InterruptedException { throws IOException, InterruptedException {
Assertions.checkStateNotNull(output); // Asserts init has been called.
long inputLength = input.getLength(); long inputLength = input.getLength();
boolean canReadDuration = inputLength != C.LENGTH_UNSET; boolean canReadDuration = inputLength != C.LENGTH_UNSET;
...@@ -221,7 +226,7 @@ public final class PsExtractor implements Extractor { ...@@ -221,7 +226,7 @@ public final class PsExtractor implements Extractor {
PesReader payloadReader = psPayloadReaders.get(streamId); PesReader payloadReader = psPayloadReaders.get(streamId);
if (!foundAllTracks) { if (!foundAllTracks) {
if (payloadReader == null) { if (payloadReader == null) {
ElementaryStreamReader elementaryStreamReader = null; @Nullable ElementaryStreamReader elementaryStreamReader = null;
if (streamId == PRIVATE_STREAM_1) { if (streamId == PRIVATE_STREAM_1) {
// Private stream, used for AC3 audio. // Private stream, used for AC3 audio.
// NOTE: This may need further parsing to determine if its DTS, but that's likely only // NOTE: This may need further parsing to determine if its DTS, but that's likely only
...@@ -278,6 +283,7 @@ public final class PsExtractor implements Extractor { ...@@ -278,6 +283,7 @@ public final class PsExtractor implements Extractor {
// Internals. // Internals.
@RequiresNonNull("output")
private void maybeOutputSeekMap(long inputLength) { private void maybeOutputSeekMap(long inputLength) {
if (!hasOutputSeekMap) { if (!hasOutputSeekMap) {
hasOutputSeekMap = true; hasOutputSeekMap = true;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
...@@ -45,7 +46,7 @@ public final class SeiReader { ...@@ -45,7 +46,7 @@ public final class SeiReader {
idGenerator.generateNewId(); idGenerator.generateNewId();
TrackOutput output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_TEXT); TrackOutput output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_TEXT);
Format channelFormat = closedCaptionFormats.get(i); Format channelFormat = closedCaptionFormats.get(i);
String channelMimeType = channelFormat.sampleMimeType; @Nullable String channelMimeType = channelFormat.sampleMimeType;
Assertions.checkArgument(MimeTypes.APPLICATION_CEA608.equals(channelMimeType) Assertions.checkArgument(MimeTypes.APPLICATION_CEA608.equals(channelMimeType)
|| MimeTypes.APPLICATION_CEA708.equals(channelMimeType), || MimeTypes.APPLICATION_CEA708.equals(channelMimeType),
"Invalid closed caption mime type provided: " + channelMimeType); "Invalid closed caption mime type provided: " + channelMimeType);
...@@ -69,5 +70,4 @@ public final class SeiReader { ...@@ -69,5 +70,4 @@ public final class SeiReader {
public void consume(long pesTimeUs, ParsableByteArray seiBuffer) { public void consume(long pesTimeUs, ParsableByteArray seiBuffer) {
CeaUtil.consume(pesTimeUs, seiBuffer, outputs); CeaUtil.consume(pesTimeUs, seiBuffer, outputs);
} }
} }
...@@ -19,17 +19,21 @@ import com.google.android.exoplayer2.C; ...@@ -19,17 +19,21 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.android.exoplayer2.util.Util;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* Parses splice info sections as defined by SCTE35. * Parses splice info sections as defined by SCTE35.
*/ */
public final class SpliceInfoSectionReader implements SectionPayloadReader { public final class SpliceInfoSectionReader implements SectionPayloadReader {
private TimestampAdjuster timestampAdjuster; @MonotonicNonNull private TimestampAdjuster timestampAdjuster;
private TrackOutput output; @MonotonicNonNull private TrackOutput output;
private boolean formatDeclared; private boolean formatDeclared;
@Override @Override
...@@ -44,6 +48,7 @@ public final class SpliceInfoSectionReader implements SectionPayloadReader { ...@@ -44,6 +48,7 @@ public final class SpliceInfoSectionReader implements SectionPayloadReader {
@Override @Override
public void consume(ParsableByteArray sectionData) { public void consume(ParsableByteArray sectionData) {
assertInitialized();
if (!formatDeclared) { if (!formatDeclared) {
if (timestampAdjuster.getTimestampOffsetUs() == C.TIME_UNSET) { if (timestampAdjuster.getTimestampOffsetUs() == C.TIME_UNSET) {
// There is not enough information to initialize the timestamp adjuster. // There is not enough information to initialize the timestamp adjuster.
...@@ -59,4 +64,9 @@ public final class SpliceInfoSectionReader implements SectionPayloadReader { ...@@ -59,4 +64,9 @@ public final class SpliceInfoSectionReader implements SectionPayloadReader {
sampleSize, 0, null); sampleSize, 0, null);
} }
@EnsuresNonNull({"timestampAdjuster", "output"})
private void assertInitialized() {
Assertions.checkStateNotNull(timestampAdjuster);
Util.castNonNull(output);
}
} }
...@@ -21,6 +21,7 @@ import android.util.SparseArray; ...@@ -21,6 +21,7 @@ import android.util.SparseArray;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.Extractor;
...@@ -587,8 +588,11 @@ public final class TsExtractor implements Extractor { ...@@ -587,8 +588,11 @@ public final class TsExtractor implements Extractor {
continue; continue;
} }
TsPayloadReader reader = mode == MODE_HLS && streamType == TS_STREAM_TYPE_ID3 ? id3Reader @Nullable
: payloadReaderFactory.createPayloadReader(streamType, esInfo); TsPayloadReader reader =
mode == MODE_HLS && streamType == TS_STREAM_TYPE_ID3
? id3Reader
: payloadReaderFactory.createPayloadReader(streamType, esInfo);
if (mode != MODE_HLS if (mode != MODE_HLS
|| elementaryPid < trackIdToPidScratch.get(trackId, MAX_PID_PLUS_ONE)) { || elementaryPid < trackIdToPidScratch.get(trackId, MAX_PID_PLUS_ONE)) {
trackIdToPidScratch.put(trackId, elementaryPid); trackIdToPidScratch.put(trackId, elementaryPid);
...@@ -602,7 +606,7 @@ public final class TsExtractor implements Extractor { ...@@ -602,7 +606,7 @@ public final class TsExtractor implements Extractor {
int trackPid = trackIdToPidScratch.valueAt(i); int trackPid = trackIdToPidScratch.valueAt(i);
trackIds.put(trackId, true); trackIds.put(trackId, true);
trackPids.put(trackPid, true); trackPids.put(trackPid, true);
TsPayloadReader reader = trackIdToReaderScratch.valueAt(i); @Nullable TsPayloadReader reader = trackIdToReaderScratch.valueAt(i);
if (reader != null) { if (reader != null) {
if (reader != id3Reader) { if (reader != id3Reader) {
reader.init(timestampAdjuster, output, reader.init(timestampAdjuster, output,
......
...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.ts; ...@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.extractor.ts;
import android.util.SparseArray; import android.util.SparseArray;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
...@@ -53,11 +54,11 @@ public interface TsPayloadReader { ...@@ -53,11 +54,11 @@ public interface TsPayloadReader {
* *
* @param streamType Stream type value as defined in the PMT entry or associated descriptors. * @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. * @param esInfo Information associated to the elementary stream provided in the PMT.
* @return A {@link TsPayloadReader} for the packet stream carried by the provided pid. * @return A {@link TsPayloadReader} for the packet stream carried by the provided pid, or
* {@code null} if the stream is not supported. * {@code null} if the stream is not supported.
*/ */
@Nullable
TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo); TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo);
} }
/** /**
...@@ -66,18 +67,21 @@ public interface TsPayloadReader { ...@@ -66,18 +67,21 @@ public interface TsPayloadReader {
final class EsInfo { final class EsInfo {
public final int streamType; public final int streamType;
public final String language; @Nullable public final String language;
public final List<DvbSubtitleInfo> dvbSubtitleInfos; public final List<DvbSubtitleInfo> dvbSubtitleInfos;
public final byte[] descriptorBytes; public final byte[] descriptorBytes;
/** /**
* @param streamType The type of the stream as defined by the * @param streamType The type of the stream as defined by the {@link TsExtractor}{@code
* {@link TsExtractor}{@code .TS_STREAM_TYPE_*}. * .TS_STREAM_TYPE_*}.
* @param language The language of the stream, as defined by ISO/IEC 13818-1, section 2.6.18. * @param language The language of the stream, as defined by ISO/IEC 13818-1, section 2.6.18.
* @param dvbSubtitleInfos Information about DVB subtitles associated to the stream. * @param dvbSubtitleInfos Information about DVB subtitles associated to the stream.
* @param descriptorBytes The descriptor bytes associated to the stream. * @param descriptorBytes The descriptor bytes associated to the stream.
*/ */
public EsInfo(int streamType, String language, List<DvbSubtitleInfo> dvbSubtitleInfos, public EsInfo(
int streamType,
@Nullable String language,
@Nullable List<DvbSubtitleInfo> dvbSubtitleInfos,
byte[] descriptorBytes) { byte[] descriptorBytes) {
this.streamType = streamType; this.streamType = streamType;
this.language = language; this.language = language;
...@@ -134,6 +138,7 @@ public interface TsPayloadReader { ...@@ -134,6 +138,7 @@ public interface TsPayloadReader {
this.firstTrackId = firstTrackId; this.firstTrackId = firstTrackId;
this.trackIdIncrement = trackIdIncrement; this.trackIdIncrement = trackIdIncrement;
trackId = ID_UNSET; trackId = ID_UNSET;
formatId = "";
} }
/** /**
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.extractor.ts; package com.google.android.exoplayer2.extractor.ts;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.extractor.ExtractorOutput; import com.google.android.exoplayer2.extractor.ExtractorOutput;
...@@ -44,7 +45,7 @@ import java.util.List; ...@@ -44,7 +45,7 @@ import java.util.List;
idGenerator.generateNewId(); idGenerator.generateNewId();
TrackOutput output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_TEXT); TrackOutput output = extractorOutput.track(idGenerator.getTrackId(), C.TRACK_TYPE_TEXT);
Format channelFormat = closedCaptionFormats.get(i); Format channelFormat = closedCaptionFormats.get(i);
String channelMimeType = channelFormat.sampleMimeType; @Nullable String channelMimeType = channelFormat.sampleMimeType;
Assertions.checkArgument( Assertions.checkArgument(
MimeTypes.APPLICATION_CEA608.equals(channelMimeType) MimeTypes.APPLICATION_CEA608.equals(channelMimeType)
|| MimeTypes.APPLICATION_CEA708.equals(channelMimeType), || MimeTypes.APPLICATION_CEA708.equals(channelMimeType),
......
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@NonNullApi
package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.util.NonNullApi;
...@@ -773,7 +773,7 @@ public final class DownloadHelper { ...@@ -773,7 +773,7 @@ public final class DownloadHelper {
} }
// Initialization of array of Lists. // Initialization of array of Lists.
@SuppressWarnings("unchecked") @SuppressWarnings({"unchecked", "rawtypes"})
private void onMediaPrepared() { private void onMediaPrepared() {
Assertions.checkNotNull(mediaPreparer); Assertions.checkNotNull(mediaPreparer);
Assertions.checkNotNull(mediaPreparer.mediaPeriods); Assertions.checkNotNull(mediaPreparer.mediaPeriods);
......
...@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ts; ...@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.extractor.ts;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.util.SparseArray; import android.util.SparseArray;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
...@@ -172,6 +173,7 @@ public final class TsExtractorTest { ...@@ -172,6 +173,7 @@ public final class TsExtractorTest {
} }
} }
@Nullable
@Override @Override
public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) { public TsPayloadReader createPayloadReader(int streamType, EsInfo esInfo) {
if (provideCustomEsReader && streamType == 3) { if (provideCustomEsReader && streamType == 3) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment