Commit 9a45d504 by aquilescanta Committed by Andrew Lewis

Fix playback of live HLS streams with #EXT-X-PROGRAM-DATE-TIME tags

Issue:#4239
Issue:#4254

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=196796569
parent 5ffb4d8f
...@@ -13,6 +13,9 @@ ...@@ -13,6 +13,9 @@
([#4134](https://github.com/google/ExoPlayer/issues/4134)). ([#4134](https://github.com/google/ExoPlayer/issues/4134)).
* Audio: Fix extraction of PCM in MP4/MOV * Audio: Fix extraction of PCM in MP4/MOV
([#4228](https://github.com/google/ExoPlayer/issues/4228)). ([#4228](https://github.com/google/ExoPlayer/issues/4228)).
* HLS:
* Fix playback of livestreams with EXT-X-PROGRAM-DATE-TIME tags
([#4239](https://github.com/google/ExoPlayer/issues/4239)).
### 2.8.0 ### ### 2.8.0 ###
......
...@@ -198,24 +198,24 @@ import java.util.List; ...@@ -198,24 +198,24 @@ import java.util.List;
/** /**
* Returns the next chunk to load. * Returns the next chunk to load.
* <p> *
* If a chunk is available then {@link HlsChunkHolder#chunk} is set. If the end of the stream has * <p>If a chunk is available then {@link HlsChunkHolder#chunk} is set. If the end of the stream
* been reached then {@link HlsChunkHolder#endOfStream} is set. If a chunk is not available but * has been reached then {@link HlsChunkHolder#endOfStream} is set. If a chunk is not available
* the end of the stream has not been reached, {@link HlsChunkHolder#playlist} is set to * but the end of the stream has not been reached, {@link HlsChunkHolder#playlist} is set to
* contain the {@link HlsUrl} that refers to the playlist that needs refreshing. * contain the {@link HlsUrl} that refers to the playlist that needs refreshing.
* *
* @param previous The most recently loaded media chunk. * @param previous The most recently loaded media chunk.
* @param playbackPositionUs The current playback position in microseconds. If playback of the * @param playbackPositionUs The current playback position relative to the period start in
* period to which this chunk source belongs has not yet started, the value will be the * microseconds. If playback of the period to which this chunk source belongs has not yet
* starting position in the period minus the duration of any media in previous periods still * started, the value will be the starting position in the period minus the duration of any
* to be played. * media in previous periods still to be played.
* @param loadPositionUs The current load position in microseconds. If {@code previous} is null, * @param loadPositionUs The current load position relative to the period start in microseconds.
* this is the starting position from which chunks should be provided. Else it's equal to * If {@code previous} is null, this is the starting position from which chunks should be
* {@code previous.endTimeUs}. * provided. Else it's equal to {@code previous.endTimeUs}.
* @param out A holder to populate. * @param out A holder to populate.
*/ */
public void getNextChunk(HlsMediaChunk previous, long playbackPositionUs, long loadPositionUs, public void getNextChunk(
HlsChunkHolder out) { HlsMediaChunk previous, long playbackPositionUs, long loadPositionUs, HlsChunkHolder out) {
int oldVariantIndex = previous == null ? C.INDEX_UNSET int oldVariantIndex = previous == null ? C.INDEX_UNSET
: trackGroup.indexOf(previous.trackFormat); : trackGroup.indexOf(previous.trackFormat);
long bufferedDurationUs = loadPositionUs - playbackPositionUs; long bufferedDurationUs = loadPositionUs - playbackPositionUs;
...@@ -261,12 +261,13 @@ import java.util.List; ...@@ -261,12 +261,13 @@ import java.util.List;
// If the playlist is too old to contain the chunk, we need to refresh it. // If the playlist is too old to contain the chunk, we need to refresh it.
chunkMediaSequence = mediaPlaylist.mediaSequence + mediaPlaylist.segments.size(); chunkMediaSequence = mediaPlaylist.mediaSequence + mediaPlaylist.segments.size();
} else { } else {
// The playlist start time is subtracted from the target position because the segment start long positionOfPlaylistInPeriodUs =
// times are relative to the start of the playlist, but the target position is not. mediaPlaylist.startTimeUs - playlistTracker.getInitialStartTimeUs();
long targetPositionInPlaylistUs = targetPositionUs - positionOfPlaylistInPeriodUs;
chunkMediaSequence = chunkMediaSequence =
Util.binarySearchFloor( Util.binarySearchFloor(
mediaPlaylist.segments, mediaPlaylist.segments,
/* value= */ targetPositionUs - mediaPlaylist.startTimeUs, /* value= */ targetPositionInPlaylistUs,
/* inclusive= */ true, /* inclusive= */ true,
/* stayInBounds= */ !playlistTracker.isLive() || previous == null) /* stayInBounds= */ !playlistTracker.isLive() || previous == null)
+ mediaPlaylist.mediaSequence; + mediaPlaylist.mediaSequence;
...@@ -330,9 +331,9 @@ import java.util.List; ...@@ -330,9 +331,9 @@ import java.util.List;
} }
// Compute start time of the next chunk. // Compute start time of the next chunk.
long offsetFromInitialStartTimeUs = long positionOfPlaylistInPeriodUs =
mediaPlaylist.startTimeUs - playlistTracker.getInitialStartTimeUs(); mediaPlaylist.startTimeUs - playlistTracker.getInitialStartTimeUs();
long startTimeUs = offsetFromInitialStartTimeUs + segment.relativeStartTimeUs; long segmentStartTimeInPeriodUs = positionOfPlaylistInPeriodUs + segment.relativeStartTimeUs;
int discontinuitySequence = mediaPlaylist.discontinuitySequence int discontinuitySequence = mediaPlaylist.discontinuitySequence
+ segment.relativeDiscontinuitySequence; + segment.relativeDiscontinuitySequence;
TimestampAdjuster timestampAdjuster = timestampAdjusterProvider.getAdjuster( TimestampAdjuster timestampAdjuster = timestampAdjusterProvider.getAdjuster(
...@@ -352,8 +353,8 @@ import java.util.List; ...@@ -352,8 +353,8 @@ import java.util.List;
muxedCaptionFormats, muxedCaptionFormats,
trackSelection.getSelectionReason(), trackSelection.getSelectionReason(),
trackSelection.getSelectionData(), trackSelection.getSelectionData(),
startTimeUs, segmentStartTimeInPeriodUs,
startTimeUs + segment.durationUs, segmentStartTimeInPeriodUs + segment.durationUs,
chunkMediaSequence, chunkMediaSequence,
discontinuitySequence, discontinuitySequence,
segment.hasGapTag, segment.hasGapTag,
......
...@@ -146,7 +146,9 @@ public final class HlsMediaPlaylist extends HlsPlaylist { ...@@ -146,7 +146,9 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
*/ */
public final long startOffsetUs; public final long startOffsetUs;
/** /**
* The start time of the playlist in playback timebase in microseconds. * If {@link #hasProgramDateTime} is true, contains the datetime as microseconds since epoch.
* Otherwise, contains the aggregated duration of removed segments up to this snapshot of the
* playlist.
*/ */
public final long startTimeUs; public final long startTimeUs;
/** /**
......
...@@ -208,7 +208,10 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable ...@@ -208,7 +208,10 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
return snapshot; return snapshot;
} }
/** Returns the start time of the first loaded primary playlist. */ /**
* Returns the start time of the first loaded primary playlist, or {@link C#TIME_UNSET} if no
* media playlist has been loaded.
*/
public long getInitialStartTimeUs() { public long getInitialStartTimeUs() {
return initialStartTimeUs; return initialStartTimeUs;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment