diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 2de117e9d7..2f8d0045d3 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -394,8 +394,7 @@ public class PlayerActivity extends AppCompatActivity if (haveStartPosition) { player.seekTo(startWindow, startPosition); } - player.setMediaItem(mediaSource); - player.prepare(); + player.prepare(mediaSource, !haveStartPosition, false); updateButtonVisibility(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index 99089a2afc..7c8a454191 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -331,51 +331,26 @@ public interface ExoPlayer extends Player { */ void retry(); - /** Prepares the player. */ - void prepare(); - /** - * @deprecated Use {@code setMediaItem(mediaSource, C.TIME_UNSET)} and {@link #prepare()} instead. + * Prepares the player to play the provided {@link MediaSource}. Equivalent to {@code + * prepare(mediaSource, true, true)}. */ - @Deprecated void prepare(MediaSource mediaSource); - /** @deprecated Use {@link #setMediaItem(MediaSource, long)} and {@link #prepare()} instead. */ - @Deprecated + /** + * Prepares the player to play the provided {@link MediaSource}, optionally resetting the playback + * position the default position in the first {@link Timeline.Window}. + * + * @param mediaSource The {@link MediaSource} to play. + * @param resetPosition Whether the playback position should be reset to the default position in + * the first {@link Timeline.Window}. If false, playback will start from the position defined + * by {@link #getCurrentWindowIndex()} and {@link #getCurrentPosition()}. + * @param resetState Whether the timeline, manifest, tracks and track selections should be reset. + * Should be true unless the player is being prepared to play the same media as it was playing + * previously (e.g. if playback failed and is being retried). + */ void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState); - /** - * Sets the specified {@link MediaSource}. - * - *

Note: This is an intermediate implementation towards a larger change. Until then {@link - * #prepare()} has to be called immediately after calling this method. - * - * @param mediaItem The new {@link MediaSource}. - */ - void setMediaItem(MediaSource mediaItem); - - /** - * Sets the specified {@link MediaSource}. - * - *

Note: This is an intermediate implementation towards a larger change. Until then {@link - * #prepare()} has to be called immediately after calling this method. - * - *

This intermediate implementation calls {@code stop(true)} before seeking to avoid seeking in - * a media item that has been set previously. It is equivalent with calling - * - *


-   *   if (!getCurrentTimeline().isEmpty()) {
-   *     player.stop(true);
-   *   }
-   *   player.seekTo(0, startPositionMs);
-   *   player.setMediaItem(mediaItem);
-   * 
- * - * @param mediaItem The new {@link MediaSource}. - * @param startPositionMs The position in milliseconds to start playback from. - */ - void setMediaItem(MediaSource mediaItem, long startPositionMs); - /** * Creates a message that can be sent to a {@link PlayerMessage.Target}. By default, the message * will be delivered immediately without blocking on the playback thread. The default {@link diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 97658d2906..dd8fbee53c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -62,7 +62,7 @@ import java.util.concurrent.CopyOnWriteArrayList; private final Timeline.Period period; private final ArrayDeque pendingListenerNotifications; - @Nullable private MediaSource mediaSource; + private MediaSource mediaSource; private boolean playWhenReady; @PlaybackSuppressionReason private int playbackSuppressionReason; @RepeatMode private int repeatMode; @@ -219,38 +219,34 @@ import java.util.concurrent.CopyOnWriteArrayList; } @Override - @Deprecated public void prepare(MediaSource mediaSource) { - setMediaItem(mediaSource); - prepareInternal(/* resetPosition= */ true, /* resetState= */ true); + prepare(mediaSource, /* resetPosition= */ true, /* resetState= */ true); } @Override - @Deprecated public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { - setMediaItem(mediaSource); - prepareInternal(resetPosition, resetState); + this.mediaSource = mediaSource; + PlaybackInfo playbackInfo = + getResetPlaybackInfo( + resetPosition, + resetState, + /* resetError= */ true, + /* playbackState= */ Player.STATE_BUFFERING); + // Trigger internal prepare first before updating the playback info and notifying external + // listeners to ensure that new operations issued in the listener notifications reach the + // player after this prepare. The internal player can't change the playback info immediately + // because it uses a callback. + hasPendingPrepare = true; + pendingOperationAcks++; + internalPlayer.prepare(mediaSource, resetPosition, resetState); + updatePlaybackInfo( + playbackInfo, + /* positionDiscontinuity= */ false, + /* ignored */ DISCONTINUITY_REASON_INTERNAL, + TIMELINE_CHANGE_REASON_RESET, + /* seekProcessed= */ false); } - @Override - public void prepare() { - Assertions.checkNotNull(mediaSource); - prepareInternal(/* resetPosition= */ false, /* resetState= */ true); - } - - @Override - public void setMediaItem(MediaSource mediaItem, long startPositionMs) { - if (!getCurrentTimeline().isEmpty()) { - stop(/* reset= */ true); - } - seekTo(/* windowIndex= */ 0, startPositionMs); - setMediaItem(mediaItem); - } - - @Override - public void setMediaItem(MediaSource mediaItem) { - mediaSource = mediaItem; - } @Override public void setPlayWhenReady(boolean playWhenReady) { @@ -610,29 +606,6 @@ import java.util.concurrent.CopyOnWriteArrayList; } } - /* package */ void prepareInternal(boolean resetPosition, boolean resetState) { - Assertions.checkNotNull(mediaSource); - PlaybackInfo playbackInfo = - getResetPlaybackInfo( - resetPosition, - resetState, - /* resetError= */ true, - /* playbackState= */ Player.STATE_BUFFERING); - // Trigger internal prepare first before updating the playback info and notifying external - // listeners to ensure that new operations issued in the listener notifications reach the - // player after this prepare. The internal player can't change the playback info immediately - // because it uses a callback. - hasPendingPrepare = true; - pendingOperationAcks++; - internalPlayer.prepare(mediaSource, resetPosition, resetState); - updatePlaybackInfo( - playbackInfo, - /* positionDiscontinuity= */ false, - /* ignored */ DISCONTINUITY_REASON_INTERNAL, - TIMELINE_CHANGE_REASON_RESET, - /* seekProcessed= */ false); - } - private void handlePlaybackParameters( PlaybackParameters playbackParameters, boolean operationAck) { if (operationAck) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index f1d01a114f..729dd150ae 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -1162,7 +1162,6 @@ public class SimpleExoPlayer extends BasePlayer } @Override - @SuppressWarnings("deprecation") public void retry() { verifyApplicationThread(); if (mediaSource != null @@ -1172,38 +1171,23 @@ public class SimpleExoPlayer extends BasePlayer } @Override - @Deprecated - @SuppressWarnings("deprecation") public void prepare(MediaSource mediaSource) { prepare(mediaSource, /* resetPosition= */ true, /* resetState= */ true); } @Override - @Deprecated public void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState) { verifyApplicationThread(); - setMediaItem(mediaSource); - prepareInternal(resetPosition, resetState); - } - - @Override - public void prepare() { - verifyApplicationThread(); - prepareInternal(/* resetPosition= */ false, /* resetState= */ true); - } - - @Override - public void setMediaItem(MediaSource mediaItem, long startPositionMs) { - verifyApplicationThread(); - setMediaItemInternal(mediaItem); - player.setMediaItem(mediaItem, startPositionMs); - } - - @Override - public void setMediaItem(MediaSource mediaItem) { - verifyApplicationThread(); - setMediaItemInternal(mediaItem); - player.setMediaItem(mediaItem); + if (this.mediaSource != null) { + this.mediaSource.removeEventListener(analyticsCollector); + analyticsCollector.resetForNewMediaSource(); + } + this.mediaSource = mediaSource; + mediaSource.addEventListener(eventHandler, analyticsCollector); + @AudioFocusManager.PlayerCommand + int playerCommand = audioFocusManager.handlePrepare(getPlayWhenReady()); + updatePlayWhenReady(getPlayWhenReady(), playerCommand); + player.prepare(mediaSource, resetPosition, resetState); } @Override @@ -1448,23 +1432,6 @@ public class SimpleExoPlayer extends BasePlayer // Internal methods. - private void prepareInternal(boolean resetPosition, boolean resetState) { - Assertions.checkNotNull(mediaSource); - @AudioFocusManager.PlayerCommand - int playerCommand = audioFocusManager.handlePrepare(getPlayWhenReady()); - updatePlayWhenReady(getPlayWhenReady(), playerCommand); - player.prepareInternal(resetPosition, resetState); - } - - private void setMediaItemInternal(MediaSource mediaItem) { - if (mediaSource != null) { - mediaSource.removeEventListener(analyticsCollector); - analyticsCollector.resetForNewMediaSource(); - } - mediaSource = mediaItem; - mediaSource.addEventListener(eventHandler, analyticsCollector); - } - private void removeSurfaceCallbacks() { if (textureView != null) { if (textureView.getSurfaceTextureListener() != componentListener) { diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 8bd6b1ba09..8ec8cca06c 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -16,7 +16,6 @@ package com.google.android.exoplayer2; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.fail; import static org.robolectric.Shadows.shadowOf; @@ -36,10 +35,7 @@ import com.google.android.exoplayer2.Timeline.Window; import com.google.android.exoplayer2.analytics.AnalyticsListener; import com.google.android.exoplayer2.audio.AudioAttributes; import com.google.android.exoplayer2.source.ClippingMediaSource; -import com.google.android.exoplayer2.source.CompositeMediaSource; import com.google.android.exoplayer2.source.ConcatenatingMediaSource; -import com.google.android.exoplayer2.source.LoopingMediaSource; -import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher; @@ -1588,7 +1584,6 @@ public final class ExoPlayerTest { AtomicInteger counter = new AtomicInteger(); ActionSchedule actionSchedule = new ActionSchedule.Builder("testSendMessagesFromStartPositionOnlyOnce") - .waitForTimelineChanged() .pause() .sendMessage( (messageType, payload) -> { @@ -2931,218 +2926,6 @@ public final class ExoPlayerTest { assertThat(seenPlaybackSuppression.get()).isFalse(); } - @Test - public void testDelegatingMediaSourceApproach() throws Exception { - Timeline fakeTimeline = - new FakeTimeline( - new TimelineWindowDefinition( - /* isSeekable= */ true, /* isDynamic= */ false, /* durationUs= */ 10_000)); - final ConcatenatingMediaSource underlyingSource = new ConcatenatingMediaSource(); - CompositeMediaSource delegatingMediaSource = - new CompositeMediaSource() { - @Override - public void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) { - super.prepareSourceInternal(mediaTransferListener); - underlyingSource.addMediaSource( - new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT)); - underlyingSource.addMediaSource( - new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT)); - prepareChildSource(null, underlyingSource); - } - - @Override - public MediaPeriod createPeriod( - MediaPeriodId id, Allocator allocator, long startPositionUs) { - return underlyingSource.createPeriod(id, allocator, startPositionUs); - } - - @Override - public void releasePeriod(MediaPeriod mediaPeriod) { - underlyingSource.releasePeriod(mediaPeriod); - } - - @Override - protected void onChildSourceInfoRefreshed( - Void id, MediaSource mediaSource, Timeline timeline) { - refreshSourceInfo(timeline); - } - }; - int[] currentWindowIndices = new int[1]; - long[] currentPlaybackPositions = new long[1]; - long[] windowCounts = new long[1]; - int seekToWindowIndex = 1; - ActionSchedule actionSchedule = - new ActionSchedule.Builder("testDelegatingMediaSourceApproach") - .seek(/* windowIndex= */ 1, /* positionMs= */ 5000) - .waitForSeekProcessed() - .executeRunnable( - new PlayerRunnable() { - @Override - public void run(SimpleExoPlayer player) { - currentWindowIndices[0] = player.getCurrentWindowIndex(); - currentPlaybackPositions[0] = player.getCurrentPosition(); - windowCounts[0] = player.getCurrentTimeline().getWindowCount(); - } - }) - .build(); - ExoPlayerTestRunner exoPlayerTestRunner = - new Builder() - .setMediaSource(delegatingMediaSource) - .setActionSchedule(actionSchedule) - .build(context) - .start() - .blockUntilActionScheduleFinished(TIMEOUT_MS) - .blockUntilEnded(TIMEOUT_MS); - exoPlayerTestRunner.assertTimelineChangeReasonsEqual(Player.TIMELINE_CHANGE_REASON_PREPARED); - assertArrayEquals(new long[] {2}, windowCounts); - assertArrayEquals(new int[] {seekToWindowIndex}, currentWindowIndices); - assertArrayEquals(new long[] {5_000}, currentPlaybackPositions); - } - - @Test - public void testSeekTo_windowIndexIsReset_deprecated() throws Exception { - FakeTimeline fakeTimeline = new FakeTimeline(/* windowCount= */ 1); - FakeMediaSource mediaSource = new FakeMediaSource(fakeTimeline); - LoopingMediaSource loopingMediaSource = new LoopingMediaSource(mediaSource, 2); - final int[] windowIndex = {C.INDEX_UNSET}; - final long[] positionMs = {C.TIME_UNSET}; - ActionSchedule actionSchedule = - new ActionSchedule.Builder("testSeekTo_windowIndexIsReset_deprecated") - .seek(/* windowIndex= */ 1, /* positionMs= */ C.TIME_UNSET) - .waitForSeekProcessed() - .playUntilPosition(/* windowIndex= */ 1, /* positionMs= */ 5000) - .executeRunnable( - new PlayerRunnable() { - @Override - public void run(SimpleExoPlayer player) { - //noinspection deprecation - player.prepare(mediaSource); - player.seekTo(/* positionMs= */ 5000); - } - }) - .executeRunnable( - new PlayerRunnable() { - @Override - public void run(SimpleExoPlayer player) { - windowIndex[0] = player.getCurrentWindowIndex(); - positionMs[0] = player.getCurrentPosition(); - } - }) - .build(); - new ExoPlayerTestRunner.Builder() - .setMediaSource(loopingMediaSource) - .setActionSchedule(actionSchedule) - .build(context) - .start() - .blockUntilActionScheduleFinished(TIMEOUT_MS); - - assertThat(windowIndex[0]).isEqualTo(0); - assertThat(positionMs[0]).isAtLeast(5000L); - } - - @Test - public void testSeekTo_windowIndexIsReset() throws Exception { - FakeTimeline fakeTimeline = new FakeTimeline(/* windowCount= */ 1); - FakeMediaSource mediaSource = new FakeMediaSource(fakeTimeline); - LoopingMediaSource loopingMediaSource = new LoopingMediaSource(mediaSource, 2); - final int[] windowIndex = {C.INDEX_UNSET}; - final long[] positionMs = {C.TIME_UNSET}; - ActionSchedule actionSchedule = - new ActionSchedule.Builder("testSeekTo_windowIndexIsReset") - .seek(/* windowIndex= */ 1, /* positionMs= */ C.TIME_UNSET) - .waitForSeekProcessed() - .playUntilPosition(/* windowIndex= */ 1, /* positionMs= */ 5000) - .executeRunnable( - new PlayerRunnable() { - @Override - public void run(SimpleExoPlayer player) { - player.setMediaItem(mediaSource, /* startPositionMs= */ 5000); - player.prepare(); - } - }) - .executeRunnable( - new PlayerRunnable() { - @Override - public void run(SimpleExoPlayer player) { - windowIndex[0] = player.getCurrentWindowIndex(); - positionMs[0] = player.getCurrentPosition(); - } - }) - .build(); - new ExoPlayerTestRunner.Builder() - .setMediaSource(loopingMediaSource) - .setActionSchedule(actionSchedule) - .build(context) - .start() - .blockUntilActionScheduleFinished(TIMEOUT_MS); - - assertThat(windowIndex[0]).isEqualTo(0); - assertThat(positionMs[0]).isAtLeast(5000L); - } - - @Test - public void becomingNoisyIgnoredIfBecomingNoisyHandlingIsDisabled() throws Exception { - CountDownLatch becomingNoisyHandlingDisabled = new CountDownLatch(1); - CountDownLatch becomingNoisyDelivered = new CountDownLatch(1); - PlayerStateGrabber playerStateGrabber = new PlayerStateGrabber(); - ActionSchedule actionSchedule = - new ActionSchedule.Builder("becomingNoisyIgnoredIfBecomingNoisyHandlingIsDisabled") - .executeRunnable( - new PlayerRunnable() { - @Override - public void run(SimpleExoPlayer player) { - player.setHandleAudioBecomingNoisy(false); - becomingNoisyHandlingDisabled.countDown(); - - // Wait for the broadcast to be delivered from the main thread. - try { - becomingNoisyDelivered.await(); - } catch (InterruptedException e) { - throw new IllegalStateException(e); - } - } - }) - .delay(1) // Handle pending messages on the playback thread. - .executeRunnable(playerStateGrabber) - .build(); - - ExoPlayerTestRunner testRunner = - new ExoPlayerTestRunner.Builder().setActionSchedule(actionSchedule).build(context).start(); - becomingNoisyHandlingDisabled.await(); - deliverBroadcast(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); - becomingNoisyDelivered.countDown(); - - testRunner.blockUntilActionScheduleFinished(TIMEOUT_MS).blockUntilEnded(TIMEOUT_MS); - assertThat(playerStateGrabber.playWhenReady).isTrue(); - } - - @Test - public void pausesWhenBecomingNoisyIfBecomingNoisyHandlingIsEnabled() throws Exception { - CountDownLatch becomingNoisyHandlingEnabled = new CountDownLatch(1); - ActionSchedule actionSchedule = - new ActionSchedule.Builder("pausesWhenBecomingNoisyIfBecomingNoisyHandlingIsEnabled") - .executeRunnable( - new PlayerRunnable() { - @Override - public void run(SimpleExoPlayer player) { - player.setHandleAudioBecomingNoisy(true); - becomingNoisyHandlingEnabled.countDown(); - } - }) - .waitForPlayWhenReady(false) // Becoming noisy should set playWhenReady = false - .play() - .build(); - - ExoPlayerTestRunner testRunner = - new ExoPlayerTestRunner.Builder().setActionSchedule(actionSchedule).build(context).start(); - becomingNoisyHandlingEnabled.await(); - deliverBroadcast(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); - - // If the player fails to handle becoming noisy, blockUntilActionScheduleFinished will time out - // and throw, causing the test to fail. - testRunner.blockUntilActionScheduleFinished(TIMEOUT_MS).blockUntilEnded(TIMEOUT_MS); - } - // Internal methods. private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) { diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java index bf3cc90a78..d64a44ac04 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java @@ -431,8 +431,7 @@ public final class ExoPlayerTestRunner implements Player.EventListener, ActionSc if (actionSchedule != null) { actionSchedule.start(player, trackSelector, null, handler, ExoPlayerTestRunner.this); } - player.setMediaItem(mediaSource); - player.prepare(); + player.prepare(mediaSource, /* resetPosition= */ false, /* resetState= */ false); } catch (Exception e) { handleException(e); } diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java index 47f34712b9..18eaec2cd7 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java @@ -96,11 +96,6 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } - @Override - public void prepare() { - throw new UnsupportedOperationException(); - } - @Override public void prepare(MediaSource mediaSource) { throw new UnsupportedOperationException(); @@ -111,16 +106,6 @@ public abstract class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } - @Override - public void setMediaItem(MediaSource mediaItem) { - throw new UnsupportedOperationException(); - } - - @Override - public void setMediaItem(MediaSource mediaItem, long startPositionMs) { - throw new UnsupportedOperationException(); - } - @Override public void setPlayWhenReady(boolean playWhenReady) { throw new UnsupportedOperationException();