mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add package level NonNull to extractor.ts
Also remove most classes from the nullness blacklist PiperOrigin-RevId: 288494712
This commit is contained in:
parent
fb42f818ec
commit
63f90adef0
23 changed files with 307 additions and 139 deletions
|
|
@ -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 @@
|
||||||
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;
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
|
|
||||||
// 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 {
|
||||||
*
|
*
|
||||||
* @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 {
|
||||||
@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 {
|
||||||
|
|
||||||
@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 {
|
||||||
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 @@
|
||||||
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;
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
|
|
||||||
// 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 {
|
||||||
*
|
*
|
||||||
* @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 {
|
||||||
@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 {
|
||||||
|
|
||||||
@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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 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 {
|
||||||
|| !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 @@
|
||||||
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;
|
||||||
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 {
|
||||||
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 {
|
||||||
// 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 {
|
||||||
* @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 {
|
||||||
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 {
|
||||||
|
|
||||||
@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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
// 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 @@
|
||||||
*/
|
*/
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
// 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 {
|
||||||
*
|
*
|
||||||
* @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 {
|
||||||
|
|
||||||
@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 {
|
||||||
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 {
|
||||||
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 @@
|
||||||
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 {
|
||||||
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};
|
||||||
|
|
||||||
|
@Nullable private final UserDataReader userDataReader;
|
||||||
|
@Nullable private final ParsableByteArray userDataParsable;
|
||||||
|
|
||||||
|
// State that should be reset on seek.
|
||||||
|
@Nullable private final NalUnitTargetBuffer userData;
|
||||||
|
private final boolean[] prefixFlags;
|
||||||
|
private final CsdBuffer csdBuffer;
|
||||||
|
private long totalBytesWritten;
|
||||||
|
private boolean startedFirstSample;
|
||||||
|
|
||||||
// State that should not be reset on seek.
|
// State that should not be reset on seek.
|
||||||
private boolean hasOutputFormat;
|
private boolean hasOutputFormat;
|
||||||
private long frameDurationUs;
|
private long frameDurationUs;
|
||||||
|
|
||||||
private final UserDataReader userDataReader;
|
|
||||||
private final ParsableByteArray userDataParsable;
|
|
||||||
|
|
||||||
// State that should be reset on seek.
|
|
||||||
private final boolean[] prefixFlags;
|
|
||||||
private final CsdBuffer csdBuffer;
|
|
||||||
private final NalUnitTargetBuffer userData;
|
|
||||||
private long totalBytesWritten;
|
|
||||||
private boolean startedFirstSample;
|
|
||||||
|
|
||||||
// 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
@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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
*
|
*
|
||||||
* @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;
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
@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 {
|
||||||
|
|
||||||
@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 {
|
||||||
// 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
&& 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;
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
|
|
||||||
@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 {
|
||||||
|
|
||||||
@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 {
|
||||||
// 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
|
|
||||||
@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 {
|
||||||
|
|
||||||
@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;
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
@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 {
|
||||||
*
|
*
|
||||||
* @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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 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 {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
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 {
|
||||||
|
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
@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 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 {
|
||||||
frameBytesRead = 0;
|
frameBytesRead = 0;
|
||||||
state = STATE_FINDING_HEADER;
|
state = STATE_FINDING_HEADER;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
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 {
|
||||||
|
|
||||||
@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 {
|
||||||
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 {
|
||||||
* @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 {
|
||||||
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 @@
|
||||||
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;
|
||||||
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 {
|
||||||
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 {
|
||||||
@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 {
|
||||||
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 {
|
||||||
|
|
||||||
// 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 @@
|
||||||
*/
|
*/
|
||||||
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 {
|
||||||
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 {
|
||||||
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;
|
||||||
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 {
|
||||||
|
|
||||||
@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 {
|
||||||
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;
|
||||||
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 {
|
||||||
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 {
|
||||||
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;
|
||||||
|
|
||||||
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 {
|
||||||
*
|
*
|
||||||
* @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 {
|
||||||
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 {
|
||||||
this.firstTrackId = firstTrackId;
|
this.firstTrackId = firstTrackId;
|
||||||
this.trackIdIncrement = trackIdIncrement;
|
this.trackIdIncrement = trackIdIncrement;
|
||||||
trackId = ID_UNSET;
|
trackId = ID_UNSET;
|
||||||
|
formatId = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
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),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
/*
|
||||||
|
* 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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue