blob: 6e62177fc7ff3aafe919fe370c61dd67f428defb [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 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 {
/** The splice event id. */
public final long spliceEventId;
/** True if the event with id {@link #spliceEventId} has been canceled. */
public final boolean spliceEventCancelIndicator;
/**
* If true, the splice event is an opportunity to exit from the network feed. If false,
* indicates an opportunity to return to the network feed.
*/
public final boolean outOfNetworkIndicator;
/**
* Whether the splice mode is program splice mode, whereby all PIDs/components are to be
* spliced. If false, splicing is done per PID/component.
*/
public final boolean programSpliceFlag;
/**
* Represents the time of the signaled splice event as the number of seconds since 00 hours UTC,
* January 6th, 1980, with the count of intervening leap seconds included.
*/
public final long utcSpliceTime;
/**
* If {@link #programSpliceFlag} is false, a non-empty list containing the {@link
* ComponentSplice}s. Otherwise, an empty list.
*/
public final List<ComponentSplice> componentSpliceList;
/**
* If {@link #breakDurationUs} is not {@link C#TIME_UNSET}, defines whether {@link
* #breakDurationUs} should be used to know when to return to the network feed. If {@link
* #breakDurationUs} is {@link C#TIME_UNSET}, the value is undefined.
*/
public final boolean autoReturn;
/**
* The duration of the splice in microseconds, or {@link C#TIME_UNSET} if no duration is
* present.
*/
public final long breakDurationUs;
/** The unique program id as defined in SCTE35, Section 9.3.2. */
public final int uniqueProgramId;
/** Holds the value of {@code avail_num} as defined in SCTE35, Section 9.3.2. */
public final int availNum;
/** Holds the value of {@code avails_expected} as defined in SCTE35, Section 9.3.2. */
public final int availsExpected;
private Event(
long spliceEventId,
boolean spliceEventCancelIndicator,
boolean outOfNetworkIndicator,
boolean programSpliceFlag,
List<ComponentSplice> componentSpliceList,
long utcSpliceTime,
boolean autoReturn,
long breakDurationUs,
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.breakDurationUs = breakDurationUs;
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.breakDurationUs = 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 breakDurationUs = 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;
long breakDuration90khz = ((firstByte & 0x01) << 32) | sectionData.readUnsignedInt();
breakDurationUs = breakDuration90khz * 1000 / 90;
}
uniqueProgramId = sectionData.readUnsignedShort();
availNum = sectionData.readUnsignedByte();
availsExpected = sectionData.readUnsignedByte();
}
return new Event(
spliceEventId,
spliceEventCancelIndicator,
outOfNetworkIndicator,
programSpliceFlag,
componentSplices,
utcSpliceTime,
autoReturn,
breakDurationUs,
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(breakDurationUs);
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);
}
}
/** The list of scheduled events. */
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];
}
};
}