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
This commit is contained in:
parent
2b97455a8c
commit
cd6c2e989f
@ -25,6 +25,9 @@
|
||||
* Fix `mediaMetadata` being reset when media is repeated
|
||||
([#9458](https://github.com/google/ExoPlayer/issues/9458)).
|
||||
* Remove final dependency on `jcenter()`.
|
||||
* Adjust `ExoPlayer` `MediaMetadata` update priority, such that values
|
||||
input through the `MediaItem.MediaMetadata` are used above media
|
||||
derived values.
|
||||
* Video:
|
||||
* Fix bug in `MediaCodecVideoRenderer` that resulted in re-using a
|
||||
released `Surface` when playing without an app-provided `Surface`
|
||||
|
@ -383,6 +383,108 @@ public final class MediaMetadata implements Bundleable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Populates all the fields from {@code mediaMetadata}, provided they are non-null. */
|
||||
public Builder populate(@Nullable MediaMetadata mediaMetadata) {
|
||||
if (mediaMetadata == null) {
|
||||
return this;
|
||||
}
|
||||
if (mediaMetadata.title != null) {
|
||||
setTitle(mediaMetadata.title);
|
||||
}
|
||||
if (mediaMetadata.artist != null) {
|
||||
setArtist(mediaMetadata.artist);
|
||||
}
|
||||
if (mediaMetadata.albumTitle != null) {
|
||||
setAlbumTitle(mediaMetadata.albumTitle);
|
||||
}
|
||||
if (mediaMetadata.albumArtist != null) {
|
||||
setAlbumArtist(mediaMetadata.albumArtist);
|
||||
}
|
||||
if (mediaMetadata.displayTitle != null) {
|
||||
setDisplayTitle(mediaMetadata.displayTitle);
|
||||
}
|
||||
if (mediaMetadata.subtitle != null) {
|
||||
setSubtitle(mediaMetadata.subtitle);
|
||||
}
|
||||
if (mediaMetadata.description != null) {
|
||||
setDescription(mediaMetadata.description);
|
||||
}
|
||||
if (mediaMetadata.mediaUri != null) {
|
||||
setMediaUri(mediaMetadata.mediaUri);
|
||||
}
|
||||
if (mediaMetadata.userRating != null) {
|
||||
setUserRating(mediaMetadata.userRating);
|
||||
}
|
||||
if (mediaMetadata.overallRating != null) {
|
||||
setOverallRating(mediaMetadata.overallRating);
|
||||
}
|
||||
if (mediaMetadata.artworkData != null) {
|
||||
setArtworkData(mediaMetadata.artworkData, mediaMetadata.artworkDataType);
|
||||
}
|
||||
if (mediaMetadata.artworkUri != null) {
|
||||
setArtworkUri(mediaMetadata.artworkUri);
|
||||
}
|
||||
if (mediaMetadata.trackNumber != null) {
|
||||
setTrackNumber(mediaMetadata.trackNumber);
|
||||
}
|
||||
if (mediaMetadata.totalTrackCount != null) {
|
||||
setTotalTrackCount(mediaMetadata.totalTrackCount);
|
||||
}
|
||||
if (mediaMetadata.folderType != null) {
|
||||
setFolderType(mediaMetadata.folderType);
|
||||
}
|
||||
if (mediaMetadata.isPlayable != null) {
|
||||
setIsPlayable(mediaMetadata.isPlayable);
|
||||
}
|
||||
if (mediaMetadata.year != null) {
|
||||
setRecordingYear(mediaMetadata.year);
|
||||
}
|
||||
if (mediaMetadata.recordingYear != null) {
|
||||
setRecordingYear(mediaMetadata.recordingYear);
|
||||
}
|
||||
if (mediaMetadata.recordingMonth != null) {
|
||||
setRecordingMonth(mediaMetadata.recordingMonth);
|
||||
}
|
||||
if (mediaMetadata.recordingDay != null) {
|
||||
setRecordingDay(mediaMetadata.recordingDay);
|
||||
}
|
||||
if (mediaMetadata.releaseYear != null) {
|
||||
setReleaseYear(mediaMetadata.releaseYear);
|
||||
}
|
||||
if (mediaMetadata.releaseMonth != null) {
|
||||
setReleaseMonth(mediaMetadata.releaseMonth);
|
||||
}
|
||||
if (mediaMetadata.releaseDay != null) {
|
||||
setReleaseDay(mediaMetadata.releaseDay);
|
||||
}
|
||||
if (mediaMetadata.writer != null) {
|
||||
setWriter(mediaMetadata.writer);
|
||||
}
|
||||
if (mediaMetadata.composer != null) {
|
||||
setComposer(mediaMetadata.composer);
|
||||
}
|
||||
if (mediaMetadata.conductor != null) {
|
||||
setConductor(mediaMetadata.conductor);
|
||||
}
|
||||
if (mediaMetadata.discNumber != null) {
|
||||
setDiscNumber(mediaMetadata.discNumber);
|
||||
}
|
||||
if (mediaMetadata.totalDiscCount != null) {
|
||||
setTotalDiscCount(mediaMetadata.totalDiscCount);
|
||||
}
|
||||
if (mediaMetadata.genre != null) {
|
||||
setGenre(mediaMetadata.genre);
|
||||
}
|
||||
if (mediaMetadata.compilation != null) {
|
||||
setCompilation(mediaMetadata.compilation);
|
||||
}
|
||||
if (mediaMetadata.extras != null) {
|
||||
setExtras(mediaMetadata.extras);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Returns a new {@link MediaMetadata} instance with the current builder values. */
|
||||
public MediaMetadata build() {
|
||||
return new MediaMetadata(/* builder= */ this);
|
||||
|
@ -2065,7 +2065,9 @@ public interface Player {
|
||||
*
|
||||
* <p>This {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata} and the
|
||||
* static and dynamic metadata from the {@link TrackSelection#getFormat(int) track selections'
|
||||
* formats} and {@link Listener#onMetadata(Metadata)}.
|
||||
* formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in the {@link
|
||||
* MediaItem#mediaMetadata}, it will be prioritised above the same field coming from static or
|
||||
* dynamic metadata.
|
||||
*/
|
||||
MediaMetadata getMediaMetadata();
|
||||
|
||||
|
@ -27,6 +27,9 @@ import org.junit.runner.RunWith;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class MediaMetadataTest {
|
||||
|
||||
private static final String EXTRAS_KEY = "exampleKey";
|
||||
private static final String EXTRAS_VALUE = "exampleValue";
|
||||
|
||||
@Test
|
||||
public void builder_minimal_correctDefaults() {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder().build();
|
||||
@ -91,41 +94,62 @@ public class MediaMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripViaBundle_yieldsEqualInstance() {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("exampleKey", "exampleValue");
|
||||
public void populate_populatesEveryField() {
|
||||
MediaMetadata mediaMetadata = getFullyPopulatedMediaMetadata();
|
||||
MediaMetadata populated = new MediaMetadata.Builder().populate(mediaMetadata).build();
|
||||
|
||||
MediaMetadata mediaMetadata =
|
||||
new MediaMetadata.Builder()
|
||||
.setTitle("title")
|
||||
.setAlbumArtist("the artist")
|
||||
.setMediaUri(Uri.parse("https://www.google.com"))
|
||||
.setUserRating(new HeartRating(false))
|
||||
.setOverallRating(new PercentageRating(87.4f))
|
||||
.setArtworkData(
|
||||
new byte[] {-88, 12, 3, 2, 124, -54, -33, 69}, MediaMetadata.PICTURE_TYPE_MEDIA)
|
||||
.setTrackNumber(4)
|
||||
.setTotalTrackCount(12)
|
||||
.setFolderType(MediaMetadata.FOLDER_TYPE_PLAYLISTS)
|
||||
.setIsPlayable(true)
|
||||
.setRecordingYear(2000)
|
||||
.setRecordingMonth(11)
|
||||
.setRecordingDay(23)
|
||||
.setReleaseYear(2001)
|
||||
.setReleaseMonth(1)
|
||||
.setReleaseDay(2)
|
||||
.setComposer("Composer")
|
||||
.setConductor("Conductor")
|
||||
.setWriter("Writer")
|
||||
.setDiscNumber(1)
|
||||
.setTotalDiscCount(3)
|
||||
.setGenre("Pop")
|
||||
.setCompilation("Amazing songs.")
|
||||
.setExtras(extras) // Extras is not implemented in MediaMetadata.equals(Object o).
|
||||
.build();
|
||||
// If this assertion fails, it's likely that a field is not being updated in
|
||||
// MediaMetadata.Builder#populate(MediaMetadata).
|
||||
assertThat(populated).isEqualTo(mediaMetadata);
|
||||
assertThat(populated.extras.getString(EXTRAS_KEY)).isEqualTo(EXTRAS_VALUE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void roundTripViaBundle_yieldsEqualInstance() {
|
||||
MediaMetadata mediaMetadata = getFullyPopulatedMediaMetadata();
|
||||
|
||||
MediaMetadata fromBundle = MediaMetadata.CREATOR.fromBundle(mediaMetadata.toBundle());
|
||||
assertThat(fromBundle).isEqualTo(mediaMetadata);
|
||||
assertThat(fromBundle.extras.getString("exampleKey")).isEqualTo("exampleValue");
|
||||
// Extras is not implemented in MediaMetadata.equals(Object o).
|
||||
assertThat(fromBundle.extras.getString(EXTRAS_KEY)).isEqualTo(EXTRAS_VALUE);
|
||||
}
|
||||
|
||||
private static MediaMetadata getFullyPopulatedMediaMetadata() {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
|
||||
|
||||
return new MediaMetadata.Builder()
|
||||
.setTitle("title")
|
||||
.setArtist("artist")
|
||||
.setAlbumTitle("album title")
|
||||
.setAlbumArtist("album artist")
|
||||
.setDisplayTitle("display title")
|
||||
.setSubtitle("subtitle")
|
||||
.setDescription("description")
|
||||
.setMediaUri(Uri.parse("https://www.google.com"))
|
||||
.setUserRating(new HeartRating(false))
|
||||
.setOverallRating(new PercentageRating(87.4f))
|
||||
.setArtworkData(
|
||||
new byte[] {-88, 12, 3, 2, 124, -54, -33, 69}, MediaMetadata.PICTURE_TYPE_MEDIA)
|
||||
.setArtworkUri(Uri.parse("https://www.google.com"))
|
||||
.setTrackNumber(4)
|
||||
.setTotalTrackCount(12)
|
||||
.setFolderType(MediaMetadata.FOLDER_TYPE_PLAYLISTS)
|
||||
.setIsPlayable(true)
|
||||
.setRecordingYear(2000)
|
||||
.setRecordingMonth(11)
|
||||
.setRecordingDay(23)
|
||||
.setReleaseYear(2001)
|
||||
.setReleaseMonth(1)
|
||||
.setReleaseDay(2)
|
||||
.setComposer("Composer")
|
||||
.setConductor("Conductor")
|
||||
.setWriter("Writer")
|
||||
.setDiscNumber(1)
|
||||
.setTotalDiscCount(3)
|
||||
.setGenre("Pop")
|
||||
.setCompilation("Amazing songs.")
|
||||
.setExtras(extras)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import com.google.android.exoplayer2.source.MediaSource;
|
||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||
import com.google.android.exoplayer2.source.MediaSourceFactory;
|
||||
import com.google.android.exoplayer2.source.ShuffleOrder;
|
||||
import com.google.android.exoplayer2.source.TrackGroup;
|
||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||
import com.google.android.exoplayer2.text.Cue;
|
||||
import com.google.android.exoplayer2.trackselection.ExoTrackSelection;
|
||||
@ -111,6 +112,10 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
private MediaMetadata mediaMetadata;
|
||||
private MediaMetadata playlistMetadata;
|
||||
|
||||
// MediaMetadata built from static (TrackGroup Format) and dynamic (onMetadata(Metadata)) metadata
|
||||
// sources.
|
||||
private MediaMetadata staticAndDynamicMediaMetadata;
|
||||
|
||||
// Playback information when there is no pending seek/set source operation.
|
||||
private PlaybackInfo playbackInfo;
|
||||
|
||||
@ -229,6 +234,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
.build();
|
||||
mediaMetadata = MediaMetadata.EMPTY;
|
||||
playlistMetadata = MediaMetadata.EMPTY;
|
||||
staticAndDynamicMediaMetadata = MediaMetadata.EMPTY;
|
||||
maskingWindowIndex = C.INDEX_UNSET;
|
||||
playbackInfoUpdateHandler = clock.createHandler(applicationLooper, /* callback= */ null);
|
||||
playbackInfoUpdateListener =
|
||||
@ -986,8 +992,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
}
|
||||
|
||||
public void onMetadata(Metadata metadata) {
|
||||
MediaMetadata newMediaMetadata =
|
||||
mediaMetadata.buildUpon().populateFromMetadata(metadata).build();
|
||||
staticAndDynamicMediaMetadata =
|
||||
staticAndDynamicMediaMetadata.buildUpon().populateFromMetadata(metadata).build();
|
||||
|
||||
MediaMetadata newMediaMetadata = buildUpdatedMediaMetadata();
|
||||
|
||||
if (newMediaMetadata.equals(mediaMetadata)) {
|
||||
return;
|
||||
}
|
||||
@ -1235,12 +1244,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
.windowIndex;
|
||||
mediaItem = newPlaybackInfo.timeline.getWindow(windowIndex, window).mediaItem;
|
||||
}
|
||||
newMediaMetadata = mediaItem != null ? mediaItem.mediaMetadata : MediaMetadata.EMPTY;
|
||||
staticAndDynamicMediaMetadata = MediaMetadata.EMPTY;
|
||||
}
|
||||
if (mediaItemTransitioned
|
||||
|| !previousPlaybackInfo.staticMetadata.equals(newPlaybackInfo.staticMetadata)) {
|
||||
newMediaMetadata =
|
||||
newMediaMetadata.buildUpon().populateFromMetadata(newPlaybackInfo.staticMetadata).build();
|
||||
staticAndDynamicMediaMetadata =
|
||||
staticAndDynamicMediaMetadata
|
||||
.buildUpon()
|
||||
.populateFromMetadata(newPlaybackInfo.staticMetadata)
|
||||
.build();
|
||||
|
||||
newMediaMetadata = buildUpdatedMediaMetadata();
|
||||
}
|
||||
boolean metadataChanged = !newMediaMetadata.equals(mediaMetadata);
|
||||
mediaMetadata = newMediaMetadata;
|
||||
@ -1794,6 +1808,24 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
return positionUs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link MediaMetadata} from the main sources.
|
||||
*
|
||||
* <p>{@link MediaItem} {@link MediaMetadata} is prioritized, with any gaps/missing fields
|
||||
* populated by metadata from static ({@link TrackGroup} {@link Format}) and dynamic ({@link
|
||||
* #onMetadata(Metadata)}) sources.
|
||||
*/
|
||||
private MediaMetadata buildUpdatedMediaMetadata() {
|
||||
@Nullable MediaItem mediaItem = getCurrentMediaItem();
|
||||
|
||||
if (mediaItem == null) {
|
||||
return staticAndDynamicMediaMetadata;
|
||||
}
|
||||
|
||||
// MediaItem metadata is prioritized over metadata within the media.
|
||||
return staticAndDynamicMediaMetadata.buildUpon().populate(mediaItem.mediaMetadata).build();
|
||||
}
|
||||
|
||||
private static boolean isPlaying(PlaybackInfo playbackInfo) {
|
||||
return playbackInfo.playbackState == Player.STATE_READY
|
||||
&& playbackInfo.playWhenReady
|
||||
|
@ -11181,6 +11181,46 @@ public final class ExoPlayerTest {
|
||||
assertThat(videoRenderer.get().positionResetCount).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setMediaItem_withMediaMetadata_updatesMediaMetadata() {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder().setTitle("the title").build();
|
||||
|
||||
ExoPlayer player = new TestExoPlayerBuilder(context).build();
|
||||
player.setMediaItem(
|
||||
new MediaItem.Builder()
|
||||
.setMediaId("id")
|
||||
.setUri(Uri.EMPTY)
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.build());
|
||||
|
||||
assertThat(player.getMediaMetadata()).isEqualTo(mediaMetadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playingMedia_withNoMetadata_doesNotUpdateMediaMetadata() throws Exception {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder().setTitle("the title").build();
|
||||
|
||||
ExoPlayer player = new TestExoPlayerBuilder(context).build();
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder()
|
||||
.setMediaId("id")
|
||||
.setUri(Uri.parse("asset://android_asset/media/mp4/sample.mp4"))
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.build();
|
||||
player.setMediaItem(mediaItem);
|
||||
|
||||
assertThat(player.getMediaMetadata()).isEqualTo(mediaMetadata);
|
||||
|
||||
player.prepare();
|
||||
TestPlayerRunHelper.playUntilPosition(
|
||||
player, /* windowIndex= */ 0, /* positionMs= */ 5 * C.MILLIS_PER_SECOND);
|
||||
player.stop();
|
||||
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
assertThat(player.getMediaMetadata()).isEqualTo(mediaMetadata);
|
||||
}
|
||||
|
||||
// Internal methods.
|
||||
|
||||
private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user