1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.android.exoplayer2.source.dash.manifest; 17 18 import android.net.Uri; 19 import androidx.annotation.Nullable; 20 import com.google.android.exoplayer2.C; 21 import com.google.android.exoplayer2.offline.FilterableManifest; 22 import com.google.android.exoplayer2.offline.StreamKey; 23 import java.util.ArrayList; 24 import java.util.Collections; 25 import java.util.LinkedList; 26 import java.util.List; 27 28 /** 29 * Represents a DASH media presentation description (mpd), as defined by ISO/IEC 23009-1:2014 30 * Section 5.3.1.2. 31 */ 32 public class DashManifest implements FilterableManifest<DashManifest> { 33 34 /** 35 * The {@code availabilityStartTime} value in milliseconds since epoch, or {@link C#TIME_UNSET} if 36 * not present. 37 */ 38 public final long availabilityStartTimeMs; 39 40 /** 41 * The duration of the presentation in milliseconds, or {@link C#TIME_UNSET} if not applicable. 42 */ 43 public final long durationMs; 44 45 /** 46 * The {@code minBufferTime} value in milliseconds, or {@link C#TIME_UNSET} if not present. 47 */ 48 public final long minBufferTimeMs; 49 50 /** 51 * Whether the manifest has value "dynamic" for the {@code type} attribute. 52 */ 53 public final boolean dynamic; 54 55 /** 56 * The {@code minimumUpdatePeriod} value in milliseconds, or {@link C#TIME_UNSET} if not 57 * applicable. 58 */ 59 public final long minUpdatePeriodMs; 60 61 /** 62 * The {@code timeShiftBufferDepth} value in milliseconds, or {@link C#TIME_UNSET} if not 63 * present. 64 */ 65 public final long timeShiftBufferDepthMs; 66 67 /** 68 * The {@code suggestedPresentationDelay} value in milliseconds, or {@link C#TIME_UNSET} if not 69 * present. 70 */ 71 public final long suggestedPresentationDelayMs; 72 73 /** 74 * The {@code publishTime} value in milliseconds since epoch, or {@link C#TIME_UNSET} if 75 * not present. 76 */ 77 public final long publishTimeMs; 78 79 /** 80 * The {@link UtcTimingElement}, or null if not present. Defined in DVB A168:7/2016, Section 81 * 4.7.2. 82 */ 83 @Nullable public final UtcTimingElement utcTiming; 84 85 /** The location of this manifest, or null if not present. */ 86 @Nullable public final Uri location; 87 88 /** The {@link ProgramInformation}, or null if not present. */ 89 @Nullable public final ProgramInformation programInformation; 90 91 private final List<Period> periods; 92 93 /** 94 * @deprecated Use {@link #DashManifest(long, long, long, boolean, long, long, long, long, 95 * ProgramInformation, UtcTimingElement, Uri, List)}. 96 */ 97 @Deprecated DashManifest( long availabilityStartTimeMs, long durationMs, long minBufferTimeMs, boolean dynamic, long minUpdatePeriodMs, long timeShiftBufferDepthMs, long suggestedPresentationDelayMs, long publishTimeMs, @Nullable UtcTimingElement utcTiming, @Nullable Uri location, List<Period> periods)98 public DashManifest( 99 long availabilityStartTimeMs, 100 long durationMs, 101 long minBufferTimeMs, 102 boolean dynamic, 103 long minUpdatePeriodMs, 104 long timeShiftBufferDepthMs, 105 long suggestedPresentationDelayMs, 106 long publishTimeMs, 107 @Nullable UtcTimingElement utcTiming, 108 @Nullable Uri location, 109 List<Period> periods) { 110 this( 111 availabilityStartTimeMs, 112 durationMs, 113 minBufferTimeMs, 114 dynamic, 115 minUpdatePeriodMs, 116 timeShiftBufferDepthMs, 117 suggestedPresentationDelayMs, 118 publishTimeMs, 119 /* programInformation= */ null, 120 utcTiming, 121 location, 122 periods); 123 } 124 DashManifest( long availabilityStartTimeMs, long durationMs, long minBufferTimeMs, boolean dynamic, long minUpdatePeriodMs, long timeShiftBufferDepthMs, long suggestedPresentationDelayMs, long publishTimeMs, @Nullable ProgramInformation programInformation, @Nullable UtcTimingElement utcTiming, @Nullable Uri location, List<Period> periods)125 public DashManifest( 126 long availabilityStartTimeMs, 127 long durationMs, 128 long minBufferTimeMs, 129 boolean dynamic, 130 long minUpdatePeriodMs, 131 long timeShiftBufferDepthMs, 132 long suggestedPresentationDelayMs, 133 long publishTimeMs, 134 @Nullable ProgramInformation programInformation, 135 @Nullable UtcTimingElement utcTiming, 136 @Nullable Uri location, 137 List<Period> periods) { 138 this.availabilityStartTimeMs = availabilityStartTimeMs; 139 this.durationMs = durationMs; 140 this.minBufferTimeMs = minBufferTimeMs; 141 this.dynamic = dynamic; 142 this.minUpdatePeriodMs = minUpdatePeriodMs; 143 this.timeShiftBufferDepthMs = timeShiftBufferDepthMs; 144 this.suggestedPresentationDelayMs = suggestedPresentationDelayMs; 145 this.publishTimeMs = publishTimeMs; 146 this.programInformation = programInformation; 147 this.utcTiming = utcTiming; 148 this.location = location; 149 this.periods = periods == null ? Collections.emptyList() : periods; 150 } 151 getPeriodCount()152 public final int getPeriodCount() { 153 return periods.size(); 154 } 155 getPeriod(int index)156 public final Period getPeriod(int index) { 157 return periods.get(index); 158 } 159 getPeriodDurationMs(int index)160 public final long getPeriodDurationMs(int index) { 161 return index == periods.size() - 1 162 ? (durationMs == C.TIME_UNSET ? C.TIME_UNSET : (durationMs - periods.get(index).startMs)) 163 : (periods.get(index + 1).startMs - periods.get(index).startMs); 164 } 165 getPeriodDurationUs(int index)166 public final long getPeriodDurationUs(int index) { 167 return C.msToUs(getPeriodDurationMs(index)); 168 } 169 170 @Override copy(List<StreamKey> streamKeys)171 public final DashManifest copy(List<StreamKey> streamKeys) { 172 LinkedList<StreamKey> keys = new LinkedList<>(streamKeys); 173 Collections.sort(keys); 174 keys.add(new StreamKey(-1, -1, -1)); // Add a stopper key to the end 175 176 ArrayList<Period> copyPeriods = new ArrayList<>(); 177 long shiftMs = 0; 178 for (int periodIndex = 0; periodIndex < getPeriodCount(); periodIndex++) { 179 if (keys.peek().periodIndex != periodIndex) { 180 // No representations selected in this period. 181 long periodDurationMs = getPeriodDurationMs(periodIndex); 182 if (periodDurationMs != C.TIME_UNSET) { 183 shiftMs += periodDurationMs; 184 } 185 } else { 186 Period period = getPeriod(periodIndex); 187 ArrayList<AdaptationSet> copyAdaptationSets = 188 copyAdaptationSets(period.adaptationSets, keys); 189 Period copiedPeriod = new Period(period.id, period.startMs - shiftMs, copyAdaptationSets, 190 period.eventStreams); 191 copyPeriods.add(copiedPeriod); 192 } 193 } 194 long newDuration = durationMs != C.TIME_UNSET ? durationMs - shiftMs : C.TIME_UNSET; 195 return new DashManifest( 196 availabilityStartTimeMs, 197 newDuration, 198 minBufferTimeMs, 199 dynamic, 200 minUpdatePeriodMs, 201 timeShiftBufferDepthMs, 202 suggestedPresentationDelayMs, 203 publishTimeMs, 204 programInformation, 205 utcTiming, 206 location, 207 copyPeriods); 208 } 209 copyAdaptationSets( List<AdaptationSet> adaptationSets, LinkedList<StreamKey> keys)210 private static ArrayList<AdaptationSet> copyAdaptationSets( 211 List<AdaptationSet> adaptationSets, LinkedList<StreamKey> keys) { 212 StreamKey key = keys.poll(); 213 int periodIndex = key.periodIndex; 214 ArrayList<AdaptationSet> copyAdaptationSets = new ArrayList<>(); 215 do { 216 int adaptationSetIndex = key.groupIndex; 217 AdaptationSet adaptationSet = adaptationSets.get(adaptationSetIndex); 218 219 List<Representation> representations = adaptationSet.representations; 220 ArrayList<Representation> copyRepresentations = new ArrayList<>(); 221 do { 222 Representation representation = representations.get(key.trackIndex); 223 copyRepresentations.add(representation); 224 key = keys.poll(); 225 } while (key.periodIndex == periodIndex && key.groupIndex == adaptationSetIndex); 226 227 copyAdaptationSets.add( 228 new AdaptationSet( 229 adaptationSet.id, 230 adaptationSet.type, 231 copyRepresentations, 232 adaptationSet.accessibilityDescriptors, 233 adaptationSet.essentialProperties, 234 adaptationSet.supplementalProperties)); 235 } while(key.periodIndex == periodIndex); 236 // Add back the last key which doesn't belong to the period being processed 237 keys.addFirst(key); 238 return copyAdaptationSets; 239 } 240 241 } 242