From 963bae9dd8061ad36f873fde1862c856577bc987 Mon Sep 17 00:00:00 2001 From: Copybara-Service Date: Wed, 19 Mar 2025 10:38:33 -0700 Subject: [PATCH] Merge pull request #2235 from MGaetan89:add_CastPlayer_playlistMetadata PiperOrigin-RevId: 738455260 (cherry picked from commit 2d1bcc77be3acd0a8297220f7564483c12db32b3) --- RELEASENOTES.md | 78 +++++++++++++++++++ .../java/androidx/media3/cast/CastPlayer.java | 18 +++-- .../androidx/media3/cast/CastPlayerTest.java | 35 ++++++++- 3 files changed, 123 insertions(+), 8 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4906aa7ce1..0b19f601d9 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,83 @@ # Release notes +* Cast extension: + * Add support for playlist metadata + ([#2235](https://github.com/androidx/media/pull/2235)). + +### Unreleased changes + +* Common Library: +* ExoPlayer: +* Transformer: +* Track Selection: +* Extractors: + * MP3: Use duration and data size from unseekable Xing, VBRI and similar + variable bitrate metadata when falling back to constant bitrate seeking + due to `FLAG_ENABLE_CONSTANT_BITRATE_SEEKING(_ALWAYS)` + ([#2194](https://github.com/androidx/media/issues/2194)). +* DataSource: +* Audio: + * Allow constant power upmixing/downmixing in DefaultAudioMixer. +* Video: + * Add experimental `ExoPlayer` API to include the + `MediaCodec.BUFFER_FLAG_DECODE_ONLY` flag when queuing decode-only input + buffers. This flag will signal the decoder to skip the decode-only + buffers thereby resulting in faster seeking. Enable it with + `DefaultRenderersFactory.experimentalSetEnableMediaCodecBufferDecodeOnlyFlag`. +* Text: +* Metadata: +* Image: +* DataSource: +* DRM: +* Effect: + * Add `Presentation.createForShortSide(int)` that creates a `Presentation` + that ensures the shortest side always matches the given value, + regardless of input orientation. +* Muxers: + * `writeSampleData()` API now uses muxer specific `BufferInfo` class + instead of `MediaCodec.BufferInfo`. +* IMA extension: +* Session: +* UI: +* Downloads: + * Add partial download support for progressive streams. Apps can prepare a + progressive stream with `DownloadHelper`, and request a + `DownloadRequest` from the helper with specifying the time-based media + start and end positions that the download should cover. The returned + `DownloadRequest` carries the resolved byte range, with which a + `ProgressiveDownloader` can be created and download the content + correspondingly. +* OkHttp extension: +* Cronet extension: +* RTMP extension: +* HLS extension: +* DASH extension: +* Smooth Streaming extension: +* RTSP extension: +* Decoder extensions (FFmpeg, VP9, AV1, etc.): +* MIDI extension: +* Leanback extension: +* Cast extension: +* Test Utilities: +* Demo app: + * Add `PlaybackSpeedPopUpButton` Composable UI element to be part of + `ExtraControls` in `demo-compose`. +* Remove deprecated symbols: + * Removed deprecated `SegmentDownloader` constructor + `SegmentDownloader(MediaItem, Parser, CacheDataSource.Factory, + Executor)` and the corresponding constructors in its subclasses + `DashDownloader`, `HlsDownloader` and `SsDownloader`. + * Removed deprecated `Player.hasNext()`, `Player.hasNextWindow()`. Use + `Player.hasNextMediaItem()` instead. + * Removed deprecated `Player.next()`. Use `Player.seekToNextMediaItem()` + instead. + * Removed deprecated `Player.seekToPreviousWindow()`. Use + `Player.seekToPreviousMediaItem()` instead. + * Removed deprecated `Player.seekToNextWindow()`. Use + `Player.seekToNextMediaItem()` instead. + * Removed deprecated `BaseAudioProcessor` in `exoplayer` module. Use + `BaseAudioProcessor` under `common` module. + ## 1.6 ### 1.6.0 (2025-03-26) 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 f57ab9005c..fea8df2056 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -16,6 +16,7 @@ package androidx.media3.cast; import static androidx.media3.common.util.Assertions.checkArgument; +import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Util.SDK_INT; import static androidx.media3.common.util.Util.castNonNull; import static java.lang.Math.min; @@ -166,6 +167,7 @@ public final class CastPlayer extends BasePlayer { private long pendingSeekPositionMs; @Nullable private PositionInfo pendingMediaItemRemovalPosition; private MediaMetadata mediaMetadata; + private MediaMetadata playlistMetadata; private DeviceInfo deviceInfo; /** @@ -268,6 +270,7 @@ public final class CastPlayer extends BasePlayer { playbackState = STATE_IDLE; currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE; mediaMetadata = MediaMetadata.EMPTY; + playlistMetadata = MediaMetadata.EMPTY; currentTracks = Tracks.EMPTY; availableCommands = new Commands.Builder().addAll(PERMANENT_AVAILABLE_COMMANDS).build(); pendingSeekWindowIndex = C.INDEX_UNSET; @@ -656,14 +659,19 @@ public final class CastPlayer extends BasePlayer { @Override public MediaMetadata getPlaylistMetadata() { - // CastPlayer does not currently support metadata. - return MediaMetadata.EMPTY; + return playlistMetadata; } - /** This method is not supported and does nothing. */ @Override - public void setPlaylistMetadata(MediaMetadata mediaMetadata) { - // CastPlayer does not currently support metadata. + public void setPlaylistMetadata(MediaMetadata playlistMetadata) { + checkNotNull(playlistMetadata); + if (playlistMetadata.equals(this.playlistMetadata)) { + return; + } + this.playlistMetadata = playlistMetadata; + listeners.sendEvent( + EVENT_PLAYLIST_METADATA_CHANGED, + listener -> listener.onPlaylistMetadataChanged(this.playlistMetadata)); } @Override 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 2af48add70..7eeb29b27b 100644 --- a/libraries/cast/src/test/java/androidx/media3/cast/CastPlayerTest.java +++ b/libraries/cast/src/test/java/androidx/media3/cast/CastPlayerTest.java @@ -1800,7 +1800,7 @@ public class CastPlayerTest { } @Test - public void setMediaItems_doesNotifyOnMetadataChanged() { + public void setMediaItems_doesNotifyOnMediaMetadataChanged() { when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null))) .thenReturn(mockPendingResult); ArgumentCaptor metadataCaptor = ArgumentCaptor.forClass(MediaMetadata.class); @@ -1827,7 +1827,7 @@ public class CastPlayerTest { .build()); castPlayer.addListener(mockListener); - MediaMetadata intitalMetadata = castPlayer.getMediaMetadata(); + MediaMetadata initialMetadata = castPlayer.getMediaMetadata(); castPlayer.setMediaItems(firstPlaylist, /* startIndex= */ 0, /* startPositionMs= */ 2000L); updateTimeLine(firstPlaylist, /* mediaQueueItemIds= */ new int[] {1}, /* currentItemId= */ 1); MediaMetadata firstMetadata = castPlayer.getMediaMetadata(); @@ -1850,7 +1850,7 @@ public class CastPlayerTest { secondPlaylist.get(1).mediaMetadata, secondPlaylist.get(0).mediaMetadata) .inOrder(); - assertThat(intitalMetadata).isEqualTo(MediaMetadata.EMPTY); + assertThat(initialMetadata).isEqualTo(MediaMetadata.EMPTY); assertThat(ImmutableList.of(firstMetadata, secondMetadata, thirdMetadata)) .containsExactly( firstPlaylist.get(0).mediaMetadata, @@ -1898,6 +1898,35 @@ public class CastPlayerTest { verify(mockListener, never()).onMediaMetadataChanged(any()); } + @Test + public void setPlaylistMetadata_doesNotifyOnPlaylistMetadataChanged() { + castPlayer.addListener(mockListener); + + MediaMetadata metadata = new MediaMetadata.Builder().setArtist("foo").build(); + + assertThat(castPlayer.getPlaylistMetadata()).isEqualTo(MediaMetadata.EMPTY); + + castPlayer.setPlaylistMetadata(metadata); + + assertThat(castPlayer.getPlaylistMetadata()).isEqualTo(metadata); + + verify(mockListener).onPlaylistMetadataChanged(metadata); + } + + @Test + public void setPlaylistMetadata_equalMetadata_doesNotNotifyOnPlaylistMetadataChanged() { + castPlayer.addListener(mockListener); + + MediaMetadata metadata = new MediaMetadata.Builder().setArtist("foo").build(); + + castPlayer.setPlaylistMetadata(metadata); + castPlayer.setPlaylistMetadata(metadata); + + assertThat(castPlayer.getPlaylistMetadata()).isEqualTo(metadata); + + verify(mockListener, times(1)).onPlaylistMetadataChanged(metadata); + } + @Test public void getDeviceInfo_returnsCorrectDeviceInfoWithPlaybackTypeRemote() { DeviceInfo deviceInfo = castPlayer.getDeviceInfo();