diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 747cdcd14c..435658a954 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -77,6 +77,8 @@ ([#4145](https://github.com/google/ExoPlayer/issues/4145)). * Preeptively declare an ID3 track in chunkless preparation ([#4016](https://github.com/google/ExoPlayer/issues/4016)). + * Add support for multiple #EXT-X-MAP tags in a media playlist + ([#4164](https://github.com/google/ExoPlayer/issues/4182)). * Fix ClearKey decryption error if the key contains a forward slash ([#4075](https://github.com/google/ExoPlayer/issues/4075)). * Fix crash when switching surface on Huawei P9 Lite diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java index f3cc43b8a1..7271b3b09c 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java @@ -320,7 +320,7 @@ import java.util.List; } DataSpec initDataSpec = null; - Segment initSegment = mediaPlaylist.initializationSegment; + Segment initSegment = segment.initializationSegment; if (initSegment != null) { Uri initSegmentUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, initSegment.url); initDataSpec = new DataSpec(initSegmentUri, initSegment.byterangeOffset, diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloader.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloader.java index e5ed5e48b5..1ede70c666 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloader.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloader.java @@ -94,14 +94,16 @@ public final class HlsDownloader extends SegmentDownloader hlsSegments = mediaPlaylist.segments; for (int i = 0; i < hlsSegments.size(); i++) { - addSegment(segments, mediaPlaylist, hlsSegments.get(i), encryptionKeyUris); + HlsMediaPlaylist.Segment segment = hlsSegments.get(i); + HlsMediaPlaylist.Segment initSegment = segment.initializationSegment; + if (initSegment != null && initSegment != lastInitSegment) { + lastInitSegment = initSegment; + addSegment(segments, mediaPlaylist, initSegment, encryptionKeyUris); + } + addSegment(segments, mediaPlaylist, segment, encryptionKeyUris); } } return segments; diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java index e54caf47b4..094ce56107 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java @@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.hls.playlist; import android.support.annotation.IntDef; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.drm.DrmInitData; import java.lang.annotation.Retention; @@ -38,8 +39,12 @@ public final class HlsMediaPlaylist extends HlsPlaylist { */ public final String url; /** - * The duration of the segment in microseconds, as defined by #EXTINF. + * The media initialization section for this segment, as defined by #EXT-X-MAP. May be null if + * the media playlist does not define a media section for this segment. The same instance is + * used for all segments that share an EXT-X-MAP tag. */ + @Nullable public final Segment initializationSegment; + /** The duration of the segment in microseconds, as defined by #EXTINF. */ public final long durationUs; /** * The number of #EXT-X-DISCONTINUITY tags in the playlist before the segment. @@ -78,11 +83,12 @@ public final class HlsMediaPlaylist extends HlsPlaylist { * @param byterangeLength See {@link #byterangeLength}. */ public Segment(String uri, long byterangeOffset, long byterangeLength) { - this(uri, 0, -1, C.TIME_UNSET, null, null, byterangeOffset, byterangeLength, false); + this(uri, null, 0, -1, C.TIME_UNSET, null, null, byterangeOffset, byterangeLength, false); } /** * @param url See {@link #url}. + * @param initializationSegment See {@link #initializationSegment}. * @param durationUs See {@link #durationUs}. * @param relativeDiscontinuitySequence See {@link #relativeDiscontinuitySequence}. * @param relativeStartTimeUs See {@link #relativeStartTimeUs}. @@ -94,6 +100,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { */ public Segment( String url, + Segment initializationSegment, long durationUs, int relativeDiscontinuitySequence, long relativeStartTimeUs, @@ -103,6 +110,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { long byterangeLength, boolean hasGapTag) { this.url = url; + this.initializationSegment = initializationSegment; this.durationUs = durationUs; this.relativeDiscontinuitySequence = relativeDiscontinuitySequence; this.relativeStartTimeUs = relativeStartTimeUs; @@ -182,10 +190,6 @@ public final class HlsMediaPlaylist extends HlsPlaylist { * encryption. */ public final DrmInitData drmInitData; - /** - * The initialization segment, as defined by #EXT-X-MAP. - */ - public final Segment initializationSegment; /** * The list of segments in the playlist. */ @@ -210,7 +214,6 @@ public final class HlsMediaPlaylist extends HlsPlaylist { * @param hasEndTag See {@link #hasEndTag}. * @param hasProgramDateTime See {@link #hasProgramDateTime}. * @param drmInitData See {@link #drmInitData}. - * @param initializationSegment See {@link #initializationSegment}. * @param segments See {@link #segments}. */ public HlsMediaPlaylist( @@ -228,7 +231,6 @@ public final class HlsMediaPlaylist extends HlsPlaylist { boolean hasEndTag, boolean hasProgramDateTime, DrmInitData drmInitData, - Segment initializationSegment, List segments) { super(baseUri, tags); this.playlistType = playlistType; @@ -242,7 +244,6 @@ public final class HlsMediaPlaylist extends HlsPlaylist { this.hasEndTag = hasEndTag; this.hasProgramDateTime = hasProgramDateTime; this.drmInitData = drmInitData; - this.initializationSegment = initializationSegment; this.segments = Collections.unmodifiableList(segments); if (!segments.isEmpty()) { Segment last = segments.get(segments.size() - 1); @@ -296,9 +297,22 @@ public final class HlsMediaPlaylist extends HlsPlaylist { * @return The playlist. */ public HlsMediaPlaylist copyWith(long startTimeUs, int discontinuitySequence) { - return new HlsMediaPlaylist(playlistType, baseUri, tags, startOffsetUs, startTimeUs, true, - discontinuitySequence, mediaSequence, version, targetDurationUs, hasIndependentSegmentsTag, - hasEndTag, hasProgramDateTime, drmInitData, initializationSegment, segments); + return new HlsMediaPlaylist( + playlistType, + baseUri, + tags, + startOffsetUs, + startTimeUs, + /* hasDiscontinuitySequence= */ true, + discontinuitySequence, + mediaSequence, + version, + targetDurationUs, + hasIndependentSegmentsTag, + hasEndTag, + hasProgramDateTime, + drmInitData, + segments); } /** @@ -311,9 +325,21 @@ public final class HlsMediaPlaylist extends HlsPlaylist { if (this.hasEndTag) { return this; } - return new HlsMediaPlaylist(playlistType, baseUri, tags, startOffsetUs, startTimeUs, - hasDiscontinuitySequence, discontinuitySequence, mediaSequence, version, targetDurationUs, - hasIndependentSegmentsTag, true, hasProgramDateTime, drmInitData, initializationSegment, + return new HlsMediaPlaylist( + playlistType, + baseUri, + tags, + startOffsetUs, + startTimeUs, + hasDiscontinuitySequence, + discontinuitySequence, + mediaSequence, + version, + targetDurationUs, + hasIndependentSegmentsTag, + /* hasEndTag= */ true, + hasProgramDateTime, + drmInitData, segments); } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index 68491f4434..7187bdb0ca 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -474,6 +474,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser segments = playlist.segments; + assertThat(segments.get(0).initializationSegment).isNull(); + assertThat(segments.get(1).initializationSegment) + .isSameAs(segments.get(2).initializationSegment); + assertThat(segments.get(1).initializationSegment.url).isEqualTo("init1.ts"); + assertThat(segments.get(3).initializationSegment.url).isEqualTo("init2.ts"); + } }