Skip to content

Commit

Permalink
Prevent old playlist snapshots from being used on live HLS streams
Browse files Browse the repository at this point in the history
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
  • Loading branch information
AquilesCanta authored and ojw28 committed Jan 30, 2017
1 parent 25c18ca commit c49d142
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,16 @@ public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, HlsChu

// 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;
Expand All @@ -218,8 +219,9 @@ public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, HlsChu
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();
}
}
Expand All @@ -236,7 +238,7 @@ public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, HlsChu
if (mediaPlaylist.hasEndTag) {
out.endOfStream = true;
} else /* Live */ {
out.playlist = variants[newVariantIndex];
out.playlist = selectedUrl;
}
return;
}
Expand All @@ -249,7 +251,7 @@ public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, HlsChu
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;
}
Expand Down Expand Up @@ -279,7 +281,7 @@ public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, HlsChu
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,24 @@ public HlsMasterPlaylist getMasterPlaylist() {
* be null if no snapshot has been loaded yet.
*/
public HlsMediaPlaylist getPlaylistSnapshot(HlsUrl url) {
maybeSetPrimaryUrl(url);
return playlistBundles.get(url).getPlaylistSnapshot();
HlsMediaPlaylist snapshot = playlistBundles.get(url).getPlaylistSnapshot();
if (snapshot != null) {
maybeSetPrimaryUrl(url);
}
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();
}

/**
Expand Down Expand Up @@ -412,6 +428,7 @@ private final class MediaPlaylistBundle implements Loader.Callback<ParsingLoadab
private final ParsingLoadable<HlsPlaylist> mediaPlaylistLoadable;

private HlsMediaPlaylist playlistSnapshot;
private long lastSnapshotLoadMs;
private long lastSnapshotAccessTimeMs;
private long blacklistUntilMs;

Expand All @@ -429,6 +446,17 @@ public HlsMediaPlaylist getPlaylistSnapshot() {
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();
}
Expand Down Expand Up @@ -488,6 +516,7 @@ public void run() {

private void processLoadedPlaylist(HlsMediaPlaylist loadedPlaylist) {
HlsMediaPlaylist oldPlaylist = playlistSnapshot;
lastSnapshotLoadMs = SystemClock.elapsedRealtime();
playlistSnapshot = getLatestPlaylistSnapshot(oldPlaylist, loadedPlaylist);
long refreshDelayUs = C.TIME_UNSET;
if (playlistSnapshot != oldPlaylist) {
Expand Down

0 comments on commit c49d142

Please sign in to comment.