mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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) {
|
||||
return other == null || mediaSequence > other.mediaSequence
|
||||
|| (mediaSequence == other.mediaSequence && segments.size() > other.segments.size())
|
||||
|| (hasEndTag && !other.hasEndTag);
|
||||
if (other == null || mediaSequence > other.mediaSequence) {
|
||||
return true;
|
||||
}
|
||||
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() {
|
||||
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) {
|
||||
if (this.startTimeUs == startTimeUs) {
|
||||
return this;
|
||||
}
|
||||
return new HlsMediaPlaylist(baseUri, startTimeUs, mediaSequence, version, targetDurationUs,
|
||||
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.
|
||||
*/
|
||||
private HlsMediaPlaylist adjustPlaylistTimestamps(HlsMediaPlaylist oldPlaylist,
|
||||
HlsMediaPlaylist newPlaylist) {
|
||||
if (newPlaylist.hasProgramDateTime) {
|
||||
if (newPlaylist.isNewerThan(oldPlaylist)) {
|
||||
return newPlaylist;
|
||||
// TODO: Track discontinuities for media playlists that don't include the discontinuity number.
|
||||
private HlsMediaPlaylist getLatestPlaylistSnapshot(HlsMediaPlaylist oldPlaylist,
|
||||
HlsMediaPlaylist loadedPlaylist) {
|
||||
if (loadedPlaylist.hasProgramDateTime) {
|
||||
if (loadedPlaylist.isNewerThan(oldPlaylist)) {
|
||||
return loadedPlaylist;
|
||||
} 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
|
||||
@ -351,28 +353,28 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
||||
long primarySnapshotStartTimeUs = primaryUrlSnapshot != null
|
||||
? primaryUrlSnapshot.startTimeUs : 0;
|
||||
if (oldPlaylist == null) {
|
||||
if (newPlaylist.startTimeUs == primarySnapshotStartTimeUs) {
|
||||
if (loadedPlaylist.startTimeUs == primarySnapshotStartTimeUs) {
|
||||
// Playback has just started or is VOD so no adjustment is needed.
|
||||
return newPlaylist;
|
||||
return loadedPlaylist;
|
||||
} 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;
|
||||
int oldPlaylistSize = oldSegments.size();
|
||||
if (!newPlaylist.isNewerThan(oldPlaylist)) {
|
||||
// Playlist has not changed.
|
||||
return oldPlaylist;
|
||||
}
|
||||
int mediaSequenceOffset = newPlaylist.mediaSequence - oldPlaylist.mediaSequence;
|
||||
int mediaSequenceOffset = loadedPlaylist.mediaSequence - oldPlaylist.mediaSequence;
|
||||
if (mediaSequenceOffset <= oldPlaylistSize) {
|
||||
long adjustedNewPlaylistStartTimeUs = mediaSequenceOffset == oldPlaylistSize
|
||||
? oldPlaylist.getEndTimeUs()
|
||||
: 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.
|
||||
return newPlaylist.copyWithStartTimeUs(primarySnapshotStartTimeUs);
|
||||
return loadedPlaylist.copyWithStartTimeUs(primarySnapshotStartTimeUs);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -460,15 +462,15 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
|
||||
|
||||
// Internal methods.
|
||||
|
||||
private void processLoadedPlaylist(HlsMediaPlaylist loadedMediaPlaylist) {
|
||||
private void processLoadedPlaylist(HlsMediaPlaylist loadedPlaylist) {
|
||||
HlsMediaPlaylist oldPlaylist = playlistSnapshot;
|
||||
playlistSnapshot = adjustPlaylistTimestamps(oldPlaylist, loadedMediaPlaylist);
|
||||
playlistSnapshot = getLatestPlaylistSnapshot(oldPlaylist, loadedPlaylist);
|
||||
long refreshDelayUs = C.TIME_UNSET;
|
||||
if (oldPlaylist != playlistSnapshot) {
|
||||
if (playlistSnapshot != oldPlaylist) {
|
||||
if (onPlaylistUpdated(playlistUrl, playlistSnapshot)) {
|
||||
refreshDelayUs = playlistSnapshot.targetDurationUs;
|
||||
}
|
||||
} else if (!loadedMediaPlaylist.hasEndTag) {
|
||||
} else if (!playlistSnapshot.hasEndTag) {
|
||||
refreshDelayUs = playlistSnapshot.targetDurationUs / 2;
|
||||
}
|
||||
if (refreshDelayUs != C.TIME_UNSET) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user