Add luma and chroma bit depths to Format, set them as signalled in containers and parameter sets

This commit is contained in:
Daniele Sparano 2023-06-28 16:50:46 +01:00 committed by microkatz
parent dc0bee9307
commit e71ca5a740
9 changed files with 155 additions and 12 deletions

View File

@ -96,6 +96,8 @@ import java.util.UUID;
* <li>{@link #projectionData}
* <li>{@link #stereoMode}
* <li>{@link #colorInfo}
* <li>{@link #lumaBitdepth}
* <li>{@link #chromaBitdepth}
* </ul>
*
* <h2 id="audio-formats">Fields relevant to audio formats</h2>
@ -167,6 +169,8 @@ public final class Format implements Bundleable {
@Nullable private byte[] projectionData;
private @C.StereoMode int stereoMode;
@Nullable private ColorInfo colorInfo;
private int lumaBitdepth;
private int chromaBitdepth;
// Audio specific.
@ -203,6 +207,8 @@ public final class Format implements Bundleable {
frameRate = NO_VALUE;
pixelWidthHeightRatio = 1.0f;
stereoMode = NO_VALUE;
lumaBitdepth = 8;
chromaBitdepth = 8;
// Audio specific.
channelCount = NO_VALUE;
sampleRate = NO_VALUE;
@ -249,6 +255,8 @@ public final class Format implements Bundleable {
this.projectionData = format.projectionData;
this.stereoMode = format.stereoMode;
this.colorInfo = format.colorInfo;
this.lumaBitdepth = format.lumaBitdepth;
this.chromaBitdepth = format.chromaBitdepth;
// Audio specific.
this.channelCount = format.channelCount;
this.sampleRate = format.sampleRate;
@ -560,6 +568,30 @@ public final class Format implements Bundleable {
return this;
}
/**
* Sets {@link Format#lumaBitdepth}. The default value is 8.
*
* @param lumaBitdepth The {@link Format#lumaBitdepth}.
* @return The builder.
*/
@CanIgnoreReturnValue
public Builder setLumaBitdepth(int lumaBitdepth) {
this.lumaBitdepth = lumaBitdepth;
return this;
}
/**
* Sets {@link Format#chromaBitdepth}. The default value is 8.
*
* @param chromaBitdepth The {@link Format#chromaBitdepth}.
* @return The builder.
*/
@CanIgnoreReturnValue
public Builder setChromaBitdepth(int chromaBitdepth) {
this.chromaBitdepth = chromaBitdepth;
return this;
}
// Audio specific.
/**
@ -872,6 +904,10 @@ public final class Format implements Bundleable {
/** The color metadata associated with the video, or null if not applicable. */
@UnstableApi @Nullable public final ColorInfo colorInfo;
/** The bit depth of the luma samples of the video. */
public final int lumaBitdepth;
/** The bit depth of the chroma samples of the video. It might differ from the luma bit depth. */
public final int chromaBitdepth;
// Audio specific.
@ -959,6 +995,8 @@ public final class Format implements Bundleable {
projectionData = builder.projectionData;
stereoMode = builder.stereoMode;
colorInfo = builder.colorInfo;
lumaBitdepth = builder.lumaBitdepth;
chromaBitdepth = builder.chromaBitdepth;
// Audio specific.
channelCount = builder.channelCount;
sampleRate = builder.sampleRate;
@ -1092,6 +1130,10 @@ public final class Format implements Bundleable {
+ ", "
+ frameRate
+ ", "
+ lumaBitdepth
+ ", "
+ chromaBitdepth
+ ", "
+ colorInfo
+ "]"
+ ", ["
@ -1132,6 +1174,8 @@ public final class Format implements Bundleable {
// [Omitted] projectionData.
result = 31 * result + stereoMode;
// [Omitted] colorInfo.
result = 31 * result + lumaBitdepth;
result = 31 * result + chromaBitdepth;
// Audio specific.
result = 31 * result + channelCount;
result = 31 * result + sampleRate;
@ -1173,6 +1217,8 @@ public final class Format implements Bundleable {
&& height == other.height
&& rotationDegrees == other.rotationDegrees
&& stereoMode == other.stereoMode
&& lumaBitdepth == other.lumaBitdepth
&& chromaBitdepth == other.chromaBitdepth
&& channelCount == other.channelCount
&& sampleRate == other.sampleRate
&& pcmEncoding == other.pcmEncoding
@ -1259,6 +1305,9 @@ public final class Format implements Bundleable {
if (format.width != NO_VALUE && format.height != NO_VALUE) {
builder.append(", res=").append(format.width).append("x").append(format.height);
}
if (format.lumaBitdepth != NO_VALUE && format.chromaBitdepth != NO_VALUE) {
builder.append(", bitdepth=[").append(format.lumaBitdepth).append(",").append(format.chromaBitdepth).append(']');
}
if (format.colorInfo != null && format.colorInfo.isValid()) {
builder.append(", color=").append(format.colorInfo.toLogString());
}
@ -1373,15 +1422,17 @@ public final class Format implements Bundleable {
private static final String FIELD_PROJECTION_DATA = Util.intToStringMaxRadix(20);
private static final String FIELD_STEREO_MODE = Util.intToStringMaxRadix(21);
private static final String FIELD_COLOR_INFO = Util.intToStringMaxRadix(22);
private static final String FIELD_CHANNEL_COUNT = Util.intToStringMaxRadix(23);
private static final String FIELD_SAMPLE_RATE = Util.intToStringMaxRadix(24);
private static final String FIELD_PCM_ENCODING = Util.intToStringMaxRadix(25);
private static final String FIELD_ENCODER_DELAY = Util.intToStringMaxRadix(26);
private static final String FIELD_ENCODER_PADDING = Util.intToStringMaxRadix(27);
private static final String FIELD_ACCESSIBILITY_CHANNEL = Util.intToStringMaxRadix(28);
private static final String FIELD_CRYPTO_TYPE = Util.intToStringMaxRadix(29);
private static final String FIELD_TILE_COUNT_HORIZONTAL = Util.intToStringMaxRadix(30);
private static final String FIELD_TILE_COUNT_VERTICAL = Util.intToStringMaxRadix(31);
private static final String FIELD_LUMA_BITDEPTH = Util.intToStringMaxRadix(23);
private static final String FIELD_CHROMA_BITDEPTH = Util.intToStringMaxRadix(24);
private static final String FIELD_CHANNEL_COUNT = Util.intToStringMaxRadix(25);
private static final String FIELD_SAMPLE_RATE = Util.intToStringMaxRadix(26);
private static final String FIELD_PCM_ENCODING = Util.intToStringMaxRadix(27);
private static final String FIELD_ENCODER_DELAY = Util.intToStringMaxRadix(28);
private static final String FIELD_ENCODER_PADDING = Util.intToStringMaxRadix(29);
private static final String FIELD_ACCESSIBILITY_CHANNEL = Util.intToStringMaxRadix(30);
private static final String FIELD_CRYPTO_TYPE = Util.intToStringMaxRadix(31);
private static final String FIELD_TILE_COUNT_HORIZONTAL = Util.intToStringMaxRadix(32);
private static final String FIELD_TILE_COUNT_VERTICAL = Util.intToStringMaxRadix(33);
@UnstableApi
@Override
@ -1431,6 +1482,8 @@ public final class Format implements Bundleable {
if (colorInfo != null) {
bundle.putBundle(FIELD_COLOR_INFO, colorInfo.toBundle());
}
bundle.putInt(FIELD_LUMA_BITDEPTH, lumaBitdepth);
bundle.putInt(FIELD_CHROMA_BITDEPTH, chromaBitdepth);
// Audio specific.
bundle.putInt(FIELD_CHANNEL_COUNT, channelCount);
bundle.putInt(FIELD_SAMPLE_RATE, sampleRate);
@ -1496,6 +1549,8 @@ public final class Format implements Bundleable {
if (colorInfoBundle != null) {
builder.setColorInfo(ColorInfo.CREATOR.fromBundle(colorInfoBundle));
}
builder.setLumaBitdepth(bundle.getInt(FIELD_LUMA_BITDEPTH, DEFAULT.lumaBitdepth));
builder.setChromaBitdepth(bundle.getInt(FIELD_CHROMA_BITDEPTH, DEFAULT.chromaBitdepth));
// Audio specific.
builder
.setChannelCount(bundle.getInt(FIELD_CHANNEL_COUNT, DEFAULT.channelCount))

View File

@ -66,6 +66,8 @@ public final class NalUnitUtil {
public final int width;
public final int height;
public final float pixelWidthHeightRatio;
public final int bitDepthLumaMinus8;
public final int bitDepthChromaMinus8;
public final boolean separateColorPlaneFlag;
public final boolean frameMbsOnlyFlag;
public final int frameNumLength;
@ -85,6 +87,8 @@ public final class NalUnitUtil {
int width,
int height,
float pixelWidthHeightRatio,
int bitDepthLumaMinus8,
int bitDepthChromaMinus8,
boolean separateColorPlaneFlag,
boolean frameMbsOnlyFlag,
int frameNumLength,
@ -102,6 +106,8 @@ public final class NalUnitUtil {
this.width = width;
this.height = height;
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
this.bitDepthLumaMinus8 = bitDepthLumaMinus8;
this.bitDepthChromaMinus8 = bitDepthChromaMinus8;
this.separateColorPlaneFlag = separateColorPlaneFlag;
this.frameMbsOnlyFlag = frameMbsOnlyFlag;
this.frameNumLength = frameNumLength;
@ -382,6 +388,8 @@ public final class NalUnitUtil {
int chromaFormatIdc = 1; // Default is 4:2:0
boolean separateColorPlaneFlag = false;
int bitDepthLumaMinus8 = 0;
int bitDepthChromaMinus8 = 0;
if (profileIdc == 100
|| profileIdc == 110
|| profileIdc == 122
@ -396,8 +404,8 @@ public final class NalUnitUtil {
if (chromaFormatIdc == 3) {
separateColorPlaneFlag = data.readBit();
}
data.readUnsignedExpGolombCodedInt(); // bit_depth_luma_minus8
data.readUnsignedExpGolombCodedInt(); // bit_depth_chroma_minus8
bitDepthLumaMinus8 = data.readUnsignedExpGolombCodedInt();
bitDepthChromaMinus8 = data.readUnsignedExpGolombCodedInt();
data.skipBit(); // qpprime_y_zero_transform_bypass_flag
boolean seqScalingMatrixPresentFlag = data.readBit();
if (seqScalingMatrixPresentFlag) {
@ -511,6 +519,8 @@ public final class NalUnitUtil {
frameWidth,
frameHeight,
pixelWidthHeightRatio,
bitDepthLumaMinus8,
bitDepthChromaMinus8,
separateColorPlaneFlag,
frameMbsOnlyFlag,
frameNumLength,

View File

@ -139,6 +139,7 @@ public class DebugTextViewHelper {
+ format.width
+ "x"
+ format.height
+ getBitdepthInfoString(format.lumaBitdepth)
+ getColorInfoString(format.colorInfo)
+ getPixelAspectRatioString(format.pixelWidthHeightRatio)
+ getDecoderCountersBufferCountString(decoderCounters)
@ -188,6 +189,10 @@ public class DebugTextViewHelper {
+ counters.droppedToKeyframeCount;
}
private static String getBitdepthInfoString(int lumaBitdepth) {
return lumaBitdepth != -1 ? " b:" + lumaBitdepth : "";
}
private static String getColorInfoString(@Nullable ColorInfo colorInfo) {
return colorInfo != null && colorInfo.isValid() ? " colr:" + colorInfo.toLogString() : "";
}

View File

@ -58,6 +58,8 @@ public final class AvcConfig {
int width = Format.NO_VALUE;
int height = Format.NO_VALUE;
int bitdepthLuma = Format.NO_VALUE;
int bitdepthChroma = Format.NO_VALUE;
@C.ColorSpace int colorSpace = Format.NO_VALUE;
@C.ColorRange int colorRange = Format.NO_VALUE;
@C.ColorTransfer int colorTransfer = Format.NO_VALUE;
@ -70,6 +72,8 @@ public final class AvcConfig {
initializationData.get(0), nalUnitLengthFieldLength, sps.length);
width = spsData.width;
height = spsData.height;
bitdepthLuma = spsData.bitDepthLumaMinus8 + 8;
bitdepthChroma = spsData.bitDepthChromaMinus8 + 8;
colorSpace = spsData.colorSpace;
colorRange = spsData.colorRange;
colorTransfer = spsData.colorTransfer;
@ -84,6 +88,8 @@ public final class AvcConfig {
nalUnitLengthFieldLength,
width,
height,
bitdepthLuma,
bitdepthChroma,
colorSpace,
colorRange,
colorTransfer,
@ -110,6 +116,12 @@ public final class AvcConfig {
/** The height of each decoded frame, or {@link Format#NO_VALUE} if unknown. */
public final int height;
/** The bit depth of the luma samples, or {@link Format#NO_VALUE} if unknown. */
public final int bitdepthLuma;
/** The bit depth of the chroma samples, or {@link Format#NO_VALUE} if unknown. */
public final int bitdepthChroma;
/**
* The {@link C.ColorSpace} of the video, or {@link Format#NO_VALUE} if unknown or not applicable.
*/
@ -141,6 +153,8 @@ public final class AvcConfig {
int nalUnitLengthFieldLength,
int width,
int height,
int bitdepthLuma,
int bitdepthChroma,
@C.ColorSpace int colorSpace,
@C.ColorRange int colorRange,
@C.ColorTransfer int colorTransfer,
@ -150,6 +164,8 @@ public final class AvcConfig {
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
this.width = width;
this.height = height;
this.bitdepthLuma = bitdepthLuma;
this.bitdepthChroma = bitdepthChroma;
this.colorSpace = colorSpace;
this.colorRange = colorRange;
this.colorTransfer = colorTransfer;

View File

@ -63,6 +63,8 @@ public final class HevcConfig {
int bufferPosition = 0;
int width = Format.NO_VALUE;
int height = Format.NO_VALUE;
int bitdepthLuma = Format.NO_VALUE;
int bitdepthChroma = Format.NO_VALUE;
@C.ColorSpace int colorSpace = Format.NO_VALUE;
@C.ColorRange int colorRange = Format.NO_VALUE;
@C.ColorTransfer int colorTransfer = Format.NO_VALUE;
@ -89,6 +91,8 @@ public final class HevcConfig {
buffer, bufferPosition, bufferPosition + nalUnitLength);
width = spsData.width;
height = spsData.height;
bitdepthLuma = spsData.bitDepthLumaMinus8 + 8;
bitdepthChroma = spsData.bitDepthChromaMinus8 + 8;
colorSpace = spsData.colorSpace;
colorRange = spsData.colorRange;
colorTransfer = spsData.colorTransfer;
@ -114,6 +118,8 @@ public final class HevcConfig {
lengthSizeMinusOne + 1,
width,
height,
bitdepthLuma,
bitdepthChroma,
colorSpace,
colorRange,
colorTransfer,
@ -142,6 +148,12 @@ public final class HevcConfig {
/** The height of each decoded frame, or {@link Format#NO_VALUE} if unknown. */
public final int height;
/** The bit depth of the luma samples, or {@link Format#NO_VALUE} if unknown. */
public final int bitdepthLuma;
/** The bit depth of the chroma samples, or {@link Format#NO_VALUE} if unknown. */
public final int bitdepthChroma;
/**
* The {@link C.ColorSpace} of the video or {@link Format#NO_VALUE} if unknown or not applicable.
*/
@ -173,6 +185,8 @@ public final class HevcConfig {
int nalUnitLengthFieldLength,
int width,
int height,
int bitdepthLuma,
int bitdepthChroma,
@C.ColorSpace int colorSpace,
@C.ColorRange int colorRange,
@C.ColorTransfer int colorTransfer,
@ -182,6 +196,8 @@ public final class HevcConfig {
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
this.width = width;
this.height = height;
this.bitdepthLuma = bitdepthLuma;
this.bitdepthChroma = bitdepthChroma;
this.colorSpace = colorSpace;
this.colorRange = colorRange;
this.colorTransfer = colorTransfer;

View File

@ -232,6 +232,7 @@ public class MatroskaExtractor implements Extractor {
private static final int ID_STEREO_MODE = 0x53B8;
private static final int ID_COLOUR = 0x55B0;
private static final int ID_COLOUR_RANGE = 0x55B9;
private static final int ID_COLOUR_BITS_PER_CHANNEL = 0x55B2;
private static final int ID_COLOUR_TRANSFER = 0x55BA;
private static final int ID_COLOUR_PRIMARIES = 0x55BB;
private static final int ID_MAX_CLL = 0x55BC;
@ -608,6 +609,7 @@ public class MatroskaExtractor implements Extractor {
case ID_CUE_CLUSTER_POSITION:
case ID_REFERENCE_BLOCK:
case ID_STEREO_MODE:
case ID_COLOUR_BITS_PER_CHANNEL:
case ID_COLOUR_RANGE:
case ID_COLOUR_TRANSFER:
case ID_COLOUR_PRIMARIES:
@ -1017,6 +1019,10 @@ public class MatroskaExtractor implements Extractor {
currentTrack.colorTransfer = colorTransfer;
}
break;
case ID_COLOUR_BITS_PER_CHANNEL:
assertInTrackEntry(id);
currentTrack.bitsPerChannel = (int)value;
break;
case ID_COLOUR_RANGE:
assertInTrackEntry(id);
switch ((int) value) {
@ -2013,6 +2019,7 @@ public class MatroskaExtractor implements Extractor {
// Video elements.
public int width = Format.NO_VALUE;
public int height = Format.NO_VALUE;
public int bitsPerChannel = Format.NO_VALUE;
public int displayWidth = Format.NO_VALUE;
public int displayHeight = Format.NO_VALUE;
public int displayUnit = DISPLAY_UNIT_PIXELS;
@ -2325,6 +2332,8 @@ public class MatroskaExtractor implements Extractor {
formatBuilder
.setWidth(width)
.setHeight(height)
.setLumaBitdepth(bitsPerChannel)
.setChromaBitdepth(bitsPerChannel)
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
.setRotationDegrees(rotationDegrees)
.setProjectionData(projectionData)

View File

@ -1111,6 +1111,9 @@ import java.util.List;
int height = parent.readUnsignedShort();
boolean pixelWidthHeightRatioFromPasp = false;
float pixelWidthHeightRatio = 1;
// Set default luma and chroma bit depths to 8 as old codecs might not even signal them
int bitdepthLuma = 8;
int bitdepthChroma = 8;
parent.skipBytes(50);
int childPosition = parent.getPosition();
@ -1177,6 +1180,8 @@ import java.util.List;
colorSpace = avcConfig.colorSpace;
colorRange = avcConfig.colorRange;
colorTransfer = avcConfig.colorTransfer;
bitdepthLuma = avcConfig.bitdepthLuma;
bitdepthChroma = avcConfig.bitdepthChroma;
} else if (childAtomType == Atom.TYPE_hvcC) {
ExtractorUtil.checkContainerInput(mimeType == null, /* message= */ null);
mimeType = MimeTypes.VIDEO_H265;
@ -1191,6 +1196,8 @@ import java.util.List;
colorSpace = hevcConfig.colorSpace;
colorRange = hevcConfig.colorRange;
colorTransfer = hevcConfig.colorTransfer;
bitdepthLuma = hevcConfig.bitdepthLuma;
bitdepthChroma = hevcConfig.bitdepthChroma;
} else if (childAtomType == Atom.TYPE_dvcC || childAtomType == Atom.TYPE_dvvC) {
@Nullable DolbyVisionConfig dolbyVisionConfig = DolbyVisionConfig.parse(parent);
if (dolbyVisionConfig != null) {
@ -1203,7 +1210,10 @@ import java.util.List;
parent.setPosition(childStartPosition + Atom.FULL_HEADER_SIZE);
// See vpcC atom syntax: https://www.webmproject.org/vp9/mp4/#syntax_1
parent.skipBytes(2); // profile(8), level(8)
boolean fullRangeFlag = (parent.readUnsignedByte() & 1) != 0;
int byte3 = parent.readUnsignedByte();
bitdepthLuma = byte3 >> 4;
bitdepthChroma = bitdepthLuma;
boolean fullRangeFlag = (byte3 & 0b1) != 0;
int colorPrimaries = parent.readUnsignedByte();
int transferCharacteristics = parent.readUnsignedByte();
colorSpace = ColorInfo.isoColorPrimariesToColorSpace(colorPrimaries);
@ -1213,6 +1223,22 @@ import java.util.List;
} else if (childAtomType == Atom.TYPE_av1C) {
ExtractorUtil.checkContainerInput(mimeType == null, /* message= */ null);
mimeType = MimeTypes.VIDEO_AV1;
parent.setPosition(childStartPosition + Atom.HEADER_SIZE);
if (childAtomSize > Atom.HEADER_SIZE) {
parent.skipBytes(1); ; // marker(1), version(7)
int byte2 = parent.readUnsignedByte();
int seqProfile = byte2 >> 5;
int byte3 = parent.readUnsignedByte();
boolean highBitdepth = ((byte3 >> 6) & 0b1) != 0;
boolean twelveBit = ((byte3 >> 5) & 0b1) != 0;
// From https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=44
if (seqProfile == 2 && highBitdepth) {
bitdepthLuma = twelveBit ? 12 : 10;
} else if (seqProfile <= 2) {
bitdepthLuma = highBitdepth ? 10 : 8;
}
bitdepthChroma = bitdepthLuma;
}
} else if (childAtomType == Atom.TYPE_clli) {
if (hdrStaticInfo == null) {
hdrStaticInfo = allocateHdrStaticInfo();
@ -1334,6 +1360,8 @@ import java.util.List;
.setCodecs(codecs)
.setWidth(width)
.setHeight(height)
.setLumaBitdepth(bitdepthLuma)
.setChromaBitdepth(bitdepthChroma)
.setPixelWidthHeightRatio(pixelWidthHeightRatio)
.setRotationDegrees(rotationDegrees)
.setProjectionData(projectionData)

View File

@ -219,6 +219,8 @@ public final class H264Reader implements ElementaryStreamReader {
.setCodecs(codecs)
.setWidth(spsData.width)
.setHeight(spsData.height)
.setLumaBitdepth(spsData.bitDepthLumaMinus8 + 8)
.setChromaBitdepth(spsData.bitDepthChromaMinus8 + 8)
.setPixelWidthHeightRatio(spsData.pixelWidthHeightRatio)
.setInitializationData(initializationData)
.build());

View File

@ -264,6 +264,8 @@ public final class H265Reader implements ElementaryStreamReader {
.setCodecs(codecs)
.setWidth(spsData.width)
.setHeight(spsData.height)
.setLumaBitdepth(spsData.bitDepthLumaMinus8 + 8)
.setChromaBitdepth(spsData.bitDepthChromaMinus8 + 8)
.setPixelWidthHeightRatio(spsData.pixelWidthHeightRatio)
.setInitializationData(Collections.singletonList(csdData))
.build();