Skip to content

Commit

Permalink
Use onMediaMetadataChanged for updating the legacy session
Browse files Browse the repository at this point in the history
Issue: #219
PiperOrigin-RevId: 501080612
  • Loading branch information
marcbaechinger authored and rohitjoins committed Jan 17, 2023
1 parent 9882a20 commit 375299b
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 56 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Release notes
for custom players.
* Add helper method to convert platform session token to Media3
`SessionToken` ([#171](https://github.com/androidx/media/issues/171)).
* Use `onMediaMetadataChanged` to trigger updates of the platform media
session ([#219](https://github.com/androidx/media/issues/219)).
* Metadata:
* Parse multiple null-separated values from ID3 frames, as permitted by
ID3 v2.4.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
import com.google.common.util.concurrent.MoreExecutors;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
Expand Down Expand Up @@ -850,12 +851,15 @@ public boolean equals(@Nullable Object obj) {

private final class ControllerLegacyCbForBroadcast implements ControllerCb {

@Nullable private MediaItem currentMediaItemForMetadataUpdate;

private long durationMsForMetadataUpdate;
private MediaMetadata lastMediaMetadata;
private String lastMediaId;
@Nullable private Uri lastMediaUri;
private long lastDurationMs;

public ControllerLegacyCbForBroadcast() {
durationMsForMetadataUpdate = C.TIME_UNSET;
lastMediaMetadata = MediaMetadata.EMPTY;
lastMediaId = MediaItem.DEFAULT_MEDIA_ID;
lastDurationMs = C.TIME_UNSET;
}

@Override
Expand Down Expand Up @@ -992,6 +996,7 @@ public void onPlaybackParametersChanged(int seq, PlaybackParameters playbackPara
public void onMediaItemTransition(
int seq, @Nullable MediaItem mediaItem, @Player.MediaItemTransitionReason int reason)
throws RemoteException {
// MediaMetadataCompat needs to be updated when the media ID or URI of the media item changes.
updateMetadataIfChanged();
if (mediaItem == null) {
sessionCompat.setRatingType(RatingCompat.RATING_NONE);
Expand All @@ -1004,6 +1009,11 @@ public void onMediaItemTransition(
.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
}

@Override
public void onMediaMetadataChanged(int seq, MediaMetadata mediaMetadata) {
updateMetadataIfChanged();
}

@Override
public void onTimelineChanged(
int seq, Timeline timeline, @Player.TimelineChangeReason int reason)
Expand All @@ -1014,7 +1024,6 @@ public void onTimelineChanged(
}

updateQueue(timeline);

// Duration might be unknown at onMediaItemTransition and become available afterward.
updateMetadataIfChanged();
}
Expand Down Expand Up @@ -1146,22 +1155,30 @@ public void onPeriodicSessionPositionInfoChanged(
.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
}

@Override
public void onMediaMetadataChanged(int seq, MediaMetadata mediaMetadata) {
// Metadata change will be notified by onMediaItemTransition.
}

private void updateMetadataIfChanged() {
@Nullable MediaItem currentMediaItem = sessionImpl.getPlayerWrapper().getCurrentMediaItem();
long durationMs = sessionImpl.getPlayerWrapper().getDuration();

if (ObjectsCompat.equals(currentMediaItemForMetadataUpdate, currentMediaItem)
&& durationMsForMetadataUpdate == durationMs) {
Player player = sessionImpl.getPlayerWrapper();
@Nullable MediaItem currentMediaItem = player.getCurrentMediaItem();
MediaMetadata newMediaMetadata = player.getMediaMetadata();
long newDurationMs = player.getDuration();
String newMediaId =
currentMediaItem != null ? currentMediaItem.mediaId : MediaItem.DEFAULT_MEDIA_ID;
@Nullable
Uri newMediaUri =
currentMediaItem != null && currentMediaItem.localConfiguration != null
? currentMediaItem.localConfiguration.uri
: null;

if (Objects.equals(lastMediaMetadata, newMediaMetadata)
&& Objects.equals(lastMediaId, newMediaId)
&& Objects.equals(lastMediaUri, newMediaUri)
&& lastDurationMs == newDurationMs) {
return;
}

currentMediaItemForMetadataUpdate = currentMediaItem;
durationMsForMetadataUpdate = durationMs;
lastMediaId = newMediaId;
lastMediaUri = newMediaUri;
lastMediaMetadata = newMediaMetadata;
lastDurationMs = newDurationMs;

if (currentMediaItem == null) {
setMetadata(sessionCompat, /* metadataCompat= */ null);
Expand All @@ -1170,7 +1187,7 @@ private void updateMetadataIfChanged() {

@Nullable Bitmap artworkBitmap = null;
ListenableFuture<Bitmap> bitmapFuture =
sessionImpl.getBitmapLoader().loadBitmapFromMetadata(currentMediaItem.mediaMetadata);
sessionImpl.getBitmapLoader().loadBitmapFromMetadata(newMediaMetadata);
if (bitmapFuture != null) {
pendingBitmapLoadCallback = null;
if (bitmapFuture.isDone()) {
Expand All @@ -1190,7 +1207,11 @@ public void onSuccess(Bitmap result) {
setMetadata(
sessionCompat,
MediaUtils.convertToMediaMetadataCompat(
currentMediaItem, durationMs, result));
newMediaMetadata,
newMediaId,
newMediaUri,
newDurationMs,
/* artworkBitmap= */ result));
sessionImpl.onNotificationRefreshRequired();
}

Expand All @@ -1210,7 +1231,8 @@ public void onFailure(Throwable t) {
}
setMetadata(
sessionCompat,
MediaUtils.convertToMediaMetadataCompat(currentMediaItem, durationMs, artworkBitmap));
MediaUtils.convertToMediaMetadataCompat(
newMediaMetadata, newMediaId, newMediaUri, newDurationMs, artworkBitmap));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,14 +531,25 @@ private static CharSequence getFirstText(
return null;
}

/** Converts a {@link MediaItem} to a {@link MediaMetadataCompat}. */
/**
* Converts a {@link MediaMetadata} to a {@link MediaMetadataCompat}.
*
* @param metadata The {@link MediaMetadata} instance to convert.
* @param mediaId The corresponding media ID.
* @param mediaUri The corresponding media URI, or null if unknown.
* @param durationMs The duration of the media, in milliseconds or {@link C#TIME_UNSET}, if no
* duration should be included.
* @return An instance of the legacy {@link MediaMetadataCompat}.
*/
public static MediaMetadataCompat convertToMediaMetadataCompat(
MediaItem mediaItem, long durationMs, @Nullable Bitmap artworkBitmap) {
MediaMetadata metadata,
String mediaId,
@Nullable Uri mediaUri,
long durationMs,
@Nullable Bitmap artworkBitmap) {
MediaMetadataCompat.Builder builder =
new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, mediaItem.mediaId);

MediaMetadata metadata = mediaItem.mediaMetadata;
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, mediaId);

if (metadata.title != null) {
builder.putText(MediaMetadataCompat.METADATA_KEY_TITLE, metadata.title);
Expand Down Expand Up @@ -569,10 +580,8 @@ public static MediaMetadataCompat convertToMediaMetadataCompat(
builder.putLong(MediaMetadataCompat.METADATA_KEY_YEAR, metadata.recordingYear);
}

if (mediaItem.requestMetadata.mediaUri != null) {
builder.putString(
MediaMetadataCompat.METADATA_KEY_MEDIA_URI,
mediaItem.requestMetadata.mediaUri.toString());
if (mediaUri != null) {
builder.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI, mediaUri.toString());
}

if (metadata.artworkUri != null) {
Expand All @@ -597,21 +606,18 @@ public static MediaMetadataCompat convertToMediaMetadataCompat(
builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, durationMs);
}

@Nullable
RatingCompat userRatingCompat = convertToRatingCompat(mediaItem.mediaMetadata.userRating);
@Nullable RatingCompat userRatingCompat = convertToRatingCompat(metadata.userRating);
if (userRatingCompat != null) {
builder.putRating(MediaMetadataCompat.METADATA_KEY_USER_RATING, userRatingCompat);
}

@Nullable
RatingCompat overallRatingCompat = convertToRatingCompat(mediaItem.mediaMetadata.overallRating);
@Nullable RatingCompat overallRatingCompat = convertToRatingCompat(metadata.overallRating);
if (overallRatingCompat != null) {
builder.putRating(MediaMetadataCompat.METADATA_KEY_RATING, overallRatingCompat);
}

if (mediaItem.mediaMetadata.mediaType != null) {
builder.putLong(
MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT, mediaItem.mediaMetadata.mediaType);
if (metadata.mediaType != null) {
builder.putLong(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT, metadata.mediaType);
}

return builder.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ interface IRemoteMediaSession {

void setTimeline(String sessionId, in Bundle timeline);
void createAndSetFakeTimeline(String sessionId, int windowCount);
void setMediaMetadata(String sessionId, in Bundle metadata);
void setPlaylistMetadata(String sessionId, in Bundle metadata);
void setShuffleModeEnabled(String sessionId, boolean shuffleMode);
void setRepeatMode(String sessionId, int repeatMode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public void gettersAfterConnected() throws Exception {
.setBufferedPosition(testBufferingPosition)
.setPlaybackParameters(new PlaybackParameters(testSpeed))
.setTimeline(testTimeline)
.setMediaMetadata(testMediaItems.get(testItemIndex).mediaMetadata)
.setPlaylistMetadata(testPlaylistMetadata)
.setCurrentMediaItemIndex(testItemIndex)
.setShuffleModeEnabled(testShuffleModeEnabled)
Expand Down Expand Up @@ -370,6 +371,7 @@ public void onShuffleModeChanged(int shuffleMode) {
.setDuration(testDurationMs)
.setPlaybackParameters(playbackParameters)
.setTimeline(testTimeline)
.setMediaMetadata(testMediaItems.get(testItemIndex).mediaMetadata)
.setPlaylistMetadata(testPlaylistMetadata)
.setCurrentMediaItemIndex(testItemIndex)
.setShuffleModeEnabled(testShuffleModeEnabled)
Expand Down Expand Up @@ -979,60 +981,125 @@ public void onExtrasChanged(Bundle extras) {
}

@Test
public void currentMediaItemChange() throws Exception {
public void onMediaItemTransition_updatesLegacyMetadataAndPlaybackState_correctModelConversion()
throws Exception {
int testItemIndex = 3;
long testPosition = 1234;
String testDisplayTitle = "displayTitle";
long testDurationMs = 30_000;
List<MediaItem> testMediaItems = MediaTestUtils.createMediaItems(/* size= */ 5);
String testCurrentMediaId = testMediaItems.get(testItemIndex).mediaId;
MediaMetadata testMediaMetadata =
new MediaMetadata.Builder().setTitle(testDisplayTitle).build();
testMediaItems.set(
testItemIndex,
new MediaItem.Builder()
.setMediaId(testMediaItems.get(testItemIndex).mediaId)
.setMediaMetadata(new MediaMetadata.Builder().setTitle(testDisplayTitle).build())
.setMediaMetadata(testMediaMetadata)
.build());
Timeline timeline = new PlaylistTimeline(testMediaItems);
session.getMockPlayer().setTimeline(timeline);
session.getMockPlayer().setTimeline(new PlaylistTimeline(testMediaItems));
session.getMockPlayer().setCurrentMediaItemIndex(testItemIndex);
session.getMockPlayer().setCurrentPosition(testPosition);
session.getMockPlayer().setDuration(testDurationMs);
session.getMockPlayer().setMediaMetadata(testMediaMetadata);
AtomicReference<MediaMetadataCompat> metadataRef = new AtomicReference<>();
AtomicReference<PlaybackStateCompat> playbackStateRef = new AtomicReference<>();
CountDownLatch latchForMetadata = new CountDownLatch(1);
CountDownLatch latchForPlaybackState = new CountDownLatch(1);
List<String> callbackOrder = new ArrayList<>();
MediaControllerCompat.Callback callback =
new MediaControllerCompat.Callback() {
@Override
public void onMetadataChanged(MediaMetadataCompat metadata) {
metadataRef.set(metadata);
callbackOrder.add("onMetadataChanged");
latchForMetadata.countDown();
}

@Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
playbackStateRef.set(state);
callbackOrder.add("onPlaybackStateChanged");
latchForPlaybackState.countDown();
}
};
controllerCompat.registerCallback(callback, handler);

session.getMockPlayer().setCurrentMediaItemIndex(testItemIndex);
session.getMockPlayer().setCurrentPosition(testPosition);
session
.getMockPlayer()
.notifyMediaItemTransition(testItemIndex, Player.MEDIA_ITEM_TRANSITION_REASON_SEEK);

// Assert metadata.
assertThat(latchForMetadata.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(metadataRef.get().getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE))
MediaMetadataCompat parameterMetadataCompat = metadataRef.get();
MediaMetadataCompat getterMetadataCompat = controllerCompat.getMetadata();
assertThat(parameterMetadataCompat.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE))
.isEqualTo(testDisplayTitle);
assertThat(
controllerCompat
.getMetadata()
.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE))
assertThat(getterMetadataCompat.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE))
.isEqualTo(testDisplayTitle);
assertThat(parameterMetadataCompat.getLong(METADATA_KEY_DURATION)).isEqualTo(testDurationMs);
assertThat(getterMetadataCompat.getLong(METADATA_KEY_DURATION)).isEqualTo(testDurationMs);
assertThat(parameterMetadataCompat.getString(METADATA_KEY_MEDIA_ID))
.isEqualTo(testCurrentMediaId);
assertThat(getterMetadataCompat.getString(METADATA_KEY_MEDIA_ID)).isEqualTo(testCurrentMediaId);
// Assert the playback state.
assertThat(latchForPlaybackState.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(playbackStateRef.get().getPosition()).isEqualTo(testPosition);
assertThat(controllerCompat.getPlaybackState().getPosition()).isEqualTo(testPosition);
assertThat(playbackStateRef.get().getActiveQueueItemId())
.isEqualTo(MediaUtils.convertToQueueItemId(testItemIndex));
assertThat(controllerCompat.getPlaybackState().getActiveQueueItemId())
.isEqualTo(MediaUtils.convertToQueueItemId(testItemIndex));
assertThat(callbackOrder)
.containsExactly("onMetadataChanged", "onPlaybackStateChanged")
.inOrder();
}

@Test
public void onMediaMetadataChanged_updatesLegacyMetadata_correctModelConversion()
throws Exception {
int testItemIndex = 3;
String testDisplayTitle = "displayTitle";
long testDurationMs = 30_000;
List<MediaItem> testMediaItems = MediaTestUtils.createMediaItems(/* size= */ 5);
String testCurrentMediaId = testMediaItems.get(testItemIndex).mediaId;
MediaMetadata testMediaMetadata =
new MediaMetadata.Builder().setTitle(testDisplayTitle).build();
testMediaItems.set(
testItemIndex,
new MediaItem.Builder()
.setMediaId(testMediaItems.get(testItemIndex).mediaId)
.setMediaMetadata(testMediaMetadata)
.build());
session.getMockPlayer().setTimeline(new PlaylistTimeline(testMediaItems));
session.getMockPlayer().setCurrentMediaItemIndex(testItemIndex);
session.getMockPlayer().setDuration(testDurationMs);
AtomicReference<MediaMetadataCompat> metadataRef = new AtomicReference<>();
CountDownLatch latchForMetadata = new CountDownLatch(1);
MediaControllerCompat.Callback callback =
new MediaControllerCompat.Callback() {
@Override
public void onMetadataChanged(MediaMetadataCompat metadata) {
metadataRef.set(metadata);
latchForMetadata.countDown();
}
};
controllerCompat.registerCallback(callback, handler);

session.getMockPlayer().notifyMediaMetadataChanged(testMediaMetadata);

assertThat(latchForMetadata.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
MediaMetadataCompat parameterMetadataCompat = metadataRef.get();
MediaMetadataCompat getterMetadataCompat = controllerCompat.getMetadata();
assertThat(parameterMetadataCompat.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE))
.isEqualTo(testDisplayTitle);
assertThat(getterMetadataCompat.getString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE))
.isEqualTo(testDisplayTitle);
assertThat(parameterMetadataCompat.getLong(METADATA_KEY_DURATION)).isEqualTo(testDurationMs);
assertThat(getterMetadataCompat.getLong(METADATA_KEY_DURATION)).isEqualTo(testDurationMs);
assertThat(parameterMetadataCompat.getString(METADATA_KEY_MEDIA_ID))
.isEqualTo(testCurrentMediaId);
assertThat(getterMetadataCompat.getString(METADATA_KEY_MEDIA_ID)).isEqualTo(testCurrentMediaId);
}

@Test
Expand Down
Loading

0 comments on commit 375299b

Please sign in to comment.