From 1fa790348e7e90c987d3385a79a2c7c150cd6824 Mon Sep 17 00:00:00 2001 From: tonihei Date: Mon, 22 May 2023 13:41:39 +0100 Subject: [PATCH] Untangle PlayerInfo/PlaybackInfo updates The methods in ExoPlayerImpl and MediaControllerImplBase that determine the new PlayerInfo/PlaybackInfo currently have a hard-to-reason-about setup where the method generating the new info accesses other methods that rely on the existing class field instead of working with the passed in PlayerInfo/PlaybackInfo. This prevents reuse of the util methods (e.g. for replaceMediaItems) because they access potentially stale state. This change untangles these methods a bit by making the util methods either static or at least ensure that they don't rely on existing class fields of PlayerInfo/PlaybackInfo. Overall, the change is a complete no-op. #minor-release PiperOrigin-RevId: 534036633 --- .../media3/exoplayer/ExoPlayerImpl.java | 109 ++++--- .../session/MediaControllerImplBase.java | 265 +++++++++--------- 2 files changed, 202 insertions(+), 172 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 77b0252ce7..8301ab99e1 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -659,16 +659,7 @@ import java.util.concurrent.TimeoutException; setMediaSources(mediaSources, /* resetPosition= */ maskingWindowIndex == C.INDEX_UNSET); return; } - Timeline oldTimeline = getCurrentTimeline(); - pendingOperationAcks++; - List holders = addMediaSourceHolders(index, mediaSources); - Timeline newTimeline = createMaskingTimeline(); - PlaybackInfo newPlaybackInfo = - maskTimelineAndPosition( - playbackInfo, - newTimeline, - getPeriodPositionUsAfterTimelineChanged(oldTimeline, newTimeline)); - internalPlayer.addMediaSources(index, holders, shuffleOrder); + PlaybackInfo newPlaybackInfo = addMediaSourcesInternal(playbackInfo, index, mediaSources); updatePlaybackInfo( newPlaybackInfo, /* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, @@ -691,7 +682,7 @@ import java.util.concurrent.TimeoutException; // Do nothing. return; } - PlaybackInfo newPlaybackInfo = removeMediaItemsInternal(fromIndex, toIndex); + PlaybackInfo newPlaybackInfo = removeMediaItemsInternal(playbackInfo, fromIndex, toIndex); boolean positionDiscontinuity = !newPlaybackInfo.periodId.periodUid.equals(playbackInfo.periodId.periodUid); updatePlaybackInfo( @@ -725,7 +716,11 @@ import java.util.concurrent.TimeoutException; maskTimelineAndPosition( playbackInfo, newTimeline, - getPeriodPositionUsAfterTimelineChanged(oldTimeline, newTimeline)); + getPeriodPositionUsAfterTimelineChanged( + oldTimeline, + newTimeline, + getCurrentWindowIndexInternal(playbackInfo), + getContentPositionInternal(playbackInfo))); internalPlayer.moveMediaSources(fromIndex, toIndex, newFromIndex, shuffleOrder); updatePlaybackInfo( newPlaybackInfo, @@ -1057,7 +1052,7 @@ import java.util.concurrent.TimeoutException; @Override public int getCurrentMediaItemIndex() { verifyApplicationThread(); - int currentWindowIndex = getCurrentWindowIndexInternal(); + int currentWindowIndex = getCurrentWindowIndexInternal(playbackInfo); return currentWindowIndex == C.INDEX_UNSET ? 0 : currentWindowIndex; } @@ -1117,17 +1112,7 @@ import java.util.concurrent.TimeoutException; @Override public long getContentPosition() { verifyApplicationThread(); - if (isPlayingAd()) { - playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period); - return playbackInfo.requestedContentPositionUs == C.TIME_UNSET - ? playbackInfo - .timeline - .getWindow(getCurrentMediaItemIndex(), window) - .getDefaultPositionMs() - : period.getPositionInWindowMs() + Util.usToMs(playbackInfo.requestedContentPositionUs); - } else { - return getCurrentPosition(); - } + return getContentPositionInternal(playbackInfo); } @Override @@ -1853,13 +1838,25 @@ import java.util.concurrent.TimeoutException; /* repeatCurrentMediaItem= */ false); } - private int getCurrentWindowIndexInternal() { + private int getCurrentWindowIndexInternal(PlaybackInfo playbackInfo) { if (playbackInfo.timeline.isEmpty()) { return maskingWindowIndex; - } else { - return playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period) - .windowIndex; } + return playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period) + .windowIndex; + } + + private long getContentPositionInternal(PlaybackInfo playbackInfo) { + if (playbackInfo.periodId.isAd()) { + playbackInfo.timeline.getPeriodByUid(playbackInfo.periodId.periodUid, period); + return playbackInfo.requestedContentPositionUs == C.TIME_UNSET + ? playbackInfo + .timeline + .getWindow(getCurrentWindowIndexInternal(playbackInfo), window) + .getDefaultPositionMs() + : period.getPositionInWindowMs() + Util.usToMs(playbackInfo.requestedContentPositionUs); + } + return Util.usToMs(getCurrentPositionUsInternal(playbackInfo)); } private long getCurrentPositionUsInternal(PlaybackInfo playbackInfo) { @@ -1874,10 +1871,9 @@ import java.util.concurrent.TimeoutException; if (playbackInfo.periodId.isAd()) { return positionUs; - } else { - return periodPositionUsToWindowPositionUs( - playbackInfo.timeline, playbackInfo.periodId, positionUs); } + return periodPositionUsToWindowPositionUs( + playbackInfo.timeline, playbackInfo.periodId, positionUs); } private List createMediaSources(List mediaItems) { @@ -2276,7 +2272,7 @@ import java.util.concurrent.TimeoutException; int startWindowIndex, long startPositionMs, boolean resetToDefaultPosition) { - int currentWindowIndex = getCurrentWindowIndexInternal(); + int currentWindowIndex = getCurrentWindowIndexInternal(playbackInfo); long currentPositionMs = getCurrentPosition(); pendingOperationAcks++; if (!mediaSourceHolderSnapshots.isEmpty()) { @@ -2347,9 +2343,30 @@ import java.util.concurrent.TimeoutException; return holders; } - private PlaybackInfo removeMediaItemsInternal(int fromIndex, int toIndex) { - int currentIndex = getCurrentMediaItemIndex(); - Timeline oldTimeline = getCurrentTimeline(); + private PlaybackInfo addMediaSourcesInternal( + PlaybackInfo playbackInfo, int index, List mediaSources) { + Timeline oldTimeline = playbackInfo.timeline; + pendingOperationAcks++; + List holders = addMediaSourceHolders(index, mediaSources); + Timeline newTimeline = createMaskingTimeline(); + PlaybackInfo newPlaybackInfo = + maskTimelineAndPosition( + playbackInfo, + newTimeline, + getPeriodPositionUsAfterTimelineChanged( + oldTimeline, + newTimeline, + getCurrentWindowIndexInternal(playbackInfo), + getContentPositionInternal(playbackInfo))); + internalPlayer.addMediaSources(index, holders, shuffleOrder); + return newPlaybackInfo; + } + + private PlaybackInfo removeMediaItemsInternal( + PlaybackInfo playbackInfo, int fromIndex, int toIndex) { + int currentIndex = getCurrentWindowIndexInternal(playbackInfo); + long contentPositionMs = getContentPositionInternal(playbackInfo); + Timeline oldTimeline = playbackInfo.timeline; int currentMediaSourceCount = mediaSourceHolderSnapshots.size(); pendingOperationAcks++; removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */ toIndex); @@ -2358,7 +2375,8 @@ import java.util.concurrent.TimeoutException; maskTimelineAndPosition( playbackInfo, newTimeline, - getPeriodPositionUsAfterTimelineChanged(oldTimeline, newTimeline)); + getPeriodPositionUsAfterTimelineChanged( + oldTimeline, newTimeline, currentIndex, contentPositionMs)); // Player transitions to STATE_ENDED if the current index is part of the removed tail. final boolean transitionsToEnded = newPlaybackInfo.playbackState != STATE_IDLE @@ -2387,7 +2405,9 @@ import java.util.concurrent.TimeoutException; private PlaybackInfo maskTimelineAndPosition( PlaybackInfo playbackInfo, Timeline timeline, @Nullable Pair periodPositionUs) { checkArgument(timeline.isEmpty() || periodPositionUs != null); + // Get the old timeline and position before updating playbackInfo. Timeline oldTimeline = playbackInfo.timeline; + long oldContentPositionMs = getContentPositionInternal(playbackInfo); // Mask the timeline. playbackInfo = playbackInfo.copyWithTimeline(timeline); @@ -2415,7 +2435,7 @@ import java.util.concurrent.TimeoutException; MediaPeriodId newPeriodId = playingPeriodChanged ? new MediaPeriodId(periodPositionUs.first) : playbackInfo.periodId; long newContentPositionUs = periodPositionUs.second; - long oldContentPositionUs = Util.msToUs(getContentPosition()); + long oldContentPositionUs = Util.msToUs(oldContentPositionMs); if (!oldTimeline.isEmpty()) { oldContentPositionUs -= oldTimeline.getPeriodByUid(oldPeriodUid, period).getPositionInWindowUs(); @@ -2491,20 +2511,21 @@ import java.util.concurrent.TimeoutException; @Nullable private Pair getPeriodPositionUsAfterTimelineChanged( - Timeline oldTimeline, Timeline newTimeline) { - long currentPositionMs = getContentPosition(); + Timeline oldTimeline, + Timeline newTimeline, + int currentWindowIndexInternal, + long contentPositionMs) { if (oldTimeline.isEmpty() || newTimeline.isEmpty()) { boolean isCleared = !oldTimeline.isEmpty() && newTimeline.isEmpty(); return maskWindowPositionMsOrGetPeriodPositionUs( newTimeline, - isCleared ? C.INDEX_UNSET : getCurrentWindowIndexInternal(), - isCleared ? C.TIME_UNSET : currentPositionMs); + isCleared ? C.INDEX_UNSET : currentWindowIndexInternal, + isCleared ? C.TIME_UNSET : contentPositionMs); } - int currentMediaItemIndex = getCurrentMediaItemIndex(); @Nullable Pair oldPeriodPositionUs = oldTimeline.getPeriodPositionUs( - window, period, currentMediaItemIndex, Util.msToUs(currentPositionMs)); + window, period, currentWindowIndexInternal, Util.msToUs(contentPositionMs)); Object periodUid = castNonNull(oldPeriodPositionUs).first; if (newTimeline.getIndexOfPeriod(periodUid) != C.INDEX_UNSET) { // The old period position is still available in the new timeline. @@ -2556,7 +2577,7 @@ import java.util.concurrent.TimeoutException; } private PlayerMessage createMessageInternal(Target target) { - int currentWindowIndex = getCurrentWindowIndexInternal(); + int currentWindowIndex = getCurrentWindowIndexInternal(playbackInfo); return new PlayerMessage( internalPlayer, target, diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java index 58870ee93c..cfe1a1175f 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplBase.java @@ -920,7 +920,19 @@ import org.checkerframework.checker.nullness.qual.NonNull; } // Add media items to the end of the timeline if the index exceeds the window count. index = min(index, playerInfo.timeline.getWindowCount()); + PlayerInfo newPlayerInfo = maskPlaybackInfoForAddedItems(playerInfo, index, mediaItems); + updatePlayerInfo( + newPlayerInfo, + /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, + /* positionDiscontinuity= */ false, + /* ignored */ Player.DISCONTINUITY_REASON_INTERNAL, + /* mediaItemTransition= */ playerInfo.timeline.isEmpty(), + Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + } + private static PlayerInfo maskPlaybackInfoForAddedItems( + PlayerInfo playerInfo, int index, List mediaItems) { Timeline oldTimeline = playerInfo.timeline; List newWindows = new ArrayList<>(); List newPeriods = new ArrayList<>(); @@ -948,21 +960,12 @@ import org.checkerframework.checker.nullness.qual.NonNull; ? playerInfo.sessionPositionInfo.positionInfo.periodIndex + mediaItems.size() : playerInfo.sessionPositionInfo.positionInfo.periodIndex; } - PlayerInfo newPlayerInfo = - maskTimelineAndPositionInfo( - playerInfo, - newTimeline, - newMediaItemIndex, - newPeriodIndex, - Player.DISCONTINUITY_REASON_INTERNAL); - updatePlayerInfo( - newPlayerInfo, - /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - /* positionDiscontinuity= */ false, - /* ignored */ Player.DISCONTINUITY_REASON_INTERNAL, - /* mediaItemTransition= */ oldTimeline.isEmpty(), - Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + return maskTimelineAndPositionInfo( + playerInfo, + newTimeline, + newMediaItemIndex, + newPeriodIndex, + Player.DISCONTINUITY_REASON_INTERNAL); } @Override @@ -1004,13 +1007,29 @@ import org.checkerframework.checker.nullness.qual.NonNull; } private void removeMediaItemsInternal(int fromIndex, int toIndex) { - Timeline oldTimeline = playerInfo.timeline; int playlistSize = playerInfo.timeline.getWindowCount(); toIndex = min(toIndex, playlistSize); - if (fromIndex >= playlistSize || fromIndex == toIndex) { + if (fromIndex >= playlistSize || fromIndex == toIndex || playlistSize == 0) { return; } + boolean currentItemRemoved = + getCurrentMediaItemIndex() >= fromIndex && getCurrentMediaItemIndex() < toIndex; + PlayerInfo newPlayerInfo = maskPlayerInfoForRemovedItems(playerInfo, fromIndex, toIndex); + updatePlayerInfo( + newPlayerInfo, + /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, + /* positionDiscontinuity= */ currentItemRemoved, + Player.DISCONTINUITY_REASON_REMOVE, + /* mediaItemTransition= */ playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex + >= fromIndex + && playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex < toIndex, + Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + } + private static PlayerInfo maskPlayerInfoForRemovedItems( + PlayerInfo playerInfo, int fromIndex, int toIndex) { + Timeline oldTimeline = playerInfo.timeline; List newWindows = new ArrayList<>(); List newPeriods = new ArrayList<>(); for (int i = 0; i < oldTimeline.getWindowCount(); i++) { @@ -1021,124 +1040,109 @@ import org.checkerframework.checker.nullness.qual.NonNull; rebuildPeriods(oldTimeline, newWindows, newPeriods); Timeline newTimeline = createMaskingTimeline(newWindows, newPeriods); - int oldMediaItemIndex = getCurrentMediaItemIndex(); + int oldMediaItemIndex = getCurrentMediaItemIndexInternal(playerInfo); int newMediaItemIndex = oldMediaItemIndex; int oldPeriodIndex = playerInfo.sessionPositionInfo.positionInfo.periodIndex; int newPeriodIndex = oldPeriodIndex; - boolean currentItemRemoved = - getCurrentMediaItemIndex() >= fromIndex && getCurrentMediaItemIndex() < toIndex; Window window = new Window(); - if (oldTimeline.isEmpty()) { - // No masking required. Just forwarding command to session. + boolean currentItemRemoved = oldMediaItemIndex >= fromIndex && oldMediaItemIndex < toIndex; + if (newTimeline.isEmpty()) { + newMediaItemIndex = C.INDEX_UNSET; + newPeriodIndex = 0; } else { - if (newTimeline.isEmpty()) { - newMediaItemIndex = C.INDEX_UNSET; - newPeriodIndex = 0; - } else { - if (currentItemRemoved) { - int oldNextMediaItemIndex = - resolveSubsequentMediaItemIndex( - getRepeatMode(), - getShuffleModeEnabled(), - oldMediaItemIndex, - oldTimeline, - fromIndex, - toIndex); - if (oldNextMediaItemIndex == C.INDEX_UNSET) { - newMediaItemIndex = newTimeline.getFirstWindowIndex(getShuffleModeEnabled()); - } else if (oldNextMediaItemIndex >= toIndex) { - newMediaItemIndex = oldNextMediaItemIndex - (toIndex - fromIndex); - } else { - newMediaItemIndex = oldNextMediaItemIndex; - } - newPeriodIndex = newTimeline.getWindow(newMediaItemIndex, window).firstPeriodIndex; - } else if (oldMediaItemIndex >= toIndex) { - newMediaItemIndex -= (toIndex - fromIndex); - newPeriodIndex = - getNewPeriodIndexWithoutRemovedPeriods( - oldTimeline, oldPeriodIndex, fromIndex, toIndex); - } - } - - PlayerInfo newPlayerInfo; if (currentItemRemoved) { - PositionInfo newPositionInfo; - if (newMediaItemIndex == C.INDEX_UNSET) { - newPositionInfo = SessionPositionInfo.DEFAULT_POSITION_INFO; - newPlayerInfo = - maskTimelineAndPositionInfo( - playerInfo, - newTimeline, - newPositionInfo, - SessionPositionInfo.DEFAULT, - Player.DISCONTINUITY_REASON_REMOVE); + int oldNextMediaItemIndex = + resolveSubsequentMediaItemIndex( + playerInfo.repeatMode, + playerInfo.shuffleModeEnabled, + oldMediaItemIndex, + oldTimeline, + fromIndex, + toIndex); + if (oldNextMediaItemIndex == C.INDEX_UNSET) { + newMediaItemIndex = newTimeline.getFirstWindowIndex(playerInfo.shuffleModeEnabled); + } else if (oldNextMediaItemIndex >= toIndex) { + newMediaItemIndex = oldNextMediaItemIndex - (toIndex - fromIndex); } else { - Window newWindow = newTimeline.getWindow(newMediaItemIndex, new Window()); - long defaultPositionMs = newWindow.getDefaultPositionMs(); - long durationMs = newWindow.getDurationMs(); - newPositionInfo = - new PositionInfo( - /* windowUid= */ null, - newMediaItemIndex, - newWindow.mediaItem, - /* periodUid= */ null, - newPeriodIndex, - /* positionMs= */ defaultPositionMs, - /* contentPositionMs= */ defaultPositionMs, - /* adGroupIndex= */ C.INDEX_UNSET, - /* adIndexInAdGroup= */ C.INDEX_UNSET); - newPlayerInfo = - maskTimelineAndPositionInfo( - playerInfo, - newTimeline, - newPositionInfo, - new SessionPositionInfo( - newPositionInfo, - /* isPlayingAd= */ false, - /* eventTimeMs= */ SystemClock.elapsedRealtime(), - /* durationMs= */ durationMs, - /* bufferedPositionMs= */ defaultPositionMs, - /* bufferedPercentage= */ calculateBufferedPercentage( - defaultPositionMs, durationMs), - /* totalBufferedDurationMs= */ 0, - /* currentLiveOffsetMs= */ C.TIME_UNSET, - /* contentDurationMs= */ durationMs, - /* contentBufferedPositionMs= */ defaultPositionMs), - Player.DISCONTINUITY_REASON_REMOVE); + newMediaItemIndex = oldNextMediaItemIndex; } - } else { + newPeriodIndex = newTimeline.getWindow(newMediaItemIndex, window).firstPeriodIndex; + } else if (oldMediaItemIndex >= toIndex) { + newMediaItemIndex -= (toIndex - fromIndex); + newPeriodIndex = + getNewPeriodIndexWithoutRemovedPeriods(oldTimeline, oldPeriodIndex, fromIndex, toIndex); + } + } + + PlayerInfo newPlayerInfo; + if (currentItemRemoved) { + PositionInfo newPositionInfo; + if (newMediaItemIndex == C.INDEX_UNSET) { + newPositionInfo = SessionPositionInfo.DEFAULT_POSITION_INFO; newPlayerInfo = maskTimelineAndPositionInfo( playerInfo, newTimeline, + newPositionInfo, + SessionPositionInfo.DEFAULT, + Player.DISCONTINUITY_REASON_REMOVE); + } else { + Window newWindow = newTimeline.getWindow(newMediaItemIndex, new Window()); + long defaultPositionMs = newWindow.getDefaultPositionMs(); + long durationMs = newWindow.getDurationMs(); + newPositionInfo = + new PositionInfo( + /* windowUid= */ null, newMediaItemIndex, + newWindow.mediaItem, + /* periodUid= */ null, newPeriodIndex, + /* positionMs= */ defaultPositionMs, + /* contentPositionMs= */ defaultPositionMs, + /* adGroupIndex= */ C.INDEX_UNSET, + /* adIndexInAdGroup= */ C.INDEX_UNSET); + newPlayerInfo = + maskTimelineAndPositionInfo( + playerInfo, + newTimeline, + newPositionInfo, + new SessionPositionInfo( + newPositionInfo, + /* isPlayingAd= */ false, + /* eventTimeMs= */ SystemClock.elapsedRealtime(), + /* durationMs= */ durationMs, + /* bufferedPositionMs= */ defaultPositionMs, + /* bufferedPercentage= */ calculateBufferedPercentage( + defaultPositionMs, durationMs), + /* totalBufferedDurationMs= */ 0, + /* currentLiveOffsetMs= */ C.TIME_UNSET, + /* contentDurationMs= */ durationMs, + /* contentBufferedPositionMs= */ defaultPositionMs), Player.DISCONTINUITY_REASON_REMOVE); } - - // Player transitions to Player.STATE_ENDED if the current index is part of the removed tail. - final boolean transitionsToEnded = - newPlayerInfo.playbackState != Player.STATE_IDLE - && newPlayerInfo.playbackState != Player.STATE_ENDED - && fromIndex < toIndex - && toIndex == oldTimeline.getWindowCount() - && getCurrentMediaItemIndex() >= fromIndex; - if (transitionsToEnded) { - newPlayerInfo = - newPlayerInfo.copyWithPlaybackState(Player.STATE_ENDED, /* playerError= */ null); - } - - updatePlayerInfo( - newPlayerInfo, - /* timelineChangeReason= */ Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - /* ignored */ Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, - /* positionDiscontinuity= */ currentItemRemoved, - Player.DISCONTINUITY_REASON_REMOVE, - /* mediaItemTransition= */ playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex - >= fromIndex - && playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex < toIndex, - Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED); + } else { + newPlayerInfo = + maskTimelineAndPositionInfo( + playerInfo, + newTimeline, + newMediaItemIndex, + newPeriodIndex, + Player.DISCONTINUITY_REASON_REMOVE); } + + // Player transitions to Player.STATE_ENDED if the current index is part of the removed tail. + final boolean transitionsToEnded = + newPlayerInfo.playbackState != Player.STATE_IDLE + && newPlayerInfo.playbackState != Player.STATE_ENDED + && fromIndex < toIndex + && toIndex == oldTimeline.getWindowCount() + && oldMediaItemIndex >= fromIndex; + if (transitionsToEnded) { + newPlayerInfo = + newPlayerInfo.copyWithPlaybackState(Player.STATE_ENDED, /* playerError= */ null); + } + + return newPlayerInfo; } @Override @@ -1176,9 +1180,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; @Override public int getCurrentMediaItemIndex() { - return playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex == C.INDEX_UNSET - ? 0 - : playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex; + return getCurrentMediaItemIndexInternal(playerInfo); } // TODO(b/184479406): Get the index directly from Player rather than Timeline. @@ -1773,7 +1775,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; return null; } - private Timeline createMaskingTimeline(List windows, List periods) { + private static Timeline createMaskingTimeline(List windows, List periods) { return new RemotableTimeline( new ImmutableList.Builder().addAll(windows).build(), new ImmutableList.Builder().addAll(periods).build(), @@ -2751,7 +2753,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; } @Nullable - private PeriodInfo getPeriodInfo( + private static PeriodInfo getPeriodInfo( Timeline timeline, Window window, Period period, int windowIndex, long windowPositionUs) { checkIndex(windowIndex, 0, timeline.getWindowCount()); timeline.getWindow(windowIndex, window); @@ -2773,7 +2775,13 @@ import org.checkerframework.checker.nullness.qual.NonNull; return new PeriodInfo(periodIndex, periodPositionUs); } - private PlayerInfo maskTimelineAndPositionInfo( + private static int getCurrentMediaItemIndexInternal(PlayerInfo playerInfo) { + return playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex == C.INDEX_UNSET + ? 0 + : playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex; + } + + private static PlayerInfo maskTimelineAndPositionInfo( PlayerInfo playerInfo, Timeline timeline, int newMediaItemIndex, @@ -2808,7 +2816,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; discontinuityReason); } - private PlayerInfo maskTimelineAndPositionInfo( + private static PlayerInfo maskTimelineAndPositionInfo( PlayerInfo playerInfo, Timeline timeline, PositionInfo newPositionInfo, @@ -2853,14 +2861,15 @@ import org.checkerframework.checker.nullness.qual.NonNull; currentPositionMs = estimatedPositionMs; } - private Period getPeriodWithNewWindowIndex(Timeline timeline, int periodIndex, int windowIndex) { + private static Period getPeriodWithNewWindowIndex( + Timeline timeline, int periodIndex, int windowIndex) { Period period = new Period(); timeline.getPeriod(periodIndex, period); period.windowIndex = windowIndex; return period; } - private int getNewPeriodIndexWithoutRemovedPeriods( + private static int getNewPeriodIndexWithoutRemovedPeriods( Timeline timeline, int oldPeriodIndex, int fromIndex, int toIndex) { if (oldPeriodIndex == C.INDEX_UNSET) { return oldPeriodIndex; @@ -2906,7 +2915,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; /* isPlaceholder= */ true); } - private void rebuildPeriods( + private static void rebuildPeriods( Timeline oldTimeline, List newWindows, List newPeriods) { for (int i = 0; i < newWindows.size(); i++) { Window window = newWindows.get(i);