Add PreloadMediaSource.clear to discard the preloading period

PiperOrigin-RevId: 633167081
This commit is contained in:
tianyifeng 2024-05-13 04:45:18 -07:00 committed by Copybara-Service
parent 71e36ac6d2
commit 0db23ae904
3 changed files with 101 additions and 2 deletions

View File

@ -26,6 +26,7 @@
source or take other actions.
* Fix bug where silence skipping at the end of items can trigger a
playback exception.
* Add `clear` to `PreloadMediaSource` to discard the preloading period.
* Transformer:
* Work around a decoder bug where the number of audio channels was capped
at stereo when handling PCM input.

View File

@ -259,6 +259,21 @@ public final class PreloadMediaSource extends WrappingMediaSource {
});
}
/**
* Clears the preloading {@link PreloadMediaPeriod} in {@link PreloadMediaSource}.
*
* <p>Can be called from any thread.
*/
public void clear() {
preloadHandler.post(
() -> {
if (preloadingMediaPeriodAndKey != null) {
mediaSource.releasePeriod(preloadingMediaPeriodAndKey.first.mediaPeriod);
preloadingMediaPeriodAndKey = null;
}
});
}
@Override
protected void prepareSourceInternal() {
if (isUsedByPlayer() && !onUsedByPlayerNotified) {
@ -337,8 +352,7 @@ public final class PreloadMediaSource extends WrappingMediaSource {
&& preloadMediaPeriod == checkNotNull(playingPreloadedMediaPeriodAndId).first) {
playingPreloadedMediaPeriodAndId = null;
}
MediaPeriod periodToRelease = preloadMediaPeriod.mediaPeriod;
mediaSource.releasePeriod(periodToRelease);
mediaSource.releasePeriod(preloadMediaPeriod.mediaPeriod);
}
@Override

View File

@ -29,6 +29,7 @@ import static org.robolectric.Shadows.shadowOf;
import android.net.Uri;
import android.os.Looper;
import android.util.Pair;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.Timeline;
@ -36,16 +37,21 @@ import androidx.media3.common.Tracks;
import androidx.media3.common.util.SystemClock;
import androidx.media3.common.util.Util;
import androidx.media3.datasource.DefaultDataSource;
import androidx.media3.datasource.TransferListener;
import androidx.media3.exoplayer.Renderer;
import androidx.media3.exoplayer.RendererCapabilities;
import androidx.media3.exoplayer.RendererConfiguration;
import androidx.media3.exoplayer.RenderersFactory;
import androidx.media3.exoplayer.analytics.PlayerId;
import androidx.media3.exoplayer.audio.AudioRendererEventListener;
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
import androidx.media3.exoplayer.drm.DrmSessionManager;
import androidx.media3.exoplayer.metadata.MetadataOutput;
import androidx.media3.exoplayer.source.MediaPeriod;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.source.MediaSourceEventListener;
import androidx.media3.exoplayer.source.ProgressiveMediaSource;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.text.TextOutput;
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
@ -57,6 +63,7 @@ import androidx.media3.exoplayer.upstream.DefaultAllocator;
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
import androidx.media3.exoplayer.video.VideoRendererEventListener;
import androidx.media3.test.utils.FakeAudioRenderer;
import androidx.media3.test.utils.FakeMediaPeriod;
import androidx.media3.test.utils.FakeMediaSource;
import androidx.media3.test.utils.FakeMediaSourceFactory;
import androidx.media3.test.utils.FakeTimeline;
@ -795,6 +802,83 @@ public final class PreloadMediaSourceTest {
verify(internalSourceReference.get(), times(2)).createPeriod(any(), any(), anyLong());
}
@Test
public void clear_preloadingPeriodReleased() throws Exception {
AtomicBoolean onPreparedCalled = new AtomicBoolean();
PreloadMediaSource.PreloadControl preloadControl =
new PreloadMediaSource.PreloadControl() {
@Override
public boolean onTimelineRefreshed(PreloadMediaSource mediaSource) {
return true;
}
@Override
public boolean onPrepared(PreloadMediaSource mediaSource) {
onPreparedCalled.set(true);
return false;
}
@Override
public boolean onContinueLoadingRequested(
PreloadMediaSource mediaSource, long bufferedPositionUs) {
return false;
}
@Override
public void onUsedByPlayer(PreloadMediaSource mediaSource) {}
};
MediaSource.Factory mockMediaSourceFactory = mock(MediaSource.Factory.class);
AtomicBoolean preloadingMediaPeriodReleased = new AtomicBoolean();
when(mockMediaSourceFactory.createMediaSource(any()))
.thenReturn(
new FakeMediaSource() {
@Override
protected MediaPeriod createMediaPeriod(
MediaPeriodId id,
TrackGroupArray trackGroupArray,
Allocator allocator,
MediaSourceEventListener.EventDispatcher mediaSourceEventDispatcher,
DrmSessionManager drmSessionManager,
DrmSessionEventListener.EventDispatcher drmEventDispatcher,
@Nullable TransferListener transferListener) {
return new FakeMediaPeriod(
trackGroupArray,
allocator,
FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
mediaSourceEventDispatcher) {
@Override
public void release() {
preloadingMediaPeriodReleased.set(true);
}
};
}
});
TrackSelector trackSelector =
new DefaultTrackSelector(ApplicationProvider.getApplicationContext());
trackSelector.init(() -> {}, bandwidthMeter);
PreloadMediaSource.Factory preloadMediaSourceFactory =
new PreloadMediaSource.Factory(
mockMediaSourceFactory,
preloadControl,
trackSelector,
bandwidthMeter,
getRendererCapabilities(renderersFactory),
allocator,
Util.getCurrentOrMainLooper());
PreloadMediaSource preloadMediaSource =
preloadMediaSourceFactory.createMediaSource(
new MediaItem.Builder()
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
.build());
preloadMediaSource.preload(/* startPositionUs= */ 0L);
runMainLooperUntil(onPreparedCalled::get);
preloadMediaSource.clear();
shadowOf(Looper.getMainLooper()).idle();
assertThat(preloadingMediaPeriodReleased.get()).isTrue();
}
@Test
public void releaseSourceByAllExternalCallers_preloadNotCalledBefore_releaseInternalSource() {
PreloadMediaSource.PreloadControl preloadControl =