Use the public MediaItem in the timeline of CastPlayer
The media item needs to be assigned to `Window.mediaItem` in `CastTimeline.setWindow`. For this the `MediaItem` needs to be available in the timeline. When a `MediaItem` is passed to the `set/addMediaItems` method, we can't yet know the Cast `MediaQueueItem.itemId` that is generated on the device and arrives with an async update of the `RemoteMediaClient` state. Hence in the `CastTimelineTracker`, we need to store the `MediaItem` by Casts's `MediaItem.contentId`. When we then receive the updated queue, we look the media item up by the content ID to augment the `ItemData` that is available in the `CastTimeline`. Issue: androidx/media#25 Issue: google/ExoPlayer#8212 #minor-release PiperOrigin-RevId: 460325235
This commit is contained in:
parent
7dc54efdb9
commit
30fbc3a27d
@ -35,6 +35,11 @@
|
|||||||
* Leanback extension:
|
* Leanback extension:
|
||||||
* Listen to `playWhenReady` changes in `LeanbackAdapter`
|
* Listen to `playWhenReady` changes in `LeanbackAdapter`
|
||||||
([10420](https://github.com/google/ExoPlayer/issues/10420)).
|
([10420](https://github.com/google/ExoPlayer/issues/10420)).
|
||||||
|
* Cast:
|
||||||
|
* Use the `MediaItem` that has been passed to the playlist methods as
|
||||||
|
`Window.mediaItem` in `CastTimeline`
|
||||||
|
([#25](https://github.com/androidx/media/issues/25),
|
||||||
|
[#8212](https://github.com/google/ExoPlayer/issues/8212)).
|
||||||
|
|
||||||
### 1.0.0-beta01 (2022-06-16)
|
### 1.0.0-beta01 (2022-06-16)
|
||||||
|
|
||||||
|
@ -200,7 +200,7 @@ public final class CastPlayer extends BasePlayer {
|
|||||||
this.mediaItemConverter = mediaItemConverter;
|
this.mediaItemConverter = mediaItemConverter;
|
||||||
this.seekBackIncrementMs = seekBackIncrementMs;
|
this.seekBackIncrementMs = seekBackIncrementMs;
|
||||||
this.seekForwardIncrementMs = seekForwardIncrementMs;
|
this.seekForwardIncrementMs = seekForwardIncrementMs;
|
||||||
timelineTracker = new CastTimelineTracker();
|
timelineTracker = new CastTimelineTracker(mediaItemConverter);
|
||||||
period = new Timeline.Period();
|
period = new Timeline.Period();
|
||||||
statusListener = new StatusListener();
|
statusListener = new StatusListener();
|
||||||
seekResultCallback = new SeekResultCallback();
|
seekResultCallback = new SeekResultCallback();
|
||||||
@ -285,8 +285,7 @@ public final class CastPlayer extends BasePlayer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMediaItems(List<MediaItem> mediaItems, int startIndex, long startPositionMs) {
|
public void setMediaItems(List<MediaItem> mediaItems, int startIndex, long startPositionMs) {
|
||||||
setMediaItemsInternal(
|
setMediaItemsInternal(mediaItems, startIndex, startPositionMs, repeatMode.value);
|
||||||
toMediaQueueItems(mediaItems), startIndex, startPositionMs, repeatMode.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -296,7 +295,7 @@ public final class CastPlayer extends BasePlayer {
|
|||||||
if (index < currentTimeline.getWindowCount()) {
|
if (index < currentTimeline.getWindowCount()) {
|
||||||
uid = (int) currentTimeline.getWindow(/* windowIndex= */ index, window).uid;
|
uid = (int) currentTimeline.getWindow(/* windowIndex= */ index, window).uid;
|
||||||
}
|
}
|
||||||
addMediaItemsInternal(toMediaQueueItems(mediaItems), uid);
|
addMediaItemsInternal(mediaItems, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1022,14 +1021,13 @@ public final class CastPlayer extends BasePlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private void setMediaItemsInternal(
|
||||||
private PendingResult<MediaChannelResult> setMediaItemsInternal(
|
List<MediaItem> mediaItems,
|
||||||
MediaQueueItem[] mediaQueueItems,
|
|
||||||
int startIndex,
|
int startIndex,
|
||||||
long startPositionMs,
|
long startPositionMs,
|
||||||
@RepeatMode int repeatMode) {
|
@RepeatMode int repeatMode) {
|
||||||
if (remoteMediaClient == null || mediaQueueItems.length == 0) {
|
if (remoteMediaClient == null || mediaItems.isEmpty()) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
startPositionMs = startPositionMs == C.TIME_UNSET ? 0 : startPositionMs;
|
startPositionMs = startPositionMs == C.TIME_UNSET ? 0 : startPositionMs;
|
||||||
if (startIndex == C.INDEX_UNSET) {
|
if (startIndex == C.INDEX_UNSET) {
|
||||||
@ -1040,34 +1038,35 @@ public final class CastPlayer extends BasePlayer {
|
|||||||
if (!currentTimeline.isEmpty()) {
|
if (!currentTimeline.isEmpty()) {
|
||||||
pendingMediaItemRemovalPosition = getCurrentPositionInfo();
|
pendingMediaItemRemovalPosition = getCurrentPositionInfo();
|
||||||
}
|
}
|
||||||
return remoteMediaClient.queueLoad(
|
MediaQueueItem[] mediaQueueItems = toMediaQueueItems(mediaItems);
|
||||||
|
timelineTracker.onMediaItemsSet(mediaItems, mediaQueueItems);
|
||||||
|
remoteMediaClient.queueLoad(
|
||||||
mediaQueueItems,
|
mediaQueueItems,
|
||||||
min(startIndex, mediaQueueItems.length - 1),
|
min(startIndex, mediaItems.size() - 1),
|
||||||
getCastRepeatMode(repeatMode),
|
getCastRepeatMode(repeatMode),
|
||||||
startPositionMs,
|
startPositionMs,
|
||||||
/* customData= */ null);
|
/* customData= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private void addMediaItemsInternal(List<MediaItem> mediaItems, int uid) {
|
||||||
private PendingResult<MediaChannelResult> addMediaItemsInternal(MediaQueueItem[] items, int uid) {
|
|
||||||
if (remoteMediaClient == null || getMediaStatus() == null) {
|
if (remoteMediaClient == null || getMediaStatus() == null) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
return remoteMediaClient.queueInsertItems(items, uid, /* customData= */ null);
|
MediaQueueItem[] itemsToInsert = toMediaQueueItems(mediaItems);
|
||||||
|
timelineTracker.onMediaItemsAdded(mediaItems, itemsToInsert);
|
||||||
|
remoteMediaClient.queueInsertItems(itemsToInsert, uid, /* customData= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private void moveMediaItemsInternal(int[] uids, int fromIndex, int newIndex) {
|
||||||
private PendingResult<MediaChannelResult> moveMediaItemsInternal(
|
|
||||||
int[] uids, int fromIndex, int newIndex) {
|
|
||||||
if (remoteMediaClient == null || getMediaStatus() == null) {
|
if (remoteMediaClient == null || getMediaStatus() == null) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
int insertBeforeIndex = fromIndex < newIndex ? newIndex + uids.length : newIndex;
|
int insertBeforeIndex = fromIndex < newIndex ? newIndex + uids.length : newIndex;
|
||||||
int insertBeforeItemId = MediaQueueItem.INVALID_ITEM_ID;
|
int insertBeforeItemId = MediaQueueItem.INVALID_ITEM_ID;
|
||||||
if (insertBeforeIndex < currentTimeline.getWindowCount()) {
|
if (insertBeforeIndex < currentTimeline.getWindowCount()) {
|
||||||
insertBeforeItemId = (int) currentTimeline.getWindow(insertBeforeIndex, window).uid;
|
insertBeforeItemId = (int) currentTimeline.getWindow(insertBeforeIndex, window).uid;
|
||||||
}
|
}
|
||||||
return remoteMediaClient.queueReorderItems(uids, insertBeforeItemId, /* customData= */ null);
|
remoteMediaClient.queueReorderItems(uids, insertBeforeItemId, /* customData= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -15,13 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.cast;
|
package androidx.media3.cast;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
|
import com.google.android.gms.cast.MediaInfo;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/** A {@link Timeline} for Cast media queues. */
|
/** A {@link Timeline} for Cast media queues. */
|
||||||
@ -30,12 +30,16 @@ import java.util.Arrays;
|
|||||||
/** Holds {@link Timeline} related data for a Cast media item. */
|
/** Holds {@link Timeline} related data for a Cast media item. */
|
||||||
public static final class ItemData {
|
public static final class ItemData {
|
||||||
|
|
||||||
|
/* package */ static final String UNKNOWN_CONTENT_ID = "UNKNOWN_CONTENT_ID";
|
||||||
|
|
||||||
/** Holds no media information. */
|
/** Holds no media information. */
|
||||||
public static final ItemData EMPTY =
|
public static final ItemData EMPTY =
|
||||||
new ItemData(
|
new ItemData(
|
||||||
/* durationUs= */ C.TIME_UNSET,
|
/* durationUs= */ C.TIME_UNSET,
|
||||||
/* defaultPositionUs= */ C.TIME_UNSET,
|
/* defaultPositionUs= */ C.TIME_UNSET,
|
||||||
/* isLive= */ false);
|
/* isLive= */ false,
|
||||||
|
MediaItem.EMPTY,
|
||||||
|
UNKNOWN_CONTENT_ID);
|
||||||
|
|
||||||
/** The duration of the item in microseconds, or {@link C#TIME_UNSET} if unknown. */
|
/** The duration of the item in microseconds, or {@link C#TIME_UNSET} if unknown. */
|
||||||
public final long durationUs;
|
public final long durationUs;
|
||||||
@ -45,6 +49,10 @@ import java.util.Arrays;
|
|||||||
public final long defaultPositionUs;
|
public final long defaultPositionUs;
|
||||||
/** Whether the item is live content, or {@code false} if unknown. */
|
/** Whether the item is live content, or {@code false} if unknown. */
|
||||||
public final boolean isLive;
|
public final boolean isLive;
|
||||||
|
/** The original media item that has been set or added to the playlist. */
|
||||||
|
public final MediaItem mediaItem;
|
||||||
|
/** The {@linkplain MediaInfo#getContentId() content ID} of the cast media queue item. */
|
||||||
|
public final String contentId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
@ -52,11 +60,20 @@ import java.util.Arrays;
|
|||||||
* @param durationUs See {@link #durationsUs}.
|
* @param durationUs See {@link #durationsUs}.
|
||||||
* @param defaultPositionUs See {@link #defaultPositionUs}.
|
* @param defaultPositionUs See {@link #defaultPositionUs}.
|
||||||
* @param isLive See {@link #isLive}.
|
* @param isLive See {@link #isLive}.
|
||||||
|
* @param mediaItem See {@link #mediaItem}.
|
||||||
|
* @param contentId See {@link #contentId}.
|
||||||
*/
|
*/
|
||||||
public ItemData(long durationUs, long defaultPositionUs, boolean isLive) {
|
public ItemData(
|
||||||
|
long durationUs,
|
||||||
|
long defaultPositionUs,
|
||||||
|
boolean isLive,
|
||||||
|
MediaItem mediaItem,
|
||||||
|
String contentId) {
|
||||||
this.durationUs = durationUs;
|
this.durationUs = durationUs;
|
||||||
this.defaultPositionUs = defaultPositionUs;
|
this.defaultPositionUs = defaultPositionUs;
|
||||||
this.isLive = isLive;
|
this.isLive = isLive;
|
||||||
|
this.mediaItem = mediaItem;
|
||||||
|
this.contentId = contentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,14 +83,23 @@ import java.util.Arrays;
|
|||||||
* @param defaultPositionUs The default start position in microseconds, or {@link C#TIME_UNSET}
|
* @param defaultPositionUs The default start position in microseconds, or {@link C#TIME_UNSET}
|
||||||
* if unknown.
|
* if unknown.
|
||||||
* @param isLive Whether the item is live, or {@code false} if unknown.
|
* @param isLive Whether the item is live, or {@code false} if unknown.
|
||||||
|
* @param mediaItem The media item.
|
||||||
|
* @param contentId The content ID.
|
||||||
*/
|
*/
|
||||||
public ItemData copyWithNewValues(long durationUs, long defaultPositionUs, boolean isLive) {
|
public ItemData copyWithNewValues(
|
||||||
|
long durationUs,
|
||||||
|
long defaultPositionUs,
|
||||||
|
boolean isLive,
|
||||||
|
MediaItem mediaItem,
|
||||||
|
String contentId) {
|
||||||
if (durationUs == this.durationUs
|
if (durationUs == this.durationUs
|
||||||
&& defaultPositionUs == this.defaultPositionUs
|
&& defaultPositionUs == this.defaultPositionUs
|
||||||
&& isLive == this.isLive) {
|
&& isLive == this.isLive
|
||||||
|
&& contentId.equals(this.contentId)
|
||||||
|
&& mediaItem.equals(this.mediaItem)) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new ItemData(durationUs, defaultPositionUs, isLive);
|
return new ItemData(durationUs, defaultPositionUs, isLive, mediaItem, contentId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,6 +108,7 @@ import java.util.Arrays;
|
|||||||
new CastTimeline(new int[0], new SparseArray<>());
|
new CastTimeline(new int[0], new SparseArray<>());
|
||||||
|
|
||||||
private final SparseIntArray idsToIndex;
|
private final SparseIntArray idsToIndex;
|
||||||
|
private final MediaItem[] mediaItems;
|
||||||
private final int[] ids;
|
private final int[] ids;
|
||||||
private final long[] durationsUs;
|
private final long[] durationsUs;
|
||||||
private final long[] defaultPositionsUs;
|
private final long[] defaultPositionsUs;
|
||||||
@ -100,10 +127,12 @@ import java.util.Arrays;
|
|||||||
durationsUs = new long[itemCount];
|
durationsUs = new long[itemCount];
|
||||||
defaultPositionsUs = new long[itemCount];
|
defaultPositionsUs = new long[itemCount];
|
||||||
isLive = new boolean[itemCount];
|
isLive = new boolean[itemCount];
|
||||||
|
mediaItems = new MediaItem[itemCount];
|
||||||
for (int i = 0; i < ids.length; i++) {
|
for (int i = 0; i < ids.length; i++) {
|
||||||
int id = ids[i];
|
int id = ids[i];
|
||||||
idsToIndex.put(id, i);
|
idsToIndex.put(id, i);
|
||||||
ItemData data = itemIdToData.get(id, ItemData.EMPTY);
|
ItemData data = itemIdToData.get(id, ItemData.EMPTY);
|
||||||
|
mediaItems[i] = data.mediaItem.buildUpon().setTag(id).build();
|
||||||
durationsUs[i] = data.durationUs;
|
durationsUs[i] = data.durationUs;
|
||||||
defaultPositionsUs[i] = data.defaultPositionUs == C.TIME_UNSET ? 0 : data.defaultPositionUs;
|
defaultPositionsUs[i] = data.defaultPositionUs == C.TIME_UNSET ? 0 : data.defaultPositionUs;
|
||||||
isLive[i] = data.isLive;
|
isLive[i] = data.isLive;
|
||||||
@ -121,18 +150,16 @@ import java.util.Arrays;
|
|||||||
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
|
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
|
||||||
long durationUs = durationsUs[windowIndex];
|
long durationUs = durationsUs[windowIndex];
|
||||||
boolean isDynamic = durationUs == C.TIME_UNSET;
|
boolean isDynamic = durationUs == C.TIME_UNSET;
|
||||||
MediaItem mediaItem =
|
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(ids[windowIndex]).build();
|
|
||||||
return window.set(
|
return window.set(
|
||||||
/* uid= */ ids[windowIndex],
|
/* uid= */ ids[windowIndex],
|
||||||
/* mediaItem= */ mediaItem,
|
/* mediaItem= */ mediaItems[windowIndex],
|
||||||
/* manifest= */ null,
|
/* manifest= */ null,
|
||||||
/* presentationStartTimeMs= */ C.TIME_UNSET,
|
/* presentationStartTimeMs= */ C.TIME_UNSET,
|
||||||
/* windowStartTimeMs= */ C.TIME_UNSET,
|
/* windowStartTimeMs= */ C.TIME_UNSET,
|
||||||
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
||||||
/* isSeekable= */ !isDynamic,
|
/* isSeekable= */ !isDynamic,
|
||||||
isDynamic,
|
isDynamic,
|
||||||
isLive[windowIndex] ? mediaItem.liveConfiguration : null,
|
isLive[windowIndex] ? mediaItems[windowIndex].liveConfiguration : null,
|
||||||
defaultPositionsUs[windowIndex],
|
defaultPositionsUs[windowIndex],
|
||||||
durationUs,
|
durationUs,
|
||||||
/* firstPeriodIndex= */ windowIndex,
|
/* firstPeriodIndex= */ windowIndex,
|
||||||
|
@ -15,14 +15,23 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.cast;
|
package androidx.media3.cast;
|
||||||
|
|
||||||
|
import static androidx.media3.cast.CastTimeline.ItemData.UNKNOWN_CONTENT_ID;
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
|
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.Player;
|
||||||
import com.google.android.gms.cast.MediaInfo;
|
import com.google.android.gms.cast.MediaInfo;
|
||||||
import com.google.android.gms.cast.MediaQueueItem;
|
import com.google.android.gms.cast.MediaQueueItem;
|
||||||
import com.google.android.gms.cast.MediaStatus;
|
import com.google.android.gms.cast.MediaStatus;
|
||||||
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
|
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates {@link CastTimeline CastTimelines} from cast receiver app status updates.
|
* Creates {@link CastTimeline CastTimelines} from cast receiver app status updates.
|
||||||
@ -33,9 +42,47 @@ import java.util.HashSet;
|
|||||||
/* package */ final class CastTimelineTracker {
|
/* package */ final class CastTimelineTracker {
|
||||||
|
|
||||||
private final SparseArray<CastTimeline.ItemData> itemIdToData;
|
private final SparseArray<CastTimeline.ItemData> itemIdToData;
|
||||||
|
private final MediaItemConverter mediaItemConverter;
|
||||||
|
@VisibleForTesting /* package */ final HashMap<String, MediaItem> mediaItemsByContentId;
|
||||||
|
|
||||||
public CastTimelineTracker() {
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
*
|
||||||
|
* @param mediaItemConverter The converter used to convert from a {@link MediaQueueItem} to a
|
||||||
|
* {@link MediaItem}.
|
||||||
|
*/
|
||||||
|
public CastTimelineTracker(MediaItemConverter mediaItemConverter) {
|
||||||
|
this.mediaItemConverter = mediaItemConverter;
|
||||||
itemIdToData = new SparseArray<>();
|
itemIdToData = new SparseArray<>();
|
||||||
|
mediaItemsByContentId = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when media items {@linkplain Player#setMediaItems have been set to the playlist} and are
|
||||||
|
* sent to the cast playback queue. A future queue update of the {@link RemoteMediaClient} will
|
||||||
|
* reflect this addition.
|
||||||
|
*
|
||||||
|
* @param mediaItems The media items that have been set.
|
||||||
|
* @param mediaQueueItems The corresponding media queue items.
|
||||||
|
*/
|
||||||
|
public void onMediaItemsSet(List<MediaItem> mediaItems, MediaQueueItem[] mediaQueueItems) {
|
||||||
|
mediaItemsByContentId.clear();
|
||||||
|
onMediaItemsAdded(mediaItems, mediaQueueItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when media items {@linkplain Player#addMediaItems(List) have been added} and are sent to
|
||||||
|
* the cast playback queue. A future queue update of the {@link RemoteMediaClient} will reflect
|
||||||
|
* this addition.
|
||||||
|
*
|
||||||
|
* @param mediaItems The media items that have been added.
|
||||||
|
* @param mediaQueueItems The corresponding media queue items.
|
||||||
|
*/
|
||||||
|
public void onMediaItemsAdded(List<MediaItem> mediaItems, MediaQueueItem[] mediaQueueItems) {
|
||||||
|
for (int i = 0; i < mediaItems.size(); i++) {
|
||||||
|
mediaItemsByContentId.put(
|
||||||
|
checkNotNull(mediaQueueItems[i].getMedia()).getContentId(), mediaItems.get(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -63,18 +110,36 @@ import java.util.HashSet;
|
|||||||
}
|
}
|
||||||
|
|
||||||
int currentItemId = mediaStatus.getCurrentItemId();
|
int currentItemId = mediaStatus.getCurrentItemId();
|
||||||
|
String currentContentId = checkStateNotNull(mediaStatus.getMediaInfo()).getContentId();
|
||||||
|
MediaItem mediaItem = mediaItemsByContentId.get(currentContentId);
|
||||||
updateItemData(
|
updateItemData(
|
||||||
currentItemId, mediaStatus.getMediaInfo(), /* defaultPositionUs= */ C.TIME_UNSET);
|
currentItemId,
|
||||||
|
mediaItem != null ? mediaItem : MediaItem.EMPTY,
|
||||||
|
mediaStatus.getMediaInfo(),
|
||||||
|
currentContentId,
|
||||||
|
/* defaultPositionUs= */ C.TIME_UNSET);
|
||||||
|
|
||||||
for (MediaQueueItem item : mediaStatus.getQueueItems()) {
|
for (MediaQueueItem queueItem : mediaStatus.getQueueItems()) {
|
||||||
long defaultPositionUs = (long) (item.getStartTime() * C.MICROS_PER_SECOND);
|
long defaultPositionUs = (long) (queueItem.getStartTime() * C.MICROS_PER_SECOND);
|
||||||
updateItemData(item.getItemId(), item.getMedia(), defaultPositionUs);
|
@Nullable MediaInfo mediaInfo = queueItem.getMedia();
|
||||||
|
String contentId = mediaInfo != null ? mediaInfo.getContentId() : UNKNOWN_CONTENT_ID;
|
||||||
|
mediaItem = mediaItemsByContentId.get(contentId);
|
||||||
|
updateItemData(
|
||||||
|
queueItem.getItemId(),
|
||||||
|
mediaItem != null ? mediaItem : mediaItemConverter.toMediaItem(queueItem),
|
||||||
|
mediaInfo,
|
||||||
|
contentId,
|
||||||
|
defaultPositionUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CastTimeline(itemIds, itemIdToData);
|
return new CastTimeline(itemIds, itemIdToData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateItemData(int itemId, @Nullable MediaInfo mediaInfo, long defaultPositionUs) {
|
private void updateItemData(
|
||||||
|
int itemId,
|
||||||
|
MediaItem mediaItem,
|
||||||
|
@Nullable MediaInfo mediaInfo,
|
||||||
|
String contentId,
|
||||||
|
long defaultPositionUs) {
|
||||||
CastTimeline.ItemData previousData = itemIdToData.get(itemId, CastTimeline.ItemData.EMPTY);
|
CastTimeline.ItemData previousData = itemIdToData.get(itemId, CastTimeline.ItemData.EMPTY);
|
||||||
long durationUs = CastUtils.getStreamDurationUs(mediaInfo);
|
long durationUs = CastUtils.getStreamDurationUs(mediaInfo);
|
||||||
if (durationUs == C.TIME_UNSET) {
|
if (durationUs == C.TIME_UNSET) {
|
||||||
@ -87,7 +152,10 @@ import java.util.HashSet;
|
|||||||
if (defaultPositionUs == C.TIME_UNSET) {
|
if (defaultPositionUs == C.TIME_UNSET) {
|
||||||
defaultPositionUs = previousData.defaultPositionUs;
|
defaultPositionUs = previousData.defaultPositionUs;
|
||||||
}
|
}
|
||||||
itemIdToData.put(itemId, previousData.copyWithNewValues(durationUs, defaultPositionUs, isLive));
|
itemIdToData.put(
|
||||||
|
itemId,
|
||||||
|
previousData.copyWithNewValues(
|
||||||
|
durationUs, defaultPositionUs, isLive, mediaItem, contentId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeUnusedItemDataEntries(int[] itemIds) {
|
private void removeUnusedItemDataEntries(int[] itemIds) {
|
||||||
@ -99,6 +167,8 @@ import java.util.HashSet;
|
|||||||
int index = 0;
|
int index = 0;
|
||||||
while (index < itemIdToData.size()) {
|
while (index < itemIdToData.size()) {
|
||||||
if (!scratchItemIds.contains(itemIdToData.keyAt(index))) {
|
if (!scratchItemIds.contains(itemIdToData.keyAt(index))) {
|
||||||
|
CastTimeline.ItemData itemData = itemIdToData.valueAt(index);
|
||||||
|
mediaItemsByContentId.remove(itemData.contentId);
|
||||||
itemIdToData.removeAt(index);
|
itemIdToData.removeAt(index);
|
||||||
} else {
|
} else {
|
||||||
index++;
|
index++;
|
||||||
|
@ -63,6 +63,7 @@ import static org.mockito.MockitoAnnotations.initMocks;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.MediaMetadata;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.PlaybackParameters;
|
import androidx.media3.common.PlaybackParameters;
|
||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
@ -126,6 +127,7 @@ public class CastPlayerTest {
|
|||||||
when(mockCastSession.getRemoteMediaClient()).thenReturn(mockRemoteMediaClient);
|
when(mockCastSession.getRemoteMediaClient()).thenReturn(mockRemoteMediaClient);
|
||||||
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus);
|
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus);
|
||||||
when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue);
|
when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(new MediaInfo.Builder("contentId").build());
|
||||||
when(mockMediaQueue.getItemIds()).thenReturn(new int[0]);
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[0]);
|
||||||
// Make the remote media client present the same default values as ExoPlayer:
|
// Make the remote media client present the same default values as ExoPlayer:
|
||||||
when(mockRemoteMediaClient.isPaused()).thenReturn(true);
|
when(mockRemoteMediaClient.isPaused()).thenReturn(true);
|
||||||
@ -388,7 +390,7 @@ public class CastPlayerTest {
|
|||||||
mediaItems.add(
|
mediaItems.add(
|
||||||
new MediaItem.Builder().setUri(uri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
new MediaItem.Builder().setUri(uri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
||||||
|
|
||||||
castPlayer.setMediaItems(mediaItems, /* startWindowIndex= */ 1, /* startPositionMs= */ 2000L);
|
castPlayer.setMediaItems(mediaItems, /* startIndex= */ 1, /* startPositionMs= */ 2000L);
|
||||||
|
|
||||||
verify(mockRemoteMediaClient)
|
verify(mockRemoteMediaClient)
|
||||||
.queueLoad(queueItemsArgumentCaptor.capture(), eq(1), anyInt(), eq(2000L), any());
|
.queueLoad(queueItemsArgumentCaptor.capture(), eq(1), anyInt(), eq(2000L), any());
|
||||||
@ -424,32 +426,42 @@ public class CastPlayerTest {
|
|||||||
String uri1 = "http://www.google.com/video1";
|
String uri1 = "http://www.google.com/video1";
|
||||||
String uri2 = "http://www.google.com/video2";
|
String uri2 = "http://www.google.com/video2";
|
||||||
firstPlaylist.add(
|
firstPlaylist.add(
|
||||||
new MediaItem.Builder().setUri(uri1).setMimeType(MimeTypes.APPLICATION_MPD).build());
|
new MediaItem.Builder()
|
||||||
|
.setUri(uri1)
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
|
.setTag(1)
|
||||||
|
.build());
|
||||||
firstPlaylist.add(
|
firstPlaylist.add(
|
||||||
new MediaItem.Builder().setUri(uri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
new MediaItem.Builder()
|
||||||
|
.setUri(uri2)
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MP4)
|
||||||
|
.setTag(2)
|
||||||
|
.build());
|
||||||
ImmutableList<MediaItem> secondPlaylist =
|
ImmutableList<MediaItem> secondPlaylist =
|
||||||
ImmutableList.of(
|
ImmutableList.of(
|
||||||
new MediaItem.Builder()
|
new MediaItem.Builder()
|
||||||
.setUri(Uri.EMPTY)
|
.setUri(Uri.EMPTY)
|
||||||
|
.setTag(3)
|
||||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
castPlayer.setMediaItems(
|
castPlayer.setMediaItems(firstPlaylist, /* startIndex= */ 1, /* startPositionMs= */ 2000L);
|
||||||
firstPlaylist, /* startWindowIndex= */ 1, /* startPositionMs= */ 2000L);
|
|
||||||
updateTimeLine(
|
updateTimeLine(
|
||||||
firstPlaylist, /* mediaQueueItemIds= */ new int[] {1, 2}, /* currentItemId= */ 2);
|
firstPlaylist, /* mediaQueueItemIds= */ new int[] {1, 2}, /* currentItemId= */ 2);
|
||||||
// Replacing existing playlist.
|
// Replacing existing playlist.
|
||||||
castPlayer.setMediaItems(
|
castPlayer.setMediaItems(secondPlaylist, /* startIndex= */ 0, /* startPositionMs= */ 1000L);
|
||||||
secondPlaylist, /* startWindowIndex= */ 0, /* startPositionMs= */ 1000L);
|
|
||||||
updateTimeLine(secondPlaylist, /* mediaQueueItemIds= */ new int[] {3}, /* currentItemId= */ 3);
|
updateTimeLine(secondPlaylist, /* mediaQueueItemIds= */ new int[] {3}, /* currentItemId= */ 3);
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener, times(2))
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(
|
.onMediaItemTransition(
|
||||||
mediaItemCaptor.capture(), eq(MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
eq(firstPlaylist.get(1)), eq(MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
|
inOrder
|
||||||
|
.verify(mockListener)
|
||||||
|
.onMediaItemTransition(
|
||||||
|
eq(secondPlaylist.get(0)), eq(MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
assertThat(mediaItemCaptor.getAllValues().get(1).localConfiguration.tag).isEqualTo(3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Verifies deprecated callback being called correctly.
|
@SuppressWarnings("deprecation") // Verifies deprecated callback being called correctly.
|
||||||
@ -459,18 +471,26 @@ public class CastPlayerTest {
|
|||||||
String uri1 = "http://www.google.com/video1";
|
String uri1 = "http://www.google.com/video1";
|
||||||
String uri2 = "http://www.google.com/video2";
|
String uri2 = "http://www.google.com/video2";
|
||||||
firstPlaylist.add(
|
firstPlaylist.add(
|
||||||
new MediaItem.Builder().setUri(uri1).setMimeType(MimeTypes.APPLICATION_MPD).build());
|
new MediaItem.Builder()
|
||||||
|
.setUri(uri1)
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
|
.setTag(1)
|
||||||
|
.build());
|
||||||
firstPlaylist.add(
|
firstPlaylist.add(
|
||||||
new MediaItem.Builder().setUri(uri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
new MediaItem.Builder()
|
||||||
|
.setUri(uri2)
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MP4)
|
||||||
|
.setTag(2)
|
||||||
|
.build());
|
||||||
ImmutableList<MediaItem> secondPlaylist =
|
ImmutableList<MediaItem> secondPlaylist =
|
||||||
ImmutableList.of(
|
ImmutableList.of(
|
||||||
new MediaItem.Builder()
|
new MediaItem.Builder()
|
||||||
.setUri(Uri.EMPTY)
|
.setUri(Uri.EMPTY)
|
||||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
|
.setTag(3)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
castPlayer.setMediaItems(
|
castPlayer.setMediaItems(firstPlaylist, /* startIndex= */ 1, /* startPositionMs= */ 2000L);
|
||||||
firstPlaylist, /* startWindowIndex= */ 1, /* startPositionMs= */ 2000L);
|
|
||||||
updateTimeLine(
|
updateTimeLine(
|
||||||
firstPlaylist,
|
firstPlaylist,
|
||||||
/* mediaQueueItemIds= */ new int[] {1, 2},
|
/* mediaQueueItemIds= */ new int[] {1, 2},
|
||||||
@ -481,8 +501,7 @@ public class CastPlayerTest {
|
|||||||
/* durationsMs= */ new long[] {20_000, 20_000},
|
/* durationsMs= */ new long[] {20_000, 20_000},
|
||||||
/* positionMs= */ 2000L);
|
/* positionMs= */ 2000L);
|
||||||
// Replacing existing playlist.
|
// Replacing existing playlist.
|
||||||
castPlayer.setMediaItems(
|
castPlayer.setMediaItems(secondPlaylist, /* startIndex= */ 0, /* startPositionMs= */ 1000L);
|
||||||
secondPlaylist, /* startWindowIndex= */ 0, /* startPositionMs= */ 1000L);
|
|
||||||
updateTimeLine(
|
updateTimeLine(
|
||||||
secondPlaylist,
|
secondPlaylist,
|
||||||
/* mediaQueueItemIds= */ new int[] {3},
|
/* mediaQueueItemIds= */ new int[] {3},
|
||||||
@ -494,8 +513,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 2,
|
/* windowUid= */ 2,
|
||||||
/* windowIndex= */ 1,
|
/* mediaItemIndex= */ 1,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
firstPlaylist.get(1),
|
||||||
/* periodUid= */ 2,
|
/* periodUid= */ 2,
|
||||||
/* periodIndex= */ 1,
|
/* periodIndex= */ 1,
|
||||||
/* positionMs= */ 2000,
|
/* positionMs= */ 2000,
|
||||||
@ -505,8 +524,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 3,
|
/* windowUid= */ 3,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(3).build(),
|
secondPlaylist.get(0),
|
||||||
/* periodUid= */ 3,
|
/* periodUid= */ 3,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 1000,
|
/* positionMs= */ 1000,
|
||||||
@ -720,10 +739,8 @@ public class CastPlayerTest {
|
|||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(
|
.onMediaItemTransition(
|
||||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
eq(mediaItem), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
assertThat(mediaItemCaptor.getValue().localConfiguration.tag)
|
|
||||||
.isEqualTo(mediaItem.localConfiguration.tag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -742,7 +759,8 @@ public class CastPlayerTest {
|
|||||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
.onMediaItemTransition(
|
||||||
|
eq(mediaItems.get(0)), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(
|
.onMediaItemTransition(
|
||||||
@ -776,8 +794,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 1234,
|
/* positionMs= */ 1234,
|
||||||
@ -787,7 +805,7 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ null,
|
/* windowUid= */ null,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
/* mediaItem= */ null,
|
/* mediaItem= */ null,
|
||||||
/* periodUid= */ null,
|
/* periodUid= */ null,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
@ -827,10 +845,8 @@ public class CastPlayerTest {
|
|||||||
.onMediaItemTransition(
|
.onMediaItemTransition(
|
||||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
assertThat(mediaItemCaptor.getAllValues().get(0).localConfiguration.tag)
|
assertThat(mediaItemCaptor.getAllValues().get(0)).isEqualTo(mediaItem1);
|
||||||
.isEqualTo(mediaItem1.localConfiguration.tag);
|
assertThat(mediaItemCaptor.getAllValues().get(1)).isEqualTo(mediaItem2);
|
||||||
assertThat(mediaItemCaptor.getAllValues().get(1).localConfiguration.tag)
|
|
||||||
.isEqualTo(mediaItem2.localConfiguration.tag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -862,8 +878,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
mediaItem1,
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 1234,
|
/* positionMs= */ 1234,
|
||||||
@ -873,8 +889,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 2,
|
/* windowUid= */ 2,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
mediaItem2,
|
||||||
/* periodUid= */ 2,
|
/* periodUid= */ 2,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 0,
|
/* positionMs= */ 0,
|
||||||
@ -912,10 +928,8 @@ public class CastPlayerTest {
|
|||||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
List<MediaItem> capturedMediaItems = mediaItemCaptor.getAllValues();
|
List<MediaItem> capturedMediaItems = mediaItemCaptor.getAllValues();
|
||||||
assertThat(capturedMediaItems.get(0).localConfiguration.tag)
|
assertThat(capturedMediaItems.get(0)).isEqualTo(mediaItem1);
|
||||||
.isEqualTo(mediaItem1.localConfiguration.tag);
|
assertThat(capturedMediaItems.get(1)).isEqualTo(mediaItem2);
|
||||||
assertThat(capturedMediaItems.get(1).localConfiguration.tag)
|
|
||||||
.isEqualTo(mediaItem2.localConfiguration.tag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -945,8 +959,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
mediaItem1,
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 0, // position at which we receive the timeline change
|
/* positionMs= */ 0, // position at which we receive the timeline change
|
||||||
@ -956,8 +970,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 2,
|
/* windowUid= */ 2,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
mediaItem2,
|
||||||
/* periodUid= */ 2,
|
/* periodUid= */ 2,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 0,
|
/* positionMs= */ 0,
|
||||||
@ -992,7 +1006,8 @@ public class CastPlayerTest {
|
|||||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
.onMediaItemTransition(
|
||||||
|
eq(mediaItem1), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1027,19 +1042,17 @@ public class CastPlayerTest {
|
|||||||
|
|
||||||
castPlayer.addMediaItems(mediaItems);
|
castPlayer.addMediaItems(mediaItems);
|
||||||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||||
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 1234);
|
castPlayer.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 1234);
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
.onMediaItemTransition(
|
||||||
|
eq(mediaItem1), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(
|
.onMediaItemTransition(eq(mediaItem2), eq(Player.MEDIA_ITEM_TRANSITION_REASON_SEEK));
|
||||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_SEEK));
|
|
||||||
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
||||||
assertThat(mediaItemCaptor.getValue().localConfiguration.tag)
|
|
||||||
.isEqualTo(mediaItem2.localConfiguration.tag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -1054,13 +1067,13 @@ public class CastPlayerTest {
|
|||||||
|
|
||||||
castPlayer.addMediaItems(mediaItems);
|
castPlayer.addMediaItems(mediaItems);
|
||||||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||||
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 1234);
|
castPlayer.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 1234);
|
||||||
|
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
mediaItem1,
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 0,
|
/* positionMs= */ 0,
|
||||||
@ -1070,8 +1083,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 2,
|
/* windowUid= */ 2,
|
||||||
/* windowIndex= */ 1,
|
/* mediaItemIndex= */ 1,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
mediaItem2,
|
||||||
/* periodUid= */ 2,
|
/* periodUid= */ 2,
|
||||||
/* periodIndex= */ 1,
|
/* periodIndex= */ 1,
|
||||||
/* positionMs= */ 1234,
|
/* positionMs= */ 1234,
|
||||||
@ -1097,12 +1110,13 @@ public class CastPlayerTest {
|
|||||||
|
|
||||||
castPlayer.addMediaItems(mediaItems);
|
castPlayer.addMediaItems(mediaItems);
|
||||||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 1234);
|
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 1234);
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
.onMediaItemTransition(
|
||||||
|
eq(mediaItems.get(0)), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1115,14 +1129,13 @@ public class CastPlayerTest {
|
|||||||
|
|
||||||
castPlayer.addMediaItems(mediaItems);
|
castPlayer.addMediaItems(mediaItems);
|
||||||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 1234);
|
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 1234);
|
||||||
|
|
||||||
MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build();
|
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 0,
|
/* positionMs= */ 0,
|
||||||
@ -1132,8 +1145,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 1234,
|
/* positionMs= */ 1234,
|
||||||
@ -1164,13 +1177,12 @@ public class CastPlayerTest {
|
|||||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
.onMediaItemTransition(
|
||||||
|
eq(mediaItems.get(0)), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(
|
.onMediaItemTransition(eq(mediaItems.get(1)), eq(Player.MEDIA_ITEM_TRANSITION_REASON_AUTO));
|
||||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_AUTO));
|
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
assertThat(mediaItemCaptor.getValue().localConfiguration.tag).isEqualTo(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -1203,8 +1215,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 12500,
|
/* positionMs= */ 12500,
|
||||||
@ -1214,8 +1226,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 2,
|
/* windowUid= */ 2,
|
||||||
/* windowIndex= */ 1,
|
/* mediaItemIndex= */ 1,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
mediaItems.get(1),
|
||||||
/* periodUid= */ 2,
|
/* periodUid= */ 2,
|
||||||
/* periodIndex= */ 1,
|
/* periodIndex= */ 1,
|
||||||
/* positionMs= */ 0,
|
/* positionMs= */ 0,
|
||||||
@ -1250,12 +1262,11 @@ public class CastPlayerTest {
|
|||||||
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
|
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
|
||||||
castPlayer.seekBack();
|
castPlayer.seekBack();
|
||||||
|
|
||||||
MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build();
|
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 2 * C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
/* positionMs= */ 2 * C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
||||||
@ -1265,8 +1276,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
/* positionMs= */ C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
||||||
@ -1299,12 +1310,11 @@ public class CastPlayerTest {
|
|||||||
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
|
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
|
||||||
castPlayer.seekForward();
|
castPlayer.seekForward();
|
||||||
|
|
||||||
MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build();
|
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 0,
|
/* positionMs= */ 0,
|
||||||
@ -1314,8 +1324,8 @@ public class CastPlayerTest {
|
|||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ C.DEFAULT_SEEK_FORWARD_INCREMENT_MS,
|
/* positionMs= */ C.DEFAULT_SEEK_FORWARD_INCREMENT_MS,
|
||||||
@ -1475,14 +1485,14 @@ public class CastPlayerTest {
|
|||||||
// Check that there were no other calls to onAvailableCommandsChanged.
|
// Check that there were no other calls to onAvailableCommandsChanged.
|
||||||
verify(mockListener).onAvailableCommandsChanged(any());
|
verify(mockListener).onAvailableCommandsChanged(any());
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
|
castPlayer.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 0);
|
||||||
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
|
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
|
||||||
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
|
castPlayer.seekTo(/* mediaItemIndex= */ 2, /* positionMs= */ 0);
|
||||||
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 3, /* positionMs= */ 0);
|
castPlayer.seekTo(/* mediaItemIndex= */ 3, /* positionMs= */ 0);
|
||||||
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
|
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
|
||||||
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
||||||
}
|
}
|
||||||
@ -1509,14 +1519,14 @@ public class CastPlayerTest {
|
|||||||
// Check that there were no other calls to onAvailableCommandsChanged.
|
// Check that there were no other calls to onAvailableCommandsChanged.
|
||||||
verify(mockListener).onAvailableCommandsChanged(any());
|
verify(mockListener).onAvailableCommandsChanged(any());
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
|
castPlayer.seekTo(/* mediaItemIndex= */ 2, /* positionMs= */ 0);
|
||||||
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
|
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
|
||||||
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
|
castPlayer.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 0);
|
||||||
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 0);
|
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 0);
|
||||||
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
|
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
|
||||||
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
||||||
}
|
}
|
||||||
@ -1533,8 +1543,8 @@ public class CastPlayerTest {
|
|||||||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||||
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
|
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 200);
|
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 200);
|
||||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 100);
|
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 100);
|
||||||
// Check that there were no other calls to onAvailableCommandsChanged.
|
// Check that there were no other calls to onAvailableCommandsChanged.
|
||||||
verify(mockListener).onAvailableCommandsChanged(any());
|
verify(mockListener).onAvailableCommandsChanged(any());
|
||||||
}
|
}
|
||||||
@ -1782,6 +1792,7 @@ public class CastPlayerTest {
|
|||||||
private MediaItem createMediaItem(int mediaQueueItemId) {
|
private MediaItem createMediaItem(int mediaQueueItemId) {
|
||||||
return new MediaItem.Builder()
|
return new MediaItem.Builder()
|
||||||
.setUri("http://www.google.com/video" + mediaQueueItemId)
|
.setUri("http://www.google.com/video" + mediaQueueItemId)
|
||||||
|
.setMediaMetadata(new MediaMetadata.Builder().setArtist("Foo Bar").build())
|
||||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
.setTag(mediaQueueItemId)
|
.setTag(mediaQueueItemId)
|
||||||
.build();
|
.build();
|
||||||
@ -1821,8 +1832,12 @@ public class CastPlayerTest {
|
|||||||
int mediaQueueItemId = mediaQueueItemIds[i];
|
int mediaQueueItemId = mediaQueueItemIds[i];
|
||||||
int streamType = streamTypes[i];
|
int streamType = streamTypes[i];
|
||||||
long durationMs = durationsMs[i];
|
long durationMs = durationsMs[i];
|
||||||
|
String contentId =
|
||||||
|
mediaItem.mediaId.equals(MediaItem.DEFAULT_MEDIA_ID)
|
||||||
|
? mediaItem.localConfiguration.uri.toString()
|
||||||
|
: mediaItem.mediaId;
|
||||||
MediaInfo.Builder mediaInfoBuilder =
|
MediaInfo.Builder mediaInfoBuilder =
|
||||||
new MediaInfo.Builder(mediaItem.localConfiguration.uri.toString())
|
new MediaInfo.Builder(contentId)
|
||||||
.setStreamType(streamType)
|
.setStreamType(streamType)
|
||||||
.setContentType(mediaItem.localConfiguration.mimeType);
|
.setContentType(mediaItem.localConfiguration.mimeType);
|
||||||
if (durationMs != C.TIME_UNSET) {
|
if (durationMs != C.TIME_UNSET) {
|
||||||
|
@ -15,21 +15,30 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.cast;
|
package androidx.media3.cast;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
|
import androidx.media3.common.Timeline.Window;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.test.utils.TimelineAsserts;
|
import androidx.media3.test.utils.TimelineAsserts;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.gms.cast.MediaInfo;
|
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.MediaStatus;
|
||||||
import com.google.android.gms.cast.framework.media.MediaQueue;
|
import com.google.android.gms.cast.framework.media.MediaQueue;
|
||||||
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
|
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mockito;
|
|
||||||
|
|
||||||
/** Tests for {@link CastTimelineTracker}. */
|
/** Tests for {@link CastTimelineTracker}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@ -40,10 +49,19 @@ public class CastTimelineTrackerTest {
|
|||||||
private static final long DURATION_4_MS = 4000;
|
private static final long DURATION_4_MS = 4000;
|
||||||
private static final long DURATION_5_MS = 5000;
|
private static final long DURATION_5_MS = 5000;
|
||||||
|
|
||||||
|
private MediaItemConverter mediaItemConverter;
|
||||||
|
private CastTimelineTracker castTimelineTracker;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
mediaItemConverter = new DefaultMediaItemConverter();
|
||||||
|
castTimelineTracker = new CastTimelineTracker(mediaItemConverter);
|
||||||
|
}
|
||||||
|
|
||||||
/** Tests that duration of the current media info is correctly propagated to the timeline. */
|
/** Tests that duration of the current media info is correctly propagated to the timeline. */
|
||||||
@Test
|
@Test
|
||||||
public void getCastTimelinePersistsDuration() {
|
public void getCastTimelinePersistsDuration() {
|
||||||
CastTimelineTracker tracker = new CastTimelineTracker();
|
CastTimelineTracker tracker = new CastTimelineTracker(new DefaultMediaItemConverter());
|
||||||
|
|
||||||
RemoteMediaClient remoteMediaClient =
|
RemoteMediaClient remoteMediaClient =
|
||||||
mockRemoteMediaClient(
|
mockRemoteMediaClient(
|
||||||
@ -104,10 +122,179 @@ public class CastTimelineTrackerTest {
|
|||||||
Util.msToUs(DURATION_5_MS));
|
Util.msToUs(DURATION_5_MS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCastTimeline_onMediaItemsSet_correctMediaItemsInTimeline() {
|
||||||
|
RemoteMediaClient mockRemoteMediaClient = mock(RemoteMediaClient.class);
|
||||||
|
MediaQueue mockMediaQueue = mock(MediaQueue.class);
|
||||||
|
MediaStatus mockMediaStatus = mock(MediaStatus.class);
|
||||||
|
ImmutableList<MediaItem> playlistMediaItems =
|
||||||
|
ImmutableList.of(createMediaItem(0), createMediaItem(1));
|
||||||
|
MediaQueueItem[] playlistMediaQueueItems =
|
||||||
|
new MediaQueueItem[] {
|
||||||
|
createMediaQueueItem(playlistMediaItems.get(0), 0),
|
||||||
|
createMediaQueueItem(playlistMediaItems.get(1), 1)
|
||||||
|
};
|
||||||
|
castTimelineTracker.onMediaItemsSet(playlistMediaItems, playlistMediaQueueItems);
|
||||||
|
// Mock remote media client state after adding two items.
|
||||||
|
when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue);
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[] {0, 1});
|
||||||
|
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus);
|
||||||
|
when(mockMediaStatus.getCurrentItemId()).thenReturn(0);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(playlistMediaQueueItems[0].getMedia());
|
||||||
|
when(mockMediaStatus.getQueueItems()).thenReturn(Arrays.asList(playlistMediaQueueItems));
|
||||||
|
|
||||||
|
CastTimeline castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(2);
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 0, new Window()).mediaItem)
|
||||||
|
.isEqualTo(playlistMediaItems.get(0));
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 1, new Window()).mediaItem)
|
||||||
|
.isEqualTo(playlistMediaItems.get(1));
|
||||||
|
|
||||||
|
MediaItem thirdMediaItem = createMediaItem(2);
|
||||||
|
MediaQueueItem thirdMediaQueueItem = createMediaQueueItem(thirdMediaItem, 2);
|
||||||
|
castTimelineTracker.onMediaItemsSet(
|
||||||
|
ImmutableList.of(thirdMediaItem), new MediaQueueItem[] {thirdMediaQueueItem});
|
||||||
|
// Mock remote media client state after a single item overrides the previous playlist.
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[] {2});
|
||||||
|
when(mockMediaStatus.getCurrentItemId()).thenReturn(2);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(thirdMediaQueueItem.getMedia());
|
||||||
|
when(mockMediaStatus.getQueueItems()).thenReturn(ImmutableList.of(thirdMediaQueueItem));
|
||||||
|
|
||||||
|
castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(1);
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 0, new Window()).mediaItem)
|
||||||
|
.isEqualTo(thirdMediaItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCastTimeline_onMediaItemsAdded_correctMediaItemsInTimeline() {
|
||||||
|
RemoteMediaClient mockRemoteMediaClient = mock(RemoteMediaClient.class);
|
||||||
|
MediaQueue mockMediaQueue = mock(MediaQueue.class);
|
||||||
|
MediaStatus mockMediaStatus = mock(MediaStatus.class);
|
||||||
|
ImmutableList<MediaItem> playlistMediaItems =
|
||||||
|
ImmutableList.of(createMediaItem(0), createMediaItem(1));
|
||||||
|
MediaQueueItem[] playlistQueueItems =
|
||||||
|
new MediaQueueItem[] {
|
||||||
|
createMediaQueueItem(playlistMediaItems.get(0), /* uid= */ 0),
|
||||||
|
createMediaQueueItem(playlistMediaItems.get(1), /* uid= */ 1)
|
||||||
|
};
|
||||||
|
ImmutableList<MediaItem> secondPlaylistMediaItems =
|
||||||
|
new ImmutableList.Builder<MediaItem>()
|
||||||
|
.addAll(playlistMediaItems)
|
||||||
|
.add(createMediaItem(2))
|
||||||
|
.build();
|
||||||
|
castTimelineTracker.onMediaItemsAdded(playlistMediaItems, playlistQueueItems);
|
||||||
|
when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue);
|
||||||
|
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus);
|
||||||
|
// Mock remote media client state after two items have been added.
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[] {0, 1});
|
||||||
|
when(mockMediaStatus.getCurrentItemId()).thenReturn(0);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(playlistQueueItems[0].getMedia());
|
||||||
|
when(mockMediaStatus.getQueueItems()).thenReturn(Arrays.asList(playlistQueueItems));
|
||||||
|
|
||||||
|
CastTimeline castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(2);
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 0, new Window()).mediaItem)
|
||||||
|
.isEqualTo(playlistMediaItems.get(0));
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 1, new Window()).mediaItem)
|
||||||
|
.isEqualTo(playlistMediaItems.get(1));
|
||||||
|
|
||||||
|
// Mock remote media client state after adding a third item.
|
||||||
|
List<MediaQueueItem> playlistThreeQueueItems =
|
||||||
|
new ArrayList<>(Arrays.asList(playlistQueueItems));
|
||||||
|
playlistThreeQueueItems.add(createMediaQueueItem(secondPlaylistMediaItems.get(2), 2));
|
||||||
|
castTimelineTracker.onMediaItemsAdded(
|
||||||
|
secondPlaylistMediaItems, playlistThreeQueueItems.toArray(new MediaQueueItem[0]));
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[] {0, 1, 2});
|
||||||
|
when(mockMediaStatus.getQueueItems()).thenReturn(playlistThreeQueueItems);
|
||||||
|
|
||||||
|
castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(3);
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 0, new Window()).mediaItem)
|
||||||
|
.isEqualTo(secondPlaylistMediaItems.get(0));
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 1, new Window()).mediaItem)
|
||||||
|
.isEqualTo(secondPlaylistMediaItems.get(1));
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 2, new Window()).mediaItem)
|
||||||
|
.isEqualTo(secondPlaylistMediaItems.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCastTimeline_itemsRemoved_correctMediaItemsInTimelineAndMapCleanedUp() {
|
||||||
|
RemoteMediaClient mockRemoteMediaClient = mock(RemoteMediaClient.class);
|
||||||
|
MediaQueue mockMediaQueue = mock(MediaQueue.class);
|
||||||
|
MediaStatus mockMediaStatus = mock(MediaStatus.class);
|
||||||
|
ImmutableList<MediaItem> playlistMediaItems =
|
||||||
|
ImmutableList.of(createMediaItem(0), createMediaItem(1));
|
||||||
|
MediaQueueItem[] initialPlaylistTwoQueueItems =
|
||||||
|
new MediaQueueItem[] {
|
||||||
|
createMediaQueueItem(playlistMediaItems.get(0), 0),
|
||||||
|
createMediaQueueItem(playlistMediaItems.get(1), 1)
|
||||||
|
};
|
||||||
|
castTimelineTracker.onMediaItemsSet(playlistMediaItems, initialPlaylistTwoQueueItems);
|
||||||
|
when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue);
|
||||||
|
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus);
|
||||||
|
// Mock remote media client state with two items in the queue.
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[] {0, 1});
|
||||||
|
when(mockMediaStatus.getCurrentItemId()).thenReturn(0);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(initialPlaylistTwoQueueItems[0].getMedia());
|
||||||
|
when(mockMediaStatus.getQueueItems()).thenReturn(Arrays.asList(initialPlaylistTwoQueueItems));
|
||||||
|
|
||||||
|
CastTimeline castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(2);
|
||||||
|
assertThat(castTimelineTracker.mediaItemsByContentId).hasSize(2);
|
||||||
|
|
||||||
|
// Mock remote media client state after the first item has been removed.
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[] {1});
|
||||||
|
when(mockMediaStatus.getCurrentItemId()).thenReturn(1);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(initialPlaylistTwoQueueItems[1].getMedia());
|
||||||
|
when(mockMediaStatus.getQueueItems())
|
||||||
|
.thenReturn(ImmutableList.of(initialPlaylistTwoQueueItems[1]));
|
||||||
|
|
||||||
|
castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(1);
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 0, new Window()).mediaItem)
|
||||||
|
.isEqualTo(playlistMediaItems.get(1));
|
||||||
|
// Assert that the removed item has been removed from the content ID map.
|
||||||
|
assertThat(castTimelineTracker.mediaItemsByContentId).hasSize(1);
|
||||||
|
|
||||||
|
// Mock remote media client state for empty queue.
|
||||||
|
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(null);
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[0]);
|
||||||
|
when(mockMediaStatus.getCurrentItemId()).thenReturn(MediaQueueItem.INVALID_ITEM_ID);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(null);
|
||||||
|
when(mockMediaStatus.getQueueItems()).thenReturn(ImmutableList.of());
|
||||||
|
|
||||||
|
castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(0);
|
||||||
|
// Queue is not emptied when remote media client is empty. See [Internal ref: b/128825216].
|
||||||
|
assertThat(castTimelineTracker.mediaItemsByContentId).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MediaItem createMediaItem(int uid) {
|
||||||
|
return new MediaItem.Builder()
|
||||||
|
.setUri("http://www.google.com/" + uid)
|
||||||
|
.setMimeType(MimeTypes.AUDIO_MPEG)
|
||||||
|
.setTag(uid)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MediaQueueItem createMediaQueueItem(MediaItem mediaItem, int uid) {
|
||||||
|
return new MediaQueueItem.Builder(mediaItemConverter.toMediaQueueItem(mediaItem))
|
||||||
|
.setItemId(uid)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private static RemoteMediaClient mockRemoteMediaClient(
|
private static RemoteMediaClient mockRemoteMediaClient(
|
||||||
int[] itemIds, int currentItemId, long currentDurationMs) {
|
int[] itemIds, int currentItemId, long currentDurationMs) {
|
||||||
RemoteMediaClient remoteMediaClient = Mockito.mock(RemoteMediaClient.class);
|
RemoteMediaClient remoteMediaClient = mock(RemoteMediaClient.class);
|
||||||
MediaStatus status = Mockito.mock(MediaStatus.class);
|
MediaStatus status = mock(MediaStatus.class);
|
||||||
when(status.getQueueItems()).thenReturn(Collections.emptyList());
|
when(status.getQueueItems()).thenReturn(Collections.emptyList());
|
||||||
when(remoteMediaClient.getMediaStatus()).thenReturn(status);
|
when(remoteMediaClient.getMediaStatus()).thenReturn(status);
|
||||||
when(status.getMediaInfo()).thenReturn(getMediaInfo(currentDurationMs));
|
when(status.getMediaInfo()).thenReturn(getMediaInfo(currentDurationMs));
|
||||||
@ -118,7 +305,7 @@ public class CastTimelineTrackerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static MediaQueue mockMediaQueue(int[] itemIds) {
|
private static MediaQueue mockMediaQueue(int[] itemIds) {
|
||||||
MediaQueue mediaQueue = Mockito.mock(MediaQueue.class);
|
MediaQueue mediaQueue = mock(MediaQueue.class);
|
||||||
when(mediaQueue.getItemIds()).thenReturn(itemIds);
|
when(mediaQueue.getItemIds()).thenReturn(itemIds);
|
||||||
return mediaQueue;
|
return mediaQueue;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user