From 97716cd0a11ef3b703499d4b790ec1b9164c56e5 Mon Sep 17 00:00:00 2001 From: bachinger Date: Tue, 12 Jul 2022 16:04:45 +0000 Subject: [PATCH] Enable onMediaMetadataChanged in CastPlayer Issue: androidx/media#25 PiperOrigin-RevId: 460476841 (cherry picked from commit 6922bd58ee844cc8293ef885918d01e2d0fbc02b) --- RELEASENOTES.md | 2 + .../java/androidx/media3/cast/CastPlayer.java | 24 +++- .../androidx/media3/cast/CastPlayerTest.java | 112 +++++++++++++++++- 3 files changed, 134 insertions(+), 4 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index c7b9bba99c..3ccb449b8c 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -34,6 +34,8 @@ `Window.mediaItem` in `CastTimeline` ([#25](https://github.com/androidx/media/issues/25), [#8212](https://github.com/google/ExoPlayer/issues/8212)). + * Support `Player.getMetadata()` and `Listener.onMediaMetadataChanged()` + with `CastPlayer` ([#25](https://github.com/androidx/media/issues/25)). ### 1.0.0-beta01 (2022-06-16) diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index 56f434461c..acdd0fe8c7 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -145,6 +145,7 @@ public final class CastPlayer extends BasePlayer { private int pendingSeekWindowIndex; private long pendingSeekPositionMs; @Nullable private PositionInfo pendingMediaItemRemovalPosition; + private MediaMetadata mediaMetadata; /** * Creates a new cast player. @@ -212,6 +213,7 @@ public CastPlayer( playbackParameters = new StateHolder<>(PlaybackParameters.DEFAULT); playbackState = STATE_IDLE; currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE; + mediaMetadata = MediaMetadata.EMPTY; currentTracks = Tracks.EMPTY; availableCommands = new Commands.Builder().addAll(PERMANENT_AVAILABLE_COMMANDS).build(); pendingSeekWindowIndex = C.INDEX_UNSET; @@ -425,6 +427,13 @@ public void seekTo(int mediaItemIndex, long positionMs) { Player.EVENT_MEDIA_ITEM_TRANSITION, listener -> listener.onMediaItemTransition(mediaItem, MEDIA_ITEM_TRANSITION_REASON_SEEK)); + MediaMetadata oldMediaMetadata = mediaMetadata; + mediaMetadata = getMediaMetadataInternal(); + if (!oldMediaMetadata.equals(mediaMetadata)) { + listeners.queueEvent( + Player.EVENT_MEDIA_METADATA_CHANGED, + listener -> listener.onMediaMetadataChanged(mediaMetadata)); + } } updateAvailableCommandsAndNotifyIfChanged(); } else if (pendingSeekCount == 0) { @@ -562,8 +571,12 @@ public void setTrackSelectionParameters(TrackSelectionParameters parameters) {} @Override public MediaMetadata getMediaMetadata() { - // CastPlayer does not currently support metadata. - return MediaMetadata.EMPTY; + return mediaMetadata; + } + + public MediaMetadata getMediaMetadataInternal() { + MediaItem currentMediaItem = getCurrentMediaItem(); + return currentMediaItem != null ? currentMediaItem.mediaMetadata : MediaMetadata.EMPTY; } @Override @@ -760,6 +773,7 @@ private void updateInternalStateAndNotifyIfChanged() { return; } int oldWindowIndex = this.currentWindowIndex; + MediaMetadata oldMediaMetadata = mediaMetadata; @Nullable Object oldPeriodUid = !getCurrentTimeline().isEmpty() @@ -771,6 +785,7 @@ private void updateInternalStateAndNotifyIfChanged() { boolean playingPeriodChangedByTimelineChange = updateTimelineAndNotifyIfChanged(); Timeline currentTimeline = getCurrentTimeline(); currentWindowIndex = fetchCurrentWindowIndex(remoteMediaClient, currentTimeline); + mediaMetadata = getMediaMetadataInternal(); @Nullable Object currentPeriodUid = !currentTimeline.isEmpty() @@ -824,6 +839,11 @@ private void updateInternalStateAndNotifyIfChanged() { listeners.queueEvent( Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(currentTracks)); } + if (!oldMediaMetadata.equals(mediaMetadata)) { + listeners.queueEvent( + Player.EVENT_MEDIA_METADATA_CHANGED, + listener -> listener.onMediaMetadataChanged(mediaMetadata)); + } updateAvailableCommandsAndNotifyIfChanged(); listeners.flushEvents(); } diff --git a/libraries/cast/src/test/java/androidx/media3/cast/CastPlayerTest.java b/libraries/cast/src/test/java/androidx/media3/cast/CastPlayerTest.java index 83273d2a9a..11bbf97f79 100644 --- a/libraries/cast/src/test/java/androidx/media3/cast/CastPlayerTest.java +++ b/libraries/cast/src/test/java/androidx/media3/cast/CastPlayerTest.java @@ -67,6 +67,7 @@ import androidx.media3.common.MimeTypes; import androidx.media3.common.PlaybackParameters; import androidx.media3.common.Player; +import androidx.media3.common.Player.Listener; import androidx.media3.common.Timeline; import androidx.media3.common.util.Assertions; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -107,7 +108,7 @@ public class CastPlayerTest { @Mock private CastContext mockCastContext; @Mock private SessionManager mockSessionManager; @Mock private CastSession mockCastSession; - @Mock private Player.Listener mockListener; + @Mock private Listener mockListener; @Mock private PendingResult mockPendingResult; @Captor @@ -1042,7 +1043,9 @@ public void seekTo_otherWindow_notifiesMediaItemTransition() { castPlayer.addMediaItems(mediaItems); updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1); + MediaMetadata firstMediaMetadata = castPlayer.getMediaMetadata(); castPlayer.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 1234); + MediaMetadata secondMediaMetadata = castPlayer.getMediaMetadata(); InOrder inOrder = Mockito.inOrder(mockListener); inOrder @@ -1053,6 +1056,8 @@ public void seekTo_otherWindow_notifiesMediaItemTransition() { .verify(mockListener) .onMediaItemTransition(eq(mediaItem2), eq(Player.MEDIA_ITEM_TRANSITION_REASON_SEEK)); inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt()); + assertThat(firstMediaMetadata).isEqualTo(mediaItem1.mediaMetadata); + assertThat(secondMediaMetadata).isEqualTo(mediaItem2.mediaMetadata); } @Test @@ -1773,6 +1778,108 @@ public void setRepeatMode_one_doesNotNotifyAvailableCommandsChanged() { verify(mockListener).onAvailableCommandsChanged(any()); } + @Test + public void setMediaItems_doesNotifyOnMetadataChanged() { + when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null))) + .thenReturn(mockPendingResult); + ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(MediaMetadata.class); + String uri1 = "http://www.google.com/video1"; + String uri2 = "http://www.google.com/video2"; + ImmutableList firstPlaylist = + ImmutableList.of( + new MediaItem.Builder() + .setUri(uri1) + .setMimeType(MimeTypes.APPLICATION_MPD) + .setMediaMetadata(new MediaMetadata.Builder().setArtist("foo").build()) + .setTag(1) + .build()); + ImmutableList secondPlaylist = + ImmutableList.of( + new MediaItem.Builder() + .setUri(Uri.EMPTY) + .setTag(2) + .setMediaMetadata(new MediaMetadata.Builder().setArtist("bar").build()) + .setMimeType(MimeTypes.APPLICATION_MPD) + .build(), + new MediaItem.Builder() + .setUri(uri2) + .setMimeType(MimeTypes.APPLICATION_MP4) + .setMediaMetadata(new MediaMetadata.Builder().setArtist("foobar").build()) + .setTag(3) + .build()); + castPlayer.addListener(mockListener); + + MediaMetadata intitalMetadata = castPlayer.getMediaMetadata(); + castPlayer.setMediaItems(firstPlaylist, /* startIndex= */ 0, /* startPositionMs= */ 2000L); + updateTimeLine(firstPlaylist, /* mediaQueueItemIds= */ new int[] {1}, /* currentItemId= */ 1); + MediaMetadata firstMetadata = castPlayer.getMediaMetadata(); + // Replacing existing playlist. + castPlayer.setMediaItems(secondPlaylist, /* startIndex= */ 1, /* startPositionMs= */ 0L); + updateTimeLine( + secondPlaylist, /* mediaQueueItemIds= */ new int[] {2, 3}, /* currentItemId= */ 3); + MediaMetadata secondMetadata = castPlayer.getMediaMetadata(); + castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 0); + MediaMetadata thirdMetadata = castPlayer.getMediaMetadata(); + + verify(mockListener, times(3)).onMediaItemTransition(mediaItemCaptor.capture(), anyInt()); + assertThat(mediaItemCaptor.getAllValues()) + .containsExactly(firstPlaylist.get(0), secondPlaylist.get(1), secondPlaylist.get(0)) + .inOrder(); + verify(mockListener, times(3)).onMediaMetadataChanged(metadataCaptor.capture()); + assertThat(metadataCaptor.getAllValues()) + .containsExactly( + firstPlaylist.get(0).mediaMetadata, + secondPlaylist.get(1).mediaMetadata, + secondPlaylist.get(0).mediaMetadata) + .inOrder(); + assertThat(intitalMetadata).isEqualTo(MediaMetadata.EMPTY); + assertThat(ImmutableList.of(firstMetadata, secondMetadata, thirdMetadata)) + .containsExactly( + firstPlaylist.get(0).mediaMetadata, + secondPlaylist.get(1).mediaMetadata, + secondPlaylist.get(0).mediaMetadata) + .inOrder(); + } + + @Test + public void setMediaItems_equalMetadata_doesNotNotifyOnMediaMetadataChanged() { + when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null))) + .thenReturn(mockPendingResult); + String uri1 = "http://www.google.com/video1"; + String uri2 = "http://www.google.com/video2"; + ImmutableList firstPlaylist = + ImmutableList.of( + new MediaItem.Builder() + .setUri(uri1) + .setMimeType(MimeTypes.APPLICATION_MPD) + .setTag(1) + .build()); + ImmutableList secondPlaylist = + ImmutableList.of( + new MediaItem.Builder() + .setMediaMetadata(MediaMetadata.EMPTY) + .setUri(Uri.EMPTY) + .setTag(2) + .setMimeType(MimeTypes.APPLICATION_MPD) + .build(), + new MediaItem.Builder() + .setUri(uri2) + .setMimeType(MimeTypes.APPLICATION_MP4) + .setTag(3) + .build()); + castPlayer.addListener(mockListener); + + castPlayer.setMediaItems(firstPlaylist, /* startIndex= */ 0, /* startPositionMs= */ 2000L); + updateTimeLine(firstPlaylist, /* mediaQueueItemIds= */ new int[] {1}, /* currentItemId= */ 1); + castPlayer.setMediaItems(secondPlaylist, /* startIndex= */ 1, /* startPositionMs= */ 0L); + updateTimeLine( + secondPlaylist, /* mediaQueueItemIds= */ new int[] {2, 3}, /* currentItemId= */ 3); + castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 0); + + verify(mockListener, times(3)).onMediaItemTransition(any(), anyInt()); + verify(mockListener, never()).onMediaMetadataChanged(any()); + } + private int[] createMediaQueueItemIds(int numberOfIds) { int[] mediaQueueItemIds = new int[numberOfIds]; for (int i = 0; i < numberOfIds; i++) { @@ -1792,7 +1899,8 @@ private List createMediaItems(int[] mediaQueueItemIds) { private MediaItem createMediaItem(int mediaQueueItemId) { return new MediaItem.Builder() .setUri("http://www.google.com/video" + mediaQueueItemId) - .setMediaMetadata(new MediaMetadata.Builder().setArtist("Foo Bar").build()) + .setMediaMetadata( + new MediaMetadata.Builder().setArtist("Foo Bar - " + mediaQueueItemId).build()) .setMimeType(MimeTypes.APPLICATION_MPD) .setTag(mediaQueueItemId) .build();