diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/BasePreloadManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/BasePreloadManager.java index d8e218f856..13961ab684 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/BasePreloadManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/BasePreloadManager.java @@ -256,6 +256,11 @@ public abstract class BasePreloadManager { /** Called when the given {@link MediaSource} completes preloading. */ protected final void onPreloadCompleted(MediaSource source) { + synchronized (lock) { + if (!isPreloading(source)) { + return; + } + } applicationHandler.post( () -> { listeners.sendEvent( @@ -267,6 +272,11 @@ public abstract class BasePreloadManager { /** Called when an error occurs. */ protected final void onPreloadError(PreloadException error, MediaSource source) { + synchronized (lock) { + if (!isPreloading(source)) { + return; + } + } applicationHandler.post( () -> { listeners.sendEvent(/* eventFlag= */ C.INDEX_UNSET, listener -> listener.onError(error)); @@ -276,13 +286,17 @@ public abstract class BasePreloadManager { /** Called when the given {@link MediaSource} has been skipped before completing preloading. */ protected final void onPreloadSkipped(MediaSource source) { + synchronized (lock) { + if (!isPreloading(source)) { + return; + } + } applicationHandler.post(() -> maybeAdvanceToNextSource(source)); } - private void maybeAdvanceToNextSource(MediaSource preloadingSource) { + private void maybeAdvanceToNextSource(MediaSource currentSource) { synchronized (lock) { - if (sourceHolderPriorityQueue.isEmpty() - || checkNotNull(sourceHolderPriorityQueue.peek()).mediaSource != preloadingSource) { + if (!isPreloading(currentSource)) { return; } do { @@ -291,6 +305,13 @@ public abstract class BasePreloadManager { } } + /** Returns whether the {@link MediaSource} is currently preloading. */ + @GuardedBy("lock") + private boolean isPreloading(MediaSource mediaSource) { + return !sourceHolderPriorityQueue.isEmpty() + && checkNotNull(sourceHolderPriorityQueue.peek()).mediaSource == mediaSource; + } + /** * Returns the {@linkplain TargetPreloadStatusControl.PreloadStatus target preload status} of the * given {@link MediaSource}. @@ -299,8 +320,7 @@ public abstract class BasePreloadManager { protected final TargetPreloadStatusControl.PreloadStatus getTargetPreloadStatus( MediaSource source) { synchronized (lock) { - if (sourceHolderPriorityQueue.isEmpty() - || checkNotNull(sourceHolderPriorityQueue.peek()).mediaSource != source) { + if (!isPreloading(source)) { return null; } return targetPreloadStatusOfCurrentPreloadingSource; diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java index e5a9299de0..2cdcf0c739 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java @@ -237,6 +237,7 @@ public class DefaultPreloadManagerTest { preloadManager.add(mediaItem2, /* rankingData= */ 2); preloadManager.invalidate(); + shadowOf(preloadThread.getLooper()).idle(); runMainLooperUntil(() -> preloadManagerListener.onCompletedMediaItemRecords.size() == 3); assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1, 2).inOrder(); @@ -300,6 +301,7 @@ public class DefaultPreloadManagerTest { currentPlayingItemIndex.set(2); preloadManager.invalidate(); + shadowOf(preloadThread.getLooper()).idle(); runMainLooperUntil(() -> preloadManagerListener.onCompletedMediaItemRecords.size() == 3); assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1, 0).inOrder(); @@ -353,6 +355,7 @@ public class DefaultPreloadManagerTest { (source, timeline) -> {}, bandwidthMeter.getTransferListener(), PlayerId.UNSET); wrappedMediaSource0.setAllowPreparation(true); wrappedMediaSource1.setAllowPreparation(true); + shadowOf(preloadThread.getLooper()).idle(); runMainLooperUntil(() -> preloadManagerListener.onCompletedMediaItemRecords.size() == 1); assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1).inOrder(); @@ -404,6 +407,7 @@ public class DefaultPreloadManagerTest { preloadManager.invalidate(); wrappedMediaSource0.setAllowPreparation(true); + shadowOf(preloadThread.getLooper()).idle(); runMainLooperUntil(() -> preloadManagerListener.onCompletedMediaItemRecords.size() == 1); assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1).inOrder(); assertThat(preloadManagerListener.onCompletedMediaItemRecords).containsExactly(mediaItem0); @@ -420,6 +424,7 @@ public class DefaultPreloadManagerTest { // preloadManagerListener.onCompletedMediaItemRecords. wrappedMediaSource1.setAllowPreparation(true); wrappedMediaSource2.setAllowPreparation(true); + shadowOf(preloadThread.getLooper()).idle(); runMainLooperUntil(() -> preloadManagerListener.onCompletedMediaItemRecords.size() == 3); assertThat(targetPreloadStatusControlCallStates).containsExactly(2, 1, 0).inOrder(); assertThat(preloadManagerListener.onCompletedMediaItemRecords) @@ -471,6 +476,7 @@ public class DefaultPreloadManagerTest { preloadManager.add(mediaItem1, /* rankingData= */ 1); preloadManager.invalidate(); + shadowOf(preloadThread.getLooper()).idle(); runMainLooperUntil(() -> preloadManagerListener.onCompletedMediaItemRecords.size() == 1); assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1); @@ -557,6 +563,7 @@ public class DefaultPreloadManagerTest { preloadManager.add(mediaItem1, /* rankingData= */ 1); preloadManager.invalidate(); + shadowOf(preloadThread.getLooper()).idle(); runMainLooperUntil(() -> preloadManagerListener.onCompletedMediaItemRecords.size() == 1); assertThat(targetPreloadStatusControlCallStates).containsExactly(0, 1).inOrder();