From efa714ab4fc3ae85843300d5a93f1c9dfbe22304 Mon Sep 17 00:00:00 2001 From: aquilescanta Date: Fri, 29 Jun 2018 04:28:33 -0700 Subject: [PATCH] Support EXT-X-INDEPENDENT-SEGMENTS in the master playlist From the spec: If the EXT-X-INDEPENDENT-SEGMENTS tag appears in a Master Playlist, it applies to every Media Segment in every Media Playlist in the Master Playlist. ---- This requires propagation of attributes from the master playlist to the media playlists. This CL only includes independent segments, but other inheritable attributes will be supported in following changes. Other inheritable attributes include variable substitution definitions and session keys. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=202628422 --- RELEASENOTES.md | 9 ++-- .../exoplayer2/source/hls/HlsChunkSource.java | 2 +- .../playlist/DefaultHlsPlaylistTracker.java | 3 ++ .../hls/playlist/HlsMasterPlaylist.java | 29 +++++++--- .../source/hls/playlist/HlsMediaPlaylist.java | 53 ++++++++++++++----- .../source/hls/playlist/HlsPlaylist.java | 9 +++- .../hls/playlist/HlsPlaylistParser.java | 16 ++++-- .../playlist/HlsMasterPlaylistParserTest.java | 23 ++++++++ .../playlist/HlsMediaPlaylistParserTest.java | 39 ++++++++++++++ 9 files changed, 155 insertions(+), 28 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index ca6914a763..ad9b18d73f 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -18,9 +18,12 @@ * Error handling: * Allow configuration of the Loader retry delay ([#3370](https://github.com/google/ExoPlayer/issues/3370)). -* HLS: Pass HTTP response headers to `HlsExtractorFactory.createExtractor`. -* DRM: Allow DrmInitData to carry a license server URL - ([#3393](https://github.com/google/ExoPlayer/issues/3393)). +* HLS: + * Pass HTTP response headers to `HlsExtractorFactory.createExtractor`. + * Add support for EXT-X-INDEPENDENT-SEGMENTS in the master playlist. +* DRM: + * Allow DrmInitData to carry a license server URL + ([#3393](https://github.com/google/ExoPlayer/issues/3393)). * Add method to `BandwidthMeter` to return the `TransferListener` used to gather bandwidth information. * Add callback to `VideoListener` to notify of surface size changes. 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 37804b81f4..4a8509d8ac 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 @@ -248,7 +248,7 @@ import java.util.List; return; } HlsMediaPlaylist mediaPlaylist = playlistTracker.getPlaylistSnapshot(selectedUrl); - independentSegments = mediaPlaylist.hasIndependentSegmentsTag; + independentSegments = mediaPlaylist.hasIndependentSegments; updateLiveEdgeTimeUs(mediaPlaylist); diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java index 97a4ecc181..6e877b0321 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java @@ -544,6 +544,9 @@ public final class DefaultHlsPlaylistTracker } private void processLoadedPlaylist(HlsMediaPlaylist loadedPlaylist) { + // Update the loaded playlist with any inheritable information from the master playlist. + loadedPlaylist = loadedPlaylist.copyWithMasterPlaylistInfo(masterPlaylist); + HlsMediaPlaylist oldPlaylist = playlistSnapshot; long currentTimeMs = SystemClock.elapsedRealtime(); lastSnapshotLoadMs = currentTimeMs; diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java index 1a3b1f583b..e5deb5068d 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java @@ -99,11 +99,18 @@ public final class HlsMasterPlaylist extends HlsPlaylist { * @param subtitles See {@link #subtitles}. * @param muxedAudioFormat See {@link #muxedAudioFormat}. * @param muxedCaptionFormats See {@link #muxedCaptionFormats}. + * @param hasIndependentSegments See {@link #hasIndependentSegments}. */ - public HlsMasterPlaylist(String baseUri, List tags, List variants, - List audios, List subtitles, Format muxedAudioFormat, - List muxedCaptionFormats) { - super(baseUri, tags); + public HlsMasterPlaylist( + String baseUri, + List tags, + List variants, + List audios, + List subtitles, + Format muxedAudioFormat, + List muxedCaptionFormats, + boolean hasIndependentSegments) { + super(baseUri, tags, hasIndependentSegments); this.variants = Collections.unmodifiableList(variants); this.audios = Collections.unmodifiableList(audios); this.subtitles = Collections.unmodifiableList(subtitles); @@ -121,7 +128,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist { copyRenditionsList(audios, GROUP_INDEX_AUDIO, streamKeys), copyRenditionsList(subtitles, GROUP_INDEX_SUBTITLE, streamKeys), muxedAudioFormat, - muxedCaptionFormats); + muxedCaptionFormats, + hasIndependentSegments); } /** @@ -133,8 +141,15 @@ public final class HlsMasterPlaylist extends HlsPlaylist { public static HlsMasterPlaylist createSingleVariantMasterPlaylist(String variantUrl) { List variant = Collections.singletonList(HlsUrl.createMediaPlaylistHlsUrl(variantUrl)); List emptyList = Collections.emptyList(); - return new HlsMasterPlaylist(null, Collections.emptyList(), variant, emptyList, - emptyList, null, null); + return new HlsMasterPlaylist( + null, + Collections.emptyList(), + variant, + emptyList, + emptyList, + /* muxedAudioFormat= */ null, + /* muxedCaptionFormats= */ null, + /* hasIndependentSegments= */ false); } private static List copyRenditionsList( 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 d54d433258..93e58c6371 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 @@ -174,10 +174,6 @@ public final class HlsMediaPlaylist extends HlsPlaylist { * The target duration in microseconds, as defined by #EXT-X-TARGETDURATION. */ public final long targetDurationUs; - /** - * Whether the playlist contains the #EXT-X-INDEPENDENT-SEGMENTS tag. - */ - public final boolean hasIndependentSegmentsTag; /** * Whether the playlist contains the #EXT-X-ENDLIST tag. */ @@ -211,7 +207,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { * @param mediaSequence See {@link #mediaSequence}. * @param version See {@link #version}. * @param targetDurationUs See {@link #targetDurationUs}. - * @param hasIndependentSegmentsTag See {@link #hasIndependentSegmentsTag}. + * @param hasIndependentSegments See {@link #hasIndependentSegments}. * @param hasEndTag See {@link #hasEndTag}. * @param hasProgramDateTime See {@link #hasProgramDateTime}. * @param drmInitData See {@link #drmInitData}. @@ -228,12 +224,12 @@ public final class HlsMediaPlaylist extends HlsPlaylist { long mediaSequence, int version, long targetDurationUs, - boolean hasIndependentSegmentsTag, + boolean hasIndependentSegments, boolean hasEndTag, boolean hasProgramDateTime, DrmInitData drmInitData, List segments) { - super(baseUri, tags); + super(baseUri, tags, hasIndependentSegments); this.playlistType = playlistType; this.startTimeUs = startTimeUs; this.hasDiscontinuitySequence = hasDiscontinuitySequence; @@ -241,7 +237,6 @@ public final class HlsMediaPlaylist extends HlsPlaylist { this.mediaSequence = mediaSequence; this.version = version; this.targetDurationUs = targetDurationUs; - this.hasIndependentSegmentsTag = hasIndependentSegmentsTag; this.hasEndTag = hasEndTag; this.hasProgramDateTime = hasProgramDateTime; this.drmInitData = drmInitData; @@ -295,7 +290,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { * * @param startTimeUs The start time for the returned playlist. * @param discontinuitySequence The discontinuity sequence for the returned playlist. - * @return The playlist. + * @return An identical playlist including the provided discontinuity and timing information. */ public HlsMediaPlaylist copyWith(long startTimeUs, int discontinuitySequence) { return new HlsMediaPlaylist( @@ -309,7 +304,41 @@ public final class HlsMediaPlaylist extends HlsPlaylist { mediaSequence, version, targetDurationUs, - hasIndependentSegmentsTag, + hasIndependentSegments, + hasEndTag, + hasProgramDateTime, + drmInitData, + segments); + } + + /** + * Returns a playlist identical to this one, except for adding any inheritable attributes from the + * provided {@link HlsMasterPlaylist}. + * + *

The inheritable attributes are: + * + *

    + *
  • {@link #hasIndependentSegments}. + *
+ * + * @return An identical playlist including the inheritable attributes from {@code masterPlaylist}. + */ + public HlsMediaPlaylist copyWithMasterPlaylistInfo(HlsMasterPlaylist masterPlaylist) { + if (hasIndependentSegments || !masterPlaylist.hasIndependentSegments) { + return this; + } + return new HlsMediaPlaylist( + playlistType, + baseUri, + tags, + startOffsetUs, + startTimeUs, + hasDiscontinuitySequence, + discontinuitySequence, + mediaSequence, + version, + targetDurationUs, + hasIndependentSegments || masterPlaylist.hasIndependentSegments, hasEndTag, hasProgramDateTime, drmInitData, @@ -319,8 +348,6 @@ public final class HlsMediaPlaylist extends HlsPlaylist { /** * Returns a playlist identical to this one except that an end tag is added. If an end tag is * already present then the playlist will return itself. - * - * @return The playlist. */ public HlsMediaPlaylist copyWithEndTag() { if (this.hasEndTag) { @@ -337,7 +364,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist { mediaSequence, version, targetDurationUs, - hasIndependentSegmentsTag, + hasIndependentSegments, /* hasEndTag= */ true, hasProgramDateTime, drmInitData, diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylist.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylist.java index 1b21d3d13c..9cec1cd33b 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylist.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylist.java @@ -30,14 +30,21 @@ public abstract class HlsPlaylist implements FilterableManifest { * The list of tags in the playlist. */ public final List tags; + /** + * Whether the media is formed of independent segments, as defined by the + * #EXT-X-INDEPENDENT-SEGMENTS tag. + */ + public final boolean hasIndependentSegments; /** * @param baseUri See {@link #baseUri}. * @param tags See {@link #tags}. + * @param hasIndependentSegments See {@link #hasIndependentSegments}. */ - protected HlsPlaylist(String baseUri, List tags) { + protected HlsPlaylist(String baseUri, List tags, boolean hasIndependentSegments) { this.baseUri = baseUri; this.tags = Collections.unmodifiableList(tags); + this.hasIndependentSegments = hasIndependentSegments; } } 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 7187bdb0ca..2a67108bf9 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 @@ -217,6 +217,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser muxedCaptionFormats = null; boolean noClosedCaptions = false; + boolean hasIndependentSegmentsTag = false; String line; while (iterator.hasNext()) { @@ -227,7 +228,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser