Add support to fetch ColorInfo from hvcc box in AtomParsers

#minor-release

PiperOrigin-RevId: 517086016
This commit is contained in:
rohks 2023-03-16 11:37:04 +00:00 committed by Rohit Singh
parent eb6fc93b15
commit e4446c37fb
4 changed files with 81 additions and 7 deletions

View File

@ -1208,6 +1208,15 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
pixelWidthHeightRatio = hevcConfig.pixelWidthHeightRatio; pixelWidthHeightRatio = hevcConfig.pixelWidthHeightRatio;
} }
codecs = hevcConfig.codecs; codecs = hevcConfig.codecs;
// Modify these values only if they have not already been set. If 'Atom.TYPE_colr' atom is
// present, these values may be overridden.
if (colorSpace == Format.NO_VALUE
&& colorRange == Format.NO_VALUE
&& colorTransfer == Format.NO_VALUE) {
colorSpace = hevcConfig.colorSpace;
colorRange = hevcConfig.colorRange;
colorTransfer = hevcConfig.colorTransfer;
}
} else if (childAtomType == Atom.TYPE_dvcC || childAtomType == Atom.TYPE_dvvC) { } else if (childAtomType == Atom.TYPE_dvcC || childAtomType == Atom.TYPE_dvvC) {
@Nullable DolbyVisionConfig dolbyVisionConfig = DolbyVisionConfig.parse(parent); @Nullable DolbyVisionConfig dolbyVisionConfig = DolbyVisionConfig.parse(parent);
if (dolbyVisionConfig != null) { if (dolbyVisionConfig != null) {

View File

@ -19,6 +19,8 @@ import static java.lang.Math.min;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.video.ColorInfo;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
@ -105,6 +107,9 @@ public final class NalUnitUtil {
public final int width; public final int width;
public final int height; public final int height;
public final float pixelWidthHeightRatio; public final float pixelWidthHeightRatio;
public final @C.ColorSpace int colorSpace;
public final @C.ColorRange int colorRange;
public final @C.ColorTransfer int colorTransfer;
public H265SpsData( public H265SpsData(
int generalProfileSpace, int generalProfileSpace,
@ -116,7 +121,10 @@ public final class NalUnitUtil {
int seqParameterSetId, int seqParameterSetId,
int width, int width,
int height, int height,
float pixelWidthHeightRatio) { float pixelWidthHeightRatio,
@C.ColorSpace int colorSpace,
@C.ColorRange int colorRange,
@C.ColorTransfer int colorTransfer) {
this.generalProfileSpace = generalProfileSpace; this.generalProfileSpace = generalProfileSpace;
this.generalTierFlag = generalTierFlag; this.generalTierFlag = generalTierFlag;
this.generalProfileIdc = generalProfileIdc; this.generalProfileIdc = generalProfileIdc;
@ -127,6 +135,9 @@ public final class NalUnitUtil {
this.width = width; this.width = width;
this.height = height; this.height = height;
this.pixelWidthHeightRatio = pixelWidthHeightRatio; this.pixelWidthHeightRatio = pixelWidthHeightRatio;
this.colorSpace = colorSpace;
this.colorRange = colorRange;
this.colorTransfer = colorTransfer;
} }
} }
@ -483,6 +494,10 @@ public final class NalUnitUtil {
public static H265SpsData parseH265SpsNalUnitPayload( public static H265SpsData parseH265SpsNalUnitPayload(
byte[] nalData, int nalOffset, int nalLimit) { byte[] nalData, int nalOffset, int nalLimit) {
ParsableNalUnitBitArray data = new ParsableNalUnitBitArray(nalData, nalOffset, nalLimit); ParsableNalUnitBitArray data = new ParsableNalUnitBitArray(nalData, nalOffset, nalLimit);
// HDR related metadata.
@C.ColorSpace int colorSpace = Format.NO_VALUE;
@C.ColorRange int colorRange = Format.NO_VALUE;
@C.ColorTransfer int colorTransfer = Format.NO_VALUE;
data.skipBits(4); // sps_video_parameter_set_id data.skipBits(4); // sps_video_parameter_set_id
int maxSubLayersMinus1 = data.readBits(3); int maxSubLayersMinus1 = data.readBits(3);
data.skipBit(); // sps_temporal_id_nesting_flag data.skipBit(); // sps_temporal_id_nesting_flag
@ -589,10 +604,17 @@ public final class NalUnitUtil {
data.skipBit(); // overscan_appropriate_flag data.skipBit(); // overscan_appropriate_flag
} }
if (data.readBit()) { // video_signal_type_present_flag if (data.readBit()) { // video_signal_type_present_flag
data.skipBits(4); // video_format, video_full_range_flag data.skipBits(3); // video_format
boolean fullRangeFlag = data.readBit(); // video_full_range_flag
if (data.readBit()) { // colour_description_present_flag if (data.readBit()) { // colour_description_present_flag
// colour_primaries, transfer_characteristics, matrix_coeffs int colorPrimaries = data.readBits(8); // colour_primaries
data.skipBits(24); int transferCharacteristics = data.readBits(8); // transfer_characteristics
data.skipBits(8); // matrix_coeffs
colorSpace = ColorInfo.isoColorPrimariesToColorSpace(colorPrimaries);
colorRange = fullRangeFlag ? C.COLOR_RANGE_FULL : C.COLOR_RANGE_LIMITED;
colorTransfer =
ColorInfo.isoTransferCharacteristicsToColorTransfer(transferCharacteristics);
} }
} }
if (data.readBit()) { // chroma_loc_info_present_flag if (data.readBit()) { // chroma_loc_info_present_flag
@ -617,7 +639,10 @@ public final class NalUnitUtil {
seqParameterSetId, seqParameterSetId,
frameWidth, frameWidth,
frameHeight, frameHeight,
pixelWidthHeightRatio); pixelWidthHeightRatio,
colorSpace,
colorRange,
colorTransfer);
} }
/** /**

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.video; package com.google.android.exoplayer2.video;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException; import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil; import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
@ -60,6 +61,9 @@ public final class HevcConfig {
int bufferPosition = 0; int bufferPosition = 0;
int width = Format.NO_VALUE; int width = Format.NO_VALUE;
int height = Format.NO_VALUE; int height = Format.NO_VALUE;
@C.ColorSpace int colorSpace = Format.NO_VALUE;
@C.ColorRange int colorRange = Format.NO_VALUE;
@C.ColorTransfer int colorTransfer = Format.NO_VALUE;
float pixelWidthHeightRatio = 1; float pixelWidthHeightRatio = 1;
@Nullable String codecs = null; @Nullable String codecs = null;
for (int i = 0; i < numberOfArrays; i++) { for (int i = 0; i < numberOfArrays; i++) {
@ -83,6 +87,9 @@ public final class HevcConfig {
buffer, bufferPosition, bufferPosition + nalUnitLength); buffer, bufferPosition, bufferPosition + nalUnitLength);
width = spsData.width; width = spsData.width;
height = spsData.height; height = spsData.height;
colorSpace = spsData.colorSpace;
colorRange = spsData.colorRange;
colorTransfer = spsData.colorTransfer;
pixelWidthHeightRatio = spsData.pixelWidthHeightRatio; pixelWidthHeightRatio = spsData.pixelWidthHeightRatio;
codecs = codecs =
CodecSpecificDataUtil.buildHevcCodecString( CodecSpecificDataUtil.buildHevcCodecString(
@ -101,7 +108,15 @@ public final class HevcConfig {
List<byte[]> initializationData = List<byte[]> initializationData =
csdLength == 0 ? Collections.emptyList() : Collections.singletonList(buffer); csdLength == 0 ? Collections.emptyList() : Collections.singletonList(buffer);
return new HevcConfig( return new HevcConfig(
initializationData, lengthSizeMinusOne + 1, width, height, pixelWidthHeightRatio, codecs); initializationData,
lengthSizeMinusOne + 1,
width,
height,
pixelWidthHeightRatio,
codecs,
colorSpace,
colorRange,
colorTransfer);
} catch (ArrayIndexOutOfBoundsException e) { } catch (ArrayIndexOutOfBoundsException e) {
throw ParserException.createForMalformedContainer("Error parsing HEVC config", e); throw ParserException.createForMalformedContainer("Error parsing HEVC config", e);
} }
@ -128,6 +143,22 @@ public final class HevcConfig {
/** The pixel width to height ratio. */ /** The pixel width to height ratio. */
public final float pixelWidthHeightRatio; public final float pixelWidthHeightRatio;
/**
* The {@link C.ColorSpace} of the video or {@link Format#NO_VALUE} if unknown or not applicable.
*/
public final @C.ColorSpace int colorSpace;
/**
* The {@link C.ColorRange} of the video or {@link Format#NO_VALUE} if unknown or not applicable.
*/
public final @C.ColorRange int colorRange;
/**
* The {@link C.ColorTransfer} of the video or {@link Format#NO_VALUE} if unknown or not
* applicable.
*/
public final @C.ColorTransfer int colorTransfer;
/** /**
* An RFC 6381 codecs string representing the video format, or {@code null} if not known. * An RFC 6381 codecs string representing the video format, or {@code null} if not known.
* *
@ -141,12 +172,18 @@ public final class HevcConfig {
int width, int width,
int height, int height,
float pixelWidthHeightRatio, float pixelWidthHeightRatio,
@Nullable String codecs) { @Nullable String codecs,
@C.ColorSpace int colorSpace,
@C.ColorRange int colorRange,
@C.ColorTransfer int colorTransfer) {
this.initializationData = initializationData; this.initializationData = initializationData;
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength; this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
this.width = width; this.width = width;
this.height = height; this.height = height;
this.pixelWidthHeightRatio = pixelWidthHeightRatio; this.pixelWidthHeightRatio = pixelWidthHeightRatio;
this.codecs = codecs; this.codecs = codecs;
this.colorSpace = colorSpace;
this.colorRange = colorRange;
this.colorTransfer = colorTransfer;
} }
} }

View File

@ -192,6 +192,9 @@ public final class NalUnitUtilTest {
assertThat(spsData.pixelWidthHeightRatio).isEqualTo(1); assertThat(spsData.pixelWidthHeightRatio).isEqualTo(1);
assertThat(spsData.seqParameterSetId).isEqualTo(0); assertThat(spsData.seqParameterSetId).isEqualTo(0);
assertThat(spsData.width).isEqualTo(3840); assertThat(spsData.width).isEqualTo(3840);
assertThat(spsData.colorSpace).isEqualTo(6);
assertThat(spsData.colorRange).isEqualTo(2);
assertThat(spsData.colorTransfer).isEqualTo(6);
} }
private static byte[] buildTestData() { private static byte[] buildTestData() {