• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.hls.playlist;
17 
18 import androidx.annotation.IntDef;
19 import androidx.annotation.Nullable;
20 import com.google.android.exoplayer2.C;
21 import com.google.android.exoplayer2.drm.DrmInitData;
22 import com.google.android.exoplayer2.offline.StreamKey;
23 import java.lang.annotation.Documented;
24 import java.lang.annotation.Retention;
25 import java.lang.annotation.RetentionPolicy;
26 import java.util.Collections;
27 import java.util.List;
28 
29 /** Represents an HLS media playlist. */
30 public final class HlsMediaPlaylist extends HlsPlaylist {
31 
32   /** Media segment reference. */
33   @SuppressWarnings("ComparableType")
34   public static final class Segment implements Comparable<Long> {
35 
36     /**
37      * The url of the segment.
38      */
39     public final String url;
40     /**
41      * The media initialization section for this segment, as defined by #EXT-X-MAP. May be null if
42      * the media playlist does not define a media section for this segment. The same instance is
43      * used for all segments that share an EXT-X-MAP tag.
44      */
45     @Nullable public final Segment initializationSegment;
46     /** The duration of the segment in microseconds, as defined by #EXTINF. */
47     public final long durationUs;
48     /** The human readable title of the segment. */
49     public final String title;
50     /**
51      * The number of #EXT-X-DISCONTINUITY tags in the playlist before the segment.
52      */
53     public final int relativeDiscontinuitySequence;
54     /**
55      * The start time of the segment in microseconds, relative to the start of the playlist.
56      */
57     public final long relativeStartTimeUs;
58     /**
59      * DRM initialization data for sample decryption, or null if the segment does not use CDM-DRM
60      * protection.
61      */
62     @Nullable public final DrmInitData drmInitData;
63     /**
64      * The encryption identity key uri as defined by #EXT-X-KEY, or null if the segment does not use
65      * full segment encryption with identity key.
66      */
67     @Nullable public final String fullSegmentEncryptionKeyUri;
68     /**
69      * The encryption initialization vector as defined by #EXT-X-KEY, or null if the segment is not
70      * encrypted.
71      */
72     @Nullable public final String encryptionIV;
73     /** The segment's byte range offset, as defined by #EXT-X-BYTERANGE. */
74     public final long byteRangeOffset;
75     /**
76      * The segment's byte range length, as defined by #EXT-X-BYTERANGE, or {@link C#LENGTH_UNSET} if
77      * no byte range is specified.
78      */
79     public final long byteRangeLength;
80 
81     /** Whether the segment is tagged with #EXT-X-GAP. */
82     public final boolean hasGapTag;
83 
84     /**
85      * @param uri See {@link #url}.
86      * @param byteRangeOffset See {@link #byteRangeOffset}.
87      * @param byteRangeLength See {@link #byteRangeLength}.
88      * @param fullSegmentEncryptionKeyUri See {@link #fullSegmentEncryptionKeyUri}.
89      * @param encryptionIV See {@link #encryptionIV}.
90      */
Segment( String uri, long byteRangeOffset, long byteRangeLength, @Nullable String fullSegmentEncryptionKeyUri, @Nullable String encryptionIV)91     public Segment(
92         String uri,
93         long byteRangeOffset,
94         long byteRangeLength,
95         @Nullable String fullSegmentEncryptionKeyUri,
96         @Nullable String encryptionIV) {
97       this(
98           uri,
99           /* initializationSegment= */ null,
100           /* title= */ "",
101           /* durationUs= */ 0,
102           /* relativeDiscontinuitySequence= */ -1,
103           /* relativeStartTimeUs= */ C.TIME_UNSET,
104           /* drmInitData= */ null,
105           fullSegmentEncryptionKeyUri,
106           encryptionIV,
107           byteRangeOffset,
108           byteRangeLength,
109           /* hasGapTag= */ false);
110     }
111 
112     /**
113      * @param url See {@link #url}.
114      * @param initializationSegment See {@link #initializationSegment}.
115      * @param title See {@link #title}.
116      * @param durationUs See {@link #durationUs}.
117      * @param relativeDiscontinuitySequence See {@link #relativeDiscontinuitySequence}.
118      * @param relativeStartTimeUs See {@link #relativeStartTimeUs}.
119      * @param drmInitData See {@link #drmInitData}.
120      * @param fullSegmentEncryptionKeyUri See {@link #fullSegmentEncryptionKeyUri}.
121      * @param encryptionIV See {@link #encryptionIV}.
122      * @param byteRangeOffset See {@link #byteRangeOffset}.
123      * @param byteRangeLength See {@link #byteRangeLength}.
124      * @param hasGapTag See {@link #hasGapTag}.
125      */
Segment( String url, @Nullable Segment initializationSegment, String title, long durationUs, int relativeDiscontinuitySequence, long relativeStartTimeUs, @Nullable DrmInitData drmInitData, @Nullable String fullSegmentEncryptionKeyUri, @Nullable String encryptionIV, long byteRangeOffset, long byteRangeLength, boolean hasGapTag)126     public Segment(
127         String url,
128         @Nullable Segment initializationSegment,
129         String title,
130         long durationUs,
131         int relativeDiscontinuitySequence,
132         long relativeStartTimeUs,
133         @Nullable DrmInitData drmInitData,
134         @Nullable String fullSegmentEncryptionKeyUri,
135         @Nullable String encryptionIV,
136         long byteRangeOffset,
137         long byteRangeLength,
138         boolean hasGapTag) {
139       this.url = url;
140       this.initializationSegment = initializationSegment;
141       this.title = title;
142       this.durationUs = durationUs;
143       this.relativeDiscontinuitySequence = relativeDiscontinuitySequence;
144       this.relativeStartTimeUs = relativeStartTimeUs;
145       this.drmInitData = drmInitData;
146       this.fullSegmentEncryptionKeyUri = fullSegmentEncryptionKeyUri;
147       this.encryptionIV = encryptionIV;
148       this.byteRangeOffset = byteRangeOffset;
149       this.byteRangeLength = byteRangeLength;
150       this.hasGapTag = hasGapTag;
151     }
152 
153     @Override
compareTo(Long relativeStartTimeUs)154     public int compareTo(Long relativeStartTimeUs) {
155       return this.relativeStartTimeUs > relativeStartTimeUs
156           ? 1 : (this.relativeStartTimeUs < relativeStartTimeUs ? -1 : 0);
157     }
158 
159   }
160 
161   /**
162    * Type of the playlist, as defined by #EXT-X-PLAYLIST-TYPE. One of {@link
163    * #PLAYLIST_TYPE_UNKNOWN}, {@link #PLAYLIST_TYPE_VOD} or {@link #PLAYLIST_TYPE_EVENT}.
164    */
165   @Documented
166   @Retention(RetentionPolicy.SOURCE)
167   @IntDef({PLAYLIST_TYPE_UNKNOWN, PLAYLIST_TYPE_VOD, PLAYLIST_TYPE_EVENT})
168   public @interface PlaylistType {}
169 
170   public static final int PLAYLIST_TYPE_UNKNOWN = 0;
171   public static final int PLAYLIST_TYPE_VOD = 1;
172   public static final int PLAYLIST_TYPE_EVENT = 2;
173 
174   /**
175    * The type of the playlist. See {@link PlaylistType}.
176    */
177   @PlaylistType public final int playlistType;
178   /**
179    * The start offset in microseconds, as defined by #EXT-X-START.
180    */
181   public final long startOffsetUs;
182   /**
183    * If {@link #hasProgramDateTime} is true, contains the datetime as microseconds since epoch.
184    * Otherwise, contains the aggregated duration of removed segments up to this snapshot of the
185    * playlist.
186    */
187   public final long startTimeUs;
188   /**
189    * Whether the playlist contains the #EXT-X-DISCONTINUITY-SEQUENCE tag.
190    */
191   public final boolean hasDiscontinuitySequence;
192   /**
193    * The discontinuity sequence number of the first media segment in the playlist, as defined by
194    * #EXT-X-DISCONTINUITY-SEQUENCE.
195    */
196   public final int discontinuitySequence;
197   /**
198    * The media sequence number of the first media segment in the playlist, as defined by
199    * #EXT-X-MEDIA-SEQUENCE.
200    */
201   public final long mediaSequence;
202   /**
203    * The compatibility version, as defined by #EXT-X-VERSION.
204    */
205   public final int version;
206   /**
207    * The target duration in microseconds, as defined by #EXT-X-TARGETDURATION.
208    */
209   public final long targetDurationUs;
210   /**
211    * Whether the playlist contains the #EXT-X-ENDLIST tag.
212    */
213   public final boolean hasEndTag;
214   /**
215    * Whether the playlist contains a #EXT-X-PROGRAM-DATE-TIME tag.
216    */
217   public final boolean hasProgramDateTime;
218   /**
219    * Contains the CDM protection schemes used by segments in this playlist. Does not contain any key
220    * acquisition data. Null if none of the segments in the playlist is CDM-encrypted.
221    */
222   @Nullable public final DrmInitData protectionSchemes;
223   /**
224    * The list of segments in the playlist.
225    */
226   public final List<Segment> segments;
227   /**
228    * The total duration of the playlist in microseconds.
229    */
230   public final long durationUs;
231 
232   /**
233    * @param playlistType See {@link #playlistType}.
234    * @param baseUri See {@link #baseUri}.
235    * @param tags See {@link #tags}.
236    * @param startOffsetUs See {@link #startOffsetUs}.
237    * @param startTimeUs See {@link #startTimeUs}.
238    * @param hasDiscontinuitySequence See {@link #hasDiscontinuitySequence}.
239    * @param discontinuitySequence See {@link #discontinuitySequence}.
240    * @param mediaSequence See {@link #mediaSequence}.
241    * @param version See {@link #version}.
242    * @param targetDurationUs See {@link #targetDurationUs}.
243    * @param hasIndependentSegments See {@link #hasIndependentSegments}.
244    * @param hasEndTag See {@link #hasEndTag}.
245    * @param protectionSchemes See {@link #protectionSchemes}.
246    * @param hasProgramDateTime See {@link #hasProgramDateTime}.
247    * @param segments See {@link #segments}.
248    */
HlsMediaPlaylist( @laylistType int playlistType, String baseUri, List<String> tags, long startOffsetUs, long startTimeUs, boolean hasDiscontinuitySequence, int discontinuitySequence, long mediaSequence, int version, long targetDurationUs, boolean hasIndependentSegments, boolean hasEndTag, boolean hasProgramDateTime, @Nullable DrmInitData protectionSchemes, List<Segment> segments)249   public HlsMediaPlaylist(
250       @PlaylistType int playlistType,
251       String baseUri,
252       List<String> tags,
253       long startOffsetUs,
254       long startTimeUs,
255       boolean hasDiscontinuitySequence,
256       int discontinuitySequence,
257       long mediaSequence,
258       int version,
259       long targetDurationUs,
260       boolean hasIndependentSegments,
261       boolean hasEndTag,
262       boolean hasProgramDateTime,
263       @Nullable DrmInitData protectionSchemes,
264       List<Segment> segments) {
265     super(baseUri, tags, hasIndependentSegments);
266     this.playlistType = playlistType;
267     this.startTimeUs = startTimeUs;
268     this.hasDiscontinuitySequence = hasDiscontinuitySequence;
269     this.discontinuitySequence = discontinuitySequence;
270     this.mediaSequence = mediaSequence;
271     this.version = version;
272     this.targetDurationUs = targetDurationUs;
273     this.hasEndTag = hasEndTag;
274     this.hasProgramDateTime = hasProgramDateTime;
275     this.protectionSchemes = protectionSchemes;
276     this.segments = Collections.unmodifiableList(segments);
277     if (!segments.isEmpty()) {
278       Segment last = segments.get(segments.size() - 1);
279       durationUs = last.relativeStartTimeUs + last.durationUs;
280     } else {
281       durationUs = 0;
282     }
283     this.startOffsetUs = startOffsetUs == C.TIME_UNSET ? C.TIME_UNSET
284         : startOffsetUs >= 0 ? startOffsetUs : durationUs + startOffsetUs;
285   }
286 
287   @Override
copy(List<StreamKey> streamKeys)288   public HlsMediaPlaylist copy(List<StreamKey> streamKeys) {
289     return this;
290   }
291 
292   /**
293    * Returns whether this playlist is newer than {@code other}.
294    *
295    * @param other The playlist to compare.
296    * @return Whether this playlist is newer than {@code other}.
297    */
isNewerThan(HlsMediaPlaylist other)298   public boolean isNewerThan(HlsMediaPlaylist other) {
299     if (other == null || mediaSequence > other.mediaSequence) {
300       return true;
301     }
302     if (mediaSequence < other.mediaSequence) {
303       return false;
304     }
305     // The media sequences are equal.
306     int segmentCount = segments.size();
307     int otherSegmentCount = other.segments.size();
308     return segmentCount > otherSegmentCount
309         || (segmentCount == otherSegmentCount && hasEndTag && !other.hasEndTag);
310   }
311 
312   /**
313    * Returns the result of adding the duration of the playlist to its start time.
314    */
getEndTimeUs()315   public long getEndTimeUs() {
316     return startTimeUs + durationUs;
317   }
318 
319   /**
320    * Returns a playlist identical to this one except for the start time, the discontinuity sequence
321    * and {@code hasDiscontinuitySequence} values. The first two are set to the specified values,
322    * {@code hasDiscontinuitySequence} is set to true.
323    *
324    * @param startTimeUs The start time for the returned playlist.
325    * @param discontinuitySequence The discontinuity sequence for the returned playlist.
326    * @return An identical playlist including the provided discontinuity and timing information.
327    */
copyWith(long startTimeUs, int discontinuitySequence)328   public HlsMediaPlaylist copyWith(long startTimeUs, int discontinuitySequence) {
329     return new HlsMediaPlaylist(
330         playlistType,
331         baseUri,
332         tags,
333         startOffsetUs,
334         startTimeUs,
335         /* hasDiscontinuitySequence= */ true,
336         discontinuitySequence,
337         mediaSequence,
338         version,
339         targetDurationUs,
340         hasIndependentSegments,
341         hasEndTag,
342         hasProgramDateTime,
343         protectionSchemes,
344         segments);
345   }
346 
347   /**
348    * Returns a playlist identical to this one except that an end tag is added. If an end tag is
349    * already present then the playlist will return itself.
350    */
copyWithEndTag()351   public HlsMediaPlaylist copyWithEndTag() {
352     if (this.hasEndTag) {
353       return this;
354     }
355     return new HlsMediaPlaylist(
356         playlistType,
357         baseUri,
358         tags,
359         startOffsetUs,
360         startTimeUs,
361         hasDiscontinuitySequence,
362         discontinuitySequence,
363         mediaSequence,
364         version,
365         targetDurationUs,
366         hasIndependentSegments,
367         /* hasEndTag= */ true,
368         hasProgramDateTime,
369         protectionSchemes,
370         segments);
371   }
372 
373 }
374