From d717a0c5d50174b672ad0c2a42251b0eddf26ed6 Mon Sep 17 00:00:00 2001 From: Gilles Khouzam Date: Fri, 31 May 2024 12:19:31 -0700 Subject: [PATCH] Add a CustomData field to the Format class Summary: This change aims to add a generic `CustomData` field to the `Format` class. The intent is to allow ExoPlayer customers to add extra data to the Format class without forcing specific data to be included, impacting customers that do not need it and would allow for the data to be changed without requiring changes to the `Media3` codebase. --- .../java/androidx/media3/common/Format.java | 35 +++++++++++++++- .../androidx/media3/common/FormatTest.java | 42 ++++++++++++++++++- .../exoplayer/audio/DecoderAudioRenderer.java | 1 + .../audio/MediaCodecAudioRenderer.java | 1 + 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/libraries/common/src/main/java/androidx/media3/common/Format.java b/libraries/common/src/main/java/androidx/media3/common/Format.java index 188a7f25d8..046a86fe51 100644 --- a/libraries/common/src/main/java/androidx/media3/common/Format.java +++ b/libraries/common/src/main/java/androidx/media3/common/Format.java @@ -196,6 +196,9 @@ public final class Format { private @C.CryptoType int cryptoType; + // Extra custom data added to the class. + @Nullable private Object customData; + /** Creates a new instance with default values. */ public Builder() { labels = ImmutableList.of(); @@ -273,6 +276,8 @@ public final class Format { this.tileCountVertical = format.tileCountVertical; // Provided by the source. this.cryptoType = format.cryptoType; + // Extra custom data added to the class. + this.customData = format.customData; } /** @@ -729,6 +734,20 @@ public final class Format { return this; } + // Extra custom data added to the class. + + /** + * Sets the opaque object {@link Format#customData}. The default value is null. + * + * @param customData The {@link Format#customData}. + * @return The builder. + */ + @CanIgnoreReturnValue + public Builder setCustomData(@Nullable Object customData) { + this.customData = customData; + return this; + } + // Build. public Format build() { @@ -985,6 +1004,12 @@ public final class Format { */ @UnstableApi public final @C.CryptoType int cryptoType; + /** + * An extra opaque object that can be added to the {@link Format} to provide additional information + * that can be passed through the player. + */ + @UnstableApi @Nullable public final Object customData; + // Lazily initialized hashcode. private int hashCode; @@ -1060,6 +1085,8 @@ public final class Format { } else { cryptoType = builder.cryptoType; } + // Extra custom data added to the class. + customData = builder.customData; } /** Returns a {@link Format.Builder} initialized with the values of this instance. */ @@ -1234,6 +1261,8 @@ public final class Format { result = 31 * result + tileCountVertical; // Provided by the source. result = 31 * result + cryptoType; + // Extra custom data added to the class. + result = 31 * result + (customData == null ? 0 : customData.hashCode()); hashCode = result; } return hashCode; @@ -1284,7 +1313,8 @@ public final class Format { && Util.areEqual(metadata, other.metadata) && Util.areEqual(colorInfo, other.colorInfo) && Util.areEqual(drmInitData, other.drmInitData) - && initializationDataEquals(other); + && initializationDataEquals(other) + && Util.areEqual(customData, other.customData); } /** @@ -1382,6 +1412,9 @@ public final class Format { Joiner.on(',').appendTo(builder, Util.getRoleFlagStrings(format.roleFlags)); builder.append("]"); } + if (format.customData != null) { + builder.append(", customData=").append(format.customData); + } return builder.toString(); } diff --git a/libraries/common/src/test/java/androidx/media3/common/FormatTest.java b/libraries/common/src/test/java/androidx/media3/common/FormatTest.java index e95ee68631..a216276b71 100644 --- a/libraries/common/src/test/java/androidx/media3/common/FormatTest.java +++ b/libraries/common/src/test/java/androidx/media3/common/FormatTest.java @@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import android.os.Bundle; +import androidx.media3.common.util.Util; import androidx.media3.test.utils.FakeMetadataEntry; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; @@ -35,6 +36,36 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public final class FormatTest { + public static class ExoCustomData { + public final String extraMetadata; + public final int customInt; + + public ExoCustomData(String extraMetadata, int customInt) { + this.extraMetadata = extraMetadata; + this.customInt = customInt; + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + (extraMetadata == null ? 0 : extraMetadata.hashCode()); + result = 31 * result + customInt; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + ExoCustomData other = (ExoCustomData) obj; + return Util.areEqual(extraMetadata, other.extraMetadata) && customInt == other.customInt; + } + } + @Test public void buildUponFormat_createsEqualFormat() { Format testFormat = createTestFormat(); @@ -116,7 +147,16 @@ public final class FormatTest { .build()); } - private static Format createTestFormat() { + @Test + public void copyFormat_copiesCustomData() { + Format format = createTestFormat().buildUpon().setCustomData(new ExoCustomData("CustomData", 100)).build(); + + Format copy = format.buildUpon().build(); + assertThat(format.customData).isEqualTo(copy.customData); + assertThat(format.customData).isEqualTo(new ExoCustomData("CustomData", 100)); + } + +private static Format createTestFormat() { byte[] initData1 = new byte[] {1, 2, 3}; byte[] initData2 = new byte[] {4, 5, 6}; List initializationData = new ArrayList<>(); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java index 12b4860c13..5a40f90214 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/DecoderAudioRenderer.java @@ -449,6 +449,7 @@ public abstract class DecoderAudioRenderer< .setEncoderDelay(encoderDelay) .setEncoderPadding(encoderPadding) .setMetadata(inputFormat.metadata) + .setCustomData(inputFormat.customData) .setId(inputFormat.id) .setLabel(inputFormat.label) .setLabels(inputFormat.labels) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java index 24d4cf307b..e3329d2739 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/MediaCodecAudioRenderer.java @@ -573,6 +573,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media .setEncoderDelay(format.encoderDelay) .setEncoderPadding(format.encoderPadding) .setMetadata(format.metadata) + .setCustomData(format.customData) .setId(format.id) .setLabel(format.label) .setLabels(format.labels)