From 28c70e5e5415ed5b0081f8c009be8c3247860816 Mon Sep 17 00:00:00 2001 From: tianyifeng Date: Fri, 5 Apr 2024 08:24:29 -0700 Subject: [PATCH] Add `remove(MediaSource)` to `BasePreloadManager` Both `remove(MediaItem)` and `remove(MediaSource)` return a boolean suggesting that whether the preload manager is holding the corresponding `MediaSource` and it has been removed. PiperOrigin-RevId: 622185427 --- RELEASENOTES.md | 1 + .../source/preload/BasePreloadManager.java | 26 ++++++- .../preload/DefaultPreloadManagerTest.java | 71 +++++++++++++++++-- 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c1a3a290c9..a9afbed8d5 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -34,6 +34,7 @@ * Allow a null value to be returned by `TargetPreloadStatusControl.getTargetPreloadStatus(T)` to indicate not to preload a `MediaSource` with the given `rankingData`. + * Add `remove(MediaSource)` to `BasePreloadManager`. * Transformer: * Add `audioConversionProcess` and `videoConversionProcess` to `ExportResult` indicating how the respective track in the output file 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 5e89c2545f..2e841da271 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 @@ -151,13 +151,37 @@ public abstract class BasePreloadManager { * Removes a {@link MediaItem} from the preload manager. * * @param mediaItem The {@link MediaItem} to remove. + * @return {@code true} if the preload manager is holding a {@link MediaSource} of the given + * {@link MediaItem} and it has been removed, otherwise {@code false}. */ - public final void remove(MediaItem mediaItem) { + public final boolean remove(MediaItem mediaItem) { if (mediaItemMediaSourceHolderMap.containsKey(mediaItem)) { MediaSource mediaSource = mediaItemMediaSourceHolderMap.get(mediaItem).mediaSource; mediaItemMediaSourceHolderMap.remove(mediaItem); releaseSourceInternal(mediaSource); + return true; } + return false; + } + + /** + * Removes a {@link MediaSource} from the preload manager. + * + * @param mediaSource The {@link MediaSource} to remove. + * @return {@code true} if the preload manager is holding the given {@link MediaSource} instance + * and it has been removed, otherwise {@code false}. + */ + public final boolean remove(MediaSource mediaSource) { + MediaItem mediaItem = mediaSource.getMediaItem(); + if (mediaItemMediaSourceHolderMap.containsKey(mediaItem)) { + MediaSource heldMediaSource = mediaItemMediaSourceHolderMap.get(mediaItem).mediaSource; + if (mediaSource == heldMediaSource) { + mediaItemMediaSourceHolderMap.remove(mediaItem); + releaseSourceInternal(mediaSource); + return true; + } + } + return false; } /** Releases the preload manager. */ 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 788095a12b..1657968dca 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 @@ -411,7 +411,7 @@ public class DefaultPreloadManagerTest { } @Test - public void removeMediaItemPreviouslyAdded_returnsCorrectCountAndNullSource_sourceReleased() { + public void removeByMediaItems_correspondingHeldSourceRemovedAndReleased() { TargetPreloadStatusControl targetPreloadStatusControl = rankingData -> new DefaultPreloadManager.Status(DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED); @@ -449,16 +449,75 @@ public class DefaultPreloadManagerTest { }; }); preloadManager.add(mediaItem1, /* rankingData= */ 1); - preloadManager.add(mediaItem2, /* rankingData= */ 2); preloadManager.invalidate(); shadowOf(Looper.getMainLooper()).idle(); - preloadManager.remove(mediaItem1); + boolean mediaItem1Removed = preloadManager.remove(mediaItem1); + boolean mediaItem2Removed = preloadManager.remove(mediaItem2); shadowOf(Looper.getMainLooper()).idle(); - assertThat(preloadManager.getSourceCount()).isEqualTo(1); - assertThat(preloadManager.getMediaSource(mediaItem1)).isNull(); - assertThat(preloadManager.getMediaSource(mediaItem2).getMediaItem()).isEqualTo(mediaItem2); + assertThat(mediaItem1Removed).isTrue(); + assertThat(mediaItem2Removed).isFalse(); + assertThat(preloadManager.getSourceCount()).isEqualTo(0); + assertThat(internalSourceToReleaseReferenceByMediaId).containsExactly("mediaId1"); + } + + @Test + public void removeByMediaSources_heldSourceRemovedAndReleased() { + TargetPreloadStatusControl targetPreloadStatusControl = + rankingData -> + new DefaultPreloadManager.Status(DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED); + MediaSource.Factory mockMediaSourceFactory = mock(MediaSource.Factory.class); + DefaultPreloadManager preloadManager = + new DefaultPreloadManager( + targetPreloadStatusControl, + mockMediaSourceFactory, + trackSelector, + bandwidthMeter, + rendererCapabilitiesListFactory, + allocator, + Util.getCurrentOrMainLooper()); + MediaItem.Builder mediaItemBuilder = new MediaItem.Builder(); + MediaItem mediaItem1 = + mediaItemBuilder.setMediaId("mediaId1").setUri("http://exoplayer.dev/video1").build(); + MediaItem mediaItem2 = + mediaItemBuilder.setMediaId("mediaId2").setUri("http://exoplayer.dev/video2").build(); + ArrayList internalSourceToReleaseReferenceByMediaId = new ArrayList<>(); + when(mockMediaSourceFactory.createMediaSource(any())) + .thenAnswer( + invocation -> { + MediaItem mediaItem = invocation.getArgument(0); + return new FakeMediaSource() { + @Override + public MediaItem getMediaItem() { + return mediaItem; + } + + @Override + protected void releaseSourceInternal() { + internalSourceToReleaseReferenceByMediaId.add(mediaItem.mediaId); + super.releaseSourceInternal(); + } + }; + }); + preloadManager.add(mediaItem1, /* rankingData= */ 1); + preloadManager.invalidate(); + shadowOf(Looper.getMainLooper()).idle(); + MediaSource mediaSource1 = preloadManager.getMediaSource(mediaItem1); + DefaultMediaSourceFactory defaultMediaSourceFactory = + new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext()); + MediaSource mediaSource2 = defaultMediaSourceFactory.createMediaSource(mediaItem1); + MediaSource mediaSource3 = defaultMediaSourceFactory.createMediaSource(mediaItem2); + + boolean mediaSource1Removed = preloadManager.remove(mediaSource1); + boolean mediaSource2Removed = preloadManager.remove(mediaSource2); + boolean mediaSource3Removed = preloadManager.remove(mediaSource3); + shadowOf(Looper.getMainLooper()).idle(); + + assertThat(mediaSource1Removed).isTrue(); + assertThat(mediaSource2Removed).isFalse(); + assertThat(mediaSource3Removed).isFalse(); + assertThat(preloadManager.getSourceCount()).isEqualTo(0); assertThat(internalSourceToReleaseReferenceByMediaId).containsExactly("mediaId1"); }