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.
This commit is contained in:
Gilles Khouzam 2024-05-31 12:19:31 -07:00 committed by tonihei
parent 476ec607f2
commit d717a0c5d5
4 changed files with 77 additions and 2 deletions

View File

@ -196,6 +196,9 @@ public final class Format {
private @C.CryptoType int cryptoType; private @C.CryptoType int cryptoType;
// Extra custom data added to the class.
@Nullable private Object customData;
/** Creates a new instance with default values. */ /** Creates a new instance with default values. */
public Builder() { public Builder() {
labels = ImmutableList.of(); labels = ImmutableList.of();
@ -273,6 +276,8 @@ public final class Format {
this.tileCountVertical = format.tileCountVertical; this.tileCountVertical = format.tileCountVertical;
// Provided by the source. // Provided by the source.
this.cryptoType = format.cryptoType; 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; 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. // Build.
public Format build() { public Format build() {
@ -985,6 +1004,12 @@ public final class Format {
*/ */
@UnstableApi public final @C.CryptoType int cryptoType; @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. // Lazily initialized hashcode.
private int hashCode; private int hashCode;
@ -1060,6 +1085,8 @@ public final class Format {
} else { } else {
cryptoType = builder.cryptoType; 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. */ /** Returns a {@link Format.Builder} initialized with the values of this instance. */
@ -1234,6 +1261,8 @@ public final class Format {
result = 31 * result + tileCountVertical; result = 31 * result + tileCountVertical;
// Provided by the source. // Provided by the source.
result = 31 * result + cryptoType; result = 31 * result + cryptoType;
// Extra custom data added to the class.
result = 31 * result + (customData == null ? 0 : customData.hashCode());
hashCode = result; hashCode = result;
} }
return hashCode; return hashCode;
@ -1284,7 +1313,8 @@ public final class Format {
&& Util.areEqual(metadata, other.metadata) && Util.areEqual(metadata, other.metadata)
&& Util.areEqual(colorInfo, other.colorInfo) && Util.areEqual(colorInfo, other.colorInfo)
&& Util.areEqual(drmInitData, other.drmInitData) && 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)); Joiner.on(',').appendTo(builder, Util.getRoleFlagStrings(format.roleFlags));
builder.append("]"); builder.append("]");
} }
if (format.customData != null) {
builder.append(", customData=").append(format.customData);
}
return builder.toString(); return builder.toString();
} }

View File

@ -23,6 +23,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import android.os.Bundle; import android.os.Bundle;
import androidx.media3.common.util.Util;
import androidx.media3.test.utils.FakeMetadataEntry; import androidx.media3.test.utils.FakeMetadataEntry;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -35,6 +36,36 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public final class FormatTest { 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 @Test
public void buildUponFormat_createsEqualFormat() { public void buildUponFormat_createsEqualFormat() {
Format testFormat = createTestFormat(); Format testFormat = createTestFormat();
@ -116,6 +147,15 @@ public final class FormatTest {
.build()); .build());
} }
@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() { private static Format createTestFormat() {
byte[] initData1 = new byte[] {1, 2, 3}; byte[] initData1 = new byte[] {1, 2, 3};
byte[] initData2 = new byte[] {4, 5, 6}; byte[] initData2 = new byte[] {4, 5, 6};

View File

@ -449,6 +449,7 @@ public abstract class DecoderAudioRenderer<
.setEncoderDelay(encoderDelay) .setEncoderDelay(encoderDelay)
.setEncoderPadding(encoderPadding) .setEncoderPadding(encoderPadding)
.setMetadata(inputFormat.metadata) .setMetadata(inputFormat.metadata)
.setCustomData(inputFormat.customData)
.setId(inputFormat.id) .setId(inputFormat.id)
.setLabel(inputFormat.label) .setLabel(inputFormat.label)
.setLabels(inputFormat.labels) .setLabels(inputFormat.labels)

View File

@ -573,6 +573,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
.setEncoderDelay(format.encoderDelay) .setEncoderDelay(format.encoderDelay)
.setEncoderPadding(format.encoderPadding) .setEncoderPadding(format.encoderPadding)
.setMetadata(format.metadata) .setMetadata(format.metadata)
.setCustomData(format.customData)
.setId(format.id) .setId(format.id)
.setLabel(format.label) .setLabel(format.label)
.setLabels(format.labels) .setLabels(format.labels)