Commit 74b7b905 by aquilescanta Committed by Oliver Woodman

Propagate non-MediaSource-preparation playlist load errors through HlsChunkSource

Erroneous condition:
===================
If the track selection contains a subset of the available variants in the
master playlist, but only the selected variants return 404, the playlist
tracker will never propagate the error.

Fix:
====
The Chunk source will propagate the playlist load error if no more
alternative playlists are available (because all are already blacklisted).

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=190624484
parent 71e5b2c0
......@@ -50,6 +50,8 @@
* Removed default renderer time offset of 60000000 from internal player. The
actual renderer timestamp offset can be obtained by listening to
`BaseRenderer.onStreamChanged`.
* HLS: Fix playlist loading error propagation when the current selection does
not include all of the playlist's variants.
### 2.7.1 ###
......
......@@ -105,6 +105,7 @@ import java.util.List;
// in TrackSelection to avoid unexpected behavior.
private TrackSelection trackSelection;
private long liveEdgeTimeUs;
private boolean seenExpectedPlaylistError;
/**
* @param extractorFactory An {@link HlsExtractorFactory} from which to obtain the extractors for
......@@ -150,7 +151,7 @@ import java.util.List;
if (fatalError != null) {
throw fatalError;
}
if (expectedPlaylistUrl != null) {
if (expectedPlaylistUrl != null && seenExpectedPlaylistError) {
playlistTracker.maybeThrowPlaylistRefreshError(expectedPlaylistUrl);
}
}
......@@ -217,8 +218,6 @@ import java.util.List;
HlsChunkHolder out) {
int oldVariantIndex = previous == null ? C.INDEX_UNSET
: trackGroup.indexOf(previous.trackFormat);
expectedPlaylistUrl = null;
long bufferedDurationUs = loadPositionUs - playbackPositionUs;
long timeToLiveEdgeUs = resolveTimeToLiveEdgeUs(playbackPositionUs);
if (previous != null && !independentSegments) {
......@@ -243,6 +242,7 @@ import java.util.List;
HlsUrl selectedUrl = variants[selectedVariantIndex];
if (!playlistTracker.isSnapshotValid(selectedUrl)) {
out.playlist = selectedUrl;
seenExpectedPlaylistError &= expectedPlaylistUrl == selectedUrl;
expectedPlaylistUrl = selectedUrl;
// Retry when playlist is refreshed.
return;
......@@ -291,10 +291,14 @@ import java.util.List;
out.endOfStream = true;
} else /* Live */ {
out.playlist = selectedUrl;
seenExpectedPlaylistError &= expectedPlaylistUrl == selectedUrl;
expectedPlaylistUrl = selectedUrl;
}
return;
}
// We have a valid playlist snapshot, we can discard any playlist errors at this point.
seenExpectedPlaylistError = false;
expectedPlaylistUrl = null;
// Handle encryption.
HlsMediaPlaylist.Segment segment = mediaPlaylist.segments.get(chunkIndex);
......@@ -389,19 +393,25 @@ import java.util.List;
}
/**
* Called when a playlist is blacklisted.
* Called when a playlist load encounters an error.
*
* @param url The url that references the blacklisted playlist.
* @param blacklistMs The amount of milliseconds for which the playlist was blacklisted.
* @param url The url of the playlist whose load encountered an error.
* @param shouldBlacklist Whether the playlist should be blacklisted.
* @return True if blacklisting did not encounter errors. False otherwise.
*/
public void onPlaylistBlacklisted(HlsUrl url, long blacklistMs) {
public boolean onPlaylistError(HlsUrl url, boolean shouldBlacklist) {
int trackGroupIndex = trackGroup.indexOf(url.format);
if (trackGroupIndex != C.INDEX_UNSET) {
int trackSelectionIndex = trackSelection.indexOf(trackGroupIndex);
if (trackSelectionIndex != C.INDEX_UNSET) {
trackSelection.blacklist(trackSelectionIndex, blacklistMs);
}
if (trackGroupIndex == C.INDEX_UNSET) {
return true;
}
int trackSelectionIndex = trackSelection.indexOf(trackGroupIndex);
if (trackSelectionIndex == C.INDEX_UNSET) {
return true;
}
seenExpectedPlaylistError |= expectedPlaylistUrl == url;
return !shouldBlacklist
|| trackSelection.blacklist(
trackSelectionIndex, ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS);
}
// Private methods.
......
......@@ -305,11 +305,13 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
}
@Override
public void onPlaylistBlacklisted(HlsUrl url, long blacklistMs) {
public boolean onPlaylistError(HlsUrl url, boolean shouldBlacklist) {
boolean noBlacklistingFailure = true;
for (HlsSampleStreamWrapper streamWrapper : sampleStreamWrappers) {
streamWrapper.onPlaylistBlacklisted(url, blacklistMs);
noBlacklistingFailure &= streamWrapper.onPlaylistError(url, shouldBlacklist);
}
callback.onContinueLoadingRequested(this);
return noBlacklistingFailure;
}
// Internal methods.
......
......@@ -414,8 +414,8 @@ import java.util.Arrays;
chunkSource.setIsTimestampMaster(isTimestampMaster);
}
public void onPlaylistBlacklisted(HlsUrl url, long blacklistMs) {
chunkSource.onPlaylistBlacklisted(url, blacklistMs);
public boolean onPlaylistError(HlsUrl url, boolean shouldBlacklist) {
return chunkSource.onPlaylistError(url, shouldBlacklist);
}
// SampleStream implementation.
......
......@@ -99,11 +99,10 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
* Called if an error is encountered while loading a playlist.
*
* @param url The loaded url that caused the error.
* @param blacklistDurationMs The number of milliseconds for which the playlist has been
* blacklisted.
* @param shouldBlacklist Whether the playlist should be blacklisted.
* @return True if blacklisting did not encounter errors. False otherwise.
*/
void onPlaylistBlacklisted(HlsUrl url, long blacklistDurationMs);
boolean onPlaylistError(HlsUrl url, boolean shouldBlacklist);
}
/**
......@@ -391,11 +390,13 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
}
}
private void notifyPlaylistBlacklisting(HlsUrl url, long blacklistMs) {
private boolean notifyPlaylistError(HlsUrl playlistUrl, boolean shouldBlacklist) {
int listenersSize = listeners.size();
boolean anyBlacklistingFailed = false;
for (int i = 0; i < listenersSize; i++) {
listeners.get(i).onPlaylistBlacklisted(url, blacklistMs);
anyBlacklistingFailed |= !listeners.get(i).onPlaylistError(playlistUrl, shouldBlacklist);
}
return anyBlacklistingFailed;
}
private HlsMediaPlaylist getLatestPlaylistSnapshot(HlsMediaPlaylist oldPlaylist,
......@@ -565,14 +566,15 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
boolean isFatal = error instanceof ParserException;
eventDispatcher.loadError(loadable.dataSpec, C.DATA_TYPE_MANIFEST, elapsedRealtimeMs,
loadDurationMs, loadable.bytesLoaded(), error, isFatal);
boolean shouldBlacklist = ChunkedTrackBlacklistUtil.shouldBlacklist(error);
boolean shouldRetryIfNotFatal = notifyPlaylistError(playlistUrl, shouldBlacklist);
if (isFatal) {
return Loader.DONT_RETRY_FATAL;
}
boolean shouldRetry = true;
if (ChunkedTrackBlacklistUtil.shouldBlacklist(error)) {
shouldRetry = blacklistPlaylist();
if (shouldBlacklist) {
shouldRetryIfNotFatal |= blacklistPlaylist();
}
return shouldRetry ? Loader.RETRY : Loader.DONT_RETRY;
return shouldRetryIfNotFatal ? Loader.RETRY : Loader.DONT_RETRY;
}
// Runnable implementation.
......@@ -603,11 +605,13 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
< playlistSnapshot.mediaSequence) {
// The media sequence jumped backwards. The server has probably reset.
playlistError = new PlaylistResetException(playlistUrl.url);
notifyPlaylistError(playlistUrl, false);
} else if (currentTimeMs - lastSnapshotChangeMs
> C.usToMs(playlistSnapshot.targetDurationUs)
* PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT) {
// The playlist seems to be stuck. Blacklist it.
playlistError = new PlaylistStuckException(playlistUrl.url);
notifyPlaylistError(playlistUrl, true);
blacklistPlaylist();
}
}
......@@ -631,7 +635,6 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
private boolean blacklistPlaylist() {
blacklistUntilMs = SystemClock.elapsedRealtime()
+ ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS;
notifyPlaylistBlacklisting(playlistUrl, ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS);
return primaryHlsUrl == playlistUrl && !maybeSelectNewPrimaryUrl();
}
......
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