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 27d5cc0ddc..5948f7959b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -1879,7 +1879,9 @@ import java.util.concurrent.atomic.AtomicBoolean; // The reselection did not change any prepared periods. return; } - newTrackSelectorResult = periodHolder.selectTracks(playbackSpeed, playbackInfo.timeline); + newTrackSelectorResult = + periodHolder.selectTracks( + playbackSpeed, playbackInfo.timeline, playbackInfo.playWhenReady); if (periodHolder == queue.getPlayingPeriod()) { newPlayingPeriodTrackSelectorResult = newTrackSelectorResult; } @@ -2563,7 +2565,9 @@ import java.util.concurrent.atomic.AtomicBoolean; if (preloadHolder != null) { checkState(!preloadHolder.prepared); preloadHolder.handlePrepared( - mediaClock.getPlaybackParameters().speed, playbackInfo.timeline); + mediaClock.getPlaybackParameters().speed, + playbackInfo.timeline, + playbackInfo.playWhenReady); if (queue.isPreloading(mediaPeriod)) { maybeContinuePreloading(); } @@ -2575,7 +2579,9 @@ import java.util.concurrent.atomic.AtomicBoolean; throws ExoPlaybackException { if (!loadingPeriodHolder.prepared) { loadingPeriodHolder.handlePrepared( - mediaClock.getPlaybackParameters().speed, playbackInfo.timeline); + mediaClock.getPlaybackParameters().speed, + playbackInfo.timeline, + playbackInfo.playWhenReady); } updateLoadControlTrackSelection( loadingPeriodHolder.info.id, diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodHolder.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodHolder.java index cb82d463be..79c9a74ded 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodHolder.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/MediaPeriodHolder.java @@ -203,12 +203,14 @@ import java.io.IOException; * * @param playbackSpeed The current factor by which playback is sped up. * @param timeline The current {@link Timeline}. + * @param playWhenReady The current value of whether playback should proceed when ready. * @throws ExoPlaybackException If an error occurs during track selection. */ - public void handlePrepared(float playbackSpeed, Timeline timeline) throws ExoPlaybackException { + public void handlePrepared(float playbackSpeed, Timeline timeline, boolean playWhenReady) + throws ExoPlaybackException { prepared = true; trackGroups = mediaPeriod.getTrackGroups(); - TrackSelectorResult selectorResult = selectTracks(playbackSpeed, timeline); + TrackSelectorResult selectorResult = selectTracks(playbackSpeed, timeline, playWhenReady); long requestedStartPositionUs = info.startPositionUs; if (info.durationUs != C.TIME_UNSET && requestedStartPositionUs >= info.durationUs) { // Make sure start position doesn't exceed period duration. @@ -254,11 +256,12 @@ import java.io.IOException; * * @param playbackSpeed The current factor by which playback is sped up. * @param timeline The current {@link Timeline}. + * @param playWhenReady The current value of whether playback should proceed when ready. * @return The {@link TrackSelectorResult}. * @throws ExoPlaybackException If an error occurs during track selection. */ - public TrackSelectorResult selectTracks(float playbackSpeed, Timeline timeline) - throws ExoPlaybackException { + public TrackSelectorResult selectTracks( + float playbackSpeed, Timeline timeline, boolean playWhenReady) throws ExoPlaybackException { TrackSelectorResult selectorResult = trackSelector.selectTracks(rendererCapabilities, getTrackGroups(), info.id, timeline); for (int i = 0; i < selectorResult.length; i++) { @@ -273,6 +276,7 @@ import java.io.IOException; for (ExoTrackSelection trackSelection : selectorResult.selections) { if (trackSelection != null) { trackSelection.onPlaybackSpeed(playbackSpeed); + trackSelection.onPlayWhenReadyChanged(playWhenReady); } } return selectorResult; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/BaseTrackSelection.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/BaseTrackSelection.java index b346fe6cb3..2b7fbc864a 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/BaseTrackSelection.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/trackselection/BaseTrackSelection.java @@ -18,6 +18,7 @@ package androidx.media3.exoplayer.trackselection; import static java.lang.Math.max; import android.os.SystemClock; +import androidx.annotation.CallSuper; import androidx.annotation.Nullable; import androidx.media3.common.C; import androidx.media3.common.Format; @@ -54,6 +55,9 @@ public abstract class BaseTrackSelection implements ExoTrackSelection { // Lazily initialized hashcode. private int hashCode; + /** The current value of whether playback will proceed when ready. */ + private boolean playWhenReady; + /** * @param group The {@link TrackGroup}. Must not be null. * @param tracks The indices of the selected tracks within the {@link TrackGroup}. Must not be @@ -87,6 +91,7 @@ public abstract class BaseTrackSelection implements ExoTrackSelection { this.tracks[i] = group.indexOf(formats[i]); } excludeUntilTimes = new long[length]; + playWhenReady = false; } // TrackSelection implementation. @@ -191,6 +196,17 @@ public abstract class BaseTrackSelection implements ExoTrackSelection { return excludeUntilTimes[index] > nowMs; } + @CallSuper + @Override + public void onPlayWhenReadyChanged(boolean playWhenReady) { + this.playWhenReady = playWhenReady; + } + + /** Returns whether the playback using this track selection will proceed when ready. */ + protected final boolean getPlayWhenReady() { + return playWhenReady; + } + // Object overrides. @Override 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 8255dfd23e..615845e144 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -16174,6 +16174,79 @@ public class ExoPlayerTest { assertThat(deviceVolumeChanged.get()).isFalse(); } + @Test + public void playWhenReadyChanges_areForwardedToTrackSelection() throws Exception { + ArrayList reportedPlayWhenReadyChanges = new ArrayList<>(); + ArrayList playWhenReadyStatesInTrackSelector = new ArrayList<>(); + ExoPlayer player = + new TestExoPlayerBuilder(context) + .setTrackSelector( + new FakeTrackSelector( + new FakeTrackSelector.FakeTrackSelectionFactory( + /* mayReuseTrackSelection= */ false) { + @Override + protected ExoTrackSelection createTrackSelection(TrackGroup trackGroup) { + return new FakeTrackSelection(trackGroup) { + @Override + public void onPlayWhenReadyChanged(boolean playWhenReady) { + super.onPlayWhenReadyChanged(playWhenReady); + reportedPlayWhenReadyChanges.add(playWhenReady); + playWhenReadyStatesInTrackSelector.add(getPlayWhenReady()); + } + }; + } + })) + .build(); + player.setMediaSource( + new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT)); + + player.setPlayWhenReady(true); + player.prepare(); + run(player).untilState(Player.STATE_READY); + player.setPlayWhenReady(false); + player.setPlayWhenReady(true); + run(player).untilPendingCommandsAreFullyHandled(); + player.release(); + + assertThat(reportedPlayWhenReadyChanges).containsExactly(true, false, true).inOrder(); + assertThat(playWhenReadyStatesInTrackSelector).containsExactly(true, false, true).inOrder(); + } + + @Test + public void playbackSpeedChanges_areForwardedToTrackSelection() throws Exception { + ArrayList reportedSpeedChanges = new ArrayList<>(); + ExoPlayer player = + new TestExoPlayerBuilder(context) + .setTrackSelector( + new FakeTrackSelector( + new FakeTrackSelector.FakeTrackSelectionFactory( + /* mayReuseTrackSelection= */ false) { + @Override + protected ExoTrackSelection createTrackSelection(TrackGroup trackGroup) { + return new FakeTrackSelection(trackGroup) { + @Override + public void onPlaybackSpeed(float playbackSpeed) { + super.onPlaybackSpeed(playbackSpeed); + reportedSpeedChanges.add(playbackSpeed); + } + }; + } + })) + .build(); + player.setMediaSource( + new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.VIDEO_FORMAT)); + + player.setPlaybackSpeed(2f); + player.prepare(); + run(player).untilState(Player.STATE_READY); + player.setPlaybackSpeed(1.5f); + player.setPlaybackSpeed(1f); + run(player).untilPendingCommandsAreFullyHandled(); + player.release(); + + assertThat(reportedSpeedChanges).containsExactly(2f, 1.5f, 1f).inOrder(); + } + // Internal methods. private void addWatchAsSystemFeature() { diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTrackSelection.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTrackSelection.java index 8e1031a7b7..de0b974d59 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTrackSelection.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTrackSelection.java @@ -33,7 +33,7 @@ import java.util.List; * of calls to its methods. */ @UnstableApi -public final class FakeTrackSelection extends BaseTrackSelection { +public class FakeTrackSelection extends BaseTrackSelection { private final TrackGroup rendererTrackGroup; private final int selectedIndex; diff --git a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTrackSelector.java b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTrackSelector.java index 12da157fd5..8f44b7db48 100644 --- a/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTrackSelector.java +++ b/libraries/test_utils/src/main/java/androidx/media3/test/utils/FakeTrackSelector.java @@ -50,7 +50,11 @@ public class FakeTrackSelector extends DefaultTrackSelector { this(new FakeTrackSelectionFactory(mayReuseTrackSelection)); } - private FakeTrackSelector(FakeTrackSelectionFactory fakeTrackSelectionFactory) { + /** + * @param fakeTrackSelectionFactory The {@link FakeTrackSelectionFactory} used to create the + * {@link FakeTrackSelection} instances. + */ + public FakeTrackSelector(FakeTrackSelectionFactory fakeTrackSelectionFactory) { super(ApplicationProvider.getApplicationContext(), fakeTrackSelectionFactory); this.fakeTrackSelectionFactory = fakeTrackSelectionFactory; } @@ -80,7 +84,11 @@ public class FakeTrackSelector extends DefaultTrackSelector { return fakeTrackSelectionFactory.trackSelections; } - private static class FakeTrackSelectionFactory implements ExoTrackSelection.Factory { + /** + * A factory for the {@link androidx.media3.test.utils.FakeTrackSelection} instances requested by + * the {@link FakeTrackSelector}. + */ + public static class FakeTrackSelectionFactory implements ExoTrackSelection.Factory { private final List trackSelections; private final boolean mayReuseTrackSelection; @@ -91,7 +99,7 @@ public class FakeTrackSelector extends DefaultTrackSelector { } @Override - public ExoTrackSelection[] createTrackSelections( + public final ExoTrackSelection[] createTrackSelections( ExoTrackSelection.@NullableType Definition[] definitions, BandwidthMeter bandwidthMeter, MediaPeriodId mediaPeriodId, @@ -106,7 +114,8 @@ public class FakeTrackSelector extends DefaultTrackSelector { return selections; } - private ExoTrackSelection createTrackSelection(TrackGroup trackGroup) { + /** Creates the {@link FakeTrackSelection} from a {@link TrackGroup}. */ + protected ExoTrackSelection createTrackSelection(TrackGroup trackGroup) { if (mayReuseTrackSelection) { for (FakeTrackSelection trackSelection : trackSelections) { if (trackSelection.getTrackGroup().equals(trackGroup)) {