From 872d8f078b5781673597b909e29536ebb1551e9f Mon Sep 17 00:00:00 2001 From: bachinger Date: Fri, 14 Jun 2024 06:40:19 -0700 Subject: [PATCH] Rollback of https://github.com/androidx/media/commit/cd9b914c423900edcfe1e64bfb775a601bc23844 PiperOrigin-RevId: 643329324 --- .../androidx/media3/exoplayer/ExoPlayer.java | 38 +++++++++ .../media3/exoplayer/ExoPlayerImpl.java | 26 +----- .../exoplayer/ExoPlayerImplInternal.java | 12 +-- .../media3/exoplayer/MediaPeriodQueue.java | 7 +- .../media3/exoplayer/SimpleExoPlayer.java | 12 +++ .../media3/exoplayer/ExoPlayerTest.java | 85 ++++++++++++++++++- .../exoplayer/MediaPeriodQueueTest.java | 2 +- .../test/utils/ExoPlayerTestRunner.java | 10 +++ .../media3/test/utils/StubExoPlayer.java | 10 +++ .../test/utils/TestExoPlayerBuilder.java | 16 ++++ 10 files changed, 180 insertions(+), 38 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java index ce12d42a93..08428368ef 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayer.java @@ -437,6 +437,31 @@ public interface ExoPlayer extends Player { default void onOffloadedPlayback(boolean isOffloadedPlayback) {} } + /** Configuration options for preloading playlist items. */ + @UnstableApi + class PreloadConfiguration { + + /** Default preload configuration that disables playlist preloading. */ + public static final PreloadConfiguration DEFAULT = + new PreloadConfiguration(/* targetPreloadDurationUs= */ C.TIME_UNSET); + + /** + * The target duration to buffer when preloading, in microseconds or {@link C#TIME_UNSET} to + * disable preloading. + */ + public final long targetPreloadDurationUs; + + /** + * Creates an instance. + * + * @param targetPreloadDurationUs The target duration to preload, in microseconds or {@link + * C#TIME_UNSET} to disable preloading. + */ + public PreloadConfiguration(long targetPreloadDurationUs) { + this.targetPreloadDurationUs = targetPreloadDurationUs; + } + } + /** * A builder for {@link ExoPlayer} instances. * @@ -1556,6 +1581,19 @@ public interface ExoPlayer extends Player { @UnstableApi void setShuffleOrder(ShuffleOrder shuffleOrder); + /** + * Sets the {@linkplain PreloadConfiguration preload configuration} to configure playlist + * preloading. + * + * @param preloadConfiguration The preload configuration. + */ + @UnstableApi + void setPreloadConfiguration(PreloadConfiguration preloadConfiguration); + + /** Returns the {@linkplain PreloadConfiguration preload configuration}. */ + @UnstableApi + PreloadConfiguration getPreloadConfiguration(); + /** * {@inheritDoc} * diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 6edf593406..edcde1d4cc 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -141,30 +141,6 @@ import java.util.concurrent.TimeoutException; MediaLibraryInfo.registerModule("media3.exoplayer"); } - /** Configuration options for preloading playlist items. */ - public static final class PreloadConfiguration { - - /** Default preload configuration that disables playlist preloading. */ - public static final PreloadConfiguration DEFAULT = - new PreloadConfiguration(/* targetPreloadDurationUs= */ C.TIME_UNSET); - - /** - * The target duration to buffer when preloading, in microseconds or {@link C#TIME_UNSET} to - * disable preloading. - */ - public final long targetPreloadDurationUs; - - /** - * Creates an instance. - * - * @param targetPreloadDurationUs The target duration to preload, in microseconds or {@link - * C#TIME_UNSET} to disable preloading. - */ - public PreloadConfiguration(long targetPreloadDurationUs) { - this.targetPreloadDurationUs = targetPreloadDurationUs; - } - } - private static final String TAG = "ExoPlayerImpl"; /** @@ -909,6 +885,7 @@ import java.util.concurrent.TimeoutException; return shuffleModeEnabled; } + @Override public void setPreloadConfiguration(PreloadConfiguration preloadConfiguration) { verifyApplicationThread(); if (this.preloadConfiguration.equals(preloadConfiguration)) { @@ -918,6 +895,7 @@ import java.util.concurrent.TimeoutException; internalPlayer.setPreloadConfiguration(preloadConfiguration); } + @Override public PreloadConfiguration getPreloadConfiguration() { return preloadConfiguration; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index 01ce88e26f..3878ba2036 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -57,6 +57,7 @@ import androidx.media3.common.util.TraceUtil; import androidx.media3.common.util.Util; import androidx.media3.datasource.DataSourceException; import androidx.media3.exoplayer.DefaultMediaClock.PlaybackParametersListener; +import androidx.media3.exoplayer.ExoPlayer.PreloadConfiguration; import androidx.media3.exoplayer.analytics.AnalyticsCollector; import androidx.media3.exoplayer.analytics.PlayerId; import androidx.media3.exoplayer.drm.DrmSession; @@ -241,7 +242,7 @@ import java.util.concurrent.atomic.AtomicBoolean; @Nullable private ExoPlaybackException pendingRecoverableRendererError; private long setForegroundModeTimeoutMs; private long playbackMaybeBecameStuckAtMs; - private ExoPlayerImpl.PreloadConfiguration preloadConfiguration; + private PreloadConfiguration preloadConfiguration; private Timeline lastPreloadPoolInvalidationTimeline; public ExoPlayerImplInternal( @@ -263,7 +264,7 @@ import java.util.concurrent.atomic.AtomicBoolean; PlaybackInfoUpdateListener playbackInfoUpdateListener, PlayerId playerId, Looper playbackLooper, - ExoPlayerImpl.PreloadConfiguration preloadConfiguration) { + PreloadConfiguration preloadConfiguration) { this.playbackInfoUpdateListener = playbackInfoUpdateListener; this.renderers = renderers; this.trackSelector = trackSelector; @@ -372,7 +373,7 @@ import java.util.concurrent.atomic.AtomicBoolean; handler.obtainMessage(MSG_SET_SHUFFLE_ENABLED, shuffleModeEnabled ? 1 : 0, 0).sendToTarget(); } - public void setPreloadConfiguration(ExoPlayerImpl.PreloadConfiguration preloadConfiguration) { + public void setPreloadConfiguration(PreloadConfiguration preloadConfiguration) { handler.obtainMessage(MSG_SET_PRELOAD_CONFIGURATION, preloadConfiguration).sendToTarget(); } @@ -561,7 +562,7 @@ import java.util.concurrent.atomic.AtomicBoolean; setShuffleModeEnabledInternal(msg.arg1 != 0); break; case MSG_SET_PRELOAD_CONFIGURATION: - setPreloadConfigurationInternal((ExoPlayerImpl.PreloadConfiguration) msg.obj); + setPreloadConfigurationInternal((PreloadConfiguration) msg.obj); break; case MSG_DO_SOME_WORK: doSomeWork(); @@ -950,8 +951,7 @@ import java.util.concurrent.atomic.AtomicBoolean; handleLoadingMediaPeriodChanged(/* loadingTrackSelectionChanged= */ false); } - private void setPreloadConfigurationInternal( - ExoPlayerImpl.PreloadConfiguration preloadConfiguration) { + private void setPreloadConfigurationInternal(PreloadConfiguration preloadConfiguration) { this.preloadConfiguration = preloadConfiguration; queue.updatePreloadConfiguration(playbackInfo.timeline, preloadConfiguration); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java index 5554b09c88..35c3637963 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodQueue.java @@ -28,6 +28,7 @@ import androidx.media3.common.Player.RepeatMode; import androidx.media3.common.Timeline; import androidx.media3.common.util.Assertions; import androidx.media3.common.util.HandlerWrapper; +import androidx.media3.exoplayer.ExoPlayer.PreloadConfiguration; import androidx.media3.exoplayer.analytics.AnalyticsCollector; import androidx.media3.exoplayer.source.MediaPeriod; import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId; @@ -84,7 +85,7 @@ import java.util.List; private int length; @Nullable private Object oldFrontPeriodUid; private long oldFrontPeriodWindowSequenceNumber; - private ExoPlayerImpl.PreloadConfiguration preloadConfiguration; + private PreloadConfiguration preloadConfiguration; private List preloadPriorityList; /** @@ -99,7 +100,7 @@ import java.util.List; AnalyticsCollector analyticsCollector, HandlerWrapper analyticsCollectorHandler, MediaPeriodHolder.Factory mediaPeriodHolderFactory, - ExoPlayerImpl.PreloadConfiguration preloadConfiguration) { + PreloadConfiguration preloadConfiguration) { this.analyticsCollector = analyticsCollector; this.analyticsCollectorHandler = analyticsCollectorHandler; this.mediaPeriodHolderFactory = mediaPeriodHolderFactory; @@ -142,7 +143,7 @@ import java.util.List; * @param preloadConfiguration The new preload configuration. */ public void updatePreloadConfiguration( - Timeline timeline, ExoPlayerImpl.PreloadConfiguration preloadConfiguration) { + Timeline timeline, PreloadConfiguration preloadConfiguration) { this.preloadConfiguration = preloadConfiguration; invalidatePreloadPool(timeline); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java index 231ba4af43..0c38eccc6f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/SimpleExoPlayer.java @@ -992,6 +992,18 @@ public class SimpleExoPlayer extends BasePlayer player.setRepeatMode(repeatMode); } + @Override + public void setPreloadConfiguration(PreloadConfiguration preloadConfiguration) { + blockUntilConstructorFinished(); + player.setPreloadConfiguration(preloadConfiguration); + } + + @Override + public PreloadConfiguration getPreloadConfiguration() { + blockUntilConstructorFinished(); + return player.getPreloadConfiguration(); + } + @Override public void setShuffleModeEnabled(boolean shuffleModeEnabled) { blockUntilConstructorFinished(); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index 505650ba62..8ce6082f24 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -274,14 +274,16 @@ public class ExoPlayerTest { } private TestExoPlayerBuilder parameterizeTestExoPlayerBuilder(TestExoPlayerBuilder builder) { - // TODO: set PreloadConfiguration when enabled again after the release. - return builder; + return builder.setPreloadConfiguration(createPreloadConfiguration()); } private ExoPlayerTestRunner.Builder parameterizeExoPlayerTestRunnerBuilder( ExoPlayerTestRunner.Builder builder) { - // TODO: set PreloadConfiguration when enabled again after the release. - return builder; + return builder.setPreloadConfiguration(createPreloadConfiguration()); + } + + private ExoPlayer.PreloadConfiguration createPreloadConfiguration() { + return new ExoPlayer.PreloadConfiguration(getTargetPreloadDurationUs()); } /** @@ -6806,6 +6808,80 @@ public class ExoPlayerTest { Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE /* source prepared */); } + @Test + public void prepare_preloadingEnabled_nextWindowPeriodCreatedForPreloading() throws Exception { + FakeMediaSource mediaSource1 = + new FakeMediaSource( + new FakeTimeline( + new TimelineWindowDefinition( + /* isSeekable= */ true, + /* isDynamic= */ false, + /* durationUs= */ DefaultLoadControl.DEFAULT_MAX_BUFFER_MS * 2))); + List createdMediaPeriodIds = new ArrayList<>(); + FakeMediaSource mediaSource2 = + new FakeMediaSource() { + @Override + public MediaPeriod createPeriod( + MediaPeriodId id, Allocator allocator, long startPositionUs) { + createdMediaPeriodIds.add(id); + return super.createPeriod(id, allocator, startPositionUs); + } + }; + ExoPlayer player = + // Intentionally not using `parameterizeTestExoPlayerBuilder()` for preload specific test. + new TestExoPlayerBuilder(context) + .setPreloadConfiguration( + new ExoPlayer.PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)) + .build(); + player.setMediaSources(ImmutableList.of(mediaSource1, mediaSource2)); + + player.prepare(); + run(player).untilPendingCommandsAreFullyHandled(); + + assertThat(createdMediaPeriodIds).hasSize(1); + play(player).untilState(Player.STATE_ENDED); + assertThat(createdMediaPeriodIds).hasSize(1); + player.release(); + } + + @Test + public void prepare_preloadingEnabledRepeatModeOne_sameWindowPeriodCreatedForPreloading() + throws Exception { + FakeTimeline timeline = + new FakeTimeline( + new TimelineWindowDefinition( + /* isSeekable= */ true, + /* isDynamic= */ false, + /* durationUs= */ DefaultLoadControl.DEFAULT_MAX_BUFFER_MS * 2)); + List createdMediaPeriodIds = new ArrayList<>(); + FakeMediaSource mediaSource = + new FakeMediaSource(timeline) { + @Override + public MediaPeriod createPeriod( + MediaPeriodId id, Allocator allocator, long startPositionUs) { + createdMediaPeriodIds.add(id); + return super.createPeriod(id, allocator, startPositionUs); + } + }; + ExoPlayer player = + // Intentionally not using `parameterizeTestExoPlayerBuilder()` for preload specific test. + new TestExoPlayerBuilder(context) + .setPreloadConfiguration( + new ExoPlayer.PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)) + .build(); + player.setRepeatMode(Player.REPEAT_MODE_ONE); + player.setMediaSource(mediaSource); + + player.prepare(); + run(player).untilPendingCommandsAreFullyHandled(); + + assertThat(createdMediaPeriodIds).hasSize(2); + player.setRepeatMode(Player.REPEAT_MODE_OFF); + play(player).untilState(Player.STATE_ENDED); + assertThat(createdMediaPeriodIds).hasSize(2); + player.release(); + } + @Test public void seekToIndexLargerThanNumberOfPlaylistItems() throws Exception { Timeline fakeTimeline = @@ -14507,6 +14583,7 @@ public class ExoPlayerTest { new DefaultRenderersFactory(context).setAllowedVideoJoiningTimeMs(0)) .setClock(new FakeClock(/* isAutoAdvancing= */ true)) .build(); + player.setPreloadConfiguration(createPreloadConfiguration()); player.setPauseAtEndOfMediaItems(true); Surface surface = new Surface(new SurfaceTexture(/* texName= */ 0)); player.setVideoSurface(surface); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java index f87bf94642..b9232ee1f0 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/MediaPeriodQueueTest.java @@ -40,7 +40,7 @@ import androidx.media3.common.Timeline; import androidx.media3.common.Tracks; import androidx.media3.common.util.Clock; import androidx.media3.common.util.HandlerWrapper; -import androidx.media3.exoplayer.ExoPlayerImpl.PreloadConfiguration; +import androidx.media3.exoplayer.ExoPlayer.PreloadConfiguration; import androidx.media3.exoplayer.analytics.AnalyticsCollector; import androidx.media3.exoplayer.analytics.DefaultAnalyticsCollector; import androidx.media3.exoplayer.analytics.PlayerId; diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/ExoPlayerTestRunner.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/ExoPlayerTestRunner.java index 0daf21bf59..808268ff34 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/ExoPlayerTestRunner.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/ExoPlayerTestRunner.java @@ -284,6 +284,16 @@ public final class ExoPlayerTestRunner implements Player.Listener, ActionSchedul return this; } + /** + * @see ExoPlayer#setPreloadConfiguration(ExoPlayer.PreloadConfiguration) + * @return This builder. + */ + @CanIgnoreReturnValue + public Builder setPreloadConfiguration(ExoPlayer.PreloadConfiguration preloadConfiguration) { + testPlayerBuilder.setPreloadConfiguration(preloadConfiguration); + return this; + } + /** * Sets an {@link ActionSchedule} to be run by the test runner. The first action will be * executed immediately before {@link ExoPlayer#prepare()}. diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java index f21d7283f2..8a13d08712 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/StubExoPlayer.java @@ -154,6 +154,16 @@ public class StubExoPlayer extends StubPlayer implements ExoPlayer { throw new UnsupportedOperationException(); } + @Override + public void setPreloadConfiguration(PreloadConfiguration preloadConfiguration) { + throw new UnsupportedOperationException(); + } + + @Override + public PreloadConfiguration getPreloadConfiguration() { + throw new UnsupportedOperationException(); + } + @Override public void setMediaSource(MediaSource mediaSource) { throw new UnsupportedOperationException(); diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java index 378a01e81e..73bdb26ea4 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/TestExoPlayerBuilder.java @@ -56,6 +56,7 @@ public class TestExoPlayerBuilder { private long seekForwardIncrementMs; private boolean deviceVolumeControlEnabled; private boolean suppressPlaybackWhenUnsuitableOutput; + @Nullable private ExoPlayer.PreloadConfiguration preloadConfiguration; private boolean dynamicSchedulingEnabled; public TestExoPlayerBuilder(Context context) { @@ -161,6 +162,18 @@ public class TestExoPlayerBuilder { return this; } + /** + * Sets the preload configuration. + * + * @see ExoPlayer#setPreloadConfiguration(ExoPlayer.PreloadConfiguration) + */ + @CanIgnoreReturnValue + public TestExoPlayerBuilder setPreloadConfiguration( + ExoPlayer.PreloadConfiguration preloadConfiguration) { + this.preloadConfiguration = preloadConfiguration; + return this; + } + /** * Returns the {@link Renderer Renderers} that have been set with {@link #setRenderers} or null if * no {@link Renderer Renderers} have been explicitly set. Note that these renderers may not be @@ -373,6 +386,9 @@ public class TestExoPlayerBuilder { builder.setMediaSourceFactory(mediaSourceFactory); } ExoPlayer exoPlayer = builder.build(); + if (preloadConfiguration != null) { + exoPlayer.setPreloadConfiguration(preloadConfiguration); + } return exoPlayer; } }