From b336df3ed321a5a4f4cfbec9e726581048e51419 Mon Sep 17 00:00:00 2001 From: samrobinson Date: Tue, 27 Apr 2021 20:29:24 +0100 Subject: [PATCH] Move the ownership of MediaMetadata to ExoPlayerImpl. Add the onMediaMetadataChanged event to onEvents. PiperOrigin-RevId: 370738521 --- .../android/exoplayer2/MediaMetadata.java | 22 ++++++++ .../com/google/android/exoplayer2/Player.java | 8 ++- .../android/exoplayer2/ExoPlayerImpl.java | 52 ++++++++++++++----- .../android/exoplayer2/SimpleExoPlayer.java | 30 +---------- .../analytics/AnalyticsCollector.java | 10 ++++ .../analytics/AnalyticsListener.java | 18 +++++++ 6 files changed, 99 insertions(+), 41 deletions(-) diff --git a/library/common/src/main/java/com/google/android/exoplayer2/MediaMetadata.java b/library/common/src/main/java/com/google/android/exoplayer2/MediaMetadata.java index d9de4e141f..eecfd2e68e 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/MediaMetadata.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/MediaMetadata.java @@ -23,6 +23,7 @@ import com.google.android.exoplayer2.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; /** Metadata of a {@link MediaItem} or a playlist. */ public final class MediaMetadata implements Bundleable { @@ -68,6 +69,27 @@ public final class MediaMetadata implements Bundleable { return this; } + /** + * Sets all fields supported by the {@link Metadata.Entry entries} within the list of {@link + * Metadata}. + * + *

Fields are only set if the {@link Metadata.Entry} has an implementation for {@link + * Metadata.Entry#populateMediaMetadata(Builder)}. + * + *

In the event that multiple {@link Metadata.Entry} objects within any of the {@link + * Metadata} relate to the same {@link MediaMetadata} field, then the last one will be used. + */ + public Builder populateFromMetadata(List metadataList) { + for (int i = 0; i < metadataList.size(); i++) { + Metadata metadata = metadataList.get(i); + for (int j = 0; j < metadata.length(); j++) { + Metadata.Entry entry = metadata.get(j); + entry.populateMediaMetadata(this); + } + } + return this; + } + /** Returns a new {@link MediaMetadata} instance with the current builder values. */ public MediaMetadata build() { return new MediaMetadata(/* builder= */ this); 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 fc7ca7a316..4e3348fb8a 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 @@ -157,6 +157,9 @@ public interface Player { * and the static and dynamic metadata sourced from {@link * EventListener#onStaticMetadataChanged(List)} and {@link MetadataOutput#onMetadata(Metadata)}. * + *

{@link #onEvents(Player, Events)} will also be called to report this event along with + * other events that happen in the same {@link Looper} message queue iteration. + * * @param mediaMetadata The combined {@link MediaMetadata}. */ default void onMediaMetadataChanged(MediaMetadata mediaMetadata) {} @@ -899,7 +902,8 @@ public interface Player { EVENT_PLAYER_ERROR, EVENT_POSITION_DISCONTINUITY, EVENT_PLAYBACK_PARAMETERS_CHANGED, - EVENT_AVAILABLE_COMMANDS_CHANGED + EVENT_AVAILABLE_COMMANDS_CHANGED, + EVENT_MEDIA_METADATA_CHANGED }) @interface EventFlags {} /** {@link #getCurrentTimeline()} changed. */ @@ -935,6 +939,8 @@ public interface Player { int EVENT_PLAYBACK_PARAMETERS_CHANGED = 13; /** {@link #isCommandAvailable(int)} changed for at least one {@link Command}. */ int EVENT_AVAILABLE_COMMANDS_CHANGED = 14; + /** {@link #getMediaMetadata()} changed. */ + int EVENT_MEDIA_METADATA_CHANGED = 15; /** * Commands that can be executed on a {@code Player}. One of {@link #COMMAND_PLAY_PAUSE}, {@link 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 c5f126a59f..c6f8e18ae4 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 @@ -102,6 +102,7 @@ import java.util.concurrent.CopyOnWriteArraySet; private ShuffleOrder shuffleOrder; private boolean pauseAtEndOfMediaItems; private Commands availableCommands; + private MediaMetadata mediaMetadata; // Playback information when there is no pending seek/set source operation. private PlaybackInfo playbackInfo; @@ -208,6 +209,7 @@ import java.util.concurrent.CopyOnWriteArraySet; .add(COMMAND_SEEK_TO_DEFAULT_POSITION) .add(COMMAND_SEEK_TO_MEDIA_ITEM) .build(); + mediaMetadata = MediaMetadata.EMPTY; maskingWindowIndex = C.INDEX_UNSET; playbackInfoUpdateHandler = clock.createHandler(applicationLooper, /* callback= */ null); playbackInfoUpdateListener = @@ -983,8 +985,18 @@ import java.util.concurrent.CopyOnWriteArraySet; @Override public MediaMetadata getMediaMetadata() { - // Unsupported operation. - return MediaMetadata.EMPTY; + return mediaMetadata; + } + + public void onMetadata(Metadata metadata) { + MediaMetadata newMediaMetadata = + mediaMetadata.buildUpon().populateFromMetadata(metadata).build(); + if (newMediaMetadata.equals(mediaMetadata)) { + return; + } + mediaMetadata = newMediaMetadata; + listeners.sendEvent( + EVENT_MEDIA_METADATA_CHANGED, listener -> listener.onMediaMetadataChanged(mediaMetadata)); } @Override @@ -1194,6 +1206,24 @@ import java.util.concurrent.CopyOnWriteArraySet; !previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline)); boolean mediaItemTransitioned = mediaItemTransitionInfo.first; int mediaItemTransitionReason = mediaItemTransitionInfo.second; + MediaMetadata newMediaMetadata = mediaMetadata; + @Nullable MediaItem mediaItem = null; + if (mediaItemTransitioned) { + if (!newPlaybackInfo.timeline.isEmpty()) { + int windowIndex = + newPlaybackInfo.timeline.getPeriodByUid(newPlaybackInfo.periodId.periodUid, period) + .windowIndex; + mediaItem = newPlaybackInfo.timeline.getWindow(windowIndex, window).mediaItem; + } + mediaMetadata = mediaItem != null ? mediaItem.mediaMetadata : MediaMetadata.EMPTY; + } + if (!previousPlaybackInfo.staticMetadata.equals(newPlaybackInfo.staticMetadata)) { + newMediaMetadata = + newMediaMetadata.buildUpon().populateFromMetadata(newPlaybackInfo.staticMetadata).build(); + } + boolean metadataChanged = !newMediaMetadata.equals(mediaMetadata); + mediaMetadata = newMediaMetadata; + if (!previousPlaybackInfo.timeline.equals(newPlaybackInfo.timeline)) { listeners.queueEvent( Player.EVENT_TIMELINE_CHANGED, @@ -1222,18 +1252,10 @@ import java.util.concurrent.CopyOnWriteArraySet; }); } if (mediaItemTransitioned) { - @Nullable final MediaItem mediaItem; - if (!newPlaybackInfo.timeline.isEmpty()) { - int windowIndex = - newPlaybackInfo.timeline.getPeriodByUid(newPlaybackInfo.periodId.periodUid, period) - .windowIndex; - mediaItem = newPlaybackInfo.timeline.getWindow(windowIndex, window).mediaItem; - } else { - mediaItem = null; - } + @Nullable final MediaItem finalMediaItem = mediaItem; listeners.queueEvent( Player.EVENT_MEDIA_ITEM_TRANSITION, - listener -> listener.onMediaItemTransition(mediaItem, mediaItemTransitionReason)); + listener -> listener.onMediaItemTransition(finalMediaItem, mediaItemTransitionReason)); } if (previousPlaybackInfo.playbackError != newPlaybackInfo.playbackError && newPlaybackInfo.playbackError != null) { @@ -1254,6 +1276,12 @@ import java.util.concurrent.CopyOnWriteArraySet; Player.EVENT_STATIC_METADATA_CHANGED, listener -> listener.onStaticMetadataChanged(newPlaybackInfo.staticMetadata)); } + if (metadataChanged) { + final MediaMetadata finalMediaMetadata = mediaMetadata; + listeners.queueEvent( + Player.EVENT_MEDIA_METADATA_CHANGED, + listener -> listener.onMediaMetadataChanged(finalMediaMetadata)); + } if (previousPlaybackInfo.isLoading != newPlaybackInfo.isLoading) { listeners.queueEvent( Player.EVENT_IS_LOADING_CHANGED, 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 bed3526dd8..e70497dcd1 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 @@ -636,7 +636,6 @@ public class SimpleExoPlayer extends BasePlayer private boolean isPriorityTaskManagerRegistered; private boolean playerReleased; private DeviceInfo deviceInfo; - private MediaMetadata currentMediaMetadata; /** @deprecated Use the {@link Builder} and pass it to {@link #SimpleExoPlayer(Builder)}. */ @Deprecated @@ -749,7 +748,6 @@ public class SimpleExoPlayer extends BasePlayer wifiLockManager = new WifiLockManager(builder.context); wifiLockManager.setEnabled(builder.wakeMode == C.WAKE_MODE_NETWORK); deviceInfo = createDeviceInfo(streamVolumeManager); - currentMediaMetadata = MediaMetadata.EMPTY; sendRendererMessage(C.TRACK_TYPE_AUDIO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); sendRendererMessage(C.TRACK_TYPE_VIDEO, MSG_SET_AUDIO_SESSION_ID, audioSessionId); @@ -1656,7 +1654,7 @@ public class SimpleExoPlayer extends BasePlayer @Override public MediaMetadata getMediaMetadata() { - return currentMediaMetadata; + return player.getMediaMetadata(); } @Override @@ -2069,14 +2067,6 @@ public class SimpleExoPlayer extends BasePlayer return keepSessionIdAudioTrack.getAudioSessionId(); } - private void setMediaMetadata(MediaMetadata mediaMetadata) { - if (this.currentMediaMetadata.equals(mediaMetadata)) { - return; - } - this.currentMediaMetadata = mediaMetadata; - componentListener.onMediaMetadataChanged(this.currentMediaMetadata); - } - private static DeviceInfo createDeviceInfo(StreamVolumeManager streamVolumeManager) { return new DeviceInfo( DeviceInfo.PLAYBACK_TYPE_LOCAL, @@ -2252,7 +2242,7 @@ public class SimpleExoPlayer extends BasePlayer @Override public void onMetadata(Metadata metadata) { analyticsCollector.onMetadata(metadata); - setMediaMetadata(getMediaMetadata().buildUpon().populateFromMetadata(metadata).build()); + player.onMetadata(metadata); for (MetadataOutput metadataOutput : metadataOutputs) { metadataOutput.onMetadata(metadata); } @@ -2376,22 +2366,6 @@ public class SimpleExoPlayer extends BasePlayer } } - @Override - public void onMediaItemTransition( - @Nullable MediaItem mediaItem, @MediaItemTransitionReason int reason) { - currentMediaMetadata = mediaItem == null ? MediaMetadata.EMPTY : mediaItem.mediaMetadata; - } - - @Override - public void onStaticMetadataChanged(List metadataList) { - MediaMetadata.Builder metadataBuilder = getMediaMetadata().buildUpon(); - for (int i = 0; i < metadataList.size(); i++) { - metadataBuilder.populateFromMetadata(metadataList.get(i)); - } - - setMediaMetadata(metadataBuilder.build()); - } - @Override public void onPlaybackStateChanged(@State int playbackState) { updateWakeAndWifiLock(); 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 51c89217f4..8b47d629e1 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 @@ -25,6 +25,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.MediaMetadata; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player.PlaybackSuppressionReason; @@ -732,6 +733,15 @@ public class AnalyticsCollector listener -> listener.onPlaybackParametersChanged(eventTime, playbackParameters)); } + @Override + public void onMediaMetadataChanged(MediaMetadata mediaMetadata) { + EventTime eventTime = generateCurrentPlayerMediaPeriodEventTime(); + sendEvent( + eventTime, + AnalyticsListener.EVENT_MEDIA_METADATA_CHANGED, + listener -> listener.onMediaMetadataChanged(eventTime, mediaMetadata)); + } + @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 7c4e9750a0..3d9d5bbf80 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 @@ -29,6 +29,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.MediaMetadata; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player.DiscontinuityReason; @@ -42,6 +43,7 @@ import com.google.android.exoplayer2.decoder.DecoderException; import com.google.android.exoplayer2.decoder.DecoderReuseEvaluation; import com.google.android.exoplayer2.drm.DrmSession; import com.google.android.exoplayer2.metadata.Metadata; +import com.google.android.exoplayer2.metadata.MetadataOutput; import com.google.android.exoplayer2.source.LoadEventInfo; import com.google.android.exoplayer2.source.MediaLoadData; import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId; @@ -166,6 +168,7 @@ public interface AnalyticsListener { EVENT_PLAYER_ERROR, EVENT_POSITION_DISCONTINUITY, EVENT_PLAYBACK_PARAMETERS_CHANGED, + EVENT_MEDIA_METADATA_CHANGED, EVENT_LOAD_STARTED, EVENT_LOAD_COMPLETED, EVENT_LOAD_CANCELED, @@ -242,6 +245,8 @@ public interface AnalyticsListener { int EVENT_POSITION_DISCONTINUITY = Player.EVENT_POSITION_DISCONTINUITY; /** {@link Player#getPlaybackParameters()} changed. */ int EVENT_PLAYBACK_PARAMETERS_CHANGED = Player.EVENT_PLAYBACK_PARAMETERS_CHANGED; + /** {@link Player#getMediaMetadata()} changed. */ + int EVENT_MEDIA_METADATA_CHANGED = Player.EVENT_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. */ @@ -640,6 +645,19 @@ public interface AnalyticsListener { */ default void onStaticMetadataChanged(EventTime eventTime, List metadataList) {} + /** + * Called when the combined {@link MediaMetadata} changes. + * + *

The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata} + * and the static and dynamic metadata sourced from {@link + * Player.EventListener#onStaticMetadataChanged(List)} and {@link + * MetadataOutput#onMetadata(Metadata)}. + * + * @param eventTime The event time. + * @param mediaMetadata The combined {@link MediaMetadata}. + */ + default void onMediaMetadataChanged(EventTime eventTime, MediaMetadata mediaMetadata) {} + /** * Called when a media source started loading data. *