From 4ef2a2f870a4df428ba3cc4f6162f30abbc5f9fb Mon Sep 17 00:00:00 2001 From: nift4 Date: Wed, 1 Oct 2025 11:52:08 +0200 Subject: [PATCH 1/2] 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 | 19 +++++++++++++++++ 2 files changed, 35 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 5e7ed708563..6f96427b15e 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -2513,12 +2513,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); @@ -2564,6 +2560,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..fc909b653e5 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,25 @@ 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 the first element in the shuffled order should be set to the one whose index in the + * unshuffled order is {@code startIndex}. + * + * @param insertionCount The number of elements. + * @param startIndex The index of the new element in the unshuffled order that should be the first + * in the shuffled order or {@link C#INDEX_UNSET} if the the first element in the shuffled + * order is not specified. It should be ignored if the new list is empty, or if it is larger + * than the last index (inclusive) of the new list. + * @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. * From 3125bc7de0f1214d05eeda0656dcede0b13e0953 Mon Sep 17 00:00:00 2001 From: Tianyi Feng Date: Thu, 16 Oct 2025 11:00:49 +0000 Subject: [PATCH 2/2] Add release note --- RELEASENOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e8f9a2670e0..81a1518b0b8 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -4,6 +4,8 @@ * Common Library: * ExoPlayer: + * Add `cloneAndSet(int, int)` to `ShuffleOrder` with a default + implementation ([#2834](https://github.com/androidx/media/pull/2834)). * CompositionPlayer: * Transformer: * Track Selection: