summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/PrivateCommand.java85
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceCommand.java37
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceInfoDecoder.java102
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceInsertCommand.java254
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceNullCommand.java47
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceScheduleCommand.java270
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/TimeSignalCommand.java93
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/package-info.java19
8 files changed, 907 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/PrivateCommand.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/PrivateCommand.java
new file mode 100644
index 0000000000..3437c8dd73
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/PrivateCommand.java
@@ -0,0 +1,85 @@
+/*
+ * 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 org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.scte35;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.ParsableByteArray;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Util;
+
+/**
+ * Represents a private command as defined in SCTE35, Section 9.3.6.
+ */
+public final class PrivateCommand extends SpliceCommand {
+
+ /**
+ * The {@code pts_adjustment} as defined in SCTE35, Section 9.2.
+ */
+ public final long ptsAdjustment;
+ /**
+ * The identifier as defined in SCTE35, Section 9.3.6.
+ */
+ public final long identifier;
+ /**
+ * The private bytes as defined in SCTE35, Section 9.3.6.
+ */
+ 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 = Util.castNonNull(in.createByteArray());
+ }
+
+ /* 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.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];
+ }
+
+ };
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceCommand.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceCommand.java
new file mode 100644
index 0000000000..866a7ec8bc
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceCommand.java
@@ -0,0 +1,37 @@
+/*
+ * 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 org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.scte35;
+
+import org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.Metadata;
+
+/**
+ * Superclass for SCTE35 splice commands.
+ */
+public abstract class SpliceCommand implements Metadata.Entry {
+
+ @Override
+ public String toString() {
+ return "SCTE-35 splice command: type=" + getClass().getSimpleName();
+ }
+
+ // Parcelable implementation.
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceInfoDecoder.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceInfoDecoder.java
new file mode 100644
index 0000000000..a90bddb078
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceInfoDecoder.java
@@ -0,0 +1,102 @@
+/*
+ * 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 org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.scte35;
+
+import androidx.annotation.Nullable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.Metadata;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.MetadataDecoder;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.MetadataInputBuffer;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.Assertions;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.ParsableBitArray;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.ParsableByteArray;
+import org.mozilla.thirdparty.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 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;
+
+ @MonotonicNonNull private TimestampAdjuster timestampAdjuster;
+
+ public SpliceInfoDecoder() {
+ sectionData = new ParsableByteArray();
+ sectionHeader = new ParsableBitArray();
+ }
+
+ @SuppressWarnings("ByteBufferBackingArray")
+ @Override
+ public Metadata decode(MetadataInputBuffer inputBuffer) {
+ ByteBuffer buffer = Assertions.checkNotNull(inputBuffer.data);
+
+ // 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);
+ }
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceInsertCommand.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceInsertCommand.java
new file mode 100644
index 0000000000..5993efb10f
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceInsertCommand.java
@@ -0,0 +1,254 @@
+/*
+ * 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 org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.scte35;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.ParsableByteArray;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.TimestampAdjuster;
+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 {
+
+ /**
+ * 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;
+ /**
+ * Whether splicing should be done at the nearest opportunity. If false, splicing should be done
+ * at the moment indicated by {@link #programSplicePlaybackPositionUs} or
+ * {@link ComponentSplice#componentSplicePlaybackPositionUs}, depending on
+ * {@link #programSpliceFlag}.
+ */
+ public final boolean spliceImmediateFlag;
+ /**
+ * If {@link #programSpliceFlag} is true, the PTS at which the program splice should occur.
+ * {@link C#TIME_UNSET} otherwise.
+ */
+ public final long programSplicePts;
+ /**
+ * Equivalent to {@link #programSplicePts} but in the playback timebase.
+ */
+ public final long programSplicePlaybackPositionUs;
+ /**
+ * 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.3.
+ */
+ public final int uniqueProgramId;
+ /**
+ * Holds the value of {@code avail_num} as defined in SCTE35, Section 9.3.3.
+ */
+ public final int availNum;
+ /**
+ * Holds the value of {@code avails_expected} as defined in SCTE35, Section 9.3.3.
+ */
+ public final int availsExpected;
+
+ private SpliceInsertCommand(long spliceEventId, boolean spliceEventCancelIndicator,
+ boolean outOfNetworkIndicator, boolean programSpliceFlag, boolean spliceImmediateFlag,
+ long programSplicePts, long programSplicePlaybackPositionUs,
+ List<ComponentSplice> componentSpliceList, boolean autoReturn, long breakDurationUs,
+ 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.programSplicePlaybackPositionUs = programSplicePlaybackPositionUs;
+ this.componentSpliceList = Collections.unmodifiableList(componentSpliceList);
+ this.autoReturn = autoReturn;
+ this.breakDurationUs = breakDurationUs;
+ 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();
+ programSplicePlaybackPositionUs = 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;
+ breakDurationUs = in.readLong();
+ uniqueProgramId = in.readInt();
+ availNum = in.readInt();
+ availsExpected = in.readInt();
+ }
+
+ /* package */ static SpliceInsertCommand parseFromSection(ParsableByteArray sectionData,
+ long ptsAdjustment, TimestampAdjuster timestampAdjuster) {
+ 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;
+ List<ComponentSplice> componentSplices = Collections.emptyList();
+ 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;
+ 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,
+ timestampAdjuster.adjustTsTimestamp(componentSplicePts)));
+ }
+ }
+ 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 SpliceInsertCommand(spliceEventId, spliceEventCancelIndicator, outOfNetworkIndicator,
+ programSpliceFlag, spliceImmediateFlag, programSplicePts,
+ timestampAdjuster.adjustTsTimestamp(programSplicePts), componentSplices, autoReturn,
+ breakDurationUs, 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;
+ public final long componentSplicePlaybackPositionUs;
+
+ private ComponentSplice(int componentTag, long componentSplicePts,
+ long componentSplicePlaybackPositionUs) {
+ this.componentTag = componentTag;
+ this.componentSplicePts = componentSplicePts;
+ this.componentSplicePlaybackPositionUs = componentSplicePlaybackPositionUs;
+ }
+
+ public void writeToParcel(Parcel dest) {
+ dest.writeInt(componentTag);
+ dest.writeLong(componentSplicePts);
+ dest.writeLong(componentSplicePlaybackPositionUs);
+ }
+
+ public static ComponentSplice createFromParcel(Parcel in) {
+ return new ComponentSplice(in.readInt(), in.readLong(), 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);
+ dest.writeLong(programSplicePlaybackPositionUs);
+ 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(breakDurationUs);
+ 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];
+ }
+
+ };
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceNullCommand.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceNullCommand.java
new file mode 100644
index 0000000000..afc88bbeab
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceNullCommand.java
@@ -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 org.mozilla.thirdparty.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];
+ }
+
+ };
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceScheduleCommand.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceScheduleCommand.java
new file mode 100644
index 0000000000..e1d369bc87
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/SpliceScheduleCommand.java
@@ -0,0 +1,270 @@
+/*
+ * 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 org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.scte35;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
+import org.mozilla.thirdparty.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];
+ }
+
+ };
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/TimeSignalCommand.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/TimeSignalCommand.java
new file mode 100644
index 0000000000..f50a029f1b
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/TimeSignalCommand.java
@@ -0,0 +1,93 @@
+/*
+ * 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 org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.scte35;
+
+import android.os.Parcel;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.ParsableByteArray;
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.TimestampAdjuster;
+
+/**
+ * Represents a time signal command as defined in SCTE35, Section 9.3.4.
+ */
+public final class TimeSignalCommand extends SpliceCommand {
+
+ /**
+ * A PTS value, as defined in SCTE35, Section 9.3.4.
+ */
+ public final long ptsTime;
+ /**
+ * Equivalent to {@link #ptsTime} but in the playback timebase.
+ */
+ public final long playbackPositionUs;
+
+ private TimeSignalCommand(long ptsTime, long playbackPositionUs) {
+ this.ptsTime = ptsTime;
+ this.playbackPositionUs = playbackPositionUs;
+ }
+
+ /* package */ static TimeSignalCommand parseFromSection(ParsableByteArray sectionData,
+ long ptsAdjustment, TimestampAdjuster timestampAdjuster) {
+ long ptsTime = parseSpliceTime(sectionData, ptsAdjustment);
+ long playbackPositionUs = timestampAdjuster.adjustTsTimestamp(ptsTime);
+ return new TimeSignalCommand(ptsTime, playbackPositionUs);
+ }
+
+ /**
+ * 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);
+ dest.writeLong(playbackPositionUs);
+ }
+
+ public static final Creator<TimeSignalCommand> CREATOR =
+ new Creator<TimeSignalCommand>() {
+
+ @Override
+ public TimeSignalCommand createFromParcel(Parcel in) {
+ return new TimeSignalCommand(in.readLong(), in.readLong());
+ }
+
+ @Override
+ public TimeSignalCommand[] newArray(int size) {
+ return new TimeSignalCommand[size];
+ }
+
+ };
+
+}
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/package-info.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/package-info.java
new file mode 100644
index 0000000000..17ce76bb9f
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/metadata/scte35/package-info.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+@NonNullApi
+package org.mozilla.thirdparty.com.google.android.exoplayer2.metadata.scte35;
+
+import org.mozilla.thirdparty.com.google.android.exoplayer2.util.NonNullApi;