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 {@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 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.
*