Prevent old playlist snapshots from being used on live HLS streams

This aims to replace InvalidCodeResponse's with BLWE's caused by trying to
load chunks that have been removed from the server.

Issue:#2344

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=145679171
This commit is contained in:
aquilescanta 2017-01-26 08:55:42 -08:00 committed by Oliver Woodman
parent 25c18ca1b4
commit c49d142981
2 changed files with 43 additions and 12 deletions

View File

@ -194,15 +194,16 @@ import java.util.Locale;
// Select the variant.
trackSelection.updateSelectedTrack(bufferedDurationUs);
int newVariantIndex = trackSelection.getSelectedIndexInTrackGroup();
int selectedVariantIndex = trackSelection.getSelectedIndexInTrackGroup();
boolean switchingVariant = oldVariantIndex != newVariantIndex;
HlsMediaPlaylist mediaPlaylist = playlistTracker.getPlaylistSnapshot(variants[newVariantIndex]);
if (mediaPlaylist == null) {
out.playlist = variants[newVariantIndex];
boolean switchingVariant = oldVariantIndex != selectedVariantIndex;
HlsUrl selectedUrl = variants[selectedVariantIndex];
if (!playlistTracker.isSnapshotValid(selectedUrl)) {
out.playlist = selectedUrl;
// Retry when playlist is refreshed.
return;
}
HlsMediaPlaylist mediaPlaylist = playlistTracker.getPlaylistSnapshot(selectedUrl);
// Select the chunk.
int chunkMediaSequence;
@ -218,8 +219,9 @@ import java.util.Locale;
if (chunkMediaSequence < mediaPlaylist.mediaSequence && previous != null) {
// We try getting the next chunk without adapting in case that's the reason for falling
// behind the live window.
newVariantIndex = oldVariantIndex;
mediaPlaylist = playlistTracker.getPlaylistSnapshot(variants[newVariantIndex]);
selectedVariantIndex = oldVariantIndex;
selectedUrl = variants[selectedVariantIndex];
mediaPlaylist = playlistTracker.getPlaylistSnapshot(selectedUrl);
chunkMediaSequence = previous.getNextChunkIndex();
}
}
@ -236,7 +238,7 @@ import java.util.Locale;
if (mediaPlaylist.hasEndTag) {
out.endOfStream = true;
} else /* Live */ {
out.playlist = variants[newVariantIndex];
out.playlist = selectedUrl;
}
return;
}
@ -249,7 +251,7 @@ import java.util.Locale;
Uri keyUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.encryptionKeyUri);
if (!keyUri.equals(encryptionKeyUri)) {
// Encryption is specified and the key has changed.
out.chunk = newEncryptionKeyChunk(keyUri, segment.encryptionIV, newVariantIndex,
out.chunk = newEncryptionKeyChunk(keyUri, segment.encryptionIV, selectedVariantIndex,
trackSelection.getSelectionReason(), trackSelection.getSelectionData());
return;
}
@ -279,7 +281,7 @@ import java.util.Locale;
Uri chunkUri = UriUtil.resolveToUri(mediaPlaylist.baseUri, segment.url);
DataSpec dataSpec = new DataSpec(chunkUri, segment.byterangeOffset, segment.byterangeLength,
null);
out.chunk = new HlsMediaChunk(dataSource, dataSpec, initDataSpec, variants[newVariantIndex],
out.chunk = new HlsMediaChunk(dataSource, dataSpec, initDataSpec, selectedUrl,
trackSelection.getSelectionReason(), trackSelection.getSelectionData(),
startTimeUs, startTimeUs + segment.durationUs, chunkMediaSequence, discontinuitySequence,
isTimestampMaster, timestampAdjuster, previous, encryptionKey, encryptionIv);

View File

@ -166,8 +166,24 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
* be null if no snapshot has been loaded yet.
*/
public HlsMediaPlaylist getPlaylistSnapshot(HlsUrl url) {
HlsMediaPlaylist snapshot = playlistBundles.get(url).getPlaylistSnapshot();
if (snapshot != null) {
maybeSetPrimaryUrl(url);
return playlistBundles.get(url).getPlaylistSnapshot();
}
return snapshot;
}
/**
* Returns whether the snapshot of the playlist referenced by the provided {@link HlsUrl} is
* valid, meaning all the segments referenced by the playlist are expected to be available. If the
* playlist is not valid then some of the segments may no longer be available.
* @param url The {@link HlsUrl}.
* @return Whether the snapshot of the playlist referenced by the provided {@link HlsUrl} is
* valid.
*/
public boolean isSnapshotValid(HlsUrl url) {
return playlistBundles.get(url).isSnapshotValid();
}
/**
@ -412,6 +428,7 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
private final ParsingLoadable<HlsPlaylist> mediaPlaylistLoadable;
private HlsMediaPlaylist playlistSnapshot;
private long lastSnapshotLoadMs;
private long lastSnapshotAccessTimeMs;
private long blacklistUntilMs;
@ -429,6 +446,17 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
return playlistSnapshot;
}
public boolean isSnapshotValid() {
if (playlistSnapshot == null) {
return false;
}
// TODO: Return true for event playlists once playlist types are supported.
long currentTimeMs = SystemClock.elapsedRealtime();
long snapshotValidityDurationMs = Math.max(30000, C.usToMs(playlistSnapshot.durationUs));
return playlistSnapshot.hasEndTag
|| lastSnapshotLoadMs + snapshotValidityDurationMs > currentTimeMs;
}
public void release() {
mediaPlaylistLoader.release();
}
@ -488,6 +516,7 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
private void processLoadedPlaylist(HlsMediaPlaylist loadedPlaylist) {
HlsMediaPlaylist oldPlaylist = playlistSnapshot;
lastSnapshotLoadMs = SystemClock.elapsedRealtime();
playlistSnapshot = getLatestPlaylistSnapshot(oldPlaylist, loadedPlaylist);
long refreshDelayUs = C.TIME_UNSET;
if (playlistSnapshot != oldPlaylist) {