use of androidx.media3.common.Player.STATE_ENDED in project media by androidx.
the class MediaControllerStateMaskingTest method removeMediaItems_removedTailIncludesCurrentItem_callsOnPlaybackStateChanged.
@Test
public void removeMediaItems_removedTailIncludesCurrentItem_callsOnPlaybackStateChanged() throws Exception {
int initialMediaItemIndex = 1;
int initialPlaybackState = STATE_READY;
int testFromIndex = 1;
int testToIndex = 3;
int testPlaybackState = STATE_ENDED;
Timeline testTimeline = createTimeline(createMediaItems(/* size= */
3));
Bundle playerConfig = new RemoteMediaSession.MockPlayerConfigBuilder().setTimeline(testTimeline).setCurrentMediaItemIndex(initialMediaItemIndex).setCurrentPeriodIndex(initialMediaItemIndex).setPlaybackState(initialPlaybackState).build();
remoteSession.setPlayer(playerConfig);
MediaController controller = controllerTestRule.createController(remoteSession.getToken());
CountDownLatch latch = new CountDownLatch(1);
AtomicInteger newPlaybackStateRef = new AtomicInteger();
Player.Listener listener = new Player.Listener() {
@Override
public void onPlaybackStateChanged(int playbackState) {
newPlaybackStateRef.set(playbackState);
latch.countDown();
}
};
threadTestRule.getHandler().postAndSync(() -> controller.addListener(listener));
threadTestRule.getHandler().postAndSync(() -> {
controller.removeMediaItems(/* fromIndex= */
testFromIndex, /* toIndex= */
testToIndex);
});
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(newPlaybackStateRef.get()).isEqualTo(testPlaybackState);
}
use of androidx.media3.common.Player.STATE_ENDED in project media by androidx.
the class MediaControllerStateMaskingTest method removeMediaItems_removeAllItems.
@Test
public void removeMediaItems_removeAllItems() throws Exception {
int initialMediaItemIndex = 1;
int initialPlaybackState = STATE_READY;
long initialCurrentPosition = 3_000;
String firstMediaId = "firstMediaId";
String secondMediaId = "secondMediaId";
String thirdMediaId = "thirdMediaId";
int testFromIndex = 0;
int testToIndex = 3;
int testMediaItemCount = 0;
int testCurrentMediaItemIndex = 0;
int testNextMediaItemIndex = C.INDEX_UNSET;
int testPreviousMediaItemIndex = C.INDEX_UNSET;
int testPlaybackState = STATE_ENDED;
long testCurrentPosition = 0;
Bundle playerConfig = new RemoteMediaSession.MockPlayerConfigBuilder().setTimeline(MediaTestUtils.createTimeline(createMediaItems(firstMediaId, secondMediaId, thirdMediaId))).setCurrentMediaItemIndex(initialMediaItemIndex).setCurrentPeriodIndex(initialMediaItemIndex).setCurrentPosition(initialCurrentPosition).setPlaybackState(initialPlaybackState).build();
remoteSession.setPlayer(playerConfig);
MediaController controller = controllerTestRule.createController(remoteSession.getToken());
CountDownLatch latch = new CountDownLatch(4);
AtomicReference<Timeline> newTimelineRef = new AtomicReference<>();
AtomicReference<MediaItem> newMediaItemRef = new AtomicReference<>();
AtomicInteger newPlaybackStateRef = new AtomicInteger();
AtomicReference<Player.Events> onEventsRef = new AtomicReference<>();
Player.Listener listener = new Player.Listener() {
@Override
public void onTimelineChanged(Timeline timeline, int reason) {
newTimelineRef.set(timeline);
latch.countDown();
}
@Override
public void onMediaItemTransition(@Nullable MediaItem mediaItem, int reason) {
newMediaItemRef.set(mediaItem);
latch.countDown();
}
@Override
public void onPlaybackStateChanged(int playbackState) {
newPlaybackStateRef.set(playbackState);
latch.countDown();
}
@Override
public void onEvents(Player player, Player.Events events) {
onEventsRef.set(events);
latch.countDown();
}
};
threadTestRule.getHandler().postAndSync(() -> controller.addListener(listener));
AtomicInteger currentMediaItemIndexRef = new AtomicInteger();
AtomicInteger nextMediaItemIndexRef = new AtomicInteger();
AtomicInteger previousMediaItemIndexRef = new AtomicInteger();
AtomicLong newCurrentPositionRef = new AtomicLong();
threadTestRule.getHandler().postAndSync(() -> {
controller.removeMediaItems(/* fromIndex= */
testFromIndex, /* toIndex= */
testToIndex);
currentMediaItemIndexRef.set(controller.getCurrentMediaItemIndex());
nextMediaItemIndexRef.set(controller.getNextMediaItemIndex());
previousMediaItemIndexRef.set(controller.getPreviousMediaItemIndex());
newCurrentPositionRef.set(controller.getCurrentPosition());
});
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertTimeline(newTimelineRef.get(), testMediaItemCount, /* ignored= */
C.INDEX_UNSET, /* ignored= */
C.INDEX_UNSET, /* ignored= */
C.INDEX_UNSET);
assertThat(newMediaItemRef.get()).isNull();
assertThat(newPlaybackStateRef.get()).isEqualTo(testPlaybackState);
assertThat(onEventsRef.get().contains(Player.EVENT_TIMELINE_CHANGED)).isTrue();
assertThat(onEventsRef.get().contains(Player.EVENT_MEDIA_ITEM_TRANSITION)).isTrue();
assertThat(onEventsRef.get().contains(Player.EVENT_PLAYBACK_STATE_CHANGED)).isTrue();
assertThat(currentMediaItemIndexRef.get()).isEqualTo(testCurrentMediaItemIndex);
assertThat(nextMediaItemIndexRef.get()).isEqualTo(testNextMediaItemIndex);
assertThat(previousMediaItemIndexRef.get()).isEqualTo(testPreviousMediaItemIndex);
assertThat(newCurrentPositionRef.get()).isEqualTo(testCurrentPosition);
}
use of androidx.media3.common.Player.STATE_ENDED in project media by androidx.
the class ExoPlayerImpl method setMediaSourcesInternal.
private void setMediaSourcesInternal(List<MediaSource> mediaSources, int startWindowIndex, long startPositionMs, boolean resetToDefaultPosition) {
int currentWindowIndex = getCurrentWindowIndexInternal();
long currentPositionMs = getCurrentPosition();
pendingOperationAcks++;
if (!mediaSourceHolderSnapshots.isEmpty()) {
removeMediaSourceHolders(/* fromIndex= */
0, /* toIndexExclusive= */
mediaSourceHolderSnapshots.size());
}
List<MediaSourceList.MediaSourceHolder> holders = addMediaSourceHolders(/* index= */
0, mediaSources);
Timeline timeline = createMaskingTimeline();
if (!timeline.isEmpty() && startWindowIndex >= timeline.getWindowCount()) {
throw new IllegalSeekPositionException(timeline, startWindowIndex, startPositionMs);
}
// Evaluate the actual start position.
if (resetToDefaultPosition) {
startWindowIndex = timeline.getFirstWindowIndex(shuffleModeEnabled);
startPositionMs = C.TIME_UNSET;
} else if (startWindowIndex == C.INDEX_UNSET) {
startWindowIndex = currentWindowIndex;
startPositionMs = currentPositionMs;
}
PlaybackInfo newPlaybackInfo = maskTimelineAndPosition(playbackInfo, timeline, maskWindowPositionMsOrGetPeriodPositionUs(timeline, startWindowIndex, startPositionMs));
// Mask the playback state.
int maskingPlaybackState = newPlaybackInfo.playbackState;
if (startWindowIndex != C.INDEX_UNSET && newPlaybackInfo.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.
maskingPlaybackState = STATE_ENDED;
} else {
maskingPlaybackState = STATE_BUFFERING;
}
}
newPlaybackInfo = newPlaybackInfo.copyWithPlaybackState(maskingPlaybackState);
internalPlayer.setMediaSources(holders, startWindowIndex, Util.msToUs(startPositionMs), shuffleOrder);
boolean positionDiscontinuity = !playbackInfo.periodId.periodUid.equals(newPlaybackInfo.periodId.periodUid) && !playbackInfo.timeline.isEmpty();
updatePlaybackInfo(newPlaybackInfo, /* timelineChangeReason= */
TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, /* ignored */
PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, /* seekProcessed= */
false, /* positionDiscontinuity= */
positionDiscontinuity, DISCONTINUITY_REASON_REMOVE, /* discontinuityWindowStartPositionUs= */
getCurrentPositionUsInternal(newPlaybackInfo), /* ignored */
C.INDEX_UNSET);
}
use of androidx.media3.common.Player.STATE_ENDED in project media by androidx.
the class ExoPlayerTest method onPlayerErrorChanged_isNotifiedForNullError.
@Test
public void onPlayerErrorChanged_isNotifiedForNullError() throws Exception {
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addMediaSource(new FakeMediaSource(/* timeline= */
null) {
@Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
throw new IOException();
}
});
Player.Listener mockListener = mock(Player.Listener.class);
player.addListener(mockListener);
player.prepare();
player.play();
ExoPlaybackException error = TestPlayerRunHelper.runUntilError(player);
// The media source fails preparation, so we expect both methods to be called.
verify(mockListener).onPlayerErrorChanged(error);
verify(mockListener).onPlayerError(error);
reset(mockListener);
player.setMediaSource(new FakeMediaSource());
player.prepare();
player.play();
TestPlayerRunHelper.runUntilPlaybackState(player, STATE_ENDED);
// Now the player, which had a playback error, was re-prepared causing the error to be cleared.
// We expect the change to null to be notified, but not onPlayerError.
verify(mockListener).onPlayerErrorChanged(ArgumentMatchers.isNull());
verify(mockListener, never()).onPlayerError(any());
}
use of androidx.media3.common.Player.STATE_ENDED in project media by androidx.
the class ExoPlayerImpl method removeMediaItemsInternal.
private PlaybackInfo removeMediaItemsInternal(int fromIndex, int toIndex) {
Assertions.checkArgument(fromIndex >= 0 && toIndex >= fromIndex && toIndex <= mediaSourceHolderSnapshots.size());
int currentIndex = getCurrentMediaItemIndex();
Timeline oldTimeline = getCurrentTimeline();
int currentMediaSourceCount = mediaSourceHolderSnapshots.size();
pendingOperationAcks++;
removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */
toIndex);
Timeline newTimeline = createMaskingTimeline();
PlaybackInfo newPlaybackInfo = maskTimelineAndPosition(playbackInfo, newTimeline, getPeriodPositionUsAfterTimelineChanged(oldTimeline, newTimeline));
// Player transitions to STATE_ENDED if the current index is part of the removed tail.
final boolean transitionsToEnded = newPlaybackInfo.playbackState != STATE_IDLE && newPlaybackInfo.playbackState != STATE_ENDED && fromIndex < toIndex && toIndex == currentMediaSourceCount && currentIndex >= newPlaybackInfo.timeline.getWindowCount();
if (transitionsToEnded) {
newPlaybackInfo = newPlaybackInfo.copyWithPlaybackState(STATE_ENDED);
}
internalPlayer.removeMediaSources(fromIndex, toIndex, shuffleOrder);
return newPlaybackInfo;
}
Aggregations