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));
}
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);
}
}
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());
}
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);
}
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);
}
Aggregations