Skip to content

Commit

Permalink
Detect playlist stuck and playlist reset conditions in HLS
Browse files Browse the repository at this point in the history
This CL aims that the player fails upon:

- Playlist that don't change in a suspiciously long time,
  which might mean there are server side issues.
- Playlist with a media sequence lower that its last snapshot
  and no overlapping segments.

This two error conditions are propagated through the renderer,
but not through MediaSource#maybeThrowSourceInfoRefreshError.

Issue:#2872

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=160899995
  • Loading branch information
AquilesCanta authored and Oliver Woodman committed Jul 15, 2017
1 parent 1855423 commit 055abc7
Showing 1 changed file with 72 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,38 @@
*/
public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable<HlsPlaylist>> {

/**
* Thrown when a playlist is considered to be stuck due to a server side error.
*/
public static final class PlaylistStuckException extends IOException {

/**
* The url of the stuck playlist.
*/
public final String url;

private PlaylistStuckException(String url) {
this.url = url;
}

}

/**
* Thrown when the media sequence of a new snapshot indicates the server has reset.
*/
public static final class PlaylistResetException extends IOException {

/**
* The url of the reset playlist.
*/
public final String url;

private PlaylistResetException(String url) {
this.url = url;
}

}

/**
* Listener for primary playlist changes.
*/
Expand Down Expand Up @@ -75,6 +107,11 @@ public interface PlaylistEventListener {

}

/**
* Coefficient applied on the target duration of a playlist to determine the amount of time after
* which an unchanging playlist is considered stuck.
*/
private static final double PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT = 3.5;
/**
* The minimum number of milliseconds that a url is kept as primary url, if no
* {@link #getPlaylistSnapshot} call is made for that url.
Expand Down Expand Up @@ -213,14 +250,14 @@ public void maybeThrowPrimaryPlaylistRefreshError() throws IOException {
}

/**
* If the playlist is having trouble loading 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 HlsUrl}, this method throws the underlying error.
*
* @param url The {@link HlsUrl}.
* @throws IOException The underyling error.
*/
public void maybeThrowPlaylistRefreshError(HlsUrl url) throws IOException {
playlistBundles.get(url).mediaPlaylistLoader.maybeThrowError();
playlistBundles.get(url).maybeThrowPlaylistRefreshError();
}

/**
Expand Down Expand Up @@ -441,9 +478,11 @@ private final class MediaPlaylistBundle implements Loader.Callback<ParsingLoadab

private HlsMediaPlaylist playlistSnapshot;
private long lastSnapshotLoadMs;
private long lastSnapshotChangeMs;
private long lastSnapshotAccessTimeMs;
private long blacklistUntilMs;
private boolean pendingRefresh;
private IOException playlistError;

public MediaPlaylistBundle(HlsUrl playlistUrl, long initialLastSnapshotAccessTimeMs) {
this.playlistUrl = playlistUrl;
Expand Down Expand Up @@ -483,6 +522,13 @@ public void loadPlaylist() {
}
}

public void maybeThrowPlaylistRefreshError() throws IOException {
mediaPlaylistLoader.maybeThrowError();
if (playlistError != null) {
throw playlistError;
}
}

// Loader.Callback implementation.

@Override
Expand All @@ -494,8 +540,7 @@ public void onLoadCompleted(ParsingLoadable<HlsPlaylist> loadable, long elapsedR
eventDispatcher.loadCompleted(loadable.dataSpec, C.DATA_TYPE_MANIFEST, elapsedRealtimeMs,
loadDurationMs, loadable.bytesLoaded());
} else {
onLoadError(loadable, elapsedRealtimeMs, loadDurationMs,
new ParserException("Loaded playlist has unexpected type."));
playlistError = new ParserException("Loaded playlist has unexpected type.");
}
}

Expand All @@ -517,10 +562,7 @@ public int onLoadError(ParsingLoadable<HlsPlaylist> loadable, long elapsedRealti
}
boolean shouldRetry = true;
if (ChunkedTrackBlacklistUtil.shouldBlacklist(error)) {
blacklistUntilMs =
SystemClock.elapsedRealtime() + ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS;
notifyPlaylistBlacklisting(playlistUrl,
ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS);
blacklistPlaylist();
shouldRetry = primaryHlsUrl == playlistUrl && !maybeSelectNewPrimaryUrl();
}
return shouldRetry ? Loader.RETRY : Loader.DONT_RETRY;
Expand All @@ -538,14 +580,28 @@ public void run() {

private void processLoadedPlaylist(HlsMediaPlaylist loadedPlaylist) {
HlsMediaPlaylist oldPlaylist = playlistSnapshot;
lastSnapshotLoadMs = SystemClock.elapsedRealtime();
long currentTimeMs = SystemClock.elapsedRealtime();
lastSnapshotLoadMs = currentTimeMs;
playlistSnapshot = getLatestPlaylistSnapshot(oldPlaylist, loadedPlaylist);
long refreshDelayUs = C.TIME_UNSET;
if (playlistSnapshot != oldPlaylist) {
playlistError = null;
lastSnapshotChangeMs = currentTimeMs;
if (onPlaylistUpdated(playlistUrl, playlistSnapshot)) {
refreshDelayUs = playlistSnapshot.targetDurationUs;
}
} else if (!playlistSnapshot.hasEndTag) {
if (currentTimeMs - lastSnapshotChangeMs
> C.usToMs(playlistSnapshot.targetDurationUs)
* PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT) {
// The playlist seems to be stuck, we blacklist it.
playlistError = new PlaylistStuckException(playlistUrl.url);
blacklistPlaylist();
} else if (loadedPlaylist.mediaSequence + loadedPlaylist.segments.size()
< playlistSnapshot.mediaSequence) {
// The media sequence has jumped backwards. The server has likely reset.
playlistError = new PlaylistResetException(playlistUrl.url);
}
refreshDelayUs = playlistSnapshot.targetDurationUs / 2;
}
if (refreshDelayUs != C.TIME_UNSET) {
Expand All @@ -554,6 +610,12 @@ private void processLoadedPlaylist(HlsMediaPlaylist loadedPlaylist) {
}
}

private void blacklistPlaylist() {
blacklistUntilMs = SystemClock.elapsedRealtime()
+ ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS;
notifyPlaylistBlacklisting(playlistUrl, ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS);
}

}

}

0 comments on commit 055abc7

Please sign in to comment.