mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Playlist API: move media item based API to Player
This moves the playlist API methods to the Player interface. Implementation is moved from ExoPlayerImpl to BasePlayer where possible. Further the CastPlayer is changed to implement the Player interface. Proper migration of the Playermanager to not use the ConcatenatingMediaSource anymore follows in a separate, future CL. PiperOrigin-RevId: 302937779
This commit is contained in:
parent
4ff04696c4
commit
d0fc83ed8a
@ -66,6 +66,7 @@
|
||||
floating point audio without adjustment, pass `enableFloatOutput=true`
|
||||
to the `DefaultAudioSink` constructor
|
||||
([#7134](https://github.com/google/ExoPlayer/issues/7134)).
|
||||
* Add media item based playlist API to Player.
|
||||
* Text:
|
||||
* Parse `<ruby>` and `<rt>` tags in WebVTT subtitles (rendering is coming
|
||||
later).
|
||||
@ -129,6 +130,8 @@
|
||||
([#6922](https://github.com/google/ExoPlayer/pull/6922)).
|
||||
* The demo app startup selected item is the last played one.
|
||||
* Add support for x86_64 for the ffmpeg extension.
|
||||
* Cast extension: Implement playlist API and deprecate the old queue
|
||||
manipulation API.
|
||||
|
||||
### 2.11.3 (2020-02-19) ###
|
||||
|
||||
|
@ -27,10 +27,7 @@ import com.google.android.exoplayer2.Player.EventListener;
|
||||
import com.google.android.exoplayer2.Player.TimelineChangeReason;
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.Timeline.Period;
|
||||
import com.google.android.exoplayer2.ext.cast.CastPlayer;
|
||||
import com.google.android.exoplayer2.ext.cast.DefaultMediaItemConverter;
|
||||
import com.google.android.exoplayer2.ext.cast.MediaItemConverter;
|
||||
import com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener;
|
||||
import com.google.android.exoplayer2.source.ConcatenatingMediaSource;
|
||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
||||
@ -41,7 +38,6 @@ import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
|
||||
import com.google.android.exoplayer2.ui.PlayerControlView;
|
||||
import com.google.android.exoplayer2.ui.PlayerView;
|
||||
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
|
||||
import com.google.android.gms.cast.MediaQueueItem;
|
||||
import com.google.android.gms.cast.framework.CastContext;
|
||||
import java.util.ArrayList;
|
||||
|
||||
@ -75,7 +71,6 @@ import java.util.ArrayList;
|
||||
private final ArrayList<MediaItem> mediaQueue;
|
||||
private final Listener listener;
|
||||
private final ConcatenatingMediaSource concatenatingMediaSource;
|
||||
private final MediaItemConverter mediaItemConverter;
|
||||
|
||||
private TrackGroupArray lastSeenTrackGroupArray;
|
||||
private int currentItemIndex;
|
||||
@ -102,7 +97,6 @@ import java.util.ArrayList;
|
||||
mediaQueue = new ArrayList<>();
|
||||
currentItemIndex = C.INDEX_UNSET;
|
||||
concatenatingMediaSource = new ConcatenatingMediaSource();
|
||||
mediaItemConverter = new DefaultMediaItemConverter();
|
||||
|
||||
trackSelector = new DefaultTrackSelector(context);
|
||||
exoPlayer = new SimpleExoPlayer.Builder(context).setTrackSelector(trackSelector).build();
|
||||
@ -143,7 +137,7 @@ import java.util.ArrayList;
|
||||
mediaQueue.add(item);
|
||||
concatenatingMediaSource.addMediaSource(defaultMediaSourceFactory.createMediaSource(item));
|
||||
if (currentPlayer == castPlayer) {
|
||||
castPlayer.addItems(mediaItemConverter.toMediaQueueItem(item));
|
||||
castPlayer.addMediaItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,7 +174,7 @@ import java.util.ArrayList;
|
||||
if (castTimeline.getPeriodCount() <= itemIndex) {
|
||||
return false;
|
||||
}
|
||||
castPlayer.removeItem((int) castTimeline.getPeriod(itemIndex, new Period()).id);
|
||||
castPlayer.removeMediaItem(itemIndex);
|
||||
}
|
||||
}
|
||||
mediaQueue.remove(itemIndex);
|
||||
@ -196,34 +190,33 @@ import java.util.ArrayList;
|
||||
* Moves an item within the queue.
|
||||
*
|
||||
* @param item The item to move.
|
||||
* @param toIndex The target index of the item in the queue.
|
||||
* @param newIndex The target index of the item in the queue.
|
||||
* @return Whether the item move was successful.
|
||||
*/
|
||||
public boolean moveItem(MediaItem item, int toIndex) {
|
||||
public boolean moveItem(MediaItem item, int newIndex) {
|
||||
int fromIndex = mediaQueue.indexOf(item);
|
||||
if (fromIndex == -1) {
|
||||
return false;
|
||||
}
|
||||
// Player update.
|
||||
concatenatingMediaSource.moveMediaSource(fromIndex, toIndex);
|
||||
concatenatingMediaSource.moveMediaSource(fromIndex, newIndex);
|
||||
if (currentPlayer == castPlayer && castPlayer.getPlaybackState() != Player.STATE_IDLE) {
|
||||
Timeline castTimeline = castPlayer.getCurrentTimeline();
|
||||
int periodCount = castTimeline.getPeriodCount();
|
||||
if (periodCount <= fromIndex || periodCount <= toIndex) {
|
||||
if (periodCount <= fromIndex || periodCount <= newIndex) {
|
||||
return false;
|
||||
}
|
||||
int elementId = (int) castTimeline.getPeriod(fromIndex, new Period()).id;
|
||||
castPlayer.moveItem(elementId, toIndex);
|
||||
castPlayer.moveMediaItem(fromIndex, newIndex);
|
||||
}
|
||||
|
||||
mediaQueue.add(toIndex, mediaQueue.remove(fromIndex));
|
||||
mediaQueue.add(newIndex, mediaQueue.remove(fromIndex));
|
||||
|
||||
// Index update.
|
||||
if (fromIndex == currentItemIndex) {
|
||||
maybeSetCurrentItemAndNotify(toIndex);
|
||||
} else if (fromIndex < currentItemIndex && toIndex >= currentItemIndex) {
|
||||
maybeSetCurrentItemAndNotify(newIndex);
|
||||
} else if (fromIndex < currentItemIndex && newIndex >= currentItemIndex) {
|
||||
maybeSetCurrentItemAndNotify(currentItemIndex - 1);
|
||||
} else if (fromIndex > currentItemIndex && toIndex <= currentItemIndex) {
|
||||
} else if (fromIndex > currentItemIndex && newIndex <= currentItemIndex) {
|
||||
maybeSetCurrentItemAndNotify(currentItemIndex + 1);
|
||||
}
|
||||
|
||||
@ -353,7 +346,8 @@ import java.util.ArrayList;
|
||||
|
||||
// Media queue management.
|
||||
if (currentPlayer == exoPlayer) {
|
||||
exoPlayer.prepare(concatenatingMediaSource);
|
||||
exoPlayer.setMediaSource(concatenatingMediaSource, /* resetPosition= */ true);
|
||||
exoPlayer.prepare();
|
||||
}
|
||||
|
||||
// Playback transition.
|
||||
@ -372,11 +366,7 @@ import java.util.ArrayList;
|
||||
private void setCurrentItem(int itemIndex, long positionMs, boolean playWhenReady) {
|
||||
maybeSetCurrentItemAndNotify(itemIndex);
|
||||
if (currentPlayer == castPlayer && castPlayer.getCurrentTimeline().isEmpty()) {
|
||||
MediaQueueItem[] items = new MediaQueueItem[mediaQueue.size()];
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
items[i] = mediaItemConverter.toMediaQueueItem(mediaQueue.get(i));
|
||||
}
|
||||
castPlayer.loadItems(items, itemIndex, positionMs, Player.REPEAT_MODE_OFF);
|
||||
castPlayer.setMediaItems(mediaQueue, itemIndex, positionMs);
|
||||
} else {
|
||||
currentPlayer.seekTo(itemIndex, positionMs);
|
||||
currentPlayer.setPlayWhenReady(playWhenReady);
|
||||
|
@ -21,6 +21,7 @@ import com.google.android.exoplayer2.BasePlayer;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.ExoPlayerLibraryInfo;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
@ -83,6 +84,7 @@ public final class CastPlayer extends BasePlayer {
|
||||
private static final long[] EMPTY_TRACK_ID_ARRAY = new long[0];
|
||||
|
||||
private final CastContext castContext;
|
||||
private final MediaItemConverter mediaItemConverter;
|
||||
// TODO: Allow custom implementations of CastTimelineTracker.
|
||||
private final CastTimelineTracker timelineTracker;
|
||||
private final Timeline.Period period;
|
||||
@ -112,10 +114,23 @@ public final class CastPlayer extends BasePlayer {
|
||||
private long pendingSeekPositionMs;
|
||||
|
||||
/**
|
||||
* Creates a new cast player that uses a {@link DefaultMediaItemConverter}.
|
||||
*
|
||||
* @param castContext The context from which the cast session is obtained.
|
||||
*/
|
||||
public CastPlayer(CastContext castContext) {
|
||||
this(castContext, new DefaultMediaItemConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new cast player.
|
||||
*
|
||||
* @param castContext The context from which the cast session is obtained.
|
||||
* @param mediaItemConverter The {@link MediaItemConverter} to use.
|
||||
*/
|
||||
public CastPlayer(CastContext castContext, MediaItemConverter mediaItemConverter) {
|
||||
this.castContext = castContext;
|
||||
this.mediaItemConverter = mediaItemConverter;
|
||||
timelineTracker = new CastTimelineTracker();
|
||||
period = new Timeline.Period();
|
||||
statusListener = new StatusListener();
|
||||
@ -142,105 +157,61 @@ public final class CastPlayer extends BasePlayer {
|
||||
|
||||
// Media Queue manipulation methods.
|
||||
|
||||
/**
|
||||
* Loads a single item media queue. If no session is available, does nothing.
|
||||
*
|
||||
* @param item The item to load.
|
||||
* @param positionMs The position at which the playback should start in milliseconds relative to
|
||||
* the start of the item at {@code startIndex}. If {@link C#TIME_UNSET} is passed, playback
|
||||
* starts at position 0.
|
||||
* @return The Cast {@code PendingResult}, or null if no session is available.
|
||||
*/
|
||||
/** @deprecated Use {@link #setMediaItems(List, int, long)} instead. */
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public PendingResult<MediaChannelResult> loadItem(MediaQueueItem item, long positionMs) {
|
||||
return loadItems(new MediaQueueItem[] {item}, 0, positionMs, REPEAT_MODE_OFF);
|
||||
return setMediaItemsInternal(
|
||||
new MediaQueueItem[] {item}, /* startWindowIndex= */ 0, positionMs, repeatMode.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a media queue. If no session is available, does nothing.
|
||||
*
|
||||
* @param items The items to load.
|
||||
* @param startIndex The index of the item at which playback should start.
|
||||
* @param positionMs The position at which the playback should start in milliseconds relative to
|
||||
* the start of the item at {@code startIndex}. If {@link C#TIME_UNSET} is passed, playback
|
||||
* starts at position 0.
|
||||
* @param repeatMode The repeat mode for the created media queue.
|
||||
* @return The Cast {@code PendingResult}, or null if no session is available.
|
||||
* @deprecated Use {@link #setMediaItems(List, int, long)} and {@link #setRepeatMode(int)}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public PendingResult<MediaChannelResult> loadItems(
|
||||
MediaQueueItem[] items, int startIndex, long positionMs, @RepeatMode int repeatMode) {
|
||||
if (remoteMediaClient != null) {
|
||||
positionMs = positionMs != C.TIME_UNSET ? positionMs : 0;
|
||||
return remoteMediaClient.queueLoad(items, startIndex, getCastRepeatMode(repeatMode),
|
||||
positionMs, null);
|
||||
}
|
||||
return null;
|
||||
return setMediaItemsInternal(items, startIndex, positionMs, repeatMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a sequence of items to the media queue. If no media queue exists, does nothing.
|
||||
*
|
||||
* @param items The items to append.
|
||||
* @return The Cast {@code PendingResult}, or null if no media queue exists.
|
||||
*/
|
||||
/** @deprecated Use {@link #addMediaItems(List)} instead. */
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public PendingResult<MediaChannelResult> addItems(MediaQueueItem... items) {
|
||||
return addItems(MediaQueueItem.INVALID_ITEM_ID, items);
|
||||
return addMediaItemsInternal(items, MediaQueueItem.INVALID_ITEM_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a sequence of items into the media queue. If no media queue or period with id {@code
|
||||
* periodId} exist, does nothing.
|
||||
*
|
||||
* @param periodId The id of the period ({@link #getCurrentTimeline}) that corresponds to the item
|
||||
* that will follow immediately after the inserted items.
|
||||
* @param items The items to insert.
|
||||
* @return The Cast {@code PendingResult}, or null if no media queue or no period with id {@code
|
||||
* periodId} exist.
|
||||
*/
|
||||
/** @deprecated Use {@link #addMediaItems(int, List)} instead. */
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public PendingResult<MediaChannelResult> addItems(int periodId, MediaQueueItem... items) {
|
||||
if (getMediaStatus() != null && (periodId == MediaQueueItem.INVALID_ITEM_ID
|
||||
|| currentTimeline.getIndexOfPeriod(periodId) != C.INDEX_UNSET)) {
|
||||
return remoteMediaClient.queueInsertItems(items, periodId, null);
|
||||
if (periodId == MediaQueueItem.INVALID_ITEM_ID
|
||||
|| currentTimeline.getIndexOfPeriod(periodId) != C.INDEX_UNSET) {
|
||||
return addMediaItemsInternal(items, periodId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item from the media queue. If no media queue or period with id {@code periodId}
|
||||
* exist, does nothing.
|
||||
*
|
||||
* @param periodId The id of the period ({@link #getCurrentTimeline}) that corresponds to the item
|
||||
* to remove.
|
||||
* @return The Cast {@code PendingResult}, or null if no media queue or no period with id {@code
|
||||
* periodId} exist.
|
||||
*/
|
||||
/** @deprecated Use {@link #removeMediaItem(int)} instead. */
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public PendingResult<MediaChannelResult> removeItem(int periodId) {
|
||||
if (getMediaStatus() != null && currentTimeline.getIndexOfPeriod(periodId) != C.INDEX_UNSET) {
|
||||
return remoteMediaClient.queueRemoveItem(periodId, null);
|
||||
if (currentTimeline.getIndexOfPeriod(periodId) != C.INDEX_UNSET) {
|
||||
return removeMediaItemsInternal(new int[] {periodId});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves an existing item within the media queue. If no media queue or period with id {@code
|
||||
* periodId} exist, does nothing.
|
||||
*
|
||||
* @param periodId The id of the period ({@link #getCurrentTimeline}) that corresponds to the item
|
||||
* to move.
|
||||
* @param newIndex The target index of the item in the media queue. Must be in the range 0 <=
|
||||
* index < {@link Timeline#getPeriodCount()}, as provided by {@link #getCurrentTimeline()}.
|
||||
* @return The Cast {@code PendingResult}, or null if no media queue or no period with id {@code
|
||||
* periodId} exist.
|
||||
*/
|
||||
/** @deprecated Use {@link #moveMediaItem(int, int)} instead. */
|
||||
@Deprecated
|
||||
@Nullable
|
||||
public PendingResult<MediaChannelResult> moveItem(int periodId, int newIndex) {
|
||||
Assertions.checkArgument(newIndex >= 0 && newIndex < currentTimeline.getPeriodCount());
|
||||
if (getMediaStatus() != null && currentTimeline.getIndexOfPeriod(periodId) != C.INDEX_UNSET) {
|
||||
return remoteMediaClient.queueMoveItemToNewIndex(periodId, newIndex, null);
|
||||
Assertions.checkArgument(newIndex >= 0 && newIndex < currentTimeline.getWindowCount());
|
||||
int fromIndex = currentTimeline.getIndexOfPeriod(periodId);
|
||||
if (fromIndex != C.INDEX_UNSET && fromIndex != newIndex) {
|
||||
return moveMediaItemsInternal(new int[] {periodId}, fromIndex, newIndex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -325,6 +296,73 @@ public final class CastPlayer extends BasePlayer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaItems(
|
||||
List<MediaItem> mediaItems, int startWindowIndex, long startPositionMs) {
|
||||
setMediaItemsInternal(
|
||||
toMediaQueueItems(mediaItems), startWindowIndex, startPositionMs, repeatMode.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMediaItems(List<MediaItem> mediaItems) {
|
||||
addMediaItemsInternal(toMediaQueueItems(mediaItems), MediaQueueItem.INVALID_ITEM_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMediaItems(int index, List<MediaItem> mediaItems) {
|
||||
Assertions.checkArgument(index >= 0);
|
||||
int uid = MediaQueueItem.INVALID_ITEM_ID;
|
||||
if (index < currentTimeline.getWindowCount()) {
|
||||
uid = (int) currentTimeline.getWindow(/* windowIndex= */ index, window).uid;
|
||||
}
|
||||
addMediaItemsInternal(toMediaQueueItems(mediaItems), uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveMediaItems(int fromIndex, int toIndex, int newIndex) {
|
||||
Assertions.checkArgument(
|
||||
fromIndex >= 0
|
||||
&& fromIndex <= toIndex
|
||||
&& toIndex <= currentTimeline.getWindowCount()
|
||||
&& newIndex >= 0
|
||||
&& newIndex < currentTimeline.getWindowCount());
|
||||
newIndex = Math.min(newIndex, currentTimeline.getWindowCount() - (toIndex - fromIndex));
|
||||
if (fromIndex == toIndex || fromIndex == newIndex) {
|
||||
// Do nothing.
|
||||
return;
|
||||
}
|
||||
int[] uids = new int[toIndex - fromIndex];
|
||||
for (int i = 0; i < uids.length; i++) {
|
||||
uids[i] = (int) currentTimeline.getWindow(/* windowIndex= */ i + fromIndex, window).uid;
|
||||
}
|
||||
moveMediaItemsInternal(uids, fromIndex, newIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMediaItems(int fromIndex, int toIndex) {
|
||||
Assertions.checkArgument(
|
||||
fromIndex >= 0 && toIndex >= fromIndex && toIndex <= currentTimeline.getWindowCount());
|
||||
if (fromIndex == toIndex) {
|
||||
// Do nothing.
|
||||
return;
|
||||
}
|
||||
int[] uids = new int[toIndex - fromIndex];
|
||||
for (int i = 0; i < uids.length; i++) {
|
||||
uids[i] = (int) currentTimeline.getWindow(/* windowIndex= */ i + fromIndex, window).uid;
|
||||
}
|
||||
removeMediaItemsInternal(uids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearMediaItems() {
|
||||
removeMediaItems(/* fromIndex= */ 0, /* toIndex= */ currentTimeline.getWindowCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
@Player.State
|
||||
public int getPlaybackState() {
|
||||
@ -739,6 +777,58 @@ public final class CastPlayer extends BasePlayer {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PendingResult<MediaChannelResult> setMediaItemsInternal(
|
||||
MediaQueueItem[] mediaQueueItems,
|
||||
int startWindowIndex,
|
||||
long startPositionMs,
|
||||
@RepeatMode int repeatMode) {
|
||||
if (remoteMediaClient == null || mediaQueueItems.length == 0) {
|
||||
return null;
|
||||
}
|
||||
startPositionMs = startPositionMs == C.TIME_UNSET ? 0 : startPositionMs;
|
||||
if (startWindowIndex == C.INDEX_UNSET) {
|
||||
startWindowIndex = getCurrentWindowIndex();
|
||||
startPositionMs = getCurrentPosition();
|
||||
}
|
||||
return remoteMediaClient.queueLoad(
|
||||
mediaQueueItems,
|
||||
Math.min(startWindowIndex, mediaQueueItems.length - 1),
|
||||
getCastRepeatMode(repeatMode),
|
||||
startPositionMs,
|
||||
/* customData= */ null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PendingResult<MediaChannelResult> addMediaItemsInternal(MediaQueueItem[] items, int uid) {
|
||||
if (remoteMediaClient == null || getMediaStatus() == null) {
|
||||
return null;
|
||||
}
|
||||
return remoteMediaClient.queueInsertItems(items, uid, /* customData= */ null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PendingResult<MediaChannelResult> moveMediaItemsInternal(
|
||||
int[] uids, int fromIndex, int newIndex) {
|
||||
if (remoteMediaClient == null || getMediaStatus() == null) {
|
||||
return null;
|
||||
}
|
||||
int insertBeforeIndex = fromIndex < newIndex ? newIndex + uids.length : newIndex;
|
||||
int insertBeforeItemId = MediaQueueItem.INVALID_ITEM_ID;
|
||||
if (insertBeforeIndex < currentTimeline.getWindowCount()) {
|
||||
insertBeforeItemId = (int) currentTimeline.getWindow(insertBeforeIndex, window).uid;
|
||||
}
|
||||
return remoteMediaClient.queueReorderItems(uids, insertBeforeItemId, /* customData= */ null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PendingResult<MediaChannelResult> removeMediaItemsInternal(int[] uids) {
|
||||
if (remoteMediaClient == null || getMediaStatus() == null) {
|
||||
return null;
|
||||
}
|
||||
return remoteMediaClient.queueRemoveItems(uids, /* customData= */ null);
|
||||
}
|
||||
|
||||
private void setRepeatModeAndNotifyIfChanged(@Player.RepeatMode int repeatMode) {
|
||||
if (this.repeatMode.value != repeatMode) {
|
||||
this.repeatMode.value = repeatMode;
|
||||
@ -789,6 +879,7 @@ public final class CastPlayer extends BasePlayer {
|
||||
remoteMediaClient.addProgressListener(statusListener, PROGRESS_REPORT_PERIOD_MS);
|
||||
updateInternalStateAndNotifyIfChanged();
|
||||
} else {
|
||||
updateTimelineAndNotifyIfChanged();
|
||||
if (sessionAvailabilityListener != null) {
|
||||
sessionAvailabilityListener.onCastSessionUnavailable();
|
||||
}
|
||||
@ -888,6 +979,14 @@ public final class CastPlayer extends BasePlayer {
|
||||
}
|
||||
}
|
||||
|
||||
private MediaQueueItem[] toMediaQueueItems(List<MediaItem> mediaItems) {
|
||||
MediaQueueItem[] mediaQueueItems = new MediaQueueItem[mediaItems.size()];
|
||||
for (int i = 0; i < mediaItems.size(); i++) {
|
||||
mediaQueueItems[i] = mediaItemConverter.toMediaQueueItem(mediaItems.get(i));
|
||||
}
|
||||
return mediaQueueItems;
|
||||
}
|
||||
|
||||
// Internal classes.
|
||||
|
||||
private final class StatusListener
|
||||
|
@ -18,13 +18,22 @@ package com.google.android.exoplayer2.ext.cast;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.MockitoAnnotations.initMocks;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.android.gms.cast.MediaInfo;
|
||||
import com.google.android.gms.cast.MediaQueueItem;
|
||||
import com.google.android.gms.cast.MediaStatus;
|
||||
import com.google.android.gms.cast.framework.CastContext;
|
||||
import com.google.android.gms.cast.framework.CastSession;
|
||||
@ -33,6 +42,9 @@ import com.google.android.gms.cast.framework.media.MediaQueue;
|
||||
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
|
||||
import com.google.android.gms.common.api.PendingResult;
|
||||
import com.google.android.gms.common.api.ResultCallback;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -46,9 +58,13 @@ import org.mockito.Mockito;
|
||||
public class CastPlayerTest {
|
||||
|
||||
private CastPlayer castPlayer;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private RemoteMediaClient.Listener remoteMediaClientListener;
|
||||
|
||||
@Mock private RemoteMediaClient mockRemoteMediaClient;
|
||||
@Mock private MediaStatus mockMediaStatus;
|
||||
@Mock private MediaInfo mockMediaInfo;
|
||||
@Mock private MediaQueue mockMediaQueue;
|
||||
@Mock private CastContext mockCastContext;
|
||||
@Mock private SessionManager mockSessionManager;
|
||||
@ -62,6 +78,9 @@ public class CastPlayerTest {
|
||||
|
||||
@Captor private ArgumentCaptor<RemoteMediaClient.Listener> listenerArgumentCaptor;
|
||||
|
||||
@Captor private ArgumentCaptor<MediaQueueItem[]> queueItemsArgumentCaptor;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Before
|
||||
public void setUp() {
|
||||
initMocks(this);
|
||||
@ -209,4 +228,281 @@ public class CastPlayerTest {
|
||||
verify(mockListener).onRepeatModeChanged(Player.REPEAT_MODE_ONE);
|
||||
assertThat(castPlayer.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMediaItems_callsRemoteMediaClient() {
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
String sourceUri1 = "http://www.google.com/video1";
|
||||
String sourceUri2 = "http://www.google.com/video2";
|
||||
mediaItems.add(
|
||||
new MediaItem.Builder()
|
||||
.setSourceUri(sourceUri1)
|
||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||
.build());
|
||||
mediaItems.add(
|
||||
new MediaItem.Builder()
|
||||
.setSourceUri(sourceUri2)
|
||||
.setMimeType(MimeTypes.APPLICATION_MP4)
|
||||
.build());
|
||||
|
||||
castPlayer.setMediaItems(mediaItems, /* startWindowIndex= */ 1, /* startPositionMs= */ 2000L);
|
||||
|
||||
verify(mockRemoteMediaClient)
|
||||
.queueLoad(queueItemsArgumentCaptor.capture(), eq(1), anyInt(), eq(2000L), any());
|
||||
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
||||
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(sourceUri1);
|
||||
assertThat(mediaQueueItems[1].getMedia().getContentId()).isEqualTo(sourceUri2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMediaItems_doNotReset_callsRemoteMediaClient() {
|
||||
MediaItem.Builder builder = new MediaItem.Builder();
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
String sourceUri1 = "http://www.google.com/video1";
|
||||
String sourceUri2 = "http://www.google.com/video2";
|
||||
mediaItems.add(builder.setSourceUri(sourceUri1).setMimeType(MimeTypes.APPLICATION_MPD).build());
|
||||
mediaItems.add(builder.setSourceUri(sourceUri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
||||
int startWindowIndex = C.INDEX_UNSET;
|
||||
long startPositionMs = 2000L;
|
||||
|
||||
castPlayer.setMediaItems(mediaItems, startWindowIndex, startPositionMs);
|
||||
|
||||
verify(mockRemoteMediaClient)
|
||||
.queueLoad(queueItemsArgumentCaptor.capture(), eq(0), anyInt(), eq(0L), any());
|
||||
|
||||
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
||||
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(sourceUri1);
|
||||
assertThat(mediaQueueItems[1].getMedia().getContentId()).isEqualTo(sourceUri2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addMediaItems_callsRemoteMediaClient() {
|
||||
MediaItem.Builder builder = new MediaItem.Builder();
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
String sourceUri1 = "http://www.google.com/video1";
|
||||
String sourceUri2 = "http://www.google.com/video2";
|
||||
mediaItems.add(builder.setSourceUri(sourceUri1).setMimeType(MimeTypes.APPLICATION_MPD).build());
|
||||
mediaItems.add(builder.setSourceUri(sourceUri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
||||
|
||||
castPlayer.addMediaItems(mediaItems);
|
||||
|
||||
verify(mockRemoteMediaClient)
|
||||
.queueInsertItems(
|
||||
queueItemsArgumentCaptor.capture(), eq(MediaQueueItem.INVALID_ITEM_ID), any());
|
||||
|
||||
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
||||
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(sourceUri1);
|
||||
assertThat(mediaQueueItems[1].getMedia().getContentId()).isEqualTo(sourceUri2);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void addMediaItems_insertAtIndex_callsRemoteMediaClient() {
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 2);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
fillTimeline(mediaItems, mediaQueueItemIds);
|
||||
String sourceUri = "http://www.google.com/video3";
|
||||
MediaItem anotherMediaItem =
|
||||
new MediaItem.Builder()
|
||||
.setSourceUri(sourceUri)
|
||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||
.build();
|
||||
|
||||
// Add another on position 1
|
||||
int index = 1;
|
||||
castPlayer.addMediaItems(index, Collections.singletonList(anotherMediaItem));
|
||||
|
||||
verify(mockRemoteMediaClient)
|
||||
.queueInsertItems(
|
||||
queueItemsArgumentCaptor.capture(),
|
||||
eq((int) mediaItems.get(index).playbackProperties.tag),
|
||||
any());
|
||||
|
||||
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
||||
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(sourceUri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveMediaItem_callsRemoteMediaClient() {
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
fillTimeline(mediaItems, mediaQueueItemIds);
|
||||
|
||||
castPlayer.moveMediaItem(/* currentIndex= */ 1, /* newIndex= */ 2);
|
||||
|
||||
verify(mockRemoteMediaClient)
|
||||
.queueReorderItems(new int[] {2}, /* insertBeforeItemId= */ 4, /* customData= */ null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveMediaItem_toBegin_callsRemoteMediaClient() {
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
fillTimeline(mediaItems, mediaQueueItemIds);
|
||||
|
||||
castPlayer.moveMediaItem(/* currentIndex= */ 1, /* newIndex= */ 0);
|
||||
|
||||
verify(mockRemoteMediaClient)
|
||||
.queueReorderItems(new int[] {2}, /* insertBeforeItemId= */ 1, /* customData= */ null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveMediaItem_toEnd_callsRemoteMediaClient() {
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
fillTimeline(mediaItems, mediaQueueItemIds);
|
||||
|
||||
castPlayer.moveMediaItem(/* currentIndex= */ 1, /* newIndex= */ 4);
|
||||
|
||||
verify(mockRemoteMediaClient)
|
||||
.queueReorderItems(
|
||||
new int[] {2},
|
||||
/* insertBeforeItemId= */ MediaQueueItem.INVALID_ITEM_ID,
|
||||
/* customData= */ null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveMediaItems_callsRemoteMediaClient() {
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
fillTimeline(mediaItems, mediaQueueItemIds);
|
||||
|
||||
castPlayer.moveMediaItems(/* fromIndex= */ 0, /* toIndex= */ 3, /* newIndex= */ 1);
|
||||
|
||||
verify(mockRemoteMediaClient)
|
||||
.queueReorderItems(
|
||||
new int[] {1, 2, 3}, /* insertBeforeItemId= */ 5, /* customData= */ null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveMediaItems_toBeginning_callsRemoteMediaClient() {
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
fillTimeline(mediaItems, mediaQueueItemIds);
|
||||
|
||||
castPlayer.moveMediaItems(/* fromIndex= */ 1, /* toIndex= */ 4, /* newIndex= */ 0);
|
||||
|
||||
verify(mockRemoteMediaClient)
|
||||
.queueReorderItems(
|
||||
new int[] {2, 3, 4}, /* insertBeforeItemId= */ 1, /* customData= */ null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveMediaItems_toEnd_callsRemoteMediaClient() {
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
fillTimeline(mediaItems, mediaQueueItemIds);
|
||||
|
||||
castPlayer.moveMediaItems(/* fromIndex= */ 0, /* toIndex= */ 2, /* newIndex= */ 3);
|
||||
|
||||
verify(mockRemoteMediaClient)
|
||||
.queueReorderItems(
|
||||
new int[] {1, 2},
|
||||
/* insertBeforeItemId= */ MediaQueueItem.INVALID_ITEM_ID,
|
||||
/* customData= */ null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveMediaItems_noItems_doesNotCallRemoteMediaClient() {
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
fillTimeline(mediaItems, mediaQueueItemIds);
|
||||
|
||||
castPlayer.moveMediaItems(/* fromIndex= */ 1, /* toIndex= */ 1, /* newIndex= */ 0);
|
||||
|
||||
verify(mockRemoteMediaClient, never()).queueReorderItems(any(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveMediaItems_noMove_doesNotCallRemoteMediaClient() {
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
fillTimeline(mediaItems, mediaQueueItemIds);
|
||||
|
||||
castPlayer.moveMediaItems(/* fromIndex= */ 1, /* toIndex= */ 3, /* newIndex= */ 1);
|
||||
|
||||
verify(mockRemoteMediaClient, never()).queueReorderItems(any(), anyInt(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeMediaItems_callsRemoteMediaClient() {
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
fillTimeline(mediaItems, mediaQueueItemIds);
|
||||
|
||||
castPlayer.removeMediaItems(/* fromIndex= */ 1, /* toIndex= */ 4);
|
||||
|
||||
verify(mockRemoteMediaClient).queueRemoveItems(new int[] {2, 3, 4}, /* customData= */ null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearMediaItems_callsRemoteMediaClient() {
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
fillTimeline(mediaItems, mediaQueueItemIds);
|
||||
|
||||
castPlayer.clearMediaItems();
|
||||
|
||||
verify(mockRemoteMediaClient)
|
||||
.queueRemoveItems(new int[] {1, 2, 3, 4, 5}, /* customData= */ null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void addMediaItems_fillsTimeline() {
|
||||
Timeline.Window window = new Timeline.Window();
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
|
||||
fillTimeline(mediaItems, mediaQueueItemIds);
|
||||
|
||||
Timeline currentTimeline = castPlayer.getCurrentTimeline();
|
||||
for (int i = 0; i < mediaItems.size(); i++) {
|
||||
assertThat(currentTimeline.getWindow(/* windowIndex= */ i, window).uid)
|
||||
.isEqualTo(mediaItems.get(i).playbackProperties.tag);
|
||||
}
|
||||
}
|
||||
|
||||
private int[] createMediaQueueItemIds(int numberOfIds) {
|
||||
int[] mediaQueueItemIds = new int[numberOfIds];
|
||||
for (int i = 0; i < numberOfIds; i++) {
|
||||
mediaQueueItemIds[i] = i + 1;
|
||||
}
|
||||
return mediaQueueItemIds;
|
||||
}
|
||||
|
||||
private List<MediaItem> createMediaItems(int[] mediaQueueItemIds) {
|
||||
MediaItem.Builder builder = new MediaItem.Builder();
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
for (int mediaQueueItemId : mediaQueueItemIds) {
|
||||
MediaItem mediaItem =
|
||||
builder
|
||||
.setSourceUri("http://www.google.com/video" + mediaQueueItemId)
|
||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||
.setTag(mediaQueueItemId)
|
||||
.build();
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
return mediaItems;
|
||||
}
|
||||
|
||||
private void fillTimeline(List<MediaItem> mediaItems, int[] mediaQueueItemIds) {
|
||||
Assertions.checkState(mediaItems.size() == mediaQueueItemIds.length);
|
||||
List<MediaQueueItem> queueItems = new ArrayList<>();
|
||||
DefaultMediaItemConverter converter = new DefaultMediaItemConverter();
|
||||
for (MediaItem mediaItem : mediaItems) {
|
||||
queueItems.add(converter.toMediaQueueItem(mediaItem));
|
||||
}
|
||||
|
||||
// Set up mocks to allow the player to update the timeline.
|
||||
when(mockMediaQueue.getItemIds()).thenReturn(mediaQueueItemIds);
|
||||
when(mockMediaStatus.getCurrentItemId()).thenReturn(1);
|
||||
when(mockMediaStatus.getMediaInfo()).thenReturn(mockMediaInfo);
|
||||
when(mockMediaInfo.getStreamType()).thenReturn(MediaInfo.STREAM_TYPE_NONE);
|
||||
when(mockMediaStatus.getQueueItems()).thenReturn(queueItems);
|
||||
|
||||
castPlayer.addMediaItems(mediaItems);
|
||||
// Call listener to update the timeline of the player.
|
||||
remoteMediaClientListener.onQueueStatusUpdated();
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ package com.google.android.exoplayer2;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** Abstract base {@link Player} which implements common implementation independent methods. */
|
||||
public abstract class BasePlayer implements Player {
|
||||
@ -27,6 +29,54 @@ public abstract class BasePlayer implements Player {
|
||||
window = new Timeline.Window();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaItem(MediaItem mediaItem) {
|
||||
setMediaItems(Collections.singletonList(mediaItem));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaItem(MediaItem mediaItem, long startPositionMs) {
|
||||
setMediaItems(Collections.singletonList(mediaItem), /* startWindowIndex= */ 0, startPositionMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaItem(MediaItem mediaItem, boolean resetPosition) {
|
||||
setMediaItems(Collections.singletonList(mediaItem), resetPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaItems(List<MediaItem> mediaItems, boolean resetPosition) {
|
||||
setMediaItems(
|
||||
mediaItems, /* startWindowIndex= */ C.INDEX_UNSET, /* startPositionMs= */ C.TIME_UNSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaItems(List<MediaItem> mediaItems) {
|
||||
setMediaItems(mediaItems, /* resetPosition= */ true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMediaItem(int index, MediaItem mediaItem) {
|
||||
addMediaItems(index, Collections.singletonList(mediaItem));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMediaItem(MediaItem mediaItem) {
|
||||
addMediaItems(Collections.singletonList(mediaItem));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveMediaItem(int currentIndex, int newIndex) {
|
||||
if (currentIndex != newIndex) {
|
||||
moveMediaItems(/* fromIndex= */ currentIndex, /* toIndex= */ currentIndex + 1, newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMediaItem(int index) {
|
||||
removeMediaItems(/* fromIndex= */ index, /* toIndex= */ index + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void play() {
|
||||
setPlayWhenReady(true);
|
||||
|
@ -410,9 +410,6 @@ public interface ExoPlayer extends Player {
|
||||
@Deprecated
|
||||
void prepare(MediaSource mediaSource, boolean resetPosition, boolean resetState);
|
||||
|
||||
/** Prepares the player. */
|
||||
void prepare();
|
||||
|
||||
/**
|
||||
* Clears the playlist, adds the specified {@link MediaSource MediaSources} and resets the
|
||||
* position to the default position.
|
||||
@ -500,131 +497,6 @@ public interface ExoPlayer extends Player {
|
||||
*/
|
||||
void addMediaSources(int index, List<MediaSource> mediaSources);
|
||||
|
||||
/**
|
||||
* Clears the playlist, adds the specified {@link MediaItem MediaItems} and resets the position to
|
||||
* the default position.
|
||||
*
|
||||
* @param mediaItems The new {@link MediaItem MediaItems}.
|
||||
*/
|
||||
void setMediaItems(List<MediaItem> mediaItems);
|
||||
|
||||
/**
|
||||
* Clears the playlist and adds the specified {@link MediaItem MediaItems}.
|
||||
*
|
||||
* @param mediaItems The new {@link MediaItem MediaItems}.
|
||||
* @param resetPosition Whether the playback position should be reset to the default position in
|
||||
* the first {@link Timeline.Window}. If false, playback will start from the position defined
|
||||
* by {@link #getCurrentWindowIndex()} and {@link #getCurrentPosition()}.
|
||||
*/
|
||||
void setMediaItems(List<MediaItem> mediaItems, boolean resetPosition);
|
||||
|
||||
/**
|
||||
* Clears the playlist and adds the specified {@link MediaItem MediaItems}.
|
||||
*
|
||||
* @param mediaItems The new {@link MediaItem MediaItems}.
|
||||
* @param startWindowIndex The window index to start playback from. If {@link C#INDEX_UNSET} is
|
||||
* passed, the current position is not reset.
|
||||
* @param startPositionMs The position in milliseconds to start playback from. If {@link
|
||||
* C#TIME_UNSET} is passed, the default position of the given window is used. In any case, if
|
||||
* {@code startWindowIndex} is set to {@link C#INDEX_UNSET}, this parameter is ignored and the
|
||||
* position is not reset at all.
|
||||
*/
|
||||
void setMediaItems(List<MediaItem> mediaItems, int startWindowIndex, long startPositionMs);
|
||||
|
||||
/**
|
||||
* Clears the playlist, adds the specified {@link MediaItem} and resets the position to the
|
||||
* default position.
|
||||
*
|
||||
* @param mediaItem The new {@link MediaItem}.
|
||||
*/
|
||||
void setMediaItem(MediaItem mediaItem);
|
||||
|
||||
/**
|
||||
* Clears the playlist and adds the specified {@link MediaItem}.
|
||||
*
|
||||
* @param mediaItem The new {@link MediaItem}.
|
||||
* @param startPositionMs The position in milliseconds to start playback from.
|
||||
*/
|
||||
void setMediaItem(MediaItem mediaItem, long startPositionMs);
|
||||
|
||||
/**
|
||||
* Clears the playlist and adds the specified {@link MediaItem}.
|
||||
*
|
||||
* @param mediaItem The new {@link MediaItem}.
|
||||
* @param resetPosition Whether the playback position should be reset to the default position. If
|
||||
* false, playback will start from the position defined by {@link #getCurrentWindowIndex()}
|
||||
* and {@link #getCurrentPosition()}.
|
||||
*/
|
||||
void setMediaItem(MediaItem mediaItem, boolean resetPosition);
|
||||
|
||||
/**
|
||||
* Adds a media item to the end of the playlist.
|
||||
*
|
||||
* @param mediaItem The {@link MediaItem} to add.
|
||||
*/
|
||||
void addMediaItem(MediaItem mediaItem);
|
||||
|
||||
/**
|
||||
* Adds a media item at the given index of the playlist.
|
||||
*
|
||||
* @param index The index at which to add the item.
|
||||
* @param mediaItem The {@link MediaItem} to add.
|
||||
*/
|
||||
void addMediaItem(int index, MediaItem mediaItem);
|
||||
|
||||
/**
|
||||
* Adds a list of media items to the end of the playlist.
|
||||
*
|
||||
* @param mediaItems The {@link MediaItem MediaItems} to add.
|
||||
*/
|
||||
void addMediaItems(List<MediaItem> mediaItems);
|
||||
|
||||
/**
|
||||
* Adds a list of media items at the given index of the playlist.
|
||||
*
|
||||
* @param index The index at which to add the media items.
|
||||
* @param mediaItems The {@link MediaItem MediaItems} to add.
|
||||
*/
|
||||
void addMediaItems(int index, List<MediaItem> mediaItems);
|
||||
|
||||
/**
|
||||
* Moves the media item at the current index to the new index.
|
||||
*
|
||||
* @param currentIndex The current index of the media item to move.
|
||||
* @param newIndex The new index of the media item. If the new index is larger than the size of
|
||||
* the playlist the item is moved to the end of the playlist.
|
||||
*/
|
||||
void moveMediaItem(int currentIndex, int newIndex);
|
||||
|
||||
/**
|
||||
* Moves the media item range to the new index.
|
||||
*
|
||||
* @param fromIndex The start of the range to move.
|
||||
* @param toIndex The first item not to be included in the range (exclusive).
|
||||
* @param newIndex The new index of the first media item of the range. If the new index is larger
|
||||
* than the size of the remaining playlist after removing the range, the range is moved to the
|
||||
* end of the playlist.
|
||||
*/
|
||||
void moveMediaItems(int fromIndex, int toIndex, int newIndex);
|
||||
|
||||
/**
|
||||
* Removes the media item at the given index of the playlist.
|
||||
*
|
||||
* @param index The index at which to remove the media item.
|
||||
*/
|
||||
void removeMediaItem(int index);
|
||||
|
||||
/**
|
||||
* Removes a range of media items from the playlist.
|
||||
*
|
||||
* @param fromIndex The index at which to start removing media items.
|
||||
* @param toIndex The index of the first item to be kept (exclusive).
|
||||
*/
|
||||
void removeMediaItems(int fromIndex, int toIndex);
|
||||
|
||||
/** Clears the playlist. */
|
||||
void clearMediaItems();
|
||||
|
||||
/**
|
||||
* Sets the shuffle order.
|
||||
*
|
||||
|
@ -321,31 +321,6 @@ import java.util.concurrent.TimeoutException;
|
||||
prepare();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaItem(MediaItem mediaItem) {
|
||||
setMediaItems(Collections.singletonList(mediaItem));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaItem(MediaItem mediaItem, long startPositionMs) {
|
||||
setMediaItems(Collections.singletonList(mediaItem), /* startWindowIndex= */ 0, startPositionMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaItem(MediaItem mediaItem, boolean resetPosition) {
|
||||
setMediaItems(Collections.singletonList(mediaItem), resetPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaItems(List<MediaItem> mediaItems) {
|
||||
setMediaItems(mediaItems, /* resetPosition= */ true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaItems(List<MediaItem> mediaItems, boolean resetPosition) {
|
||||
setMediaSources(createMediaSources(mediaItems), resetPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaItems(
|
||||
List<MediaItem> mediaItems, int startWindowIndex, long startPositionMs) {
|
||||
@ -389,16 +364,6 @@ import java.util.concurrent.TimeoutException;
|
||||
mediaSources, startWindowIndex, startPositionMs, /* resetToDefaultPosition= */ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMediaItem(int index, MediaItem mediaItem) {
|
||||
addMediaItems(index, Collections.singletonList(mediaItem));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMediaItem(MediaItem mediaItem) {
|
||||
addMediaItems(Collections.singletonList(mediaItem));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMediaItems(List<MediaItem> mediaItems) {
|
||||
addMediaItems(/* index= */ mediaSourceHolders.size(), mediaItems);
|
||||
@ -444,23 +409,12 @@ import java.util.concurrent.TimeoutException;
|
||||
/* seekProcessed= */ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMediaItem(int index) {
|
||||
removeMediaItemsInternal(/* fromIndex= */ index, /* toIndex= */ index + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMediaItems(int fromIndex, int toIndex) {
|
||||
Assertions.checkArgument(toIndex > fromIndex);
|
||||
removeMediaItemsInternal(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveMediaItem(int currentIndex, int newIndex) {
|
||||
Assertions.checkArgument(currentIndex != newIndex);
|
||||
moveMediaItems(/* fromIndex= */ currentIndex, /* toIndex= */ currentIndex + 1, newIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveMediaItems(int fromIndex, int toIndex, int newFromIndex) {
|
||||
Assertions.checkArgument(
|
||||
|
@ -39,6 +39,7 @@ import com.google.android.exoplayer2.video.spherical.CameraMotionListener;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A media player interface defining traditional high-level functionality, such as the ability to
|
||||
@ -754,6 +755,134 @@ public interface Player {
|
||||
*/
|
||||
void removeListener(EventListener listener);
|
||||
|
||||
/**
|
||||
* Clears the playlist, adds the specified {@link MediaItem MediaItems} and resets the position to
|
||||
* the default position.
|
||||
*
|
||||
* @param mediaItems The new {@link MediaItem MediaItems}.
|
||||
*/
|
||||
void setMediaItems(List<MediaItem> mediaItems);
|
||||
|
||||
/**
|
||||
* Clears the playlist and adds the specified {@link MediaItem MediaItems}.
|
||||
*
|
||||
* @param mediaItems The new {@link MediaItem MediaItems}.
|
||||
* @param resetPosition Whether the playback position should be reset to the default position in
|
||||
* the first {@link Timeline.Window}. If false, playback will start from the position defined
|
||||
* by {@link #getCurrentWindowIndex()} and {@link #getCurrentPosition()}.
|
||||
*/
|
||||
void setMediaItems(List<MediaItem> mediaItems, boolean resetPosition);
|
||||
|
||||
/**
|
||||
* Clears the playlist and adds the specified {@link MediaItem MediaItems}.
|
||||
*
|
||||
* @param mediaItems The new {@link MediaItem MediaItems}.
|
||||
* @param startWindowIndex The window index to start playback from. If {@link C#INDEX_UNSET} is
|
||||
* passed, the current position is not reset.
|
||||
* @param startPositionMs The position in milliseconds to start playback from. If {@link
|
||||
* C#TIME_UNSET} is passed, the default position of the given window is used. In any case, if
|
||||
* {@code startWindowIndex} is set to {@link C#INDEX_UNSET}, this parameter is ignored and the
|
||||
* position is not reset at all.
|
||||
*/
|
||||
void setMediaItems(List<MediaItem> mediaItems, int startWindowIndex, long startPositionMs);
|
||||
|
||||
/**
|
||||
* Clears the playlist, adds the specified {@link MediaItem} and resets the position to the
|
||||
* default position.
|
||||
*
|
||||
* @param mediaItem The new {@link MediaItem}.
|
||||
*/
|
||||
void setMediaItem(MediaItem mediaItem);
|
||||
|
||||
/**
|
||||
* Clears the playlist and adds the specified {@link MediaItem}.
|
||||
*
|
||||
* @param mediaItem The new {@link MediaItem}.
|
||||
* @param startPositionMs The position in milliseconds to start playback from.
|
||||
*/
|
||||
void setMediaItem(MediaItem mediaItem, long startPositionMs);
|
||||
|
||||
/**
|
||||
* Clears the playlist and adds the specified {@link MediaItem}.
|
||||
*
|
||||
* @param mediaItem The new {@link MediaItem}.
|
||||
* @param resetPosition Whether the playback position should be reset to the default position. If
|
||||
* false, playback will start from the position defined by {@link #getCurrentWindowIndex()}
|
||||
* and {@link #getCurrentPosition()}.
|
||||
*/
|
||||
void setMediaItem(MediaItem mediaItem, boolean resetPosition);
|
||||
|
||||
/**
|
||||
* Adds a media item to the end of the playlist.
|
||||
*
|
||||
* @param mediaItem The {@link MediaItem} to add.
|
||||
*/
|
||||
void addMediaItem(MediaItem mediaItem);
|
||||
|
||||
/**
|
||||
* Adds a media item at the given index of the playlist.
|
||||
*
|
||||
* @param index The index at which to add the item.
|
||||
* @param mediaItem The {@link MediaItem} to add.
|
||||
*/
|
||||
void addMediaItem(int index, MediaItem mediaItem);
|
||||
|
||||
/**
|
||||
* Adds a list of media items to the end of the playlist.
|
||||
*
|
||||
* @param mediaItems The {@link MediaItem MediaItems} to add.
|
||||
*/
|
||||
void addMediaItems(List<MediaItem> mediaItems);
|
||||
|
||||
/**
|
||||
* Adds a list of media items at the given index of the playlist.
|
||||
*
|
||||
* @param index The index at which to add the media items.
|
||||
* @param mediaItems The {@link MediaItem MediaItems} to add.
|
||||
*/
|
||||
void addMediaItems(int index, List<MediaItem> mediaItems);
|
||||
|
||||
/**
|
||||
* Moves the media item at the current index to the new index.
|
||||
*
|
||||
* @param currentIndex The current index of the media item to move.
|
||||
* @param newIndex The new index of the media item. If the new index is larger than the size of
|
||||
* the playlist the item is moved to the end of the playlist.
|
||||
*/
|
||||
void moveMediaItem(int currentIndex, int newIndex);
|
||||
|
||||
/**
|
||||
* Moves the media item range to the new index.
|
||||
*
|
||||
* @param fromIndex The start of the range to move.
|
||||
* @param toIndex The first item not to be included in the range (exclusive).
|
||||
* @param newIndex The new index of the first media item of the range. If the new index is larger
|
||||
* than the size of the remaining playlist after removing the range, the range is moved to the
|
||||
* end of the playlist.
|
||||
*/
|
||||
void moveMediaItems(int fromIndex, int toIndex, int newIndex);
|
||||
|
||||
/**
|
||||
* Removes the media item at the given index of the playlist.
|
||||
*
|
||||
* @param index The index at which to remove the media item.
|
||||
*/
|
||||
void removeMediaItem(int index);
|
||||
|
||||
/**
|
||||
* Removes a range of media items from the playlist.
|
||||
*
|
||||
* @param fromIndex The index at which to start removing media items.
|
||||
* @param toIndex The index of the first item to be kept (exclusive).
|
||||
*/
|
||||
void removeMediaItems(int fromIndex, int toIndex);
|
||||
|
||||
/** Clears the playlist. */
|
||||
void clearMediaItems();
|
||||
|
||||
/** Prepares the player. */
|
||||
void prepare();
|
||||
|
||||
/**
|
||||
* Returns the current {@link State playback state} of the player.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user