mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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 2b54b1ebbeb6f13abb6944b7ff593c168c927192)
This commit is contained in:
parent
461a1fa037
commit
dba31108a3
@ -22,6 +22,7 @@ import static java.lang.Math.abs;
|
|||||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
@ -327,6 +328,7 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||||||
private final TrackSelector trackSelector;
|
private final TrackSelector trackSelector;
|
||||||
private final PlaybackLooperProvider preloadLooperProvider;
|
private final PlaybackLooperProvider preloadLooperProvider;
|
||||||
private final PreloadMediaSource.Factory preloadMediaSourceFactory;
|
private final PreloadMediaSource.Factory preloadMediaSourceFactory;
|
||||||
|
private final Handler preloadHandler;
|
||||||
private final boolean deprecatedConstructorCalled;
|
private final boolean deprecatedConstructorCalled;
|
||||||
|
|
||||||
private DefaultPreloadManager(Builder builder) {
|
private DefaultPreloadManager(Builder builder) {
|
||||||
@ -341,6 +343,7 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||||||
trackSelector = builder.trackSelectorFactory.createTrackSelector(builder.context);
|
trackSelector = builder.trackSelectorFactory.createTrackSelector(builder.context);
|
||||||
BandwidthMeter bandwidthMeter = builder.bandwidthMeterSupplier.get();
|
BandwidthMeter bandwidthMeter = builder.bandwidthMeterSupplier.get();
|
||||||
trackSelector.init(() -> {}, bandwidthMeter);
|
trackSelector.init(() -> {}, bandwidthMeter);
|
||||||
|
Looper preloadLooper = preloadLooperProvider.obtainLooper();
|
||||||
preloadMediaSourceFactory =
|
preloadMediaSourceFactory =
|
||||||
new PreloadMediaSource.Factory(
|
new PreloadMediaSource.Factory(
|
||||||
builder.mediaSourceFactorySupplier.get(),
|
builder.mediaSourceFactorySupplier.get(),
|
||||||
@ -349,7 +352,8 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||||||
bandwidthMeter,
|
bandwidthMeter,
|
||||||
rendererCapabilitiesList.getRendererCapabilities(),
|
rendererCapabilitiesList.getRendererCapabilities(),
|
||||||
builder.loadControlSupplier.get().getAllocator(),
|
builder.loadControlSupplier.get().getAllocator(),
|
||||||
preloadLooperProvider.obtainLooper());
|
preloadLooper);
|
||||||
|
preloadHandler = Util.createHandler(preloadLooper, /* callback= */ null);
|
||||||
deprecatedConstructorCalled = false;
|
deprecatedConstructorCalled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,6 +374,7 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||||||
rendererCapabilitiesListFactory.createRendererCapabilitiesList();
|
rendererCapabilitiesListFactory.createRendererCapabilitiesList();
|
||||||
this.preloadLooperProvider = new PlaybackLooperProvider(preloadLooper);
|
this.preloadLooperProvider = new PlaybackLooperProvider(preloadLooper);
|
||||||
this.trackSelector = trackSelector;
|
this.trackSelector = trackSelector;
|
||||||
|
Looper obtainedPreloadLooper = preloadLooperProvider.obtainLooper();
|
||||||
preloadMediaSourceFactory =
|
preloadMediaSourceFactory =
|
||||||
new PreloadMediaSource.Factory(
|
new PreloadMediaSource.Factory(
|
||||||
mediaSourceFactory,
|
mediaSourceFactory,
|
||||||
@ -378,7 +383,8 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||||||
bandwidthMeter,
|
bandwidthMeter,
|
||||||
rendererCapabilitiesList.getRendererCapabilities(),
|
rendererCapabilitiesList.getRendererCapabilities(),
|
||||||
allocator,
|
allocator,
|
||||||
preloadLooperProvider.obtainLooper());
|
obtainedPreloadLooper);
|
||||||
|
preloadHandler = Util.createHandler(obtainedPreloadLooper, /* callback= */ null);
|
||||||
deprecatedConstructorCalled = true;
|
deprecatedConstructorCalled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,13 +424,16 @@ public final class DefaultPreloadManager extends BasePreloadManager<Integer> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void releaseInternal() {
|
protected void releaseInternal() {
|
||||||
|
preloadHandler.post(
|
||||||
|
() -> {
|
||||||
rendererCapabilitiesList.release();
|
rendererCapabilitiesList.release();
|
||||||
preloadLooperProvider.releaseLooper();
|
|
||||||
if (!deprecatedConstructorCalled) {
|
if (!deprecatedConstructorCalled) {
|
||||||
// TODO: Remove the property deprecatedConstructorCalled and release the TrackSelector anyway
|
// TODO: Remove the property deprecatedConstructorCalled and release the TrackSelector
|
||||||
// after the deprecated constructor is removed.
|
// anyway after the deprecated constructor is removed.
|
||||||
trackSelector.release();
|
trackSelector.release();
|
||||||
}
|
}
|
||||||
|
preloadLooperProvider.releaseLooper();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class RankingDataComparator implements Comparator<Integer> {
|
private static final class RankingDataComparator implements Comparator<Integer> {
|
||||||
|
@ -830,7 +830,8 @@ public class DefaultPreloadManagerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void release_returnZeroCount_sourcesAndRendererCapabilitiesListReleased() {
|
public void release_returnZeroCount_sourcesAndRendererCapabilitiesListReleased()
|
||||||
|
throws Exception {
|
||||||
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
TargetPreloadStatusControl<Integer> targetPreloadStatusControl =
|
||||||
rankingData -> new DefaultPreloadManager.Status(STAGE_SOURCE_PREPARED);
|
rankingData -> new DefaultPreloadManager.Status(STAGE_SOURCE_PREPARED);
|
||||||
MediaSource.Factory mockMediaSourceFactory = mock(MediaSource.Factory.class);
|
MediaSource.Factory mockMediaSourceFactory = mock(MediaSource.Factory.class);
|
||||||
@ -853,11 +854,13 @@ public class DefaultPreloadManagerTest {
|
|||||||
underlyingRenderers.add(fakeAudioRenderer);
|
underlyingRenderers.add(fakeAudioRenderer);
|
||||||
return underlyingRenderers.toArray(new Renderer[2]);
|
return underlyingRenderers.toArray(new Renderer[2]);
|
||||||
};
|
};
|
||||||
|
HandlerThread preloadThread = new HandlerThread("preload");
|
||||||
|
preloadThread.start();
|
||||||
DefaultPreloadManager preloadManager =
|
DefaultPreloadManager preloadManager =
|
||||||
new DefaultPreloadManager.Builder(context, targetPreloadStatusControl)
|
new DefaultPreloadManager.Builder(context, targetPreloadStatusControl)
|
||||||
.setMediaSourceFactory(mockMediaSourceFactory)
|
.setMediaSourceFactory(mockMediaSourceFactory)
|
||||||
.setRenderersFactory(renderersFactory)
|
.setRenderersFactory(renderersFactory)
|
||||||
.setPreloadLooper(Util.getCurrentOrMainLooper())
|
.setPreloadLooper(preloadThread.getLooper())
|
||||||
.build();
|
.build();
|
||||||
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
MediaItem.Builder mediaItemBuilder = new MediaItem.Builder();
|
||||||
MediaItem mediaItem1 =
|
MediaItem mediaItem1 =
|
||||||
@ -885,16 +888,19 @@ public class DefaultPreloadManagerTest {
|
|||||||
preloadManager.add(mediaItem1, /* rankingData= */ 1);
|
preloadManager.add(mediaItem1, /* rankingData= */ 1);
|
||||||
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
preloadManager.add(mediaItem2, /* rankingData= */ 2);
|
||||||
preloadManager.invalidate();
|
preloadManager.invalidate();
|
||||||
|
shadowOf(preloadThread.getLooper()).idle();
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(Looper.getMainLooper()).idle();
|
||||||
|
|
||||||
preloadManager.release();
|
preloadManager.release();
|
||||||
shadowOf(Looper.getMainLooper()).idle();
|
shadowOf(preloadThread.getLooper()).idle();
|
||||||
|
|
||||||
assertThat(preloadManager.getSourceCount()).isEqualTo(0);
|
assertThat(preloadManager.getSourceCount()).isEqualTo(0);
|
||||||
assertThat(internalSourceToReleaseReferenceByMediaId).containsExactly("mediaId1", "mediaId2");
|
assertThat(internalSourceToReleaseReferenceByMediaId).containsExactly("mediaId1", "mediaId2");
|
||||||
for (FakeRenderer renderer : underlyingRenderers) {
|
for (FakeRenderer renderer : underlyingRenderers) {
|
||||||
assertThat(renderer.isReleased).isTrue();
|
assertThat(renderer.isReleased).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preloadThread.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TestPreloadManagerListener implements BasePreloadManager.Listener {
|
private static class TestPreloadManagerListener implements BasePreloadManager.Listener {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user