HDR: Centralize getting Colorinfo from MediaFormat

* Sets KEY_HDR_STATIC_INFO from MediaFormat in the DefaultCodec.
* Adds checks in mediaparser to ensure color space, range, and transfer are valid
  values.

PiperOrigin-RevId: 463921325
This commit is contained in:
huangdarwin 2022-07-28 21:07:43 +00:00 committed by tonihei
parent 92816023f1
commit 65a2a3a0a0
5 changed files with 86 additions and 66 deletions

View File

@ -1044,6 +1044,7 @@ public final class C {
*/ */
@UnstableApi public static final int STEREO_MODE_STEREO_MESH = 3; @UnstableApi public static final int STEREO_MODE_STEREO_MESH = 3;
// LINT.IfChange(color_space)
/** /**
* Video colorspaces. One of {@link Format#NO_VALUE}, {@link #COLOR_SPACE_BT709}, {@link * Video colorspaces. One of {@link Format#NO_VALUE}, {@link #COLOR_SPACE_BT709}, {@link
* #COLOR_SPACE_BT601} or {@link #COLOR_SPACE_BT2020}. * #COLOR_SPACE_BT601} or {@link #COLOR_SPACE_BT2020}.
@ -1067,6 +1068,7 @@ public final class C {
*/ */
@UnstableApi public static final int COLOR_SPACE_BT2020 = MediaFormat.COLOR_STANDARD_BT2020; @UnstableApi public static final int COLOR_SPACE_BT2020 = MediaFormat.COLOR_STANDARD_BT2020;
// LINT.IfChange(color_transfer)
/** /**
* Video color transfer characteristics. One of {@link Format#NO_VALUE}, {@link * Video color transfer characteristics. One of {@link Format#NO_VALUE}, {@link
* #COLOR_TRANSFER_SDR}, {@link #COLOR_TRANSFER_ST2084} or {@link #COLOR_TRANSFER_HLG}. * #COLOR_TRANSFER_SDR}, {@link #COLOR_TRANSFER_ST2084} or {@link #COLOR_TRANSFER_HLG}.
@ -1090,6 +1092,7 @@ public final class C {
*/ */
@UnstableApi public static final int COLOR_TRANSFER_HLG = MediaFormat.COLOR_TRANSFER_HLG; @UnstableApi public static final int COLOR_TRANSFER_HLG = MediaFormat.COLOR_TRANSFER_HLG;
// LINT.IfChange(color_range)
/** /**
* Video color range. One of {@link Format#NO_VALUE}, {@link #COLOR_RANGE_LIMITED} or {@link * Video color range. One of {@link Format#NO_VALUE}, {@link #COLOR_RANGE_LIMITED} or {@link
* #COLOR_RANGE_FULL}. * #COLOR_RANGE_FULL}.

View File

@ -77,7 +77,9 @@ public final class ColorInfo implements Bundleable {
/** Returns whether the {@code ColorInfo} uses an HDR {@link C.ColorTransfer}. */ /** Returns whether the {@code ColorInfo} uses an HDR {@link C.ColorTransfer}. */
public static boolean isHdr(@Nullable ColorInfo colorInfo) { public static boolean isHdr(@Nullable ColorInfo colorInfo) {
return colorInfo != null && colorInfo.colorTransfer != C.COLOR_TRANSFER_SDR; return colorInfo != null
&& colorInfo.colorTransfer != Format.NO_VALUE
&& colorInfo.colorTransfer != C.COLOR_TRANSFER_SDR;
} }
/** /**

View File

@ -15,6 +15,8 @@
*/ */
package androidx.media3.common.util; package androidx.media3.common.util;
import static androidx.media3.common.util.Util.SDK_INT;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.media.AudioFormat; import android.media.AudioFormat;
import android.media.MediaFormat; import android.media.MediaFormat;
@ -191,6 +193,51 @@ public final class MediaFormatUtil {
} }
} }
/**
* Creates and returns a {@code ColorInfo}, if a valid instance is described in the {@link
* MediaFormat}.
*/
@Nullable
public static ColorInfo getColorInfo(MediaFormat mediaFormat) {
if (SDK_INT < 29) {
return null;
}
int colorSpace =
mediaFormat.getInteger(MediaFormat.KEY_COLOR_STANDARD, /* defaultValue= */ Format.NO_VALUE);
int colorRange =
mediaFormat.getInteger(MediaFormat.KEY_COLOR_RANGE, /* defaultValue= */ Format.NO_VALUE);
int colorTransfer =
mediaFormat.getInteger(MediaFormat.KEY_COLOR_TRANSFER, /* defaultValue= */ Format.NO_VALUE);
@Nullable
ByteBuffer hdrStaticInfoByteBuffer = mediaFormat.getByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO);
@Nullable
byte[] hdrStaticInfo =
hdrStaticInfoByteBuffer != null ? getArray(hdrStaticInfoByteBuffer) : null;
if (!isValidColorSpace(colorSpace)) {
colorSpace = Format.NO_VALUE;
}
if (!isValidColorRange(colorRange)) {
colorRange = Format.NO_VALUE;
}
if (!isValidColorTransfer(colorTransfer)) {
colorTransfer = Format.NO_VALUE;
}
if (colorSpace != Format.NO_VALUE
|| colorRange != Format.NO_VALUE
|| colorTransfer != Format.NO_VALUE
|| hdrStaticInfo != null) {
return new ColorInfo(colorSpace, colorRange, colorTransfer, hdrStaticInfo);
}
return null;
}
public static byte[] getArray(ByteBuffer byteBuffer) {
byte[] array = new byte[byteBuffer.remaining()];
byteBuffer.get(array);
return array;
}
// Internal methods. // Internal methods.
private static void setBooleanAsInt(MediaFormat format, String key, int value) { private static void setBooleanAsInt(MediaFormat format, String key, int value) {
@ -253,5 +300,31 @@ public final class MediaFormatUtil {
mediaFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, mediaFormatPcmEncoding); mediaFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, mediaFormatPcmEncoding);
} }
/** Whether this is a valid {@link C.ColorSpace} instance. */
private static boolean isValidColorSpace(int colorSpace) {
// LINT.IfChange(color_space)
return colorSpace == C.COLOR_SPACE_BT709
|| colorSpace == C.COLOR_SPACE_BT601
|| colorSpace == C.COLOR_SPACE_BT2020
|| colorSpace == Format.NO_VALUE;
}
/** Whether this is a valid {@link C.ColorRange} instance. */
private static boolean isValidColorRange(int colorRange) {
// LINT.IfChange(color_range)
return colorRange == C.COLOR_RANGE_LIMITED
|| colorRange == C.COLOR_RANGE_FULL
|| colorRange == Format.NO_VALUE;
}
/** Whether this is a valid {@link C.ColorTransfer} instance. */
private static boolean isValidColorTransfer(int colorTransfer) {
// LINT.IfChange(color_transfer)
return colorTransfer == C.COLOR_TRANSFER_SDR
|| colorTransfer == C.COLOR_TRANSFER_ST2084
|| colorTransfer == C.COLOR_TRANSFER_HLG
|| colorTransfer == Format.NO_VALUE;
}
private MediaFormatUtil() {} private MediaFormatUtil() {}
} }

View File

@ -42,7 +42,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.C.SelectionFlags; import androidx.media3.common.C.SelectionFlags;
import androidx.media3.common.ColorInfo;
import androidx.media3.common.DataReader; import androidx.media3.common.DataReader;
import androidx.media3.common.DrmInitData; import androidx.media3.common.DrmInitData;
import androidx.media3.common.DrmInitData.SchemeData; import androidx.media3.common.DrmInitData.SchemeData;
@ -50,6 +49,7 @@ import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes; import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Log; import androidx.media3.common.util.Log;
import androidx.media3.common.util.MediaFormatUtil;
import androidx.media3.common.util.TimestampAdjuster; import androidx.media3.common.util.TimestampAdjuster;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
@ -474,7 +474,7 @@ public final class OutputConsumerAdapterV30 implements MediaParser.OutputConsume
.setChannelCount( .setChannelCount(
mediaFormat.getInteger( mediaFormat.getInteger(
MediaFormat.KEY_CHANNEL_COUNT, /* defaultValue= */ Format.NO_VALUE)) MediaFormat.KEY_CHANNEL_COUNT, /* defaultValue= */ Format.NO_VALUE))
.setColorInfo(getColorInfo(mediaFormat)) .setColorInfo(MediaFormatUtil.getColorInfo(mediaFormat))
.setSampleMimeType(mediaFormatMimeType) .setSampleMimeType(mediaFormatMimeType)
.setCodecs(mediaFormat.getString(MediaFormat.KEY_CODECS_STRING)) .setCodecs(mediaFormat.getString(MediaFormat.KEY_CODECS_STRING))
.setFrameRate( .setFrameRate(
@ -572,40 +572,11 @@ public final class OutputConsumerAdapterV30 implements MediaParser.OutputConsume
if (byteBuffer == null) { if (byteBuffer == null) {
break; break;
} }
initData.add(getArray(byteBuffer)); initData.add(MediaFormatUtil.getArray(byteBuffer));
} }
return initData; return initData;
} }
@Nullable
private static ColorInfo getColorInfo(MediaFormat mediaFormat) {
@Nullable
ByteBuffer hdrStaticInfoByteBuffer = mediaFormat.getByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO);
@Nullable
byte[] hdrStaticInfo =
hdrStaticInfoByteBuffer != null ? getArray(hdrStaticInfoByteBuffer) : null;
int colorTransfer =
mediaFormat.getInteger(MediaFormat.KEY_COLOR_TRANSFER, /* defaultValue= */ Format.NO_VALUE);
int colorRange =
mediaFormat.getInteger(MediaFormat.KEY_COLOR_RANGE, /* defaultValue= */ Format.NO_VALUE);
int colorStandard =
mediaFormat.getInteger(MediaFormat.KEY_COLOR_STANDARD, /* defaultValue= */ Format.NO_VALUE);
if (hdrStaticInfo != null
|| colorTransfer != Format.NO_VALUE
|| colorRange != Format.NO_VALUE
|| colorStandard != Format.NO_VALUE) {
return new ColorInfo(colorStandard, colorRange, colorTransfer, hdrStaticInfo);
}
return null;
}
private static byte[] getArray(ByteBuffer byteBuffer) {
byte[] array = new byte[byteBuffer.remaining()];
byteBuffer.get(array);
return array;
}
private static String getMimeType(String parserName) { private static String getMimeType(String parserName) {
switch (parserName) { switch (parserName) {
case PARSER_NAME_MATROSKA: case PARSER_NAME_MATROSKA:

View File

@ -32,6 +32,7 @@ import androidx.media3.common.C;
import androidx.media3.common.ColorInfo; import androidx.media3.common.ColorInfo;
import androidx.media3.common.Format; import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes; import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.MediaFormatUtil;
import androidx.media3.common.util.TraceUtil; import androidx.media3.common.util.TraceUtil;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.decoder.DecoderInputBuffer; import androidx.media3.decoder.DecoderInputBuffer;
@ -317,7 +318,7 @@ public final class DefaultCodec implements Codec {
} }
if (outputBufferIndex < 0) { if (outputBufferIndex < 0) {
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
outputFormat = getFormat(mediaCodec.getOutputFormat()); outputFormat = convertToFormat(mediaCodec.getOutputFormat());
if (!isColorTransferEqual(configuredOutputColor, outputFormat.colorInfo)) { if (!isColorTransferEqual(configuredOutputColor, outputFormat.colorInfo)) {
// TODO(b/237674316): These exceptions throw when the container ColorInfo doesn't match // TODO(b/237674316): These exceptions throw when the container ColorInfo doesn't match
// the video ColorInfo. Instead of throwing when seeing unexpected ColorInfos, consider // the video ColorInfo. Instead of throwing when seeing unexpected ColorInfos, consider
@ -412,7 +413,7 @@ public final class DefaultCodec implements Codec {
return TransformationException.createForUnexpected(cause); return TransformationException.createForUnexpected(cause);
} }
private static Format getFormat(MediaFormat mediaFormat) { private static Format convertToFormat(MediaFormat mediaFormat) {
ImmutableList.Builder<byte[]> csdBuffers = new ImmutableList.Builder<>(); ImmutableList.Builder<byte[]> csdBuffers = new ImmutableList.Builder<>();
int csdIndex = 0; int csdIndex = 0;
while (true) { while (true) {
@ -432,7 +433,7 @@ public final class DefaultCodec implements Codec {
formatBuilder formatBuilder
.setWidth(mediaFormat.getInteger(MediaFormat.KEY_WIDTH)) .setWidth(mediaFormat.getInteger(MediaFormat.KEY_WIDTH))
.setHeight(mediaFormat.getInteger(MediaFormat.KEY_HEIGHT)) .setHeight(mediaFormat.getInteger(MediaFormat.KEY_HEIGHT))
.setColorInfo(getColorInfo(mediaFormat)); .setColorInfo(MediaFormatUtil.getColorInfo(mediaFormat));
} else if (MimeTypes.isAudio(mimeType)) { } else if (MimeTypes.isAudio(mimeType)) {
// TODO(b/178685617): Only set the PCM encoding for audio/raw, once we have a way to // TODO(b/178685617): Only set the PCM encoding for audio/raw, once we have a way to
// simulate more realistic codec input/output formats in tests. // simulate more realistic codec input/output formats in tests.
@ -472,34 +473,4 @@ public final class DefaultCodec implements Codec {
return SDK_INT < 29 return SDK_INT < 29
|| context.getApplicationContext().getApplicationInfo().targetSdkVersion < 29; || context.getApplicationContext().getApplicationInfo().targetSdkVersion < 29;
} }
@Nullable
private static ColorInfo getColorInfo(MediaFormat mediaFormat) {
if (SDK_INT < 29) {
return null;
}
// TODO(b/227624622): Set hdrStaticInfo accordingly using KEY_HDR_STATIC_INFO.
int colorSpace = mediaFormat.getInteger(MediaFormat.KEY_COLOR_STANDARD, Format.NO_VALUE);
int colorRange = mediaFormat.getInteger(MediaFormat.KEY_COLOR_RANGE, Format.NO_VALUE);
int colorTransfer = mediaFormat.getInteger(MediaFormat.KEY_COLOR_TRANSFER, Format.NO_VALUE);
if (colorSpace != C.COLOR_SPACE_BT709
&& colorSpace != C.COLOR_SPACE_BT601
&& colorSpace != C.COLOR_SPACE_BT2020
&& colorSpace != Format.NO_VALUE) {
return null;
}
if (colorRange != C.COLOR_RANGE_LIMITED
&& colorRange != C.COLOR_RANGE_FULL
&& colorRange != Format.NO_VALUE) {
return null;
}
if (colorTransfer != C.COLOR_TRANSFER_SDR
&& colorTransfer != C.COLOR_TRANSFER_ST2084
&& colorTransfer != C.COLOR_TRANSFER_HLG
&& colorTransfer != Format.NO_VALUE) {
return null;
}
return new ColorInfo(colorSpace, colorRange, colorTransfer, /* hdrStaticInfo= */ null);
}
} }