Search in sources :

Example 21 with DiscontinuityReason

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));
}
Also used : Player(androidx.media3.common.Player) AnalyticsListener(androidx.media3.exoplayer.analytics.AnalyticsListener) TransferListener(androidx.media3.datasource.TransferListener) MediaSourceEventListener(androidx.media3.exoplayer.source.MediaSourceEventListener) Listener(androidx.media3.common.Player.Listener) DrmSessionEventListener(androidx.media3.exoplayer.drm.DrmSessionEventListener) DiscontinuityReason(androidx.media3.common.Player.DiscontinuityReason) ActionSchedule(androidx.media3.test.utils.ActionSchedule) PlayerRunnable(androidx.media3.test.utils.ActionSchedule.PlayerRunnable) TestExoPlayerBuilder(androidx.media3.test.utils.TestExoPlayerBuilder) AtomicReference(java.util.concurrent.atomic.AtomicReference) Listener(androidx.media3.common.Player.Listener) Timeline(androidx.media3.common.Timeline) NoUidTimeline(androidx.media3.test.utils.NoUidTimeline) FakeTimeline(androidx.media3.test.utils.FakeTimeline) SinglePeriodTimeline(androidx.media3.exoplayer.source.SinglePeriodTimeline) AtomicLong(java.util.concurrent.atomic.AtomicLong) FakeTimeline(androidx.media3.test.utils.FakeTimeline) TimelineWindowDefinition(androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition) ExoPlayerTestRunner(androidx.media3.test.utils.ExoPlayerTestRunner) Test(org.junit.Test)

Example 22 with DiscontinuityReason

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);
}
Also used : Player(androidx.media3.common.Player) AnalyticsListener(androidx.media3.exoplayer.analytics.AnalyticsListener) TransferListener(androidx.media3.datasource.TransferListener) MediaSourceEventListener(androidx.media3.exoplayer.source.MediaSourceEventListener) Listener(androidx.media3.common.Player.Listener) DrmSessionEventListener(androidx.media3.exoplayer.drm.DrmSessionEventListener) FakeMediaSource(androidx.media3.test.utils.FakeMediaSource) DiscontinuityReason(androidx.media3.common.Player.DiscontinuityReason) ActionSchedule(androidx.media3.test.utils.ActionSchedule) PlayerRunnable(androidx.media3.test.utils.ActionSchedule.PlayerRunnable) TestExoPlayerBuilder(androidx.media3.test.utils.TestExoPlayerBuilder) AtomicReference(java.util.concurrent.atomic.AtomicReference) Listener(androidx.media3.common.Player.Listener) Timeline(androidx.media3.common.Timeline) NoUidTimeline(androidx.media3.test.utils.NoUidTimeline) FakeTimeline(androidx.media3.test.utils.FakeTimeline) SinglePeriodTimeline(androidx.media3.exoplayer.source.SinglePeriodTimeline) AtomicLong(java.util.concurrent.atomic.AtomicLong) ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState(androidx.media3.exoplayer.source.ads.ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState) AdPlaybackState(androidx.media3.common.AdPlaybackState) FakeTimeline(androidx.media3.test.utils.FakeTimeline) TimelineWindowDefinition(androidx.media3.test.utils.FakeTimeline.TimelineWindowDefinition) ExoPlayerTestRunner(androidx.media3.test.utils.ExoPlayerTestRunner) Test(org.junit.Test)

Example 23 with DiscontinuityReason

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);
}
Also used : TrackSelectorResult(androidx.media3.exoplayer.trackselection.TrackSelectorResult) TrackGroupArray(androidx.media3.common.TrackGroupArray) Metadata(androidx.media3.common.Metadata) Nullable(androidx.annotation.Nullable) CheckResult(androidx.annotation.CheckResult)

Example 24 with DiscontinuityReason

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();
}
Also used : PlaybackException(androidx.media3.common.PlaybackException)

Example 25 with DiscontinuityReason

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);
    }
}
Also used : MediaItem(androidx.media3.common.MediaItem)

Aggregations

DiscontinuityReason (androidx.media3.common.Player.DiscontinuityReason)20 Test (org.junit.Test)19 PositionInfo (androidx.media3.common.Player.PositionInfo)18 Player (androidx.media3.common.Player)17 CountDownLatch (java.util.concurrent.CountDownLatch)15 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)13 AtomicReference (java.util.concurrent.atomic.AtomicReference)13 AtomicLong (java.util.concurrent.atomic.AtomicLong)9 PlaybackStateCompat (android.support.v4.media.session.PlaybackStateCompat)8 MediaItem (androidx.media3.common.MediaItem)8 Timeline (androidx.media3.common.Timeline)8 LargeTest (androidx.test.filters.LargeTest)8 Nullable (androidx.annotation.Nullable)7 MediumTest (androidx.test.filters.MediumTest)7 QueueItem (android.support.v4.media.session.MediaSessionCompat.QueueItem)6 State (androidx.media3.common.Player.State)5 AnalyticsListener (androidx.media3.exoplayer.analytics.AnalyticsListener)5 MediaMetadataCompat (android.support.v4.media.MediaMetadataCompat)4 Listener (androidx.media3.common.Player.Listener)4 TransferListener (androidx.media3.datasource.TransferListener)4