use of androidx.media3.common.Player.DiscontinuityReason in project media by androidx.
the class ExoPlayerTest method periodTransitionReportsCorrectBufferedPosition.
@Test
public void periodTransitionReportsCorrectBufferedPosition() throws Exception {
int periodCount = 3;
long periodDurationUs = 5 * C.MICROS_PER_SECOND;
long windowDurationUs = periodCount * periodDurationUs;
Timeline timeline = new FakeTimeline(new TimelineWindowDefinition(periodCount, /* id= */
new Object(), /* isSeekable= */
true, /* isDynamic= */
false, windowDurationUs));
AtomicReference<Player> playerReference = new AtomicReference<>();
AtomicLong bufferedPositionAtFirstDiscontinuityMs = new AtomicLong(C.TIME_UNSET);
Player.Listener playerListener = new Player.Listener() {
@Override
public void onPositionDiscontinuity(@DiscontinuityReason int reason) {
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
if (bufferedPositionAtFirstDiscontinuityMs.get() == C.TIME_UNSET) {
bufferedPositionAtFirstDiscontinuityMs.set(playerReference.get().getBufferedPosition());
}
}
}
};
ActionSchedule actionSchedule = new ActionSchedule.Builder(TAG).executeRunnable(new PlayerRunnable() {
@Override
public void run(ExoPlayer player) {
playerReference.set(player);
player.addListener(playerListener);
}
}).pause().waitForIsLoading(/* targetIsLoading= */
true).waitForIsLoading(/* targetIsLoading= */
false).play().build();
new ExoPlayerTestRunner.Builder(context).setTimeline(timeline).setActionSchedule(actionSchedule).build().start().blockUntilEnded(TIMEOUT_MS);
assertThat(bufferedPositionAtFirstDiscontinuityMs.get()).isEqualTo(Util.usToMs(windowDurationUs));
}
use of androidx.media3.common.Player.DiscontinuityReason in project media by androidx.
the class ExoPlayerTest method contentWithoutInitialSeekStartsAtDefaultPositionAfterPrerollAd.
@Test
public void contentWithoutInitialSeekStartsAtDefaultPositionAfterPrerollAd() throws Exception {
AdPlaybackState adPlaybackState = FakeTimeline.createAdPlaybackState(/* adsPerAdGroup= */
3, /* adGroupTimesUs...= */
0);
Timeline fakeTimeline = new FakeTimeline(new TimelineWindowDefinition(/* periodCount= */
1, /* id= */
0, /* isSeekable= */
true, /* isDynamic= */
false, /* isLive= */
false, /* isPlaceholder= */
false, /* durationUs= */
10_000_000, /* defaultPositionUs= */
5_000_000, TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US, adPlaybackState));
FakeMediaSource fakeMediaSource = new FakeMediaSource(/* timeline= */
null);
AtomicReference<Player> playerReference = new AtomicReference<>();
AtomicLong contentStartPositionMs = new AtomicLong(C.TIME_UNSET);
Player.Listener playerListener = new Player.Listener() {
@Override
public void onPositionDiscontinuity(@DiscontinuityReason int reason) {
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
contentStartPositionMs.set(playerReference.get().getContentPosition());
}
}
};
ActionSchedule actionSchedule = new ActionSchedule.Builder(TAG).executeRunnable(new PlayerRunnable() {
@Override
public void run(ExoPlayer player) {
playerReference.set(player);
player.addListener(playerListener);
}
}).waitForPlaybackState(Player.STATE_BUFFERING).executeRunnable(() -> fakeMediaSource.setNewSourceInfo(fakeTimeline)).build();
new ExoPlayerTestRunner.Builder(context).setMediaSources(fakeMediaSource).setActionSchedule(actionSchedule).build().start().blockUntilEnded(TIMEOUT_MS);
assertThat(contentStartPositionMs.get()).isAtLeast(5_000L);
}
use of androidx.media3.common.Player.DiscontinuityReason in project media by androidx.
the class ExoPlayerImplInternal method handlePositionDiscontinuity.
@CheckResult
private PlaybackInfo handlePositionDiscontinuity(MediaPeriodId mediaPeriodId, long positionUs, long requestedContentPositionUs, long discontinuityStartPositionUs, boolean reportDiscontinuity, @DiscontinuityReason int discontinuityReason) {
deliverPendingMessageAtStartPositionRequired = deliverPendingMessageAtStartPositionRequired || positionUs != playbackInfo.positionUs || !mediaPeriodId.equals(playbackInfo.periodId);
resetPendingPauseAtEndOfPeriod();
TrackGroupArray trackGroupArray = playbackInfo.trackGroups;
TrackSelectorResult trackSelectorResult = playbackInfo.trackSelectorResult;
List<Metadata> staticMetadata = playbackInfo.staticMetadata;
if (mediaSourceList.isPrepared()) {
@Nullable MediaPeriodHolder playingPeriodHolder = queue.getPlayingPeriod();
trackGroupArray = playingPeriodHolder == null ? TrackGroupArray.EMPTY : playingPeriodHolder.getTrackGroups();
trackSelectorResult = playingPeriodHolder == null ? emptyTrackSelectorResult : playingPeriodHolder.getTrackSelectorResult();
staticMetadata = extractMetadataFromTrackSelectionArray(trackSelectorResult.selections);
// Ensure the media period queue requested content position matches the new playback info.
if (playingPeriodHolder != null && playingPeriodHolder.info.requestedContentPositionUs != requestedContentPositionUs) {
playingPeriodHolder.info = playingPeriodHolder.info.copyWithRequestedContentPositionUs(requestedContentPositionUs);
}
} else if (!mediaPeriodId.equals(playbackInfo.periodId)) {
// Reset previously kept track info if unprepared and the period changes.
trackGroupArray = TrackGroupArray.EMPTY;
trackSelectorResult = emptyTrackSelectorResult;
staticMetadata = ImmutableList.of();
}
if (reportDiscontinuity) {
playbackInfoUpdate.setPositionDiscontinuity(discontinuityReason);
}
return playbackInfo.copyWithNewPosition(mediaPeriodId, positionUs, requestedContentPositionUs, discontinuityStartPositionUs, getTotalBufferedDurationUs(), trackGroupArray, trackSelectorResult, staticMetadata);
}
use of androidx.media3.common.Player.DiscontinuityReason in project media by androidx.
the class MediaControllerImplLegacy method updateControllerInfo.
private void updateControllerInfo(boolean notifyConnected, LegacyPlayerInfo newLegacyPlayerInfo, ControllerInfo newControllerInfo, @Nullable @Player.DiscontinuityReason Integer discontinuityReason, @Nullable @Player.MediaItemTransitionReason Integer mediaItemTransitionReason) {
LegacyPlayerInfo oldLegacyPlayerInfo = legacyPlayerInfo;
ControllerInfo oldControllerInfo = controllerInfo;
if (legacyPlayerInfo != newLegacyPlayerInfo) {
legacyPlayerInfo = new LegacyPlayerInfo(newLegacyPlayerInfo);
}
pendingLegacyPlayerInfo = legacyPlayerInfo;
controllerInfo = newControllerInfo;
if (notifyConnected) {
instance.notifyAccepted();
if (!oldControllerInfo.customLayout.equals(newControllerInfo.customLayout)) {
instance.notifyControllerListener(listener -> ignoreFuture(listener.onSetCustomLayout(instance, newControllerInfo.customLayout)));
}
return;
}
if (!oldControllerInfo.playerInfo.timeline.equals(newControllerInfo.playerInfo.timeline)) {
listeners.queueEvent(EVENT_TIMELINE_CHANGED, (listener) -> listener.onTimelineChanged(newControllerInfo.playerInfo.timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED));
}
if (!Util.areEqual(oldLegacyPlayerInfo.queueTitle, newLegacyPlayerInfo.queueTitle)) {
// TODO(b/187152483): Set proper event code when available.
listeners.queueEvent(C.INDEX_UNSET, (listener) -> listener.onPlaylistMetadataChanged(newControllerInfo.playerInfo.playlistMetadata));
}
if (discontinuityReason != null) {
listeners.queueEvent(Player.EVENT_POSITION_DISCONTINUITY, (listener) -> listener.onPositionDiscontinuity(oldControllerInfo.playerInfo.sessionPositionInfo.positionInfo, newControllerInfo.playerInfo.sessionPositionInfo.positionInfo, discontinuityReason));
}
if (mediaItemTransitionReason != null) {
listeners.queueEvent(EVENT_MEDIA_ITEM_TRANSITION, (listener) -> listener.onMediaItemTransition(newControllerInfo.playerInfo.getCurrentMediaItem(), mediaItemTransitionReason));
}
if (!MediaUtils.areEqualError(oldLegacyPlayerInfo.playbackStateCompat, newLegacyPlayerInfo.playbackStateCompat)) {
PlaybackException error = MediaUtils.convertToPlaybackException(newLegacyPlayerInfo.playbackStateCompat);
listeners.queueEvent(EVENT_PLAYER_ERROR, (listener) -> listener.onPlayerErrorChanged(error));
if (error != null) {
listeners.queueEvent(EVENT_PLAYER_ERROR, (listener) -> listener.onPlayerError(error));
}
}
if (oldLegacyPlayerInfo.mediaMetadataCompat != newLegacyPlayerInfo.mediaMetadataCompat) {
listeners.queueEvent(EVENT_MEDIA_METADATA_CHANGED, (listener) -> listener.onMediaMetadataChanged(controllerInfo.playerInfo.mediaMetadata));
}
if (oldControllerInfo.playerInfo.playbackState != newControllerInfo.playerInfo.playbackState) {
listeners.queueEvent(EVENT_PLAYBACK_STATE_CHANGED, (listener) -> listener.onPlaybackStateChanged(newControllerInfo.playerInfo.playbackState));
}
if (oldControllerInfo.playerInfo.playWhenReady != newControllerInfo.playerInfo.playWhenReady) {
listeners.queueEvent(EVENT_PLAY_WHEN_READY_CHANGED, (listener) -> listener.onPlayWhenReadyChanged(newControllerInfo.playerInfo.playWhenReady, Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE));
}
if (oldControllerInfo.playerInfo.isPlaying != newControllerInfo.playerInfo.isPlaying) {
listeners.queueEvent(EVENT_IS_PLAYING_CHANGED, (listener) -> listener.onIsPlayingChanged(newControllerInfo.playerInfo.isPlaying));
}
if (!oldControllerInfo.playerInfo.playbackParameters.equals(newControllerInfo.playerInfo.playbackParameters)) {
listeners.queueEvent(EVENT_PLAYBACK_PARAMETERS_CHANGED, (listener) -> listener.onPlaybackParametersChanged(newControllerInfo.playerInfo.playbackParameters));
}
if (oldControllerInfo.playerInfo.repeatMode != newControllerInfo.playerInfo.repeatMode) {
listeners.queueEvent(Player.EVENT_REPEAT_MODE_CHANGED, (listener) -> listener.onRepeatModeChanged(newControllerInfo.playerInfo.repeatMode));
}
if (oldControllerInfo.playerInfo.shuffleModeEnabled != newControllerInfo.playerInfo.shuffleModeEnabled) {
listeners.queueEvent(Player.EVENT_SHUFFLE_MODE_ENABLED_CHANGED, (listener) -> listener.onShuffleModeEnabledChanged(newControllerInfo.playerInfo.shuffleModeEnabled));
}
if (!oldControllerInfo.playerInfo.audioAttributes.equals(newControllerInfo.playerInfo.audioAttributes)) {
// TODO(b/187152483): Set proper event code when available.
listeners.queueEvent(C.INDEX_UNSET, (listener) -> listener.onAudioAttributesChanged(newControllerInfo.playerInfo.audioAttributes));
}
if (!oldControllerInfo.playerInfo.deviceInfo.equals(newControllerInfo.playerInfo.deviceInfo)) {
// TODO(b/187152483): Set proper event code when available.
listeners.queueEvent(C.INDEX_UNSET, (listener) -> listener.onDeviceInfoChanged(newControllerInfo.playerInfo.deviceInfo));
}
if (oldControllerInfo.playerInfo.deviceVolume != newControllerInfo.playerInfo.deviceVolume || oldControllerInfo.playerInfo.deviceMuted != newControllerInfo.playerInfo.deviceMuted) {
// TODO(b/187152483): Set proper event code when available.
listeners.queueEvent(C.INDEX_UNSET, (listener) -> listener.onDeviceVolumeChanged(newControllerInfo.playerInfo.deviceVolume, newControllerInfo.playerInfo.deviceMuted));
}
if (!oldControllerInfo.availablePlayerCommands.equals(newControllerInfo.availablePlayerCommands)) {
listeners.queueEvent(Player.EVENT_AVAILABLE_COMMANDS_CHANGED, (listener) -> listener.onAvailableCommandsChanged(newControllerInfo.availablePlayerCommands));
}
if (!oldControllerInfo.availableSessionCommands.equals(newControllerInfo.availableSessionCommands)) {
instance.notifyControllerListener(listener -> listener.onAvailableSessionCommandsChanged(instance, newControllerInfo.availableSessionCommands));
}
if (!oldControllerInfo.customLayout.equals(newControllerInfo.customLayout)) {
instance.notifyControllerListener(listener -> ignoreFuture(listener.onSetCustomLayout(instance, newControllerInfo.customLayout)));
}
listeners.flushEvents();
}
use of androidx.media3.common.Player.DiscontinuityReason in project media by androidx.
the class MediaControllerImplLegacy method addMediaItems.
@Override
public void addMediaItems(int index, List<MediaItem> mediaItems) {
if (mediaItems.isEmpty()) {
return;
}
index = min(index, getCurrentTimeline().getWindowCount());
QueueTimeline queueTimeline = (QueueTimeline) controllerInfo.playerInfo.timeline;
QueueTimeline newQueueTimeline = queueTimeline.copyWithNewMediaItems(index, mediaItems);
int currentMediaItemIndex = getCurrentMediaItemIndex();
int newCurrentMediaItemIndex = calculateCurrentItemIndexAfterAddItems(currentMediaItemIndex, index, mediaItems.size());
PlayerInfo maskedPlayerInfo = controllerInfo.playerInfo.copyWithTimeline(newQueueTimeline, newCurrentMediaItemIndex);
ControllerInfo maskedControllerInfo = new ControllerInfo(maskedPlayerInfo, controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, controllerInfo.customLayout);
updateStateMaskedControllerInfo(maskedControllerInfo, /* discontinuityReason= */
null, /* mediaItemTransitionReason= */
null);
for (int i = 0; i < mediaItems.size(); i++) {
MediaItem mediaItem = mediaItems.get(i);
controllerCompat.addQueueItem(MediaUtils.convertToMediaDescriptionCompat(mediaItem), index + i);
}
}
Aggregations