Add initial playWhenReady setting and tests

Also includes a new test for a similar logic that already exists
for speed changes.
This commit is contained in:
tonihei 2024-10-09 16:17:44 +01:00
parent e4b32e4e31
commit 513ebf67b7
6 changed files with 112 additions and 17 deletions

View File

@ -1879,7 +1879,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
// The reselection did not change any prepared periods. // The reselection did not change any prepared periods.
return; return;
} }
newTrackSelectorResult = periodHolder.selectTracks(playbackSpeed, playbackInfo.timeline); newTrackSelectorResult =
periodHolder.selectTracks(
playbackSpeed, playbackInfo.timeline, playbackInfo.playWhenReady);
if (periodHolder == queue.getPlayingPeriod()) { if (periodHolder == queue.getPlayingPeriod()) {
newPlayingPeriodTrackSelectorResult = newTrackSelectorResult; newPlayingPeriodTrackSelectorResult = newTrackSelectorResult;
} }
@ -2563,7 +2565,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
if (preloadHolder != null) { if (preloadHolder != null) {
checkState(!preloadHolder.prepared); checkState(!preloadHolder.prepared);
preloadHolder.handlePrepared( preloadHolder.handlePrepared(
mediaClock.getPlaybackParameters().speed, playbackInfo.timeline); mediaClock.getPlaybackParameters().speed,
playbackInfo.timeline,
playbackInfo.playWhenReady);
if (queue.isPreloading(mediaPeriod)) { if (queue.isPreloading(mediaPeriod)) {
maybeContinuePreloading(); maybeContinuePreloading();
} }
@ -2575,7 +2579,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
throws ExoPlaybackException { throws ExoPlaybackException {
if (!loadingPeriodHolder.prepared) { if (!loadingPeriodHolder.prepared) {
loadingPeriodHolder.handlePrepared( loadingPeriodHolder.handlePrepared(
mediaClock.getPlaybackParameters().speed, playbackInfo.timeline); mediaClock.getPlaybackParameters().speed,
playbackInfo.timeline,
playbackInfo.playWhenReady);
} }
updateLoadControlTrackSelection( updateLoadControlTrackSelection(
loadingPeriodHolder.info.id, loadingPeriodHolder.info.id,

View File

@ -203,12 +203,14 @@ import java.io.IOException;
* *
* @param playbackSpeed The current factor by which playback is sped up. * @param playbackSpeed The current factor by which playback is sped up.
* @param timeline The current {@link Timeline}. * @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. * @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; prepared = true;
trackGroups = mediaPeriod.getTrackGroups(); trackGroups = mediaPeriod.getTrackGroups();
TrackSelectorResult selectorResult = selectTracks(playbackSpeed, timeline); TrackSelectorResult selectorResult = selectTracks(playbackSpeed, timeline, playWhenReady);
long requestedStartPositionUs = info.startPositionUs; long requestedStartPositionUs = info.startPositionUs;
if (info.durationUs != C.TIME_UNSET && requestedStartPositionUs >= info.durationUs) { if (info.durationUs != C.TIME_UNSET && requestedStartPositionUs >= info.durationUs) {
// Make sure start position doesn't exceed period duration. // 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 playbackSpeed The current factor by which playback is sped up.
* @param timeline The current {@link Timeline}. * @param timeline The current {@link Timeline}.
* @param playWhenReady The current value of whether playback should proceed when ready.
* @return The {@link TrackSelectorResult}. * @return The {@link TrackSelectorResult}.
* @throws ExoPlaybackException If an error occurs during track selection. * @throws ExoPlaybackException If an error occurs during track selection.
*/ */
public TrackSelectorResult selectTracks(float playbackSpeed, Timeline timeline) public TrackSelectorResult selectTracks(
throws ExoPlaybackException { float playbackSpeed, Timeline timeline, boolean playWhenReady) throws ExoPlaybackException {
TrackSelectorResult selectorResult = TrackSelectorResult selectorResult =
trackSelector.selectTracks(rendererCapabilities, getTrackGroups(), info.id, timeline); trackSelector.selectTracks(rendererCapabilities, getTrackGroups(), info.id, timeline);
for (int i = 0; i < selectorResult.length; i++) { for (int i = 0; i < selectorResult.length; i++) {
@ -273,6 +276,7 @@ import java.io.IOException;
for (ExoTrackSelection trackSelection : selectorResult.selections) { for (ExoTrackSelection trackSelection : selectorResult.selections) {
if (trackSelection != null) { if (trackSelection != null) {
trackSelection.onPlaybackSpeed(playbackSpeed); trackSelection.onPlaybackSpeed(playbackSpeed);
trackSelection.onPlayWhenReadyChanged(playWhenReady);
} }
} }
return selectorResult; return selectorResult;

View File

@ -18,6 +18,7 @@ package androidx.media3.exoplayer.trackselection;
import static java.lang.Math.max; import static java.lang.Math.max;
import android.os.SystemClock; import android.os.SystemClock;
import androidx.annotation.CallSuper;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.Format; import androidx.media3.common.Format;
@ -54,8 +55,8 @@ public abstract class BaseTrackSelection implements ExoTrackSelection {
// Lazily initialized hashcode. // Lazily initialized hashcode.
private int hashCode; private int hashCode;
/** The last playWhenReady value when {@link #onPlayWhenReadyChanged(boolean)} was triggered */ /** The current value of whether playback will proceed when ready. */
private boolean lastPlayWhenReady; private boolean playWhenReady;
/** /**
* @param group The {@link TrackGroup}. Must not be null. * @param group The {@link TrackGroup}. Must not be null.
@ -90,7 +91,7 @@ public abstract class BaseTrackSelection implements ExoTrackSelection {
this.tracks[i] = group.indexOf(formats[i]); this.tracks[i] = group.indexOf(formats[i]);
} }
excludeUntilTimes = new long[length]; excludeUntilTimes = new long[length];
lastPlayWhenReady = false; playWhenReady = false;
} }
// TrackSelection implementation. // TrackSelection implementation.
@ -195,13 +196,15 @@ public abstract class BaseTrackSelection implements ExoTrackSelection {
return excludeUntilTimes[index] > nowMs; return excludeUntilTimes[index] > nowMs;
} }
@CallSuper
@Override @Override
public void onPlayWhenReadyChanged(boolean playWhenReady) { public void onPlayWhenReadyChanged(boolean playWhenReady) {
lastPlayWhenReady = playWhenReady; this.playWhenReady = playWhenReady;
} }
/** Returns whether the playback using this track selection will proceed when ready. */
protected final boolean getPlayWhenReady() { protected final boolean getPlayWhenReady() {
return lastPlayWhenReady; return playWhenReady;
} }
// Object overrides. // Object overrides.

View File

@ -16174,6 +16174,79 @@ public class ExoPlayerTest {
assertThat(deviceVolumeChanged.get()).isFalse(); assertThat(deviceVolumeChanged.get()).isFalse();
} }
@Test
public void playWhenReadyChanges_areForwardedToTrackSelection() throws Exception {
ArrayList<Boolean> reportedPlayWhenReadyChanges = new ArrayList<>();
ArrayList<Boolean> 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<Float> 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. // Internal methods.
private void addWatchAsSystemFeature() { private void addWatchAsSystemFeature() {

View File

@ -33,7 +33,7 @@ import java.util.List;
* of calls to its methods. * of calls to its methods.
*/ */
@UnstableApi @UnstableApi
public final class FakeTrackSelection extends BaseTrackSelection { public class FakeTrackSelection extends BaseTrackSelection {
private final TrackGroup rendererTrackGroup; private final TrackGroup rendererTrackGroup;
private final int selectedIndex; private final int selectedIndex;

View File

@ -50,7 +50,11 @@ public class FakeTrackSelector extends DefaultTrackSelector {
this(new FakeTrackSelectionFactory(mayReuseTrackSelection)); 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); super(ApplicationProvider.getApplicationContext(), fakeTrackSelectionFactory);
this.fakeTrackSelectionFactory = fakeTrackSelectionFactory; this.fakeTrackSelectionFactory = fakeTrackSelectionFactory;
} }
@ -80,7 +84,11 @@ public class FakeTrackSelector extends DefaultTrackSelector {
return fakeTrackSelectionFactory.trackSelections; 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<FakeTrackSelection> trackSelections; private final List<FakeTrackSelection> trackSelections;
private final boolean mayReuseTrackSelection; private final boolean mayReuseTrackSelection;
@ -91,7 +99,7 @@ public class FakeTrackSelector extends DefaultTrackSelector {
} }
@Override @Override
public ExoTrackSelection[] createTrackSelections( public final ExoTrackSelection[] createTrackSelections(
ExoTrackSelection.@NullableType Definition[] definitions, ExoTrackSelection.@NullableType Definition[] definitions,
BandwidthMeter bandwidthMeter, BandwidthMeter bandwidthMeter,
MediaPeriodId mediaPeriodId, MediaPeriodId mediaPeriodId,
@ -106,7 +114,8 @@ public class FakeTrackSelector extends DefaultTrackSelector {
return selections; return selections;
} }
private ExoTrackSelection createTrackSelection(TrackGroup trackGroup) { /** Creates the {@link FakeTrackSelection} from a {@link TrackGroup}. */
protected ExoTrackSelection createTrackSelection(TrackGroup trackGroup) {
if (mayReuseTrackSelection) { if (mayReuseTrackSelection) {
for (FakeTrackSelection trackSelection : trackSelections) { for (FakeTrackSelection trackSelection : trackSelections) {
if (trackSelection.getTrackGroup().equals(trackGroup)) { if (trackSelection.getTrackGroup().equals(trackGroup)) {