use of com.google.android.exoplayer2.Player.DiscontinuityReason in project ExoPlayer by google.
the class ExoPlayerImpl method updatePlaybackInfo.
// Calling deprecated listeners.
@SuppressWarnings("deprecation")
private void updatePlaybackInfo(PlaybackInfo playbackInfo, @TimelineChangeReason int timelineChangeReason, @PlayWhenReadyChangeReason int playWhenReadyChangeReason, boolean seekProcessed, boolean positionDiscontinuity, @DiscontinuityReason int positionDiscontinuityReason, long discontinuityWindowStartPositionUs, int oldMaskingMediaItemIndex) {
// Assign playback info immediately such that all getters return the right values, but keep
// snapshot of previous and new state so that listener invocations are triggered correctly.
PlaybackInfo previousPlaybackInfo = this.playbackInfo;
PlaybackInfo newPlaybackInfo = playbackInfo;
this.playbackInfo = playbackInfo;
Pair<Boolean, Integer> mediaItemTransitionInfo = evaluateMediaItemTransitionReason(newPlaybackInfo, previousPlaybackInfo, positionDiscontinuity, positionDiscontinuityReason, !previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline));
boolean mediaItemTransitioned = mediaItemTransitionInfo.first;
int mediaItemTransitionReason = mediaItemTransitionInfo.second;
MediaMetadata newMediaMetadata = mediaMetadata;
@Nullable MediaItem mediaItem = null;
if (mediaItemTransitioned) {
if (!newPlaybackInfo.timeline.isEmpty()) {
int windowIndex = newPlaybackInfo.timeline.getPeriodByUid(newPlaybackInfo.periodId.periodUid, period).windowIndex;
mediaItem = newPlaybackInfo.timeline.getWindow(windowIndex, window).mediaItem;
}
staticAndDynamicMediaMetadata = MediaMetadata.EMPTY;
}
if (mediaItemTransitioned || !previousPlaybackInfo.staticMetadata.equals(newPlaybackInfo.staticMetadata)) {
staticAndDynamicMediaMetadata = staticAndDynamicMediaMetadata.buildUpon().populateFromMetadata(newPlaybackInfo.staticMetadata).build();
newMediaMetadata = buildUpdatedMediaMetadata();
}
boolean metadataChanged = !newMediaMetadata.equals(mediaMetadata);
mediaMetadata = newMediaMetadata;
boolean playWhenReadyChanged = previousPlaybackInfo.playWhenReady != newPlaybackInfo.playWhenReady;
boolean playbackStateChanged = previousPlaybackInfo.playbackState != newPlaybackInfo.playbackState;
if (playbackStateChanged || playWhenReadyChanged) {
updateWakeAndWifiLock();
}
boolean isLoadingChanged = previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading;
if (isLoadingChanged) {
updatePriorityTaskManagerForIsLoadingChange(newPlaybackInfo.isLoading);
}
if (!previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline)) {
listeners.queueEvent(Player.EVENT_TIMELINE_CHANGED, listener -> listener.onTimelineChanged(newPlaybackInfo.timeline, timelineChangeReason));
}
if (positionDiscontinuity) {
PositionInfo previousPositionInfo = getPreviousPositionInfo(positionDiscontinuityReason, previousPlaybackInfo, oldMaskingMediaItemIndex);
PositionInfo positionInfo = getPositionInfo(discontinuityWindowStartPositionUs);
listeners.queueEvent(Player.EVENT_POSITION_DISCONTINUITY, listener -> {
listener.onPositionDiscontinuity(positionDiscontinuityReason);
listener.onPositionDiscontinuity(previousPositionInfo, positionInfo, positionDiscontinuityReason);
});
}
if (mediaItemTransitioned) {
@Nullable final MediaItem finalMediaItem = mediaItem;
listeners.queueEvent(Player.EVENT_MEDIA_ITEM_TRANSITION, listener -> listener.onMediaItemTransition(finalMediaItem, mediaItemTransitionReason));
}
if (previousPlaybackInfo.playbackError != newPlaybackInfo.playbackError) {
listeners.queueEvent(Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerErrorChanged(newPlaybackInfo.playbackError));
if (newPlaybackInfo.playbackError != null) {
listeners.queueEvent(Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError(newPlaybackInfo.playbackError));
}
}
if (previousPlaybackInfo.trackSelectorResult != newPlaybackInfo.trackSelectorResult) {
trackSelector.onSelectionActivated(newPlaybackInfo.trackSelectorResult.info);
TrackSelectionArray newSelection = new TrackSelectionArray(newPlaybackInfo.trackSelectorResult.selections);
listeners.queueEvent(Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(newPlaybackInfo.trackGroups, newSelection));
listeners.queueEvent(Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksInfoChanged(newPlaybackInfo.trackSelectorResult.tracksInfo));
}
if (metadataChanged) {
final MediaMetadata finalMediaMetadata = mediaMetadata;
listeners.queueEvent(EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(finalMediaMetadata));
}
if (isLoadingChanged) {
listeners.queueEvent(Player.EVENT_IS_LOADING_CHANGED, listener -> {
listener.onLoadingChanged(newPlaybackInfo.isLoading);
listener.onIsLoadingChanged(newPlaybackInfo.isLoading);
});
}
if (playbackStateChanged || playWhenReadyChanged) {
listeners.queueEvent(/* eventFlag= */
C.INDEX_UNSET, listener -> listener.onPlayerStateChanged(newPlaybackInfo.playWhenReady, newPlaybackInfo.playbackState));
}
if (playbackStateChanged) {
listeners.queueEvent(Player.EVENT_PLAYBACK_STATE_CHANGED, listener -> listener.onPlaybackStateChanged(newPlaybackInfo.playbackState));
}
if (playWhenReadyChanged) {
listeners.queueEvent(Player.EVENT_PLAY_WHEN_READY_CHANGED, listener -> listener.onPlayWhenReadyChanged(newPlaybackInfo.playWhenReady, playWhenReadyChangeReason));
}
if (previousPlaybackInfo.playbackSuppressionReason != newPlaybackInfo.playbackSuppressionReason) {
listeners.queueEvent(Player.EVENT_PLAYBACK_SUPPRESSION_REASON_CHANGED, listener -> listener.onPlaybackSuppressionReasonChanged(newPlaybackInfo.playbackSuppressionReason));
}
if (isPlaying(previousPlaybackInfo) != isPlaying(newPlaybackInfo)) {
listeners.queueEvent(Player.EVENT_IS_PLAYING_CHANGED, listener -> listener.onIsPlayingChanged(isPlaying(newPlaybackInfo)));
}
if (!previousPlaybackInfo.playbackParameters.equals(newPlaybackInfo.playbackParameters)) {
listeners.queueEvent(Player.EVENT_PLAYBACK_PARAMETERS_CHANGED, listener -> listener.onPlaybackParametersChanged(newPlaybackInfo.playbackParameters));
}
if (seekProcessed) {
listeners.queueEvent(/* eventFlag= */
C.INDEX_UNSET, Listener::onSeekProcessed);
}
updateAvailableCommands();
listeners.flushEvents();
if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) {
for (AudioOffloadListener listener : audioOffloadListeners) {
listener.onExperimentalOffloadSchedulingEnabledChanged(newPlaybackInfo.offloadSchedulingEnabled);
}
}
if (previousPlaybackInfo.sleepingForOffload != newPlaybackInfo.sleepingForOffload) {
for (AudioOffloadListener listener : audioOffloadListeners) {
listener.onExperimentalSleepingForOffloadChanged(newPlaybackInfo.sleepingForOffload);
}
}
}
use of com.google.android.exoplayer2.Player.DiscontinuityReason in project ExoPlayer by google.
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 com.google.android.exoplayer2.Player.DiscontinuityReason in project ExoPlayer by google.
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 com.google.android.exoplayer2.Player.DiscontinuityReason in project ExoPlayer by google.
the class ExoPlayerTest method clippedLoopedPeriodsArePlayedFully.
@Test
public void clippedLoopedPeriodsArePlayedFully() throws Exception {
long startPositionUs = 300_000;
long expectedDurationUs = 700_000;
MediaSource mediaSource = new ClippingMediaSource(new FakeMediaSource(), startPositionUs, startPositionUs + expectedDurationUs);
Clock clock = new FakeClock(/* isAutoAdvancing= */
true);
AtomicReference<Player> playerReference = new AtomicReference<>();
AtomicLong positionAtDiscontinuityMs = new AtomicLong(C.TIME_UNSET);
AtomicLong clockAtStartMs = new AtomicLong(C.TIME_UNSET);
AtomicLong clockAtDiscontinuityMs = new AtomicLong(C.TIME_UNSET);
Player.Listener playerListener = new Player.Listener() {
@Override
public void onPlaybackStateChanged(@Player.State int playbackState) {
if (playbackState == Player.STATE_READY && clockAtStartMs.get() == C.TIME_UNSET) {
clockAtStartMs.set(clock.elapsedRealtime());
}
}
@Override
public void onPositionDiscontinuity(@DiscontinuityReason int reason) {
if (reason == Player.DISCONTINUITY_REASON_AUTO_TRANSITION) {
positionAtDiscontinuityMs.set(playerReference.get().getCurrentPosition());
clockAtDiscontinuityMs.set(clock.elapsedRealtime());
}
}
};
ActionSchedule actionSchedule = new ActionSchedule.Builder(TAG).executeRunnable(new PlayerRunnable() {
@Override
public void run(ExoPlayer player) {
playerReference.set(player);
player.addListener(playerListener);
}
}).pause().setRepeatMode(Player.REPEAT_MODE_ALL).waitForPlaybackState(Player.STATE_READY).playUntilPosition(/* mediaItemIndex= */
0, /* positionMs= */
1).playUntilStartOfMediaItem(/* mediaItemIndex= */
0).setRepeatMode(Player.REPEAT_MODE_OFF).play().build();
new ExoPlayerTestRunner.Builder(context).setClock(clock).setMediaSources(mediaSource).setActionSchedule(actionSchedule).build().start().blockUntilEnded(TIMEOUT_MS);
assertThat(positionAtDiscontinuityMs.get()).isAtLeast(0L);
assertThat(clockAtDiscontinuityMs.get() - clockAtStartMs.get()).isAtLeast(Util.usToMs(expectedDurationUs));
}
use of com.google.android.exoplayer2.Player.DiscontinuityReason in project ExoPlayer by google.
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));
}
Aggregations