Fix some EventListener logic in ExoPlayerImpl
We currently have multiple places in ExoPlayerImpl that assign PlaybackInfo instances and then inform listeners of all current changes. This is not ideal because it causes multiple issues: 1. Some changes may easily be forgotten, e.g. there are clearly some checks missing to see if isPlaying changed (e.g. in seekTo or setMediaSources) 2. Some callbacks didn't check if the value actually changed before sending the callback (e.g. for the timeline change in setMediaSources - if the timeline is still the same, we shouldn't send a onTimelineChanged event). 3. Having multiple callbacks in a single Runnable changes the order of listener invocations slightly: Currently all events for one listener will be send first before moving to the next listener. It should however send a single event to all listeners first before moving to the next event. All these issues can be solved by always using updatePlaybackInfo and never assigning playbackInfo directly in another place. Some tests needed to be updated as well because of issues (2) and (3). Also added a new test to cover issue (1). PiperOrigin-RevId: 302844981
This commit is contained in:
parent
c3cbccbbf8
commit
46a10ec01a
@ -432,11 +432,16 @@ import java.util.concurrent.TimeoutException;
|
||||
Timeline oldTimeline = getCurrentTimeline();
|
||||
pendingOperationAcks++;
|
||||
List<Playlist.MediaSourceHolder> holders = addMediaSourceHolders(index, mediaSources);
|
||||
Timeline timeline =
|
||||
PlaybackInfo playbackInfo =
|
||||
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
|
||||
internalPlayer.addMediaSources(index, holders, shuffleOrder);
|
||||
notifyListeners(
|
||||
listener -> listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED));
|
||||
updatePlaybackInfo(
|
||||
playbackInfo,
|
||||
/* positionDiscontinuity= */ false,
|
||||
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
|
||||
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||
/* seekProcessed= */ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -469,11 +474,16 @@ import java.util.concurrent.TimeoutException;
|
||||
pendingOperationAcks++;
|
||||
newFromIndex = Math.min(newFromIndex, mediaSourceHolders.size() - (toIndex - fromIndex));
|
||||
Playlist.moveMediaSourceHolders(mediaSourceHolders, fromIndex, toIndex, newFromIndex);
|
||||
Timeline timeline =
|
||||
PlaybackInfo playbackInfo =
|
||||
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
|
||||
internalPlayer.moveMediaSources(fromIndex, toIndex, newFromIndex, shuffleOrder);
|
||||
notifyListeners(
|
||||
listener -> listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED));
|
||||
updatePlaybackInfo(
|
||||
playbackInfo,
|
||||
/* positionDiscontinuity= */ false,
|
||||
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
|
||||
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||
/* seekProcessed= */ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -486,13 +496,18 @@ import java.util.concurrent.TimeoutException;
|
||||
|
||||
@Override
|
||||
public void setShuffleOrder(ShuffleOrder shuffleOrder) {
|
||||
Timeline timeline = maskTimeline();
|
||||
PlaybackInfo playbackInfo = maskTimeline();
|
||||
maskWithCurrentPosition();
|
||||
pendingOperationAcks++;
|
||||
this.shuffleOrder = shuffleOrder;
|
||||
internalPlayer.setShuffleOrder(shuffleOrder);
|
||||
notifyListeners(
|
||||
listener -> listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED));
|
||||
updatePlaybackInfo(
|
||||
playbackInfo,
|
||||
/* positionDiscontinuity= */ false,
|
||||
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
|
||||
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||
/* seekProcessed= */ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -517,38 +532,26 @@ import java.util.concurrent.TimeoutException;
|
||||
return pauseAtEndOfMediaItems;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setPlayWhenReady(
|
||||
boolean playWhenReady,
|
||||
@PlaybackSuppressionReason int playbackSuppressionReason,
|
||||
@PlayWhenReadyChangeReason int playWhenReadyChangeReason) {
|
||||
boolean oldIsPlaying = isPlaying();
|
||||
boolean playWhenReadyChanged = playbackInfo.playWhenReady != playWhenReady;
|
||||
boolean suppressionReasonChanged =
|
||||
playbackInfo.playbackSuppressionReason != playbackSuppressionReason;
|
||||
if (!playWhenReadyChanged && !suppressionReasonChanged) {
|
||||
if (playbackInfo.playWhenReady == playWhenReady
|
||||
&& playbackInfo.playbackSuppressionReason == playbackSuppressionReason) {
|
||||
return;
|
||||
}
|
||||
maskWithCurrentPosition();
|
||||
pendingOperationAcks++;
|
||||
playbackInfo = playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason);
|
||||
PlaybackInfo playbackInfo =
|
||||
this.playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason);
|
||||
internalPlayer.setPlayWhenReady(playWhenReady, playbackSuppressionReason);
|
||||
boolean isPlaying = isPlaying();
|
||||
boolean isPlayingChanged = oldIsPlaying != isPlaying;
|
||||
int playbackState = playbackInfo.playbackState;
|
||||
notifyListeners(
|
||||
listener -> {
|
||||
if (playWhenReadyChanged) {
|
||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
||||
listener.onPlayWhenReadyChanged(playWhenReady, playWhenReadyChangeReason);
|
||||
}
|
||||
if (suppressionReasonChanged) {
|
||||
listener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason);
|
||||
}
|
||||
if (isPlayingChanged) {
|
||||
listener.onIsPlayingChanged(isPlaying);
|
||||
}
|
||||
});
|
||||
updatePlaybackInfo(
|
||||
playbackInfo,
|
||||
/* positionDiscontinuity= */ false,
|
||||
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
|
||||
/* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
playWhenReadyChangeReason,
|
||||
/* seekProcessed= */ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -589,7 +592,6 @@ import java.util.concurrent.TimeoutException;
|
||||
return playbackInfo.isLoading;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void seekTo(int windowIndex, long positionMs) {
|
||||
Timeline timeline = playbackInfo.timeline;
|
||||
@ -597,8 +599,6 @@ import java.util.concurrent.TimeoutException;
|
||||
throw new IllegalSeekPositionException(timeline, windowIndex, positionMs);
|
||||
}
|
||||
hasPendingSeek = true;
|
||||
boolean playWhenReady = getPlayWhenReady();
|
||||
@Player.State int playbackState = getPlaybackState();
|
||||
pendingOperationAcks++;
|
||||
if (isPlayingAd()) {
|
||||
// TODO: Investigate adding support for seeking during ads. This is complicated to do in
|
||||
@ -617,18 +617,16 @@ import java.util.concurrent.TimeoutException;
|
||||
maskWindowIndexAndPositionForSeek(timeline, windowIndex, positionMs);
|
||||
@Player.State
|
||||
int newPlaybackState =
|
||||
playbackState == Player.STATE_IDLE ? Player.STATE_IDLE : Player.STATE_BUFFERING;
|
||||
boolean playbackStateChanged = playbackState != newPlaybackState;
|
||||
playbackInfo = playbackInfo.copyWithPlaybackState(newPlaybackState);
|
||||
getPlaybackState() == Player.STATE_IDLE ? Player.STATE_IDLE : Player.STATE_BUFFERING;
|
||||
PlaybackInfo playbackInfo = this.playbackInfo.copyWithPlaybackState(newPlaybackState);
|
||||
internalPlayer.seekTo(timeline, windowIndex, C.msToUs(positionMs));
|
||||
notifyListeners(
|
||||
listener -> {
|
||||
listener.onPositionDiscontinuity(DISCONTINUITY_REASON_SEEK);
|
||||
if (playbackStateChanged) {
|
||||
listener.onPlayerStateChanged(playWhenReady, newPlaybackState);
|
||||
listener.onPlaybackStateChanged(newPlaybackState);
|
||||
}
|
||||
});
|
||||
updatePlaybackInfo(
|
||||
playbackInfo,
|
||||
/* positionDiscontinuity= */ true,
|
||||
/* positionDiscontinuityReason= */ DISCONTINUITY_REASON_SEEK,
|
||||
/* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||
/* seekProcessed= */ false);
|
||||
}
|
||||
|
||||
/** @deprecated Use {@link #setPlaybackSpeed(float)} instead. */
|
||||
@ -1011,7 +1009,6 @@ import java.util.concurrent.TimeoutException;
|
||||
seekProcessed));
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void setMediaSourcesInternal(
|
||||
List<MediaSource> mediaItems,
|
||||
int startWindowIndex,
|
||||
@ -1019,14 +1016,14 @@ import java.util.concurrent.TimeoutException;
|
||||
boolean resetToDefaultPosition) {
|
||||
int currentWindowIndex = getCurrentWindowIndexInternal();
|
||||
long currentPositionMs = getCurrentPosition();
|
||||
boolean currentPlayWhenReady = getPlayWhenReady();
|
||||
pendingOperationAcks++;
|
||||
if (!mediaSourceHolders.isEmpty()) {
|
||||
removeMediaSourceHolders(
|
||||
/* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolders.size());
|
||||
}
|
||||
List<Playlist.MediaSourceHolder> holders = addMediaSourceHolders(/* index= */ 0, mediaItems);
|
||||
Timeline timeline = maskTimeline();
|
||||
PlaybackInfo playbackInfo = maskTimeline();
|
||||
Timeline timeline = playbackInfo.timeline;
|
||||
if (!timeline.isEmpty() && startWindowIndex >= timeline.getWindowCount()) {
|
||||
throw new IllegalSeekPositionException(timeline, startWindowIndex, startPositionMs);
|
||||
}
|
||||
@ -1040,9 +1037,9 @@ import java.util.concurrent.TimeoutException;
|
||||
}
|
||||
maskWindowIndexAndPositionForSeek(
|
||||
timeline, startWindowIndex == C.INDEX_UNSET ? 0 : startWindowIndex, startPositionMs);
|
||||
// mask the playback state
|
||||
// Mask the playback state.
|
||||
int maskingPlaybackState = playbackInfo.playbackState;
|
||||
if (startWindowIndex != C.INDEX_UNSET) {
|
||||
if (startWindowIndex != C.INDEX_UNSET && playbackInfo.playbackState != STATE_IDLE) {
|
||||
// Position reset to startWindowIndex (results in pending initial seek).
|
||||
if (timeline.isEmpty() || startWindowIndex >= timeline.getWindowCount()) {
|
||||
// Setting an empty timeline or invalid seek transitions to ended.
|
||||
@ -1051,23 +1048,16 @@ import java.util.concurrent.TimeoutException;
|
||||
maskingPlaybackState = STATE_BUFFERING;
|
||||
}
|
||||
}
|
||||
boolean playbackStateChanged =
|
||||
playbackInfo.playbackState != STATE_IDLE
|
||||
&& playbackInfo.playbackState != maskingPlaybackState;
|
||||
int finalMaskingPlaybackState = maskingPlaybackState;
|
||||
if (playbackStateChanged) {
|
||||
playbackInfo = playbackInfo.copyWithPlaybackState(finalMaskingPlaybackState);
|
||||
}
|
||||
playbackInfo = playbackInfo.copyWithPlaybackState(maskingPlaybackState);
|
||||
internalPlayer.setMediaSources(
|
||||
holders, startWindowIndex, C.msToUs(startPositionMs), shuffleOrder);
|
||||
notifyListeners(
|
||||
listener -> {
|
||||
listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
||||
if (playbackStateChanged) {
|
||||
listener.onPlayerStateChanged(currentPlayWhenReady, finalMaskingPlaybackState);
|
||||
listener.onPlaybackStateChanged(finalMaskingPlaybackState);
|
||||
}
|
||||
});
|
||||
updatePlaybackInfo(
|
||||
playbackInfo,
|
||||
/* positionDiscontinuity= */ false,
|
||||
/* ignored */ Player.DISCONTINUITY_REASON_INTERNAL,
|
||||
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||
/* seekProcessed= */ false);
|
||||
}
|
||||
|
||||
private List<Playlist.MediaSourceHolder> addMediaSourceHolders(
|
||||
@ -1085,18 +1075,16 @@ import java.util.concurrent.TimeoutException;
|
||||
return holders;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void removeMediaItemsInternal(int fromIndex, int toIndex) {
|
||||
Assertions.checkArgument(
|
||||
fromIndex >= 0 && toIndex >= fromIndex && toIndex <= mediaSourceHolders.size());
|
||||
int currentWindowIndex = getCurrentWindowIndex();
|
||||
long currentPositionMs = getCurrentPosition();
|
||||
boolean currentPlayWhenReady = getPlayWhenReady();
|
||||
Timeline oldTimeline = getCurrentTimeline();
|
||||
int currentMediaSourceCount = mediaSourceHolders.size();
|
||||
pendingOperationAcks++;
|
||||
removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */ toIndex);
|
||||
Timeline timeline =
|
||||
PlaybackInfo playbackInfo =
|
||||
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
|
||||
// Player transitions to STATE_ENDED if the current index is part of the removed tail.
|
||||
final boolean transitionsToEnded =
|
||||
@ -1104,19 +1092,18 @@ import java.util.concurrent.TimeoutException;
|
||||
&& playbackInfo.playbackState != STATE_ENDED
|
||||
&& fromIndex < toIndex
|
||||
&& toIndex == currentMediaSourceCount
|
||||
&& currentWindowIndex >= timeline.getWindowCount();
|
||||
&& currentWindowIndex >= playbackInfo.timeline.getWindowCount();
|
||||
if (transitionsToEnded) {
|
||||
playbackInfo = playbackInfo.copyWithPlaybackState(STATE_ENDED);
|
||||
}
|
||||
internalPlayer.removeMediaSources(fromIndex, toIndex, shuffleOrder);
|
||||
notifyListeners(
|
||||
listener -> {
|
||||
listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
||||
if (transitionsToEnded) {
|
||||
listener.onPlayerStateChanged(currentPlayWhenReady, STATE_ENDED);
|
||||
listener.onPlaybackStateChanged(STATE_ENDED);
|
||||
}
|
||||
});
|
||||
updatePlaybackInfo(
|
||||
playbackInfo,
|
||||
/* positionDiscontinuity= */ false,
|
||||
/* ignored */ Player.DISCONTINUITY_REASON_INTERNAL,
|
||||
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||
/* seekProcessed= */ false);
|
||||
}
|
||||
|
||||
private List<Playlist.MediaSourceHolder> removeMediaSourceHolders(
|
||||
@ -1129,18 +1116,17 @@ import java.util.concurrent.TimeoutException;
|
||||
return removed;
|
||||
}
|
||||
|
||||
private Timeline maskTimeline() {
|
||||
playbackInfo =
|
||||
playbackInfo.copyWithTimeline(
|
||||
mediaSourceHolders.isEmpty()
|
||||
? Timeline.EMPTY
|
||||
: new Playlist.PlaylistTimeline(mediaSourceHolders, shuffleOrder));
|
||||
return playbackInfo.timeline;
|
||||
private PlaybackInfo maskTimeline() {
|
||||
return playbackInfo.copyWithTimeline(
|
||||
mediaSourceHolders.isEmpty()
|
||||
? Timeline.EMPTY
|
||||
: new Playlist.PlaylistTimeline(mediaSourceHolders, shuffleOrder));
|
||||
}
|
||||
|
||||
private Timeline maskTimelineAndWindowIndex(
|
||||
private PlaybackInfo maskTimelineAndWindowIndex(
|
||||
int currentWindowIndex, long currentPositionMs, Timeline oldTimeline) {
|
||||
Timeline maskingTimeline = maskTimeline();
|
||||
PlaybackInfo playbackInfo = maskTimeline();
|
||||
Timeline maskingTimeline = playbackInfo.timeline;
|
||||
if (oldTimeline.isEmpty()) {
|
||||
// The index is the default index or was set by a seek in the empty old timeline.
|
||||
maskingWindowIndex = currentWindowIndex;
|
||||
@ -1148,7 +1134,7 @@ import java.util.concurrent.TimeoutException;
|
||||
// The seek is not valid in the new timeline.
|
||||
maskWithDefaultPosition(maskingTimeline);
|
||||
}
|
||||
return maskingTimeline;
|
||||
return playbackInfo;
|
||||
}
|
||||
@Nullable
|
||||
Pair<Object, Long> periodPosition =
|
||||
@ -1186,7 +1172,7 @@ import java.util.concurrent.TimeoutException;
|
||||
maskWithDefaultPosition(maskingTimeline);
|
||||
}
|
||||
}
|
||||
return maskingTimeline;
|
||||
return playbackInfo;
|
||||
}
|
||||
|
||||
private void maskWindowIndexAndPositionForSeek(
|
||||
|
@ -3391,7 +3391,6 @@ public final class ExoPlayerTest {
|
||||
.blockUntilActionScheduleFinished(TIMEOUT_MS)
|
||||
.blockUntilEnded(TIMEOUT_MS);
|
||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
||||
assertArrayEquals(new long[] {2}, windowCounts);
|
||||
assertArrayEquals(new int[] {seekToWindowIndex}, currentWindowIndices);
|
||||
@ -4157,7 +4156,7 @@ public final class ExoPlayerTest {
|
||||
int seekToWindowIndex = 1;
|
||||
ActionSchedule actionSchedule =
|
||||
new ActionSchedule.Builder(TAG)
|
||||
.waitForTimelineChanged()
|
||||
.waitForPlaybackState(Player.STATE_ENDED)
|
||||
.executeRunnable(
|
||||
new PlayerRunnable() {
|
||||
@Override
|
||||
@ -4168,12 +4167,11 @@ public final class ExoPlayerTest {
|
||||
}
|
||||
})
|
||||
.executeRunnable(
|
||||
() -> {
|
||||
concatenatingMediaSource.addMediaSource(
|
||||
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT));
|
||||
concatenatingMediaSource.addMediaSource(
|
||||
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT));
|
||||
})
|
||||
() ->
|
||||
concatenatingMediaSource.addMediaSources(
|
||||
Arrays.asList(
|
||||
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT),
|
||||
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT))))
|
||||
.waitForTimelineChanged()
|
||||
.executeRunnable(
|
||||
new PlayerRunnable() {
|
||||
@ -4203,7 +4201,7 @@ public final class ExoPlayerTest {
|
||||
ConcatenatingMediaSource concatenatingMediaSource =
|
||||
new ConcatenatingMediaSource(/* isAtomic= */ false);
|
||||
ActionSchedule actionSchedule =
|
||||
new ActionSchedule.Builder(TAG).waitForTimelineChanged().build();
|
||||
new ActionSchedule.Builder(TAG).waitForPlaybackState(Player.STATE_ENDED).build();
|
||||
ExoPlayerTestRunner exoPlayerTestRunner =
|
||||
new Builder()
|
||||
.setMediaSources(concatenatingMediaSource)
|
||||
@ -4229,7 +4227,7 @@ public final class ExoPlayerTest {
|
||||
int seekToWindowIndex = 1;
|
||||
ActionSchedule actionSchedule =
|
||||
new ActionSchedule.Builder(TAG)
|
||||
.waitForTimelineChanged()
|
||||
.waitForPlaybackState(Player.STATE_ENDED)
|
||||
.executeRunnable(
|
||||
new PlayerRunnable() {
|
||||
@Override
|
||||
@ -4240,12 +4238,11 @@ public final class ExoPlayerTest {
|
||||
}
|
||||
})
|
||||
.executeRunnable(
|
||||
() -> {
|
||||
concatenatingMediaSource.addMediaSource(
|
||||
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT));
|
||||
concatenatingMediaSource.addMediaSource(
|
||||
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT));
|
||||
})
|
||||
() ->
|
||||
concatenatingMediaSource.addMediaSources(
|
||||
Arrays.asList(
|
||||
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT),
|
||||
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT))))
|
||||
.waitForTimelineChanged()
|
||||
.executeRunnable(
|
||||
new PlayerRunnable() {
|
||||
@ -4727,7 +4724,6 @@ public final class ExoPlayerTest {
|
||||
new int[] {Player.STATE_IDLE, Player.STATE_IDLE, Player.STATE_IDLE, Player.STATE_IDLE},
|
||||
maskingPlaybackStates);
|
||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
||||
@ -4768,7 +4764,6 @@ public final class ExoPlayerTest {
|
||||
Player.STATE_BUFFERING, Player.STATE_READY, Player.STATE_ENDED);
|
||||
assertArrayEquals(new int[] {Player.STATE_IDLE}, maskingPlaybackStates);
|
||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
||||
}
|
||||
@ -4841,7 +4836,6 @@ public final class ExoPlayerTest {
|
||||
Player.STATE_BUFFERING, Player.STATE_READY, Player.STATE_ENDED);
|
||||
assertArrayEquals(new int[] {Player.STATE_IDLE}, maskingPlaybackStates);
|
||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
||||
}
|
||||
@ -4924,9 +4918,6 @@ public final class ExoPlayerTest {
|
||||
},
|
||||
maskingPlaybackStates);
|
||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
@ -4969,7 +4960,6 @@ public final class ExoPlayerTest {
|
||||
exoPlayerTestRunner.assertPlaybackStatesEqual(Player.STATE_ENDED);
|
||||
assertArrayEquals(new int[] {Player.STATE_ENDED}, maskingPlaybackStates);
|
||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
||||
}
|
||||
@ -5050,7 +5040,6 @@ public final class ExoPlayerTest {
|
||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
||||
}
|
||||
|
||||
@ -5213,7 +5202,6 @@ public final class ExoPlayerTest {
|
||||
Player.STATE_ENDED);
|
||||
assertArrayEquals(new int[] {Player.STATE_ENDED}, maskingPlaybackStates);
|
||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE,
|
||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||
@ -6161,6 +6149,7 @@ public final class ExoPlayerTest {
|
||||
public void loading_withLargeAllocationCausingOom_playsRemainingMediaAndThenThrows() {
|
||||
Loader.Loadable loadable =
|
||||
new Loader.Loadable() {
|
||||
@SuppressWarnings("UnusedVariable")
|
||||
@Override
|
||||
public void load() throws IOException {
|
||||
@SuppressWarnings("unused") // This test needs the allocation to cause an OOM.
|
||||
@ -6229,6 +6218,97 @@ public final class ExoPlayerTest {
|
||||
assertThat(renderer.sampleBufferReadCount).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void seekTo_whileReady_callsOnIsPlayingChanged() throws Exception {
|
||||
ActionSchedule actionSchedule =
|
||||
new ActionSchedule.Builder(TAG)
|
||||
.waitForPlaybackState(Player.STATE_READY)
|
||||
.seek(/* positionMs= */ 0)
|
||||
.waitForPlaybackState(Player.STATE_ENDED)
|
||||
.build();
|
||||
List<Boolean> onIsPlayingChanges = new ArrayList<>();
|
||||
Player.EventListener eventListener =
|
||||
new Player.EventListener() {
|
||||
@Override
|
||||
public void onIsPlayingChanged(boolean isPlaying) {
|
||||
onIsPlayingChanges.add(isPlaying);
|
||||
}
|
||||
};
|
||||
new ExoPlayerTestRunner.Builder()
|
||||
.setEventListener(eventListener)
|
||||
.setActionSchedule(actionSchedule)
|
||||
.build(context)
|
||||
.start()
|
||||
.blockUntilActionScheduleFinished(TIMEOUT_MS)
|
||||
.blockUntilEnded(TIMEOUT_MS);
|
||||
|
||||
assertThat(onIsPlayingChanges).containsExactly(true, false, true, false).inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleListenersAndMultipleCallbacks_callbacksAreOrderedByType() throws Exception {
|
||||
String playWhenReadyChange1 = "playWhenReadyChange1";
|
||||
String playWhenReadyChange2 = "playWhenReadyChange2";
|
||||
String isPlayingChange1 = "isPlayingChange1";
|
||||
String isPlayingChange2 = "isPlayingChange2";
|
||||
ArrayList<String> events = new ArrayList<>();
|
||||
Player.EventListener eventListener1 =
|
||||
new Player.EventListener() {
|
||||
@Override
|
||||
public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
|
||||
events.add(playWhenReadyChange1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsPlayingChanged(boolean isPlaying) {
|
||||
events.add(isPlayingChange1);
|
||||
}
|
||||
};
|
||||
Player.EventListener eventListener2 =
|
||||
new Player.EventListener() {
|
||||
@Override
|
||||
public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
|
||||
events.add(playWhenReadyChange2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onIsPlayingChanged(boolean isPlaying) {
|
||||
events.add(isPlayingChange2);
|
||||
}
|
||||
};
|
||||
ActionSchedule actionSchedule =
|
||||
new ActionSchedule.Builder(TAG)
|
||||
.pause()
|
||||
.executeRunnable(
|
||||
new PlayerRunnable() {
|
||||
@Override
|
||||
public void run(SimpleExoPlayer player) {
|
||||
player.addListener(eventListener1);
|
||||
player.addListener(eventListener2);
|
||||
}
|
||||
})
|
||||
.waitForPlaybackState(Player.STATE_READY)
|
||||
.play()
|
||||
.waitForPlaybackState(Player.STATE_ENDED)
|
||||
.build();
|
||||
new ExoPlayerTestRunner.Builder()
|
||||
.setActionSchedule(actionSchedule)
|
||||
.build(context)
|
||||
.start()
|
||||
.blockUntilActionScheduleFinished(TIMEOUT_MS)
|
||||
.blockUntilEnded(TIMEOUT_MS);
|
||||
|
||||
assertThat(events)
|
||||
.containsExactly(
|
||||
playWhenReadyChange1,
|
||||
playWhenReadyChange2,
|
||||
isPlayingChange1,
|
||||
isPlayingChange2,
|
||||
isPlayingChange1,
|
||||
isPlayingChange2)
|
||||
.inOrder();
|
||||
}
|
||||
|
||||
// Internal methods.
|
||||
|
||||
private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user