Better handle inconsistent HLS timeline updates
Issue: #2249 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=143555467
This commit is contained in:
parent
44d6b1a271
commit
f735a86ebb
@ -96,19 +96,58 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this playlist is newer than {@code other}.
|
||||||
|
*
|
||||||
|
* @param other The playlist to compare.
|
||||||
|
* @return Whether this playlist is newer than {@code other}.
|
||||||
|
*/
|
||||||
public boolean isNewerThan(HlsMediaPlaylist other) {
|
public boolean isNewerThan(HlsMediaPlaylist other) {
|
||||||
return other == null || mediaSequence > other.mediaSequence
|
if (other == null || mediaSequence > other.mediaSequence) {
|
||||||
|| (mediaSequence == other.mediaSequence && segments.size() > other.segments.size())
|
return true;
|
||||||
|| (hasEndTag && !other.hasEndTag);
|
}
|
||||||
|
if (mediaSequence < other.mediaSequence) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// The media sequences are equal.
|
||||||
|
int segmentCount = segments.size();
|
||||||
|
int otherSegmentCount = other.segments.size();
|
||||||
|
return segmentCount > otherSegmentCount
|
||||||
|
|| (segmentCount == otherSegmentCount && hasEndTag && !other.hasEndTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getEndTimeUs() {
|
public long getEndTimeUs() {
|
||||||
return startTimeUs + durationUs;
|
return startTimeUs + durationUs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a playlist identical to this one except for the start time, which is set to the
|
||||||
|
* specified value. If the start time already equals the specified value then the playlist will
|
||||||
|
* return itself.
|
||||||
|
*
|
||||||
|
* @param startTimeUs The start time for the returned playlist.
|
||||||
|
* @return The playlist.
|
||||||
|
*/
|
||||||
public HlsMediaPlaylist copyWithStartTimeUs(long startTimeUs) {
|
public HlsMediaPlaylist copyWithStartTimeUs(long startTimeUs) {
|
||||||
|
if (this.startTimeUs == startTimeUs) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
return new HlsMediaPlaylist(baseUri, startTimeUs, mediaSequence, version, targetDurationUs,
|
return new HlsMediaPlaylist(baseUri, startTimeUs, mediaSequence, version, targetDurationUs,
|
||||||
hasEndTag, hasProgramDateTime, initializationSegment, segments);
|
hasEndTag, hasProgramDateTime, initializationSegment, segments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a playlist identical to this one except that an end tag is added. If an end tag is
|
||||||
|
* already present then the playlist will return itself.
|
||||||
|
*
|
||||||
|
* @return The playlist.
|
||||||
|
*/
|
||||||
|
public HlsMediaPlaylist copyWithEndTag() {
|
||||||
|
if (this.hasEndTag) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return new HlsMediaPlaylist(baseUri, startTimeUs, mediaSequence, version, targetDurationUs,
|
||||||
|
true, hasProgramDateTime, initializationSegment, segments);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -334,16 +334,18 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// TODO: Track discontinuities for media playlists that don't include the discontinuity number.
|
||||||
* TODO: Track discontinuities for media playlists that don't include the discontinuity number.
|
private HlsMediaPlaylist getLatestPlaylistSnapshot(HlsMediaPlaylist oldPlaylist,
|
||||||
*/
|
HlsMediaPlaylist loadedPlaylist) {
|
||||||
private HlsMediaPlaylist adjustPlaylistTimestamps(HlsMediaPlaylist oldPlaylist,
|
if (loadedPlaylist.hasProgramDateTime) {
|
||||||
HlsMediaPlaylist newPlaylist) {
|
if (loadedPlaylist.isNewerThan(oldPlaylist)) {
|
||||||
if (newPlaylist.hasProgramDateTime) {
|
return loadedPlaylist;
|
||||||
if (newPlaylist.isNewerThan(oldPlaylist)) {
|
|
||||||
return newPlaylist;
|
|
||||||
} else {
|
} else {
|
||||||
return oldPlaylist;
|
// If the loaded playlist has an end tag but is not newer than the old playlist then we have
|
||||||
|
// an inconsistent state. This is typically caused by the server incorrectly resetting the
|
||||||
|
// media sequence when appending the end tag. We resolve this case as best we can by
|
||||||
|
// returning the old playlist with the end tag appended.
|
||||||
|
return loadedPlaylist.hasEndTag ? oldPlaylist.copyWithEndTag() : oldPlaylist;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Once playlist type support is added, the snapshot's age can be added by using the
|
// TODO: Once playlist type support is added, the snapshot's age can be added by using the
|
||||||
@ -351,28 +353,28 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||||||
long primarySnapshotStartTimeUs = primaryUrlSnapshot != null
|
long primarySnapshotStartTimeUs = primaryUrlSnapshot != null
|
||||||
? primaryUrlSnapshot.startTimeUs : 0;
|
? primaryUrlSnapshot.startTimeUs : 0;
|
||||||
if (oldPlaylist == null) {
|
if (oldPlaylist == null) {
|
||||||
if (newPlaylist.startTimeUs == primarySnapshotStartTimeUs) {
|
if (loadedPlaylist.startTimeUs == primarySnapshotStartTimeUs) {
|
||||||
// Playback has just started or is VOD so no adjustment is needed.
|
// Playback has just started or is VOD so no adjustment is needed.
|
||||||
return newPlaylist;
|
return loadedPlaylist;
|
||||||
} else {
|
} else {
|
||||||
return newPlaylist.copyWithStartTimeUs(primarySnapshotStartTimeUs);
|
return loadedPlaylist.copyWithStartTimeUs(primarySnapshotStartTimeUs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!loadedPlaylist.isNewerThan(oldPlaylist)) {
|
||||||
|
// See comment above.
|
||||||
|
return loadedPlaylist.hasEndTag ? oldPlaylist.copyWithEndTag() : oldPlaylist;
|
||||||
|
}
|
||||||
List<Segment> oldSegments = oldPlaylist.segments;
|
List<Segment> oldSegments = oldPlaylist.segments;
|
||||||
int oldPlaylistSize = oldSegments.size();
|
int oldPlaylistSize = oldSegments.size();
|
||||||
if (!newPlaylist.isNewerThan(oldPlaylist)) {
|
int mediaSequenceOffset = loadedPlaylist.mediaSequence - oldPlaylist.mediaSequence;
|
||||||
// Playlist has not changed.
|
|
||||||
return oldPlaylist;
|
|
||||||
}
|
|
||||||
int mediaSequenceOffset = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
|
|
||||||
if (mediaSequenceOffset <= oldPlaylistSize) {
|
if (mediaSequenceOffset <= oldPlaylistSize) {
|
||||||
long adjustedNewPlaylistStartTimeUs = mediaSequenceOffset == oldPlaylistSize
|
long adjustedNewPlaylistStartTimeUs = mediaSequenceOffset == oldPlaylistSize
|
||||||
? oldPlaylist.getEndTimeUs()
|
? oldPlaylist.getEndTimeUs()
|
||||||
: oldPlaylist.startTimeUs + oldSegments.get(mediaSequenceOffset).relativeStartTimeUs;
|
: oldPlaylist.startTimeUs + oldSegments.get(mediaSequenceOffset).relativeStartTimeUs;
|
||||||
return newPlaylist.copyWithStartTimeUs(adjustedNewPlaylistStartTimeUs);
|
return loadedPlaylist.copyWithStartTimeUs(adjustedNewPlaylistStartTimeUs);
|
||||||
}
|
}
|
||||||
// No segments overlap, we assume the new playlist start coincides with the primary playlist.
|
// No segments overlap, we assume the new playlist start coincides with the primary playlist.
|
||||||
return newPlaylist.copyWithStartTimeUs(primarySnapshotStartTimeUs);
|
return loadedPlaylist.copyWithStartTimeUs(primarySnapshotStartTimeUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -460,15 +462,15 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
|||||||
|
|
||||||
// Internal methods.
|
// Internal methods.
|
||||||
|
|
||||||
private void processLoadedPlaylist(HlsMediaPlaylist loadedMediaPlaylist) {
|
private void processLoadedPlaylist(HlsMediaPlaylist loadedPlaylist) {
|
||||||
HlsMediaPlaylist oldPlaylist = playlistSnapshot;
|
HlsMediaPlaylist oldPlaylist = playlistSnapshot;
|
||||||
playlistSnapshot = adjustPlaylistTimestamps(oldPlaylist, loadedMediaPlaylist);
|
playlistSnapshot = getLatestPlaylistSnapshot(oldPlaylist, loadedPlaylist);
|
||||||
long refreshDelayUs = C.TIME_UNSET;
|
long refreshDelayUs = C.TIME_UNSET;
|
||||||
if (oldPlaylist != playlistSnapshot) {
|
if (playlistSnapshot != oldPlaylist) {
|
||||||
if (onPlaylistUpdated(playlistUrl, playlistSnapshot)) {
|
if (onPlaylistUpdated(playlistUrl, playlistSnapshot)) {
|
||||||
refreshDelayUs = playlistSnapshot.targetDurationUs;
|
refreshDelayUs = playlistSnapshot.targetDurationUs;
|
||||||
}
|
}
|
||||||
} else if (!loadedMediaPlaylist.hasEndTag) {
|
} else if (!playlistSnapshot.hasEndTag) {
|
||||||
refreshDelayUs = playlistSnapshot.targetDurationUs / 2;
|
refreshDelayUs = playlistSnapshot.targetDurationUs / 2;
|
||||||
}
|
}
|
||||||
if (refreshDelayUs != C.TIME_UNSET) {
|
if (refreshDelayUs != C.TIME_UNSET) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user