Populate ICY headers into MediaMetadata

Populate ICY headers into MediaMetadata so that they can
propagate to the app via AnalyticsListener#onMediaMetadataChanged().
This change copies IcyHeaders.name into MediaMetadata.description
and IcyHeaders.genre into MediaMetadata.genre.

Note: MediaItem.metadata maintain their precedence and overwrite any
ICY headers parsed.

Issue: google/ExoPlayer#9677

#minor-release

PiperOrigin-RevId: 410495676
This commit is contained in:
christosts 2021-11-17 12:55:41 +00:00 committed by Ian Baker
parent 9d21051c22
commit d5b6250350
4 changed files with 55 additions and 2 deletions

View File

@ -75,6 +75,7 @@ public final class MediaMetadata implements Bundleable {
@Nullable private Integer totalDiscCount; @Nullable private Integer totalDiscCount;
@Nullable private CharSequence genre; @Nullable private CharSequence genre;
@Nullable private CharSequence compilation; @Nullable private CharSequence compilation;
@Nullable private CharSequence station;
@Nullable private Bundle extras; @Nullable private Bundle extras;
public Builder() {} public Builder() {}
@ -110,6 +111,7 @@ public final class MediaMetadata implements Bundleable {
this.totalDiscCount = mediaMetadata.totalDiscCount; this.totalDiscCount = mediaMetadata.totalDiscCount;
this.genre = mediaMetadata.genre; this.genre = mediaMetadata.genre;
this.compilation = mediaMetadata.compilation; this.compilation = mediaMetadata.compilation;
this.station = mediaMetadata.station;
this.extras = mediaMetadata.extras; this.extras = mediaMetadata.extras;
} }
@ -348,6 +350,12 @@ public final class MediaMetadata implements Bundleable {
return this; return this;
} }
/** Sets the name of the station streaming the media. */
public Builder setStation(@Nullable CharSequence station) {
this.station = station;
return this;
}
/** Sets the extras {@link Bundle}. */ /** Sets the extras {@link Bundle}. */
public Builder setExtras(@Nullable Bundle extras) { public Builder setExtras(@Nullable Bundle extras) {
this.extras = extras; this.extras = extras;
@ -490,6 +498,9 @@ public final class MediaMetadata implements Bundleable {
if (mediaMetadata.compilation != null) { if (mediaMetadata.compilation != null) {
setCompilation(mediaMetadata.compilation); setCompilation(mediaMetadata.compilation);
} }
if (mediaMetadata.station != null) {
setStation(mediaMetadata.station);
}
if (mediaMetadata.extras != null) { if (mediaMetadata.extras != null) {
setExtras(mediaMetadata.extras); setExtras(mediaMetadata.extras);
} }
@ -684,6 +695,8 @@ public final class MediaMetadata implements Bundleable {
@Nullable public final CharSequence genre; @Nullable public final CharSequence genre;
/** Optional compilation. */ /** Optional compilation. */
@Nullable public final CharSequence compilation; @Nullable public final CharSequence compilation;
/** Optional name of the station streaming the media. */
@Nullable public final CharSequence station;
/** /**
* Optional extras {@link Bundle}. * Optional extras {@link Bundle}.
@ -725,6 +738,7 @@ public final class MediaMetadata implements Bundleable {
this.totalDiscCount = builder.totalDiscCount; this.totalDiscCount = builder.totalDiscCount;
this.genre = builder.genre; this.genre = builder.genre;
this.compilation = builder.compilation; this.compilation = builder.compilation;
this.station = builder.station;
this.extras = builder.extras; this.extras = builder.extras;
} }
@ -771,7 +785,8 @@ public final class MediaMetadata implements Bundleable {
&& Util.areEqual(discNumber, that.discNumber) && Util.areEqual(discNumber, that.discNumber)
&& Util.areEqual(totalDiscCount, that.totalDiscCount) && Util.areEqual(totalDiscCount, that.totalDiscCount)
&& Util.areEqual(genre, that.genre) && Util.areEqual(genre, that.genre)
&& Util.areEqual(compilation, that.compilation); && Util.areEqual(compilation, that.compilation)
&& Util.areEqual(station, that.station);
} }
@Override @Override
@ -806,7 +821,8 @@ public final class MediaMetadata implements Bundleable {
discNumber, discNumber,
totalDiscCount, totalDiscCount,
genre, genre,
compilation); compilation,
station);
} }
// Bundleable implementation. // Bundleable implementation.
@ -844,6 +860,7 @@ public final class MediaMetadata implements Bundleable {
FIELD_TOTAL_DISC_COUNT, FIELD_TOTAL_DISC_COUNT,
FIELD_GENRE, FIELD_GENRE,
FIELD_COMPILATION, FIELD_COMPILATION,
FIELD_STATION,
FIELD_EXTRAS FIELD_EXTRAS
}) })
private @interface FieldNumber {} private @interface FieldNumber {}
@ -878,6 +895,7 @@ public final class MediaMetadata implements Bundleable {
private static final int FIELD_GENRE = 27; private static final int FIELD_GENRE = 27;
private static final int FIELD_COMPILATION = 28; private static final int FIELD_COMPILATION = 28;
private static final int FIELD_ARTWORK_DATA_TYPE = 29; private static final int FIELD_ARTWORK_DATA_TYPE = 29;
private static final int FIELD_STATION = 30;
private static final int FIELD_EXTRAS = 1000; private static final int FIELD_EXTRAS = 1000;
@UnstableApi @UnstableApi
@ -899,6 +917,7 @@ public final class MediaMetadata implements Bundleable {
bundle.putCharSequence(keyForField(FIELD_CONDUCTOR), conductor); bundle.putCharSequence(keyForField(FIELD_CONDUCTOR), conductor);
bundle.putCharSequence(keyForField(FIELD_GENRE), genre); bundle.putCharSequence(keyForField(FIELD_GENRE), genre);
bundle.putCharSequence(keyForField(FIELD_COMPILATION), compilation); bundle.putCharSequence(keyForField(FIELD_COMPILATION), compilation);
bundle.putCharSequence(keyForField(FIELD_STATION), station);
if (userRating != null) { if (userRating != null) {
bundle.putBundle(keyForField(FIELD_USER_RATING), userRating.toBundle()); bundle.putBundle(keyForField(FIELD_USER_RATING), userRating.toBundle());
@ -976,6 +995,7 @@ public final class MediaMetadata implements Bundleable {
.setConductor(bundle.getCharSequence(keyForField(FIELD_CONDUCTOR))) .setConductor(bundle.getCharSequence(keyForField(FIELD_CONDUCTOR)))
.setGenre(bundle.getCharSequence(keyForField(FIELD_GENRE))) .setGenre(bundle.getCharSequence(keyForField(FIELD_GENRE)))
.setCompilation(bundle.getCharSequence(keyForField(FIELD_COMPILATION))) .setCompilation(bundle.getCharSequence(keyForField(FIELD_COMPILATION)))
.setStation(bundle.getCharSequence(keyForField(FIELD_STATION)))
.setExtras(bundle.getBundle(keyForField(FIELD_EXTRAS))); .setExtras(bundle.getBundle(keyForField(FIELD_EXTRAS)));
if (bundle.containsKey(keyForField(FIELD_USER_RATING))) { if (bundle.containsKey(keyForField(FIELD_USER_RATING))) {

View File

@ -64,6 +64,7 @@ public class MediaMetadataTest {
assertThat(mediaMetadata.totalDiscCount).isNull(); assertThat(mediaMetadata.totalDiscCount).isNull();
assertThat(mediaMetadata.genre).isNull(); assertThat(mediaMetadata.genre).isNull();
assertThat(mediaMetadata.compilation).isNull(); assertThat(mediaMetadata.compilation).isNull();
assertThat(mediaMetadata.station).isNull();
assertThat(mediaMetadata.extras).isNull(); assertThat(mediaMetadata.extras).isNull();
} }
@ -149,6 +150,7 @@ public class MediaMetadataTest {
.setTotalDiscCount(3) .setTotalDiscCount(3)
.setGenre("Pop") .setGenre("Pop")
.setCompilation("Amazing songs.") .setCompilation("Amazing songs.")
.setStation("radio station")
.setExtras(extras) .setExtras(extras)
.build(); .build();
} }

View File

@ -20,6 +20,7 @@ import android.os.Parcelable;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.Format; import androidx.media3.common.Format;
import androidx.media3.common.MediaMetadata;
import androidx.media3.common.Metadata; import androidx.media3.common.Metadata;
import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Log; import androidx.media3.common.util.Log;
@ -171,6 +172,16 @@ public final class IcyHeaders implements Metadata.Entry {
metadataInterval = in.readInt(); metadataInterval = in.readInt();
} }
@Override
public void populateMediaMetadata(MediaMetadata.Builder builder) {
if (name != null) {
builder.setStation(name);
}
if (genre != null) {
builder.setGenre(genre);
}
}
@Override @Override
public boolean equals(@Nullable Object obj) { public boolean equals(@Nullable Object obj) {
if (this == obj) { if (this == obj) {

View File

@ -18,6 +18,7 @@ package androidx.media3.extractor.metadata.icy;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel; import android.os.Parcel;
import androidx.media3.common.MediaMetadata;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -45,4 +46,23 @@ public final class IcyHeadersTest {
// Assert equals. // Assert equals.
assertThat(fromParcelIcyHeaders).isEqualTo(icyHeaders); assertThat(fromParcelIcyHeaders).isEqualTo(icyHeaders);
} }
@Test
public void populateMediaMetadata() {
IcyHeaders headers =
new IcyHeaders(
/* bitrate= */ 1234,
/* genre= */ "pop",
/* name= */ "radio station",
/* url= */ "url",
/* isPublic= */ true,
/* metadataInterval= */ 5678);
MediaMetadata.Builder builder = new MediaMetadata.Builder();
headers.populateMediaMetadata(builder);
MediaMetadata mediaMetadata = builder.build();
assertThat(mediaMetadata.station.toString()).isEqualTo("radio station");
assertThat(mediaMetadata.genre.toString()).isEqualTo("pop");
}
} }