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();
|
Timeline oldTimeline = getCurrentTimeline();
|
||||||
pendingOperationAcks++;
|
pendingOperationAcks++;
|
||||||
List<Playlist.MediaSourceHolder> holders = addMediaSourceHolders(index, mediaSources);
|
List<Playlist.MediaSourceHolder> holders = addMediaSourceHolders(index, mediaSources);
|
||||||
Timeline timeline =
|
PlaybackInfo playbackInfo =
|
||||||
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
|
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
|
||||||
internalPlayer.addMediaSources(index, holders, shuffleOrder);
|
internalPlayer.addMediaSources(index, holders, shuffleOrder);
|
||||||
notifyListeners(
|
updatePlaybackInfo(
|
||||||
listener -> listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED));
|
playbackInfo,
|
||||||
|
/* positionDiscontinuity= */ false,
|
||||||
|
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
|
||||||
|
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||||
|
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||||
|
/* seekProcessed= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -469,11 +474,16 @@ import java.util.concurrent.TimeoutException;
|
|||||||
pendingOperationAcks++;
|
pendingOperationAcks++;
|
||||||
newFromIndex = Math.min(newFromIndex, mediaSourceHolders.size() - (toIndex - fromIndex));
|
newFromIndex = Math.min(newFromIndex, mediaSourceHolders.size() - (toIndex - fromIndex));
|
||||||
Playlist.moveMediaSourceHolders(mediaSourceHolders, fromIndex, toIndex, newFromIndex);
|
Playlist.moveMediaSourceHolders(mediaSourceHolders, fromIndex, toIndex, newFromIndex);
|
||||||
Timeline timeline =
|
PlaybackInfo playbackInfo =
|
||||||
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
|
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
|
||||||
internalPlayer.moveMediaSources(fromIndex, toIndex, newFromIndex, shuffleOrder);
|
internalPlayer.moveMediaSources(fromIndex, toIndex, newFromIndex, shuffleOrder);
|
||||||
notifyListeners(
|
updatePlaybackInfo(
|
||||||
listener -> listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED));
|
playbackInfo,
|
||||||
|
/* positionDiscontinuity= */ false,
|
||||||
|
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
|
||||||
|
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||||
|
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||||
|
/* seekProcessed= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -486,13 +496,18 @@ import java.util.concurrent.TimeoutException;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setShuffleOrder(ShuffleOrder shuffleOrder) {
|
public void setShuffleOrder(ShuffleOrder shuffleOrder) {
|
||||||
Timeline timeline = maskTimeline();
|
PlaybackInfo playbackInfo = maskTimeline();
|
||||||
maskWithCurrentPosition();
|
maskWithCurrentPosition();
|
||||||
pendingOperationAcks++;
|
pendingOperationAcks++;
|
||||||
this.shuffleOrder = shuffleOrder;
|
this.shuffleOrder = shuffleOrder;
|
||||||
internalPlayer.setShuffleOrder(shuffleOrder);
|
internalPlayer.setShuffleOrder(shuffleOrder);
|
||||||
notifyListeners(
|
updatePlaybackInfo(
|
||||||
listener -> listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED));
|
playbackInfo,
|
||||||
|
/* positionDiscontinuity= */ false,
|
||||||
|
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
|
||||||
|
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||||
|
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||||
|
/* seekProcessed= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -517,38 +532,26 @@ import java.util.concurrent.TimeoutException;
|
|||||||
return pauseAtEndOfMediaItems;
|
return pauseAtEndOfMediaItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public void setPlayWhenReady(
|
public void setPlayWhenReady(
|
||||||
boolean playWhenReady,
|
boolean playWhenReady,
|
||||||
@PlaybackSuppressionReason int playbackSuppressionReason,
|
@PlaybackSuppressionReason int playbackSuppressionReason,
|
||||||
@PlayWhenReadyChangeReason int playWhenReadyChangeReason) {
|
@PlayWhenReadyChangeReason int playWhenReadyChangeReason) {
|
||||||
boolean oldIsPlaying = isPlaying();
|
if (playbackInfo.playWhenReady == playWhenReady
|
||||||
boolean playWhenReadyChanged = playbackInfo.playWhenReady != playWhenReady;
|
&& playbackInfo.playbackSuppressionReason == playbackSuppressionReason) {
|
||||||
boolean suppressionReasonChanged =
|
|
||||||
playbackInfo.playbackSuppressionReason != playbackSuppressionReason;
|
|
||||||
if (!playWhenReadyChanged && !suppressionReasonChanged) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
maskWithCurrentPosition();
|
maskWithCurrentPosition();
|
||||||
pendingOperationAcks++;
|
pendingOperationAcks++;
|
||||||
playbackInfo = playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason);
|
PlaybackInfo playbackInfo =
|
||||||
|
this.playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason);
|
||||||
internalPlayer.setPlayWhenReady(playWhenReady, playbackSuppressionReason);
|
internalPlayer.setPlayWhenReady(playWhenReady, playbackSuppressionReason);
|
||||||
boolean isPlaying = isPlaying();
|
updatePlaybackInfo(
|
||||||
boolean isPlayingChanged = oldIsPlaying != isPlaying;
|
playbackInfo,
|
||||||
int playbackState = playbackInfo.playbackState;
|
/* positionDiscontinuity= */ false,
|
||||||
notifyListeners(
|
/* ignored */ DISCONTINUITY_REASON_INTERNAL,
|
||||||
listener -> {
|
/* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||||
if (playWhenReadyChanged) {
|
playWhenReadyChangeReason,
|
||||||
listener.onPlayerStateChanged(playWhenReady, playbackState);
|
/* seekProcessed= */ false);
|
||||||
listener.onPlayWhenReadyChanged(playWhenReady, playWhenReadyChangeReason);
|
|
||||||
}
|
|
||||||
if (suppressionReasonChanged) {
|
|
||||||
listener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason);
|
|
||||||
}
|
|
||||||
if (isPlayingChanged) {
|
|
||||||
listener.onIsPlayingChanged(isPlaying);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -589,7 +592,6 @@ import java.util.concurrent.TimeoutException;
|
|||||||
return playbackInfo.isLoading;
|
return playbackInfo.isLoading;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Override
|
@Override
|
||||||
public void seekTo(int windowIndex, long positionMs) {
|
public void seekTo(int windowIndex, long positionMs) {
|
||||||
Timeline timeline = playbackInfo.timeline;
|
Timeline timeline = playbackInfo.timeline;
|
||||||
@ -597,8 +599,6 @@ import java.util.concurrent.TimeoutException;
|
|||||||
throw new IllegalSeekPositionException(timeline, windowIndex, positionMs);
|
throw new IllegalSeekPositionException(timeline, windowIndex, positionMs);
|
||||||
}
|
}
|
||||||
hasPendingSeek = true;
|
hasPendingSeek = true;
|
||||||
boolean playWhenReady = getPlayWhenReady();
|
|
||||||
@Player.State int playbackState = getPlaybackState();
|
|
||||||
pendingOperationAcks++;
|
pendingOperationAcks++;
|
||||||
if (isPlayingAd()) {
|
if (isPlayingAd()) {
|
||||||
// TODO: Investigate adding support for seeking during ads. This is complicated to do in
|
// 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);
|
maskWindowIndexAndPositionForSeek(timeline, windowIndex, positionMs);
|
||||||
@Player.State
|
@Player.State
|
||||||
int newPlaybackState =
|
int newPlaybackState =
|
||||||
playbackState == Player.STATE_IDLE ? Player.STATE_IDLE : Player.STATE_BUFFERING;
|
getPlaybackState() == Player.STATE_IDLE ? Player.STATE_IDLE : Player.STATE_BUFFERING;
|
||||||
boolean playbackStateChanged = playbackState != newPlaybackState;
|
PlaybackInfo playbackInfo = this.playbackInfo.copyWithPlaybackState(newPlaybackState);
|
||||||
playbackInfo = playbackInfo.copyWithPlaybackState(newPlaybackState);
|
|
||||||
internalPlayer.seekTo(timeline, windowIndex, C.msToUs(positionMs));
|
internalPlayer.seekTo(timeline, windowIndex, C.msToUs(positionMs));
|
||||||
notifyListeners(
|
updatePlaybackInfo(
|
||||||
listener -> {
|
playbackInfo,
|
||||||
listener.onPositionDiscontinuity(DISCONTINUITY_REASON_SEEK);
|
/* positionDiscontinuity= */ true,
|
||||||
if (playbackStateChanged) {
|
/* positionDiscontinuityReason= */ DISCONTINUITY_REASON_SEEK,
|
||||||
listener.onPlayerStateChanged(playWhenReady, newPlaybackState);
|
/* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||||
listener.onPlaybackStateChanged(newPlaybackState);
|
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||||
}
|
/* seekProcessed= */ false);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated Use {@link #setPlaybackSpeed(float)} instead. */
|
/** @deprecated Use {@link #setPlaybackSpeed(float)} instead. */
|
||||||
@ -1011,7 +1009,6 @@ import java.util.concurrent.TimeoutException;
|
|||||||
seekProcessed));
|
seekProcessed));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private void setMediaSourcesInternal(
|
private void setMediaSourcesInternal(
|
||||||
List<MediaSource> mediaItems,
|
List<MediaSource> mediaItems,
|
||||||
int startWindowIndex,
|
int startWindowIndex,
|
||||||
@ -1019,14 +1016,14 @@ import java.util.concurrent.TimeoutException;
|
|||||||
boolean resetToDefaultPosition) {
|
boolean resetToDefaultPosition) {
|
||||||
int currentWindowIndex = getCurrentWindowIndexInternal();
|
int currentWindowIndex = getCurrentWindowIndexInternal();
|
||||||
long currentPositionMs = getCurrentPosition();
|
long currentPositionMs = getCurrentPosition();
|
||||||
boolean currentPlayWhenReady = getPlayWhenReady();
|
|
||||||
pendingOperationAcks++;
|
pendingOperationAcks++;
|
||||||
if (!mediaSourceHolders.isEmpty()) {
|
if (!mediaSourceHolders.isEmpty()) {
|
||||||
removeMediaSourceHolders(
|
removeMediaSourceHolders(
|
||||||
/* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolders.size());
|
/* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolders.size());
|
||||||
}
|
}
|
||||||
List<Playlist.MediaSourceHolder> holders = addMediaSourceHolders(/* index= */ 0, mediaItems);
|
List<Playlist.MediaSourceHolder> holders = addMediaSourceHolders(/* index= */ 0, mediaItems);
|
||||||
Timeline timeline = maskTimeline();
|
PlaybackInfo playbackInfo = maskTimeline();
|
||||||
|
Timeline timeline = playbackInfo.timeline;
|
||||||
if (!timeline.isEmpty() && startWindowIndex >= timeline.getWindowCount()) {
|
if (!timeline.isEmpty() && startWindowIndex >= timeline.getWindowCount()) {
|
||||||
throw new IllegalSeekPositionException(timeline, startWindowIndex, startPositionMs);
|
throw new IllegalSeekPositionException(timeline, startWindowIndex, startPositionMs);
|
||||||
}
|
}
|
||||||
@ -1040,9 +1037,9 @@ import java.util.concurrent.TimeoutException;
|
|||||||
}
|
}
|
||||||
maskWindowIndexAndPositionForSeek(
|
maskWindowIndexAndPositionForSeek(
|
||||||
timeline, startWindowIndex == C.INDEX_UNSET ? 0 : startWindowIndex, startPositionMs);
|
timeline, startWindowIndex == C.INDEX_UNSET ? 0 : startWindowIndex, startPositionMs);
|
||||||
// mask the playback state
|
// Mask the playback state.
|
||||||
int maskingPlaybackState = playbackInfo.playbackState;
|
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).
|
// Position reset to startWindowIndex (results in pending initial seek).
|
||||||
if (timeline.isEmpty() || startWindowIndex >= timeline.getWindowCount()) {
|
if (timeline.isEmpty() || startWindowIndex >= timeline.getWindowCount()) {
|
||||||
// Setting an empty timeline or invalid seek transitions to ended.
|
// Setting an empty timeline or invalid seek transitions to ended.
|
||||||
@ -1051,23 +1048,16 @@ import java.util.concurrent.TimeoutException;
|
|||||||
maskingPlaybackState = STATE_BUFFERING;
|
maskingPlaybackState = STATE_BUFFERING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean playbackStateChanged =
|
playbackInfo = playbackInfo.copyWithPlaybackState(maskingPlaybackState);
|
||||||
playbackInfo.playbackState != STATE_IDLE
|
|
||||||
&& playbackInfo.playbackState != maskingPlaybackState;
|
|
||||||
int finalMaskingPlaybackState = maskingPlaybackState;
|
|
||||||
if (playbackStateChanged) {
|
|
||||||
playbackInfo = playbackInfo.copyWithPlaybackState(finalMaskingPlaybackState);
|
|
||||||
}
|
|
||||||
internalPlayer.setMediaSources(
|
internalPlayer.setMediaSources(
|
||||||
holders, startWindowIndex, C.msToUs(startPositionMs), shuffleOrder);
|
holders, startWindowIndex, C.msToUs(startPositionMs), shuffleOrder);
|
||||||
notifyListeners(
|
updatePlaybackInfo(
|
||||||
listener -> {
|
playbackInfo,
|
||||||
listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
/* positionDiscontinuity= */ false,
|
||||||
if (playbackStateChanged) {
|
/* ignored */ Player.DISCONTINUITY_REASON_INTERNAL,
|
||||||
listener.onPlayerStateChanged(currentPlayWhenReady, finalMaskingPlaybackState);
|
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||||
listener.onPlaybackStateChanged(finalMaskingPlaybackState);
|
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||||
}
|
/* seekProcessed= */ false);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Playlist.MediaSourceHolder> addMediaSourceHolders(
|
private List<Playlist.MediaSourceHolder> addMediaSourceHolders(
|
||||||
@ -1085,18 +1075,16 @@ import java.util.concurrent.TimeoutException;
|
|||||||
return holders;
|
return holders;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
private void removeMediaItemsInternal(int fromIndex, int toIndex) {
|
private void removeMediaItemsInternal(int fromIndex, int toIndex) {
|
||||||
Assertions.checkArgument(
|
Assertions.checkArgument(
|
||||||
fromIndex >= 0 && toIndex >= fromIndex && toIndex <= mediaSourceHolders.size());
|
fromIndex >= 0 && toIndex >= fromIndex && toIndex <= mediaSourceHolders.size());
|
||||||
int currentWindowIndex = getCurrentWindowIndex();
|
int currentWindowIndex = getCurrentWindowIndex();
|
||||||
long currentPositionMs = getCurrentPosition();
|
long currentPositionMs = getCurrentPosition();
|
||||||
boolean currentPlayWhenReady = getPlayWhenReady();
|
|
||||||
Timeline oldTimeline = getCurrentTimeline();
|
Timeline oldTimeline = getCurrentTimeline();
|
||||||
int currentMediaSourceCount = mediaSourceHolders.size();
|
int currentMediaSourceCount = mediaSourceHolders.size();
|
||||||
pendingOperationAcks++;
|
pendingOperationAcks++;
|
||||||
removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */ toIndex);
|
removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */ toIndex);
|
||||||
Timeline timeline =
|
PlaybackInfo playbackInfo =
|
||||||
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
|
maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline);
|
||||||
// Player transitions to STATE_ENDED if the current index is part of the removed tail.
|
// Player transitions to STATE_ENDED if the current index is part of the removed tail.
|
||||||
final boolean transitionsToEnded =
|
final boolean transitionsToEnded =
|
||||||
@ -1104,19 +1092,18 @@ import java.util.concurrent.TimeoutException;
|
|||||||
&& playbackInfo.playbackState != STATE_ENDED
|
&& playbackInfo.playbackState != STATE_ENDED
|
||||||
&& fromIndex < toIndex
|
&& fromIndex < toIndex
|
||||||
&& toIndex == currentMediaSourceCount
|
&& toIndex == currentMediaSourceCount
|
||||||
&& currentWindowIndex >= timeline.getWindowCount();
|
&& currentWindowIndex >= playbackInfo.timeline.getWindowCount();
|
||||||
if (transitionsToEnded) {
|
if (transitionsToEnded) {
|
||||||
playbackInfo = playbackInfo.copyWithPlaybackState(STATE_ENDED);
|
playbackInfo = playbackInfo.copyWithPlaybackState(STATE_ENDED);
|
||||||
}
|
}
|
||||||
internalPlayer.removeMediaSources(fromIndex, toIndex, shuffleOrder);
|
internalPlayer.removeMediaSources(fromIndex, toIndex, shuffleOrder);
|
||||||
notifyListeners(
|
updatePlaybackInfo(
|
||||||
listener -> {
|
playbackInfo,
|
||||||
listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
/* positionDiscontinuity= */ false,
|
||||||
if (transitionsToEnded) {
|
/* ignored */ Player.DISCONTINUITY_REASON_INTERNAL,
|
||||||
listener.onPlayerStateChanged(currentPlayWhenReady, STATE_ENDED);
|
/* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||||
listener.onPlaybackStateChanged(STATE_ENDED);
|
/* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST,
|
||||||
}
|
/* seekProcessed= */ false);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Playlist.MediaSourceHolder> removeMediaSourceHolders(
|
private List<Playlist.MediaSourceHolder> removeMediaSourceHolders(
|
||||||
@ -1129,18 +1116,17 @@ import java.util.concurrent.TimeoutException;
|
|||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Timeline maskTimeline() {
|
private PlaybackInfo maskTimeline() {
|
||||||
playbackInfo =
|
return playbackInfo.copyWithTimeline(
|
||||||
playbackInfo.copyWithTimeline(
|
|
||||||
mediaSourceHolders.isEmpty()
|
mediaSourceHolders.isEmpty()
|
||||||
? Timeline.EMPTY
|
? Timeline.EMPTY
|
||||||
: new Playlist.PlaylistTimeline(mediaSourceHolders, shuffleOrder));
|
: new Playlist.PlaylistTimeline(mediaSourceHolders, shuffleOrder));
|
||||||
return playbackInfo.timeline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Timeline maskTimelineAndWindowIndex(
|
private PlaybackInfo maskTimelineAndWindowIndex(
|
||||||
int currentWindowIndex, long currentPositionMs, Timeline oldTimeline) {
|
int currentWindowIndex, long currentPositionMs, Timeline oldTimeline) {
|
||||||
Timeline maskingTimeline = maskTimeline();
|
PlaybackInfo playbackInfo = maskTimeline();
|
||||||
|
Timeline maskingTimeline = playbackInfo.timeline;
|
||||||
if (oldTimeline.isEmpty()) {
|
if (oldTimeline.isEmpty()) {
|
||||||
// The index is the default index or was set by a seek in the empty old timeline.
|
// The index is the default index or was set by a seek in the empty old timeline.
|
||||||
maskingWindowIndex = currentWindowIndex;
|
maskingWindowIndex = currentWindowIndex;
|
||||||
@ -1148,7 +1134,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
// The seek is not valid in the new timeline.
|
// The seek is not valid in the new timeline.
|
||||||
maskWithDefaultPosition(maskingTimeline);
|
maskWithDefaultPosition(maskingTimeline);
|
||||||
}
|
}
|
||||||
return maskingTimeline;
|
return playbackInfo;
|
||||||
}
|
}
|
||||||
@Nullable
|
@Nullable
|
||||||
Pair<Object, Long> periodPosition =
|
Pair<Object, Long> periodPosition =
|
||||||
@ -1186,7 +1172,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
maskWithDefaultPosition(maskingTimeline);
|
maskWithDefaultPosition(maskingTimeline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return maskingTimeline;
|
return playbackInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maskWindowIndexAndPositionForSeek(
|
private void maskWindowIndexAndPositionForSeek(
|
||||||
|
@ -3391,7 +3391,6 @@ public final class ExoPlayerTest {
|
|||||||
.blockUntilActionScheduleFinished(TIMEOUT_MS)
|
.blockUntilActionScheduleFinished(TIMEOUT_MS)
|
||||||
.blockUntilEnded(TIMEOUT_MS);
|
.blockUntilEnded(TIMEOUT_MS);
|
||||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
|
||||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
|
||||||
assertArrayEquals(new long[] {2}, windowCounts);
|
assertArrayEquals(new long[] {2}, windowCounts);
|
||||||
assertArrayEquals(new int[] {seekToWindowIndex}, currentWindowIndices);
|
assertArrayEquals(new int[] {seekToWindowIndex}, currentWindowIndices);
|
||||||
@ -4157,7 +4156,7 @@ public final class ExoPlayerTest {
|
|||||||
int seekToWindowIndex = 1;
|
int seekToWindowIndex = 1;
|
||||||
ActionSchedule actionSchedule =
|
ActionSchedule actionSchedule =
|
||||||
new ActionSchedule.Builder(TAG)
|
new ActionSchedule.Builder(TAG)
|
||||||
.waitForTimelineChanged()
|
.waitForPlaybackState(Player.STATE_ENDED)
|
||||||
.executeRunnable(
|
.executeRunnable(
|
||||||
new PlayerRunnable() {
|
new PlayerRunnable() {
|
||||||
@Override
|
@Override
|
||||||
@ -4168,12 +4167,11 @@ public final class ExoPlayerTest {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.executeRunnable(
|
.executeRunnable(
|
||||||
() -> {
|
() ->
|
||||||
concatenatingMediaSource.addMediaSource(
|
concatenatingMediaSource.addMediaSources(
|
||||||
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT));
|
Arrays.asList(
|
||||||
concatenatingMediaSource.addMediaSource(
|
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT),
|
||||||
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT));
|
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT))))
|
||||||
})
|
|
||||||
.waitForTimelineChanged()
|
.waitForTimelineChanged()
|
||||||
.executeRunnable(
|
.executeRunnable(
|
||||||
new PlayerRunnable() {
|
new PlayerRunnable() {
|
||||||
@ -4203,7 +4201,7 @@ public final class ExoPlayerTest {
|
|||||||
ConcatenatingMediaSource concatenatingMediaSource =
|
ConcatenatingMediaSource concatenatingMediaSource =
|
||||||
new ConcatenatingMediaSource(/* isAtomic= */ false);
|
new ConcatenatingMediaSource(/* isAtomic= */ false);
|
||||||
ActionSchedule actionSchedule =
|
ActionSchedule actionSchedule =
|
||||||
new ActionSchedule.Builder(TAG).waitForTimelineChanged().build();
|
new ActionSchedule.Builder(TAG).waitForPlaybackState(Player.STATE_ENDED).build();
|
||||||
ExoPlayerTestRunner exoPlayerTestRunner =
|
ExoPlayerTestRunner exoPlayerTestRunner =
|
||||||
new Builder()
|
new Builder()
|
||||||
.setMediaSources(concatenatingMediaSource)
|
.setMediaSources(concatenatingMediaSource)
|
||||||
@ -4229,7 +4227,7 @@ public final class ExoPlayerTest {
|
|||||||
int seekToWindowIndex = 1;
|
int seekToWindowIndex = 1;
|
||||||
ActionSchedule actionSchedule =
|
ActionSchedule actionSchedule =
|
||||||
new ActionSchedule.Builder(TAG)
|
new ActionSchedule.Builder(TAG)
|
||||||
.waitForTimelineChanged()
|
.waitForPlaybackState(Player.STATE_ENDED)
|
||||||
.executeRunnable(
|
.executeRunnable(
|
||||||
new PlayerRunnable() {
|
new PlayerRunnable() {
|
||||||
@Override
|
@Override
|
||||||
@ -4240,12 +4238,11 @@ public final class ExoPlayerTest {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.executeRunnable(
|
.executeRunnable(
|
||||||
() -> {
|
() ->
|
||||||
concatenatingMediaSource.addMediaSource(
|
concatenatingMediaSource.addMediaSources(
|
||||||
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT));
|
Arrays.asList(
|
||||||
concatenatingMediaSource.addMediaSource(
|
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT),
|
||||||
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT));
|
new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT))))
|
||||||
})
|
|
||||||
.waitForTimelineChanged()
|
.waitForTimelineChanged()
|
||||||
.executeRunnable(
|
.executeRunnable(
|
||||||
new PlayerRunnable() {
|
new PlayerRunnable() {
|
||||||
@ -4727,7 +4724,6 @@ public final class ExoPlayerTest {
|
|||||||
new int[] {Player.STATE_IDLE, Player.STATE_IDLE, Player.STATE_IDLE, Player.STATE_IDLE},
|
new int[] {Player.STATE_IDLE, Player.STATE_IDLE, Player.STATE_IDLE, Player.STATE_IDLE},
|
||||||
maskingPlaybackStates);
|
maskingPlaybackStates);
|
||||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
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_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);
|
Player.STATE_BUFFERING, Player.STATE_READY, Player.STATE_ENDED);
|
||||||
assertArrayEquals(new int[] {Player.STATE_IDLE}, maskingPlaybackStates);
|
assertArrayEquals(new int[] {Player.STATE_IDLE}, maskingPlaybackStates);
|
||||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||||
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_SOURCE_UPDATE);
|
||||||
}
|
}
|
||||||
@ -4841,7 +4836,6 @@ public final class ExoPlayerTest {
|
|||||||
Player.STATE_BUFFERING, Player.STATE_READY, Player.STATE_ENDED);
|
Player.STATE_BUFFERING, Player.STATE_READY, Player.STATE_ENDED);
|
||||||
assertArrayEquals(new int[] {Player.STATE_IDLE}, maskingPlaybackStates);
|
assertArrayEquals(new int[] {Player.STATE_IDLE}, maskingPlaybackStates);
|
||||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||||
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_SOURCE_UPDATE);
|
||||||
}
|
}
|
||||||
@ -4924,9 +4918,6 @@ public final class ExoPlayerTest {
|
|||||||
},
|
},
|
||||||
maskingPlaybackStates);
|
maskingPlaybackStates);
|
||||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
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_PLAYLIST_CHANGED,
|
||||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE,
|
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE,
|
||||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||||
@ -4969,7 +4960,6 @@ public final class ExoPlayerTest {
|
|||||||
exoPlayerTestRunner.assertPlaybackStatesEqual(Player.STATE_ENDED);
|
exoPlayerTestRunner.assertPlaybackStatesEqual(Player.STATE_ENDED);
|
||||||
assertArrayEquals(new int[] {Player.STATE_ENDED}, maskingPlaybackStates);
|
assertArrayEquals(new int[] {Player.STATE_ENDED}, maskingPlaybackStates);
|
||||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||||
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_SOURCE_UPDATE);
|
||||||
}
|
}
|
||||||
@ -5050,7 +5040,6 @@ public final class ExoPlayerTest {
|
|||||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||||
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE,
|
Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE,
|
||||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
|
||||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5213,7 +5202,6 @@ public final class ExoPlayerTest {
|
|||||||
Player.STATE_ENDED);
|
Player.STATE_ENDED);
|
||||||
assertArrayEquals(new int[] {Player.STATE_ENDED}, maskingPlaybackStates);
|
assertArrayEquals(new int[] {Player.STATE_ENDED}, maskingPlaybackStates);
|
||||||
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
exoPlayerTestRunner.assertTimelineChangeReasonsEqual(
|
||||||
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_SOURCE_UPDATE,
|
||||||
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED,
|
||||||
@ -6161,6 +6149,7 @@ public final class ExoPlayerTest {
|
|||||||
public void loading_withLargeAllocationCausingOom_playsRemainingMediaAndThenThrows() {
|
public void loading_withLargeAllocationCausingOom_playsRemainingMediaAndThenThrows() {
|
||||||
Loader.Loadable loadable =
|
Loader.Loadable loadable =
|
||||||
new Loader.Loadable() {
|
new Loader.Loadable() {
|
||||||
|
@SuppressWarnings("UnusedVariable")
|
||||||
@Override
|
@Override
|
||||||
public void load() throws IOException {
|
public void load() throws IOException {
|
||||||
@SuppressWarnings("unused") // This test needs the allocation to cause an OOM.
|
@SuppressWarnings("unused") // This test needs the allocation to cause an OOM.
|
||||||
@ -6229,6 +6218,97 @@ public final class ExoPlayerTest {
|
|||||||
assertThat(renderer.sampleBufferReadCount).isEqualTo(3);
|
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.
|
// Internal methods.
|
||||||
|
|
||||||
private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {
|
private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user