Blacklist HLS media playlists that return 4xx error codes

Issue:#87

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=139443476
This commit is contained in:
aquilescanta 2016-11-17 03:58:01 -08:00 committed by Oliver Woodman
parent 2add12d5f7
commit a8a2ef4a24
4 changed files with 63 additions and 14 deletions

View file

@ -33,7 +33,7 @@ import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.source.chunk.ChunkedTrackBlacklistUtil;
import com.google.android.exoplayer2.source.chunk.DataChunk;
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.HlsMediaPlaylist.Segment;
import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker;
@ -76,7 +76,7 @@ import java.util.Locale;
/**
* Indicates that the chunk source is waiting for the referred playlist to be refreshed.
*/
public HlsMasterPlaylist.HlsUrl playlist;
public HlsUrl playlist;
/**
* Clears the holder.
@ -99,7 +99,7 @@ import java.util.Locale;
private final DataSource dataSource;
private final TimestampAdjusterProvider timestampAdjusterProvider;
private final HlsMasterPlaylist.HlsUrl[] variants;
private final HlsUrl[] variants;
private final HlsPlaylistTracker playlistTracker;
private final TrackGroup trackGroup;
@ -125,7 +125,7 @@ import java.util.Locale;
* multiple {@link HlsChunkSource}s are used for a single playback, they should all share the
* same provider.
*/
public HlsChunkSource(HlsPlaylistTracker playlistTracker, HlsMasterPlaylist.HlsUrl[] variants,
public HlsChunkSource(HlsPlaylistTracker playlistTracker, HlsUrl[] variants,
DataSource dataSource, TimestampAdjusterProvider timestampAdjusterProvider) {
this.playlistTracker = playlistTracker;
this.variants = variants;
@ -183,7 +183,7 @@ import java.util.Locale;
* 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 HlsMasterPlaylist.HlsUrl} that refers to the playlist that needs refreshing.
* contain the {@link HlsUrl} that refers to the playlist that needs refreshing.
*
* @param previous The most recently loaded media chunk.
* @param playbackPositionUs The current playback position. If {@code previous} is null then this
@ -198,6 +198,8 @@ import java.util.Locale;
// require downloading overlapping segments.
long bufferedDurationUs = previous == null ? 0
: Math.max(0, previous.getAdjustedStartTimeUs() - playbackPositionUs);
// Select the variant.
trackSelection.updateSelectedTrack(bufferedDurationUs);
int newVariantIndex = trackSelection.getSelectedIndexInTrackGroup();
@ -209,6 +211,7 @@ import java.util.Locale;
return;
}
// Select the chunk.
int chunkMediaSequence;
if (previous == null || switchingVariant) {
long targetPositionUs = previous == null ? playbackPositionUs : previous.startTimeUs;
@ -244,6 +247,7 @@ import java.util.Locale;
return;
}
// Handle encryption.
HlsMediaPlaylist.Segment segment = mediaPlaylist.segments.get(chunkIndex);
// Check if encryption is specified.
@ -272,7 +276,7 @@ import java.util.Locale;
Uri chunkUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.url);
// Configure the extractor that will read the chunk.
// Set the extractor that will read the chunk.
Extractor extractor;
boolean useInitializedExtractor = lastLoadedInitializationChunk != null
&& lastLoadedInitializationChunk.format == format;
@ -343,6 +347,7 @@ import java.util.Locale;
extractorNeedsInit = false;
}
// Initialize the extractor.
if (needNewExtractor && mediaPlaylist.initializationSegment != null
&& !useInitializedExtractor) {
out.chunk = buildInitializationChunk(mediaPlaylist, extractor, format);
@ -388,12 +393,28 @@ import java.util.Locale;
*
* @param chunk The chunk whose load encountered the error.
* @param cancelable Whether the load can be canceled.
* @param e The error.
* @param error The error.
* @return Whether the load should be canceled.
*/
public boolean onChunkLoadError(Chunk chunk, boolean cancelable, IOException e) {
public boolean onChunkLoadError(Chunk chunk, boolean cancelable, IOException error) {
return cancelable && ChunkedTrackBlacklistUtil.maybeBlacklistTrack(trackSelection,
trackSelection.indexOf(trackGroup.indexOf(chunk.trackFormat)), e);
trackSelection.indexOf(trackGroup.indexOf(chunk.trackFormat)), error);
}
/**
* Called when an error is encountered while loading a playlist.
*
* @param url The url that references the playlist whose load encountered the error.
* @param error The error.
*/
public void onPlaylistLoadError(HlsUrl url, IOException error) {
int trackGroupIndex = trackGroup.indexOf(url.format);
if (trackGroupIndex == C.INDEX_UNSET) {
// The url is not handled by this chunk source.
return;
}
ChunkedTrackBlacklistUtil.maybeBlacklistTrack(trackSelection,
trackSelection.indexOf(trackGroupIndex), error);
}
// Private methods.

View file

@ -268,6 +268,14 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
}
}
@Override
public void onPlaylistLoadError(HlsMasterPlaylist.HlsUrl url, IOException error) {
for (HlsSampleStreamWrapper sampleStreamWrapper : enabledSampleStreamWrappers) {
sampleStreamWrapper.onPlaylistLoadError(url, error);
}
callback.onContinueLoadingRequested(this);
}
// Internal methods.
private void buildAndPrepareSampleStreamWrappers() {

View file

@ -33,6 +33,7 @@ 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.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.Loader;
@ -274,6 +275,10 @@ import java.util.LinkedList;
return largestQueuedTimestampUs;
}
public void onPlaylistLoadError(HlsUrl url, IOException error) {
chunkSource.onPlaylistLoadError(url, error);
}
// SampleStream implementation.
/* package */ boolean isReady(int group) {

View file

@ -61,6 +61,14 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
*/
void onPlaylistChanged();
/**
* Called if an error is encountered while loading the target playlist.
*
* @param url The loaded url that caused the error.
* @param error The loading error.
*/
void onPlaylistLoadError(HlsUrl url, IOException error);
}
/**
@ -131,11 +139,11 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
}
/**
* Gets the most recent snapshot available of the playlist referred by the provided
* Returns the most recent snapshot available of the playlist referenced by the provided
* {@link HlsUrl}.
*
* @param url The {@link HlsUrl} corresponding to the requested media playlist.
* @return The most recent snapshot of the playlist referred by the provided {@link HlsUrl}. May
* @return The most recent snapshot of the playlist referenced by the provided {@link HlsUrl}. May
* be null if no snapshot has been loaded yet.
*/
public HlsMediaPlaylist getPlaylistSnapshot(HlsUrl url) {
@ -168,7 +176,7 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
}
/**
* Triggers a playlist refresh and sets the callback to be called once the playlist referred by
* Triggers a playlist refresh and sets the callback to be called once the playlist referenced by
* the provided {@link HlsUrl} changes.
*
* @param key The {@link HlsUrl} of the playlist to be refreshed.
@ -410,11 +418,18 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
@Override
public int onLoadError(ParsingLoadable<HlsPlaylist> loadable, long elapsedRealtimeMs,
long loadDurationMs, IOException error) {
// TODO: Add support for playlist blacklisting in response to server error codes.
// TODO: Change primary playlist if this is the primary playlist bundle.
boolean isFatal = error instanceof ParserException;
eventDispatcher.loadError(loadable.dataSpec, C.DATA_TYPE_MANIFEST, elapsedRealtimeMs,
loadDurationMs, loadable.bytesLoaded(), error, isFatal);
return isFatal ? Loader.DONT_RETRY_FATAL : Loader.RETRY;
if (callback != null) {
callback.onPlaylistLoadError(playlistUrl, error);
}
if (isFatal) {
return Loader.DONT_RETRY_FATAL;
} else {
return primaryHlsUrl == playlistUrl ? Loader.RETRY : Loader.DONT_RETRY;
}
}
// Runnable implementation.