Search in sources :

Example 1 with IllegalSeekPositionException

use of androidx.media3.common.IllegalSeekPositionException in project media by androidx.

the class ExoPlayerTest method illegalSeekPositionDoesThrow.

@Test
public void illegalSeekPositionDoesThrow() throws Exception {
    final IllegalSeekPositionException[] exception = new IllegalSeekPositionException[1];
    ActionSchedule actionSchedule = new ActionSchedule.Builder(TAG).waitForPlaybackState(Player.STATE_BUFFERING).executeRunnable(new PlayerRunnable() {

        @Override
        public void run(ExoPlayer player) {
            try {
                player.seekTo(/* mediaItemIndex= */
                100, /* positionMs= */
                0);
            } catch (IllegalSeekPositionException e) {
                exception[0] = e;
            }
        }
    }).waitForPlaybackState(Player.STATE_ENDED).build();
    new ExoPlayerTestRunner.Builder(context).setActionSchedule(actionSchedule).build().start().blockUntilActionScheduleFinished(TIMEOUT_MS).blockUntilEnded(TIMEOUT_MS);
    assertThat(exception[0]).isNotNull();
}
Also used : ActionSchedule(androidx.media3.test.utils.ActionSchedule) PlayerRunnable(androidx.media3.test.utils.ActionSchedule.PlayerRunnable) TestExoPlayerBuilder(androidx.media3.test.utils.TestExoPlayerBuilder) ExoPlayerTestRunner(androidx.media3.test.utils.ExoPlayerTestRunner) IllegalSeekPositionException(androidx.media3.common.IllegalSeekPositionException) Test(org.junit.Test)

Example 2 with IllegalSeekPositionException

use of androidx.media3.common.IllegalSeekPositionException in project media by androidx.

the class MediaControllerImplLegacy method seekToInternal.

private void seekToInternal(int mediaItemIndex, long positionMs) {
    int currentMediaItemIndex = getCurrentMediaItemIndex();
    Timeline currentTimeline = controllerInfo.playerInfo.timeline;
    if (currentMediaItemIndex != mediaItemIndex && (mediaItemIndex < 0 || mediaItemIndex >= currentTimeline.getWindowCount())) {
        throw new IllegalSeekPositionException(currentTimeline, mediaItemIndex, positionMs);
    }
    if (isPlayingAd()) {
        return;
    }
    int newMediaItemIndex = currentMediaItemIndex;
    @Nullable @MediaItemTransitionReason Integer mediaItemTransitionReason = null;
    if (mediaItemIndex != currentMediaItemIndex) {
        QueueTimeline queueTimeline = (QueueTimeline) controllerInfo.playerInfo.timeline;
        long queueId = queueTimeline.getQueueId(mediaItemIndex);
        if (queueId != QueueItem.UNKNOWN_ID) {
            controllerCompat.getTransportControls().skipToQueueItem(queueId);
            newMediaItemIndex = mediaItemIndex;
            mediaItemTransitionReason = MEDIA_ITEM_TRANSITION_REASON_SEEK;
        } else {
            Log.w(TAG, "Cannot seek to new media item due to the missing queue Id at media item," + " mediaItemIndex=" + mediaItemIndex);
        }
    }
    @Nullable @DiscontinuityReason Integer discontinuityReason;
    long currentPositionMs = getCurrentPosition();
    long newPositionMs;
    if (positionMs == C.TIME_UNSET) {
        newPositionMs = currentPositionMs;
        discontinuityReason = null;
    } else {
        controllerCompat.getTransportControls().seekTo(positionMs);
        newPositionMs = positionMs;
        discontinuityReason = DISCONTINUITY_REASON_SEEK;
    }
    long newDurationMs;
    long newBufferedPositionMs;
    int newBufferedPercentage;
    long newTotalBufferedDurationMs;
    if (mediaItemTransitionReason == null) {
        // Follows the ExoPlayerImpl's state masking for seek within the current item.
        long oldBufferedPositionMs = getBufferedPosition();
        newDurationMs = getDuration();
        newBufferedPositionMs = (newPositionMs < currentPositionMs) ? newPositionMs : max(newPositionMs, oldBufferedPositionMs);
        newBufferedPercentage = (newDurationMs == C.TIME_UNSET) ? 0 : (int) (newBufferedPositionMs * 100L / newDurationMs);
        newTotalBufferedDurationMs = newBufferedPositionMs - newPositionMs;
    } else {
        newDurationMs = C.TIME_UNSET;
        newBufferedPositionMs = 0L;
        newBufferedPercentage = 0;
        newTotalBufferedDurationMs = 0L;
    }
    PositionInfo positionInfo = createPositionInfo(newMediaItemIndex, !currentTimeline.isEmpty() ? currentTimeline.getWindow(newMediaItemIndex, new Window()).mediaItem : null, newPositionMs);
    PlayerInfo maskedPlayerInfo = controllerInfo.playerInfo.copyWithSessionPositionInfo(createSessionPositionInfo(positionInfo, /* isPlayingAd= */
    false, newDurationMs, newBufferedPositionMs, newBufferedPercentage, newTotalBufferedDurationMs));
    if (maskedPlayerInfo.playbackState != Player.STATE_IDLE) {
        maskedPlayerInfo = maskedPlayerInfo.copyWithPlaybackState(Player.STATE_BUFFERING, /* playerError= */
        null);
    }
    ControllerInfo maskedControllerInfo = new ControllerInfo(maskedPlayerInfo, controllerInfo.availableSessionCommands, controllerInfo.availablePlayerCommands, controllerInfo.customLayout);
    updateStateMaskedControllerInfo(maskedControllerInfo, discontinuityReason, mediaItemTransitionReason);
}
Also used : Window(androidx.media3.common.Timeline.Window) DiscontinuityReason(androidx.media3.common.Player.DiscontinuityReason) IllegalSeekPositionException(androidx.media3.common.IllegalSeekPositionException) Timeline(androidx.media3.common.Timeline) MediaItemTransitionReason(androidx.media3.common.Player.MediaItemTransitionReason) PositionInfo(androidx.media3.common.Player.PositionInfo) Nullable(androidx.annotation.Nullable)

Example 3 with IllegalSeekPositionException

use of androidx.media3.common.IllegalSeekPositionException in project media by androidx.

the class MediaControllerImplBase method seekToInternal.

private void seekToInternal(int windowIndex, long positionMs) {
    Timeline timeline = playerInfo.timeline;
    if (windowIndex < 0 || (!timeline.isEmpty() && windowIndex >= timeline.getWindowCount())) {
        throw new IllegalSeekPositionException(timeline, windowIndex, positionMs);
    }
    if (isPlayingAd()) {
        return;
    }
    @Player.State int newPlaybackState = getPlaybackState() == Player.STATE_IDLE ? Player.STATE_IDLE : Player.STATE_BUFFERING;
    PlayerInfo newPlayerInfo = playerInfo.copyWithPlaybackState(newPlaybackState, playerInfo.playerError);
    @Nullable PeriodInfo periodInfo = getPeriodInfo(timeline, windowIndex, positionMs);
    if (periodInfo == null) {
        // Timeline is empty.
        PositionInfo newPositionInfo = new PositionInfo(/* windowUid= */
        null, windowIndex, /* mediaItem= */
        null, /* periodUid= */
        null, /* periodIndex= */
        0, /* positionMs= */
        positionMs == C.TIME_UNSET ? 0 : positionMs, /* contentPositionMs= */
        positionMs == C.TIME_UNSET ? 0 : positionMs, /* adGroupIndex= */
        C.INDEX_UNSET, /* adIndexInAdGroup= */
        C.INDEX_UNSET);
        newPlayerInfo = maskTimelineAndPositionInfo(playerInfo, playerInfo.timeline, newPositionInfo, new SessionPositionInfo(newPositionInfo, playerInfo.sessionPositionInfo.isPlayingAd, /* eventTimeMs= */
        SystemClock.elapsedRealtime(), playerInfo.sessionPositionInfo.durationMs, /* bufferedPositionMs= */
        positionMs == C.TIME_UNSET ? 0 : positionMs, /* bufferedPercentage= */
        0, /* totalBufferedDurationMs= */
        0, playerInfo.sessionPositionInfo.currentLiveOffsetMs, playerInfo.sessionPositionInfo.contentDurationMs, /* contentBufferedPositionMs= */
        positionMs == C.TIME_UNSET ? 0 : positionMs), DISCONTINUITY_REASON_SEEK);
    } else {
        newPlayerInfo = maskPositionInfo(newPlayerInfo, timeline, periodInfo);
    }
    boolean mediaItemTransition = !playerInfo.timeline.isEmpty() && newPlayerInfo.sessionPositionInfo.positionInfo.mediaItemIndex != playerInfo.sessionPositionInfo.positionInfo.mediaItemIndex;
    boolean positionDiscontinuity = mediaItemTransition || newPlayerInfo.sessionPositionInfo.positionInfo.positionMs != playerInfo.sessionPositionInfo.positionInfo.positionMs;
    if (!positionDiscontinuity) {
        return;
    }
    updatePlayerInfo(newPlayerInfo, /* ignored */
    TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, /* ignored */
    PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, positionDiscontinuity, /* positionDiscontinuityReason= */
    DISCONTINUITY_REASON_SEEK, mediaItemTransition, MEDIA_ITEM_TRANSITION_REASON_SEEK);
}
Also used : Timeline(androidx.media3.common.Timeline) RemotableTimeline(androidx.media3.common.Timeline.RemotableTimeline) PositionInfo(androidx.media3.common.Player.PositionInfo) Nullable(androidx.annotation.Nullable) IllegalSeekPositionException(androidx.media3.common.IllegalSeekPositionException)

Example 4 with IllegalSeekPositionException

use of androidx.media3.common.IllegalSeekPositionException 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);
}
Also used : Timeline(androidx.media3.common.Timeline) SuppressLint(android.annotation.SuppressLint) IllegalSeekPositionException(androidx.media3.common.IllegalSeekPositionException)

Example 5 with IllegalSeekPositionException

use of androidx.media3.common.IllegalSeekPositionException in project media by androidx.

the class ExoPlayerImplInternal method resolveSeekPositionUs.

/**
 * Converts a {@link SeekPosition} into the corresponding (periodUid, periodPositionUs) for the
 * internal timeline.
 *
 * @param seekPosition The position to resolve.
 * @param trySubsequentPeriods Whether the position can be resolved to a subsequent matching
 *     period if the original period is no longer available.
 * @return The resolved position, or null if resolution was not successful.
 * @throws IllegalSeekPositionException If the window index of the seek position is outside the
 *     bounds of the timeline.
 */
@Nullable
private static Pair<Object, Long> resolveSeekPositionUs(Timeline timeline, SeekPosition seekPosition, boolean trySubsequentPeriods, @RepeatMode int repeatMode, boolean shuffleModeEnabled, Timeline.Window window, Timeline.Period period) {
    Timeline seekTimeline = seekPosition.timeline;
    if (timeline.isEmpty()) {
        // We don't have a valid timeline yet, so we can't resolve the position.
        return null;
    }
    if (seekTimeline.isEmpty()) {
        // The application performed a blind seek with an empty timeline (most likely based on
        // knowledge of what the future timeline will be). Use the internal timeline.
        seekTimeline = timeline;
    }
    // Map the SeekPosition to a position in the corresponding timeline.
    Pair<Object, Long> periodPositionUs;
    try {
        periodPositionUs = seekTimeline.getPeriodPositionUs(window, period, seekPosition.windowIndex, seekPosition.windowPositionUs);
    } catch (IndexOutOfBoundsException e) {
        // The window index of the seek position was outside the bounds of the timeline.
        return null;
    }
    if (timeline.equals(seekTimeline)) {
        // Our internal timeline is the seek timeline, so the mapped position is correct.
        return periodPositionUs;
    }
    // Attempt to find the mapped period in the internal timeline.
    int periodIndex = timeline.getIndexOfPeriod(periodPositionUs.first);
    if (periodIndex != C.INDEX_UNSET) {
        // We successfully located the period in the internal timeline.
        if (seekTimeline.getPeriodByUid(periodPositionUs.first, period).isPlaceholder && seekTimeline.getWindow(period.windowIndex, window).firstPeriodIndex == seekTimeline.getIndexOfPeriod(periodPositionUs.first)) {
            // The seek timeline was using a placeholder, so we need to re-resolve using the updated
            // timeline in case the resolved position changed. Only resolve the first period in a window
            // because subsequent periods must start at position 0 and don't need to be resolved.
            int newWindowIndex = timeline.getPeriodByUid(periodPositionUs.first, period).windowIndex;
            periodPositionUs = timeline.getPeriodPositionUs(window, period, newWindowIndex, seekPosition.windowPositionUs);
        }
        return periodPositionUs;
    }
    if (trySubsequentPeriods) {
        // Try and find a subsequent period from the seek timeline in the internal timeline.
        @Nullable Object periodUid = resolveSubsequentPeriod(window, period, repeatMode, shuffleModeEnabled, periodPositionUs.first, seekTimeline, timeline);
        if (periodUid != null) {
            // We found one. Use the default position of the corresponding window.
            return timeline.getPeriodPositionUs(window, period, timeline.getPeriodByUid(periodUid, period).windowIndex, /* windowPositionUs= */
            C.TIME_UNSET);
        }
    }
    // We didn't find one. Give up.
    return null;
}
Also used : Timeline(androidx.media3.common.Timeline) Nullable(androidx.annotation.Nullable) Nullable(androidx.annotation.Nullable)

Aggregations

IllegalSeekPositionException (androidx.media3.common.IllegalSeekPositionException)6 Timeline (androidx.media3.common.Timeline)6 Nullable (androidx.annotation.Nullable)4 PositionInfo (androidx.media3.common.Player.PositionInfo)3 SuppressLint (android.annotation.SuppressLint)2 RemotableTimeline (androidx.media3.common.Timeline.RemotableTimeline)2 Window (androidx.media3.common.Timeline.Window)2 DiscontinuityReason (androidx.media3.common.Player.DiscontinuityReason)1 MediaItemTransitionReason (androidx.media3.common.Player.MediaItemTransitionReason)1 Period (androidx.media3.common.Timeline.Period)1 ActionSchedule (androidx.media3.test.utils.ActionSchedule)1 PlayerRunnable (androidx.media3.test.utils.ActionSchedule.PlayerRunnable)1 ExoPlayerTestRunner (androidx.media3.test.utils.ExoPlayerTestRunner)1 TestExoPlayerBuilder (androidx.media3.test.utils.TestExoPlayerBuilder)1 ArrayList (java.util.ArrayList)1 Test (org.junit.Test)1