Skip to content

Commit cd6c2e9

Browse files
Samrobboojw28
authored andcommitted
Change MediaMetadata update priority to favour MediaItem values.
The static and dynamic metadata now build up in a list, such that when the MediaMetadata is built, they are applied in an event order. This means that newer/fresher values will overwrite older ones. The MediaItem values are then applied at the end, as they take priority over any other. #minor-release PiperOrigin-RevId: 405383177
1 parent 2b97455 commit cd6c2e9

File tree

6 files changed

+241
-38
lines changed

6 files changed

+241
-38
lines changed

RELEASENOTES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
* Fix `mediaMetadata` being reset when media is repeated
2626
([#9458](https://github.com/google/ExoPlayer/issues/9458)).
2727
* Remove final dependency on `jcenter()`.
28+
* Adjust `ExoPlayer` `MediaMetadata` update priority, such that values
29+
input through the `MediaItem.MediaMetadata` are used above media
30+
derived values.
2831
* Video:
2932
* Fix bug in `MediaCodecVideoRenderer` that resulted in re-using a
3033
released `Surface` when playing without an app-provided `Surface`

library/common/src/main/java/com/google/android/exoplayer2/MediaMetadata.java

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,108 @@ public Builder populateFromMetadata(List metadataList) {
383383
return this;
384384
}
385385

386+
/** Populates all the fields from {@code mediaMetadata}, provided they are non-null. */
387+
public Builder populate(@Nullable MediaMetadata mediaMetadata) {
388+
if (mediaMetadata == null) {
389+
return this;
390+
}
391+
if (mediaMetadata.title != null) {
392+
setTitle(mediaMetadata.title);
393+
}
394+
if (mediaMetadata.artist != null) {
395+
setArtist(mediaMetadata.artist);
396+
}
397+
if (mediaMetadata.albumTitle != null) {
398+
setAlbumTitle(mediaMetadata.albumTitle);
399+
}
400+
if (mediaMetadata.albumArtist != null) {
401+
setAlbumArtist(mediaMetadata.albumArtist);
402+
}
403+
if (mediaMetadata.displayTitle != null) {
404+
setDisplayTitle(mediaMetadata.displayTitle);
405+
}
406+
if (mediaMetadata.subtitle != null) {
407+
setSubtitle(mediaMetadata.subtitle);
408+
}
409+
if (mediaMetadata.description != null) {
410+
setDescription(mediaMetadata.description);
411+
}
412+
if (mediaMetadata.mediaUri != null) {
413+
setMediaUri(mediaMetadata.mediaUri);
414+
}
415+
if (mediaMetadata.userRating != null) {
416+
setUserRating(mediaMetadata.userRating);
417+
}
418+
if (mediaMetadata.overallRating != null) {
419+
setOverallRating(mediaMetadata.overallRating);
420+
}
421+
if (mediaMetadata.artworkData != null) {
422+
setArtworkData(mediaMetadata.artworkData, mediaMetadata.artworkDataType);
423+
}
424+
if (mediaMetadata.artworkUri != null) {
425+
setArtworkUri(mediaMetadata.artworkUri);
426+
}
427+
if (mediaMetadata.trackNumber != null) {
428+
setTrackNumber(mediaMetadata.trackNumber);
429+
}
430+
if (mediaMetadata.totalTrackCount != null) {
431+
setTotalTrackCount(mediaMetadata.totalTrackCount);
432+
}
433+
if (mediaMetadata.folderType != null) {
434+
setFolderType(mediaMetadata.folderType);
435+
}
436+
if (mediaMetadata.isPlayable != null) {
437+
setIsPlayable(mediaMetadata.isPlayable);
438+
}
439+
if (mediaMetadata.year != null) {
440+
setRecordingYear(mediaMetadata.year);
441+
}
442+
if (mediaMetadata.recordingYear != null) {
443+
setRecordingYear(mediaMetadata.recordingYear);
444+
}
445+
if (mediaMetadata.recordingMonth != null) {
446+
setRecordingMonth(mediaMetadata.recordingMonth);
447+
}
448+
if (mediaMetadata.recordingDay != null) {
449+
setRecordingDay(mediaMetadata.recordingDay);
450+
}
451+
if (mediaMetadata.releaseYear != null) {
452+
setReleaseYear(mediaMetadata.releaseYear);
453+
}
454+
if (mediaMetadata.releaseMonth != null) {
455+
setReleaseMonth(mediaMetadata.releaseMonth);
456+
}
457+
if (mediaMetadata.releaseDay != null) {
458+
setReleaseDay(mediaMetadata.releaseDay);
459+
}
460+
if (mediaMetadata.writer != null) {
461+
setWriter(mediaMetadata.writer);
462+
}
463+
if (mediaMetadata.composer != null) {
464+
setComposer(mediaMetadata.composer);
465+
}
466+
if (mediaMetadata.conductor != null) {
467+
setConductor(mediaMetadata.conductor);
468+
}
469+
if (mediaMetadata.discNumber != null) {
470+
setDiscNumber(mediaMetadata.discNumber);
471+
}
472+
if (mediaMetadata.totalDiscCount != null) {
473+
setTotalDiscCount(mediaMetadata.totalDiscCount);
474+
}
475+
if (mediaMetadata.genre != null) {
476+
setGenre(mediaMetadata.genre);
477+
}
478+
if (mediaMetadata.compilation != null) {
479+
setCompilation(mediaMetadata.compilation);
480+
}
481+
if (mediaMetadata.extras != null) {
482+
setExtras(mediaMetadata.extras);
483+
}
484+
485+
return this;
486+
}
487+
386488
/** Returns a new {@link MediaMetadata} instance with the current builder values. */
387489
public MediaMetadata build() {
388490
return new MediaMetadata(/* builder= */ this);

library/common/src/main/java/com/google/android/exoplayer2/Player.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2065,7 +2065,9 @@ default void onPlaylistMetadataChanged(MediaMetadata mediaMetadata) {}
20652065
*
20662066
*

This {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata} and the

20672067
* static and dynamic metadata from the {@link TrackSelection#getFormat(int) track selections'
2068-
* formats} and {@link Listener#onMetadata(Metadata)}.
2068+
* formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in the {@link
2069+
* MediaItem#mediaMetadata}, it will be prioritised above the same field coming from static or
2070+
* dynamic metadata.
20692071
*/
20702072
MediaMetadata getMediaMetadata();
20712073

library/common/src/test/java/com/google/android/exoplayer2/MediaMetadataTest.java

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
@RunWith(AndroidJUnit4.class)
2828
public class MediaMetadataTest {
2929

30+
private static final String EXTRAS_KEY = "exampleKey";
31+
private static final String EXTRAS_VALUE = "exampleValue";
32+
3033
@Test
3134
public void builder_minimal_correctDefaults() {
3235
MediaMetadata mediaMetadata = new MediaMetadata.Builder().build();
@@ -91,41 +94,62 @@ public void builderSetArworkUri_setsArtworkUri() {
9194
}
9295

9396
@Test
94-
public void roundTripViaBundle_yieldsEqualInstance() {
95-
Bundle extras = new Bundle();
96-
extras.putString("exampleKey", "exampleValue");
97+
public void populate_populatesEveryField() {
98+
MediaMetadata mediaMetadata = getFullyPopulatedMediaMetadata();
99+
MediaMetadata populated = new MediaMetadata.Builder().populate(mediaMetadata).build();
97100

98-
MediaMetadata mediaMetadata =
99-
new MediaMetadata.Builder()
100-
.setTitle("title")
101-
.setAlbumArtist("the artist")
102-
.setMediaUri(Uri.parse("https://www.google.com"))
103-
.setUserRating(new HeartRating(false))
104-
.setOverallRating(new PercentageRating(87.4f))
105-
.setArtworkData(
106-
new byte[] {-88, 12, 3, 2, 124, -54, -33, 69}, MediaMetadata.PICTURE_TYPE_MEDIA)
107-
.setTrackNumber(4)
108-
.setTotalTrackCount(12)
109-
.setFolderType(MediaMetadata.FOLDER_TYPE_PLAYLISTS)
110-
.setIsPlayable(true)
111-
.setRecordingYear(2000)
112-
.setRecordingMonth(11)
113-
.setRecordingDay(23)
114-
.setReleaseYear(2001)
115-
.setReleaseMonth(1)
116-
.setReleaseDay(2)
117-
.setComposer("Composer")
118-
.setConductor("Conductor")
119-
.setWriter("Writer")
120-
.setDiscNumber(1)
121-
.setTotalDiscCount(3)
122-
.setGenre("Pop")
123-
.setCompilation("Amazing songs.")
124-
.setExtras(extras) // Extras is not implemented in MediaMetadata.equals(Object o).
125-
.build();
101+
// If this assertion fails, it's likely that a field is not being updated in
102+
// MediaMetadata.Builder#populate(MediaMetadata).
103+
assertThat(populated).isEqualTo(mediaMetadata);
104+
assertThat(populated.extras.getString(EXTRAS_KEY)).isEqualTo(EXTRAS_VALUE);
105+
}
106+
107+
@Test
108+
public void roundTripViaBundle_yieldsEqualInstance() {
109+
MediaMetadata mediaMetadata = getFullyPopulatedMediaMetadata();
126110

127111
MediaMetadata fromBundle = MediaMetadata.CREATOR.fromBundle(mediaMetadata.toBundle());
128112
assertThat(fromBundle).isEqualTo(mediaMetadata);
129-
assertThat(fromBundle.extras.getString("exampleKey")).isEqualTo("exampleValue");
113+
// Extras is not implemented in MediaMetadata.equals(Object o).
114+
assertThat(fromBundle.extras.getString(EXTRAS_KEY)).isEqualTo(EXTRAS_VALUE);
115+
}
116+
117+
private static MediaMetadata getFullyPopulatedMediaMetadata() {
118+
Bundle extras = new Bundle();
119+
extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
120+
121+
return new MediaMetadata.Builder()
122+
.setTitle("title")
123+
.setArtist("artist")
124+
.setAlbumTitle("album title")
125+
.setAlbumArtist("album artist")
126+
.setDisplayTitle("display title")
127+
.setSubtitle("subtitle")
128+
.setDescription("description")
129+
.setMediaUri(Uri.parse("https://www.google.com"))
130+
.setUserRating(new HeartRating(false))
131+
.setOverallRating(new PercentageRating(87.4f))
132+
.setArtworkData(
133+
new byte[] {-88, 12, 3, 2, 124, -54, -33, 69}, MediaMetadata.PICTURE_TYPE_MEDIA)
134+
.setArtworkUri(Uri.parse("https://www.google.com"))
135+
.setTrackNumber(4)
136+
.setTotalTrackCount(12)
137+
.setFolderType(MediaMetadata.FOLDER_TYPE_PLAYLISTS)
138+
.setIsPlayable(true)
139+
.setRecordingYear(2000)
140+
.setRecordingMonth(11)
141+
.setRecordingDay(23)
142+
.setReleaseYear(2001)
143+
.setReleaseMonth(1)
144+
.setReleaseDay(2)
145+
.setComposer("Composer")
146+
.setConductor("Conductor")
147+
.setWriter("Writer")
148+
.setDiscNumber(1)
149+
.setTotalDiscCount(3)
150+
.setGenre("Pop")
151+
.setCompilation("Amazing songs.")
152+
.setExtras(extras)
153+
.build();
130154
}
131155
}

library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
4040
import com.google.android.exoplayer2.source.MediaSourceFactory;
4141
import com.google.android.exoplayer2.source.ShuffleOrder;
42+
import com.google.android.exoplayer2.source.TrackGroup;
4243
import com.google.android.exoplayer2.source.TrackGroupArray;
4344
import com.google.android.exoplayer2.text.Cue;
4445
import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
@@ -111,6 +112,10 @@
111112
private MediaMetadata mediaMetadata;
112113
private MediaMetadata playlistMetadata;
113114

115+
// MediaMetadata built from static (TrackGroup Format) and dynamic (onMetadata(Metadata)) metadata
116+
// sources.
117+
private MediaMetadata staticAndDynamicMediaMetadata;
118+
114119
// Playback information when there is no pending seek/set source operation.
115120
private PlaybackInfo playbackInfo;
116121

@@ -229,6 +234,7 @@ public ExoPlayerImpl(
229234
.build();
230235
mediaMetadata = MediaMetadata.EMPTY;
231236
playlistMetadata = MediaMetadata.EMPTY;
237+
staticAndDynamicMediaMetadata = MediaMetadata.EMPTY;
232238
maskingWindowIndex = C.INDEX_UNSET;
233239
playbackInfoUpdateHandler = clock.createHandler(applicationLooper, /* callback= */ null);
234240
playbackInfoUpdateListener =
@@ -986,8 +992,11 @@ public MediaMetadata getMediaMetadata() {
986992
}
987993

988994
public void onMetadata(Metadata metadata) {
989-
MediaMetadata newMediaMetadata =
990-
mediaMetadata.buildUpon().populateFromMetadata(metadata).build();
995+
staticAndDynamicMediaMetadata =
996+
staticAndDynamicMediaMetadata.buildUpon().populateFromMetadata(metadata).build();
997+
998+
MediaMetadata newMediaMetadata = buildUpdatedMediaMetadata();
999+
9911000
if (newMediaMetadata.equals(mediaMetadata)) {
9921001
return;
9931002
}
@@ -1235,12 +1244,17 @@ private void updatePlaybackInfo(
12351244
.windowIndex;
12361245
mediaItem = newPlaybackInfo.timeline.getWindow(windowIndex, window).mediaItem;
12371246
}
1238-
newMediaMetadata = mediaItem != null ? mediaItem.mediaMetadata : MediaMetadata.EMPTY;
1247+
staticAndDynamicMediaMetadata = MediaMetadata.EMPTY;
12391248
}
12401249
if (mediaItemTransitioned
12411250
|| !previousPlaybackInfo.staticMetadata.equals(newPlaybackInfo.staticMetadata)) {
1242-
newMediaMetadata =
1243-
newMediaMetadata.buildUpon().populateFromMetadata(newPlaybackInfo.staticMetadata).build();
1251+
staticAndDynamicMediaMetadata =
1252+
staticAndDynamicMediaMetadata
1253+
.buildUpon()
1254+
.populateFromMetadata(newPlaybackInfo.staticMetadata)
1255+
.build();
1256+
1257+
newMediaMetadata = buildUpdatedMediaMetadata();
12441258
}
12451259
boolean metadataChanged = !newMediaMetadata.equals(mediaMetadata);
12461260
mediaMetadata = newMediaMetadata;
@@ -1794,6 +1808,24 @@ private long periodPositionUsToWindowPositionUs(
17941808
return positionUs;
17951809
}
17961810

1811+
/**
1812+
* Builds a {@link MediaMetadata} from the main sources.
1813+
*
1814+
*

{@link MediaItem} {@link MediaMetadata} is prioritized, with any gaps/missing fields

1815+
* populated by metadata from static ({@link TrackGroup} {@link Format}) and dynamic ({@link
1816+
* #onMetadata(Metadata)}) sources.
1817+
*/
1818+
private MediaMetadata buildUpdatedMediaMetadata() {
1819+
@Nullable MediaItem mediaItem = getCurrentMediaItem();
1820+
1821+
if (mediaItem == null) {
1822+
return staticAndDynamicMediaMetadata;
1823+
}
1824+
1825+
// MediaItem metadata is prioritized over metadata within the media.
1826+
return staticAndDynamicMediaMetadata.buildUpon().populate(mediaItem.mediaMetadata).build();
1827+
}
1828+
17971829
private static boolean isPlaying(PlaybackInfo playbackInfo) {
17981830
return playbackInfo.playbackState == Player.STATE_READY
17991831
&& playbackInfo.playWhenReady

library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11181,6 +11181,46 @@ public void newServerSideInsertedAdAtPlaybackPosition_keepsRenderersEnabled() th
1118111181
assertThat(videoRenderer.get().positionResetCount).isEqualTo(1);
1118211182
}
1118311183

11184+
@Test
11185+
public void setMediaItem_withMediaMetadata_updatesMediaMetadata() {
11186+
MediaMetadata mediaMetadata = new MediaMetadata.Builder().setTitle("the title").build();
11187+
11188+
ExoPlayer player = new TestExoPlayerBuilder(context).build();
11189+
player.setMediaItem(
11190+
new MediaItem.Builder()
11191+
.setMediaId("id")
11192+
.setUri(Uri.EMPTY)
11193+
.setMediaMetadata(mediaMetadata)
11194+
.build());
11195+
11196+
assertThat(player.getMediaMetadata()).isEqualTo(mediaMetadata);
11197+
}
11198+
11199+
@Test
11200+
public void playingMedia_withNoMetadata_doesNotUpdateMediaMetadata() throws Exception {
11201+
MediaMetadata mediaMetadata = new MediaMetadata.Builder().setTitle("the title").build();
11202+
11203+
ExoPlayer player = new TestExoPlayerBuilder(context).build();
11204+
MediaItem mediaItem =
11205+
new MediaItem.Builder()
11206+
.setMediaId("id")
11207+
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
11208+
.setMediaMetadata(mediaMetadata)
11209+
.build();
11210+
player.setMediaItem(mediaItem);
11211+
11212+
assertThat(player.getMediaMetadata()).isEqualTo(mediaMetadata);
11213+
11214+
player.prepare();
11215+
TestPlayerRunHelper.playUntilPosition(
11216+
player, /* windowIndex= */ 0, /* positionMs= */ 5 * C.MILLIS_PER_SECOND);
11217+
player.stop();
11218+
11219+
shadowOf(Looper.getMainLooper()).idle();
11220+
11221+
assertThat(player.getMediaMetadata()).isEqualTo(mediaMetadata);
11222+
}
11223+
1118411224
// Internal methods.
1118511225

1118611226
private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {

0 commit comments

Comments
 (0)