Commit 99f89132 by olly Committed by Toni

Remove HlsUrl and introduce HlsMasterPlaylist.mediaPlaylistUrls

- This removes the need for Variant and Rendition to have a common
  base class, allowing the url field to be marked as @Nullable in
  Rendition but not in Variant.
- The addition of mediaPlaylistUrls is needed for the new StreamKey
  indexing for HLS. It's also convenient in a couple of places (e.g.
  HlsDownloader), where a list of all media playlist URLs is needed.
- Lots of places where HlsUrl was passed only needed the actual
  URL (not the Format, which is the other piece of HlsUrl). Passing
  just the URL is a little simpler, and resolves some of the naming
  confusion.

Issue: #5596
Issue: #2600
PiperOrigin-RevId: 240970466
parent 2623b4b3
......@@ -27,7 +27,6 @@ import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.source.chunk.DataChunk;
import com.google.android.exoplayer2.source.chunk.MediaChunk;
import com.google.android.exoplayer2.source.chunk.MediaChunkIterator;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker;
......@@ -70,10 +69,8 @@ import java.util.Map;
*/
public boolean endOfStream;
/**
* Indicates that the chunk source is waiting for the referred playlist to be refreshed.
*/
public HlsUrl playlist;
/** Indicates that the chunk source is waiting for the referred playlist to be refreshed. */
public Uri playlistUrl;
/**
* Clears the holder.
......@@ -81,7 +78,7 @@ import java.util.Map;
public void clear() {
chunk = null;
endOfStream = false;
playlist = null;
playlistUrl = null;
}
}
......@@ -96,7 +93,8 @@ import java.util.Map;
private final DataSource mediaDataSource;
private final DataSource encryptionDataSource;
private final TimestampAdjusterProvider timestampAdjusterProvider;
private final HlsUrl[] hlsUrls;
private final Uri[] playlistUrls;
private final Format[] playlistFormats;
private final HlsPlaylistTracker playlistTracker;
private final TrackGroup trackGroup;
private final List<Format> muxedCaptionFormats;
......@@ -105,7 +103,7 @@ import java.util.Map;
private boolean isTimestampMaster;
private byte[] scratchSpace;
private IOException fatalError;
private HlsUrl expectedPlaylistUrl;
private Uri expectedPlaylistUrl;
private boolean independentSegments;
// Note: The track group in the selection is typically *not* equal to trackGroup. This is due to
......@@ -119,8 +117,9 @@ import java.util.Map;
* @param extractorFactory An {@link HlsExtractorFactory} from which to obtain the extractors for
* media chunks.
* @param playlistTracker The {@link HlsPlaylistTracker} from which to obtain media playlists.
* @param hlsUrls The {@link HlsUrl} instances corresponding to media playlists from which this
* chunk source can obtain media.
* @param playlistUrls The {@link Uri}s of the media playlists that can be adapted between by this
* chunk source.
* @param playlistFormats The {@link Format Formats} corresponding to the media playlists.
* @param dataSourceFactory An {@link HlsDataSourceFactory} to create {@link DataSource}s for the
* chunks.
* @param mediaTransferListener The transfer listener which should be informed of any media data
......@@ -134,30 +133,30 @@ import java.util.Map;
public HlsChunkSource(
HlsExtractorFactory extractorFactory,
HlsPlaylistTracker playlistTracker,
HlsUrl[] hlsUrls,
Uri[] playlistUrls,
Format[] playlistFormats,
HlsDataSourceFactory dataSourceFactory,
@Nullable TransferListener mediaTransferListener,
TimestampAdjusterProvider timestampAdjusterProvider,
List<Format> muxedCaptionFormats) {
this.extractorFactory = extractorFactory;
this.playlistTracker = playlistTracker;
this.hlsUrls = hlsUrls;
this.playlistUrls = playlistUrls;
this.playlistFormats = playlistFormats;
this.timestampAdjusterProvider = timestampAdjusterProvider;
this.muxedCaptionFormats = muxedCaptionFormats;
keyCache = new FullSegmentEncryptionKeyCache();
liveEdgeInPeriodTimeUs = C.TIME_UNSET;
Format[] trackFormats = new Format[hlsUrls.length];
int[] initialTrackSelection = new int[hlsUrls.length];
for (int i = 0; i < hlsUrls.length; i++) {
trackFormats[i] = hlsUrls[i].format;
initialTrackSelection[i] = i;
}
mediaDataSource = dataSourceFactory.createDataSource(C.DATA_TYPE_MEDIA);
if (mediaTransferListener != null) {
mediaDataSource.addTransferListener(mediaTransferListener);
}
encryptionDataSource = dataSourceFactory.createDataSource(C.DATA_TYPE_DRM);
trackGroup = new TrackGroup(trackFormats);
trackGroup = new TrackGroup(playlistFormats);
int[] initialTrackSelection = new int[playlistUrls.length];
for (int i = 0; i < playlistUrls.length; i++) {
initialTrackSelection[i] = i;
}
trackSelection = new InitializationTrackSelection(trackGroup, initialTrackSelection);
}
......@@ -221,8 +220,8 @@ import java.util.Map;
*
* <p>If a chunk is available then {@link HlsChunkHolder#chunk} is set. If the end of the stream
* has been reached then {@link HlsChunkHolder#endOfStream} is set. If a chunk is not available
* but the end of the stream has not been reached, {@link HlsChunkHolder#playlist} is set to
* contain the {@link HlsUrl} that refers to the playlist that needs refreshing.
* but the end of the stream has not been reached, {@link HlsChunkHolder#playlistUrl} is set to
* contain the {@link Uri} that refers to the playlist that needs refreshing.
*
* @param playbackPositionUs The current playback position relative to the period start in
* microseconds. If playback of the period to which this chunk source belongs has not yet
......@@ -259,16 +258,16 @@ import java.util.Map;
int selectedTrackIndex = trackSelection.getSelectedIndexInTrackGroup();
boolean switchingTrack = oldTrackIndex != selectedTrackIndex;
HlsUrl selectedHlsUrl = hlsUrls[selectedTrackIndex];
if (!playlistTracker.isSnapshotValid(selectedHlsUrl)) {
out.playlist = selectedHlsUrl;
seenExpectedPlaylistError &= expectedPlaylistUrl == selectedHlsUrl;
expectedPlaylistUrl = selectedHlsUrl;
Uri selectedPlaylistUrl = playlistUrls[selectedTrackIndex];
if (!playlistTracker.isSnapshotValid(selectedPlaylistUrl)) {
out.playlistUrl = selectedPlaylistUrl;
seenExpectedPlaylistError &= selectedPlaylistUrl.equals(expectedPlaylistUrl);
expectedPlaylistUrl = selectedPlaylistUrl;
// Retry when playlist is refreshed.
return;
}
HlsMediaPlaylist mediaPlaylist =
playlistTracker.getPlaylistSnapshot(selectedHlsUrl, /* isForPlayback= */ true);
playlistTracker.getPlaylistSnapshot(selectedPlaylistUrl, /* isForPlayback= */ true);
independentSegments = mediaPlaylist.hasIndependentSegments;
updateLiveEdgeTimeUs(mediaPlaylist);
......@@ -284,9 +283,9 @@ import java.util.Map;
// We try getting the next chunk without adapting in case that's the reason for falling
// behind the live window.
selectedTrackIndex = oldTrackIndex;
selectedHlsUrl = hlsUrls[selectedTrackIndex];
selectedPlaylistUrl = playlistUrls[selectedTrackIndex];
mediaPlaylist =
playlistTracker.getPlaylistSnapshot(selectedHlsUrl, /* isForPlayback= */ true);
playlistTracker.getPlaylistSnapshot(selectedPlaylistUrl, /* isForPlayback= */ true);
startOfPlaylistInPeriodUs =
mediaPlaylist.startTimeUs - playlistTracker.getInitialStartTimeUs();
chunkMediaSequence = previous.getNextChunkIndex();
......@@ -301,9 +300,9 @@ import java.util.Map;
if (mediaPlaylist.hasEndTag) {
out.endOfStream = true;
} else /* Live */ {
out.playlist = selectedHlsUrl;
seenExpectedPlaylistError &= expectedPlaylistUrl == selectedHlsUrl;
expectedPlaylistUrl = selectedHlsUrl;
out.playlistUrl = selectedPlaylistUrl;
seenExpectedPlaylistError &= selectedPlaylistUrl.equals(expectedPlaylistUrl);
expectedPlaylistUrl = selectedPlaylistUrl;
}
return;
}
......@@ -330,10 +329,11 @@ import java.util.Map;
HlsMediaChunk.createInstance(
extractorFactory,
mediaDataSource,
playlistFormats[selectedTrackIndex],
startOfPlaylistInPeriodUs,
mediaPlaylist,
segmentIndexInPlaylist,
selectedHlsUrl,
selectedPlaylistUrl,
muxedCaptionFormats,
trackSelection.getSelectionReason(),
trackSelection.getSelectionData(),
......@@ -375,13 +375,19 @@ import java.util.Map;
/**
* Called when a playlist load encounters an error.
*
* @param hlsUrl The {@link HlsUrl} of the playlist whose load encountered an error.
* @param playlistUrl The {@link Uri} of the playlist whose load encountered an error.
* @param blacklistDurationMs The duration for which the playlist should be blacklisted. Or {@link
* C#TIME_UNSET} if the playlist should not be blacklisted.
* @return True if blacklisting did not encounter errors. False otherwise.
*/
public boolean onPlaylistError(HlsUrl hlsUrl, long blacklistDurationMs) {
int trackGroupIndex = trackGroup.indexOf(hlsUrl.format);
public boolean onPlaylistError(Uri playlistUrl, long blacklistDurationMs) {
int trackGroupIndex = C.INDEX_UNSET;
for (int i = 0; i < playlistUrls.length; i++) {
if (playlistUrls[i].equals(playlistUrl)) {
trackGroupIndex = i;
break;
}
}
if (trackGroupIndex == C.INDEX_UNSET) {
return true;
}
......@@ -389,7 +395,7 @@ import java.util.Map;
if (trackSelectionIndex == C.INDEX_UNSET) {
return true;
}
seenExpectedPlaylistError |= expectedPlaylistUrl == hlsUrl;
seenExpectedPlaylistError |= playlistUrl.equals(expectedPlaylistUrl);
return blacklistDurationMs == C.TIME_UNSET
|| trackSelection.blacklist(trackSelectionIndex, blacklistDurationMs);
}
......@@ -407,13 +413,13 @@ import java.util.Map;
MediaChunkIterator[] chunkIterators = new MediaChunkIterator[trackSelection.length()];
for (int i = 0; i < chunkIterators.length; i++) {
int trackIndex = trackSelection.getIndexInTrackGroup(i);
HlsUrl hlsUrl = hlsUrls[trackIndex];
if (!playlistTracker.isSnapshotValid(hlsUrl)) {
Uri playlistUrl = playlistUrls[trackIndex];
if (!playlistTracker.isSnapshotValid(playlistUrl)) {
chunkIterators[i] = MediaChunkIterator.EMPTY;
continue;
}
HlsMediaPlaylist playlist =
playlistTracker.getPlaylistSnapshot(hlsUrl, /* isForPlayback= */ false);
playlistTracker.getPlaylistSnapshot(playlistUrl, /* isForPlayback= */ false);
long startOfPlaylistInPeriodUs =
playlist.startTimeUs - playlistTracker.getInitialStartTimeUs();
boolean switchingTrack = trackIndex != oldTrackIndex;
......@@ -502,7 +508,7 @@ import java.util.Map;
return new EncryptionKeyChunk(
encryptionDataSource,
dataSpec,
hlsUrls[selectedTrackIndex].format,
playlistFormats[selectedTrackIndex],
trackSelection.getSelectionReason(),
trackSelection.getSelectionData(),
scratchSpace);
......
......@@ -27,7 +27,6 @@ import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
import com.google.android.exoplayer2.metadata.id3.PrivFrame;
import com.google.android.exoplayer2.source.chunk.MediaChunk;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DataSpec;
......@@ -52,9 +51,10 @@ import java.util.concurrent.atomic.AtomicInteger;
* @param extractorFactory A {@link HlsExtractorFactory} from which the HLS media chunk extractor
* is obtained.
* @param dataSource The source from which the data should be loaded.
* @param format The chunk format.
* @param startOfPlaylistInPeriodUs The position of the playlist in the period in microseconds.
* @param mediaPlaylist The media playlist from which this chunk was obtained.
* @param hlsUrl 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
* information is available in the master playlist.
* @param trackSelectionReason See {@link #trackSelectionReason}.
......@@ -70,10 +70,11 @@ import java.util.concurrent.atomic.AtomicInteger;
public static HlsMediaChunk createInstance(
HlsExtractorFactory extractorFactory,
DataSource dataSource,
Format format,
long startOfPlaylistInPeriodUs,
HlsMediaPlaylist mediaPlaylist,
int segmentIndexInPlaylist,
HlsUrl hlsUrl,
Uri playlistUrl,
@Nullable List<Format> muxedCaptionFormats,
int trackSelectionReason,
@Nullable Object trackSelectionData,
......@@ -126,7 +127,8 @@ import java.util.concurrent.atomic.AtomicInteger;
if (previousChunk != null) {
id3Decoder = previousChunk.id3Decoder;
scratchId3Data = previousChunk.scratchId3Data;
shouldSpliceIn = previousChunk.hlsUrl != hlsUrl || !previousChunk.loadCompleted;
shouldSpliceIn =
!playlistUrl.equals(previousChunk.playlistUrl) || !previousChunk.loadCompleted;
previousExtractor =
previousChunk.isExtractorReusable
&& previousChunk.discontinuitySequenceNumber == discontinuitySequenceNumber
......@@ -143,11 +145,12 @@ import java.util.concurrent.atomic.AtomicInteger;
extractorFactory,
mediaDataSource,
dataSpec,
format,
mediaSegmentEncrypted,
initDataSource,
initDataSpec,
initSegmentEncrypted,
hlsUrl,
playlistUrl,
muxedCaptionFormats,
trackSelectionReason,
trackSelectionData,
......@@ -180,10 +183,8 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public final int discontinuitySequenceNumber;
/**
* The url of the playlist from which this chunk was obtained.
*/
public final HlsUrl hlsUrl;
/** The url of the playlist from which this chunk was obtained. */
public final Uri playlistUrl;
@Nullable private final DataSource initDataSource;
@Nullable private final DataSpec initDataSpec;
......@@ -214,11 +215,12 @@ import java.util.concurrent.atomic.AtomicInteger;
HlsExtractorFactory extractorFactory,
DataSource mediaDataSource,
DataSpec dataSpec,
Format format,
boolean mediaSegmentEncrypted,
DataSource initDataSource,
@Nullable DataSpec initDataSpec,
boolean initSegmentEncrypted,
HlsUrl hlsUrl,
Uri playlistUrl,
@Nullable List<Format> muxedCaptionFormats,
int trackSelectionReason,
Object trackSelectionData,
......@@ -237,7 +239,7 @@ import java.util.concurrent.atomic.AtomicInteger;
super(
mediaDataSource,
dataSpec,
hlsUrl.format,
format,
trackSelectionReason,
trackSelectionData,
startTimeUs,
......@@ -248,7 +250,7 @@ import java.util.concurrent.atomic.AtomicInteger;
this.initDataSource = initDataSource;
this.initDataSpec = initDataSpec;
this.initSegmentEncrypted = initSegmentEncrypted;
this.hlsUrl = hlsUrl;
this.playlistUrl = playlistUrl;
this.isMasterTimestampSource = isMasterTimestampSource;
this.timestampAdjuster = timestampAdjuster;
this.hasGapTag = hasGapTag;
......
......@@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.source.hls;
import android.net.Uri;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import com.google.android.exoplayer2.C;
......@@ -31,8 +32,8 @@ import com.google.android.exoplayer2.source.SequenceableLoader;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
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.HlsPlaylistTracker;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator;
......@@ -410,7 +411,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
}
@Override
public void onPlaylistRefreshRequired(HlsUrl url) {
public void onPlaylistRefreshRequired(Uri url) {
playlistTracker.refreshPlaylist(url);
}
......@@ -427,7 +428,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
}
@Override
public boolean onPlaylistError(HlsUrl url, long blacklistDurationMs) {
public boolean onPlaylistError(Uri url, long blacklistDurationMs) {
boolean noBlacklistingFailure = true;
for (HlsSampleStreamWrapper streamWrapper : sampleStreamWrappers) {
noBlacklistingFailure &= streamWrapper.onPlaylistError(url, blacklistDurationMs);
......@@ -473,11 +474,12 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
// Subtitle stream wrappers. We can always use master playlist information to prepare these.
for (int i = 0; i < subtitleRenditions.size(); i++) {
HlsUrl url = subtitleRenditions.get(i);
Rendition subtitleRendition = subtitleRenditions.get(i);
HlsSampleStreamWrapper sampleStreamWrapper =
buildSampleStreamWrapper(
C.TRACK_TYPE_TEXT,
new HlsUrl[] {url},
new Uri[] {subtitleRendition.url},
new Format[] {subtitleRendition.format},
null,
Collections.emptyList(),
overridingDrmInitData,
......@@ -485,7 +487,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
manifestUrlIndicesPerWrapper.add(new int[] {i});
sampleStreamWrappers.add(sampleStreamWrapper);
sampleStreamWrapper.prepareWithMasterPlaylistInfo(
new TrackGroupArray(new TrackGroup(url.format)), 0, TrackGroupArray.EMPTY);
new TrackGroupArray(new TrackGroup(subtitleRendition.format)), 0, TrackGroupArray.EMPTY);
}
this.sampleStreamWrappers = sampleStreamWrappers.toArray(new HlsSampleStreamWrapper[0]);
......@@ -538,7 +540,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
int videoVariantCount = 0;
int audioVariantCount = 0;
for (int i = 0; i < masterPlaylist.variants.size(); i++) {
HlsUrl variant = masterPlaylist.variants.get(i);
Variant variant = masterPlaylist.variants.get(i);
Format format = variant.format;
if (format.height > 0 || Util.getCodecsOfType(format.codecs, C.TRACK_TYPE_VIDEO) != null) {
variantTypes[i] = C.TRACK_TYPE_VIDEO;
......@@ -565,21 +567,25 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
useNonAudioVariantsOnly = true;
selectedVariantsCount = variantTypes.length - audioVariantCount;
}
HlsUrl[] selectedVariants = new HlsUrl[selectedVariantsCount];
Uri[] selectedPlaylistUrls = new Uri[selectedVariantsCount];
Format[] selectedPlaylistFormats = new Format[selectedVariantsCount];
int[] selectedVariantIndices = new int[selectedVariantsCount];
int outIndex = 0;
for (int i = 0; i < masterPlaylist.variants.size(); i++) {
if ((!useVideoVariantsOnly || variantTypes[i] == C.TRACK_TYPE_VIDEO)
&& (!useNonAudioVariantsOnly || variantTypes[i] != C.TRACK_TYPE_AUDIO)) {
selectedVariants[outIndex] = masterPlaylist.variants.get(i);
Variant variant = masterPlaylist.variants.get(i);
selectedPlaylistUrls[outIndex] = variant.url;
selectedPlaylistFormats[outIndex] = variant.format;
selectedVariantIndices[outIndex++] = i;
}
}
String codecs = selectedVariants[0].format.codecs;
String codecs = selectedPlaylistFormats[0].codecs;
HlsSampleStreamWrapper sampleStreamWrapper =
buildSampleStreamWrapper(
C.TRACK_TYPE_DEFAULT,
selectedVariants,
selectedPlaylistUrls,
selectedPlaylistFormats,
masterPlaylist.muxedAudioFormat,
masterPlaylist.muxedCaptionFormats,
overridingDrmInitData,
......@@ -593,7 +599,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
if (variantsContainVideoCodecs) {
Format[] videoFormats = new Format[selectedVariantsCount];
for (int i = 0; i < videoFormats.length; i++) {
videoFormats[i] = deriveVideoFormat(selectedVariants[i].format);
videoFormats[i] = deriveVideoFormat(selectedPlaylistFormats[i]);
}
muxedTrackGroups.add(new TrackGroup(videoFormats));
......@@ -602,7 +608,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
muxedTrackGroups.add(
new TrackGroup(
deriveAudioFormat(
selectedVariants[0].format,
selectedPlaylistFormats[0],
masterPlaylist.muxedAudioFormat,
/* isPrimaryTrackInVariant= */ false)));
}
......@@ -616,10 +622,9 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
// Variants only contain audio.
Format[] audioFormats = new Format[selectedVariantsCount];
for (int i = 0; i < audioFormats.length; i++) {
Format variantFormat = selectedVariants[i].format;
audioFormats[i] =
deriveAudioFormat(
variantFormat,
/* variantFormat= */ selectedPlaylistFormats[i],
masterPlaylist.muxedAudioFormat,
/* isPrimaryTrackInVariant= */ true);
}
......@@ -652,7 +657,9 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
List<HlsSampleStreamWrapper> sampleStreamWrappers,
List<int[]> manifestUrlsIndicesPerWrapper,
Map<String, DrmInitData> overridingDrmInitData) {
ArrayList<HlsUrl> scratchRenditionList =
ArrayList<Uri> scratchPlaylistUrls =
new ArrayList<>(/* initialCapacity= */ audioRenditions.size());
ArrayList<Format> scratchPlaylistFormats =
new ArrayList<>(/* initialCapacity= */ audioRenditions.size());
ArrayList<Integer> scratchIndicesList =
new ArrayList<>(/* initialCapacity= */ audioRenditions.size());
......@@ -667,14 +674,16 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
}
boolean renditionsHaveCodecs = true;
scratchRenditionList.clear();
scratchPlaylistUrls.clear();
scratchPlaylistFormats.clear();
scratchIndicesList.clear();
// Group all renditions with matching name.
for (int renditionIndex = 0; renditionIndex < audioRenditions.size(); renditionIndex++) {
if (Util.areEqual(name, audioRenditions.get(renditionIndex).name)) {
HlsUrl rendition = audioRenditions.get(renditionIndex);
Rendition rendition = audioRenditions.get(renditionIndex);
scratchIndicesList.add(renditionIndex);
scratchRenditionList.add(rendition);
scratchPlaylistUrls.add(rendition.url);
scratchPlaylistFormats.add(rendition.format);
renditionsHaveCodecs &= rendition.format.codecs != null;
}
}
......@@ -682,7 +691,8 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
HlsSampleStreamWrapper sampleStreamWrapper =
buildSampleStreamWrapper(
C.TRACK_TYPE_AUDIO,
scratchRenditionList.toArray(new HlsUrl[0]),
scratchPlaylistUrls.toArray(new Uri[0]),
scratchPlaylistFormats.toArray(new Format[0]),
/* muxedAudioFormat= */ null,
/* muxedCaptionFormats= */ Collections.emptyList(),
overridingDrmInitData,
......@@ -691,10 +701,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
sampleStreamWrappers.add(sampleStreamWrapper);
if (allowChunklessPreparation && renditionsHaveCodecs) {
Format[] renditionFormats = new Format[scratchRenditionList.size()];
for (int i = 0; i < renditionFormats.length; i++) {
renditionFormats[i] = scratchRenditionList.get(i).format;
}
Format[] renditionFormats = scratchPlaylistFormats.toArray(new Format[0]);
sampleStreamWrapper.prepareWithMasterPlaylistInfo(
new TrackGroupArray(new TrackGroup(renditionFormats)), 0, TrackGroupArray.EMPTY);
}
......@@ -703,7 +710,8 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
private HlsSampleStreamWrapper buildSampleStreamWrapper(
int trackType,
HlsUrl[] variants,
Uri[] playlistUrls,
Format[] playlistFormats,
Format muxedAudioFormat,
List<Format> muxedCaptionFormats,
Map<String, DrmInitData> overridingDrmInitData,
......@@ -712,7 +720,8 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
new HlsChunkSource(
extractorFactory,
playlistTracker,
variants,
playlistUrls,
playlistFormats,
dataSourceFactory,
mediaTransferListener,
timestampAdjusterProvider,
......
......@@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.source.hls;
import android.net.Uri;
import android.os.Handler;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
......@@ -37,8 +38,6 @@ import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.source.chunk.MediaChunkIterator;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
import com.google.android.exoplayer2.trackselection.TrackSelection;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
......@@ -79,8 +78,7 @@ import java.util.Map;
* Called to schedule a {@link #continueLoading(long)} call when the playlist referred by the
* given url changes.
*/
void onPlaylistRefreshRequired(HlsMasterPlaylist.HlsUrl playlistUrl);
void onPlaylistRefreshRequired(Uri playlistUrl);
}
private static final String TAG = "HlsSampleStreamWrapper";
......@@ -451,8 +449,8 @@ import java.util.Map;
chunkSource.setIsTimestampMaster(isTimestampMaster);
}
public boolean onPlaylistError(HlsUrl url, long blacklistDurationMs) {
return chunkSource.onPlaylistError(url, blacklistDurationMs);
public boolean onPlaylistError(Uri playlistUrl, long blacklistDurationMs) {
return chunkSource.onPlaylistError(playlistUrl, blacklistDurationMs);
}
// SampleStream implementation.
......@@ -590,7 +588,7 @@ import java.util.Map;
chunkSource.getNextChunk(positionUs, loadPositionUs, chunkQueue, nextChunkHolder);
boolean endOfStream = nextChunkHolder.endOfStream;
Chunk loadable = nextChunkHolder.chunk;
HlsMasterPlaylist.HlsUrl playlistToLoad = nextChunkHolder.playlist;
Uri playlistUrlToLoad = nextChunkHolder.playlistUrl;
nextChunkHolder.clear();
if (endOfStream) {
......@@ -600,8 +598,8 @@ import java.util.Map;
}
if (loadable == null) {
if (playlistToLoad != null) {
callback.onPlaylistRefreshRequired(playlistToLoad);
if (playlistUrlToLoad != null) {
callback.onPlaylistRefreshRequired(playlistUrlToLoad);
}
return false;
}
......
......@@ -21,7 +21,6 @@ import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import com.google.android.exoplayer2.offline.SegmentDownloader;
import com.google.android.exoplayer2.offline.StreamKey;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser;
......@@ -81,9 +80,7 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> {
ArrayList<DataSpec> mediaPlaylistDataSpecs = new ArrayList<>();
if (playlist instanceof HlsMasterPlaylist) {
HlsMasterPlaylist masterPlaylist = (HlsMasterPlaylist) playlist;
addMediaPlaylistDataSpecs(masterPlaylist.variants, mediaPlaylistDataSpecs);
addMediaPlaylistDataSpecs(masterPlaylist.audios, mediaPlaylistDataSpecs);
addMediaPlaylistDataSpecs(masterPlaylist.subtitles, mediaPlaylistDataSpecs);
addMediaPlaylistDataSpecs(masterPlaylist.mediaPlaylistUrls, mediaPlaylistDataSpecs);
} else {
mediaPlaylistDataSpecs.add(
SegmentDownloader.getCompressibleDataSpec(Uri.parse(playlist.baseUri)));
......@@ -118,9 +115,9 @@ public final class HlsDownloader extends SegmentDownloader<HlsPlaylist> {
return segments;
}
private void addMediaPlaylistDataSpecs(List<? extends HlsUrl> urls, List<DataSpec> out) {
for (int i = 0; i < urls.size(); i++) {
out.add(SegmentDownloader.getCompressibleDataSpec(urls.get(i).url));
private void addMediaPlaylistDataSpecs(List<Uri> mediaPlaylistUrls, List<DataSpec> out) {
for (int i = 0; i < mediaPlaylistUrls.size(); i++) {
out.add(SegmentDownloader.getCompressibleDataSpec(mediaPlaylistUrls.get(i)));
}
}
......
......@@ -23,7 +23,6 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.hls.HlsDataSourceFactory;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Variant;
import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer2.upstream.DataSource;
......@@ -53,18 +52,18 @@ public final class DefaultHlsPlaylistTracker
private final HlsDataSourceFactory dataSourceFactory;
private final HlsPlaylistParserFactory playlistParserFactory;
private final LoadErrorHandlingPolicy loadErrorHandlingPolicy;
private final IdentityHashMap<HlsUrl, MediaPlaylistBundle> playlistBundles;
private final IdentityHashMap<Uri, MediaPlaylistBundle> playlistBundles;
private final List<PlaylistEventListener> listeners;
private final double playlistStuckTargetDurationCoefficient;
private @Nullable ParsingLoadable.Parser<HlsPlaylist> mediaPlaylistParser;
private @Nullable EventDispatcher eventDispatcher;
private @Nullable Loader initialPlaylistLoader;
private @Nullable Handler playlistRefreshHandler;
private @Nullable PrimaryPlaylistListener primaryPlaylistListener;
private @Nullable HlsMasterPlaylist masterPlaylist;
private @Nullable HlsUrl primaryHlsUrl;
private @Nullable HlsMediaPlaylist primaryUrlSnapshot;
@Nullable private ParsingLoadable.Parser<HlsPlaylist> mediaPlaylistParser;
@Nullable private EventDispatcher eventDispatcher;
@Nullable private Loader initialPlaylistLoader;
@Nullable private Handler playlistRefreshHandler;
@Nullable private PrimaryPlaylistListener primaryPlaylistListener;
@Nullable private HlsMasterPlaylist masterPlaylist;
@Nullable private Uri primaryMediaPlaylistUrl;
@Nullable private HlsMediaPlaylist primaryMediaPlaylistSnapshot;
private boolean isLive;
private long initialStartTimeUs;
......@@ -95,7 +94,7 @@ public final class DefaultHlsPlaylistTracker
* @param playlistStuckTargetDurationCoefficient A coefficient to apply to the target duration of
* media playlists in order to determine that a non-changing playlist is stuck. Once a
* playlist is deemed stuck, a {@link PlaylistStuckException} is thrown via {@link
* #maybeThrowPlaylistRefreshError(HlsUrl)}.
* #maybeThrowPlaylistRefreshError(Uri)}.
*/
public DefaultHlsPlaylistTracker(
HlsDataSourceFactory dataSourceFactory,
......@@ -142,8 +141,8 @@ public final class DefaultHlsPlaylistTracker
@Override
public void stop() {
primaryHlsUrl = null;
primaryUrlSnapshot = null;
primaryMediaPlaylistUrl = null;
primaryMediaPlaylistSnapshot = null;
masterPlaylist = null;
initialStartTimeUs = C.TIME_UNSET;
initialPlaylistLoader.release();
......@@ -172,7 +171,7 @@ public final class DefaultHlsPlaylistTracker
}
@Override
public HlsMediaPlaylist getPlaylistSnapshot(HlsUrl url, boolean isForPlayback) {
public HlsMediaPlaylist getPlaylistSnapshot(Uri url, boolean isForPlayback) {
HlsMediaPlaylist snapshot = playlistBundles.get(url).getPlaylistSnapshot();
if (snapshot != null && isForPlayback) {
maybeSetPrimaryUrl(url);
......@@ -186,7 +185,7 @@ public final class DefaultHlsPlaylistTracker
}
@Override
public boolean isSnapshotValid(HlsUrl url) {
public boolean isSnapshotValid(Uri url) {
return playlistBundles.get(url).isSnapshotValid();
}
......@@ -195,18 +194,18 @@ public final class DefaultHlsPlaylistTracker
if (initialPlaylistLoader != null) {
initialPlaylistLoader.maybeThrowError();
}
if (primaryHlsUrl != null) {
maybeThrowPlaylistRefreshError(primaryHlsUrl);
if (primaryMediaPlaylistUrl != null) {
maybeThrowPlaylistRefreshError(primaryMediaPlaylistUrl);
}
}
@Override
public void maybeThrowPlaylistRefreshError(HlsUrl url) throws IOException {
public void maybeThrowPlaylistRefreshError(Uri url) throws IOException {
playlistBundles.get(url).maybeThrowPlaylistRefreshError();
}
@Override
public void refreshPlaylist(HlsUrl url) {
public void refreshPlaylist(Uri url) {
playlistBundles.get(url).loadPlaylist();
}
......@@ -230,13 +229,9 @@ public final class DefaultHlsPlaylistTracker
}
this.masterPlaylist = masterPlaylist;
mediaPlaylistParser = playlistParserFactory.createPlaylistParser(masterPlaylist);
primaryHlsUrl = masterPlaylist.variants.get(0);
ArrayList<HlsUrl> urls = new ArrayList<>();
urls.addAll(masterPlaylist.variants);
urls.addAll(masterPlaylist.audios);
urls.addAll(masterPlaylist.subtitles);
createBundles(urls);
MediaPlaylistBundle primaryBundle = playlistBundles.get(primaryHlsUrl);
primaryMediaPlaylistUrl = masterPlaylist.variants.get(0).url;
createBundles(masterPlaylist.mediaPlaylistUrls);
MediaPlaylistBundle primaryBundle = playlistBundles.get(primaryMediaPlaylistUrl);
if (isMediaPlaylist) {
// We don't need to load the playlist again. We can use the same result.
primaryBundle.processLoadedPlaylist((HlsMediaPlaylist) result, loadDurationMs);
......@@ -302,9 +297,9 @@ public final class DefaultHlsPlaylistTracker
int variantsSize = variants.size();
long currentTimeMs = SystemClock.elapsedRealtime();
for (int i = 0; i < variantsSize; i++) {
MediaPlaylistBundle bundle = playlistBundles.get(variants.get(i));
MediaPlaylistBundle bundle = playlistBundles.get(variants.get(i).url);
if (currentTimeMs > bundle.blacklistUntilMs) {
primaryHlsUrl = bundle.playlistUrl;
primaryMediaPlaylistUrl = bundle.playlistUrl;
bundle.loadPlaylist();
return true;
}
......@@ -312,22 +307,33 @@ public final class DefaultHlsPlaylistTracker
return false;
}
private void maybeSetPrimaryUrl(HlsUrl url) {
if (url == primaryHlsUrl
|| !masterPlaylist.variants.contains(url)
|| (primaryUrlSnapshot != null && primaryUrlSnapshot.hasEndTag)) {
// Ignore if the primary url is unchanged, if the url is not a variant url, or if the last
// primary snapshot contains an end tag.
private void maybeSetPrimaryUrl(Uri url) {
if (url.equals(primaryMediaPlaylistUrl)
|| !isVariantUrl(url)
|| (primaryMediaPlaylistSnapshot != null && primaryMediaPlaylistSnapshot.hasEndTag)) {
// Ignore if the primary media playlist URL is unchanged, if the media playlist is not
// referenced directly by a variant, or it the last primary snapshot contains an end tag.
return;
}
primaryHlsUrl = url;
playlistBundles.get(primaryHlsUrl).loadPlaylist();
primaryMediaPlaylistUrl = url;
playlistBundles.get(primaryMediaPlaylistUrl).loadPlaylist();
}
private void createBundles(List<HlsUrl> urls) {
/** Returns whether any of the variants in the master playlist have the specified playlist URL. */
private boolean isVariantUrl(Uri playlistUrl) {
List<Variant> variants = masterPlaylist.variants;
for (int i = 0; i < variants.size(); i++) {
if (playlistUrl.equals(variants.get(i).url)) {
return true;
}
}
return false;
}
private void createBundles(List<Uri> urls) {
int listSize = urls.size();
for (int i = 0; i < listSize; i++) {
HlsUrl url = urls.get(i);
Uri url = urls.get(i);
MediaPlaylistBundle bundle = new MediaPlaylistBundle(url);
playlistBundles.put(url, bundle);
}
......@@ -339,14 +345,14 @@ public final class DefaultHlsPlaylistTracker
* @param url The url of the playlist.
* @param newSnapshot The new snapshot.
*/
private void onPlaylistUpdated(HlsUrl url, HlsMediaPlaylist newSnapshot) {
if (url == primaryHlsUrl) {
if (primaryUrlSnapshot == null) {
private void onPlaylistUpdated(Uri url, HlsMediaPlaylist newSnapshot) {
if (url.equals(primaryMediaPlaylistUrl)) {
if (primaryMediaPlaylistSnapshot == null) {
// This is the first primary url snapshot.
isLive = !newSnapshot.hasEndTag;
initialStartTimeUs = newSnapshot.startTimeUs;
}
primaryUrlSnapshot = newSnapshot;
primaryMediaPlaylistSnapshot = newSnapshot;
primaryPlaylistListener.onPrimaryPlaylistRefreshed(newSnapshot);
}
int listenersSize = listeners.size();
......@@ -355,7 +361,7 @@ public final class DefaultHlsPlaylistTracker
}
}
private boolean notifyPlaylistError(HlsUrl playlistUrl, long blacklistDurationMs) {
private boolean notifyPlaylistError(Uri playlistUrl, long blacklistDurationMs) {
int listenersSize = listeners.size();
boolean anyBlacklistingFailed = false;
for (int i = 0; i < listenersSize; i++) {
......@@ -388,7 +394,7 @@ public final class DefaultHlsPlaylistTracker
return loadedPlaylist.startTimeUs;
}
long primarySnapshotStartTimeUs =
primaryUrlSnapshot != null ? primaryUrlSnapshot.startTimeUs : 0;
primaryMediaPlaylistSnapshot != null ? primaryMediaPlaylistSnapshot.startTimeUs : 0;
if (oldPlaylist == null) {
return primarySnapshotStartTimeUs;
}
......@@ -411,7 +417,9 @@ public final class DefaultHlsPlaylistTracker
}
// TODO: Improve cross-playlist discontinuity adjustment.
int primaryUrlDiscontinuitySequence =
primaryUrlSnapshot != null ? primaryUrlSnapshot.discontinuitySequence : 0;
primaryMediaPlaylistSnapshot != null
? primaryMediaPlaylistSnapshot.discontinuitySequence
: 0;
if (oldPlaylist == null) {
return primaryUrlDiscontinuitySequence;
}
......@@ -435,7 +443,7 @@ public final class DefaultHlsPlaylistTracker
private final class MediaPlaylistBundle
implements Loader.Callback<ParsingLoadable<HlsPlaylist>>, Runnable {
private final HlsUrl playlistUrl;
private final Uri playlistUrl;
private final Loader mediaPlaylistLoader;
private final ParsingLoadable<HlsPlaylist> mediaPlaylistLoadable;
......@@ -447,13 +455,13 @@ public final class DefaultHlsPlaylistTracker
private boolean loadPending;
private IOException playlistError;
public MediaPlaylistBundle(HlsUrl playlistUrl) {
public MediaPlaylistBundle(Uri playlistUrl) {
this.playlistUrl = playlistUrl;
mediaPlaylistLoader = new Loader("DefaultHlsPlaylistTracker:MediaPlaylist");
mediaPlaylistLoadable =
new ParsingLoadable<>(
dataSourceFactory.createDataSource(C.DATA_TYPE_MANIFEST),
playlistUrl.url,
playlistUrl,
C.DATA_TYPE_MANIFEST,
mediaPlaylistParser);
}
......@@ -620,13 +628,13 @@ public final class DefaultHlsPlaylistTracker
// TODO: Allow customization of playlist resets handling.
// The media sequence jumped backwards. The server has probably reset. We do not try
// blacklisting in this case.
playlistError = new PlaylistResetException(playlistUrl.url);
playlistError = new PlaylistResetException(playlistUrl);
notifyPlaylistError(playlistUrl, C.TIME_UNSET);
} else if (currentTimeMs - lastSnapshotChangeMs
> C.usToMs(playlistSnapshot.targetDurationUs)
* playlistStuckTargetDurationCoefficient) {
// TODO: Allow customization of stuck playlists handling.
playlistError = new PlaylistStuckException(playlistUrl.url);
playlistError = new PlaylistStuckException(playlistUrl);
long blacklistDurationMs =
loadErrorHandlingPolicy.getBlacklistDurationMsFor(
C.DATA_TYPE_MANIFEST, loadDurationMs, playlistError, /* errorCount= */ 1);
......@@ -647,7 +655,7 @@ public final class DefaultHlsPlaylistTracker
// Schedule a load if this is the primary playlist and it doesn't have an end tag. Else the
// next load will be scheduled when refreshPlaylist is called, or when this playlist becomes
// the primary.
if (playlistUrl == primaryHlsUrl && !playlistSnapshot.hasEndTag) {
if (playlistUrl.equals(primaryMediaPlaylistUrl) && !playlistSnapshot.hasEndTag) {
loadPlaylist();
}
}
......@@ -661,7 +669,7 @@ public final class DefaultHlsPlaylistTracker
*/
private boolean blacklistPlaylist(long blacklistDurationMs) {
blacklistUntilMs = SystemClock.elapsedRealtime() + blacklistDurationMs;
return primaryHlsUrl == playlistUrl && !maybeSelectNewPrimaryUrl();
return playlistUrl.equals(primaryMediaPlaylistUrl) && !maybeSelectNewPrimaryUrl();
}
}
}
......@@ -50,28 +50,14 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
public static final int GROUP_INDEX_AUDIO = 1;
public static final int GROUP_INDEX_SUBTITLE = 2;
/** Represents a url in an HLS master playlist. */
public abstract static class HlsUrl {
/** A variant (i.e. an #EXT-X-STREAM-INF tag) in a master playlist. */
public static final class Variant {
/** The http url from which the media playlist can be obtained. */
/** The variant's url. */
public final Uri url;
/**
* Format information associated with the HLS url.
*/
public final Format format;
/**
* @param url See {@link #url}.
* @param format See {@link #format}.
*/
public HlsUrl(Uri url, Format format) {
this.url = url;
this.format = format;
}
}
/** A variant in a master playlist. */
public static final class Variant extends HlsUrl {
/** Format information associated with this variant. */
public final Format format;
/** The video rendition group referenced by this variant, or {@code null}. */
@Nullable public final String videoGroupId;
......@@ -100,7 +86,8 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
@Nullable String audioGroupId,
@Nullable String subtitleGroupId,
@Nullable String captionGroupId) {
super(url, format);
this.url = url;
this.format = format;
this.videoGroupId = videoGroupId;
this.audioGroupId = audioGroupId;
this.subtitleGroupId = subtitleGroupId;
......@@ -135,8 +122,14 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
}
}
/** A rendition in a master playlist. */
public static final class Rendition extends HlsUrl {
/** A rendition (i.e. an #EXT-X-MEDIA tag) in a master playlist. */
public static final class Rendition {
/** The rendition's url, or null if the tag does not have a URI attribute. */
@Nullable public final Uri url;
/** Format information associated with this rendition. */
public final Format format;
/** The group to which this rendition belongs. */
public final String groupId;
......@@ -150,14 +143,17 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
* @param groupId See {@link #groupId}.
* @param name See {@link #name}.
*/
public Rendition(Uri url, Format format, String groupId, String name) {
super(url, format);
public Rendition(@Nullable Uri url, Format format, String groupId, String name) {
this.url = url;
this.format = format;
this.groupId = groupId;
this.name = name;
}
}
/** All of the media playlist URLs referenced by the playlist. */
public final List<Uri> mediaPlaylistUrls;
/** The variants declared by the playlist. */
public final List<Variant> variants;
/** The video renditions declared by the playlist. */
......@@ -213,6 +209,9 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
Map<String, String> variableDefinitions,
List<DrmInitData> sessionKeyDrmInitData) {
super(baseUri, tags, hasIndependentSegments);
this.mediaPlaylistUrls =
Collections.unmodifiableList(
getMediaPlaylistUrls(variants, videos, audios, subtitles, closedCaptions));
this.variants = Collections.unmodifiableList(variants);
this.videos = Collections.unmodifiableList(videos);
this.audios = Collections.unmodifiableList(audios);
......@@ -268,7 +267,36 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
/* sessionKeyDrmInitData= */ Collections.emptyList());
}
private static <T extends HlsUrl> List<T> copyStreams(
private static List<Uri> getMediaPlaylistUrls(
List<Variant> variants,
List<Rendition> videos,
List<Rendition> audios,
List<Rendition> subtitles,
List<Rendition> closedCaptions) {
ArrayList<Uri> mediaPlaylistUrls = new ArrayList<>();
for (int i = 0; i < variants.size(); i++) {
Uri uri = variants.get(i).url;
if (!mediaPlaylistUrls.contains(uri)) {
mediaPlaylistUrls.add(uri);
}
}
addMediaPlaylistUrls(videos, mediaPlaylistUrls);
addMediaPlaylistUrls(audios, mediaPlaylistUrls);
addMediaPlaylistUrls(subtitles, mediaPlaylistUrls);
addMediaPlaylistUrls(closedCaptions, mediaPlaylistUrls);
return mediaPlaylistUrls;
}
private static void addMediaPlaylistUrls(List<Rendition> renditions, List<Uri> out) {
for (int i = 0; i < renditions.size(); i++) {
Uri uri = renditions.get(i).url;
if (uri != null && !out.contains(uri)) {
out.add(uri);
}
}
}
private static <T> List<T> copyStreams(
List<T> streams, int groupIndex, List<StreamKey> streamKeys) {
List<T> copiedStreams = new ArrayList<>(streamKeys.size());
// TODO:
......
......@@ -20,7 +20,6 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher;
import com.google.android.exoplayer2.source.hls.HlsDataSourceFactory;
import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl;
import com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy;
import java.io.IOException;
......@@ -81,7 +80,7 @@ public interface HlsPlaylistTracker {
* {@link C#TIME_UNSET} if the playlist should not be blacklisted.
* @return True if blacklisting did not encounter errors. False otherwise.
*/
boolean onPlaylistError(HlsUrl url, long blacklistDurationMs);
boolean onPlaylistError(Uri url, long blacklistDurationMs);
}
/** Thrown when a playlist is considered to be stuck due to a server side error. */
......@@ -164,16 +163,16 @@ public interface HlsPlaylistTracker {
/**
* Returns the most recent snapshot available of the playlist referenced by the provided {@link
* HlsUrl}.
* Uri}.
*
* @param url The {@link HlsUrl} corresponding to the requested media playlist.
* @param url The {@link Uri} corresponding to the requested media playlist.
* @param isForPlayback Whether the caller might use the snapshot to request media segments for
* playback. If true, the primary playlist may be updated to the one requested.
* @return The most recent snapshot of the playlist referenced by the provided {@link HlsUrl}. May
* be null if no snapshot has been loaded yet.
* @return The most recent snapshot of the playlist referenced by the provided {@link Uri}. May be
* null if no snapshot has been loaded yet.
*/
@Nullable
HlsMediaPlaylist getPlaylistSnapshot(HlsUrl url, boolean isForPlayback);
HlsMediaPlaylist getPlaylistSnapshot(Uri url, boolean isForPlayback);
/**
* Returns the start time of the first loaded primary playlist, or {@link C#TIME_UNSET} if no
......@@ -182,15 +181,14 @@ public interface HlsPlaylistTracker {
long getInitialStartTimeUs();
/**
* Returns whether the snapshot of the playlist referenced by the provided {@link HlsUrl} is
* valid, meaning all the segments referenced by the playlist are expected to be available. If the
* Returns whether the snapshot of the playlist referenced by the provided {@link Uri} is valid,
* meaning all the segments referenced by the playlist are expected to be available. If the
* playlist is not valid then some of the segments may no longer be available.
*
* @param url The {@link HlsUrl}.
* @return Whether the snapshot of the playlist referenced by the provided {@link HlsUrl} is
* valid.
* @param url The {@link Uri}.
* @return Whether the snapshot of the playlist referenced by the provided {@link Uri} is valid.
*/
boolean isSnapshotValid(HlsUrl url);
boolean isSnapshotValid(Uri url);
/**
* If the tracker is having trouble refreshing the master playlist or the primary playlist, this
......@@ -201,13 +199,13 @@ public interface HlsPlaylistTracker {
void maybeThrowPrimaryPlaylistRefreshError() throws IOException;
/**
* If the playlist is having trouble refreshing the playlist referenced by the given {@link
* HlsUrl}, this method throws the underlying error.
* If the playlist is having trouble refreshing the playlist referenced by the given {@link Uri},
* this method throws the underlying error.
*
* @param url The {@link HlsUrl}.
* @param url The {@link Uri}.
* @throws IOException The underyling error.
*/
void maybeThrowPlaylistRefreshError(HlsUrl url) throws IOException;
void maybeThrowPlaylistRefreshError(Uri url) throws IOException;
/**
* Requests a playlist refresh and whitelists it.
......@@ -215,9 +213,9 @@ public interface HlsPlaylistTracker {
* <p>The playlist tracker may choose the delay the playlist refresh. The request is discarded if
* a refresh was already pending.
*
* @param url The {@link HlsUrl} of the playlist to be refreshed.
* @param url The {@link Uri} of the playlist to be refreshed.
*/
void refreshPlaylist(HlsUrl url);
void refreshPlaylist(Uri url);
/**
* Returns whether the tracked playlists describe a live stream.
......
......@@ -290,7 +290,7 @@ public class HlsMasterPlaylistParserTest {
public void testVariableSubstitution() throws IOException {
HlsMasterPlaylist playlistWithSubstitutions =
parseMasterPlaylist(PLAYLIST_URI, PLAYLIST_WITH_VARIABLE_SUBSTITUTION);
HlsMasterPlaylist.HlsUrl variant = playlistWithSubstitutions.variants.get(0);
HlsMasterPlaylist.Variant variant = playlistWithSubstitutions.variants.get(0);
assertThat(variant.format.codecs).isEqualTo("mp4a.40.5");
assertThat(variant.url)
.isEqualTo(Uri.parse("http://example.com/This/{$nested}/reference/shouldnt/work"));
......
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