From ce83e1e26081698a868e0f7f17af339d330ad71d Mon Sep 17 00:00:00 2001 From: nift4 Date: Wed, 1 Oct 2025 11:52:08 +0200 Subject: [PATCH] add ShuffleOrder.cloneAndSet() to provide startIndex to shuffle order Shuffle order may want to ensure that the song that's going to be played (was chosen by user) is also the first song in shuffle playlist, as that makes sure all other songs will be playing afterwards. If first song gets random position in shuffled playlist, playback may stop before playing every song as it will start in the middle of shuffled list. --- .../media3/exoplayer/ExoPlayerImpl.java | 21 ++++++++++++++----- .../media3/exoplayer/source/ShuffleOrder.java | 16 ++++++++++++++ 2 files changed, 32 insertions(+), 5 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 5d5ce726c27..b6277305964 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -2454,12 +2454,8 @@ private void setMediaSourcesInternal( int currentWindowIndex = getCurrentWindowIndexInternal(playbackInfo); long currentPositionMs = getCurrentPosition(); pendingOperationAcks++; - if (!mediaSourceHolderSnapshots.isEmpty()) { - removeMediaSourceHolders( - /* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolderSnapshots.size()); - } List holders = - addMediaSourceHolders(/* index= */ 0, mediaSources); + setMediaSourceHolders(mediaSources, startWindowIndex); Timeline timeline = createMaskingTimeline(); if (!timeline.isEmpty() && startWindowIndex >= timeline.getWindowCount()) { throw new IllegalSeekPositionException(timeline, startWindowIndex, startPositionMs); @@ -2505,6 +2501,21 @@ private void setMediaSourcesInternal( /* repeatCurrentMediaItem= */ false); } + private List setMediaSourceHolders( + List mediaSources, int startIndex) { + mediaSourceHolderSnapshots.clear(); + List holders = new ArrayList<>(); + for (int i = 0; i < mediaSources.size(); i++) { + MediaSourceList.MediaSourceHolder holder = + new MediaSourceList.MediaSourceHolder(mediaSources.get(i), useLazyPreparation); + holders.add(holder); + mediaSourceHolderSnapshots.add( + i, new MediaSourceHolderSnapshot(holder.uid, holder.mediaSource)); + } + shuffleOrder = shuffleOrder.cloneAndSet(/* insertionCount= */ holders.size(), startIndex); + return holders; + } + private List addMediaSourceHolders( int index, List mediaSources) { List holders = new ArrayList<>(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ShuffleOrder.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ShuffleOrder.java index dbdf2f272f8..e4a28f93fd9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ShuffleOrder.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/ShuffleOrder.java @@ -284,6 +284,22 @@ default ShuffleOrder cloneAndMove(int indexFrom, int indexToExclusive, int newIn return this; } + /** + * Returns a copy of the shuffle order with all elements replaced. + * + *

The default implementation uses {@link #cloneAndClear} and {@link #cloneAndInsert(int, int)} + * to replace all elements in the shuffle order. Custom implementations can override this method + * if access to the {@code startIndex} is desired. + * + * @param insertionCount The number of elements. + * @param startIndex The index playback will start from after replacement, or {@link + * C#INDEX_UNSET} if the new list is empty or the index is unknown. + * @return A copy of this {@link ShuffleOrder} with the elements replaced. + */ + default ShuffleOrder cloneAndSet(int insertionCount, int startIndex) { + return cloneAndClear().cloneAndInsert(0, insertionCount); + } + /** * Returns a copy of the shuffle order with a range of elements removed. *