Commit 65c44453 by tonihei Committed by Ian Baker

Rename HLS master playlist to multivariant playlist

The spec renamed this type of playlist in the latest revision
to use a more inclusive technical term (see
https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis-10)

PiperOrigin-RevId: 417560274
parent e3548f26
Showing with 202 additions and 161 deletions
...@@ -237,11 +237,11 @@ ...@@ -237,11 +237,11 @@
"uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8" "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
}, },
{ {
"name": "Apple master playlist advanced (TS)", "name": "Apple multivariant playlist advanced (TS)",
"uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/master.m3u8" "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/master.m3u8"
}, },
{ {
"name": "Apple master playlist advanced (FMP4)", "name": "Apple multivariant playlist advanced (FMP4)",
"uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8" "uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_fmp4/master.m3u8"
}, },
{ {
......
...@@ -90,7 +90,7 @@ For more information, see the ...@@ -90,7 +90,7 @@ For more information, see the
A file that defines the structure and location of media in A file that defines the structure and location of media in
[adaptive streaming](#adaptive-streaming) protocols. Examples include [adaptive streaming](#adaptive-streaming) protocols. Examples include
[DASH](#dash) [MPD](#mpd) files, [HLS](#hls) master playlist files and [DASH](#dash) [MPD](#mpd) files, [HLS](#hls) multivariant playlist files and
[Smooth Streaming](#smooth-streaming) manifest files. Not to be confused with an [Smooth Streaming](#smooth-streaming) manifest files. Not to be confused with an
AndroidManifest XML file. AndroidManifest XML file.
......
...@@ -30,8 +30,8 @@ If your URI doesn't end with `.m3u8`, you can pass `MimeTypes.APPLICATION_M3U8` ...@@ -30,8 +30,8 @@ If your URI doesn't end with `.m3u8`, you can pass `MimeTypes.APPLICATION_M3U8`
to `setMimeType` of `MediaItem.Builder` to explicitly indicate the type of the to `setMimeType` of `MediaItem.Builder` to explicitly indicate the type of the
content. content.
The URI of the media item may point to either a media playlist or a master The URI of the media item may point to either a media playlist or a multivariant
playlist. If the URI points to a master playlist that declares multiple playlist. If the URI points to a multivariant playlist that declares multiple
`#EXT-X-STREAM-INF` tags then ExoPlayer will automatically adapt between `#EXT-X-STREAM-INF` tags then ExoPlayer will automatically adapt between
variants, taking into account both available bandwidth and device capabilities. variants, taking into account both available bandwidth and device capabilities.
...@@ -89,17 +89,18 @@ app's needs. See the [Customization page][] for examples. ...@@ -89,17 +89,18 @@ app's needs. See the [Customization page][] for examples.
### Disabling chunkless preparation ### ### Disabling chunkless preparation ###
By default, ExoPlayer will use chunkless preparation. This means that ExoPlayer By default, ExoPlayer will use chunkless preparation. This means that ExoPlayer
will only use the information in the master playlist to prepare the stream, will only use the information in the multivariant playlist to prepare the
which works if the `#EXT-X-STREAM-INF` tags contain the `CODECS` attribute. stream, which works if the `#EXT-X-STREAM-INF` tags contain the `CODECS`
attribute.
You may need to disable this feature if your media segments contain muxed You may need to disable this feature if your media segments contain muxed
closed-caption tracks that are not declared in the master playlist with a closed-caption tracks that are not declared in the multivariant playlist with a
`#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS` tag. Otherwise, these closed-caption tracks `#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS` tag. Otherwise, these closed-caption tracks
won't be detected and played. You can disable chunkless preparation in the won't be detected and played. You can disable chunkless preparation in the
`HlsMediaSource.Factory` as shown in the following snippet. Note that this `HlsMediaSource.Factory` as shown in the following snippet. Note that this
will increase start up time as ExoPlayer needs to download a media segment to will increase start up time as ExoPlayer needs to download a media segment to
discover these additional tracks and it is preferable to declare the discover these additional tracks and it is preferable to declare the
closed-caption tracks in the master playlist instead. closed-caption tracks in the multivariant playlist instead.
~~~ ~~~
HlsMediaSource hlsMediaSource = HlsMediaSource hlsMediaSource =
new HlsMediaSource.Factory(dataSourceFactory) new HlsMediaSource.Factory(dataSourceFactory)
...@@ -119,7 +120,7 @@ ExoPlayer][] for a full explanation. The main points are: ...@@ -119,7 +120,7 @@ ExoPlayer][] for a full explanation. The main points are:
segments. segments.
* Use the `#EXT-X-INDEPENDENT-SEGMENTS` tag. * Use the `#EXT-X-INDEPENDENT-SEGMENTS` tag.
* Prefer demuxed streams, as opposed to files that include both video and audio. * Prefer demuxed streams, as opposed to files that include both video and audio.
* Include all information you can in the Master Playlist. * Include all information you can in the Multivariant Playlist.
The following guidelines apply specifically for live streams: The following guidelines apply specifically for live streams:
......
...@@ -631,7 +631,8 @@ public final class Format implements Bundleable { ...@@ -631,7 +631,8 @@ public final class Format implements Bundleable {
* <ul> * <ul>
* <li>DASH representations: Always {@link Format#NO_VALUE}. * <li>DASH representations: Always {@link Format#NO_VALUE}.
* <li>HLS variants: The {@code AVERAGE-BANDWIDTH} attribute defined on the corresponding {@code * <li>HLS variants: The {@code AVERAGE-BANDWIDTH} attribute defined on the corresponding {@code
* EXT-X-STREAM-INF} tag in the master playlist, or {@link Format#NO_VALUE} if not present. * EXT-X-STREAM-INF} tag in the multivariant playlist, or {@link Format#NO_VALUE} if not
* present.
* <li>SmoothStreaming track elements: The {@code Bitrate} attribute defined on the * <li>SmoothStreaming track elements: The {@code Bitrate} attribute defined on the
* corresponding {@code TrackElement} in the manifest, or {@link Format#NO_VALUE} if not * corresponding {@code TrackElement} in the manifest, or {@link Format#NO_VALUE} if not
* present. * present.
......
...@@ -40,20 +40,20 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract ...@@ -40,20 +40,20 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract
private static final PositionHolder POSITION_HOLDER = new PositionHolder(); private static final PositionHolder POSITION_HOLDER = new PositionHolder();
@VisibleForTesting /* package */ final Extractor extractor; @VisibleForTesting /* package */ final Extractor extractor;
private final Format masterPlaylistFormat; private final Format multivariantPlaylistFormat;
private final TimestampAdjuster timestampAdjuster; private final TimestampAdjuster timestampAdjuster;
/** /**
* Creates a new instance. * Creates a new instance.
* *
* @param extractor The underlying {@link Extractor}. * @param extractor The underlying {@link Extractor}.
* @param masterPlaylistFormat The {@link Format} obtained from the master playlist. * @param multivariantPlaylistFormat The {@link Format} obtained from the multivariant playlist.
* @param timestampAdjuster A {@link TimestampAdjuster} to adjust sample timestamps. * @param timestampAdjuster A {@link TimestampAdjuster} to adjust sample timestamps.
*/ */
public BundledHlsMediaChunkExtractor( public BundledHlsMediaChunkExtractor(
Extractor extractor, Format masterPlaylistFormat, TimestampAdjuster timestampAdjuster) { Extractor extractor, Format multivariantPlaylistFormat, TimestampAdjuster timestampAdjuster) {
this.extractor = extractor; this.extractor = extractor;
this.masterPlaylistFormat = masterPlaylistFormat; this.multivariantPlaylistFormat = multivariantPlaylistFormat;
this.timestampAdjuster = timestampAdjuster; this.timestampAdjuster = timestampAdjuster;
} }
...@@ -85,7 +85,8 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract ...@@ -85,7 +85,8 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract
Assertions.checkState(!isReusable()); Assertions.checkState(!isReusable());
Extractor newExtractorInstance; Extractor newExtractorInstance;
if (extractor instanceof WebvttExtractor) { if (extractor instanceof WebvttExtractor) {
newExtractorInstance = new WebvttExtractor(masterPlaylistFormat.language, timestampAdjuster); newExtractorInstance =
new WebvttExtractor(multivariantPlaylistFormat.language, timestampAdjuster);
} else if (extractor instanceof AdtsExtractor) { } else if (extractor instanceof AdtsExtractor) {
newExtractorInstance = new AdtsExtractor(); newExtractorInstance = new AdtsExtractor();
} else if (extractor instanceof Ac3Extractor) { } else if (extractor instanceof Ac3Extractor) {
...@@ -99,7 +100,7 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract ...@@ -99,7 +100,7 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract
"Unexpected extractor type for recreation: " + extractor.getClass().getSimpleName()); "Unexpected extractor type for recreation: " + extractor.getClass().getSimpleName());
} }
return new BundledHlsMediaChunkExtractor( return new BundledHlsMediaChunkExtractor(
newExtractorInstance, masterPlaylistFormat, timestampAdjuster); newExtractorInstance, multivariantPlaylistFormat, timestampAdjuster);
} }
@Override @Override
......
...@@ -79,8 +79,9 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory { ...@@ -79,8 +79,9 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
* DefaultTsPayloadReaderFactory} instances. Other flags may be added on top of {@code * DefaultTsPayloadReaderFactory} instances. Other flags may be added on top of {@code
* payloadReaderFactoryFlags} when creating {@link DefaultTsPayloadReaderFactory}. * payloadReaderFactoryFlags} when creating {@link DefaultTsPayloadReaderFactory}.
* @param exposeCea608WhenMissingDeclarations Whether created {@link TsExtractor} instances should * @param exposeCea608WhenMissingDeclarations Whether created {@link TsExtractor} instances should
* expose a CEA-608 track should the master playlist contain no Closed Captions declarations. * expose a CEA-608 track should the multivariant playlist contain no Closed Captions
* If the master playlist contains any Closed Captions declarations, this flag is ignored. * declarations. If the multivariant playlist contains any Closed Captions declarations, this
* flag is ignored.
*/ */
public DefaultHlsExtractorFactory( public DefaultHlsExtractorFactory(
int payloadReaderFactoryFlags, boolean exposeCea608WhenMissingDeclarations) { int payloadReaderFactoryFlags, boolean exposeCea608WhenMissingDeclarations) {
......
...@@ -154,7 +154,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -154,7 +154,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* {@link HlsChunkSource}s are used for a single playback, they should all share the same * {@link HlsChunkSource}s are used for a single playback, they should all share the same
* provider. * provider.
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption * @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
* information is available in the master playlist. * information is available in the multivariant playlist.
*/ */
public HlsChunkSource( public HlsChunkSource(
HlsExtractorFactory extractorFactory, HlsExtractorFactory extractorFactory,
...@@ -877,8 +877,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ...@@ -877,8 +877,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public InitializationTrackSelection(TrackGroup group, int[] tracks) { public InitializationTrackSelection(TrackGroup group, int[] tracks) {
super(group, tracks); super(group, tracks);
// The initially selected index corresponds to the first EXT-X-STREAMINF tag in the master // The initially selected index corresponds to the first EXT-X-STREAMINF tag in the
// playlist. // multivariant playlist.
selectedIndex = indexOf(group.getFormat(tracks[0])); selectedIndex = indexOf(group.getFormat(tracks[0]));
} }
......
...@@ -38,7 +38,7 @@ public interface HlsExtractorFactory { ...@@ -38,7 +38,7 @@ public interface HlsExtractorFactory {
* @param uri The URI of the media chunk. * @param uri The URI of the media chunk.
* @param format A {@link Format} associated with the chunk to extract. * @param format A {@link Format} associated with the chunk to extract.
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption * @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
* information is available in the master playlist. * information is available in the multivariant playlist.
* @param timestampAdjuster Adjuster corresponding to the provided discontinuity sequence number. * @param timestampAdjuster Adjuster corresponding to the provided discontinuity sequence number.
* @param responseHeaders The HTTP response headers associated with the media segment or * @param responseHeaders The HTTP response headers associated with the media segment or
* initialization section to extract. * initialization section to extract.
......
...@@ -17,21 +17,42 @@ package com.google.android.exoplayer2.source.hls; ...@@ -17,21 +17,42 @@ package com.google.android.exoplayer2.source.hls;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist;
/** Holds a master playlist along with a snapshot of one of its media playlists. */ /** Holds a multivariant playlist along with a snapshot of one of its media playlists. */
public final class HlsManifest { public final class HlsManifest {
/** The master playlist of an HLS stream. */ /** @deprecated Use {@link #multivariantPlaylist} instead. */
@Deprecated
@SuppressWarnings("deprecation") // Keeping deprecated field with deprecated class.
public final HlsMasterPlaylist masterPlaylist; public final HlsMasterPlaylist masterPlaylist;
/** A snapshot of a media playlist referred to by {@link #masterPlaylist}. */ /** The multivariant playlist of an HLS stream. */
public final HlsMultivariantPlaylist multivariantPlaylist;
/** A snapshot of a media playlist referred to by {@link #multivariantPlaylist}. */
public final HlsMediaPlaylist mediaPlaylist; public final HlsMediaPlaylist mediaPlaylist;
/** /**
* @param masterPlaylist The master playlist. * @param multivariantPlaylist The multivariant playlist.
* @param mediaPlaylist The media playlist. * @param mediaPlaylist The media playlist.
*/ */
HlsManifest(HlsMasterPlaylist masterPlaylist, HlsMediaPlaylist mediaPlaylist) { @SuppressWarnings("deprecation") // Intentionally creating deprecated hlsMasterPlaylist field.
this.masterPlaylist = masterPlaylist; /* package */ HlsManifest(
HlsMultivariantPlaylist multivariantPlaylist, HlsMediaPlaylist mediaPlaylist) {
this.multivariantPlaylist = multivariantPlaylist;
this.mediaPlaylist = mediaPlaylist; this.mediaPlaylist = mediaPlaylist;
this.masterPlaylist =
new HlsMasterPlaylist(
multivariantPlaylist.baseUri,
multivariantPlaylist.tags,
multivariantPlaylist.variants,
multivariantPlaylist.videos,
multivariantPlaylist.audios,
multivariantPlaylist.subtitles,
multivariantPlaylist.closedCaptions,
multivariantPlaylist.muxedAudioFormat,
multivariantPlaylist.muxedCaptionFormats,
multivariantPlaylist.hasIndependentSegments,
multivariantPlaylist.variableDefinitions,
multivariantPlaylist.sessionKeyDrmInitData);
} }
} }
...@@ -64,7 +64,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -64,7 +64,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
* @param segmentBaseHolder The segment holder. * @param segmentBaseHolder The segment holder.
* @param playlistUrl The url of the playlist from which this chunk was obtained. * @param playlistUrl The url of the playlist from which this chunk was obtained.
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption * @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
* information is available in the master playlist. * information is available in the multivariant playlist.
* @param trackSelectionReason See {@link #trackSelectionReason}. * @param trackSelectionReason See {@link #trackSelectionReason}.
* @param trackSelectionData See {@link #trackSelectionData}. * @param trackSelectionData See {@link #trackSelectionData}.
* @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster. * @param isMasterTimestampSource True if the chunk can initialize the timestamp adjuster.
......
...@@ -238,7 +238,8 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -238,7 +238,8 @@ public final class HlsMediaSource extends BaseMediaSource
/** /**
* Sets whether chunkless preparation is allowed. If true, preparation without chunk downloads * Sets whether chunkless preparation is allowed. If true, preparation without chunk downloads
* will be enabled for streams that provide sufficient information in their master playlist. * will be enabled for streams that provide sufficient information in their multivariant
* playlist.
* *
* @param allowChunklessPreparation Whether chunkless preparation is allowed. * @param allowChunklessPreparation Whether chunkless preparation is allowed.
* @return This factory, for convenience. * @return This factory, for convenience.
...@@ -273,10 +274,10 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -273,10 +274,10 @@ public final class HlsMediaSource extends BaseMediaSource
} }
/** /**
* Sets whether to use #EXT-X-SESSION-KEY tags provided in the master playlist. If enabled, it's * Sets whether to use #EXT-X-SESSION-KEY tags provided in the multivariant playlist. If
* assumed that any single session key declared in the master playlist can be used to obtain all * enabled, it's assumed that any single session key declared in the multivariant playlist can
* of the keys required for playback. For media where this is not true, this option should not * be used to obtain all of the keys required for playback. For media where this is not true,
* be enabled. * this option should not be enabled.
* *
* @param useSessionKeys Whether to use #EXT-X-SESSION-KEY tags. * @param useSessionKeys Whether to use #EXT-X-SESSION-KEY tags.
* @return This factory, for convenience. * @return This factory, for convenience.
...@@ -524,9 +525,9 @@ public final class HlsMediaSource extends BaseMediaSource ...@@ -524,9 +525,9 @@ public final class HlsMediaSource extends BaseMediaSource
|| mediaPlaylist.playlistType == HlsMediaPlaylist.PLAYLIST_TYPE_VOD || mediaPlaylist.playlistType == HlsMediaPlaylist.PLAYLIST_TYPE_VOD
? windowStartTimeMs ? windowStartTimeMs
: C.TIME_UNSET; : C.TIME_UNSET;
// The master playlist is non-null because the first playlist has been fetched by now. // The multivariant playlist is non-null because the first playlist has been fetched by now.
HlsManifest manifest = HlsManifest manifest =
new HlsManifest(checkNotNull(playlistTracker.getMasterPlaylist()), mediaPlaylist); new HlsManifest(checkNotNull(playlistTracker.getMultivariantPlaylist()), mediaPlaylist);
SinglePeriodTimeline timeline = SinglePeriodTimeline timeline =
playlistTracker.isLive() playlistTracker.isLive()
? createTimelineForLive( ? createTimelineForLive(
......
...@@ -103,7 +103,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -103,7 +103,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
* Called when the wrapper has been prepared. * Called when the wrapper has been prepared.
* *
* <p>Note: This method will be called on a later handler loop than the one on which either * <p>Note: This method will be called on a later handler loop than the one on which either
* {@link #prepareWithMasterPlaylistInfo} or {@link #continuePreparing} are invoked. * {@link #prepareWithMultivariantPlaylistInfo} or {@link #continuePreparing} are invoked.
*/ */
void onPrepared(); void onPrepared();
...@@ -197,7 +197,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -197,7 +197,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
* stream's {@link DrmInitData} will be overridden. * stream's {@link DrmInitData} will be overridden.
* @param allocator An {@link Allocator} from which to obtain media buffer allocations. * @param allocator An {@link Allocator} from which to obtain media buffer allocations.
* @param positionUs The position from which to start loading media. * @param positionUs The position from which to start loading media.
* @param muxedAudioFormat Optional muxed audio {@link Format} as defined by the master playlist. * @param muxedAudioFormat Optional muxed audio {@link Format} as defined by the multivariant
* playlist.
* @param drmSessionManager The {@link DrmSessionManager} to acquire {@link DrmSession * @param drmSessionManager The {@link DrmSessionManager} to acquire {@link DrmSession
* DrmSessions} with. * DrmSessions} with.
* @param drmEventDispatcher A dispatcher to notify of {@link DrmSessionEventListener} events. * @param drmEventDispatcher A dispatcher to notify of {@link DrmSessionEventListener} events.
...@@ -261,7 +262,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -261,7 +262,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
} }
/** /**
* Prepares the sample stream wrapper with master playlist information. * Prepares the sample stream wrapper with multivariant playlist information.
* *
* @param trackGroups The {@link TrackGroup TrackGroups} to expose through {@link * @param trackGroups The {@link TrackGroup TrackGroups} to expose through {@link
* #getTrackGroups()}. * #getTrackGroups()}.
...@@ -269,7 +270,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -269,7 +270,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
* @param optionalTrackGroupsIndices The indices of any {@code trackGroups} that should not * @param optionalTrackGroupsIndices The indices of any {@code trackGroups} that should not
* trigger a failure if not found in the media playlist's segments. * trigger a failure if not found in the media playlist's segments.
*/ */
public void prepareWithMasterPlaylistInfo( public void prepareWithMultivariantPlaylistInfo(
TrackGroup[] trackGroups, int primaryTrackGroupIndex, int... optionalTrackGroupsIndices) { TrackGroup[] trackGroups, int primaryTrackGroupIndex, int... optionalTrackGroupsIndices) {
this.trackGroups = createTrackGroupArrayWithDrmInfo(trackGroups); this.trackGroups = createTrackGroupArrayWithDrmInfo(trackGroups);
optionalTrackGroups = new HashSet<>(); optionalTrackGroups = new HashSet<>();
...@@ -1298,8 +1299,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -1298,8 +1299,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
} }
} }
if (trackGroups != null) { if (trackGroups != null) {
// The track groups were created with master playlist information. They only need to be mapped // The track groups were created with multivariant playlist information. They only need to be
// to a sample queue. // mapped to a sample queue.
mapSampleQueuesToMatchTrackGroups(); mapSampleQueuesToMatchTrackGroups();
} else { } else {
// Tracks are created using media segment information. // Tracks are created using media segment information.
...@@ -1334,18 +1335,18 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -1334,18 +1335,18 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
* Builds tracks that are exposed by this {@link HlsSampleStreamWrapper} instance, as well as * Builds tracks that are exposed by this {@link HlsSampleStreamWrapper} instance, as well as
* internal data-structures required for operation. * internal data-structures required for operation.
* *
* <p>Tracks in HLS are complicated. A HLS master playlist contains a number of "variants". Each * <p>Tracks in HLS are complicated. A HLS multivariant playlist contains a number of "variants".
* variant stream typically contains muxed video, audio and (possibly) additional audio, metadata * Each variant stream typically contains muxed video, audio and (possibly) additional audio,
* and caption tracks. We wish to allow the user to select between an adaptive track that spans * metadata and caption tracks. We wish to allow the user to select between an adaptive track that
* all variants, as well as each individual variant. If multiple audio tracks are present within * spans all variants, as well as each individual variant. If multiple audio tracks are present
* each variant then we wish to allow the user to select between those also. * within each variant then we wish to allow the user to select between those also.
* *
* <p>To do this, tracks are constructed as follows. The {@link HlsChunkSource} exposes (N+1) * <p>To do this, tracks are constructed as follows. The {@link HlsChunkSource} exposes (N+1)
* tracks, where N is the number of variants defined in the HLS master playlist. These consist of * tracks, where N is the number of variants defined in the HLS multivariant playlist. These
* one adaptive track defined to span all variants and a track for each individual variant. The * consist of one adaptive track defined to span all variants and a track for each individual
* adaptive track is initially selected. The extractor is then prepared to discover the tracks * variant. The adaptive track is initially selected. The extractor is then prepared to discover
* inside of each variant stream. The two sets of tracks are then combined by this method to * the tracks inside of each variant stream. The two sets of tracks are then combined by this
* create a third set, which is the set exposed by this {@link HlsSampleStreamWrapper}: * method to create a third set, which is the set exposed by this {@link HlsSampleStreamWrapper}:
* *
* <ul> * <ul>
* <li>The extractor tracks are inspected to infer a "primary" track type. If a video track is * <li>The extractor tracks are inspected to infer a "primary" track type. If a video track is
...@@ -1518,14 +1519,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -1518,14 +1519,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
} }
/** /**
* Derives a track sample format from the corresponding format in the master playlist, and a * Derives a track sample format from the corresponding format in the multivariant playlist, and a
* sample format that may have been obtained from a chunk belonging to a different track in the * sample format that may have been obtained from a chunk belonging to a different track in the
* same track group. * same track group.
* *
* <p>Note: Since the sample format may have been obtained from a chunk belonging to a different * <p>Note: Since the sample format may have been obtained from a chunk belonging to a different
* track, it should not be used as a source for data that may vary between tracks. * track, it should not be used as a source for data that may vary between tracks.
* *
* @param playlistFormat The format information obtained from the master playlist. * @param playlistFormat The format information obtained from the multivariant playlist.
* @param sampleFormat The format information obtained from samples within a chunk. The chunk may * @param sampleFormat The format information obtained from samples within a chunk. The chunk may
* belong to a different track in the same track group. * belong to a different track in the same track group.
* @param propagateBitrates Whether the bitrates from the playlist format should be included in * @param propagateBitrates Whether the bitrates from the playlist format should be included in
......
...@@ -19,8 +19,8 @@ import android.net.Uri; ...@@ -19,8 +19,8 @@ import android.net.Uri;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.offline.SegmentDownloader; import com.google.android.exoplayer2.offline.SegmentDownloader;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser; import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
...@@ -45,14 +45,14 @@ import java.util.concurrent.Executor; ...@@ -45,14 +45,14 @@ import java.util.concurrent.Executor;
* new CacheDataSource.Factory() * new CacheDataSource.Factory()
* .setCache(cache) * .setCache(cache)
* .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory()); * .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory());
* // Create a downloader for the first variant in a master playlist. * // Create a downloader for the first variant in a multivariant playlist.
* HlsDownloader hlsDownloader = * HlsDownloader hlsDownloader =
* new HlsDownloader( * new HlsDownloader(
* new MediaItem.Builder() * new MediaItem.Builder()
* .setUri(playlistUri) * .setUri(playlistUri)
* .setStreamKeys( * .setStreamKeys(
* Collections.singletonList( * Collections.singletonList(
* new StreamKey(HlsMasterPlaylist.GROUP_INDEX_VARIANT, 0))) * new StreamKey(HlsMultivariantPlaylist.GROUP_INDEX_VARIANT, 0)))
* .build(), * .build(),
* Collections.singletonList(); * Collections.singletonList();
* // Perform the download. * // Perform the download.
...@@ -113,9 +113,9 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> { ...@@ -113,9 +113,9 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> {
protected List<Segment> getSegments(DataSource dataSource, HlsPlaylist playlist, boolean removing) protected List<Segment> getSegments(DataSource dataSource, HlsPlaylist playlist, boolean removing)
throws IOException, InterruptedException { throws IOException, InterruptedException {
ArrayList<DataSpec> mediaPlaylistDataSpecs = new ArrayList<>(); ArrayList<DataSpec> mediaPlaylistDataSpecs = new ArrayList<>();
if (playlist instanceof HlsMasterPlaylist) { if (playlist instanceof HlsMultivariantPlaylist) {
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist; HlsMultivariantPlaylist multivariantPlaylist = (HlsMultivariantPlaylist) playlist;
addMediaPlaylistDataSpecs(masterPlaylist.mediaPlaylistUrls, mediaPlaylistDataSpecs); addMediaPlaylistDataSpecs(multivariantPlaylist.mediaPlaylistUrls, mediaPlaylistDataSpecs);
} else { } else {
mediaPlaylistDataSpecs.add( mediaPlaylistDataSpecs.add(
SegmentDownloader.getCompressibleDataSpec(Uri.parse(playlist.baseUri))); SegmentDownloader.getCompressibleDataSpec(Uri.parse(playlist.baseUri)));
......
...@@ -28,7 +28,8 @@ public final class DefaultHlsPlaylistParserFactory implements HlsPlaylistParserF ...@@ -28,7 +28,8 @@ public final class DefaultHlsPlaylistParserFactory implements HlsPlaylistParserF
@Override @Override
public ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser( public ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser(
HlsMasterPlaylist masterPlaylist, @Nullable HlsMediaPlaylist previousMediaPlaylist) { HlsMultivariantPlaylist multivariantPlaylist,
return new HlsPlaylistParser(masterPlaylist, previousMediaPlaylist); @Nullable HlsMediaPlaylist previousMediaPlaylist) {
return new HlsPlaylistParser(multivariantPlaylist, previousMediaPlaylist);
} }
} }
...@@ -29,10 +29,10 @@ import com.google.android.exoplayer2.source.LoadEventInfo; ...@@ -29,10 +29,10 @@ import com.google.android.exoplayer2.source.LoadEventInfo;
import com.google.android.exoplayer2.source.MediaLoadData; import com.google.android.exoplayer2.source.MediaLoadData;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.hls.HlsDataSourceFactory; import com.google.android.exoplayer2.source.hls.HlsDataSourceFactory;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Part; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Part;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.RenditionReport; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.RenditionReport;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Variant;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy; import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
...@@ -72,7 +72,7 @@ public final class DefaultHlsPlaylistTracker ...@@ -72,7 +72,7 @@ public final class DefaultHlsPlaylistTracker
@Nullable private Loader initialPlaylistLoader; @Nullable private Loader initialPlaylistLoader;
@Nullable private Handler playlistRefreshHandler; @Nullable private Handler playlistRefreshHandler;
@Nullable private PrimaryPlaylistListener primaryPlaylistListener; @Nullable private PrimaryPlaylistListener primaryPlaylistListener;
@Nullable private HlsMasterPlaylist masterPlaylist; @Nullable private HlsMultivariantPlaylist multivariantPlaylist;
@Nullable private Uri primaryMediaPlaylistUrl; @Nullable private Uri primaryMediaPlaylistUrl;
@Nullable private HlsMediaPlaylist primaryMediaPlaylistSnapshot; @Nullable private HlsMediaPlaylist primaryMediaPlaylistSnapshot;
private boolean isLive; private boolean isLive;
...@@ -131,30 +131,33 @@ public final class DefaultHlsPlaylistTracker ...@@ -131,30 +131,33 @@ public final class DefaultHlsPlaylistTracker
this.playlistRefreshHandler = Util.createHandlerForCurrentLooper(); this.playlistRefreshHandler = Util.createHandlerForCurrentLooper();
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
this.primaryPlaylistListener = primaryPlaylistListener; this.primaryPlaylistListener = primaryPlaylistListener;
ParsingLoadable<HlsPlaylist> masterPlaylistLoadable = ParsingLoadable<HlsPlaylist> multivariantPlaylistLoadable =
new ParsingLoadable<>( new ParsingLoadable<>(
dataSourceFactory.createDataSource(C.DATA_TYPE_MANIFEST), dataSourceFactory.createDataSource(C.DATA_TYPE_MANIFEST),
initialPlaylistUri, initialPlaylistUri,
C.DATA_TYPE_MANIFEST, C.DATA_TYPE_MANIFEST,
playlistParserFactory.createPlaylistParser()); playlistParserFactory.createPlaylistParser());
Assertions.checkState(initialPlaylistLoader == null); Assertions.checkState(initialPlaylistLoader == null);
initialPlaylistLoader = new Loader("DefaultHlsPlaylistTracker:MasterPlaylist"); initialPlaylistLoader = new Loader("DefaultHlsPlaylistTracker:MultivariantPlaylist");
long elapsedRealtime = long elapsedRealtime =
initialPlaylistLoader.startLoading( initialPlaylistLoader.startLoading(
masterPlaylistLoadable, multivariantPlaylistLoadable,
this, this,
loadErrorHandlingPolicy.getMinimumLoadableRetryCount(masterPlaylistLoadable.type)); loadErrorHandlingPolicy.getMinimumLoadableRetryCount(
multivariantPlaylistLoadable.type));
eventDispatcher.loadStarted( eventDispatcher.loadStarted(
new LoadEventInfo( new LoadEventInfo(
masterPlaylistLoadable.loadTaskId, masterPlaylistLoadable.dataSpec, elapsedRealtime), multivariantPlaylistLoadable.loadTaskId,
masterPlaylistLoadable.type); multivariantPlaylistLoadable.dataSpec,
elapsedRealtime),
multivariantPlaylistLoadable.type);
} }
@Override @Override
public void stop() { public void stop() {
primaryMediaPlaylistUrl = null; primaryMediaPlaylistUrl = null;
primaryMediaPlaylistSnapshot = null; primaryMediaPlaylistSnapshot = null;
masterPlaylist = null; multivariantPlaylist = null;
initialStartTimeUs = C.TIME_UNSET; initialStartTimeUs = C.TIME_UNSET;
initialPlaylistLoader.release(); initialPlaylistLoader.release();
initialPlaylistLoader = null; initialPlaylistLoader = null;
...@@ -179,8 +182,8 @@ public final class DefaultHlsPlaylistTracker ...@@ -179,8 +182,8 @@ public final class DefaultHlsPlaylistTracker
@Override @Override
@Nullable @Nullable
public HlsMasterPlaylist getMasterPlaylist() { public HlsMultivariantPlaylist getMultivariantPlaylist() {
return masterPlaylist; return multivariantPlaylist;
} }
@Override @Override
...@@ -243,18 +246,19 @@ public final class DefaultHlsPlaylistTracker ...@@ -243,18 +246,19 @@ public final class DefaultHlsPlaylistTracker
public void onLoadCompleted( public void onLoadCompleted(
ParsingLoadable<HlsPlaylist> loadable, long elapsedRealtimeMs, long loadDurationMs) { ParsingLoadable<HlsPlaylist> loadable, long elapsedRealtimeMs, long loadDurationMs) {
HlsPlaylist result = loadable.getResult(); HlsPlaylist result = loadable.getResult();
HlsMasterPlaylist masterPlaylist; HlsMultivariantPlaylist multivariantPlaylist;
boolean isMediaPlaylist = result instanceof HlsMediaPlaylist; boolean isMediaPlaylist = result instanceof HlsMediaPlaylist;
if (isMediaPlaylist) { if (isMediaPlaylist) {
masterPlaylist = HlsMasterPlaylist.createSingleVariantMasterPlaylist(result.baseUri); multivariantPlaylist =
} else /* result instanceof HlsMasterPlaylist */ { HlsMultivariantPlaylist.createSingleVariantMultivariantPlaylist(result.baseUri);
masterPlaylist = (HlsMasterPlaylist) result; } else /* result instanceof HlsMultivariantPlaylist */ {
multivariantPlaylist = (HlsMultivariantPlaylist) result;
} }
this.masterPlaylist = masterPlaylist; this.multivariantPlaylist = multivariantPlaylist;
primaryMediaPlaylistUrl = masterPlaylist.variants.get(0).url; primaryMediaPlaylistUrl = multivariantPlaylist.variants.get(0).url;
// Add a temporary playlist listener for loading the first primary playlist. // Add a temporary playlist listener for loading the first primary playlist.
listeners.add(new FirstPrimaryMediaPlaylistListener()); listeners.add(new FirstPrimaryMediaPlaylistListener());
createBundles(masterPlaylist.mediaPlaylistUrls); createBundles(multivariantPlaylist.mediaPlaylistUrls);
LoadEventInfo loadEventInfo = LoadEventInfo loadEventInfo =
new LoadEventInfo( new LoadEventInfo(
loadable.loadTaskId, loadable.loadTaskId,
...@@ -327,7 +331,7 @@ public final class DefaultHlsPlaylistTracker ...@@ -327,7 +331,7 @@ public final class DefaultHlsPlaylistTracker
// Internal methods. // Internal methods.
private boolean maybeSelectNewPrimaryUrl() { private boolean maybeSelectNewPrimaryUrl() {
List<Variant> variants = masterPlaylist.variants; List<Variant> variants = multivariantPlaylist.variants;
int variantsSize = variants.size(); int variantsSize = variants.size();
long currentTimeMs = SystemClock.elapsedRealtime(); long currentTimeMs = SystemClock.elapsedRealtime();
for (int i = 0; i < variantsSize; i++) { for (int i = 0; i < variantsSize; i++) {
...@@ -382,9 +386,12 @@ public final class DefaultHlsPlaylistTracker ...@@ -382,9 +386,12 @@ public final class DefaultHlsPlaylistTracker
return newPrimaryPlaylistUri; return newPrimaryPlaylistUri;
} }
/** Returns whether any of the variants in the master playlist have the specified playlist URL. */ /**
* Returns whether any of the variants in the multivariant playlist have the specified playlist
* URL.
*/
private boolean isVariantUrl(Uri playlistUrl) { private boolean isVariantUrl(Uri playlistUrl) {
List<Variant> variants = masterPlaylist.variants; List<Variant> variants = multivariantPlaylist.variants;
for (int i = 0; i < variants.size(); i++) { for (int i = 0; i < variants.size(); i++) {
if (playlistUrl.equals(variants.get(i).url)) { if (playlistUrl.equals(variants.get(i).url)) {
return true; return true;
...@@ -687,7 +694,7 @@ public final class DefaultHlsPlaylistTracker ...@@ -687,7 +694,7 @@ public final class DefaultHlsPlaylistTracker
private void loadPlaylistImmediately(Uri playlistRequestUri) { private void loadPlaylistImmediately(Uri playlistRequestUri) {
ParsingLoadable.Parser<HlsPlaylist> mediaPlaylistParser = ParsingLoadable.Parser<HlsPlaylist> mediaPlaylistParser =
playlistParserFactory.createPlaylistParser(masterPlaylist, playlistSnapshot); playlistParserFactory.createPlaylistParser(multivariantPlaylist, playlistSnapshot);
ParsingLoadable<HlsPlaylist> mediaPlaylistLoadable = ParsingLoadable<HlsPlaylist> mediaPlaylistLoadable =
new ParsingLoadable<>( new ParsingLoadable<>(
mediaPlaylistDataSource, mediaPlaylistDataSource,
...@@ -823,7 +830,7 @@ public final class DefaultHlsPlaylistTracker ...@@ -823,7 +830,7 @@ public final class DefaultHlsPlaylistTracker
if (primaryMediaPlaylistSnapshot == null) { if (primaryMediaPlaylistSnapshot == null) {
long nowMs = SystemClock.elapsedRealtime(); long nowMs = SystemClock.elapsedRealtime();
int variantExclusionCounter = 0; int variantExclusionCounter = 0;
List<Variant> variants = castNonNull(masterPlaylist).variants; List<Variant> variants = castNonNull(multivariantPlaylist).variants;
for (int i = 0; i < variants.size(); i++) { for (int i = 0; i < variants.size(); i++) {
@Nullable @Nullable
MediaPlaylistBundle mediaPlaylistBundle = playlistBundles.get(variants.get(i).url); MediaPlaylistBundle mediaPlaylistBundle = playlistBundles.get(variants.get(i).url);
...@@ -835,7 +842,7 @@ public final class DefaultHlsPlaylistTracker ...@@ -835,7 +842,7 @@ public final class DefaultHlsPlaylistTracker
new LoadErrorHandlingPolicy.FallbackOptions( new LoadErrorHandlingPolicy.FallbackOptions(
/* numberOfLocations= */ 1, /* numberOfLocations= */ 1,
/* numberOfExcludedLocations= */ 0, /* numberOfExcludedLocations= */ 0,
/* numberOfTracks= */ masterPlaylist.variants.size(), /* numberOfTracks= */ multivariantPlaylist.variants.size(),
/* numberOfExcludedTracks= */ variantExclusionCounter); /* numberOfExcludedTracks= */ variantExclusionCounter);
@Nullable @Nullable
LoadErrorHandlingPolicy.FallbackSelection fallbackSelection = LoadErrorHandlingPolicy.FallbackSelection fallbackSelection =
......
...@@ -49,9 +49,10 @@ public final class FilteringHlsPlaylistParserFactory implements HlsPlaylistParse ...@@ -49,9 +49,10 @@ public final class FilteringHlsPlaylistParserFactory implements HlsPlaylistParse
@Override @Override
public ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser( public ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser(
HlsMasterPlaylist masterPlaylist, @Nullable HlsMediaPlaylist previousMediaPlaylist) { HlsMultivariantPlaylist multivariantPlaylist,
@Nullable HlsMediaPlaylist previousMediaPlaylist) {
return new FilteringManifestParser<>( return new FilteringManifestParser<>(
hlsPlaylistParserFactory.createPlaylistParser(masterPlaylist, previousMediaPlaylist), hlsPlaylistParserFactory.createPlaylistParser(multivariantPlaylist, previousMediaPlaylist),
streamKeys); streamKeys);
} }
} }
...@@ -32,11 +32,11 @@ import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil; ...@@ -32,11 +32,11 @@ import com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil;
import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.source.hls.HlsTrackMetadataEntry; import com.google.android.exoplayer2.source.hls.HlsTrackMetadataEntry;
import com.google.android.exoplayer2.source.hls.HlsTrackMetadataEntry.VariantInfo; import com.google.android.exoplayer2.source.hls.HlsTrackMetadataEntry.VariantInfo;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Rendition;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Part; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Part;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.RenditionReport; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.RenditionReport;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment; import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Rendition;
import com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Variant;
import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.upstream.ParsingLoadable;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
...@@ -223,28 +223,30 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -223,28 +223,30 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private static final Pattern REGEX_VARIABLE_REFERENCE = private static final Pattern REGEX_VARIABLE_REFERENCE =
Pattern.compile("\\{\\$([a-zA-Z0-9\\-_]+)\\}"); Pattern.compile("\\{\\$([a-zA-Z0-9\\-_]+)\\}");
private final HlsMasterPlaylist masterPlaylist; private final HlsMultivariantPlaylist multivariantPlaylist;
@Nullable private final HlsMediaPlaylist previousMediaPlaylist; @Nullable private final HlsMediaPlaylist previousMediaPlaylist;
/** /**
* Creates an instance where media playlists are parsed without inheriting attributes from a * Creates an instance where media playlists are parsed without inheriting attributes from a
* master playlist. * multivariant playlist.
*/ */
public HlsPlaylistParser() { public HlsPlaylistParser() {
this(HlsMasterPlaylist.EMPTY, /* previousMediaPlaylist= */ null); this(HlsMultivariantPlaylist.EMPTY, /* previousMediaPlaylist= */ null);
} }
/** /**
* Creates an instance where parsed media playlists inherit attributes from the given master * Creates an instance where parsed media playlists inherit attributes from the given master
* playlist. * playlist.
* *
* @param masterPlaylist The master playlist from which media playlists will inherit attributes. * @param multivariantPlaylist The multivariant playlist from which media playlists will inherit
* attributes.
* @param previousMediaPlaylist The previous media playlist from which the new media playlist may * @param previousMediaPlaylist The previous media playlist from which the new media playlist may
* inherit skipped segments. * inherit skipped segments.
*/ */
public HlsPlaylistParser( public HlsPlaylistParser(
HlsMasterPlaylist masterPlaylist, @Nullable HlsMediaPlaylist previousMediaPlaylist) { HlsMultivariantPlaylist multivariantPlaylist,
this.masterPlaylist = masterPlaylist; @Nullable HlsMediaPlaylist previousMediaPlaylist) {
this.multivariantPlaylist = multivariantPlaylist;
this.previousMediaPlaylist = previousMediaPlaylist; this.previousMediaPlaylist = previousMediaPlaylist;
} }
...@@ -264,7 +266,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -264,7 +266,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
// Do nothing. // Do nothing.
} else if (line.startsWith(TAG_STREAM_INF)) { } else if (line.startsWith(TAG_STREAM_INF)) {
extraLines.add(line); extraLines.add(line);
return parseMasterPlaylist(new LineIterator(extraLines, reader), uri.toString()); return parseMultivariantPlaylist(new LineIterator(extraLines, reader), uri.toString());
} else if (line.startsWith(TAG_TARGET_DURATION) } else if (line.startsWith(TAG_TARGET_DURATION)
|| line.startsWith(TAG_MEDIA_SEQUENCE) || line.startsWith(TAG_MEDIA_SEQUENCE)
|| line.startsWith(TAG_MEDIA_DURATION) || line.startsWith(TAG_MEDIA_DURATION)
...@@ -275,7 +277,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -275,7 +277,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
|| line.equals(TAG_ENDLIST)) { || line.equals(TAG_ENDLIST)) {
extraLines.add(line); extraLines.add(line);
return parseMediaPlaylist( return parseMediaPlaylist(
masterPlaylist, multivariantPlaylist,
previousMediaPlaylist, previousMediaPlaylist,
new LineIterator(extraLines, reader), new LineIterator(extraLines, reader),
uri.toString()); uri.toString());
...@@ -319,8 +321,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -319,8 +321,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
return c; return c;
} }
private static HlsMasterPlaylist parseMasterPlaylist(LineIterator iterator, String baseUri) private static HlsMultivariantPlaylist parseMultivariantPlaylist(
throws IOException { LineIterator iterator, String baseUri) throws IOException {
HashMap<Uri, ArrayList<VariantInfo>> urlToVariantInfos = new HashMap<>(); HashMap<Uri, ArrayList<VariantInfo>> urlToVariantInfos = new HashMap<>();
HashMap<String, String> variableDefinitions = new HashMap<>(); HashMap<String, String> variableDefinitions = new HashMap<>();
ArrayList<Variant> variants = new ArrayList<>(); ArrayList<Variant> variants = new ArrayList<>();
...@@ -578,7 +580,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -578,7 +580,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
muxedCaptionFormats = Collections.emptyList(); muxedCaptionFormats = Collections.emptyList();
} }
return new HlsMasterPlaylist( return new HlsMultivariantPlaylist(
baseUri, baseUri,
tags, tags,
deduplicatedVariants, deduplicatedVariants,
...@@ -627,7 +629,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -627,7 +629,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
} }
private static HlsMediaPlaylist parseMediaPlaylist( private static HlsMediaPlaylist parseMediaPlaylist(
HlsMasterPlaylist masterPlaylist, HlsMultivariantPlaylist multivariantPlaylist,
@Nullable HlsMediaPlaylist previousMediaPlaylist, @Nullable HlsMediaPlaylist previousMediaPlaylist,
LineIterator iterator, LineIterator iterator,
String baseUri) String baseUri)
...@@ -638,7 +640,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -638,7 +640,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
int version = 1; // Default version == 1. int version = 1; // Default version == 1.
long targetDurationUs = C.TIME_UNSET; long targetDurationUs = C.TIME_UNSET;
long partTargetDurationUs = C.TIME_UNSET; long partTargetDurationUs = C.TIME_UNSET;
boolean hasIndependentSegmentsTag = masterPlaylist.hasIndependentSegments; boolean hasIndependentSegmentsTag = multivariantPlaylist.hasIndependentSegments;
boolean hasEndTag = false; boolean hasEndTag = false;
@Nullable Segment initializationSegment = null; @Nullable Segment initializationSegment = null;
HashMap<String, String> variableDefinitions = new HashMap<>(); HashMap<String, String> variableDefinitions = new HashMap<>();
...@@ -748,11 +750,11 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli ...@@ -748,11 +750,11 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
} else if (line.startsWith(TAG_DEFINE)) { } else if (line.startsWith(TAG_DEFINE)) {
String importName = parseOptionalStringAttr(line, REGEX_IMPORT, variableDefinitions); String importName = parseOptionalStringAttr(line, REGEX_IMPORT, variableDefinitions);
if (importName != null) { if (importName != null) {
String value = masterPlaylist.variableDefinitions.get(importName); String value = multivariantPlaylist.variableDefinitions.get(importName);
if (value != null) { if (value != null) {
variableDefinitions.put(importName, value); variableDefinitions.put(importName, value);
} else { } else {
// The master playlist does not declare the imported variable. Ignore. // The multivariant playlist does not declare the imported variable. Ignore.
} }
} else { } else {
variableDefinitions.put( variableDefinitions.put(
......
...@@ -29,14 +29,16 @@ public interface HlsPlaylistParserFactory { ...@@ -29,14 +29,16 @@ public interface HlsPlaylistParserFactory {
/** /**
* Returns a playlist parser for playlists that were referenced by the given {@link * Returns a playlist parser for playlists that were referenced by the given {@link
* HlsMasterPlaylist}. Returned {@link HlsMediaPlaylist} instances may inherit attributes from * HlsMultivariantPlaylist}. Returned {@link HlsMediaPlaylist} instances may inherit attributes
* {@code masterPlaylist}. * from {@code multivariantPlaylist}.
* *
* @param masterPlaylist The master playlist that referenced any parsed media playlists. * @param multivariantPlaylist The multivariant playlist that referenced any parsed media
* playlists.
* @param previousMediaPlaylist The previous media playlist or null if there is no previous media * @param previousMediaPlaylist The previous media playlist or null if there is no previous media
* playlist. * playlist.
* @return A parser for HLS playlists. * @return A parser for HLS playlists.
*/ */
ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser( ParsingLoadable.Parser<HlsPlaylist> createPlaylistParser(
HlsMasterPlaylist masterPlaylist, @Nullable HlsMediaPlaylist previousMediaPlaylist); HlsMultivariantPlaylist multivariantPlaylist,
@Nullable HlsMediaPlaylist previousMediaPlaylist);
} }
...@@ -29,8 +29,8 @@ import java.io.IOException; ...@@ -29,8 +29,8 @@ import java.io.IOException;
* <p>The playlist tracker is responsible for exposing the seeking window, which is defined by the * <p>The playlist tracker is responsible for exposing the seeking window, which is defined by the
* segments that one of the playlists exposes. This playlist is called primary and needs to be * segments that one of the playlists exposes. This playlist is called primary and needs to be
* periodically refreshed in the case of live streams. Note that the primary playlist is one of the * periodically refreshed in the case of live streams. Note that the primary playlist is one of the
* media playlists while the master playlist is an optional kind of playlist defined by the HLS * media playlists while the multivariant playlist is an optional kind of playlist defined by the
* specification (RFC 8216). * HLS specification (RFC 8216).
* *
* <p>Playlist loads might encounter errors. The tracker may choose to exclude them to ensure a * <p>Playlist loads might encounter errors. The tracker may choose to exclude them to ensure a
* primary playlist is always available. * primary playlist is always available.
...@@ -120,8 +120,8 @@ public interface HlsPlaylistTracker { ...@@ -120,8 +120,8 @@ public interface HlsPlaylistTracker {
* <p>Must be called from the playback thread. A tracker may be restarted after a {@link #stop()} * <p>Must be called from the playback thread. A tracker may be restarted after a {@link #stop()}
* call. * call.
* *
* @param initialPlaylistUri Uri of the HLS stream. Can point to a media playlist or a master * @param initialPlaylistUri Uri of the HLS stream. Can point to a media playlist or a
* playlist. * multivariant playlist.
* @param eventDispatcher A dispatcher to notify of events. * @param eventDispatcher A dispatcher to notify of events.
* @param primaryPlaylistListener A callback for the primary playlist change events. * @param primaryPlaylistListener A callback for the primary playlist change events.
*/ */
...@@ -152,15 +152,15 @@ public interface HlsPlaylistTracker { ...@@ -152,15 +152,15 @@ public interface HlsPlaylistTracker {
void removeListener(PlaylistEventListener listener); void removeListener(PlaylistEventListener listener);
/** /**
* Returns the master playlist. * Returns the multivariant playlist.
* *
* <p>If the uri passed to {@link #start} points to a media playlist, an {@link HlsMasterPlaylist} * <p>If the uri passed to {@link #start} points to a media playlist, an {@link
* with a single variant for said media playlist is returned. * HlsMultivariantPlaylist} with a single variant for said media playlist is returned.
* *
* @return The master playlist. Null if the initial playlist has yet to be loaded. * @return The multivariant playlist. Null if the initial playlist has yet to be loaded.
*/ */
@Nullable @Nullable
HlsMasterPlaylist getMasterPlaylist(); HlsMultivariantPlaylist getMultivariantPlaylist();
/** /**
* Returns the most recent snapshot available of the playlist referenced by the provided {@link * Returns the most recent snapshot available of the playlist referenced by the provided {@link
...@@ -192,8 +192,8 @@ public interface HlsPlaylistTracker { ...@@ -192,8 +192,8 @@ public interface HlsPlaylistTracker {
boolean isSnapshotValid(Uri url); boolean isSnapshotValid(Uri url);
/** /**
* If the tracker is having trouble refreshing the master playlist or the primary playlist, this * If the tracker is having trouble refreshing the multivariant playlist or the primary playlist,
* method throws the underlying error. Otherwise, does nothing. * this method throws the underlying error. Otherwise, does nothing.
* *
* @throws IOException The underlying error. * @throws IOException The underlying error.
*/ */
......
...@@ -28,9 +28,9 @@ import com.google.android.exoplayer2.drm.DrmSessionManager; ...@@ -28,9 +28,9 @@ import com.google.android.exoplayer2.drm.DrmSessionManager;
import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory; import com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory;
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaSourceEventListener; import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Rendition; import com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Rendition;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant; import com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist.Variant;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker; import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker;
import com.google.android.exoplayer2.testutil.MediaPeriodAsserts; import com.google.android.exoplayer2.testutil.MediaPeriodAsserts;
...@@ -51,9 +51,9 @@ import org.junit.runner.RunWith; ...@@ -51,9 +51,9 @@ import org.junit.runner.RunWith;
public final class HlsMediaPeriodTest { public final class HlsMediaPeriodTest {
@Test @Test
public void getSteamKeys_isCompatibleWithHlsMasterPlaylistFilter() { public void getSteamKeys_isCompatibleWithHlsMultivariantPlaylistFilter() {
HlsMasterPlaylist testMasterPlaylist = HlsMultivariantPlaylist testMultivariantPlaylist =
createMasterPlaylist( createMultivariantPlaylist(
/* variants= */ Arrays.asList( /* variants= */ Arrays.asList(
createAudioOnlyVariant(/* peakBitrate= */ 10000), createAudioOnlyVariant(/* peakBitrate= */ 10000),
createMuxedVideoAudioVariant(/* peakBitrate= */ 200000), createMuxedVideoAudioVariant(/* peakBitrate= */ 200000),
...@@ -76,7 +76,8 @@ public final class HlsMediaPeriodTest { ...@@ -76,7 +76,8 @@ public final class HlsMediaPeriodTest {
HlsDataSourceFactory mockDataSourceFactory = mock(HlsDataSourceFactory.class); HlsDataSourceFactory mockDataSourceFactory = mock(HlsDataSourceFactory.class);
when(mockDataSourceFactory.createDataSource(anyInt())).thenReturn(mock(DataSource.class)); when(mockDataSourceFactory.createDataSource(anyInt())).thenReturn(mock(DataSource.class));
HlsPlaylistTracker mockPlaylistTracker = mock(HlsPlaylistTracker.class); HlsPlaylistTracker mockPlaylistTracker = mock(HlsPlaylistTracker.class);
when(mockPlaylistTracker.getMasterPlaylist()).thenReturn((HlsMasterPlaylist) playlist); when(mockPlaylistTracker.getMultivariantPlaylist())
.thenReturn((HlsMultivariantPlaylist) playlist);
MediaPeriodId mediaPeriodId = new MediaPeriodId(/* periodUid= */ new Object()); MediaPeriodId mediaPeriodId = new MediaPeriodId(/* periodUid= */ new Object());
return new HlsMediaPeriod( return new HlsMediaPeriod(
mock(HlsExtractorFactory.class), mock(HlsExtractorFactory.class),
...@@ -98,16 +99,16 @@ public final class HlsMediaPeriodTest { ...@@ -98,16 +99,16 @@ public final class HlsMediaPeriodTest {
}; };
MediaPeriodAsserts.assertGetStreamKeysAndManifestFilterIntegration( MediaPeriodAsserts.assertGetStreamKeysAndManifestFilterIntegration(
mediaPeriodFactory, testMasterPlaylist); mediaPeriodFactory, testMultivariantPlaylist);
} }
private static HlsMasterPlaylist createMasterPlaylist( private static HlsMultivariantPlaylist createMultivariantPlaylist(
List<Variant> variants, List<Variant> variants,
List<Rendition> audios, List<Rendition> audios,
List<Rendition> subtitles, List<Rendition> subtitles,
Format muxedAudioFormat, Format muxedAudioFormat,
List<Format> muxedCaptionFormats) { List<Format> muxedCaptionFormats) {
return new HlsMasterPlaylist( return new HlsMultivariantPlaylist(
"http://baseUri", "http://baseUri",
/* tags= */ Collections.emptyList(), /* tags= */ Collections.emptyList(),
variants, variants,
......
...@@ -20,11 +20,11 @@ import com.google.common.base.Charsets; ...@@ -20,11 +20,11 @@ import com.google.common.base.Charsets;
/** Data for HLS downloading tests. */ /** Data for HLS downloading tests. */
/* package */ interface HlsDownloadTestData { /* package */ interface HlsDownloadTestData {
String MASTER_PLAYLIST_URI = "test.m3u8"; String MULTIVARIANT_PLAYLIST_URI = "test.m3u8";
int MASTER_MEDIA_PLAYLIST_1_INDEX = 0; int MULTIVARIANT_MEDIA_PLAYLIST_1_INDEX = 0;
int MASTER_MEDIA_PLAYLIST_2_INDEX = 1; int MULTIVARIANT_MEDIA_PLAYLIST_2_INDEX = 1;
int MASTER_MEDIA_PLAYLIST_3_INDEX = 2; int MULTIVARIANT_MEDIA_PLAYLIST_3_INDEX = 2;
int MASTER_MEDIA_PLAYLIST_0_INDEX = 3; int MULTIVARIANT_MEDIA_PLAYLIST_0_INDEX = 3;
String MEDIA_PLAYLIST_0_DIR = "gear0/"; String MEDIA_PLAYLIST_0_DIR = "gear0/";
String MEDIA_PLAYLIST_0_URI = MEDIA_PLAYLIST_0_DIR + "prog_index.m3u8"; String MEDIA_PLAYLIST_0_URI = MEDIA_PLAYLIST_0_DIR + "prog_index.m3u8";
...@@ -35,7 +35,7 @@ import com.google.common.base.Charsets; ...@@ -35,7 +35,7 @@ import com.google.common.base.Charsets;
String MEDIA_PLAYLIST_3_DIR = "gear3/"; String MEDIA_PLAYLIST_3_DIR = "gear3/";
String MEDIA_PLAYLIST_3_URI = MEDIA_PLAYLIST_3_DIR + "prog_index.m3u8"; String MEDIA_PLAYLIST_3_URI = MEDIA_PLAYLIST_3_DIR + "prog_index.m3u8";
byte[] MASTER_PLAYLIST_DATA = byte[] MULTIVARIANT_PLAYLIST_DATA =
("#EXTM3U\n" ("#EXTM3U\n"
+ "#EXT-X-STREAM-INF:BANDWIDTH=232370,CODECS=\"mp4a.40.2, avc1.4d4015\"\n" + "#EXT-X-STREAM-INF:BANDWIDTH=232370,CODECS=\"mp4a.40.2, avc1.4d4015\"\n"
+ MEDIA_PLAYLIST_1_URI + MEDIA_PLAYLIST_1_URI
......
...@@ -17,10 +17,6 @@ package com.google.android.exoplayer2.source.hls.offline; ...@@ -17,10 +17,6 @@ package com.google.android.exoplayer2.source.hls.offline;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.ENC_MEDIA_PLAYLIST_DATA; import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.ENC_MEDIA_PLAYLIST_DATA;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.ENC_MEDIA_PLAYLIST_URI; import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.ENC_MEDIA_PLAYLIST_URI;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MASTER_MEDIA_PLAYLIST_1_INDEX;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MASTER_MEDIA_PLAYLIST_2_INDEX;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MASTER_PLAYLIST_DATA;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MASTER_PLAYLIST_URI;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MEDIA_PLAYLIST_0_DIR; import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MEDIA_PLAYLIST_0_DIR;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MEDIA_PLAYLIST_0_URI; import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MEDIA_PLAYLIST_0_URI;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MEDIA_PLAYLIST_1_DIR; import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MEDIA_PLAYLIST_1_DIR;
...@@ -30,6 +26,10 @@ import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestDa ...@@ -30,6 +26,10 @@ import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestDa
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MEDIA_PLAYLIST_3_DIR; import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MEDIA_PLAYLIST_3_DIR;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MEDIA_PLAYLIST_3_URI; import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MEDIA_PLAYLIST_3_URI;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MEDIA_PLAYLIST_DATA; import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MEDIA_PLAYLIST_DATA;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MULTIVARIANT_MEDIA_PLAYLIST_1_INDEX;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MULTIVARIANT_MEDIA_PLAYLIST_2_INDEX;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MULTIVARIANT_PLAYLIST_DATA;
import static com.google.android.exoplayer2.source.hls.offline.HlsDownloadTestData.MULTIVARIANT_PLAYLIST_URI;
import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCacheEmpty; import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCacheEmpty;
import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedData; import static com.google.android.exoplayer2.testutil.CacheAsserts.assertCachedData;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
...@@ -43,7 +43,7 @@ import com.google.android.exoplayer2.offline.DownloadRequest; ...@@ -43,7 +43,7 @@ import com.google.android.exoplayer2.offline.DownloadRequest;
import com.google.android.exoplayer2.offline.Downloader; import com.google.android.exoplayer2.offline.Downloader;
import com.google.android.exoplayer2.offline.DownloaderFactory; import com.google.android.exoplayer2.offline.DownloaderFactory;
import com.google.android.exoplayer2.offline.StreamKey; import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist; import com.google.android.exoplayer2.source.hls.playlist.HlsMultivariantPlaylist;
import com.google.android.exoplayer2.testutil.CacheAsserts; import com.google.android.exoplayer2.testutil.CacheAsserts;
import com.google.android.exoplayer2.testutil.FakeDataSet; import com.google.android.exoplayer2.testutil.FakeDataSet;
import com.google.android.exoplayer2.testutil.FakeDataSource; import com.google.android.exoplayer2.testutil.FakeDataSource;
...@@ -83,7 +83,7 @@ public class HlsDownloaderTest { ...@@ -83,7 +83,7 @@ public class HlsDownloaderTest {
progressListener = new ProgressListener(); progressListener = new ProgressListener();
fakeDataSet = fakeDataSet =
new FakeDataSet() new FakeDataSet()
.setData(MASTER_PLAYLIST_URI, MASTER_PLAYLIST_DATA) .setData(MULTIVARIANT_PLAYLIST_URI, MULTIVARIANT_PLAYLIST_DATA)
.setData(MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_DATA) .setData(MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_DATA)
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", 10) .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", 10)
.setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", 11) .setRandomData(MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", 11)
...@@ -122,7 +122,7 @@ public class HlsDownloaderTest { ...@@ -122,7 +122,7 @@ public class HlsDownloaderTest {
@Test @Test
public void counterMethods() throws Exception { public void counterMethods() throws Exception {
HlsDownloader downloader = HlsDownloader downloader =
getHlsDownloader(MASTER_PLAYLIST_URI, getKeys(MASTER_MEDIA_PLAYLIST_1_INDEX)); getHlsDownloader(MULTIVARIANT_PLAYLIST_URI, getKeys(MULTIVARIANT_MEDIA_PLAYLIST_1_INDEX));
downloader.download(progressListener); downloader.download(progressListener);
progressListener.assertBytesDownloaded(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12); progressListener.assertBytesDownloaded(MEDIA_PLAYLIST_DATA.length + 10 + 11 + 12);
...@@ -131,14 +131,14 @@ public class HlsDownloaderTest { ...@@ -131,14 +131,14 @@ public class HlsDownloaderTest {
@Test @Test
public void downloadRepresentation() throws Exception { public void downloadRepresentation() throws Exception {
HlsDownloader downloader = HlsDownloader downloader =
getHlsDownloader(MASTER_PLAYLIST_URI, getKeys(MASTER_MEDIA_PLAYLIST_1_INDEX)); getHlsDownloader(MULTIVARIANT_PLAYLIST_URI, getKeys(MULTIVARIANT_MEDIA_PLAYLIST_1_INDEX));
downloader.download(progressListener); downloader.download(progressListener);
assertCachedData( assertCachedData(
cache, cache,
new CacheAsserts.RequestSet(fakeDataSet) new CacheAsserts.RequestSet(fakeDataSet)
.subset( .subset(
MASTER_PLAYLIST_URI, MULTIVARIANT_PLAYLIST_URI,
MEDIA_PLAYLIST_1_URI, MEDIA_PLAYLIST_1_URI,
MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts", MEDIA_PLAYLIST_1_DIR + "fileSequence0.ts",
MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts", MEDIA_PLAYLIST_1_DIR + "fileSequence1.ts",
...@@ -149,8 +149,8 @@ public class HlsDownloaderTest { ...@@ -149,8 +149,8 @@ public class HlsDownloaderTest {
public void downloadMultipleRepresentations() throws Exception { public void downloadMultipleRepresentations() throws Exception {
HlsDownloader downloader = HlsDownloader downloader =
getHlsDownloader( getHlsDownloader(
MASTER_PLAYLIST_URI, MULTIVARIANT_PLAYLIST_URI,
getKeys(MASTER_MEDIA_PLAYLIST_1_INDEX, MASTER_MEDIA_PLAYLIST_2_INDEX)); getKeys(MULTIVARIANT_MEDIA_PLAYLIST_1_INDEX, MULTIVARIANT_MEDIA_PLAYLIST_2_INDEX));
downloader.download(progressListener); downloader.download(progressListener);
assertCachedData(cache, fakeDataSet); assertCachedData(cache, fakeDataSet);
...@@ -169,7 +169,7 @@ public class HlsDownloaderTest { ...@@ -169,7 +169,7 @@ public class HlsDownloaderTest {
.setRandomData(MEDIA_PLAYLIST_3_DIR + "fileSequence1.ts", 14) .setRandomData(MEDIA_PLAYLIST_3_DIR + "fileSequence1.ts", 14)
.setRandomData(MEDIA_PLAYLIST_3_DIR + "fileSequence2.ts", 15); .setRandomData(MEDIA_PLAYLIST_3_DIR + "fileSequence2.ts", 15);
HlsDownloader downloader = getHlsDownloader(MASTER_PLAYLIST_URI, getKeys()); HlsDownloader downloader = getHlsDownloader(MULTIVARIANT_PLAYLIST_URI, getKeys());
downloader.download(progressListener); downloader.download(progressListener);
assertCachedData(cache, fakeDataSet); assertCachedData(cache, fakeDataSet);
...@@ -179,8 +179,8 @@ public class HlsDownloaderTest { ...@@ -179,8 +179,8 @@ public class HlsDownloaderTest {
public void remove() throws Exception { public void remove() throws Exception {
HlsDownloader downloader = HlsDownloader downloader =
getHlsDownloader( getHlsDownloader(
MASTER_PLAYLIST_URI, MULTIVARIANT_PLAYLIST_URI,
getKeys(MASTER_MEDIA_PLAYLIST_1_INDEX, MASTER_MEDIA_PLAYLIST_2_INDEX)); getKeys(MULTIVARIANT_MEDIA_PLAYLIST_1_INDEX, MULTIVARIANT_MEDIA_PLAYLIST_2_INDEX));
downloader.download(progressListener); downloader.download(progressListener);
downloader.remove(); downloader.remove();
...@@ -231,7 +231,7 @@ public class HlsDownloaderTest { ...@@ -231,7 +231,7 @@ public class HlsDownloaderTest {
private static ArrayList<StreamKey> getKeys(int... variantIndices) { private static ArrayList<StreamKey> getKeys(int... variantIndices) {
ArrayList<StreamKey> streamKeys = new ArrayList<>(); ArrayList<StreamKey> streamKeys = new ArrayList<>();
for (int variantIndex : variantIndices) { for (int variantIndex : variantIndices) {
streamKeys.add(new StreamKey(HlsMasterPlaylist.GROUP_INDEX_VARIANT, variantIndex)); streamKeys.add(new StreamKey(HlsMultivariantPlaylist.GROUP_INDEX_VARIANT, variantIndex));
} }
return streamKeys; return streamKeys;
} }
......
...@@ -392,7 +392,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -392,7 +392,7 @@ public class HlsMediaPlaylistParserTest {
HlsMediaPlaylist playlist = HlsMediaPlaylist playlist =
(HlsMediaPlaylist) (HlsMediaPlaylist)
new HlsPlaylistParser(HlsMasterPlaylist.EMPTY, previousPlaylist) new HlsPlaylistParser(HlsMultivariantPlaylist.EMPTY, previousPlaylist)
.parse(playlistUri, inputStream); .parse(playlistUri, inputStream);
assertThat(playlist.segments).hasSize(3); assertThat(playlist.segments).hasSize(3);
...@@ -446,7 +446,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -446,7 +446,7 @@ public class HlsMediaPlaylistParserTest {
HlsMediaPlaylist playlist = HlsMediaPlaylist playlist =
(HlsMediaPlaylist) (HlsMediaPlaylist)
new HlsPlaylistParser(HlsMasterPlaylist.EMPTY, previousPlaylist) new HlsPlaylistParser(HlsMultivariantPlaylist.EMPTY, previousPlaylist)
.parse(playlistUri, inputStream); .parse(playlistUri, inputStream);
assertThat(playlist.segments).hasSize(2); assertThat(playlist.segments).hasSize(2);
...@@ -1363,7 +1363,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -1363,7 +1363,7 @@ public class HlsMediaPlaylistParserTest {
} }
@Test @Test
public void masterPlaylistAttributeInheritance() throws IOException { public void multivariantPlaylistAttributeInheritance() throws IOException {
Uri playlistUri = Uri.parse("https://example.com/test3.m3u8"); Uri playlistUri = Uri.parse("https://example.com/test3.m3u8");
String playlistString = String playlistString =
"#EXTM3U\n" "#EXTM3U\n"
...@@ -1386,8 +1386,8 @@ public class HlsMediaPlaylistParserTest { ...@@ -1386,8 +1386,8 @@ public class HlsMediaPlaylistParserTest {
assertThat(standalonePlaylist.hasIndependentSegments).isFalse(); assertThat(standalonePlaylist.hasIndependentSegments).isFalse();
inputStream.reset(); inputStream.reset();
HlsMasterPlaylist masterPlaylist = HlsMultivariantPlaylist multivariantPlaylist =
new HlsMasterPlaylist( new HlsMultivariantPlaylist(
/* baseUri= */ "https://example.com/", /* baseUri= */ "https://example.com/",
/* tags= */ Collections.emptyList(), /* tags= */ Collections.emptyList(),
/* variants= */ Collections.emptyList(), /* variants= */ Collections.emptyList(),
...@@ -1402,7 +1402,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -1402,7 +1402,7 @@ public class HlsMediaPlaylistParserTest {
/* sessionKeyDrmInitData= */ Collections.emptyList()); /* sessionKeyDrmInitData= */ Collections.emptyList());
HlsMediaPlaylist playlistWithInheritance = HlsMediaPlaylist playlistWithInheritance =
(HlsMediaPlaylist) (HlsMediaPlaylist)
new HlsPlaylistParser(masterPlaylist, /* previousMediaPlaylist= */ null) new HlsPlaylistParser(multivariantPlaylist, /* previousMediaPlaylist= */ null)
.parse(playlistUri, inputStream); .parse(playlistUri, inputStream);
assertThat(playlistWithInheritance.hasIndependentSegments).isTrue(); assertThat(playlistWithInheritance.hasIndependentSegments).isTrue();
} }
...@@ -1450,8 +1450,8 @@ public class HlsMediaPlaylistParserTest { ...@@ -1450,8 +1450,8 @@ public class HlsMediaPlaylistParserTest {
InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString)); InputStream inputStream = new ByteArrayInputStream(Util.getUtf8Bytes(playlistString));
HashMap<String, String> variableDefinitions = new HashMap<>(); HashMap<String, String> variableDefinitions = new HashMap<>();
variableDefinitions.put("imported_base", "long_path"); variableDefinitions.put("imported_base", "long_path");
HlsMasterPlaylist masterPlaylist = HlsMultivariantPlaylist multivariantPlaylist =
new HlsMasterPlaylist( new HlsMultivariantPlaylist(
/* baseUri= */ "", /* baseUri= */ "",
/* tags= */ Collections.emptyList(), /* tags= */ Collections.emptyList(),
/* variants= */ Collections.emptyList(), /* variants= */ Collections.emptyList(),
...@@ -1466,7 +1466,7 @@ public class HlsMediaPlaylistParserTest { ...@@ -1466,7 +1466,7 @@ public class HlsMediaPlaylistParserTest {
/* sessionKeyDrmInitData= */ Collections.emptyList()); /* sessionKeyDrmInitData= */ Collections.emptyList());
HlsMediaPlaylist playlist = HlsMediaPlaylist playlist =
(HlsMediaPlaylist) (HlsMediaPlaylist)
new HlsPlaylistParser(masterPlaylist, /* previousMediaPlaylist= */ null) new HlsPlaylistParser(multivariantPlaylist, /* previousMediaPlaylist= */ null)
.parse(playlistUri, inputStream); .parse(playlistUri, inputStream);
for (int i = 1; i <= 4; i++) { for (int i = 1; i <= 4; i++) {
assertThat(playlist.segments.get(i - 1).url).isEqualTo("long_path" + i + ".ts"); assertThat(playlist.segments.get(i - 1).url).isEqualTo("long_path" + i + ".ts");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment