mirror of
https://github.com/samsonjs/media.git
synced 2026-04-10 12:05:47 +00:00
Detect playlist stuck and playlist reset conditions in HLS
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
This commit is contained in:
parent
dda3616f5a
commit
a04663372f
1 changed files with 72 additions and 10 deletions
|
|
@ -40,6 +40,38 @@ import java.util.List;
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
|
|
@ -75,6 +107,11 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
|
@ -213,14 +250,14 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -441,9 +478,11 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||
|
||||
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;
|
||||
|
|
@ -483,6 +522,13 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||
}
|
||||
}
|
||||
|
||||
public void maybeThrowPlaylistRefreshError() throws IOException {
|
||||
mediaPlaylistLoader.maybeThrowError();
|
||||
if (playlistError != null) {
|
||||
throw playlistError;
|
||||
}
|
||||
}
|
||||
|
||||
// Loader.Callback implementation.
|
||||
|
||||
@Override
|
||||
|
|
@ -494,8 +540,7 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -517,10 +562,7 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||
}
|
||||
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;
|
||||
|
|
@ -538,14 +580,28 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||
|
||||
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) {
|
||||
|
|
@ -554,6 +610,12 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||
}
|
||||
}
|
||||
|
||||
private void blacklistPlaylist() {
|
||||
blacklistUntilMs = SystemClock.elapsedRealtime()
|
||||
+ ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS;
|
||||
notifyPlaylistBlacklisting(playlistUrl, ChunkedTrackBlacklistUtil.DEFAULT_TRACK_BLACKLIST_MS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue