From 75bb45e5108fdf3449c7a7d4865365881c739087 Mon Sep 17 00:00:00 2001 From: olly Date: Thu, 20 Feb 2020 19:19:14 +0000 Subject: [PATCH] Format: Add Builder Package private for now. It will be made visible in a child CL. Issue: #2863 PiperOrigin-RevId: 296255558 --- .../com/google/android/exoplayer2/Format.java | 1063 +++++++++++------ .../google/android/exoplayer2/FormatTest.java | 127 +- 2 files changed, 739 insertions(+), 451 deletions(-) diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Format.java b/library/common/src/main/java/com/google/android/exoplayer2/Format.java index ba64e8c5ae..77b04bbcbf 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/Format.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/Format.java @@ -30,13 +30,588 @@ import java.util.Collections; import java.util.List; /** - * Representation of a media format. + * Represents a media format. + * + *

When building formats, populate all fields whose values are known and relevant to the type of + * format being constructed. For information about different types of format, see ExoPlayer's Supported formats page. + * + *

Fields commonly relevant to all formats

+ * + * + * + *

Fields relevant to container formats

+ * + * + * + *

Fields relevant to sample formats

+ * + * + * + *

Fields relevant to video formats

+ * + * + * + *

Fields relevant to audio formats

+ * + * + * + *

Fields relevant to text formats

+ * + * */ public final class Format implements Parcelable { /** - * A value for various fields to indicate that the field's value is unknown or not applicable. + * Builds {@link Format} instances. + * + *

When building formats, populate all fields whose values are known and relevant to the type + * of format being constructed. See the {@link Format} Javadoc for information about which fields + * should be set for different types of format. */ + // TODO: Make public. + /* package */ static final class Builder { + + @Nullable private String id; + @Nullable private String label; + @Nullable private String language; + @C.SelectionFlags private int selectionFlags; + @C.RoleFlags private int roleFlags; + private int averageBitrate; + private int peakBitrate; + @Nullable private String codecs; + @Nullable private Metadata metadata; + + // Container specific. + + @Nullable private String containerMimeType; + + // Sample specific. + + @Nullable private String sampleMimeType; + private int maxInputSize; + @Nullable private List initializationData; + @Nullable private DrmInitData drmInitData; + private long subsampleOffsetUs; + + // Video specific. + + private int width; + private int height; + private float frameRate; + private int rotationDegrees; + private float pixelWidthHeightRatio; + @Nullable private byte[] projectionData; + @C.StereoMode private int stereoMode; + @Nullable private ColorInfo colorInfo; + + // Audio specific. + + private int channelCount; + private int sampleRate; + @C.PcmEncoding private int pcmEncoding; + private int encoderDelay; + private int encoderPadding; + + // Text specific. + + private int accessibilityChannel; + + // Provided by source. + + @Nullable private Class exoMediaCryptoType; + + /** Creates a new instance with default values. */ + public Builder() { + averageBitrate = NO_VALUE; + peakBitrate = NO_VALUE; + // Sample specific. + maxInputSize = NO_VALUE; + subsampleOffsetUs = OFFSET_SAMPLE_RELATIVE; + // Video specific. + width = NO_VALUE; + height = NO_VALUE; + frameRate = NO_VALUE; + pixelWidthHeightRatio = 1.0f; + stereoMode = NO_VALUE; + // Audio specific. + channelCount = NO_VALUE; + sampleRate = NO_VALUE; + pcmEncoding = NO_VALUE; + // Text specific. + accessibilityChannel = NO_VALUE; + } + + /** + * Creates a new instance to build upon the provided {@link Format}. + * + * @param format The {@link Format} to build upon. + */ + private Builder(Format format) { + this.id = format.id; + this.label = format.label; + this.language = format.language; + this.selectionFlags = format.selectionFlags; + this.roleFlags = format.roleFlags; + this.averageBitrate = format.averageBitrate; + this.peakBitrate = format.peakBitrate; + this.codecs = format.codecs; + this.metadata = format.metadata; + // Container specific. + this.containerMimeType = format.containerMimeType; + // Sample specific. + this.sampleMimeType = format.sampleMimeType; + this.maxInputSize = format.maxInputSize; + this.initializationData = format.initializationData; + this.drmInitData = format.drmInitData; + this.subsampleOffsetUs = format.subsampleOffsetUs; + // Video specific. + this.width = format.width; + this.height = format.height; + this.frameRate = format.frameRate; + this.rotationDegrees = format.rotationDegrees; + this.pixelWidthHeightRatio = format.pixelWidthHeightRatio; + this.projectionData = format.projectionData; + this.stereoMode = format.stereoMode; + this.colorInfo = format.colorInfo; + // Audio specific. + this.channelCount = format.channelCount; + this.sampleRate = format.sampleRate; + this.pcmEncoding = format.pcmEncoding; + this.encoderDelay = format.encoderDelay; + this.encoderPadding = format.encoderPadding; + // Text specific. + this.accessibilityChannel = format.accessibilityChannel; + // Provided by source. + this.exoMediaCryptoType = format.exoMediaCryptoType; + } + + /** + * Sets {@link Format#id}. The default value is {@code null}. + * + * @param id The {@link Format#id}. + * @return The builder. + */ + public Builder setId(@Nullable String id) { + this.id = id; + return this; + } + + /** + * Sets {@link Format#label}. The default value is {@code null}. + * + * @param label The {@link Format#label}. + * @return The builder. + */ + public Builder setLabel(@Nullable String label) { + this.label = label; + return this; + } + + /** + * Sets {@link Format#language}. The default value is {@code null}. + * + * @param language The {@link Format#language}. + * @return The builder. + */ + public Builder setLanguage(@Nullable String language) { + this.language = language; + return this; + } + + /** + * Sets {@link Format#selectionFlags}. The default value is 0. + * + * @param selectionFlags The {@link Format#selectionFlags}. + * @return The builder. + */ + public Builder setSelectionFlags(@C.SelectionFlags int selectionFlags) { + this.selectionFlags = selectionFlags; + return this; + } + + /** + * Sets {@link Format#roleFlags}. The default value is 0. + * + * @param roleFlags The {@link Format#roleFlags}. + * @return The builder. + */ + public Builder setRoleFlags(@C.RoleFlags int roleFlags) { + this.roleFlags = roleFlags; + return this; + } + + /** + * Sets {@link Format#averageBitrate}. The default value is {@link #NO_VALUE}. + * + * @param averageBitrate The {@link Format#averageBitrate}. + * @return The builder. + */ + public Builder setAverageBitrate(int averageBitrate) { + this.averageBitrate = averageBitrate; + return this; + } + + /** + * Sets {@link Format#peakBitrate}. The default value is {@link #NO_VALUE}. + * + * @param peakBitrate The {@link Format#peakBitrate}. + * @return The builder. + */ + public Builder setPeakBitrate(int peakBitrate) { + this.peakBitrate = peakBitrate; + return this; + } + + /** + * Sets {@link Format#codecs}. The default value is {@code null}. + * + * @param codecs The {@link Format#codecs}. + * @return The builder. + */ + public Builder setCodecs(@Nullable String codecs) { + this.codecs = codecs; + return this; + } + + /** + * Sets {@link Format#metadata}. The default value is {@code null}. + * + * @param metadata The {@link Format#metadata}. + * @return The builder. + */ + public Builder setMetadata(@Nullable Metadata metadata) { + this.metadata = metadata; + return this; + } + + // Container specific. + + /** + * Sets {@link Format#containerMimeType}. The default value is {@code null}. + * + * @param containerMimeType The {@link Format#containerMimeType}. + * @return The builder. + */ + public Builder setContainerMimeType(@Nullable String containerMimeType) { + this.containerMimeType = containerMimeType; + return this; + } + + // Sample specific. + + /** + * Sets {@link Format#sampleMimeType}. The default value is {@code null}. + * + * @param sampleMimeType {@link Format#sampleMimeType}. + * @return The builder. + */ + public Builder setSampleMimeType(@Nullable String sampleMimeType) { + this.sampleMimeType = sampleMimeType; + return this; + } + + /** + * Sets {@link Format#maxInputSize}. The default value is {@link #NO_VALUE}. + * + * @param maxInputSize The {@link Format#maxInputSize}. + * @return The builder. + */ + public Builder setMaxInputSize(int maxInputSize) { + this.maxInputSize = maxInputSize; + return this; + } + + /** + * Sets {@link Format#initializationData}. The default value is {@code null}. + * + * @param initializationData The {@link Format#initializationData}. + * @return The builder. + */ + public Builder setInitializationData(List initializationData) { + this.initializationData = initializationData; + return this; + } + + /** + * Sets {@link Format#drmInitData}. The default value is {@code null}. + * + * @param drmInitData The {@link Format#drmInitData}. + * @return The builder. + */ + public Builder setDrmInitData(@Nullable DrmInitData drmInitData) { + this.drmInitData = drmInitData; + return this; + } + + /** + * Sets {@link Format#subsampleOffsetUs}. The default value is {@link #OFFSET_SAMPLE_RELATIVE}. + * + * @param subsampleOffsetUs The {@link Format#subsampleOffsetUs}. + * @return The builder. + */ + public Builder setSubsampleOffsetUs(long subsampleOffsetUs) { + this.subsampleOffsetUs = subsampleOffsetUs; + return this; + } + + // Video specific. + + /** + * Sets {@link Format#width}. The default value is {@link #NO_VALUE}. + * + * @param width The {@link Format#width}. + * @return The builder. + */ + public Builder setWidth(int width) { + this.width = width; + return this; + } + + /** + * Sets {@link Format#height}. The default value is {@link #NO_VALUE}. + * + * @param height The {@link Format#height}. + * @return The builder. + */ + public Builder setHeight(int height) { + this.height = height; + return this; + } + + /** + * Sets {@link Format#frameRate}. The default value is {@link #NO_VALUE}. + * + * @param frameRate The {@link Format#frameRate}. + * @return The builder. + */ + public Builder setFrameRate(float frameRate) { + this.frameRate = frameRate; + return this; + } + + /** + * Sets {@link Format#rotationDegrees}. The default value is 0. + * + * @param rotationDegrees The {@link Format#rotationDegrees}. + * @return The builder. + */ + public Builder setRotationDegrees(int rotationDegrees) { + this.rotationDegrees = rotationDegrees; + return this; + } + + /** + * Sets {@link Format#pixelWidthHeightRatio}. The default value is 1.0f. + * + * @param pixelWidthHeightRatio The {@link Format#pixelWidthHeightRatio}. + * @return The builder. + */ + public Builder setPixelWidthHeightRatio(float pixelWidthHeightRatio) { + this.pixelWidthHeightRatio = pixelWidthHeightRatio; + return this; + } + + /** + * Sets {@link Format#projectionData}. The default value is {@code null}. + * + * @param projectionData The {@link Format#projectionData}. + * @return The builder. + */ + public Builder setProjectionData(@Nullable byte[] projectionData) { + this.projectionData = projectionData; + return this; + } + + /** + * Sets {@link Format#stereoMode}. The default value is {@link #NO_VALUE}. + * + * @param stereoMode The {@link Format#stereoMode}. + * @return The builder. + */ + public Builder setStereoMode(@C.StereoMode int stereoMode) { + this.stereoMode = stereoMode; + return this; + } + + /** + * Sets {@link Format#colorInfo}. The default value is {@code null}. + * + * @param colorInfo The {@link Format#colorInfo}. + * @return The builder. + */ + public Builder setColorInfo(@Nullable ColorInfo colorInfo) { + this.colorInfo = colorInfo; + return this; + } + + // Audio specific. + + /** + * Sets {@link Format#channelCount}. The default value is {@link #NO_VALUE}. + * + * @param channelCount The {@link Format#channelCount}. + * @return The builder. + */ + public Builder setChannelCount(int channelCount) { + this.channelCount = channelCount; + return this; + } + + /** + * Sets {@link Format#sampleRate}. The default value is {@link #NO_VALUE}. + * + * @param sampleRate The {@link Format#sampleRate}. + * @return The builder. + */ + public Builder setSampleRate(int sampleRate) { + this.sampleRate = sampleRate; + return this; + } + + /** + * Sets {@link Format#pcmEncoding}. The default value is {@link #NO_VALUE}. + * + * @param pcmEncoding The {@link Format#pcmEncoding}. + * @return The builder. + */ + public Builder setPcmEncoding(@C.PcmEncoding int pcmEncoding) { + this.pcmEncoding = pcmEncoding; + return this; + } + + /** + * Sets {@link Format#encoderDelay}. The default value is 0. + * + * @param encoderDelay The {@link Format#encoderDelay}. + * @return The builder. + */ + public Builder setEncoderDelay(int encoderDelay) { + this.encoderDelay = encoderDelay; + return this; + } + + /** + * Sets {@link Format#encoderPadding}. The default value is 0. + * + * @param encoderPadding The {@link Format#encoderPadding}. + * @return The builder. + */ + public Builder setEncoderPadding(int encoderPadding) { + this.encoderPadding = encoderPadding; + return this; + } + + // Text specific. + + /** + * Sets {@link Format#accessibilityChannel}. The default value is {@link #NO_VALUE}. + * + * @param accessibilityChannel The {@link Format#accessibilityChannel}. + * @return The builder. + */ + public Builder setAccessibilityChannel(int accessibilityChannel) { + this.accessibilityChannel = accessibilityChannel; + return this; + } + + // Provided by source. + + /** + * Sets {@link Format#exoMediaCryptoType}. The default value is {@code null}. + * + * @param exoMediaCryptoType The {@link Format#exoMediaCryptoType}. + * @return The builder. + */ + public Builder setExoMediaCryptoType( + @Nullable Class exoMediaCryptoType) { + this.exoMediaCryptoType = exoMediaCryptoType; + return this; + } + + // Build. + + public Format build() { + return new Format( + id, + label, + language, + selectionFlags, + roleFlags, + averageBitrate, + peakBitrate, + codecs, + metadata, + containerMimeType, + sampleMimeType, + maxInputSize, + initializationData, + drmInitData, + subsampleOffsetUs, + width, + height, + frameRate, + rotationDegrees, + pixelWidthHeightRatio, + projectionData, + stereoMode, + colorInfo, + channelCount, + sampleRate, + pcmEncoding, + encoderDelay, + encoderPadding, + accessibilityChannel, + exoMediaCryptoType); + } + } + + /** A value for various fields to indicate that the field's value is unknown or not applicable. */ public static final int NO_VALUE = -1; /** @@ -105,12 +680,9 @@ public final class Format implements Parcelable { /** The mime type of the container, or null if unknown or not applicable. */ @Nullable public final String containerMimeType; - // Elementary stream specific. + // Sample specific. - /** - * The mime type of the elementary stream (i.e. the individual samples), or null if unknown or not - * applicable. - */ + /** The sample mime type, or null if unknown or not applicable. */ @Nullable public final String sampleMimeType; /** * The maximum size of a buffer of data (typically one sample), or {@link #NO_VALUE} if unknown or @@ -153,16 +725,15 @@ public final class Format implements Parcelable { public final int rotationDegrees; /** The width to height ratio of pixels in the video, or 1.0 if unknown or not applicable. */ public final float pixelWidthHeightRatio; + /** The projection data for 360/VR video, or null if not applicable. */ + @Nullable public final byte[] projectionData; /** * The stereo layout for 360/3D/VR video, or {@link #NO_VALUE} if not applicable. Valid stereo * modes are {@link C#STEREO_MODE_MONO}, {@link C#STEREO_MODE_TOP_BOTTOM}, {@link * C#STEREO_MODE_LEFT_RIGHT}, {@link C#STEREO_MODE_STEREO_MESH}. */ - @C.StereoMode - public final int stereoMode; - /** The projection data for 360/VR video, or null if not applicable. */ - @Nullable public final byte[] projectionData; - /** The color metadata associated with the video, helps with accurate color reproduction. */ + @C.StereoMode public final int stereoMode; + /** The color metadata associated with the video, or null if not applicable. */ @Nullable public final ColorInfo colorInfo; // Audio specific. @@ -176,7 +747,7 @@ public final class Format implements Parcelable { */ public final int sampleRate; /** The {@link C.PcmEncoding} for PCM audio. Set to {@link #NO_VALUE} for other media types. */ - public final @C.PcmEncoding int pcmEncoding; + @C.PcmEncoding public final int pcmEncoding; /** * The number of frames to trim from the start of the decoded audio stream, or 0 if not * applicable. @@ -772,7 +1343,7 @@ public final class Format implements Parcelable { @Nullable Metadata metadata, // Container specific. @Nullable String containerMimeType, - // Elementary stream specific. + // Sample specific. @Nullable String sampleMimeType, int maxInputSize, @Nullable List initializationData, @@ -809,7 +1380,7 @@ public final class Format implements Parcelable { this.metadata = metadata; // Container specific. this.containerMimeType = containerMimeType; - // Elementary stream specific. + // Sample specific. this.sampleMimeType = sampleMimeType; this.maxInputSize = maxInputSize; this.initializationData = @@ -851,7 +1422,7 @@ public final class Format implements Parcelable { metadata = in.readParcelable(Metadata.class.getClassLoader()); // Container specific. containerMimeType = in.readString(); - // Elementary stream specific. + // Sample specific. sampleMimeType = in.readString(); maxInputSize = in.readInt(); int initializationDataSize = in.readInt(); @@ -883,108 +1454,34 @@ public final class Format implements Parcelable { exoMediaCryptoType = null; } + // TODO: Make public. + /** Returns a {@link Format.Builder} initialized with the values of this instance. */ + /* package */ Builder buildUpon() { + return new Builder(this); + } + + // TODO: Deprecate. + // /** @deprecated Use {@link #buildUpon()} and {@link Builder#setMaxInputSize(int)}. */ + // @Deprecated public Format copyWithMaxInputSize(int maxInputSize) { - return new Format( - id, - label, - language, - selectionFlags, - roleFlags, - averageBitrate, - peakBitrate, - codecs, - metadata, - containerMimeType, - sampleMimeType, - maxInputSize, - initializationData, - drmInitData, - subsampleOffsetUs, - width, - height, - frameRate, - rotationDegrees, - pixelWidthHeightRatio, - projectionData, - stereoMode, - colorInfo, - channelCount, - sampleRate, - pcmEncoding, - encoderDelay, - encoderPadding, - accessibilityChannel, - exoMediaCryptoType); + return buildUpon().setMaxInputSize(maxInputSize).build(); } + // TODO: Deprecate. + // /** @deprecated Use {@link #buildUpon()} and {@link Builder#setSubsampleOffsetUs(long)}. */ + // @Deprecated public Format copyWithSubsampleOffsetUs(long subsampleOffsetUs) { - return new Format( - id, - label, - language, - selectionFlags, - roleFlags, - averageBitrate, - peakBitrate, - codecs, - metadata, - containerMimeType, - sampleMimeType, - maxInputSize, - initializationData, - drmInitData, - subsampleOffsetUs, - width, - height, - frameRate, - rotationDegrees, - pixelWidthHeightRatio, - projectionData, - stereoMode, - colorInfo, - channelCount, - sampleRate, - pcmEncoding, - encoderDelay, - encoderPadding, - accessibilityChannel, - exoMediaCryptoType); + return buildUpon().setSubsampleOffsetUs(subsampleOffsetUs).build(); } + // TODO: Deprecate. + // /** @deprecated Use {@link #buildUpon()} and {@link Builder#setLabel(String)} . */ + // @Deprecated public Format copyWithLabel(@Nullable String label) { - return new Format( - id, - label, - language, - selectionFlags, - roleFlags, - averageBitrate, - peakBitrate, - codecs, - metadata, - containerMimeType, - sampleMimeType, - maxInputSize, - initializationData, - drmInitData, - subsampleOffsetUs, - width, - height, - frameRate, - rotationDegrees, - pixelWidthHeightRatio, - projectionData, - stereoMode, - colorInfo, - channelCount, - sampleRate, - pcmEncoding, - encoderDelay, - encoderPadding, - accessibilityChannel, - exoMediaCryptoType); + return buildUpon().setLabel(label).build(); } + // TODO: Inline into HlsSampleStreamWrapper and remove. public Format copyWithContainerInfo( @Nullable String id, @Nullable String label, @@ -997,42 +1494,24 @@ public final class Format implements Parcelable { int channelCount, @C.SelectionFlags int selectionFlags, @Nullable String language) { - if (this.metadata != null) { metadata = this.metadata.copyWithAppendedEntriesFrom(metadata); } - return new Format( - id, - label, - language, - selectionFlags, - roleFlags, - /* averageBitrate= */ bitrate, - /* peakBitrate= */ bitrate, - codecs, - metadata, - containerMimeType, - sampleMimeType, - maxInputSize, - initializationData, - drmInitData, - subsampleOffsetUs, - width, - height, - frameRate, - rotationDegrees, - pixelWidthHeightRatio, - projectionData, - stereoMode, - colorInfo, - channelCount, - sampleRate, - pcmEncoding, - encoderDelay, - encoderPadding, - accessibilityChannel, - exoMediaCryptoType); + return buildUpon() + .setId(id) + .setLabel(label) + .setLanguage(language) + .setSelectionFlags(selectionFlags) + .setAverageBitrate(bitrate) + .setPeakBitrate(bitrate) + .setMetadata(metadata) + .setCodecs(codecs) + .setSampleMimeType(sampleMimeType) + .setWidth(width) + .setHeight(height) + .setChannelCount(channelCount) + .build(); } @SuppressWarnings("ReferenceEquality") @@ -1087,105 +1566,36 @@ public final class Format implements Parcelable { DrmInitData drmInitData = DrmInitData.createSessionCreationData(manifestFormat.drmInitData, this.drmInitData); - return new Format( - id, - label, - language, - selectionFlags, - roleFlags, - averageBitrate, - peakBitrate, - codecs, - metadata, - containerMimeType, - sampleMimeType, - maxInputSize, - initializationData, - drmInitData, - subsampleOffsetUs, - width, - height, - frameRate, - rotationDegrees, - pixelWidthHeightRatio, - projectionData, - stereoMode, - colorInfo, - channelCount, - sampleRate, - pcmEncoding, - encoderDelay, - encoderPadding, - accessibilityChannel, - exoMediaCryptoType); + return buildUpon() + .setId(id) + .setLabel(label) + .setLanguage(language) + .setSelectionFlags(selectionFlags) + .setRoleFlags(roleFlags) + .setAverageBitrate(averageBitrate) + .setPeakBitrate(peakBitrate) + .setCodecs(codecs) + .setMetadata(metadata) + .setDrmInitData(drmInitData) + .setFrameRate(frameRate) + .build(); } + // TODO: Deprecate. + // /** + // * @deprecated Use {@link #buildUpon()}, {@link Builder#setEncoderDelay(int)} and {@link + // * Builder#setEncoderPadding(int)}. + // */ + // @Deprecated public Format copyWithGaplessInfo(int encoderDelay, int encoderPadding) { - return new Format( - id, - label, - language, - selectionFlags, - roleFlags, - averageBitrate, - peakBitrate, - codecs, - metadata, - containerMimeType, - sampleMimeType, - maxInputSize, - initializationData, - drmInitData, - subsampleOffsetUs, - width, - height, - frameRate, - rotationDegrees, - pixelWidthHeightRatio, - projectionData, - stereoMode, - colorInfo, - channelCount, - sampleRate, - pcmEncoding, - encoderDelay, - encoderPadding, - accessibilityChannel, - exoMediaCryptoType); + return buildUpon().setEncoderDelay(encoderDelay).setEncoderPadding(encoderPadding).build(); } + // TODO: Deprecate. + // /** @deprecated Use {@link #buildUpon()} and {@link Builder#setFrameRate(float)}. */ + // @Deprecated public Format copyWithFrameRate(float frameRate) { - return new Format( - id, - label, - language, - selectionFlags, - roleFlags, - averageBitrate, - peakBitrate, - codecs, - metadata, - containerMimeType, - sampleMimeType, - maxInputSize, - initializationData, - drmInitData, - subsampleOffsetUs, - width, - height, - frameRate, - rotationDegrees, - pixelWidthHeightRatio, - projectionData, - stereoMode, - colorInfo, - channelCount, - sampleRate, - pcmEncoding, - encoderDelay, - encoderPadding, - accessibilityChannel, - exoMediaCryptoType); + return buildUpon().setFrameRate(frameRate).build(); } public Format copyWithDrmInitData(@Nullable DrmInitData drmInitData) { @@ -1196,180 +1606,39 @@ public final class Format implements Parcelable { return copyWithAdjustments(drmInitData, metadata); } + // TODO: Inline into HlsSampleStreamWrapper and remove. @SuppressWarnings("ReferenceEquality") public Format copyWithAdjustments( @Nullable DrmInitData drmInitData, @Nullable Metadata metadata) { if (drmInitData == this.drmInitData && metadata == this.metadata) { return this; } - return new Format( - id, - label, - language, - selectionFlags, - roleFlags, - averageBitrate, - peakBitrate, - codecs, - metadata, - containerMimeType, - sampleMimeType, - maxInputSize, - initializationData, - drmInitData, - subsampleOffsetUs, - width, - height, - frameRate, - rotationDegrees, - pixelWidthHeightRatio, - projectionData, - stereoMode, - colorInfo, - channelCount, - sampleRate, - pcmEncoding, - encoderDelay, - encoderPadding, - accessibilityChannel, - exoMediaCryptoType); - } - - public Format copyWithRotationDegrees(int rotationDegrees) { - return new Format( - id, - label, - language, - selectionFlags, - roleFlags, - averageBitrate, - peakBitrate, - codecs, - metadata, - containerMimeType, - sampleMimeType, - maxInputSize, - initializationData, - drmInitData, - subsampleOffsetUs, - width, - height, - frameRate, - rotationDegrees, - pixelWidthHeightRatio, - projectionData, - stereoMode, - colorInfo, - channelCount, - sampleRate, - pcmEncoding, - encoderDelay, - encoderPadding, - accessibilityChannel, - exoMediaCryptoType); + return buildUpon().setDrmInitData(drmInitData).setMetadata(metadata).build(); } + // TODO: Deprecate. + // /** + // * @deprecated Use {@link #buildUpon()} and {@link Builder#setAverageBitrate(int)} and {@link + // * Builder#setPeakBitrate(int)}. + // */ + // @Deprecated public Format copyWithBitrate(int bitrate) { - return new Format( - id, - label, - language, - selectionFlags, - roleFlags, - averageBitrate, - peakBitrate, - codecs, - metadata, - containerMimeType, - sampleMimeType, - maxInputSize, - initializationData, - drmInitData, - subsampleOffsetUs, - width, - height, - frameRate, - rotationDegrees, - pixelWidthHeightRatio, - projectionData, - stereoMode, - colorInfo, - channelCount, - sampleRate, - pcmEncoding, - encoderDelay, - encoderPadding, - accessibilityChannel, - exoMediaCryptoType); + return buildUpon().setAverageBitrate(bitrate).setPeakBitrate(bitrate).build(); } + // TODO: Deprecate. + // /** + // * @deprecated Use {@link #buildUpon()}, {@link Builder#setWidth(int)} and {@link + // * Builder#setHeight(int)}. + // */ + // @Deprecated public Format copyWithVideoSize(int width, int height) { - return new Format( - id, - label, - language, - selectionFlags, - roleFlags, - averageBitrate, - peakBitrate, - codecs, - metadata, - containerMimeType, - sampleMimeType, - maxInputSize, - initializationData, - drmInitData, - subsampleOffsetUs, - width, - height, - frameRate, - rotationDegrees, - pixelWidthHeightRatio, - projectionData, - stereoMode, - colorInfo, - channelCount, - sampleRate, - pcmEncoding, - encoderDelay, - encoderPadding, - accessibilityChannel, - exoMediaCryptoType); + return buildUpon().setWidth(width).setHeight(height).build(); } public Format copyWithExoMediaCryptoType( @Nullable Class exoMediaCryptoType) { - return new Format( - id, - label, - language, - selectionFlags, - roleFlags, - averageBitrate, - peakBitrate, - codecs, - metadata, - containerMimeType, - sampleMimeType, - maxInputSize, - initializationData, - drmInitData, - subsampleOffsetUs, - width, - height, - frameRate, - rotationDegrees, - pixelWidthHeightRatio, - projectionData, - stereoMode, - colorInfo, - channelCount, - sampleRate, - pcmEncoding, - encoderDelay, - encoderPadding, - accessibilityChannel, - exoMediaCryptoType); + return buildUpon().setExoMediaCryptoType(exoMediaCryptoType).build(); } /** @@ -1420,12 +1689,13 @@ public final class Format implements Parcelable { result = 31 * result + (language == null ? 0 : language.hashCode()); result = 31 * result + selectionFlags; result = 31 * result + roleFlags; - result = 31 * result + bitrate; + result = 31 * result + averageBitrate; + result = 31 * result + peakBitrate; result = 31 * result + (codecs == null ? 0 : codecs.hashCode()); result = 31 * result + (metadata == null ? 0 : metadata.hashCode()); // Container specific. result = 31 * result + (containerMimeType == null ? 0 : containerMimeType.hashCode()); - // Elementary stream specific. + // Sample specific. result = 31 * result + (sampleMimeType == null ? 0 : sampleMimeType.hashCode()); result = 31 * result + maxInputSize; // [Omitted] initializationData. @@ -1470,7 +1740,8 @@ public final class Format implements Parcelable { // Field equality checks ordered by type, with the cheapest checks first. return selectionFlags == other.selectionFlags && roleFlags == other.roleFlags - && bitrate == other.bitrate + && averageBitrate == other.averageBitrate + && peakBitrate == other.peakBitrate && maxInputSize == other.maxInputSize && subsampleOffsetUs == other.subsampleOffsetUs && width == other.width @@ -1575,7 +1846,7 @@ public final class Format implements Parcelable { dest.writeParcelable(metadata, 0); // Container specific. dest.writeString(containerMimeType); - // Elementary stream specific. + // Sample specific. dest.writeString(sampleMimeType); dest.writeInt(maxInputSize); int initializationDataSize = initializationData.size(); diff --git a/library/common/src/test/java/com/google/android/exoplayer2/FormatTest.java b/library/common/src/test/java/com/google/android/exoplayer2/FormatTest.java index b041af2be9..135aace2a3 100644 --- a/library/common/src/test/java/com/google/android/exoplayer2/FormatTest.java +++ b/library/common/src/test/java/com/google/android/exoplayer2/FormatTest.java @@ -23,13 +23,13 @@ import static com.google.common.truth.Truth.assertThat; import android.os.Parcel; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.drm.DrmInitData; +import com.google.android.exoplayer2.drm.ExoMediaCrypto; import com.google.android.exoplayer2.metadata.Metadata; import com.google.android.exoplayer2.metadata.id3.TextInformationFrame; import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.video.ColorInfo; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -38,71 +38,88 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public final class FormatTest { - private static final List initData; - static { - byte[] initData1 = new byte[] {1, 2, 3}; - byte[] initData2 = new byte[] {4, 5, 6}; - List initDataList = new ArrayList<>(); - initDataList.add(initData1); - initDataList.add(initData2); - initData = Collections.unmodifiableList(initDataList); + @Test + public void buildUponFormat_createsEqualFormat() { + Format testFormat = createTestFormat(); + assertThat(testFormat.buildUpon().build()).isEqualTo(testFormat); } @Test - public void testParcelable() { - DrmInitData.SchemeData drmData1 = new DrmInitData.SchemeData(WIDEVINE_UUID, VIDEO_MP4, - TestUtil.buildTestData(128, 1 /* data seed */)); - DrmInitData.SchemeData drmData2 = new DrmInitData.SchemeData(C.UUID_NIL, VIDEO_WEBM, - TestUtil.buildTestData(128, 1 /* data seed */)); - DrmInitData drmInitData = new DrmInitData(drmData1, drmData2); - byte[] projectionData = new byte[] {1, 2, 3}; - Metadata metadata = new Metadata( - new TextInformationFrame("id1", "description1", "value1"), - new TextInformationFrame("id2", "description2", "value2")); - ColorInfo colorInfo = new ColorInfo(C.COLOR_SPACE_BT709, - C.COLOR_RANGE_LIMITED, C.COLOR_TRANSFER_SDR, new byte[] {1, 2, 3, 4, 5, 6, 7}); - - Format formatToParcel = - new Format( - "id", - "label", - "language", - C.SELECTION_FLAG_DEFAULT, - C.ROLE_FLAG_MAIN, - /* averageBitrate= */ 1024, - /* peakBitrate= */ 2048, - "codec", - metadata, - /* containerMimeType= */ MimeTypes.VIDEO_MP4, - /* sampleMimeType= */ MimeTypes.VIDEO_H264, - /* maxInputSize= */ 2048, - initData, - drmInitData, - Format.OFFSET_SAMPLE_RELATIVE, - /* width= */ 1920, - /* height= */ 1080, - /* frameRate= */ 24, - /* rotationDegrees= */ 90, - /* pixelWidthHeightRatio= */ 2, - projectionData, - C.STEREO_MODE_TOP_BOTTOM, - colorInfo, - /* channelCount= */ 6, - /* sampleRate= */ 44100, - C.ENCODING_PCM_24BIT, - /* encoderDelay= */ 1001, - /* encoderPadding= */ 1002, - /* accessibilityChannel= */ Format.NO_VALUE, - /* exoMediaCryptoType= */ null); + public void parcelFormat_createsEqualFormat_exceptExoMediaCryptoType() { + Format formatToParcel = createTestFormat(); Parcel parcel = Parcel.obtain(); formatToParcel.writeToParcel(parcel, 0); parcel.setDataPosition(0); Format formatFromParcel = Format.CREATOR.createFromParcel(parcel); - assertThat(formatFromParcel).isEqualTo(formatToParcel); + Format expectedFormat = formatToParcel.buildUpon().setExoMediaCryptoType(null).build(); + + assertThat(formatFromParcel.exoMediaCryptoType).isNull(); + assertThat(formatFromParcel).isEqualTo(expectedFormat); parcel.recycle(); } + private static Format createTestFormat() { + byte[] initData1 = new byte[] {1, 2, 3}; + byte[] initData2 = new byte[] {4, 5, 6}; + List initializationData = new ArrayList<>(); + initializationData.add(initData1); + initializationData.add(initData2); + + DrmInitData.SchemeData drmData1 = + new DrmInitData.SchemeData( + WIDEVINE_UUID, VIDEO_MP4, TestUtil.buildTestData(128, 1 /* data seed */)); + DrmInitData.SchemeData drmData2 = + new DrmInitData.SchemeData( + C.UUID_NIL, VIDEO_WEBM, TestUtil.buildTestData(128, 1 /* data seed */)); + DrmInitData drmInitData = new DrmInitData(drmData1, drmData2); + + byte[] projectionData = new byte[] {1, 2, 3}; + + Metadata metadata = + new Metadata( + new TextInformationFrame("id1", "description1", "value1"), + new TextInformationFrame("id2", "description2", "value2")); + + ColorInfo colorInfo = + new ColorInfo( + C.COLOR_SPACE_BT709, + C.COLOR_RANGE_LIMITED, + C.COLOR_TRANSFER_SDR, + new byte[] {1, 2, 3, 4, 5, 6, 7}); + + return new Format( + "id", + "label", + "language", + C.SELECTION_FLAG_DEFAULT, + C.ROLE_FLAG_MAIN, + /* averageBitrate= */ 1024, + /* peakBitrate= */ 2048, + "codec", + metadata, + /* containerMimeType= */ MimeTypes.VIDEO_MP4, + /* sampleMimeType= */ MimeTypes.VIDEO_H264, + /* maxInputSize= */ 5000, + initializationData, + drmInitData, + Format.OFFSET_SAMPLE_RELATIVE, + /* width= */ 1920, + /* height= */ 1080, + /* frameRate= */ 24, + /* rotationDegrees= */ 90, + /* pixelWidthHeightRatio= */ 4, + projectionData, + C.STEREO_MODE_TOP_BOTTOM, + colorInfo, + /* channelCount= */ 6, + /* sampleRate= */ 44100, + C.ENCODING_PCM_24BIT, + /* encoderDelay= */ 1001, + /* encoderPadding= */ 1002, + /* accessibilityChannel= */ 2, + /* exoMediaCryptoType= */ ExoMediaCrypto.class); + } }