blob: f26d446b80030c0e8fec13d82d5f43aa26a4a943 [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.metadata.scte35;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataInputBuffer;
import com.google.android.exoplayer2.metadata.SimpleMetadataDecoder;
import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.TimestampAdjuster;
import java.nio.ByteBuffer;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** Decodes splice info sections and produces splice commands. */
public final class SpliceInfoDecoder extends SimpleMetadataDecoder {
private static final int TYPE_SPLICE_NULL = 0x00;
private static final int TYPE_SPLICE_SCHEDULE = 0x04;
private static final int TYPE_SPLICE_INSERT = 0x05;
private static final int TYPE_TIME_SIGNAL = 0x06;
private static final int TYPE_PRIVATE_COMMAND = 0xFF;
private final ParsableByteArray sectionData;
private final ParsableBitArray sectionHeader;
private @MonotonicNonNull TimestampAdjuster timestampAdjuster;
public SpliceInfoDecoder() {
sectionData = new ParsableByteArray();
sectionHeader = new ParsableBitArray();
}
@Override
@SuppressWarnings("ByteBufferBackingArray") // Buffer validated by SimpleMetadataDecoder.decode
protected Metadata decode(MetadataInputBuffer inputBuffer, ByteBuffer buffer) {
// Internal timestamps adjustment.
if (timestampAdjuster == null
|| inputBuffer.subsampleOffsetUs != timestampAdjuster.getTimestampOffsetUs()) {
timestampAdjuster = new TimestampAdjuster(inputBuffer.timeUs);
timestampAdjuster.adjustSampleTimestamp(inputBuffer.timeUs - inputBuffer.subsampleOffsetUs);
}
byte[] data = buffer.array();
int size = buffer.limit();
sectionData.reset(data, size);
sectionHeader.reset(data, size);
// table_id(8), section_syntax_indicator(1), private_indicator(1), reserved(2),
// section_length(12), protocol_version(8), encrypted_packet(1), encryption_algorithm(6).
sectionHeader.skipBits(39);
long ptsAdjustment = sectionHeader.readBits(1);
ptsAdjustment = (ptsAdjustment << 32) | sectionHeader.readBits(32);
// cw_index(8), tier(12).
sectionHeader.skipBits(20);
int spliceCommandLength = sectionHeader.readBits(12);
int spliceCommandType = sectionHeader.readBits(8);
@Nullable SpliceCommand command = null;
// Go to the start of the command by skipping all fields up to command_type.
sectionData.skipBytes(14);
switch (spliceCommandType) {
case TYPE_SPLICE_NULL:
command = new SpliceNullCommand();
break;
case TYPE_SPLICE_SCHEDULE:
command = SpliceScheduleCommand.parseFromSection(sectionData);
break;
case TYPE_SPLICE_INSERT:
command =
SpliceInsertCommand.parseFromSection(sectionData, ptsAdjustment, timestampAdjuster);
break;
case TYPE_TIME_SIGNAL:
command = TimeSignalCommand.parseFromSection(sectionData, ptsAdjustment, timestampAdjuster);
break;
case TYPE_PRIVATE_COMMAND:
command = PrivateCommand.parseFromSection(sectionData, spliceCommandLength, ptsAdjustment);
break;
default:
// Do nothing.
break;
}
return command == null ? new Metadata() : new Metadata(command);
}
}