Skip to content

Commit

Permalink
Don't apply speed adjustment if windowStartTime is unknown.
Browse files Browse the repository at this point in the history
This may happen for HLS live streams without program date time
information.

Issue: #8560

#minor-release

PiperOrigin-RevId: 356227729
  • Loading branch information
tonihei authored and ojw28 committed Feb 9, 2021
1 parent d21a47c commit fbc8d6c
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 9 deletions.
3 changes: 3 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

### dev-v2 (not yet released)

* Core library:
* Fix playback issue for HLS live streams without program date time
information ([#8560](https://github.com/google/ExoPlayer/issues/8560)).
* UI:
* Add builder for `PlayerNotificationManager`.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,7 @@ private void updatePlaybackPositions() throws ExoPlaybackException {
// Adjust live playback speed to new position.
if (playbackInfo.playWhenReady
&& playbackInfo.playbackState == Player.STATE_READY
&& isCurrentPeriodInMovingLiveWindow()
&& shouldUseLivePlaybackSpeedControl(playbackInfo.timeline, playbackInfo.periodId)
&& playbackInfo.playbackParameters.speed == 1f) {
float adjustedSpeed =
livePlaybackSpeedControl.getAdjustedPlaybackSpeed(
Expand Down Expand Up @@ -1043,17 +1043,14 @@ private long getLiveOffsetUs(Timeline timeline, Object periodUid, long periodPos
- (periodPositionUs + period.getPositionInWindowUs());
}

private boolean isCurrentPeriodInMovingLiveWindow() {
return isInMovingLiveWindow(playbackInfo.timeline, playbackInfo.periodId);
}

private boolean isInMovingLiveWindow(Timeline timeline, MediaPeriodId mediaPeriodId) {
private boolean shouldUseLivePlaybackSpeedControl(
Timeline timeline, MediaPeriodId mediaPeriodId) {
if (mediaPeriodId.isAd() || timeline.isEmpty()) {
return false;
}
int windowIndex = timeline.getPeriodByUid(mediaPeriodId.periodUid, period).windowIndex;
timeline.getWindow(windowIndex, window);
return window.isLive() && window.isDynamic;
return window.isLive() && window.isDynamic && window.windowStartTimeMs != C.TIME_UNSET;
}

private void scheduleNextWork(long thisOperationStartTimeMs, long intervalMs) {
Expand Down Expand Up @@ -1717,7 +1714,7 @@ private boolean shouldTransitionToReadyState(boolean renderersReadyOrEnded) {
}
// Renderers are ready and we're loading. Ask the LoadControl whether to transition.
long targetLiveOffsetUs =
isInMovingLiveWindow(playbackInfo.timeline, queue.getPlayingPeriod().info.id)
shouldUseLivePlaybackSpeedControl(playbackInfo.timeline, queue.getPlayingPeriod().info.id)
? livePlaybackSpeedControl.getTargetLiveOffsetUs()
: C.TIME_UNSET;
MediaPeriodHolder loadingHolder = queue.getLoadingPeriod();
Expand Down Expand Up @@ -1823,7 +1820,7 @@ private void updateLivePlaybackSpeedControl(
Timeline oldTimeline,
MediaPeriodId oldPeriodId,
long positionForTargetOffsetOverrideUs) {
if (newTimeline.isEmpty() || !isInMovingLiveWindow(newTimeline, newPeriodId)) {
if (newTimeline.isEmpty() || !shouldUseLivePlaybackSpeedControl(newTimeline, newPeriodId)) {
// Live playback speed control is unused.
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
import com.google.android.exoplayer2.source.MediaSourceEventListener;
import com.google.android.exoplayer2.source.SilenceMediaSource;
import com.google.android.exoplayer2.source.SinglePeriodTimeline;
import com.google.android.exoplayer2.source.TrackGroup;
import com.google.android.exoplayer2.source.TrackGroupArray;
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
Expand All @@ -83,6 +84,7 @@
import com.google.android.exoplayer2.testutil.FakeAdaptiveDataSet;
import com.google.android.exoplayer2.testutil.FakeAdaptiveMediaSource;
import com.google.android.exoplayer2.testutil.FakeChunkSource;
import com.google.android.exoplayer2.testutil.FakeClock;
import com.google.android.exoplayer2.testutil.FakeDataSource;
import com.google.android.exoplayer2.testutil.FakeMediaClockRenderer;
import com.google.android.exoplayer2.testutil.FakeMediaPeriod;
Expand Down Expand Up @@ -8803,6 +8805,42 @@ public void targetLiveOffsetInMedia_withSetPlaybackParameters_usesPlaybackParame
assertThat(liveOffsetAtEnd).isIn(Range.closed(1_900L, 2_100L));
}

@Test
public void targetLiveOffsetInMedia_unknownWindowStartTime_doesNotAdjustLiveOffset()
throws Exception {
FakeClock fakeClock = new AutoAdvancingFakeClock(/* initialTimeMs= */ 987_654_321L);
ExoPlayer player = new TestExoPlayerBuilder(context).setClock(fakeClock).build();
MediaItem mediaItem =
new MediaItem.Builder().setUri(Uri.EMPTY).setLiveTargetOffsetMs(4_000).build();
Timeline liveTimeline =
new SinglePeriodTimeline(
/* presentationStartTimeMs= */ C.TIME_UNSET,
/* windowStartTimeMs= */ C.TIME_UNSET,
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
/* periodDurationUs= */ 1000 * C.MICROS_PER_SECOND,
/* windowDurationUs= */ 1000 * C.MICROS_PER_SECOND,
/* windowPositionInPeriodUs= */ 0,
/* windowDefaultStartPositionUs= */ 0,
/* isSeekable= */ true,
/* isDynamic= */ true,
/* manifest= */ null,
mediaItem,
mediaItem.liveConfiguration);
player.pause();
player.setMediaSource(new FakeMediaSource(liveTimeline));
player.prepare();
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_READY);

long playbackStartTimeMs = fakeClock.elapsedRealtime();
TestPlayerRunHelper.playUntilPosition(player, /* windowIndex= */ 0, /* positionMs= */ 999_000);
long playbackEndTimeMs = fakeClock.elapsedRealtime();
player.release();

// Assert that the time it took to play 999 seconds of media is 999 seconds (asserting that no
// playback speed adjustment was used).
assertThat(playbackEndTimeMs - playbackStartTimeMs).isEqualTo(999_000);
}

@Test
public void noTargetLiveOffsetInMedia_doesNotAdjustLiveOffset() throws Exception {
long windowStartUnixTimeMs = 987_654_321_000L;
Expand Down

0 comments on commit fbc8d6c

Please sign in to comment.