Commit 5b1370e6 by tonihei Committed by Marc Baechinger

Set duration in QueueTimeline

If the duration is reported in MediaMetadataCompat, it should
also be set in the QueueTimeline to match controller.getDuration()

PiperOrigin-RevId: 522022953
parent d66dd502
......@@ -1804,6 +1804,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
long oldActiveQueueId = getActiveQueueId(oldLegacyPlayerInfo.playbackStateCompat);
long newActiveQueueId = getActiveQueueId(newLegacyPlayerInfo.playbackStateCompat);
boolean isCurrentActiveQueueIdChanged = (oldActiveQueueId != newActiveQueueId) || initialUpdate;
long durationMs = MediaUtils.convertToDurationMs(newLegacyPlayerInfo.mediaMetadataCompat);
if (isMetadataCompatChanged || isCurrentActiveQueueIdChanged || isQueueChanged) {
currentMediaItemIndex = findQueueItemIndex(newLegacyPlayerInfo.queue, newActiveQueueId);
boolean hasMediaMetadataCompat = newLegacyPlayerInfo.mediaMetadataCompat != null;
......@@ -1829,19 +1830,17 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
+ " MediaItem.");
MediaItem fakeMediaItem =
MediaUtils.convertToMediaItem(newLegacyPlayerInfo.mediaMetadataCompat, ratingType);
// Ad a tag to make sure the fake media item can't have an equal instance by accident.
fakeMediaItem = fakeMediaItem.buildUpon().setTag(new Object()).build();
currentTimeline = currentTimeline.copyWithFakeMediaItem(fakeMediaItem);
currentTimeline = currentTimeline.copyWithFakeMediaItem(fakeMediaItem, durationMs);
currentMediaItemIndex = currentTimeline.getWindowCount() - 1;
} else {
currentTimeline = currentTimeline.copyWithFakeMediaItem(/* fakeMediaItem= */ null);
currentTimeline = currentTimeline.copyWithClearedFakeMediaItem();
// Shouldn't be C.INDEX_UNSET to make getCurrentMediaItemIndex() return masked index.
// In other words, this index is either the currently playing media item index or the
// would-be playing index when playing.
currentMediaItemIndex = 0;
}
} else if (currentMediaItemIndex != C.INDEX_UNSET) {
currentTimeline = currentTimeline.copyWithFakeMediaItem(/* fakeMediaItem= */ null);
currentTimeline = currentTimeline.copyWithClearedFakeMediaItem();
if (hasMediaMetadataCompat) {
MediaItem mediaItem =
MediaUtils.convertToMediaItem(
......@@ -1850,7 +1849,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
ratingType);
currentTimeline =
currentTimeline.copyWithNewMediaItem(
/* replaceIndex= */ currentMediaItemIndex, mediaItem);
/* replaceIndex= */ currentMediaItemIndex, mediaItem, durationMs);
}
} else {
// There's queue, but no valid queue item ID nor current media item metadata.
......@@ -1897,7 +1896,6 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
PlaybackException playerError =
MediaUtils.convertToPlaybackException(newLegacyPlayerInfo.playbackStateCompat);
long durationMs = MediaUtils.convertToDurationMs(newLegacyPlayerInfo.mediaMetadataCompat);
long currentPositionMs =
MediaUtils.convertToCurrentPositionMs(
newLegacyPlayerInfo.playbackStateCompat,
......
......@@ -16,6 +16,7 @@
package androidx.media3.session;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Util.msToUs;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat.QueueItem;
......@@ -41,17 +42,18 @@ import java.util.List;
/* package */ final class QueueTimeline extends Timeline {
public static final QueueTimeline DEFAULT =
new QueueTimeline(ImmutableList.of(), /* fakeMediaItem= */ null);
new QueueTimeline(ImmutableList.of(), /* fakeQueuedMediaItem= */ null);
private static final Object FAKE_WINDOW_UID = new Object();
private final ImmutableList<QueuedMediaItem> queuedMediaItems;
@Nullable private final MediaItem fakeMediaItem;
@Nullable private final QueuedMediaItem fakeQueuedMediaItem;
private QueueTimeline(
ImmutableList<QueuedMediaItem> queuedMediaItems, @Nullable MediaItem fakeMediaItem) {
ImmutableList<QueuedMediaItem> queuedMediaItems,
@Nullable QueuedMediaItem fakeQueuedMediaItem) {
this.queuedMediaItems = queuedMediaItems;
this.fakeMediaItem = fakeMediaItem;
this.fakeQueuedMediaItem = fakeQueuedMediaItem;
}
/** Creates a {@link QueueTimeline} from a list of {@linkplain QueueItem queue items}. */
......@@ -60,14 +62,15 @@ import java.util.List;
for (int i = 0; i < queue.size(); i++) {
QueueItem queueItem = queue.get(i);
MediaItem mediaItem = MediaUtils.convertToMediaItem(queueItem);
queuedMediaItemsBuilder.add(new QueuedMediaItem(mediaItem, queueItem.getQueueId()));
queuedMediaItemsBuilder.add(
new QueuedMediaItem(mediaItem, queueItem.getQueueId(), /* durationMs= */ C.TIME_UNSET));
}
return new QueueTimeline(queuedMediaItemsBuilder.build(), /* fakeMediaItem= */ null);
return new QueueTimeline(queuedMediaItemsBuilder.build(), /* fakeQueuedMediaItem= */ null);
}
/** Returns a copy of the current queue timeline. */
public QueueTimeline copy() {
return new QueueTimeline(queuedMediaItems, fakeMediaItem);
return new QueueTimeline(queuedMediaItems, fakeQueuedMediaItem);
}
/**
......@@ -87,10 +90,18 @@ import java.util.List;
* Copies the timeline with the given fake media item.
*
* @param fakeMediaItem The fake media item.
* @param durationMs The duration of the fake media item, in milliseconds, or {@link C#TIME_UNSET}
* if unknown.
* @return A new {@link QueueTimeline} reflecting the update.
*/
public QueueTimeline copyWithFakeMediaItem(@Nullable MediaItem fakeMediaItem) {
return new QueueTimeline(queuedMediaItems, fakeMediaItem);
public QueueTimeline copyWithFakeMediaItem(MediaItem fakeMediaItem, long durationMs) {
return new QueueTimeline(
queuedMediaItems, new QueuedMediaItem(fakeMediaItem, QueueItem.UNKNOWN_ID, durationMs));
}
/** Copies the timeline while clearing any previously set fake media item. */
public QueueTimeline copyWithClearedFakeMediaItem() {
return new QueueTimeline(queuedMediaItems, /* fakeQueuedMediaItem= */ null);
}
/**
......@@ -98,21 +109,25 @@ import java.util.List;
*
* @param replaceIndex The index at which to replace the media item.
* @param newMediaItem The new media item that replaces the old one.
* @param durationMs The duration of the media item, in milliseconds, or {@link C#TIME_UNSET} if
* unknown.
* @return A new {@link QueueTimeline} reflecting the update.
*/
public QueueTimeline copyWithNewMediaItem(int replaceIndex, MediaItem newMediaItem) {
public QueueTimeline copyWithNewMediaItem(
int replaceIndex, MediaItem newMediaItem, long durationMs) {
checkArgument(
replaceIndex < queuedMediaItems.size()
|| (replaceIndex == queuedMediaItems.size() && fakeMediaItem != null));
|| (replaceIndex == queuedMediaItems.size() && fakeQueuedMediaItem != null));
if (replaceIndex == queuedMediaItems.size()) {
return new QueueTimeline(queuedMediaItems, newMediaItem);
return new QueueTimeline(
queuedMediaItems, new QueuedMediaItem(newMediaItem, QueueItem.UNKNOWN_ID, durationMs));
}
long queueId = queuedMediaItems.get(replaceIndex).queueId;
ImmutableList.Builder<QueuedMediaItem> queuedItemsBuilder = new ImmutableList.Builder<>();
queuedItemsBuilder.addAll(queuedMediaItems.subList(0, replaceIndex));
queuedItemsBuilder.add(new QueuedMediaItem(newMediaItem, queueId));
queuedItemsBuilder.add(new QueuedMediaItem(newMediaItem, queueId, durationMs));
queuedItemsBuilder.addAll(queuedMediaItems.subList(replaceIndex + 1, queuedMediaItems.size()));
return new QueueTimeline(queuedItemsBuilder.build(), fakeMediaItem);
return new QueueTimeline(queuedItemsBuilder.build(), fakeQueuedMediaItem);
}
/**
......@@ -127,10 +142,12 @@ import java.util.List;
ImmutableList.Builder<QueuedMediaItem> queuedItemsBuilder = new ImmutableList.Builder<>();
queuedItemsBuilder.addAll(queuedMediaItems.subList(0, index));
for (int i = 0; i < newMediaItems.size(); i++) {
queuedItemsBuilder.add(new QueuedMediaItem(newMediaItems.get(i), QueueItem.UNKNOWN_ID));
queuedItemsBuilder.add(
new QueuedMediaItem(
newMediaItems.get(i), QueueItem.UNKNOWN_ID, /* durationMs= */ C.TIME_UNSET));
}
queuedItemsBuilder.addAll(queuedMediaItems.subList(index, queuedMediaItems.size()));
return new QueueTimeline(queuedItemsBuilder.build(), fakeMediaItem);
return new QueueTimeline(queuedItemsBuilder.build(), fakeQueuedMediaItem);
}
/**
......@@ -144,7 +161,7 @@ import java.util.List;
ImmutableList.Builder<QueuedMediaItem> queuedItemsBuilder = new ImmutableList.Builder<>();
queuedItemsBuilder.addAll(queuedMediaItems.subList(0, fromIndex));
queuedItemsBuilder.addAll(queuedMediaItems.subList(toIndex, queuedMediaItems.size()));
return new QueueTimeline(queuedItemsBuilder.build(), fakeMediaItem);
return new QueueTimeline(queuedItemsBuilder.build(), fakeQueuedMediaItem);
}
/**
......@@ -158,12 +175,12 @@ import java.util.List;
public QueueTimeline copyWithMovedMediaItems(int fromIndex, int toIndex, int newIndex) {
List<QueuedMediaItem> list = new ArrayList<>(queuedMediaItems);
Util.moveItems(list, fromIndex, toIndex, newIndex);
return new QueueTimeline(ImmutableList.copyOf(list), fakeMediaItem);
return new QueueTimeline(ImmutableList.copyOf(list), fakeQueuedMediaItem);
}
/** Returns whether the timeline contains the given {@link MediaItem}. */
public boolean contains(MediaItem mediaItem) {
if (mediaItem.equals(fakeMediaItem)) {
if (fakeQueuedMediaItem != null && mediaItem.equals(fakeQueuedMediaItem.mediaItem)) {
return true;
}
for (int i = 0; i < queuedMediaItems.size(); i++) {
......@@ -176,27 +193,33 @@ import java.util.List;
@Nullable
public MediaItem getMediaItemAt(int mediaItemIndex) {
if (mediaItemIndex >= 0 && mediaItemIndex < queuedMediaItems.size()) {
return queuedMediaItems.get(mediaItemIndex).mediaItem;
}
return (mediaItemIndex == queuedMediaItems.size()) ? fakeMediaItem : null;
return mediaItemIndex >= getWindowCount() ? null : getQueuedMediaItem(mediaItemIndex).mediaItem;
}
@Override
public int getWindowCount() {
return queuedMediaItems.size() + ((fakeMediaItem == null) ? 0 : 1);
return queuedMediaItems.size() + ((fakeQueuedMediaItem == null) ? 0 : 1);
}
@Override
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
// TODO(b/149713425): Set duration if it's available from MediaMetadataCompat.
MediaItem mediaItem;
if (windowIndex == queuedMediaItems.size() && fakeMediaItem != null) {
mediaItem = fakeMediaItem;
} else {
mediaItem = queuedMediaItems.get(windowIndex).mediaItem;
}
return getWindow(window, mediaItem, windowIndex);
QueuedMediaItem queuedMediaItem = getQueuedMediaItem(windowIndex);
window.set(
FAKE_WINDOW_UID,
queuedMediaItem.mediaItem,
/* manifest= */ null,
/* presentationStartTimeMs= */ C.TIME_UNSET,
/* windowStartTimeMs= */ C.TIME_UNSET,
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
/* isSeekable= */ true,
/* isDynamic= */ false,
/* liveConfiguration= */ null,
/* defaultPositionUs= */ 0,
/* durationUs= */ msToUs(queuedMediaItem.durationMs),
/* firstPeriodIndex= */ windowIndex,
/* lastPeriodIndex= */ windowIndex,
/* positionInFirstPeriodUs= */ 0);
return window;
}
@Override
......@@ -206,12 +229,12 @@ import java.util.List;
@Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
// TODO(b/149713425): Set duration if it's available from MediaMetadataCompat.
QueuedMediaItem queuedMediaItem = getQueuedMediaItem(periodIndex);
period.set(
/* id= */ null,
/* id= */ queuedMediaItem.queueId,
/* uid= */ null,
/* windowIndex= */ periodIndex,
/* durationUs= */ C.TIME_UNSET,
/* durationUs= */ msToUs(queuedMediaItem.durationMs),
/* positionInWindowUs= */ 0);
return period;
}
......@@ -236,41 +259,30 @@ import java.util.List;
}
QueueTimeline other = (QueueTimeline) obj;
return Objects.equal(queuedMediaItems, other.queuedMediaItems)
&& Objects.equal(fakeMediaItem, other.fakeMediaItem);
&& Objects.equal(fakeQueuedMediaItem, other.fakeQueuedMediaItem);
}
@Override
public int hashCode() {
return Objects.hashCode(queuedMediaItems, fakeMediaItem);
return Objects.hashCode(queuedMediaItems, fakeQueuedMediaItem);
}
private static Window getWindow(Window window, MediaItem mediaItem, int windowIndex) {
window.set(
FAKE_WINDOW_UID,
mediaItem,
/* manifest= */ null,
/* presentationStartTimeMs= */ C.TIME_UNSET,
/* windowStartTimeMs= */ C.TIME_UNSET,
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
/* isSeekable= */ true,
/* isDynamic= */ false,
/* liveConfiguration= */ null,
/* defaultPositionUs= */ 0,
/* durationUs= */ C.TIME_UNSET,
/* firstPeriodIndex= */ windowIndex,
/* lastPeriodIndex= */ windowIndex,
/* positionInFirstPeriodUs= */ 0);
return window;
private QueuedMediaItem getQueuedMediaItem(int index) {
return index == queuedMediaItems.size() && fakeQueuedMediaItem != null
? fakeQueuedMediaItem
: queuedMediaItems.get(index);
}
private static final class QueuedMediaItem {
public final MediaItem mediaItem;
public final long queueId;
public final long durationMs;
public QueuedMediaItem(MediaItem mediaItem, long queueId) {
public QueuedMediaItem(MediaItem mediaItem, long queueId, long durationMs) {
this.mediaItem = mediaItem;
this.queueId = queueId;
this.durationMs = durationMs;
}
@Override
......@@ -282,7 +294,9 @@ import java.util.List;
return false;
}
QueuedMediaItem that = (QueuedMediaItem) o;
return queueId == that.queueId && mediaItem.equals(that.mediaItem);
return queueId == that.queueId
&& mediaItem.equals(that.mediaItem)
&& durationMs == that.durationMs;
}
@Override
......@@ -290,6 +304,7 @@ import java.util.List;
int result = 7;
result = 31 * result + (int) (queueId ^ (queueId >>> 32));
result = 31 * result + mediaItem.hashCode();
result = 31 * result + (int) (durationMs ^ (durationMs >>> 32));
return result;
}
}
......
......@@ -246,6 +246,8 @@ public class MediaControllerWithMediaSessionCompatTest {
AtomicLong repeatModeRef = new AtomicLong();
AtomicReference<MediaMetadata> playlistMetadataRef = new AtomicReference<>();
AtomicBoolean isPlayingAdRef = new AtomicBoolean();
AtomicLong durationRef = new AtomicLong();
AtomicLong durationInTimelineRef = new AtomicLong();
threadTestRule
.getHandler()
.postAndSync(
......@@ -260,6 +262,12 @@ public class MediaControllerWithMediaSessionCompatTest {
shuffleModeEnabledRef.set(controller.getShuffleModeEnabled());
playlistMetadataRef.set(controller.getPlaylistMetadata());
isPlayingAdRef.set(controller.isPlayingAd());
durationRef.set(controller.getDuration());
durationInTimelineRef.set(
controller
.getCurrentTimeline()
.getWindow(/* windowIndex= */ 0, new Timeline.Window())
.getDurationMs());
});
assertThat(positionRef.get())
......@@ -273,6 +281,8 @@ public class MediaControllerWithMediaSessionCompatTest {
assertThat(repeatModeRef.get()).isEqualTo(Player.REPEAT_MODE_ALL);
assertThat(playlistMetadataRef.get().title.toString()).isEqualTo(queueTitle.toString());
assertThat(isPlayingAdRef.get()).isEqualTo(isPlayingAd);
assertThat(durationRef.get()).isEqualTo(duration);
assertThat(durationInTimelineRef.get()).isEqualTo(duration);
}
@Test
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment