mirror of
https://github.com/samsonjs/media.git
synced 2026-03-27 09:45:47 +00:00
Add support for splice info section reading in TS
This CL adds a SpliceInfoDecoder for the most common SCTE35 commands for splcing. So far, it only includes TransportStreams, but porting it to HLS and DASH should be fairly easy. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=138389807
This commit is contained in:
parent
4cd8c77053
commit
0354ef24d4
11 changed files with 793 additions and 0 deletions
|
|
@ -78,6 +78,8 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
|
|||
(flags & FLAG_DETECT_ACCESS_UNITS) != 0));
|
||||
case TsExtractor.TS_STREAM_TYPE_H265:
|
||||
return new PesReader(new H265Reader());
|
||||
case TsExtractor.TS_STREAM_TYPE_SPLICE_INFO:
|
||||
return new SectionReader(new SpliceInfoSectionReader());
|
||||
case TsExtractor.TS_STREAM_TYPE_ID3:
|
||||
return new PesReader(new Id3Reader());
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.C;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.extractor.ExtractorOutput;
|
||||
import com.google.android.exoplayer2.extractor.TimestampAdjuster;
|
||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
/**
|
||||
* Parses splice info sections as defined by SCTE35.
|
||||
*/
|
||||
public final class SpliceInfoSectionReader implements SectionPayloadReader {
|
||||
|
||||
private TrackOutput output;
|
||||
|
||||
@Override
|
||||
public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
|
||||
TsPayloadReader.TrackIdGenerator idGenerator) {
|
||||
output = extractorOutput.track(idGenerator.getNextId());
|
||||
output.format(Format.createSampleFormat(null, MimeTypes.APPLICATION_SCTE35, null,
|
||||
Format.NO_VALUE, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(ParsableByteArray sectionData) {
|
||||
int sampleSize = sectionData.bytesLeft();
|
||||
output.sampleData(sectionData, sampleSize);
|
||||
output.sampleMetadata(0, C.BUFFER_FLAG_KEY_FRAME, sampleSize, 0, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -64,6 +64,7 @@ public final class TsExtractor implements Extractor {
|
|||
public static final int TS_STREAM_TYPE_H264 = 0x1B;
|
||||
public static final int TS_STREAM_TYPE_H265 = 0x24;
|
||||
public static final int TS_STREAM_TYPE_ID3 = 0x15;
|
||||
public static final int TS_STREAM_TYPE_SPLICE_INFO = 0x86;
|
||||
|
||||
private static final int TS_PACKET_SIZE = 188;
|
||||
private static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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 android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
/**
|
||||
* Represents a private command as defined in SCTE35, Section 9.3.6.
|
||||
*/
|
||||
public final class PrivateCommand extends SpliceCommand {
|
||||
|
||||
public final long ptsAdjustment;
|
||||
public final long identifier;
|
||||
|
||||
public final byte[] commandBytes;
|
||||
|
||||
private PrivateCommand(long identifier, byte[] commandBytes, long ptsAdjustment) {
|
||||
this.ptsAdjustment = ptsAdjustment;
|
||||
this.identifier = identifier;
|
||||
this.commandBytes = commandBytes;
|
||||
}
|
||||
|
||||
private PrivateCommand(Parcel in) {
|
||||
ptsAdjustment = in.readLong();
|
||||
identifier = in.readLong();
|
||||
commandBytes = new byte[in.readInt()];
|
||||
in.readByteArray(commandBytes);
|
||||
}
|
||||
|
||||
/* package */ static PrivateCommand parseFromSection(ParsableByteArray sectionData,
|
||||
int commandLength, long ptsAdjustment) {
|
||||
long identifier = sectionData.readUnsignedInt();
|
||||
byte[] privateBytes = new byte[commandLength - 4 /* identifier size */];
|
||||
sectionData.readBytes(privateBytes, 0, privateBytes.length);
|
||||
return new PrivateCommand(identifier, privateBytes, ptsAdjustment);
|
||||
}
|
||||
|
||||
// Parcelable implementation.
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(ptsAdjustment);
|
||||
dest.writeLong(identifier);
|
||||
dest.writeInt(commandBytes.length);
|
||||
dest.writeByteArray(commandBytes);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<PrivateCommand> CREATOR =
|
||||
new Parcelable.Creator<PrivateCommand>() {
|
||||
|
||||
@Override
|
||||
public PrivateCommand createFromParcel(Parcel in) {
|
||||
return new PrivateCommand(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrivateCommand[] newArray(int size) {
|
||||
return new PrivateCommand[size];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 com.google.android.exoplayer2.metadata.Metadata;
|
||||
|
||||
/**
|
||||
* Superclass for SCTE35 splice commands.
|
||||
*/
|
||||
public abstract class SpliceCommand implements Metadata.Entry {
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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 android.text.TextUtils;
|
||||
import com.google.android.exoplayer2.metadata.Metadata;
|
||||
import com.google.android.exoplayer2.metadata.MetadataDecoder;
|
||||
import com.google.android.exoplayer2.metadata.MetadataDecoderException;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.ParsableBitArray;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
/**
|
||||
* Decodes splice info sections and produces splice commands.
|
||||
*/
|
||||
public final class SpliceInfoDecoder implements MetadataDecoder {
|
||||
|
||||
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;
|
||||
|
||||
public SpliceInfoDecoder() {
|
||||
sectionData = new ParsableByteArray();
|
||||
sectionHeader = new ParsableBitArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDecode(String mimeType) {
|
||||
return TextUtils.equals(mimeType, MimeTypes.APPLICATION_SCTE35);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metadata decode(byte[] data, int size) throws MetadataDecoderException {
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
case TYPE_TIME_SIGNAL:
|
||||
command = TimeSignalCommand.parseFromSection(sectionData, ptsAdjustment);
|
||||
break;
|
||||
case TYPE_PRIVATE_COMMAND:
|
||||
command = PrivateCommand.parseFromSection(sectionData, spliceCommandLength, ptsAdjustment);
|
||||
break;
|
||||
}
|
||||
return command == null ? new Metadata() : new Metadata(command);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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 android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a splice insert command defined in SCTE35, Section 9.3.3.
|
||||
*/
|
||||
public final class SpliceInsertCommand extends SpliceCommand {
|
||||
|
||||
public final long spliceEventId;
|
||||
public final boolean spliceEventCancelIndicator;
|
||||
public final boolean outOfNetworkIndicator;
|
||||
public final boolean programSpliceFlag;
|
||||
public final boolean spliceImmediateFlag;
|
||||
public final long programSplicePts;
|
||||
public final List<ComponentSplice> componentSpliceList;
|
||||
public final boolean autoReturn;
|
||||
public final long breakDuration;
|
||||
public final int uniqueProgramId;
|
||||
public final int availNum;
|
||||
public final int availsExpected;
|
||||
|
||||
private SpliceInsertCommand(long spliceEventId, boolean spliceEventCancelIndicator,
|
||||
boolean outOfNetworkIndicator, boolean programSpliceFlag, boolean spliceImmediateFlag,
|
||||
long programSplicePts, List<ComponentSplice> componentSpliceList, boolean autoReturn,
|
||||
long breakDuration, int uniqueProgramId, int availNum, int availsExpected) {
|
||||
this.spliceEventId = spliceEventId;
|
||||
this.spliceEventCancelIndicator = spliceEventCancelIndicator;
|
||||
this.outOfNetworkIndicator = outOfNetworkIndicator;
|
||||
this.programSpliceFlag = programSpliceFlag;
|
||||
this.spliceImmediateFlag = spliceImmediateFlag;
|
||||
this.programSplicePts = programSplicePts;
|
||||
this.componentSpliceList = Collections.unmodifiableList(componentSpliceList);
|
||||
this.autoReturn = autoReturn;
|
||||
this.breakDuration = breakDuration;
|
||||
this.uniqueProgramId = uniqueProgramId;
|
||||
this.availNum = availNum;
|
||||
this.availsExpected = availsExpected;
|
||||
}
|
||||
|
||||
private SpliceInsertCommand(Parcel in) {
|
||||
spliceEventId = in.readLong();
|
||||
spliceEventCancelIndicator = in.readByte() == 1;
|
||||
outOfNetworkIndicator = in.readByte() == 1;
|
||||
programSpliceFlag = in.readByte() == 1;
|
||||
spliceImmediateFlag = in.readByte() == 1;
|
||||
programSplicePts = in.readLong();
|
||||
int componentSpliceListSize = in.readInt();
|
||||
List<ComponentSplice> componentSpliceList = new ArrayList<>(componentSpliceListSize);
|
||||
for (int i = 0; i < componentSpliceListSize; i++) {
|
||||
componentSpliceList.add(ComponentSplice.createFromParcel(in));
|
||||
}
|
||||
this.componentSpliceList = Collections.unmodifiableList(componentSpliceList);
|
||||
autoReturn = in.readByte() == 1;
|
||||
breakDuration = in.readLong();
|
||||
uniqueProgramId = in.readInt();
|
||||
availNum = in.readInt();
|
||||
availsExpected = in.readInt();
|
||||
}
|
||||
|
||||
/* package */ static SpliceInsertCommand parseFromSection(ParsableByteArray sectionData,
|
||||
long ptsAdjustment) {
|
||||
long spliceEventId = sectionData.readUnsignedInt();
|
||||
// splice_event_cancel_indicator(1), reserved(7).
|
||||
boolean spliceEventCancelIndicator = (sectionData.readUnsignedByte() & 0x80) != 0;
|
||||
boolean outOfNetworkIndicator = false;
|
||||
boolean programSpliceFlag = false;
|
||||
boolean spliceImmediateFlag = false;
|
||||
long programSplicePts = C.TIME_UNSET;
|
||||
ArrayList<ComponentSplice> componentSplices = new ArrayList<>();
|
||||
int uniqueProgramId = 0;
|
||||
int availNum = 0;
|
||||
int availsExpected = 0;
|
||||
boolean autoReturn = false;
|
||||
long duration = C.TIME_UNSET;
|
||||
if (!spliceEventCancelIndicator) {
|
||||
int headerByte = sectionData.readUnsignedByte();
|
||||
outOfNetworkIndicator = (headerByte & 0x80) != 0;
|
||||
programSpliceFlag = (headerByte & 0x40) != 0;
|
||||
boolean durationFlag = (headerByte & 0x20) != 0;
|
||||
spliceImmediateFlag = (headerByte & 0x10) != 0;
|
||||
if (programSpliceFlag && !spliceImmediateFlag) {
|
||||
programSplicePts = TimeSignalCommand.parseSpliceTime(sectionData, ptsAdjustment);
|
||||
}
|
||||
if (!programSpliceFlag) {
|
||||
int componentCount = sectionData.readUnsignedByte();
|
||||
componentSplices = new ArrayList<>(componentCount);
|
||||
for (int i = 0; i < componentCount; i++) {
|
||||
int componentTag = sectionData.readUnsignedByte();
|
||||
long componentSplicePts = C.TIME_UNSET;
|
||||
if (!spliceImmediateFlag) {
|
||||
componentSplicePts = TimeSignalCommand.parseSpliceTime(sectionData, ptsAdjustment);
|
||||
}
|
||||
componentSplices.add(new ComponentSplice(componentTag, componentSplicePts));
|
||||
}
|
||||
}
|
||||
if (durationFlag) {
|
||||
long firstByte = sectionData.readUnsignedByte();
|
||||
autoReturn = (firstByte & 0x80) != 0;
|
||||
duration = ((firstByte & 0x01) << 32) | sectionData.readUnsignedInt();
|
||||
}
|
||||
uniqueProgramId = sectionData.readUnsignedShort();
|
||||
availNum = sectionData.readUnsignedByte();
|
||||
availsExpected = sectionData.readUnsignedByte();
|
||||
}
|
||||
return new SpliceInsertCommand(spliceEventId, spliceEventCancelIndicator, outOfNetworkIndicator,
|
||||
programSpliceFlag, spliceImmediateFlag, programSplicePts, componentSplices, autoReturn,
|
||||
duration, uniqueProgramId, availNum, availsExpected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds splicing information for specific splice insert command components.
|
||||
*/
|
||||
public static final class ComponentSplice {
|
||||
|
||||
public final int componentTag;
|
||||
public final long componentSplicePts;
|
||||
|
||||
private ComponentSplice(int componentTag, long componentSplicePts) {
|
||||
this.componentTag = componentTag;
|
||||
this.componentSplicePts = componentSplicePts;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel dest) {
|
||||
dest.writeInt(componentTag);
|
||||
dest.writeLong(componentSplicePts);
|
||||
}
|
||||
|
||||
public static ComponentSplice createFromParcel(Parcel in) {
|
||||
return new ComponentSplice(in.readInt(), in.readLong());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Parcelable implementation.
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(spliceEventId);
|
||||
dest.writeByte((byte) (spliceEventCancelIndicator ? 1 : 0));
|
||||
dest.writeByte((byte) (outOfNetworkIndicator ? 1 : 0));
|
||||
dest.writeByte((byte) (programSpliceFlag ? 1 : 0));
|
||||
dest.writeByte((byte) (spliceImmediateFlag ? 1 : 0));
|
||||
dest.writeLong(programSplicePts);
|
||||
int componentSpliceListSize = componentSpliceList.size();
|
||||
dest.writeInt(componentSpliceListSize);
|
||||
for (int i = 0; i < componentSpliceListSize; i++) {
|
||||
componentSpliceList.get(i).writeToParcel(dest);
|
||||
}
|
||||
dest.writeByte((byte) (autoReturn ? 1 : 0));
|
||||
dest.writeLong(breakDuration);
|
||||
dest.writeInt(uniqueProgramId);
|
||||
dest.writeInt(availNum);
|
||||
dest.writeInt(availsExpected);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SpliceInsertCommand> CREATOR =
|
||||
new Parcelable.Creator<SpliceInsertCommand>() {
|
||||
|
||||
@Override
|
||||
public SpliceInsertCommand createFromParcel(Parcel in) {
|
||||
return new SpliceInsertCommand(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpliceInsertCommand[] newArray(int size) {
|
||||
return new SpliceInsertCommand[size];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 android.os.Parcel;
|
||||
|
||||
/**
|
||||
* Represents a splice null command as defined in SCTE35, Section 9.3.1.
|
||||
*/
|
||||
public final class SpliceNullCommand extends SpliceCommand {
|
||||
|
||||
// Parcelable implementation.
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
public static final Creator<SpliceNullCommand> CREATOR =
|
||||
new Creator<SpliceNullCommand>() {
|
||||
|
||||
@Override
|
||||
public SpliceNullCommand createFromParcel(Parcel in) {
|
||||
return new SpliceNullCommand();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpliceNullCommand[] newArray(int size) {
|
||||
return new SpliceNullCommand[size];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* 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 android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a splice schedule command as defined in SCTE35, Section 9.3.2.
|
||||
*/
|
||||
public final class SpliceScheduleCommand extends SpliceCommand {
|
||||
|
||||
/**
|
||||
* Represents a splice event as contained in a {@link SpliceScheduleCommand}.
|
||||
*/
|
||||
public static final class Event {
|
||||
|
||||
public final long spliceEventId;
|
||||
public final boolean spliceEventCancelIndicator;
|
||||
public final boolean outOfNetworkIndicator;
|
||||
public final boolean programSpliceFlag;
|
||||
public final long utcSpliceTime;
|
||||
public final List<ComponentSplice> componentSpliceList;
|
||||
public final boolean autoReturn;
|
||||
public final long breakDuration;
|
||||
public final int uniqueProgramId;
|
||||
public final int availNum;
|
||||
public final int availsExpected;
|
||||
|
||||
private Event(long spliceEventId, boolean spliceEventCancelIndicator,
|
||||
boolean outOfNetworkIndicator, boolean programSpliceFlag,
|
||||
List<ComponentSplice> componentSpliceList, long utcSpliceTime, boolean autoReturn,
|
||||
long breakDuration, int uniqueProgramId, int availNum, int availsExpected) {
|
||||
this.spliceEventId = spliceEventId;
|
||||
this.spliceEventCancelIndicator = spliceEventCancelIndicator;
|
||||
this.outOfNetworkIndicator = outOfNetworkIndicator;
|
||||
this.programSpliceFlag = programSpliceFlag;
|
||||
this.componentSpliceList = Collections.unmodifiableList(componentSpliceList);
|
||||
this.utcSpliceTime = utcSpliceTime;
|
||||
this.autoReturn = autoReturn;
|
||||
this.breakDuration = breakDuration;
|
||||
this.uniqueProgramId = uniqueProgramId;
|
||||
this.availNum = availNum;
|
||||
this.availsExpected = availsExpected;
|
||||
}
|
||||
|
||||
private Event(Parcel in) {
|
||||
this.spliceEventId = in.readLong();
|
||||
this.spliceEventCancelIndicator = in.readByte() == 1;
|
||||
this.outOfNetworkIndicator = in.readByte() == 1;
|
||||
this.programSpliceFlag = in.readByte() == 1;
|
||||
int componentSpliceListLength = in.readInt();
|
||||
ArrayList<ComponentSplice> componentSpliceList = new ArrayList<>(componentSpliceListLength);
|
||||
for (int i = 0; i < componentSpliceListLength; i++) {
|
||||
componentSpliceList.add(ComponentSplice.createFromParcel(in));
|
||||
}
|
||||
this.componentSpliceList = Collections.unmodifiableList(componentSpliceList);
|
||||
this.utcSpliceTime = in.readLong();
|
||||
this.autoReturn = in.readByte() == 1;
|
||||
this.breakDuration = in.readLong();
|
||||
this.uniqueProgramId = in.readInt();
|
||||
this.availNum = in.readInt();
|
||||
this.availsExpected = in.readInt();
|
||||
}
|
||||
|
||||
private static Event parseFromSection(ParsableByteArray sectionData) {
|
||||
long spliceEventId = sectionData.readUnsignedInt();
|
||||
// splice_event_cancel_indicator(1), reserved(7).
|
||||
boolean spliceEventCancelIndicator = (sectionData.readUnsignedByte() & 0x80) != 0;
|
||||
boolean outOfNetworkIndicator = false;
|
||||
boolean programSpliceFlag = false;
|
||||
long utcSpliceTime = C.TIME_UNSET;
|
||||
ArrayList<ComponentSplice> componentSplices = new ArrayList<>();
|
||||
int uniqueProgramId = 0;
|
||||
int availNum = 0;
|
||||
int availsExpected = 0;
|
||||
boolean autoReturn = false;
|
||||
long duration = C.TIME_UNSET;
|
||||
if (!spliceEventCancelIndicator) {
|
||||
int headerByte = sectionData.readUnsignedByte();
|
||||
outOfNetworkIndicator = (headerByte & 0x80) != 0;
|
||||
programSpliceFlag = (headerByte & 0x40) != 0;
|
||||
boolean durationFlag = (headerByte & 0x20) != 0;
|
||||
if (programSpliceFlag) {
|
||||
utcSpliceTime = sectionData.readUnsignedInt();
|
||||
}
|
||||
if (!programSpliceFlag) {
|
||||
int componentCount = sectionData.readUnsignedByte();
|
||||
componentSplices = new ArrayList<>(componentCount);
|
||||
for (int i = 0; i < componentCount; i++) {
|
||||
int componentTag = sectionData.readUnsignedByte();
|
||||
long componentUtcSpliceTime = sectionData.readUnsignedInt();
|
||||
componentSplices.add(new ComponentSplice(componentTag, componentUtcSpliceTime));
|
||||
}
|
||||
}
|
||||
if (durationFlag) {
|
||||
long firstByte = sectionData.readUnsignedByte();
|
||||
autoReturn = (firstByte & 0x80) != 0;
|
||||
duration = ((firstByte & 0x01) << 32) | sectionData.readUnsignedInt();
|
||||
}
|
||||
uniqueProgramId = sectionData.readUnsignedShort();
|
||||
availNum = sectionData.readUnsignedByte();
|
||||
availsExpected = sectionData.readUnsignedByte();
|
||||
}
|
||||
return new Event(spliceEventId, spliceEventCancelIndicator, outOfNetworkIndicator,
|
||||
programSpliceFlag, componentSplices, utcSpliceTime, autoReturn, duration, uniqueProgramId,
|
||||
availNum, availsExpected);
|
||||
}
|
||||
|
||||
private void writeToParcel(Parcel dest) {
|
||||
dest.writeLong(spliceEventId);
|
||||
dest.writeByte((byte) (spliceEventCancelIndicator ? 1 : 0));
|
||||
dest.writeByte((byte) (outOfNetworkIndicator ? 1 : 0));
|
||||
dest.writeByte((byte) (programSpliceFlag ? 1 : 0));
|
||||
int componentSpliceListSize = componentSpliceList.size();
|
||||
dest.writeInt(componentSpliceListSize);
|
||||
for (int i = 0; i < componentSpliceListSize; i++) {
|
||||
componentSpliceList.get(i).writeToParcel(dest);
|
||||
}
|
||||
dest.writeLong(utcSpliceTime);
|
||||
dest.writeByte((byte) (autoReturn ? 1 : 0));
|
||||
dest.writeLong(breakDuration);
|
||||
dest.writeInt(uniqueProgramId);
|
||||
dest.writeInt(availNum);
|
||||
dest.writeInt(availsExpected);
|
||||
}
|
||||
|
||||
private static Event createFromParcel(Parcel in) {
|
||||
return new Event(in);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds splicing information for specific splice schedule command components.
|
||||
*/
|
||||
public static final class ComponentSplice {
|
||||
|
||||
public final int componentTag;
|
||||
public final long utcSpliceTime;
|
||||
|
||||
private ComponentSplice(int componentTag, long utcSpliceTime) {
|
||||
this.componentTag = componentTag;
|
||||
this.utcSpliceTime = utcSpliceTime;
|
||||
}
|
||||
|
||||
private static ComponentSplice createFromParcel(Parcel in) {
|
||||
return new ComponentSplice(in.readInt(), in.readLong());
|
||||
}
|
||||
|
||||
private void writeToParcel(Parcel dest) {
|
||||
dest.writeInt(componentTag);
|
||||
dest.writeLong(utcSpliceTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public final List<Event> events;
|
||||
|
||||
private SpliceScheduleCommand(List<Event> events) {
|
||||
this.events = Collections.unmodifiableList(events);
|
||||
}
|
||||
|
||||
private SpliceScheduleCommand(Parcel in) {
|
||||
int eventsSize = in.readInt();
|
||||
ArrayList<Event> events = new ArrayList<>(eventsSize);
|
||||
for (int i = 0; i < eventsSize; i++) {
|
||||
events.add(Event.createFromParcel(in));
|
||||
}
|
||||
this.events = Collections.unmodifiableList(events);
|
||||
}
|
||||
|
||||
/* package */ static SpliceScheduleCommand parseFromSection(ParsableByteArray sectionData) {
|
||||
int spliceCount = sectionData.readUnsignedByte();
|
||||
ArrayList<Event> events = new ArrayList<>(spliceCount);
|
||||
for (int i = 0; i < spliceCount; i++) {
|
||||
events.add(Event.parseFromSection(sectionData));
|
||||
}
|
||||
return new SpliceScheduleCommand(events);
|
||||
}
|
||||
|
||||
// Parcelable implementation.
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
int eventsSize = events.size();
|
||||
dest.writeInt(eventsSize);
|
||||
for (int i = 0; i < eventsSize; i++) {
|
||||
events.get(i).writeToParcel(dest);
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SpliceScheduleCommand> CREATOR =
|
||||
new Parcelable.Creator<SpliceScheduleCommand>() {
|
||||
|
||||
@Override
|
||||
public SpliceScheduleCommand createFromParcel(Parcel in) {
|
||||
return new SpliceScheduleCommand(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpliceScheduleCommand[] newArray(int size) {
|
||||
return new SpliceScheduleCommand[size];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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 android.os.Parcel;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||
|
||||
/**
|
||||
* Represents a time signal command as defined in SCTE35, Section 9.3.4.
|
||||
*/
|
||||
public final class TimeSignalCommand extends SpliceCommand {
|
||||
|
||||
public final long ptsTime;
|
||||
|
||||
private TimeSignalCommand(long ptsTime) {
|
||||
this.ptsTime = ptsTime;
|
||||
}
|
||||
|
||||
/* package */ static TimeSignalCommand parseFromSection(ParsableByteArray sectionData,
|
||||
long ptsAdjustment) {
|
||||
return new TimeSignalCommand(parseSpliceTime(sectionData, ptsAdjustment));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses pts_time from splice_time(), defined in Section 9.4.1. Returns {@link C#TIME_UNSET}, if
|
||||
* time_specified_flag is false.
|
||||
*
|
||||
* @param sectionData The section data from which the pts_time is parsed.
|
||||
* @param ptsAdjustment The pts adjustment provided by the splice info section header.
|
||||
* @return The pts_time defined by splice_time(), or {@link C#TIME_UNSET}, if time_specified_flag
|
||||
* is false.
|
||||
*/
|
||||
/* package */ static long parseSpliceTime(ParsableByteArray sectionData, long ptsAdjustment) {
|
||||
long firstByte = sectionData.readUnsignedByte();
|
||||
long ptsTime = C.TIME_UNSET;
|
||||
if ((firstByte & 0x80) != 0 /* time_specified_flag */) {
|
||||
// See SCTE35 9.2.1 for more information about pts adjustment.
|
||||
ptsTime = (firstByte & 0x01) << 32 | sectionData.readUnsignedInt();
|
||||
ptsTime += ptsAdjustment;
|
||||
ptsTime &= 0x1FFFFFFFFL;
|
||||
}
|
||||
return ptsTime;
|
||||
}
|
||||
|
||||
// Parcelable implementation.
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(ptsTime);
|
||||
}
|
||||
|
||||
public static final Creator<TimeSignalCommand> CREATOR =
|
||||
new Creator<TimeSignalCommand>() {
|
||||
|
||||
@Override
|
||||
public TimeSignalCommand createFromParcel(Parcel in) {
|
||||
return new TimeSignalCommand(in.readLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeSignalCommand[] newArray(int size) {
|
||||
return new TimeSignalCommand[size];
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -74,6 +74,7 @@ public final class MimeTypes {
|
|||
public static final String APPLICATION_RAWCC = BASE_TYPE_APPLICATION + "/x-rawcc";
|
||||
public static final String APPLICATION_VOBSUB = BASE_TYPE_APPLICATION + "/vobsub";
|
||||
public static final String APPLICATION_PGS = BASE_TYPE_APPLICATION + "/pgs";
|
||||
public static final String APPLICATION_SCTE35 = BASE_TYPE_APPLICATION + "/x-scte35";
|
||||
|
||||
private MimeTypes() {}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue