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 Marc Baechinger
parent 9ed04ef842
commit 44c42fef2a
5 changed files with 86 additions and 66 deletions

View File

@ -1022,6 +1022,7 @@ public final class C {
*/ */
public static final int STEREO_MODE_STEREO_MESH = 3; 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}.
@ -1044,6 +1045,7 @@ public final class C {
*/ */
public static final int COLOR_SPACE_BT2020 = MediaFormat.COLOR_STANDARD_BT2020; 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}.
@ -1066,6 +1068,7 @@ public final class C {
*/ */
public static final int COLOR_TRANSFER_HLG = MediaFormat.COLOR_TRANSFER_HLG; 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

@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer2.util; package com.google.android.exoplayer2.util;
import static com.google.android.exoplayer2.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;
@ -190,6 +192,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) {
@ -252,5 +299,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

@ -78,7 +78,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

@ -55,10 +55,10 @@ import com.google.android.exoplayer2.extractor.TrackOutput.CryptoData;
import com.google.android.exoplayer2.upstream.DataReader; import com.google.android.exoplayer2.upstream.DataReader;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.MediaFormatUtil;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import com.google.android.exoplayer2.video.ColorInfo;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -472,7 +472,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(
@ -570,40 +570,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

@ -31,6 +31,7 @@ import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.util.MediaFormatUtil;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.TraceUtil; import com.google.android.exoplayer2.util.TraceUtil;
import com.google.android.exoplayer2.video.ColorInfo; import com.google.android.exoplayer2.video.ColorInfo;
@ -315,7 +316,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
@ -410,7 +411,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) {
@ -430,7 +431,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.
@ -470,34 +471,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);
}
} }