mirror of
https://github.com/androidx/media.git
synced 2025-05-09 16:40:55 +08:00
Add support for multiple programs in a single TS
* Prevents calling endTracks() before all PMTs have been processed. * Adds a unique ID to the format of each track. This allows the track selector to identify which track belongs to each program. The format of each id is "<program number>/<track output id>". Note: This CL will break malformed TS files whose PAT declares more PMTs than it actually contains, which previously were supported. This does not apply for HLS mode. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=146151642
This commit is contained in:
parent
4301606200
commit
feeec77407
@ -6,7 +6,7 @@ numberOfTracks = 1
|
||||
track 0:
|
||||
format:
|
||||
bitrate = -1
|
||||
id = null
|
||||
id = 0
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/ac3
|
||||
maxInputSize = -1
|
||||
|
@ -6,7 +6,7 @@ numberOfTracks = 2
|
||||
track 0:
|
||||
format:
|
||||
bitrate = -1
|
||||
id = null
|
||||
id = 0
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/mp4a-latm
|
||||
maxInputSize = -1
|
||||
@ -606,7 +606,7 @@ track 0:
|
||||
track 1:
|
||||
format:
|
||||
bitrate = -1
|
||||
id = null
|
||||
id = 1
|
||||
containerMimeType = null
|
||||
sampleMimeType = application/id3
|
||||
maxInputSize = -1
|
||||
|
@ -6,7 +6,7 @@ numberOfTracks = 2
|
||||
track 192:
|
||||
format:
|
||||
bitrate = -1
|
||||
id = null
|
||||
id = 192
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/mpeg-L2
|
||||
maxInputSize = 4096
|
||||
@ -45,7 +45,7 @@ track 192:
|
||||
track 224:
|
||||
format:
|
||||
bitrate = -1
|
||||
id = null
|
||||
id = 224
|
||||
containerMimeType = null
|
||||
sampleMimeType = video/mpeg2
|
||||
maxInputSize = -1
|
||||
|
@ -6,7 +6,7 @@ numberOfTracks = 2
|
||||
track 256:
|
||||
format:
|
||||
bitrate = -1
|
||||
id = null
|
||||
id = 1/256
|
||||
containerMimeType = null
|
||||
sampleMimeType = video/mpeg2
|
||||
maxInputSize = -1
|
||||
@ -38,7 +38,7 @@ track 256:
|
||||
track 257:
|
||||
format:
|
||||
bitrate = -1
|
||||
id = null
|
||||
id = 1/257
|
||||
containerMimeType = null
|
||||
sampleMimeType = audio/mpeg-L2
|
||||
maxInputSize = 4096
|
||||
|
@ -92,7 +92,7 @@ public final class TsExtractorTest extends InstrumentationTestCase {
|
||||
TrackOutput trackOutput = reader.getTrackOutput();
|
||||
assertTrue(trackOutput == output.trackOutputs.get(257 /* PID of audio track. */));
|
||||
assertEquals(
|
||||
Format.createTextSampleFormat("Overriding format", "mime", null, 0, 0, "und", null, 0),
|
||||
Format.createTextSampleFormat("1/257", "mime", null, 0, 0, "und", null, 0),
|
||||
((FakeTrackOutput) trackOutput).format);
|
||||
}
|
||||
|
||||
@ -178,8 +178,9 @@ public final class TsExtractorTest extends InstrumentationTestCase {
|
||||
|
||||
@Override
|
||||
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
|
||||
output = extractorOutput.track(idGenerator.getNextId());
|
||||
output.format(Format.createTextSampleFormat("Overriding format", "mime", null, 0, 0,
|
||||
idGenerator.generateNewId();
|
||||
output = extractorOutput.track(idGenerator.getTrackId());
|
||||
output.format(Format.createTextSampleFormat(idGenerator.getFormatId(), "mime", null, 0, 0,
|
||||
language, null, 0));
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
private final ParsableByteArray headerScratchBytes;
|
||||
private final String language;
|
||||
|
||||
private String trackFormatId;
|
||||
private TrackOutput output;
|
||||
|
||||
private int state;
|
||||
@ -84,7 +85,9 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
@Override
|
||||
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator generator) {
|
||||
output = extractorOutput.track(generator.getNextId());
|
||||
generator.generateNewId();
|
||||
trackFormatId = generator.getFormatId();
|
||||
output = extractorOutput.track(generator.getTrackId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -180,8 +183,9 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
headerScratchBits.skipBits(40);
|
||||
isEac3 = headerScratchBits.readBits(5) == 16;
|
||||
headerScratchBits.setPosition(headerScratchBits.getPosition() - 45);
|
||||
format = isEac3 ? Ac3Util.parseEac3SyncframeFormat(headerScratchBits, null, language , null)
|
||||
: Ac3Util.parseAc3SyncframeFormat(headerScratchBits, null, language, null);
|
||||
format = isEac3
|
||||
? Ac3Util.parseEac3SyncframeFormat(headerScratchBits, trackFormatId, language , null)
|
||||
: Ac3Util.parseAc3SyncframeFormat(headerScratchBits, trackFormatId, language, null);
|
||||
output.format(format);
|
||||
}
|
||||
sampleSize = isEac3 ? Ac3Util.parseEAc3SyncframeSize(headerScratchBits.data)
|
||||
|
@ -61,6 +61,7 @@ import java.util.Collections;
|
||||
private final ParsableByteArray id3HeaderBuffer;
|
||||
private final String language;
|
||||
|
||||
private String formatId;
|
||||
private TrackOutput output;
|
||||
private TrackOutput id3Output;
|
||||
|
||||
@ -108,11 +109,14 @@ import java.util.Collections;
|
||||
|
||||
@Override
|
||||
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
|
||||
output = extractorOutput.track(idGenerator.getNextId());
|
||||
idGenerator.generateNewId();
|
||||
formatId = idGenerator.getFormatId();
|
||||
output = extractorOutput.track(idGenerator.getTrackId());
|
||||
if (exposeId3) {
|
||||
id3Output = extractorOutput.track(idGenerator.getNextId());
|
||||
id3Output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, null,
|
||||
Format.NO_VALUE, null));
|
||||
idGenerator.generateNewId();
|
||||
id3Output = extractorOutput.track(idGenerator.getTrackId());
|
||||
id3Output.format(Format.createSampleFormat(idGenerator.getFormatId(),
|
||||
MimeTypes.APPLICATION_ID3, null, Format.NO_VALUE, null));
|
||||
} else {
|
||||
id3Output = new DummyTrackOutput();
|
||||
}
|
||||
@ -300,7 +304,7 @@ import java.util.Collections;
|
||||
Pair<Integer, Integer> audioParams = CodecSpecificDataUtil.parseAacAudioSpecificConfig(
|
||||
audioSpecificConfig);
|
||||
|
||||
Format format = Format.createAudioSampleFormat(null, MimeTypes.AUDIO_AAC, null,
|
||||
Format format = Format.createAudioSampleFormat(formatId, MimeTypes.AUDIO_AAC, null,
|
||||
Format.NO_VALUE, Format.NO_VALUE, audioParams.second, audioParams.first,
|
||||
Collections.singletonList(audioSpecificConfig), null, 0, language);
|
||||
// In this class a sample is an access unit, but the MediaFormat sample rate specifies the
|
||||
|
@ -39,6 +39,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
private final ParsableByteArray headerScratchBytes;
|
||||
private final String language;
|
||||
|
||||
private String formatId;
|
||||
private TrackOutput output;
|
||||
|
||||
private int state;
|
||||
@ -79,7 +80,9 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
@Override
|
||||
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
|
||||
output = extractorOutput.track(idGenerator.getNextId());
|
||||
idGenerator.generateNewId();
|
||||
formatId = idGenerator.getFormatId();
|
||||
output = extractorOutput.track(idGenerator.getTrackId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -165,7 +168,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
private void parseHeader() {
|
||||
byte[] frameData = headerScratchBytes.data;
|
||||
if (format == null) {
|
||||
format = DtsUtil.parseDtsFormat(frameData, null, language, null);
|
||||
format = DtsUtil.parseDtsFormat(frameData, formatId, language, null);
|
||||
output.format(format);
|
||||
}
|
||||
sampleSize = DtsUtil.getDtsFrameSize(frameData);
|
||||
|
@ -37,6 +37,7 @@ import java.util.Collections;
|
||||
private static final int START_EXTENSION = 0xB5;
|
||||
private static final int START_GROUP = 0xB8;
|
||||
|
||||
private String formatId;
|
||||
private TrackOutput output;
|
||||
|
||||
// Maps (frame_rate_code - 1) indices to values, as defined in ITU-T H.262 Table 6-4.
|
||||
@ -78,7 +79,9 @@ import java.util.Collections;
|
||||
|
||||
@Override
|
||||
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
|
||||
output = extractorOutput.track(idGenerator.getNextId());
|
||||
idGenerator.generateNewId();
|
||||
formatId = idGenerator.getFormatId();
|
||||
output = extractorOutput.track(idGenerator.getTrackId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -126,7 +129,7 @@ import java.util.Collections;
|
||||
int bytesAlreadyPassed = lengthToStartCode < 0 ? -lengthToStartCode : 0;
|
||||
if (csdBuffer.onStartCode(startCodeValue, bytesAlreadyPassed)) {
|
||||
// The csd data is complete, so we can decode and output the media format.
|
||||
Pair<Format, Long> result = parseCsdBuffer(csdBuffer);
|
||||
Pair<Format, Long> result = parseCsdBuffer(csdBuffer, formatId);
|
||||
output.format(result.first);
|
||||
frameDurationUs = result.second;
|
||||
hasOutputFormat = true;
|
||||
@ -166,10 +169,11 @@ import java.util.Collections;
|
||||
* Parses the {@link Format} and frame duration from a csd buffer.
|
||||
*
|
||||
* @param csdBuffer The csd buffer.
|
||||
* @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
|
||||
* 0 if the duration could not be determined.
|
||||
*/
|
||||
private static Pair<Format, Long> parseCsdBuffer(CsdBuffer csdBuffer) {
|
||||
private static Pair<Format, Long> parseCsdBuffer(CsdBuffer csdBuffer, String formatId) {
|
||||
byte[] csdData = Arrays.copyOf(csdBuffer.data, csdBuffer.length);
|
||||
|
||||
int firstByte = csdData[4] & 0xFF;
|
||||
@ -195,7 +199,7 @@ import java.util.Collections;
|
||||
break;
|
||||
}
|
||||
|
||||
Format format = Format.createVideoSampleFormat(null, MimeTypes.VIDEO_MPEG2, null,
|
||||
Format format = Format.createVideoSampleFormat(formatId, MimeTypes.VIDEO_MPEG2, null,
|
||||
Format.NO_VALUE, Format.NO_VALUE, width, height, Format.NO_VALUE,
|
||||
Collections.singletonList(csdData), Format.NO_VALUE, pixelWidthHeightRatio, null);
|
||||
|
||||
|
@ -47,6 +47,7 @@ import java.util.List;
|
||||
private long totalBytesWritten;
|
||||
private final boolean[] prefixFlags;
|
||||
|
||||
private String formatId;
|
||||
private TrackOutput output;
|
||||
private SeiReader seiReader;
|
||||
private SampleReader sampleReader;
|
||||
@ -88,9 +89,13 @@ import java.util.List;
|
||||
|
||||
@Override
|
||||
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
|
||||
output = extractorOutput.track(idGenerator.getNextId());
|
||||
idGenerator.generateNewId();
|
||||
formatId = idGenerator.getFormatId();
|
||||
output = extractorOutput.track(idGenerator.getTrackId());
|
||||
sampleReader = new SampleReader(output, allowNonIdrKeyframes, detectAccessUnits);
|
||||
seiReader = new SeiReader(extractorOutput.track(idGenerator.getNextId()));
|
||||
idGenerator.generateNewId();
|
||||
seiReader = new SeiReader(extractorOutput.track(idGenerator.getTrackId()),
|
||||
idGenerator.getFormatId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -175,7 +180,7 @@ import java.util.List;
|
||||
initializationData.add(Arrays.copyOf(pps.nalData, pps.nalLength));
|
||||
NalUnitUtil.SpsData spsData = NalUnitUtil.parseSpsNalUnit(sps.nalData, 3, sps.nalLength);
|
||||
NalUnitUtil.PpsData ppsData = NalUnitUtil.parsePpsNalUnit(pps.nalData, 3, pps.nalLength);
|
||||
output.format(Format.createVideoSampleFormat(null, MimeTypes.VIDEO_H264, null,
|
||||
output.format(Format.createVideoSampleFormat(formatId, MimeTypes.VIDEO_H264, null,
|
||||
Format.NO_VALUE, Format.NO_VALUE, spsData.width, spsData.height, Format.NO_VALUE,
|
||||
initializationData, Format.NO_VALUE, spsData.pixelWidthAspectRatio, null));
|
||||
hasOutputFormat = true;
|
||||
|
@ -44,6 +44,7 @@ import java.util.Collections;
|
||||
private static final int PREFIX_SEI_NUT = 39;
|
||||
private static final int SUFFIX_SEI_NUT = 40;
|
||||
|
||||
private String formatId;
|
||||
private TrackOutput output;
|
||||
private SampleReader sampleReader;
|
||||
private SeiReader seiReader;
|
||||
@ -90,9 +91,13 @@ import java.util.Collections;
|
||||
|
||||
@Override
|
||||
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
|
||||
output = extractorOutput.track(idGenerator.getNextId());
|
||||
idGenerator.generateNewId();
|
||||
formatId = idGenerator.getFormatId();
|
||||
output = extractorOutput.track(idGenerator.getTrackId());
|
||||
sampleReader = new SampleReader(output);
|
||||
seiReader = new SeiReader(extractorOutput.track(idGenerator.getNextId()));
|
||||
idGenerator.generateNewId();
|
||||
seiReader = new SeiReader(extractorOutput.track(idGenerator.getTrackId()),
|
||||
idGenerator.getFormatId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -183,7 +188,7 @@ import java.util.Collections;
|
||||
sps.endNalUnit(discardPadding);
|
||||
pps.endNalUnit(discardPadding);
|
||||
if (vps.isCompleted() && sps.isCompleted() && pps.isCompleted()) {
|
||||
output.format(parseMediaFormat(vps, sps, pps));
|
||||
output.format(parseMediaFormat(formatId, vps, sps, pps));
|
||||
hasOutputFormat = true;
|
||||
}
|
||||
}
|
||||
@ -205,8 +210,8 @@ import java.util.Collections;
|
||||
}
|
||||
}
|
||||
|
||||
private static Format parseMediaFormat(NalUnitTargetBuffer vps, NalUnitTargetBuffer sps,
|
||||
NalUnitTargetBuffer pps) {
|
||||
private static Format parseMediaFormat(String formatId, NalUnitTargetBuffer vps,
|
||||
NalUnitTargetBuffer sps, NalUnitTargetBuffer pps) {
|
||||
// Build codec-specific data.
|
||||
byte[] csd = new byte[vps.nalLength + sps.nalLength + pps.nalLength];
|
||||
System.arraycopy(vps.nalData, 0, csd, 0, vps.nalLength);
|
||||
@ -311,7 +316,7 @@ import java.util.Collections;
|
||||
}
|
||||
}
|
||||
|
||||
return Format.createVideoSampleFormat(null, MimeTypes.VIDEO_H265, null, Format.NO_VALUE,
|
||||
return Format.createVideoSampleFormat(formatId, MimeTypes.VIDEO_H265, null, Format.NO_VALUE,
|
||||
Format.NO_VALUE, picWidthInLumaSamples, picHeightInLumaSamples, Format.NO_VALUE,
|
||||
Collections.singletonList(csd), Format.NO_VALUE, pixelWidthHeightRatio, null);
|
||||
}
|
||||
|
@ -56,9 +56,10 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
@Override
|
||||
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
|
||||
output = extractorOutput.track(idGenerator.getNextId());
|
||||
output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_ID3, null, Format.NO_VALUE,
|
||||
null));
|
||||
idGenerator.generateNewId();
|
||||
output = extractorOutput.track(idGenerator.getTrackId());
|
||||
output.format(Format.createSampleFormat(idGenerator.getFormatId(), MimeTypes.APPLICATION_ID3,
|
||||
null, Format.NO_VALUE, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -38,6 +38,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
private final MpegAudioHeader header;
|
||||
private final String language;
|
||||
|
||||
private String formatId;
|
||||
private TrackOutput output;
|
||||
|
||||
private int state;
|
||||
@ -76,7 +77,9 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
@Override
|
||||
public void createTracks(ExtractorOutput extractorOutput, TrackIdGenerator idGenerator) {
|
||||
output = extractorOutput.track(idGenerator.getNextId());
|
||||
idGenerator.generateNewId();
|
||||
formatId = idGenerator.getFormatId();
|
||||
output = extractorOutput.track(idGenerator.getTrackId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -176,9 +179,9 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
frameSize = header.frameSize;
|
||||
if (!hasOutputFormat) {
|
||||
frameDurationUs = (C.MICROS_PER_SECOND * header.samplesPerFrame) / header.sampleRate;
|
||||
Format format = Format.createAudioSampleFormat(null, header.mimeType, null, Format.NO_VALUE,
|
||||
MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate, null, null, 0,
|
||||
language);
|
||||
Format format = Format.createAudioSampleFormat(formatId, header.mimeType, null,
|
||||
Format.NO_VALUE, MpegAudioHeader.MAX_FRAME_SIZE_BYTES, header.channels, header.sampleRate,
|
||||
null, null, 0, language);
|
||||
output.format(format);
|
||||
hasOutputFormat = true;
|
||||
}
|
||||
|
@ -28,9 +28,9 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
private final TrackOutput output;
|
||||
|
||||
public SeiReader(TrackOutput output) {
|
||||
public SeiReader(TrackOutput output, String formatId) {
|
||||
this.output = output;
|
||||
output.format(Format.createTextSampleFormat(null, MimeTypes.APPLICATION_CEA608, null,
|
||||
output.format(Format.createTextSampleFormat(formatId, MimeTypes.APPLICATION_CEA608, null,
|
||||
Format.NO_VALUE, 0, null, null));
|
||||
}
|
||||
|
||||
|
@ -36,9 +36,10 @@ public final class SpliceInfoSectionReader implements SectionPayloadReader {
|
||||
public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
|
||||
TsPayloadReader.TrackIdGenerator idGenerator) {
|
||||
this.timestampAdjuster = timestampAdjuster;
|
||||
output = extractorOutput.track(idGenerator.getNextId());
|
||||
output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_SCTE35, null,
|
||||
Format.NO_VALUE, null));
|
||||
idGenerator.generateNewId();
|
||||
output = extractorOutput.track(idGenerator.getTrackId());
|
||||
output.format(Format.createSampleFormat(idGenerator.getFormatId(), MimeTypes.APPLICATION_SCTE35,
|
||||
null, Format.NO_VALUE, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,7 +34,10 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import com.google.android.exoplayer2.util.TimestampAdjuster;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Facilitates the extraction of data from the MPEG-2 TS container format.
|
||||
@ -79,7 +82,7 @@ public final class TsExtractor implements Extractor {
|
||||
private static final int BUFFER_SIZE = TS_PACKET_SIZE * BUFFER_PACKET_COUNT;
|
||||
|
||||
private final boolean hlsMode;
|
||||
private final TimestampAdjuster timestampAdjuster;
|
||||
private final List<TimestampAdjuster> timestampAdjusters;
|
||||
private final ParsableByteArray tsPacketBuffer;
|
||||
private final ParsableBitArray tsScratch;
|
||||
private final SparseIntArray continuityCounters;
|
||||
@ -89,18 +92,12 @@ public final class TsExtractor implements Extractor {
|
||||
|
||||
// Accessed only by the loading thread.
|
||||
private ExtractorOutput output;
|
||||
private int remainingPmts;
|
||||
private boolean tracksEnded;
|
||||
private TsPayloadReader id3Reader;
|
||||
|
||||
public TsExtractor() {
|
||||
this(new TimestampAdjuster(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps.
|
||||
*/
|
||||
public TsExtractor(TimestampAdjuster timestampAdjuster) {
|
||||
this(timestampAdjuster, new DefaultTsPayloadReaderFactory(), false);
|
||||
this(new TimestampAdjuster(0), new DefaultTsPayloadReaderFactory(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,7 +108,12 @@ public final class TsExtractor implements Extractor {
|
||||
*/
|
||||
public TsExtractor(TimestampAdjuster timestampAdjuster,
|
||||
TsPayloadReader.Factory payloadReaderFactory, boolean hlsMode) {
|
||||
this.timestampAdjuster = timestampAdjuster;
|
||||
if (hlsMode) {
|
||||
timestampAdjusters = Collections.singletonList(timestampAdjuster);
|
||||
} else {
|
||||
timestampAdjusters = new ArrayList<>();
|
||||
timestampAdjusters.add(timestampAdjuster);
|
||||
}
|
||||
this.payloadReaderFactory = Assertions.checkNotNull(payloadReaderFactory);
|
||||
this.hlsMode = hlsMode;
|
||||
tsPacketBuffer = new ParsableByteArray(BUFFER_SIZE);
|
||||
@ -150,7 +152,10 @@ public final class TsExtractor implements Extractor {
|
||||
|
||||
@Override
|
||||
public void seek(long position, long timeUs) {
|
||||
timestampAdjuster.reset();
|
||||
int timestampAdjustersCount = timestampAdjusters.size();
|
||||
for (int i = 0; i < timestampAdjustersCount; i++) {
|
||||
timestampAdjusters.get(i).reset();
|
||||
}
|
||||
tsPacketBuffer.reset();
|
||||
continuityCounters.clear();
|
||||
// Elementary stream readers' state should be cleared to get consistent behaviours when seeking.
|
||||
@ -307,8 +312,12 @@ public final class TsExtractor implements Extractor {
|
||||
} else {
|
||||
int pid = patScratch.readBits(13);
|
||||
tsPayloadReaders.put(pid, new SectionReader(new PmtReader(pid)));
|
||||
remainingPmts++;
|
||||
}
|
||||
}
|
||||
if (!hlsMode) {
|
||||
tsPayloadReaders.remove(TS_PAT_PID);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -345,10 +354,21 @@ public final class TsExtractor implements Extractor {
|
||||
// See ISO/IEC 13818-1, section 2.4.4.4 for more information on table id assignment.
|
||||
return;
|
||||
}
|
||||
// section_syntax_indicator(1), '0'(1), reserved(2), section_length(12), program_number (16),
|
||||
// reserved (2), version_number (5), current_next_indicator (1), // section_number (8),
|
||||
// TimestampAdjuster assignment.
|
||||
TimestampAdjuster timestampAdjuster;
|
||||
if (hlsMode || remainingPmts == 1) {
|
||||
timestampAdjuster = timestampAdjusters.get(0);
|
||||
} else {
|
||||
timestampAdjuster = new TimestampAdjuster(timestampAdjusters.get(0).firstSampleTimestampUs);
|
||||
timestampAdjusters.add(timestampAdjuster);
|
||||
}
|
||||
|
||||
// section_syntax_indicator(1), '0'(1), reserved(2), section_length(12)
|
||||
sectionData.skipBytes(2);
|
||||
int programNumber = sectionData.readUnsignedShort();
|
||||
// reserved (2), version_number (5), current_next_indicator (1), section_number (8),
|
||||
// last_section_number (8), reserved (3), PCR_PID (13)
|
||||
sectionData.skipBytes(9);
|
||||
sectionData.skipBytes(5);
|
||||
|
||||
// Read program_info_length.
|
||||
sectionData.readBytes(pmtScratch, 2);
|
||||
@ -364,7 +384,7 @@ public final class TsExtractor implements Extractor {
|
||||
EsInfo dummyEsInfo = new EsInfo(TS_STREAM_TYPE_ID3, null, new byte[0]);
|
||||
id3Reader = payloadReaderFactory.createPayloadReader(TS_STREAM_TYPE_ID3, dummyEsInfo);
|
||||
id3Reader.init(timestampAdjuster, output,
|
||||
new TrackIdGenerator(TS_STREAM_TYPE_ID3, MAX_PID_PLUS_ONE));
|
||||
new TrackIdGenerator(programNumber, TS_STREAM_TYPE_ID3, MAX_PID_PLUS_ONE));
|
||||
}
|
||||
|
||||
int remainingEntriesLength = sectionData.bytesLeft();
|
||||
@ -393,7 +413,8 @@ public final class TsExtractor implements Extractor {
|
||||
} else {
|
||||
reader = payloadReaderFactory.createPayloadReader(streamType, esInfo);
|
||||
if (reader != null) {
|
||||
reader.init(timestampAdjuster, output, new TrackIdGenerator(trackId, MAX_PID_PLUS_ONE));
|
||||
reader.init(timestampAdjuster, output,
|
||||
new TrackIdGenerator(programNumber, trackId, MAX_PID_PLUS_ONE));
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,14 +425,18 @@ public final class TsExtractor implements Extractor {
|
||||
if (hlsMode) {
|
||||
if (!tracksEnded) {
|
||||
output.endTracks();
|
||||
remainingPmts = 0;
|
||||
tracksEnded = true;
|
||||
}
|
||||
} else {
|
||||
tsPayloadReaders.remove(TS_PAT_PID);
|
||||
tsPayloadReaders.remove(pid);
|
||||
remainingPmts--;
|
||||
if (remainingPmts == 0) {
|
||||
output.endTracks();
|
||||
}
|
||||
tracksEnded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stream info read from the available descriptors. Sets {@code data}'s position to
|
||||
|
@ -81,17 +81,63 @@ public interface TsPayloadReader {
|
||||
*/
|
||||
final class TrackIdGenerator {
|
||||
|
||||
private final int firstId;
|
||||
private final int idIncrement;
|
||||
private int generatedIdCount;
|
||||
private static final int ID_UNSET = Integer.MIN_VALUE;
|
||||
|
||||
public TrackIdGenerator(int firstId, int idIncrement) {
|
||||
this.firstId = firstId;
|
||||
this.idIncrement = idIncrement;
|
||||
private final String formatIdPrefix;
|
||||
private final int firstTrackId;
|
||||
private final int trackIdIncrement;
|
||||
private int trackId;
|
||||
private String formatId;
|
||||
|
||||
public TrackIdGenerator(int firstTrackId, int trackIdIncrement) {
|
||||
this(ID_UNSET, firstTrackId, trackIdIncrement);
|
||||
}
|
||||
|
||||
public int getNextId() {
|
||||
return firstId + idIncrement * generatedIdCount++;
|
||||
public TrackIdGenerator(int programNumber, int firstTrackId, int trackIdIncrement) {
|
||||
this.formatIdPrefix = programNumber != ID_UNSET ? programNumber + "/" : "";
|
||||
this.firstTrackId = firstTrackId;
|
||||
this.trackIdIncrement = trackIdIncrement;
|
||||
trackId = ID_UNSET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new set of track and track format ids. Must be called before {@code get*}
|
||||
* methods.
|
||||
*/
|
||||
public void generateNewId() {
|
||||
trackId = trackId == ID_UNSET ? firstTrackId : trackId + trackIdIncrement;
|
||||
formatId = formatIdPrefix + trackId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last generated track id. Must be called after the first {@link #generateNewId()}
|
||||
* call.
|
||||
*
|
||||
* @return The last generated track id.
|
||||
*/
|
||||
public int getTrackId() {
|
||||
maybeThrowUninitializedError();
|
||||
return trackId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last generated format id, with the format {@code "programNumber/trackId"}. If no
|
||||
* {@code programNumber} was provided, the {@code trackId} alone is used as format id. Must be
|
||||
* called after the first {@link #generateNewId()} call.
|
||||
*
|
||||
* @return The last generated format id, with the format {@code "programNumber/trackId"}. If no
|
||||
* {@code programNumber} was provided, the {@code trackId} alone is used as
|
||||
* format id.
|
||||
*/
|
||||
public String getFormatId() {
|
||||
maybeThrowUninitializedError();
|
||||
return formatId;
|
||||
}
|
||||
|
||||
private void maybeThrowUninitializedError() {
|
||||
if (trackId == ID_UNSET) {
|
||||
throw new IllegalStateException("generateNewId() must be called before retrieving ids.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ public final class TimestampAdjuster {
|
||||
*/
|
||||
private static final long MAX_PTS_PLUS_ONE = 0x200000000L;
|
||||
|
||||
private final long firstSampleTimestampUs;
|
||||
public final long firstSampleTimestampUs;
|
||||
|
||||
private long timestampOffsetUs;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user