Search in sources :

Example 16 with RepeatMode

use of androidx.media3.common.Player.RepeatMode in project media by androidx.

the class DefaultAnalyticsCollector method onRepeatModeChanged.

@Override
public final void onRepeatModeChanged(@Player.RepeatMode int repeatMode) {
    EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime();
    sendEvent(eventTime, AnalyticsListener.EVENT_REPEAT_MODE_CHANGED, listener -> listener.onRepeatModeChanged(eventTime, repeatMode));
}
Also used : EventTime(androidx.media3.exoplayer.analytics.AnalyticsListener.EventTime)

Example 17 with RepeatMode

use of androidx.media3.common.Player.RepeatMode in project media by androidx.

the class MediaPeriodQueue method getFollowingMediaPeriodInfo.

/**
 * Returns the {@link MediaPeriodInfo} for the media period following {@code mediaPeriodHolder}'s
 * media period.
 *
 * @param timeline The current timeline.
 * @param mediaPeriodHolder The media period holder.
 * @param rendererPositionUs The current renderer position in microseconds.
 * @return The following media period's info, or {@code null} if it is not yet possible to get the
 *     next media period info.
 */
@Nullable
private MediaPeriodInfo getFollowingMediaPeriodInfo(Timeline timeline, MediaPeriodHolder mediaPeriodHolder, long rendererPositionUs) {
    // TODO: This method is called repeatedly from ExoPlayerImplInternal.maybeUpdateLoadingPeriod
    // but if the timeline is not ready to provide the next period it can't return a non-null value
    // until the timeline is updated. Store whether the next timeline period is ready when the
    // timeline is updated, to avoid repeatedly checking the same timeline.
    MediaPeriodInfo mediaPeriodInfo = mediaPeriodHolder.info;
    // The expected delay until playback transitions to the new period is equal the duration of
    // media that's currently buffered (assuming no interruptions). This is used to project forward
    // the start position for transitions to new windows.
    long bufferedDurationUs = mediaPeriodHolder.getRendererOffset() + mediaPeriodInfo.durationUs - rendererPositionUs;
    if (mediaPeriodInfo.isLastInTimelinePeriod) {
        int currentPeriodIndex = timeline.getIndexOfPeriod(mediaPeriodInfo.id.periodUid);
        int nextPeriodIndex = timeline.getNextPeriodIndex(currentPeriodIndex, period, window, repeatMode, shuffleModeEnabled);
        if (nextPeriodIndex == C.INDEX_UNSET) {
            // We can't create a next period yet.
            return null;
        }
        // We either start a new period in the same window or the first period in the next window.
        long startPositionUs = 0;
        long contentPositionUs = 0;
        int nextWindowIndex = timeline.getPeriod(nextPeriodIndex, period, /* setIds= */
        true).windowIndex;
        Object nextPeriodUid = checkNotNull(period.uid);
        long windowSequenceNumber = mediaPeriodInfo.id.windowSequenceNumber;
        if (timeline.getWindow(nextWindowIndex, window).firstPeriodIndex == nextPeriodIndex) {
            // We're starting to buffer a new window. When playback transitions to this window we'll
            // want it to be from its default start position, so project the default start position
            // forward by the duration of the buffer, and start buffering from this point.
            contentPositionUs = C.TIME_UNSET;
            @Nullable Pair<Object, Long> defaultPositionUs = timeline.getPeriodPositionUs(window, period, nextWindowIndex, /* windowPositionUs= */
            C.TIME_UNSET, /* defaultPositionProjectionUs= */
            max(0, bufferedDurationUs));
            if (defaultPositionUs == null) {
                return null;
            }
            nextPeriodUid = defaultPositionUs.first;
            startPositionUs = defaultPositionUs.second;
            @Nullable MediaPeriodHolder nextMediaPeriodHolder = mediaPeriodHolder.getNext();
            if (nextMediaPeriodHolder != null && nextMediaPeriodHolder.uid.equals(nextPeriodUid)) {
                windowSequenceNumber = nextMediaPeriodHolder.info.id.windowSequenceNumber;
            } else {
                windowSequenceNumber = nextWindowSequenceNumber++;
            }
        }
        @Nullable MediaPeriodId periodId = resolveMediaPeriodIdForAds(timeline, nextPeriodUid, startPositionUs, windowSequenceNumber, window, period);
        if (contentPositionUs != C.TIME_UNSET && mediaPeriodInfo.requestedContentPositionUs != C.TIME_UNSET) {
            boolean isPrecedingPeriodAnAd = timeline.getPeriodByUid(mediaPeriodInfo.id.periodUid, period).getAdGroupCount() > 0 && period.isServerSideInsertedAdGroup(period.getRemovedAdGroupCount());
            // Handle the requested content position for period transitions within the same window.
            if (periodId.isAd() && isPrecedingPeriodAnAd) {
                // Propagate the requested position to the following ad period in the same window.
                contentPositionUs = mediaPeriodInfo.requestedContentPositionUs;
            } else if (isPrecedingPeriodAnAd) {
                // Use the requested content position of the preceding ad period as the start position.
                startPositionUs = mediaPeriodInfo.requestedContentPositionUs;
            }
        }
        return getMediaPeriodInfo(timeline, periodId, contentPositionUs, startPositionUs);
    }
    MediaPeriodId currentPeriodId = mediaPeriodInfo.id;
    timeline.getPeriodByUid(currentPeriodId.periodUid, period);
    if (currentPeriodId.isAd()) {
        int adGroupIndex = currentPeriodId.adGroupIndex;
        int adCountInCurrentAdGroup = period.getAdCountInAdGroup(adGroupIndex);
        if (adCountInCurrentAdGroup == C.LENGTH_UNSET) {
            return null;
        }
        int nextAdIndexInAdGroup = period.getNextAdIndexToPlay(adGroupIndex, currentPeriodId.adIndexInAdGroup);
        if (nextAdIndexInAdGroup < adCountInCurrentAdGroup) {
            // Play the next ad in the ad group if it's available.
            return getMediaPeriodInfoForAd(timeline, currentPeriodId.periodUid, adGroupIndex, nextAdIndexInAdGroup, mediaPeriodInfo.requestedContentPositionUs, currentPeriodId.windowSequenceNumber);
        } else {
            // Play content from the ad group position.
            long startPositionUs = mediaPeriodInfo.requestedContentPositionUs;
            if (startPositionUs == C.TIME_UNSET) {
                // If we're transitioning from an ad group to content starting from its default position,
                // project the start position forward as if this were a transition to a new window.
                @Nullable Pair<Object, Long> defaultPositionUs = timeline.getPeriodPositionUs(window, period, period.windowIndex, /* windowPositionUs= */
                C.TIME_UNSET, /* defaultPositionProjectionUs= */
                max(0, bufferedDurationUs));
                if (defaultPositionUs == null) {
                    return null;
                }
                startPositionUs = defaultPositionUs.second;
            }
            long minStartPositionUs = getMinStartPositionAfterAdGroupUs(timeline, currentPeriodId.periodUid, currentPeriodId.adGroupIndex);
            return getMediaPeriodInfoForContent(timeline, currentPeriodId.periodUid, max(minStartPositionUs, startPositionUs), mediaPeriodInfo.requestedContentPositionUs, currentPeriodId.windowSequenceNumber);
        }
    } else {
        // Play the next ad group if it's still available.
        int adIndexInAdGroup = period.getFirstAdIndexToPlay(currentPeriodId.nextAdGroupIndex);
        boolean isPlayedServerSideInsertedAd = period.isServerSideInsertedAdGroup(currentPeriodId.nextAdGroupIndex) && period.getAdState(currentPeriodId.nextAdGroupIndex, adIndexInAdGroup) == AdPlaybackState.AD_STATE_PLAYED;
        if (adIndexInAdGroup == period.getAdCountInAdGroup(currentPeriodId.nextAdGroupIndex) || isPlayedServerSideInsertedAd) {
            // The next ad group has no ads left to play or is a played SSAI ad group. Play content from
            // the end position instead.
            long startPositionUs = getMinStartPositionAfterAdGroupUs(timeline, currentPeriodId.periodUid, currentPeriodId.nextAdGroupIndex);
            return getMediaPeriodInfoForContent(timeline, currentPeriodId.periodUid, startPositionUs, /* requestedContentPositionUs= */
            mediaPeriodInfo.durationUs, currentPeriodId.windowSequenceNumber);
        }
        return getMediaPeriodInfoForAd(timeline, currentPeriodId.periodUid, currentPeriodId.nextAdGroupIndex, adIndexInAdGroup, /* contentPositionUs= */
        mediaPeriodInfo.durationUs, currentPeriodId.windowSequenceNumber);
    }
}
Also used : MediaPeriodId(androidx.media3.exoplayer.source.MediaSource.MediaPeriodId) Nullable(androidx.annotation.Nullable) Nullable(androidx.annotation.Nullable)

Example 18 with RepeatMode

use of androidx.media3.common.Player.RepeatMode in project media by androidx.

the class MediaControllerCompatCallbackWithMediaSessionTest method gettersAfterConnected.

@Test
public void gettersAfterConnected() throws Exception {
    @State int testState = STATE_READY;
    int testBufferingPosition = 1500;
    float testSpeed = 1.5f;
    int testItemIndex = 0;
    List<MediaItem> testMediaItems = MediaTestUtils.createMediaItems(/* size= */
    3);
    testMediaItems.set(testItemIndex, new MediaItem.Builder().setMediaId(testMediaItems.get(testItemIndex).mediaId).setMediaMetadata(new MediaMetadata.Builder().setUserRating(new HeartRating(/* isHeart= */
    true)).build()).build());
    Timeline testTimeline = new PlaylistTimeline(testMediaItems);
    String testPlaylistTitle = "testPlaylistTitle";
    MediaMetadata testPlaylistMetadata = new MediaMetadata.Builder().setTitle(testPlaylistTitle).build();
    boolean testShuffleModeEnabled = true;
    @RepeatMode int testRepeatMode = Player.REPEAT_MODE_ONE;
    Bundle playerConfig = new RemoteMediaSession.MockPlayerConfigBuilder().setPlaybackState(testState).setBufferedPosition(testBufferingPosition).setPlaybackParameters(new PlaybackParameters(testSpeed)).setTimeline(testTimeline).setPlaylistMetadata(testPlaylistMetadata).setCurrentMediaItemIndex(testItemIndex).setShuffleModeEnabled(testShuffleModeEnabled).setRepeatMode(testRepeatMode).build();
    session.setPlayer(playerConfig);
    MediaControllerCompat controller = new MediaControllerCompat(context, session.getCompatToken());
    CountDownLatch latch = new CountDownLatch(1);
    controller.registerCallback(new MediaControllerCompat.Callback() {

        @Override
        public void onSessionReady() {
            latch.countDown();
        }
    }, handler);
    assertThat(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
    assertThat(MediaUtils.convertToPlaybackState(controller.getPlaybackState(), controller.getMetadata(), /* timeDiffMs= */
    C.TIME_UNSET)).isEqualTo(testState);
    assertThat(controller.getPlaybackState().getBufferedPosition()).isEqualTo(testBufferingPosition);
    assertThat(controller.getPlaybackState().getPlaybackSpeed()).isWithin(EPSILON).of(testSpeed);
    assertThat(controller.getMetadata().getString(METADATA_KEY_MEDIA_ID)).isEqualTo(testMediaItems.get(testItemIndex).mediaId);
    assertThat(controller.getRatingType()).isEqualTo(RatingCompat.RATING_HEART);
    List<QueueItem> queue = controller.getQueue();
    assertThat(queue).isNotNull();
    assertThat(queue).hasSize(testTimeline.getWindowCount());
    for (int i = 0; i < testTimeline.getWindowCount(); i++) {
        assertThat(queue.get(i).getDescription().getMediaId()).isEqualTo(testMediaItems.get(i).mediaId);
    }
    assertThat(testPlaylistTitle).isEqualTo(controller.getQueueTitle().toString());
    assertThat(PlaybackStateCompat.SHUFFLE_MODE_ALL).isEqualTo(controller.getShuffleMode());
    assertThat(PlaybackStateCompat.REPEAT_MODE_ONE).isEqualTo(controller.getRepeatMode());
}
Also used : Bundle(android.os.Bundle) CountDownLatch(java.util.concurrent.CountDownLatch) HeartRating(androidx.media3.common.HeartRating) Timeline(androidx.media3.common.Timeline) RepeatMode(androidx.media3.common.Player.RepeatMode) State(androidx.media3.common.Player.State) MediaItem(androidx.media3.common.MediaItem) MediaMetadata(androidx.media3.common.MediaMetadata) MediaControllerCompat(android.support.v4.media.session.MediaControllerCompat) QueueItem(android.support.v4.media.session.MediaSessionCompat.QueueItem) PlaybackParameters(androidx.media3.common.PlaybackParameters) LargeTest(androidx.test.filters.LargeTest) Test(org.junit.Test)

Example 19 with RepeatMode

use of androidx.media3.common.Player.RepeatMode in project media by androidx.

the class MediaControllerListenerTest method onRepeatModeChanged.

/**
 * This also tests {@link MediaController#getRepeatMode()}.
 */
@Test
public void onRepeatModeChanged() throws Exception {
    RemoteMediaSession.RemoteMockPlayer player = remoteSession.getMockPlayer();
    Timeline timeline = MediaTestUtils.createTimeline(/* windowCount= */
    3);
    player.setTimeline(timeline);
    player.notifyTimelineChanged(Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
    player.setCurrentMediaItemIndex(2);
    player.setRepeatMode(Player.REPEAT_MODE_OFF);
    player.notifyRepeatModeChanged();
    MediaController controller = controllerTestRule.createController(remoteSession.getToken());
    CountDownLatch latch = new CountDownLatch(1);
    AtomicInteger repeatModeFromParamRef = new AtomicInteger();
    AtomicInteger repeatModeFromGetterRef = new AtomicInteger();
    AtomicInteger previousIndexRef = new AtomicInteger();
    AtomicInteger nextIndexRef = new AtomicInteger();
    Player.Listener listener = new Player.Listener() {

        @Override
        public void onRepeatModeChanged(@RepeatMode int repeatMode) {
            repeatModeFromParamRef.set(repeatMode);
            repeatModeFromGetterRef.set(controller.getRepeatMode());
            previousIndexRef.set(controller.getPreviousMediaItemIndex());
            nextIndexRef.set(controller.getNextMediaItemIndex());
            latch.countDown();
        }
    };
    threadTestRule.getHandler().postAndSync(() -> controller.addListener(listener));
    int testRepeatMode = REPEAT_MODE_ALL;
    player.setRepeatMode(testRepeatMode);
    player.notifyRepeatModeChanged();
    assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
    assertThat(repeatModeFromParamRef.get()).isEqualTo(testRepeatMode);
    assertThat(repeatModeFromGetterRef.get()).isEqualTo(testRepeatMode);
    assertThat(previousIndexRef.get()).isEqualTo(1);
    assertThat(nextIndexRef.get()).isEqualTo(0);
}
Also used : MediaTestUtils.createTimeline(androidx.media3.session.MediaTestUtils.createTimeline) Timeline(androidx.media3.common.Timeline) RemoteMockPlayer(androidx.media3.session.RemoteMediaSession.RemoteMockPlayer) Player(androidx.media3.common.Player) RepeatMode(androidx.media3.common.Player.RepeatMode) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) RemoteMockPlayer(androidx.media3.session.RemoteMediaSession.RemoteMockPlayer) CountDownLatch(java.util.concurrent.CountDownLatch) LargeTest(androidx.test.filters.LargeTest) Test(org.junit.Test)

Example 20 with RepeatMode

use of androidx.media3.common.Player.RepeatMode in project media by androidx.

the class MediaControllerListenerWithMediaSessionCompatTest method onEvents_whenOnRepeatModeChanges_isCalledAfterOtherListenerMethods.

@Test
public void onEvents_whenOnRepeatModeChanges_isCalledAfterOtherListenerMethods() throws Exception {
    Player.Events testEvents = new Player.Events(new FlagSet.Builder().add(EVENT_REPEAT_MODE_CHANGED).build());
    CopyOnWriteArrayList<Integer> listenerEventCodes = new CopyOnWriteArrayList<>();
    MediaController controller = controllerTestRule.createController(session.getSessionToken());
    CountDownLatch latch = new CountDownLatch(2);
    AtomicReference<Player.Events> eventsRef = new AtomicReference<>();
    Player.Listener listener = new Player.Listener() {

        @Override
        public void onRepeatModeChanged(@Player.RepeatMode int repeatMode) {
            listenerEventCodes.add(EVENT_REPEAT_MODE_CHANGED);
            latch.countDown();
        }

        @Override
        public void onEvents(Player player, Player.Events events) {
            listenerEventCodes.add(EVENT_ON_EVENTS);
            eventsRef.set(events);
            latch.countDown();
        }
    };
    threadTestRule.getHandler().postAndSync(() -> controller.addListener(listener));
    session.setRepeatMode(PlaybackStateCompat.REPEAT_MODE_GROUP);
    assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
    assertThat(listenerEventCodes).containsExactly(EVENT_REPEAT_MODE_CHANGED, EVENT_ON_EVENTS);
    assertThat(eventsRef.get()).isEqualTo(testEvents);
}
Also used : Player(androidx.media3.common.Player) AtomicReference(java.util.concurrent.atomic.AtomicReference) CountDownLatch(java.util.concurrent.CountDownLatch) FlagSet(androidx.media3.common.FlagSet) CopyOnWriteArrayList(java.util.concurrent.CopyOnWriteArrayList) LargeTest(androidx.test.filters.LargeTest) Test(org.junit.Test)

Aggregations

Test (org.junit.Test)14 LargeTest (androidx.test.filters.LargeTest)11 CountDownLatch (java.util.concurrent.CountDownLatch)11 RepeatMode (androidx.media3.common.Player.RepeatMode)10 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)10 AtomicReference (java.util.concurrent.atomic.AtomicReference)10 Player (androidx.media3.common.Player)9 Timeline (androidx.media3.common.Timeline)9 Nullable (androidx.annotation.Nullable)7 MediaMetadata (androidx.media3.common.MediaMetadata)7 Bundle (android.os.Bundle)6 MediaItem (androidx.media3.common.MediaItem)6 PlaybackParameters (androidx.media3.common.PlaybackParameters)6 FlagSet (androidx.media3.common.FlagSet)5 State (androidx.media3.common.Player.State)5 MediaPeriodId (androidx.media3.exoplayer.source.MediaSource.MediaPeriodId)4 RemoteMockPlayer (androidx.media3.session.RemoteMediaSession.RemoteMockPlayer)4 AudioAttributes (androidx.media3.common.AudioAttributes)3 PositionInfo (androidx.media3.common.Player.PositionInfo)3 CopyOnWriteArrayList (java.util.concurrent.CopyOnWriteArrayList)3