Create the SectionPayloadReader interface

SectionPayloadReaders are provided to a SectionReader to
get whole sections(crc checked). This allows the injection
of custom section readers. E.G: SCTE35 messages.

Issue:#726

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=136816612
This commit is contained in:
aquilescanta 2016-10-21 03:46:41 -07:00 committed by Oliver Woodman
parent a0fe258e8d
commit eeb37d73e7
5 changed files with 142 additions and 113 deletions

View File

@ -78,8 +78,7 @@ public final class PesReader implements TsPayloadReader {
}
@Override
public final void consume(ParsableByteArray data, boolean payloadUnitStartIndicator,
ExtractorOutput output) {
public final void consume(ParsableByteArray data, boolean payloadUnitStartIndicator) {
if (payloadUnitStartIndicator) {
switch (state) {
case STATE_FINDING_HEADER:

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2016 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.
*/
package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.util.ParsableByteArray;
/**
* Reads section data.
*/
public interface SectionPayloadReader {
/**
* Called by a {@link SectionReader} when a full section is received.
*
* @param sectionData The data belonging to a section, including the section header but excluding
* the CRC_32 field.
*/
void consume(ParsableByteArray sectionData);
}

View File

@ -0,0 +1,89 @@
/*
* Copyright (C) 2016 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.
*/
package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.TimestampAdjuster;
import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
/**
* Reads section data packets and feeds the whole sections to a given {@link SectionPayloadReader}.
*/
public final class SectionReader implements TsPayloadReader {
private static final int SECTION_HEADER_LENGTH = 3;
private final ParsableByteArray sectionData;
private final ParsableBitArray headerScratch;
private final SectionPayloadReader reader;
private int sectionLength;
private int sectionBytesRead;
public SectionReader(SectionPayloadReader reader) {
this.reader = reader;
sectionData = new ParsableByteArray();
headerScratch = new ParsableBitArray(new byte[SECTION_HEADER_LENGTH]);
}
@Override
public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator) {
// TODO: Injectable section readers might want to generate metadata tracks.
// Do nothing.
}
@Override
public void seek() {
// Do nothing.
}
@Override
public void consume(ParsableByteArray data, boolean payloadUnitStartIndicator) {
// Skip pointer.
if (payloadUnitStartIndicator) {
int pointerField = data.readUnsignedByte();
data.skipBytes(pointerField);
// Note: see ISO/IEC 13818-1, section 2.4.4.3 for detailed information on the format of
// the header.
data.readBytes(headerScratch, SECTION_HEADER_LENGTH);
data.setPosition(data.getPosition() - SECTION_HEADER_LENGTH);
headerScratch.skipBits(12); // table_id (8), section_syntax_indicator (1), 0 (1), reserved (2)
sectionLength = headerScratch.readBits(12) + SECTION_HEADER_LENGTH;
sectionBytesRead = 0;
sectionData.reset(sectionLength);
}
int bytesToRead = Math.min(data.bytesLeft(), sectionLength - sectionBytesRead);
data.readBytes(sectionData.data, sectionBytesRead, bytesToRead);
sectionBytesRead += bytesToRead;
if (sectionBytesRead < sectionLength) {
// Not yet fully read.
return;
}
if (Util.crc(sectionData.data, 0, sectionLength, 0xFFFFFFFF) != 0) {
// CRC Invalid. The section gets discarded.
return;
}
sectionData.setLimit(sectionData.limit() - 4); // Exclude the CRC_32 field.
reader.consume(sectionData);
}
}

View File

@ -27,6 +27,8 @@ import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TimestampAdjuster;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.TsPayloadReader.EsInfo;
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.ParsableByteArray;
@ -236,7 +238,7 @@ public final class TsExtractor implements Extractor {
payloadReader.seek();
}
tsPacketBuffer.setLimit(endOfPacket);
payloadReader.consume(tsPacketBuffer, payloadUnitStartIndicator, output);
payloadReader.consume(tsPacketBuffer, payloadUnitStartIndicator);
Assertions.checkState(tsPacketBuffer.getPosition() <= endOfPacket);
tsPacketBuffer.setLimit(limit);
}
@ -251,75 +253,29 @@ public final class TsExtractor implements Extractor {
private void resetPayloadReaders() {
trackIds.clear();
tsPayloadReaders.clear();
tsPayloadReaders.put(TS_PAT_PID, new PatReader());
tsPayloadReaders.put(TS_PAT_PID, new SectionReader(new PatReader()));
id3Reader = null;
}
/**
* Parses Program Association Table data.
*/
private class PatReader implements TsPayloadReader {
private class PatReader implements SectionPayloadReader {
private final ParsableByteArray sectionData;
private final ParsableBitArray patScratch;
private int sectionLength;
private int sectionBytesRead;
private int crc;
public PatReader() {
sectionData = new ParsableByteArray();
patScratch = new ParsableBitArray(new byte[4]);
}
@Override
public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator) {
// Do nothing.
}
@Override
public void seek() {
// Do nothing.
}
@Override
public void consume(ParsableByteArray data, boolean payloadUnitStartIndicator,
ExtractorOutput output) {
// Skip pointer.
if (payloadUnitStartIndicator) {
int pointerField = data.readUnsignedByte();
data.skipBytes(pointerField);
// Note: see ISO/IEC 13818-1, section 2.4.4.3 for detailed information on the format of
// the header.
data.readBytes(patScratch, 3);
patScratch.skipBits(12); // table_id (8), section_syntax_indicator (1), 0 (1), reserved (2)
sectionLength = patScratch.readBits(12);
sectionBytesRead = 0;
crc = Util.crc(patScratch.data, 0, 3, 0xFFFFFFFF);
sectionData.reset(sectionLength);
}
int bytesToRead = Math.min(data.bytesLeft(), sectionLength - sectionBytesRead);
data.readBytes(sectionData.data, sectionBytesRead, bytesToRead);
sectionBytesRead += bytesToRead;
if (sectionBytesRead < sectionLength) {
// Not yet fully read.
return;
}
if (Util.crc(sectionData.data, 0, sectionLength, crc) != 0) {
// CRC Invalid. The section gets discarded.
return;
}
public void consume(ParsableByteArray sectionData) {
// table_id(8), section_syntax_indicator(1), '0'(1), reserved(2), section_length(12),
// transport_stream_id (16), reserved (2), version_number (5), current_next_indicator (1),
// section_number (8), last_section_number (8)
sectionData.skipBytes(5);
sectionData.skipBytes(8);
int programCount = (sectionLength - 9) / 4;
int programCount = sectionData.bytesLeft() / 4;
for (int i = 0; i < programCount; i++) {
sectionData.readBytes(patScratch, 4);
int programNumber = patScratch.readBits(16);
@ -328,7 +284,7 @@ public final class TsExtractor implements Extractor {
patScratch.skipBits(13); // network_PID (13)
} else {
int pid = patScratch.readBits(13);
tsPayloadReaders.put(pid, new PmtReader(pid));
tsPayloadReaders.put(pid, new SectionReader(new PmtReader(pid)));
}
}
}
@ -338,7 +294,7 @@ public final class TsExtractor implements Extractor {
/**
* Parses Program Map Table.
*/
private class PmtReader implements TsPayloadReader {
private class PmtReader implements SectionPayloadReader {
private static final int TS_PMT_DESC_REGISTRATION = 0x05;
private static final int TS_PMT_DESC_ISO639_LANG = 0x0A;
@ -347,66 +303,20 @@ public final class TsExtractor implements Extractor {
private static final int TS_PMT_DESC_DTS = 0x7B;
private final ParsableBitArray pmtScratch;
private final ParsableByteArray sectionData;
private final int pid;
private int sectionLength;
private int sectionBytesRead;
private int crc;
public PmtReader(int pid) {
pmtScratch = new ParsableBitArray(new byte[5]);
sectionData = new ParsableByteArray();
this.pid = pid;
}
@Override
public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator) {
// Do nothing.
}
@Override
public void seek() {
// Do nothing.
}
@Override
public void consume(ParsableByteArray data, boolean payloadUnitStartIndicator,
ExtractorOutput output) {
if (payloadUnitStartIndicator) {
// Skip pointer.
int pointerField = data.readUnsignedByte();
data.skipBytes(pointerField);
// Note: see ISO/IEC 13818-1, section 2.4.4.8 for detailed information on the format of
// the header.
data.readBytes(pmtScratch, 3);
pmtScratch.skipBits(12); // table_id (8), section_syntax_indicator (1), 0 (1), reserved (2)
sectionLength = pmtScratch.readBits(12);
sectionBytesRead = 0;
crc = Util.crc(pmtScratch.data, 0, 3, 0xFFFFFFFF);
sectionData.reset(sectionLength);
}
int bytesToRead = Math.min(data.bytesLeft(), sectionLength - sectionBytesRead);
data.readBytes(sectionData.data, sectionBytesRead, bytesToRead);
sectionBytesRead += bytesToRead;
if (sectionBytesRead < sectionLength) {
// Not yet fully read.
return;
}
if (Util.crc(sectionData.data, 0, sectionLength, crc) != 0) {
// CRC Invalid. The section gets discarded.
return;
}
public void consume(ParsableByteArray sectionData) {
// table_id(8), 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), last_section_number (8), reserved (3), PCR_PID (13)
// Skip the rest of the PMT header.
sectionData.skipBytes(7);
sectionData.skipBytes(10);
// Read program_info_length.
sectionData.readBytes(pmtScratch, 2);
@ -425,8 +335,7 @@ public final class TsExtractor implements Extractor {
new TrackIdGenerator(TS_STREAM_TYPE_ID3, MAX_PID_PLUS_ONE));
}
int remainingEntriesLength = sectionLength - 9 /* Length of fields before descriptors */
- programInfoLength - 4 /* CRC length */;
int remainingEntriesLength = sectionData.bytesLeft();
while (remainingEntriesLength > 0) {
sectionData.readBytes(pmtScratch, 5);
int streamType = pmtScratch.readBits(8);
@ -513,7 +422,7 @@ public final class TsExtractor implements Extractor {
}
data.setPosition(descriptorsEndPosition);
return new EsInfo(streamType, language,
Arrays.copyOfRange(sectionData.data, descriptorsStartPosition, descriptorsEndPosition));
Arrays.copyOfRange(data.data, descriptorsStartPosition, descriptorsEndPosition));
}
}

View File

@ -100,8 +100,8 @@ public interface TsPayloadReader {
* Notifies the reader that a seek has occurred.
* <p>
* Following a call to this method, the data passed to the next invocation of
* {@link #consume(ParsableByteArray, boolean, ExtractorOutput)} will not be a continuation of
* the data that was previously passed. Hence the reader should reset any internal state.
* {@link #consume(ParsableByteArray, boolean)} will not be a continuation of the data that was
* previously passed. Hence the reader should reset any internal state.
*/
void seek();
@ -110,8 +110,7 @@ public interface TsPayloadReader {
*
* @param data The TS packet. The position will be set to the start of the payload.
* @param payloadUnitStartIndicator Whether payloadUnitStartIndicator was set on the TS packet.
* @param output The output to which parsed data should be written.
*/
void consume(ParsableByteArray data, boolean payloadUnitStartIndicator, ExtractorOutput output);
void consume(ParsableByteArray data, boolean payloadUnitStartIndicator);
}