| /* |
| * 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 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]; |
| } |
| }; |
| } |