diff --git a/RELEASENOTES.md b/RELEASENOTES.md index f5353a24e1c..6f45ddcf93e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,11 @@ # Release notes +### 2.13.1 (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)). + ### 2.13.0 (2021-02-04) * Core library: diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 755d7511c4e..f791d05b132 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -880,7 +880,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( @@ -1051,17 +1051,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) { @@ -1725,7 +1722,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(); @@ -1831,7 +1828,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; } diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 008d8c6b537..e743858875a 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -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; @@ -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; @@ -8833,6 +8835,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;