diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java index c5ecc30b0b..ee626ceafc 100644 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java +++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java @@ -528,6 +528,18 @@ public final class CastPlayer extends BasePlayer { return MediaMetadata.EMPTY; } + @Override + public MediaMetadata getPlaylistMediaMetadata() { + // CastPlayer does not currently support metadata. + return MediaMetadata.EMPTY; + } + + /** This method is not supported and does nothing. */ + @Override + public void setPlaylistMediaMetadata(MediaMetadata mediaMetadata) { + // CastPlayer does not currently support metadata. + } + @Override public Timeline getCurrentTimeline() { return currentTimeline; diff --git a/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java b/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java index a687c734c7..fc1552684b 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java @@ -348,6 +348,16 @@ public class ForwardingPlayer implements Player { return player.getMediaMetadata(); } + @Override + public MediaMetadata getPlaylistMediaMetadata() { + return player.getPlaylistMediaMetadata(); + } + + @Override + public void setPlaylistMediaMetadata(MediaMetadata mediaMetadata) { + player.setPlaylistMediaMetadata(mediaMetadata); + } + @Nullable @Override public Object getCurrentManifest() { @@ -613,6 +623,11 @@ public class ForwardingPlayer implements Player { eventListener.onMediaMetadataChanged(mediaMetadata); } + @Override + public void onPlaylistMediaMetadataChanged(MediaMetadata mediaMetadata) { + eventListener.onPlaylistMediaMetadataChanged(mediaMetadata); + } + @Override public void onIsLoadingChanged(boolean isLoading) { eventListener.onIsLoadingChanged(isLoading); diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Player.java b/library/common/src/main/java/com/google/android/exoplayer2/Player.java index ba670c4068..c297df6bd6 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/Player.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/Player.java @@ -161,6 +161,9 @@ public interface Player { */ default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {} + /** Called when the playlist {@link MediaMetadata} changes. */ + default void onPlaylistMediaMetadataChanged(MediaMetadata mediaMetadata) {} + /** * Called when the player starts or stops loading the source. * @@ -1047,7 +1050,8 @@ public interface Player { EVENT_POSITION_DISCONTINUITY, EVENT_PLAYBACK_PARAMETERS_CHANGED, EVENT_AVAILABLE_COMMANDS_CHANGED, - EVENT_MEDIA_METADATA_CHANGED + EVENT_MEDIA_METADATA_CHANGED, + EVENT_PLAYLIST_MEDIA_METADATA_CHANGED }) @interface EventFlags {} /** {@link #getCurrentTimeline()} changed. */ @@ -1085,6 +1089,8 @@ public interface Player { int EVENT_AVAILABLE_COMMANDS_CHANGED = 14; /** {@link #getMediaMetadata()} changed. */ int EVENT_MEDIA_METADATA_CHANGED = 15; + /** {@link #getPlaylistMediaMetadata()} changed. */ + int EVENT_PLAYLIST_MEDIA_METADATA_CHANGED = 16; /** * Commands that can be executed on a {@code Player}. One of {@link #COMMAND_PLAY_PAUSE}, {@link @@ -1723,6 +1729,15 @@ public interface Player { */ MediaMetadata getMediaMetadata(); + /** + * Returns the playlist {@link MediaMetadata}, as set by {@link + * #setPlaylistMediaMetadata(MediaMetadata)}, or {@link MediaMetadata#EMPTY} if not supported. + */ + MediaMetadata getPlaylistMediaMetadata(); + + /** Sets the playlist {@link MediaMetadata}. */ + void setPlaylistMediaMetadata(MediaMetadata mediaMetadata); + /** * Returns the current manifest. The type depends on the type of media being played. May be null. */ diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 0a832aa3b6..cf7ac041ae 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -103,6 +103,7 @@ import java.util.concurrent.CopyOnWriteArraySet; private boolean pauseAtEndOfMediaItems; private Commands availableCommands; private MediaMetadata mediaMetadata; + private MediaMetadata playlistMediaMetadata; private long fastForwardIncrementMs; private long rewindIncrementMs; @@ -213,6 +214,7 @@ import java.util.concurrent.CopyOnWriteArraySet; .add(COMMAND_SEEK_TO_MEDIA_ITEM) .build(); mediaMetadata = MediaMetadata.EMPTY; + playlistMediaMetadata = MediaMetadata.EMPTY; fastForwardIncrementMs = DEFAULT_FAST_FORWARD_INCREMENT_MS; rewindIncrementMs = DEFAULT_REWIND_INCREMENT_MS; maskingWindowIndex = C.INDEX_UNSET; @@ -1026,6 +1028,23 @@ import java.util.concurrent.CopyOnWriteArraySet; EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata)); } + @Override + public MediaMetadata getPlaylistMediaMetadata() { + return playlistMediaMetadata; + } + + @Override + public void setPlaylistMediaMetadata(MediaMetadata playlistMediaMetadata) { + checkNotNull(playlistMediaMetadata); + if (playlistMediaMetadata.equals(this.playlistMediaMetadata)) { + return; + } + this.playlistMediaMetadata = playlistMediaMetadata; + listeners.sendEvent( + EVENT_PLAYLIST_MEDIA_METADATA_CHANGED, + listener -> listener.onPlaylistMediaMetadataChanged(this.playlistMediaMetadata)); + } + @Override public Timeline getCurrentTimeline() { return playbackInfo.timeline; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index f5892987d5..30b589829d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -1689,6 +1689,16 @@ public class SimpleExoPlayer extends BasePlayer return player.getMediaMetadata(); } + @Override + public MediaMetadata getPlaylistMediaMetadata() { + return player.getPlaylistMediaMetadata(); + } + + @Override + public void setPlaylistMediaMetadata(MediaMetadata mediaMetadata) { + player.setPlaylistMediaMetadata(mediaMetadata); + } + @Override public Timeline getCurrentTimeline() { verifyApplicationThread(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java index 5d1c0a9c2f..8acb47ad1b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java @@ -749,6 +749,15 @@ public class AnalyticsCollector listener -> listener.onMediaMetadataChanged(eventTime, mediaMetadata)); } + @Override + public void onPlaylistMediaMetadataChanged(MediaMetadata playlistMediaMetadata) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_PLAYLIST_MEDIA_METADATA_CHANGED, + listener -> listener.onPlaylistMediaMetadataChanged(eventTime, playlistMediaMetadata)); + } + @SuppressWarnings("deprecation") // Implementing and calling deprecated listener method. @Override public final void onSeekProcessed() { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java index 6c349b9935..d5c2768f14 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java @@ -170,6 +170,7 @@ public interface AnalyticsListener { EVENT_POSITION_DISCONTINUITY, EVENT_PLAYBACK_PARAMETERS_CHANGED, EVENT_MEDIA_METADATA_CHANGED, + EVENT_PLAYLIST_MEDIA_METADATA_CHANGED, EVENT_LOAD_STARTED, EVENT_LOAD_COMPLETED, EVENT_LOAD_CANCELED, @@ -248,6 +249,8 @@ public interface AnalyticsListener { int EVENT_PLAYBACK_PARAMETERS_CHANGED = Player.EVENT_PLAYBACK_PARAMETERS_CHANGED; /** {@link Player#getMediaMetadata()} changed. */ int EVENT_MEDIA_METADATA_CHANGED = Player.EVENT_MEDIA_METADATA_CHANGED; + /** {@link Player#getPlaylistMediaMetadata()} changed. */ + int EVENT_PLAYLIST_MEDIA_METADATA_CHANGED = Player.EVENT_PLAYLIST_MEDIA_METADATA_CHANGED; /** A source started loading data. */ int EVENT_LOAD_STARTED = 1000; // Intentional gap to leave space for new Player events /** A source started completed loading data. */ @@ -658,6 +661,15 @@ public interface AnalyticsListener { */ default void onMediaMetadataChanged(EventTime eventTime, MediaMetadata mediaMetadata) {} + /** + * Called when the playlist {@link MediaMetadata} changes. + * + * @param eventTime The event time. + * @param playlistMediaMetadata The playlist {@link MediaMetadata}. + */ + default void onPlaylistMediaMetadataChanged( + EventTime eventTime, MediaMetadata playlistMediaMetadata) {} + /** * Called when a media source started loading data. * diff --git a/library/core/src/test/java/com/google/android/exoplayer2/SimpleExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/SimpleExoPlayerTest.java index 56cc76679a..76fed93286 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/SimpleExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/SimpleExoPlayerTest.java @@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; @@ -61,6 +62,22 @@ public class SimpleExoPlayerTest { assertThat(builderThrow.get()).isNull(); } + @Test + public void onPlaybackMediaMetadataChanged_calledWhenPlaybackMediaMetadataSet() { + SimpleExoPlayer player = + new SimpleExoPlayer.Builder(ApplicationProvider.getApplicationContext()).build(); + Player.Listener playerListener = mock(Player.Listener.class); + player.addListener(playerListener); + AnalyticsListener analyticsListener = mock(AnalyticsListener.class); + player.addAnalyticsListener(analyticsListener); + + MediaMetadata mediaMetadata = new MediaMetadata.Builder().setTitle("test").build(); + player.setPlaylistMediaMetadata(mediaMetadata); + + verify(playerListener).onPlaylistMediaMetadataChanged(mediaMetadata); + verify(analyticsListener).onPlaylistMediaMetadataChanged(any(), eq(mediaMetadata)); + } + @Test public void release_triggersAllPendingEventsInAnalyticsListeners() throws Exception { SimpleExoPlayer player = diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java index 82adac4940..d64b57ff6f 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/StubExoPlayer.java @@ -390,6 +390,16 @@ public class StubExoPlayer extends BasePlayer implements ExoPlayer { throw new UnsupportedOperationException(); } + @Override + public MediaMetadata getPlaylistMediaMetadata() { + throw new UnsupportedOperationException(); + } + + @Override + public void setPlaylistMediaMetadata(MediaMetadata mediaMetadata) { + throw new UnsupportedOperationException(); + } + @Override public Timeline getCurrentTimeline() { throw new UnsupportedOperationException();