Skip to content

Commit dba3110

Browse files
tianyificbaker
authored andcommitted
Release internal components on preload thread in DefaultPreloadManager
The `RendererCapabilities` and `TrackSelector` objects are accessed on the preload thread during the preloading, when releasing them, they need to be released on the same thread. Otherwise, it is possible that they have already released on the application thread, while the PreloadMediaSource still tries to access them on the preload thread before the source is released. #cherrypick PiperOrigin-RevId: 694173131 (cherry picked from commit 2b54b1e)
1 parent 461a1fa commit dba3110

File tree

2 files changed

+27
-12
lines changed

2 files changed

+27
-12
lines changed

libraries/exoplayer/src/main/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManager.java

+18-9
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import static java.lang.annotation.ElementType.TYPE_USE;
2323

2424
import android.content.Context;
25+
import android.os.Handler;
2526
import android.os.Looper;
2627
import android.os.Process;
2728
import androidx.annotation.IntDef;
@@ -327,6 +328,7 @@ public long getValue() {
327328
private final TrackSelector trackSelector;
328329
private final PlaybackLooperProvider preloadLooperProvider;
329330
private final PreloadMediaSource.Factory preloadMediaSourceFactory;
331+
private final Handler preloadHandler;
330332
private final boolean deprecatedConstructorCalled;
331333

332334
private DefaultPreloadManager(Builder builder) {
@@ -341,6 +343,7 @@ private DefaultPreloadManager(Builder builder) {
341343
trackSelector = builder.trackSelectorFactory.createTrackSelector(builder.context);
342344
BandwidthMeter bandwidthMeter = builder.bandwidthMeterSupplier.get();
343345
trackSelector.init(() -> {}, bandwidthMeter);
346+
Looper preloadLooper = preloadLooperProvider.obtainLooper();
344347
preloadMediaSourceFactory =
345348
new PreloadMediaSource.Factory(
346349
builder.mediaSourceFactorySupplier.get(),
@@ -349,7 +352,8 @@ private DefaultPreloadManager(Builder builder) {
349352
bandwidthMeter,
350353
rendererCapabilitiesList.getRendererCapabilities(),
351354
builder.loadControlSupplier.get().getAllocator(),
352-
preloadLooperProvider.obtainLooper());
355+
preloadLooper);
356+
preloadHandler = Util.createHandler(preloadLooper, /* callback= */ null);
353357
deprecatedConstructorCalled = false;
354358
}
355359

@@ -370,6 +374,7 @@ public DefaultPreloadManager(
370374
rendererCapabilitiesListFactory.createRendererCapabilitiesList();
371375
this.preloadLooperProvider = new PlaybackLooperProvider(preloadLooper);
372376
this.trackSelector = trackSelector;
377+
Looper obtainedPreloadLooper = preloadLooperProvider.obtainLooper();
373378
preloadMediaSourceFactory =
374379
new PreloadMediaSource.Factory(
375380
mediaSourceFactory,
@@ -378,7 +383,8 @@ public DefaultPreloadManager(
378383
bandwidthMeter,
379384
rendererCapabilitiesList.getRendererCapabilities(),
380385
allocator,
381-
preloadLooperProvider.obtainLooper());
386+
obtainedPreloadLooper);
387+
preloadHandler = Util.createHandler(obtainedPreloadLooper, /* callback= */ null);
382388
deprecatedConstructorCalled = true;
383389
}
384390

@@ -418,13 +424,16 @@ protected void releaseSourceInternal(MediaSource mediaSource) {
418424

419425
@Override
420426
protected void releaseInternal() {
421-
rendererCapabilitiesList.release();
422-
preloadLooperProvider.releaseLooper();
423-
if (!deprecatedConstructorCalled) {
424-
// TODO: Remove the property deprecatedConstructorCalled and release the TrackSelector anyway
425-
// after the deprecated constructor is removed.
426-
trackSelector.release();
427-
}
427+
preloadHandler.post(
428+
() -> {
429+
rendererCapabilitiesList.release();
430+
if (!deprecatedConstructorCalled) {
431+
// TODO: Remove the property deprecatedConstructorCalled and release the TrackSelector
432+
// anyway after the deprecated constructor is removed.
433+
trackSelector.release();
434+
}
435+
preloadLooperProvider.releaseLooper();
436+
});
428437
}
429438

430439
private static final class RankingDataComparator implements Comparator<Integer> {

libraries/exoplayer/src/test/java/androidx/media3/exoplayer/source/preload/DefaultPreloadManagerTest.java

+9-3
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,8 @@ protected void releaseSourceInternal() {
830830
}
831831

832832
@Test
833-
public void release_returnZeroCount_sourcesAndRendererCapabilitiesListReleased() {
833+
public void release_returnZeroCount_sourcesAndRendererCapabilitiesListReleased()
834+
throws Exception {
834835
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
835836
rankingData -> new DefaultPreloadManager.Status(STAGE_SOURCE_PREPARED);
836837
MediaSource.Factory mockMediaSourceFactory = mock(MediaSource.Factory.class);
@@ -853,11 +854,13 @@ public void release_returnZeroCount_sourcesAndRendererCapabilitiesListReleased()
853854
underlyingRenderers.add(fakeAudioRenderer);
854855
return underlyingRenderers.toArray(new Renderer[2]);
855856
};
857+
HandlerThread preloadThread = new HandlerThread("preload");
858+
preloadThread.start();
856859
DefaultPreloadManager preloadManager =
857860
new DefaultPreloadManager.Builder(context, targetPreloadStatusControl)
858861
.setMediaSourceFactory(mockMediaSourceFactory)
859862
.setRenderersFactory(renderersFactory)
860-
.setPreloadLooper(Util.getCurrentOrMainLooper())
863+
.setPreloadLooper(preloadThread.getLooper())
861864
.build();
862865
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
863866
MediaItem mediaItem1 =
@@ -885,16 +888,19 @@ protected void releaseSourceInternal() {
885888
preloadManager.add(mediaItem1, /* rankingData= */ 1);
886889
preloadManager.add(mediaItem2, /* rankingData= */ 2);
887890
preloadManager.invalidate();
891+
shadowOf(preloadThread.getLooper()).idle();
888892
shadowOf(Looper.getMainLooper()).idle();
889893

890894
preloadManager.release();
891-
shadowOf(Looper.getMainLooper()).idle();
895+
shadowOf(preloadThread.getLooper()).idle();
892896

893897
assertThat(preloadManager.getSourceCount()).isEqualTo(0);
894898
assertThat(internalSourceToReleaseReferenceByMediaId).containsExactly("mediaId1", "mediaId2");
895899
for (FakeRenderer renderer : underlyingRenderers) {
896900
assertThat(renderer.isReleased).isTrue();
897901
}
902+
903+
preloadThread.quit();
898904
}
899905

900906
private static class TestPreloadManagerListener implements BasePreloadManager.Listener {

0 commit comments

Comments
 (0)