Commit ee834680 by Oliver Woodman

Blacklist playlists that 404/410.

parent c6c6f2d8
...@@ -22,12 +22,14 @@ import com.google.android.exoplayer.upstream.Aes128DataSource; ...@@ -22,12 +22,14 @@ import com.google.android.exoplayer.upstream.Aes128DataSource;
import com.google.android.exoplayer.upstream.BandwidthMeter; import com.google.android.exoplayer.upstream.BandwidthMeter;
import com.google.android.exoplayer.upstream.DataSource; import com.google.android.exoplayer.upstream.DataSource;
import com.google.android.exoplayer.upstream.DataSpec; import com.google.android.exoplayer.upstream.DataSpec;
import com.google.android.exoplayer.upstream.HttpDataSource.InvalidResponseCodeException;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
import com.google.android.exoplayer.util.BitArray; import com.google.android.exoplayer.util.BitArray;
import com.google.android.exoplayer.util.Util; import com.google.android.exoplayer.util.Util;
import android.net.Uri; import android.net.Uri;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
...@@ -96,6 +98,7 @@ public class HlsChunkSource { ...@@ -96,6 +98,7 @@ public class HlsChunkSource {
*/ */
public static final long DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS = 20000; public static final long DEFAULT_MAX_BUFFER_TO_SWITCH_DOWN_MS = 20000;
private static final String TAG = "HlsChunkSource";
private static final float BANDWIDTH_FRACTION = 0.8f; private static final float BANDWIDTH_FRACTION = 0.8f;
private final SamplePool samplePool = new TsExtractor.SamplePool(); private final SamplePool samplePool = new TsExtractor.SamplePool();
...@@ -113,6 +116,7 @@ public class HlsChunkSource { ...@@ -113,6 +116,7 @@ public class HlsChunkSource {
private final long maxBufferDurationToSwitchDownUs; private final long maxBufferDurationToSwitchDownUs;
/* package */ final HlsMediaPlaylist[] mediaPlaylists; /* package */ final HlsMediaPlaylist[] mediaPlaylists;
/* package */ final boolean[] mediaPlaylistBlacklistFlags;
/* package */ final long[] lastMediaPlaylistLoadTimesMs; /* package */ final long[] lastMediaPlaylistLoadTimesMs;
/* package */ boolean live; /* package */ boolean live;
/* package */ long durationUs; /* package */ long durationUs;
...@@ -165,12 +169,14 @@ public class HlsChunkSource { ...@@ -165,12 +169,14 @@ public class HlsChunkSource {
if (playlist.type == HlsPlaylist.TYPE_MEDIA) { if (playlist.type == HlsPlaylist.TYPE_MEDIA) {
enabledVariants = new Variant[] {new Variant(0, playlistUrl, 0, null, -1, -1)}; enabledVariants = new Variant[] {new Variant(0, playlistUrl, 0, null, -1, -1)};
mediaPlaylists = new HlsMediaPlaylist[1]; mediaPlaylists = new HlsMediaPlaylist[1];
mediaPlaylistBlacklistFlags = new boolean[1];
lastMediaPlaylistLoadTimesMs = new long[1]; lastMediaPlaylistLoadTimesMs = new long[1];
setMediaPlaylist(0, (HlsMediaPlaylist) playlist); setMediaPlaylist(0, (HlsMediaPlaylist) playlist);
} else { } else {
Assertions.checkState(playlist.type == HlsPlaylist.TYPE_MASTER); Assertions.checkState(playlist.type == HlsPlaylist.TYPE_MASTER);
enabledVariants = filterVariants((HlsMasterPlaylist) playlist, variantIndices); enabledVariants = filterVariants((HlsMasterPlaylist) playlist, variantIndices);
mediaPlaylists = new HlsMediaPlaylist[enabledVariants.length]; mediaPlaylists = new HlsMediaPlaylist[enabledVariants.length];
mediaPlaylistBlacklistFlags = new boolean[enabledVariants.length];
lastMediaPlaylistLoadTimesMs = new long[enabledVariants.length]; lastMediaPlaylistLoadTimesMs = new long[enabledVariants.length];
} }
...@@ -327,6 +333,37 @@ public class HlsChunkSource { ...@@ -327,6 +333,37 @@ public class HlsChunkSource {
startTimeUs, endTimeUs, chunkMediaSequence, isLastChunk); startTimeUs, endTimeUs, chunkMediaSequence, isLastChunk);
} }
/**
* Invoked when an error occurs loading a chunk.
*
* @param chunk The chunk whose load failed.
* @param e The failure.
* @return True if the error was handled by the source. False otherwise.
*/
public boolean onLoadError(HlsChunk chunk, IOException e) {
if ((chunk instanceof MediaPlaylistChunk) && (e instanceof InvalidResponseCodeException)) {
InvalidResponseCodeException responseCodeException = (InvalidResponseCodeException) e;
int responseCode = responseCodeException.responseCode;
if (responseCode == 404 || responseCode == 410) {
MediaPlaylistChunk playlistChunk = (MediaPlaylistChunk) chunk;
mediaPlaylistBlacklistFlags[playlistChunk.variantIndex] = true;
if (!allPlaylistsBlacklisted()) {
// We've handled the 404/410 by blacklisting the playlist.
Log.w(TAG, "Blacklisted playlist (" + responseCode + "): "
+ playlistChunk.dataSpec.uri);
return true;
} else {
// This was the last non-blacklisted playlist. Don't blacklist it.
Log.w(TAG, "Final playlist not blacklisted (" + responseCode + "): "
+ playlistChunk.dataSpec.uri);
mediaPlaylistBlacklistFlags[playlistChunk.variantIndex] = false;
return false;
}
}
}
return false;
}
private int getNextVariantIndex(TsChunk previousTsChunk, long playbackPositionUs) { private int getNextVariantIndex(TsChunk previousTsChunk, long playbackPositionUs) {
int idealVariantIndex = getVariantIndexForBandwdith( int idealVariantIndex = getVariantIndexForBandwdith(
(int) (bandwidthMeter.getBitrateEstimate() * BANDWIDTH_FRACTION)); (int) (bandwidthMeter.getBitrateEstimate() * BANDWIDTH_FRACTION));
...@@ -340,7 +377,8 @@ public class HlsChunkSource { ...@@ -340,7 +377,8 @@ public class HlsChunkSource {
: adaptiveMode == ADAPTIVE_MODE_SPLICE ? previousTsChunk.startTimeUs : adaptiveMode == ADAPTIVE_MODE_SPLICE ? previousTsChunk.startTimeUs
: previousTsChunk.endTimeUs; : previousTsChunk.endTimeUs;
long bufferedUs = bufferedPositionUs - playbackPositionUs; long bufferedUs = bufferedPositionUs - playbackPositionUs;
if ((idealVariantIndex > variantIndex && bufferedUs < maxBufferDurationToSwitchDownUs) if (mediaPlaylistBlacklistFlags[variantIndex]
|| (idealVariantIndex > variantIndex && bufferedUs < maxBufferDurationToSwitchDownUs)
|| (idealVariantIndex < variantIndex && bufferedUs > minBufferDurationToSwitchUpUs)) { || (idealVariantIndex < variantIndex && bufferedUs > minBufferDurationToSwitchUpUs)) {
// Switch variant. // Switch variant.
return idealVariantIndex; return idealVariantIndex;
...@@ -350,12 +388,16 @@ public class HlsChunkSource { ...@@ -350,12 +388,16 @@ public class HlsChunkSource {
} }
private int getVariantIndexForBandwdith(int bandwidth) { private int getVariantIndexForBandwdith(int bandwidth) {
for (int i = 0; i < enabledVariants.length - 1; i++) { int lowestQualityEnabledVariant = 0;
if (enabledVariants[i].bandwidth <= bandwidth) { for (int i = 0; i < enabledVariants.length; i++) {
return i; if (!mediaPlaylistBlacklistFlags[i]) {
if (enabledVariants[i].bandwidth <= bandwidth) {
return i;
}
lowestQualityEnabledVariant = i;
} }
} }
return enabledVariants.length - 1; return lowestQualityEnabledVariant;
} }
private boolean shouldRerequestMediaPlaylist(int variantIndex) { private boolean shouldRerequestMediaPlaylist(int variantIndex) {
...@@ -475,10 +517,20 @@ public class HlsChunkSource { ...@@ -475,10 +517,20 @@ public class HlsChunkSource {
return false; return false;
} }
private boolean allPlaylistsBlacklisted() {
for (int i = 0; i < mediaPlaylistBlacklistFlags.length; i++) {
if (!mediaPlaylistBlacklistFlags[i]) {
return false;
}
}
return true;
}
private class MediaPlaylistChunk extends BitArrayChunk { private class MediaPlaylistChunk extends BitArrayChunk {
@SuppressWarnings("hiding") @SuppressWarnings("hiding")
private final int variantIndex; /* package */ final int variantIndex;
private final Uri playlistBaseUri; private final Uri playlistBaseUri;
public MediaPlaylistChunk(int variantIndex, DataSource dataSource, DataSpec dataSpec, public MediaPlaylistChunk(int variantIndex, DataSource dataSource, DataSpec dataSpec,
......
...@@ -306,9 +306,14 @@ public class HlsSampleSource implements SampleSource, Loader.Callback { ...@@ -306,9 +306,14 @@ public class HlsSampleSource implements SampleSource, Loader.Callback {
@Override @Override
public void onLoadError(Loadable loadable, IOException e) { public void onLoadError(Loadable loadable, IOException e) {
currentLoadableException = e; if (chunkSource.onLoadError(currentLoadable, e)) {
currentLoadableExceptionCount++; // Error handled by source.
currentLoadableExceptionTimestamp = SystemClock.elapsedRealtime(); clearCurrentLoadable();
} else {
currentLoadableException = e;
currentLoadableExceptionCount++;
currentLoadableExceptionTimestamp = SystemClock.elapsedRealtime();
}
maybeStartLoading(); maybeStartLoading();
} }
......
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