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
This commit is contained in:
tonihei 2023-05-22 13:41:39 +01:00
parent caf1c77af1
commit 1fa790348e
2 changed files with 202 additions and 172 deletions

View File

@ -659,16 +659,7 @@ import java.util.concurrent.TimeoutException;
setMediaSources(mediaSources, /* resetPosition= */ maskingWindowIndex == C.INDEX_UNSET); setMediaSources(mediaSources, /* resetPosition= */ maskingWindowIndex == C.INDEX_UNSET);
return; return;
} }
Timeline oldTimeline = getCurrentTimeline(); PlaybackInfo newPlaybackInfo = addMediaSourcesInternal(playbackInfo, index, mediaSources);
pendingOperationAcks++;
List<MediaSourceList.MediaSourceHolder> holders = addMediaSourceHolders(index, mediaSources);
Timeline newTimeline = createMaskingTimeline();
PlaybackInfo newPlaybackInfo =
maskTimelineAndPosition(
playbackInfo,
newTimeline,
getPeriodPositionUsAfterTimelineChanged(oldTimeline, newTimeline));
internalPlayer.addMediaSources(index, holders, shuffleOrder);
updatePlaybackInfo( updatePlaybackInfo(
newPlaybackInfo, newPlaybackInfo,
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, /* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
@ -691,7 +682,7 @@ import java.util.concurrent.TimeoutException;
// Do nothing. // Do nothing.
return; return;
} }
PlaybackInfo newPlaybackInfo = removeMediaItemsInternal(fromIndex, toIndex); PlaybackInfo newPlaybackInfo = removeMediaItemsInternal(playbackInfo, fromIndex, toIndex);
boolean positionDiscontinuity = boolean positionDiscontinuity =
!newPlaybackInfo.periodId.periodUid.equals(playbackInfo.periodId.periodUid); !newPlaybackInfo.periodId.periodUid.equals(playbackInfo.periodId.periodUid);
updatePlaybackInfo( updatePlaybackInfo(
@ -725,7 +716,11 @@ import java.util.concurrent.TimeoutException;
maskTimelineAndPosition( maskTimelineAndPosition(
playbackInfo, playbackInfo,
newTimeline, newTimeline,
getPeriodPositionUsAfterTimelineChanged(oldTimeline, newTimeline)); getPeriodPositionUsAfterTimelineChanged(
oldTimeline,
newTimeline,
getCurrentWindowIndexInternal(playbackInfo),
getContentPositionInternal(playbackInfo)));
internalPlayer.moveMediaSources(fromIndex, toIndex, newFromIndex, shuffleOrder); internalPlayer.moveMediaSources(fromIndex, toIndex, newFromIndex, shuffleOrder);
updatePlaybackInfo( updatePlaybackInfo(
newPlaybackInfo, newPlaybackInfo,
@ -1057,7 +1052,7 @@ import java.util.concurrent.TimeoutException;
@Override @Override
public int getCurrentMediaItemIndex() { public int getCurrentMediaItemIndex() {
verifyApplicationThread(); verifyApplicationThread();
int currentWindowIndex = getCurrentWindowIndexInternal(); int currentWindowIndex = getCurrentWindowIndexInternal(playbackInfo);
return currentWindowIndex == C.INDEX_UNSET ? 0 : currentWindowIndex; return currentWindowIndex == C.INDEX_UNSET ? 0 : currentWindowIndex;
} }
@ -1117,17 +1112,7 @@ import java.util.concurrent.TimeoutException;
@Override @Override
public long getContentPosition() { public long getContentPosition() {
verifyApplicationThread(); verifyApplicationThread();
if (isPlayingAd()) { return getContentPositionInternal(playbackInfo);
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();
}
} }
@Override @Override
@ -1853,13 +1838,25 @@ import java.util.concurrent.TimeoutException;
/* repeatCurrentMediaItem= */ false); /* repeatCurrentMediaItem= */ false);
} }
private int getCurrentWindowIndexInternal() { private int getCurrentWindowIndexInternal(PlaybackInfo playbackInfo) {
if (playbackInfo.timeline.isEmpty()) { if (playbackInfo.timeline.isEmpty()) {
return maskingWindowIndex; 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) { private long getCurrentPositionUsInternal(PlaybackInfo playbackInfo) {
@ -1874,10 +1871,9 @@ import java.util.concurrent.TimeoutException;
if (playbackInfo.periodId.isAd()) { if (playbackInfo.periodId.isAd()) {
return positionUs; return positionUs;
} else {
return periodPositionUsToWindowPositionUs(
playbackInfo.timeline, playbackInfo.periodId, positionUs);
} }
return periodPositionUsToWindowPositionUs(
playbackInfo.timeline, playbackInfo.periodId, positionUs);
} }
private List<MediaSource> createMediaSources(List<MediaItem> mediaItems) { private List<MediaSource> createMediaSources(List<MediaItem> mediaItems) {
@ -2276,7 +2272,7 @@ import java.util.concurrent.TimeoutException;
int startWindowIndex, int startWindowIndex,
long startPositionMs, long startPositionMs,
boolean resetToDefaultPosition) { boolean resetToDefaultPosition) {
int currentWindowIndex = getCurrentWindowIndexInternal(); int currentWindowIndex = getCurrentWindowIndexInternal(playbackInfo);
long currentPositionMs = getCurrentPosition(); long currentPositionMs = getCurrentPosition();
pendingOperationAcks++; pendingOperationAcks++;
if (!mediaSourceHolderSnapshots.isEmpty()) { if (!mediaSourceHolderSnapshots.isEmpty()) {
@ -2347,9 +2343,30 @@ import java.util.concurrent.TimeoutException;
return holders; return holders;
} }
private PlaybackInfo removeMediaItemsInternal(int fromIndex, int toIndex) { private PlaybackInfo addMediaSourcesInternal(
int currentIndex = getCurrentMediaItemIndex(); PlaybackInfo playbackInfo, int index, List<MediaSource> mediaSources) {
Timeline oldTimeline = getCurrentTimeline(); Timeline oldTimeline = playbackInfo.timeline;
pendingOperationAcks++;
List<MediaSourceList.MediaSourceHolder> 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(); int currentMediaSourceCount = mediaSourceHolderSnapshots.size();
pendingOperationAcks++; pendingOperationAcks++;
removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */ toIndex); removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */ toIndex);
@ -2358,7 +2375,8 @@ import java.util.concurrent.TimeoutException;
maskTimelineAndPosition( maskTimelineAndPosition(
playbackInfo, playbackInfo,
newTimeline, newTimeline,
getPeriodPositionUsAfterTimelineChanged(oldTimeline, newTimeline)); getPeriodPositionUsAfterTimelineChanged(
oldTimeline, newTimeline, currentIndex, contentPositionMs));
// Player transitions to STATE_ENDED if the current index is part of the removed tail. // Player transitions to STATE_ENDED if the current index is part of the removed tail.
final boolean transitionsToEnded = final boolean transitionsToEnded =
newPlaybackInfo.playbackState != STATE_IDLE newPlaybackInfo.playbackState != STATE_IDLE
@ -2387,7 +2405,9 @@ import java.util.concurrent.TimeoutException;
private PlaybackInfo maskTimelineAndPosition( private PlaybackInfo maskTimelineAndPosition(
PlaybackInfo playbackInfo, Timeline timeline, @Nullable Pair<Object, Long> periodPositionUs) { PlaybackInfo playbackInfo, Timeline timeline, @Nullable Pair<Object, Long> periodPositionUs) {
checkArgument(timeline.isEmpty() || periodPositionUs != null); checkArgument(timeline.isEmpty() || periodPositionUs != null);
// Get the old timeline and position before updating playbackInfo.
Timeline oldTimeline = playbackInfo.timeline; Timeline oldTimeline = playbackInfo.timeline;
long oldContentPositionMs = getContentPositionInternal(playbackInfo);
// Mask the timeline. // Mask the timeline.
playbackInfo = playbackInfo.copyWithTimeline(timeline); playbackInfo = playbackInfo.copyWithTimeline(timeline);
@ -2415,7 +2435,7 @@ import java.util.concurrent.TimeoutException;
MediaPeriodId newPeriodId = MediaPeriodId newPeriodId =
playingPeriodChanged ? new MediaPeriodId(periodPositionUs.first) : playbackInfo.periodId; playingPeriodChanged ? new MediaPeriodId(periodPositionUs.first) : playbackInfo.periodId;
long newContentPositionUs = periodPositionUs.second; long newContentPositionUs = periodPositionUs.second;
long oldContentPositionUs = Util.msToUs(getContentPosition()); long oldContentPositionUs = Util.msToUs(oldContentPositionMs);
if (!oldTimeline.isEmpty()) { if (!oldTimeline.isEmpty()) {
oldContentPositionUs -= oldContentPositionUs -=
oldTimeline.getPeriodByUid(oldPeriodUid, period).getPositionInWindowUs(); oldTimeline.getPeriodByUid(oldPeriodUid, period).getPositionInWindowUs();
@ -2491,20 +2511,21 @@ import java.util.concurrent.TimeoutException;
@Nullable @Nullable
private Pair<Object, Long> getPeriodPositionUsAfterTimelineChanged( private Pair<Object, Long> getPeriodPositionUsAfterTimelineChanged(
Timeline oldTimeline, Timeline newTimeline) { Timeline oldTimeline,
long currentPositionMs = getContentPosition(); Timeline newTimeline,
int currentWindowIndexInternal,
long contentPositionMs) {
if (oldTimeline.isEmpty() || newTimeline.isEmpty()) { if (oldTimeline.isEmpty() || newTimeline.isEmpty()) {
boolean isCleared = !oldTimeline.isEmpty() && newTimeline.isEmpty(); boolean isCleared = !oldTimeline.isEmpty() && newTimeline.isEmpty();
return maskWindowPositionMsOrGetPeriodPositionUs( return maskWindowPositionMsOrGetPeriodPositionUs(
newTimeline, newTimeline,
isCleared ? C.INDEX_UNSET : getCurrentWindowIndexInternal(), isCleared ? C.INDEX_UNSET : currentWindowIndexInternal,
isCleared ? C.TIME_UNSET : currentPositionMs); isCleared ? C.TIME_UNSET : contentPositionMs);
} }
int currentMediaItemIndex = getCurrentMediaItemIndex();
@Nullable @Nullable
Pair<Object, Long> oldPeriodPositionUs = Pair<Object, Long> oldPeriodPositionUs =
oldTimeline.getPeriodPositionUs( oldTimeline.getPeriodPositionUs(
window, period, currentMediaItemIndex, Util.msToUs(currentPositionMs)); window, period, currentWindowIndexInternal, Util.msToUs(contentPositionMs));
Object periodUid = castNonNull(oldPeriodPositionUs).first; Object periodUid = castNonNull(oldPeriodPositionUs).first;
if (newTimeline.getIndexOfPeriod(periodUid) != C.INDEX_UNSET) { if (newTimeline.getIndexOfPeriod(periodUid) != C.INDEX_UNSET) {
// The old period position is still available in the new timeline. // 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) { private PlayerMessage createMessageInternal(Target target) {
int currentWindowIndex = getCurrentWindowIndexInternal(); int currentWindowIndex = getCurrentWindowIndexInternal(playbackInfo);
return new PlayerMessage( return new PlayerMessage(
internalPlayer, internalPlayer,
target, target,

View File

@ -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. // Add media items to the end of the timeline if the index exceeds the window count.
index = min(index, playerInfo.timeline.getWindowCount()); 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<MediaItem> mediaItems) {
Timeline oldTimeline = playerInfo.timeline; Timeline oldTimeline = playerInfo.timeline;
List<Window> newWindows = new ArrayList<>(); List<Window> newWindows = new ArrayList<>();
List<Period> newPeriods = new ArrayList<>(); List<Period> newPeriods = new ArrayList<>();
@ -948,21 +960,12 @@ import org.checkerframework.checker.nullness.qual.NonNull;
? playerInfo.sessionPositionInfo.positionInfo.periodIndex + mediaItems.size() ? playerInfo.sessionPositionInfo.positionInfo.periodIndex + mediaItems.size()
: playerInfo.sessionPositionInfo.positionInfo.periodIndex; : playerInfo.sessionPositionInfo.positionInfo.periodIndex;
} }
PlayerInfo newPlayerInfo = return maskTimelineAndPositionInfo(
maskTimelineAndPositionInfo( playerInfo,
playerInfo, newTimeline,
newTimeline, newMediaItemIndex,
newMediaItemIndex, newPeriodIndex,
newPeriodIndex, Player.DISCONTINUITY_REASON_INTERNAL);
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);
} }
@Override @Override
@ -1004,13 +1007,29 @@ import org.checkerframework.checker.nullness.qual.NonNull;
} }
private void removeMediaItemsInternal(int fromIndex, int toIndex) { private void removeMediaItemsInternal(int fromIndex, int toIndex) {
Timeline oldTimeline = playerInfo.timeline;
int playlistSize = playerInfo.timeline.getWindowCount(); int playlistSize = playerInfo.timeline.getWindowCount();
toIndex = min(toIndex, playlistSize); toIndex = min(toIndex, playlistSize);
if (fromIndex >= playlistSize || fromIndex == toIndex) { if (fromIndex >= playlistSize || fromIndex == toIndex || playlistSize == 0) {
return; 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<Window> newWindows = new ArrayList<>(); List<Window> newWindows = new ArrayList<>();
List<Period> newPeriods = new ArrayList<>(); List<Period> newPeriods = new ArrayList<>();
for (int i = 0; i < oldTimeline.getWindowCount(); i++) { for (int i = 0; i < oldTimeline.getWindowCount(); i++) {
@ -1021,124 +1040,109 @@ import org.checkerframework.checker.nullness.qual.NonNull;
rebuildPeriods(oldTimeline, newWindows, newPeriods); rebuildPeriods(oldTimeline, newWindows, newPeriods);
Timeline newTimeline = createMaskingTimeline(newWindows, newPeriods); Timeline newTimeline = createMaskingTimeline(newWindows, newPeriods);
int oldMediaItemIndex = getCurrentMediaItemIndex(); int oldMediaItemIndex = getCurrentMediaItemIndexInternal(playerInfo);
int newMediaItemIndex = oldMediaItemIndex; int newMediaItemIndex = oldMediaItemIndex;
int oldPeriodIndex = playerInfo.sessionPositionInfo.positionInfo.periodIndex; int oldPeriodIndex = playerInfo.sessionPositionInfo.positionInfo.periodIndex;
int newPeriodIndex = oldPeriodIndex; int newPeriodIndex = oldPeriodIndex;
boolean currentItemRemoved =
getCurrentMediaItemIndex() >= fromIndex && getCurrentMediaItemIndex() < toIndex;
Window window = new Window(); Window window = new Window();
if (oldTimeline.isEmpty()) { boolean currentItemRemoved = oldMediaItemIndex >= fromIndex && oldMediaItemIndex < toIndex;
// No masking required. Just forwarding command to session. if (newTimeline.isEmpty()) {
newMediaItemIndex = C.INDEX_UNSET;
newPeriodIndex = 0;
} else { } 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) { if (currentItemRemoved) {
PositionInfo newPositionInfo; int oldNextMediaItemIndex =
if (newMediaItemIndex == C.INDEX_UNSET) { resolveSubsequentMediaItemIndex(
newPositionInfo = SessionPositionInfo.DEFAULT_POSITION_INFO; playerInfo.repeatMode,
newPlayerInfo = playerInfo.shuffleModeEnabled,
maskTimelineAndPositionInfo( oldMediaItemIndex,
playerInfo, oldTimeline,
newTimeline, fromIndex,
newPositionInfo, toIndex);
SessionPositionInfo.DEFAULT, if (oldNextMediaItemIndex == C.INDEX_UNSET) {
Player.DISCONTINUITY_REASON_REMOVE); newMediaItemIndex = newTimeline.getFirstWindowIndex(playerInfo.shuffleModeEnabled);
} else if (oldNextMediaItemIndex >= toIndex) {
newMediaItemIndex = oldNextMediaItemIndex - (toIndex - fromIndex);
} else { } else {
Window newWindow = newTimeline.getWindow(newMediaItemIndex, new Window()); newMediaItemIndex = oldNextMediaItemIndex;
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);
} }
} 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 = newPlayerInfo =
maskTimelineAndPositionInfo( maskTimelineAndPositionInfo(
playerInfo, playerInfo,
newTimeline, 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, newMediaItemIndex,
newWindow.mediaItem,
/* periodUid= */ null,
newPeriodIndex, 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.DISCONTINUITY_REASON_REMOVE);
} }
} else {
// Player transitions to Player.STATE_ENDED if the current index is part of the removed tail. newPlayerInfo =
final boolean transitionsToEnded = maskTimelineAndPositionInfo(
newPlayerInfo.playbackState != Player.STATE_IDLE playerInfo,
&& newPlayerInfo.playbackState != Player.STATE_ENDED newTimeline,
&& fromIndex < toIndex newMediaItemIndex,
&& toIndex == oldTimeline.getWindowCount() newPeriodIndex,
&& getCurrentMediaItemIndex() >= fromIndex; Player.DISCONTINUITY_REASON_REMOVE);
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);
} }
// 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 @Override
@ -1176,9 +1180,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
@Override @Override
public int getCurrentMediaItemIndex() { public int getCurrentMediaItemIndex() {
return playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex == C.INDEX_UNSET return getCurrentMediaItemIndexInternal(playerInfo);
? 0
: playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex;
} }
// TODO(b/184479406): Get the index directly from Player rather than Timeline. // 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; return null;
} }
private Timeline createMaskingTimeline(List<Window> windows, List<Period> periods) { private static Timeline createMaskingTimeline(List<Window> windows, List<Period> periods) {
return new RemotableTimeline( return new RemotableTimeline(
new ImmutableList.Builder<Window>().addAll(windows).build(), new ImmutableList.Builder<Window>().addAll(windows).build(),
new ImmutableList.Builder<Period>().addAll(periods).build(), new ImmutableList.Builder<Period>().addAll(periods).build(),
@ -2751,7 +2753,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
} }
@Nullable @Nullable
private PeriodInfo getPeriodInfo( private static PeriodInfo getPeriodInfo(
Timeline timeline, Window window, Period period, int windowIndex, long windowPositionUs) { Timeline timeline, Window window, Period period, int windowIndex, long windowPositionUs) {
checkIndex(windowIndex, 0, timeline.getWindowCount()); checkIndex(windowIndex, 0, timeline.getWindowCount());
timeline.getWindow(windowIndex, window); timeline.getWindow(windowIndex, window);
@ -2773,7 +2775,13 @@ import org.checkerframework.checker.nullness.qual.NonNull;
return new PeriodInfo(periodIndex, periodPositionUs); 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, PlayerInfo playerInfo,
Timeline timeline, Timeline timeline,
int newMediaItemIndex, int newMediaItemIndex,
@ -2808,7 +2816,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
discontinuityReason); discontinuityReason);
} }
private PlayerInfo maskTimelineAndPositionInfo( private static PlayerInfo maskTimelineAndPositionInfo(
PlayerInfo playerInfo, PlayerInfo playerInfo,
Timeline timeline, Timeline timeline,
PositionInfo newPositionInfo, PositionInfo newPositionInfo,
@ -2853,14 +2861,15 @@ import org.checkerframework.checker.nullness.qual.NonNull;
currentPositionMs = estimatedPositionMs; 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(); Period period = new Period();
timeline.getPeriod(periodIndex, period); timeline.getPeriod(periodIndex, period);
period.windowIndex = windowIndex; period.windowIndex = windowIndex;
return period; return period;
} }
private int getNewPeriodIndexWithoutRemovedPeriods( private static int getNewPeriodIndexWithoutRemovedPeriods(
Timeline timeline, int oldPeriodIndex, int fromIndex, int toIndex) { Timeline timeline, int oldPeriodIndex, int fromIndex, int toIndex) {
if (oldPeriodIndex == C.INDEX_UNSET) { if (oldPeriodIndex == C.INDEX_UNSET) {
return oldPeriodIndex; return oldPeriodIndex;
@ -2906,7 +2915,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
/* isPlaceholder= */ true); /* isPlaceholder= */ true);
} }
private void rebuildPeriods( private static void rebuildPeriods(
Timeline oldTimeline, List<Window> newWindows, List<Period> newPeriods) { Timeline oldTimeline, List<Window> newWindows, List<Period> newPeriods) {
for (int i = 0; i < newWindows.size(); i++) { for (int i = 0; i < newWindows.size(); i++) {
Window window = newWindows.get(i); Window window = newWindows.get(i);