blob: 4a73799c07c65b9b47634162ce2735f05b686b28 [file] [log] [blame]
/*
* 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 static java.lang.Math.max;
import static java.lang.Math.min;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.android.exoplayer2.util.Util;
/**
* Reads section data packets and feeds the whole sections to a given {@link SectionPayloadReader}.
* Useful information on PSI sections can be found in ISO/IEC 13818-1, section 2.4.4.
*/
public final class SectionReader implements TsPayloadReader {
private static final int SECTION_HEADER_LENGTH = 3;
private static final int DEFAULT_SECTION_BUFFER_LENGTH = 32;
private static final int MAX_SECTION_LENGTH = 4098;
private final SectionPayloadReader reader;
private final ParsableByteArray sectionData;
private int totalSectionLength;
private int bytesRead;
private boolean sectionSyntaxIndicator;
private boolean waitingForPayloadStart;
public SectionReader(SectionPayloadReader reader) {
this.reader = reader;
sectionData = new ParsableByteArray(DEFAULT_SECTION_BUFFER_LENGTH);
}
@Override
public void init(
TimestampAdjuster timestampAdjuster,
ExtractorOutput extractorOutput,
TrackIdGenerator idGenerator) {
reader.init(timestampAdjuster, extractorOutput, idGenerator);
waitingForPayloadStart = true;
}
@Override
public void seek() {
waitingForPayloadStart = true;
}
@Override
public void consume(ParsableByteArray data, @Flags int flags) {
boolean payloadUnitStartIndicator = (flags & FLAG_PAYLOAD_UNIT_START_INDICATOR) != 0;
int payloadStartPosition = C.POSITION_UNSET;
if (payloadUnitStartIndicator) {
int payloadStartOffset = data.readUnsignedByte();
payloadStartPosition = data.getPosition() + payloadStartOffset;
}
if (waitingForPayloadStart) {
if (!payloadUnitStartIndicator) {
return;
}
waitingForPayloadStart = false;
data.setPosition(payloadStartPosition);
bytesRead = 0;
}
while (data.bytesLeft() > 0) {
if (bytesRead < SECTION_HEADER_LENGTH) {
// Note: see ISO/IEC 13818-1, section 2.4.4.3 for detailed information on the format of
// the header.
if (bytesRead == 0) {
int tableId = data.readUnsignedByte();
data.setPosition(data.getPosition() - 1);
if (tableId == 0xFF /* forbidden value */) {
// No more sections in this ts packet.
waitingForPayloadStart = true;
return;
}
}
int headerBytesToRead = min(data.bytesLeft(), SECTION_HEADER_LENGTH - bytesRead);
// sectionData is guaranteed to have enough space because it's initialized with a 32-element
// backing array and headerBytesToRead is at most 3.
data.readBytes(sectionData.getData(), bytesRead, headerBytesToRead);
bytesRead += headerBytesToRead;
if (bytesRead == SECTION_HEADER_LENGTH) {
sectionData.setPosition(0);
sectionData.setLimit(SECTION_HEADER_LENGTH);
sectionData.skipBytes(1); // Skip table id (8).
int secondHeaderByte = sectionData.readUnsignedByte();
int thirdHeaderByte = sectionData.readUnsignedByte();
sectionSyntaxIndicator = (secondHeaderByte & 0x80) != 0;
totalSectionLength =
(((secondHeaderByte & 0x0F) << 8) | thirdHeaderByte) + SECTION_HEADER_LENGTH;
if (sectionData.capacity() < totalSectionLength) {
// Ensure there is enough space to keep the whole section.
int limit =
min(MAX_SECTION_LENGTH, max(totalSectionLength, sectionData.capacity() * 2));
sectionData.ensureCapacity(limit);
}
}
} else {
// Reading the body.
int bodyBytesToRead = min(data.bytesLeft(), totalSectionLength - bytesRead);
// sectionData has been sized large enough for totalSectionLength when reading the header.
data.readBytes(sectionData.getData(), bytesRead, bodyBytesToRead);
bytesRead += bodyBytesToRead;
if (bytesRead == totalSectionLength) {
if (sectionSyntaxIndicator) {
// This section has common syntax as defined in ISO/IEC 13818-1, section 2.4.4.11.
if (Util.crc32(sectionData.getData(), 0, totalSectionLength, 0xFFFFFFFF) != 0) {
// The CRC is invalid so discard the section.
waitingForPayloadStart = true;
return;
}
sectionData.setLimit(totalSectionLength - 4); // Exclude the CRC_32 field.
} else {
// This is a private section with private defined syntax.
sectionData.setLimit(totalSectionLength);
}
sectionData.setPosition(0);
reader.consume(sectionData);
bytesRead = 0;
}
}
}
}
}