From 9c72fa8a7a271ab72384941a5b707f8f7eb61474 Mon Sep 17 00:00:00 2001 From: tianyifeng Date: Tue, 9 Apr 2024 06:27:29 -0700 Subject: [PATCH] Add `reset` to `BasePreloadManager` to release all the holding sources Compared to `release`, the `reset` method doesn't release the preload manager instance. This applies to the use case that an app wants to discard all the sources but keep the preload manager active for later usage. Also rename the `releaseSourceInternal` to `removeSourceInternal`, as the latter sounds more generic for different preload manager implementations. PiperOrigin-RevId: 623148723 --- RELEASENOTES.md | 2 + .../source/preload/BasePreloadManager.java | 15 ++- .../preload/DefaultPreloadManagerTest.java | 104 ++++++++++++++---- 3 files changed, 99 insertions(+), 22 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e814645c9e..d52126b445 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -24,6 +24,8 @@ `TargetPreloadStatusControl.getTargetPreloadStatus(T)` to indicate not to preload a `MediaSource` with the given `rankingData`. * Add `remove(MediaSource)` to `BasePreloadManager`. + * Add `reset` to `BasePreloadManager` to release all the holding sources + while keep the preload manager instance. * 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 2e841da271..6a66cadffe 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 @@ -184,8 +184,10 @@ public abstract class BasePreloadManager { return false; } - /** Releases the preload manager. */ - public final void release() { + /** + * Resets the preload manager. All sources that the preload manager is holding will be released. + */ + public final void reset() { for (MediaSourceHolder sourceHolder : mediaItemMediaSourceHolderMap.values()) { releaseSourceInternal(sourceHolder.mediaSource); } @@ -194,6 +196,15 @@ public abstract class BasePreloadManager { sourceHolderPriorityQueue.clear(); targetPreloadStatusOfCurrentPreloadingSource = null; } + } + + /** + * Releases the preload manager. + * + *

The preload manager must not be used after calling this method. + */ + public final void release() { + reset(); releaseInternal(); } 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 1657968dca..833ea29ef1 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 @@ -15,7 +15,6 @@ */ package androidx.media3.exoplayer.source.preload; -import static androidx.media3.common.util.Assertions.checkNotNull; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -49,10 +48,8 @@ import androidx.media3.test.utils.FakeRenderer; import androidx.media3.test.utils.FakeVideoRenderer; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; -import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -522,31 +519,101 @@ public class DefaultPreloadManagerTest { } @Test - public void release_returnZeroCountAndNullSources_sourcesReleased() { + public void reset_returnZeroCount_sourcesButNotRendererCapabilitiesListReleased() { TargetPreloadStatusControl targetPreloadStatusControl = rankingData -> new DefaultPreloadManager.Status(DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED); MediaSource.Factory mockMediaSourceFactory = mock(MediaSource.Factory.class); - AtomicReference> underlyingRenderersReference = new AtomicReference<>(); + List underlyingRenderers = new ArrayList<>(); RenderersFactory renderersFactory = (eventHandler, videoRendererEventListener, audioRendererEventListener, textRendererOutput, metadataRendererOutput) -> { - FakeRenderer[] createdRenderers = - new FakeRenderer[] { - new FakeVideoRenderer( - SystemClock.DEFAULT.createHandler( - eventHandler.getLooper(), /* callback= */ null), - videoRendererEventListener), - new FakeAudioRenderer( - SystemClock.DEFAULT.createHandler( - eventHandler.getLooper(), /* callback= */ null), - audioRendererEventListener) + FakeRenderer fakeVideoRenderer = + new FakeVideoRenderer( + SystemClock.DEFAULT.createHandler(eventHandler.getLooper(), /* callback= */ null), + videoRendererEventListener); + underlyingRenderers.add(fakeVideoRenderer); + FakeRenderer fakeAudioRenderer = + new FakeAudioRenderer( + SystemClock.DEFAULT.createHandler(eventHandler.getLooper(), /* callback= */ null), + audioRendererEventListener); + underlyingRenderers.add(fakeAudioRenderer); + return underlyingRenderers.toArray(new Renderer[2]); + }; + DefaultPreloadManager preloadManager = + new DefaultPreloadManager( + targetPreloadStatusControl, + mockMediaSourceFactory, + trackSelector, + bandwidthMeter, + new DefaultRendererCapabilitiesList.Factory(renderersFactory), + 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(); + } }; - underlyingRenderersReference.set(ImmutableList.copyOf(createdRenderers)); - return createdRenderers; + }); + preloadManager.add(mediaItem1, /* rankingData= */ 1); + preloadManager.add(mediaItem2, /* rankingData= */ 2); + preloadManager.invalidate(); + shadowOf(Looper.getMainLooper()).idle(); + + preloadManager.reset(); + shadowOf(Looper.getMainLooper()).idle(); + + assertThat(preloadManager.getSourceCount()).isEqualTo(0); + assertThat(internalSourceToReleaseReferenceByMediaId).containsExactly("mediaId1", "mediaId2"); + for (FakeRenderer renderer : underlyingRenderers) { + assertThat(renderer.isReleased).isFalse(); + } + } + + @Test + public void release_returnZeroCount_sourcesAndRendererCapabilitiesListReleased() { + TargetPreloadStatusControl targetPreloadStatusControl = + rankingData -> + new DefaultPreloadManager.Status(DefaultPreloadManager.Status.STAGE_TIMELINE_REFRESHED); + MediaSource.Factory mockMediaSourceFactory = mock(MediaSource.Factory.class); + List underlyingRenderers = new ArrayList<>(); + RenderersFactory renderersFactory = + (eventHandler, + videoRendererEventListener, + audioRendererEventListener, + textRendererOutput, + metadataRendererOutput) -> { + FakeRenderer fakeVideoRenderer = + new FakeVideoRenderer( + SystemClock.DEFAULT.createHandler(eventHandler.getLooper(), /* callback= */ null), + videoRendererEventListener); + underlyingRenderers.add(fakeVideoRenderer); + FakeRenderer fakeAudioRenderer = + new FakeAudioRenderer( + SystemClock.DEFAULT.createHandler(eventHandler.getLooper(), /* callback= */ null), + audioRendererEventListener); + underlyingRenderers.add(fakeAudioRenderer); + return underlyingRenderers.toArray(new Renderer[2]); }; DefaultPreloadManager preloadManager = new DefaultPreloadManager( @@ -589,10 +656,7 @@ public class DefaultPreloadManagerTest { shadowOf(Looper.getMainLooper()).idle(); assertThat(preloadManager.getSourceCount()).isEqualTo(0); - assertThat(preloadManager.getMediaSource(mediaItem1)).isNull(); - assertThat(preloadManager.getMediaSource(mediaItem2)).isNull(); assertThat(internalSourceToReleaseReferenceByMediaId).containsExactly("mediaId1", "mediaId2"); - List underlyingRenderers = checkNotNull(underlyingRenderersReference.get()); for (FakeRenderer renderer : underlyingRenderers) { assertThat(renderer.isReleased).isTrue(); }