Skip to content

Commit dc53028

Browse files
committed
Changed av1c parser from a class to a method
1 parent 9d43cda commit dc53028

File tree

2 files changed

+143
-173
lines changed

2 files changed

+143
-173
lines changed

RELEASENOTES.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
video track are available. The default value is `false` which means
2525
selecting a video track is prioritized.
2626
* Extractors:
27-
* Add AV1C parsing to MP4 extractor
27+
* Add additional AV1C parsing to MP4 extractor to retrieve
28+
`ColorInfo.colorSpace`, `ColorInfo.colorTransfer`, and
29+
`ColorInfo.colorRange` values
2830
([#692](https://github.com/androidx/media/pull/692)).
2931
* Audio:
3032
* Video:

libraries/extractor/src/main/java/androidx/media3/extractor/mp4/AtomParsers.java

Lines changed: 140 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import static androidx.media3.common.util.Assertions.checkNotNull;
2020
import static androidx.media3.common.util.Util.castNonNull;
2121
import static java.lang.Math.max;
22-
import static java.lang.Math.min;
2322

2423
import android.util.Pair;
2524
import androidx.annotation.Nullable;
@@ -33,6 +32,7 @@
3332
import androidx.media3.common.util.CodecSpecificDataUtil;
3433
import androidx.media3.common.util.Log;
3534
import androidx.media3.common.util.NullableType;
35+
import androidx.media3.common.util.ParsableBitArray;
3636
import androidx.media3.common.util.ParsableByteArray;
3737
import androidx.media3.common.util.Util;
3838
import androidx.media3.container.Mp4LocationData;
@@ -1222,32 +1222,15 @@ private static void parseVideoSampleEntry(
12221222
colorTransfer =
12231223
ColorInfo.isoTransferCharacteristicsToColorTransfer(transferCharacteristics);
12241224
} else if (childAtomType == Atom.TYPE_av1C) {
1225-
ExtractorUtil.checkContainerInput(mimeType == null, /* message= */ null);
12261225
mimeType = MimeTypes.VIDEO_AV1;
12271226
parent.setPosition(childStartPosition + Atom.HEADER_SIZE);
1228-
parent.skipBytes(1);
1229-
int byte2 = parent.readUnsignedByte();
1230-
int seqProfile = byte2 >> 5;
1231-
int byte3 = parent.readUnsignedByte();
1232-
boolean highBitdepth = ((byte3 >> 6) & 0b1) != 0;
1233-
// From https://aomediacodec.github.io/av1-spec/av1-spec.pdf#page=44
1234-
if (seqProfile == 2 && highBitdepth) {
1235-
boolean twelveBit = ((byte3 >> 5) & 0b1) != 0;
1236-
bitdepthLuma = twelveBit ? 12 : 10;
1237-
} else if (seqProfile <= 2) {
1238-
bitdepthLuma = highBitdepth ? 10 : 8;
1239-
}
1240-
bitdepthChroma = bitdepthLuma;
1241-
// See av1C atom syntax:
1242-
// https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-syntax
1243-
parent.skipBytes(1); // skip to configOBUs[]
1244-
Av1BitstreamParser parser = new Av1BitstreamParser(parent);
1245-
if (parser.parseSequenceHeader() && parser.colorDescriptionPresentFlag == 1) {
1246-
colorSpace = ColorInfo.isoColorPrimariesToColorSpace(parser.colorPrimaries);
1247-
colorTransfer =
1248-
ColorInfo.isoTransferCharacteristicsToColorTransfer(parser.transferCharacteristics);
1249-
colorRange = (parser.colorRange == 1) ? C.COLOR_RANGE_FULL : C.COLOR_RANGE_LIMITED;
1250-
}
1227+
ColorInfo colorInfo = parseAv1c(parent);
1228+
1229+
bitdepthLuma = colorInfo.lumaBitdepth;
1230+
bitdepthChroma = colorInfo.chromaBitdepth;
1231+
colorSpace = colorInfo.colorSpace;
1232+
colorRange = colorInfo.colorRange;
1233+
colorTransfer = colorInfo.colorTransfer;
12511234
} else if (childAtomType == Atom.TYPE_clli) {
12521235
if (hdrStaticInfo == null) {
12531236
hdrStaticInfo = allocateHdrStaticInfo();
@@ -1396,6 +1379,138 @@ private static void parseVideoSampleEntry(
13961379
out.format = formatBuilder.build();
13971380
}
13981381

1382+
/**
1383+
* Parses the av1C configuration record and OBU sequence header and returns a {@link ColorInfo}
1384+
* from their data.
1385+
*
1386+
* <p>See av1C configuration record syntax in this <a
1387+
* href="https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-syntax">spec</a>.
1388+
*
1389+
* <p>See av1C OBU syntax in this <a
1390+
* href="https://aomediacodec.github.io/av1-spec/av1-spec.pdf">spec</a>.
1391+
*
1392+
* <p>The sections referenced in the method are from these specs.
1393+
*
1394+
* @param data The av1C atom data.
1395+
* @return {@link ColorInfo} parsed from the av1C data.
1396+
*/
1397+
private static ColorInfo parseAv1c(ParsableByteArray data) {
1398+
ColorInfo.Builder colorInfo = new ColorInfo.Builder();
1399+
ParsableBitArray bitArray = new ParsableBitArray(data.getData());
1400+
bitArray.setPosition(data.getPosition() * 8); // Convert byte to bit position.
1401+
1402+
// Parse av1C config record for bitdepth info.
1403+
// See https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-syntax.
1404+
bitArray.skipBytes(1); // marker, version
1405+
int seqProfile = bitArray.readBits(3); // seq_profile
1406+
bitArray.skipBits(6); // seq_level_idx_0, seq_tier_0
1407+
boolean highBitdepth = bitArray.readBit(); // high_bitdepth
1408+
boolean twelveBit = bitArray.readBit(); // twelve_bit
1409+
if (seqProfile == 2 && highBitdepth) {
1410+
colorInfo.setLumaBitdepth(twelveBit ? 12 : 10);
1411+
colorInfo.setChromaBitdepth(twelveBit ? 12 : 10);
1412+
} else if (seqProfile <= 2) {
1413+
colorInfo.setLumaBitdepth(highBitdepth ? 10 : 8);
1414+
colorInfo.setChromaBitdepth(highBitdepth ? 10 : 8);
1415+
}
1416+
// Skip monochrome, chroma_subsampling_x, chroma_subsampling_y, chroma_sample_position,
1417+
// reserved and initial_presentation_delay.
1418+
bitArray.skipBits(13);
1419+
1420+
// 5.3.1. General OBU syntax
1421+
bitArray.skipBit(); // obu_forbidden_bit
1422+
int obuType = bitArray.readBits(4); // obu_type
1423+
if (obuType != 1) { // obu_type != OBU_SEQUENCE_HEADER
1424+
Log.i(TAG, "Unsupported obu_type: " + obuType);
1425+
return colorInfo.build();
1426+
}
1427+
if (bitArray.readBit()) { // obu_extension_flag
1428+
Log.i(TAG, "Unsupported obu_extension_flag");
1429+
return colorInfo.build();
1430+
}
1431+
boolean obuHasSizeField = bitArray.readBit(); // obu_has_size_field
1432+
bitArray.skipBit(); // obu_reserved_1bit
1433+
// obu_size is unsigned leb128 and if obu_size <= 127 then it can be simplified as readBits(8).
1434+
if (obuHasSizeField && bitArray.readBits(8) > 127) { // obu_size
1435+
Log.i(TAG, "Excessive obu_size");
1436+
return colorInfo.build();
1437+
}
1438+
// 5.5.1. General OBU sequence header syntax
1439+
int obuSeqHeaderSeqProfile = bitArray.readBits(3); // seq_profile
1440+
bitArray.skipBit(); // still_picture
1441+
if (bitArray.readBit()) { // reduced_still_picture_header
1442+
Log.i(TAG, "Unsupported reduced_still_picture_header");
1443+
return colorInfo.build();
1444+
}
1445+
if (bitArray.readBit()) { // timing_info_present_flag
1446+
Log.i(TAG, "Unsupported timing_info_present_flag");
1447+
return colorInfo.build();
1448+
}
1449+
if (bitArray.readBit()) { // initial_display_delay_present_flag
1450+
Log.i(TAG, "Unsupported initial_display_delay_present_flag");
1451+
return colorInfo.build();
1452+
}
1453+
int operatingPointsCountMinus1 = bitArray.readBits(5); // operating_points_cnt_minus_1
1454+
for (int i = 0; i <= operatingPointsCountMinus1; i++) {
1455+
bitArray.skipBits(12); // operating_point_idc[i]
1456+
int seqLevelIdx = bitArray.readBits(5); // seq_level_idx[i]
1457+
if (seqLevelIdx > 7) {
1458+
bitArray.skipBit(); // seq_tier[i]
1459+
}
1460+
}
1461+
int frameWidthBitsMinus1 = bitArray.readBits(4); // frame_width_bits_minus_1
1462+
int frameHeightBitsMinus1 = bitArray.readBits(4); // frame_height_bits_minus_1
1463+
bitArray.skipBits(frameWidthBitsMinus1 + 1); // max_frame_width_minus_1
1464+
bitArray.skipBits(frameHeightBitsMinus1 + 1); // max_frame_height_minus_1
1465+
if (bitArray.readBit()) { // frame_id_numbers_present_flag
1466+
bitArray.skipBits(7); // delta_frame_id_length_minus_2, additional_frame_id_length_minus_1
1467+
}
1468+
bitArray.skipBits(7); // use_128x128_superblock...enable_dual_filter: 7 flags
1469+
boolean enableOrderHint = bitArray.readBit(); // enable_order_hint
1470+
if (enableOrderHint) {
1471+
bitArray.skipBits(2); // enable_jnt_comp, enable_ref_frame_mvs
1472+
}
1473+
int seqForceScreenContentTools =
1474+
bitArray.readBit() // seq_choose_screen_content_tools
1475+
? 2 // SELECT_SCREEN_CONTENT_TOOLS
1476+
: bitArray.readBits(1); // seq_force_screen_content_tools
1477+
if (seqForceScreenContentTools > 0) {
1478+
if (!bitArray.readBit()) { // seq_choose_integer_mv
1479+
bitArray.skipBits(1); // seq_force_integer_mv
1480+
}
1481+
}
1482+
if (enableOrderHint) {
1483+
bitArray.skipBits(3); // order_hint_bits_minus_1
1484+
}
1485+
bitArray.skipBits(3); // enable_superres, enable_cdef, enable_restoration
1486+
// 5.5.2. OBU Color config syntax
1487+
boolean colorConfigHighBitdepth = bitArray.readBit(); // high_bitdepth
1488+
if (obuSeqHeaderSeqProfile == 2 && colorConfigHighBitdepth) {
1489+
bitArray.skipBit(); // twelve_bit
1490+
}
1491+
1492+
boolean monochrome = (obuSeqHeaderSeqProfile != 1) && bitArray.readBit(); // mono_chrome
1493+
1494+
if (bitArray.readBit()) { // color_description_present_flag
1495+
int colorPrimaries = bitArray.readBits(8); // color_primaries
1496+
int transferCharacteristics = bitArray.readBits(8); // transfer_characteristics
1497+
int matrixCoefficients = bitArray.readBits(8); // matrix_coefficients
1498+
int colorRange =
1499+
(!monochrome
1500+
&& colorPrimaries == 1 // CP_BT_709
1501+
&& transferCharacteristics == 13 // TC_SRGB
1502+
&& matrixCoefficients == 0) // MC_IDENTITY
1503+
? 1
1504+
: bitArray.readBits(1); // color_range;
1505+
colorInfo
1506+
.setColorSpace(ColorInfo.isoColorPrimariesToColorSpace(colorPrimaries))
1507+
.setColorRange((colorRange == 1) ? C.COLOR_RANGE_FULL : C.COLOR_RANGE_LIMITED)
1508+
.setColorTransfer(
1509+
ColorInfo.isoTransferCharacteristicsToColorTransfer(transferCharacteristics));
1510+
}
1511+
return colorInfo.build();
1512+
}
1513+
13991514
private static ByteBuffer allocateHdrStaticInfo() {
14001515
// For HDR static info, Android decoders expect a 25-byte array. The first byte is zero to
14011516
// represent Static Metadata Type 1, as per CTA-861-G:2017, Table 44. The following 24 bytes
@@ -2181,151 +2296,4 @@ public int readNextSampleSize() {
21812296
}
21822297
}
21832298
}
2184-
2185-
/**
2186-
* Helper class for parsing syntax elements from AV1 bitstream.
2187-
*
2188-
* <p>Bitstream specification: https://aomediacodec.github.io/av1-spec/av1-spec.pdf
2189-
*/
2190-
private static final class Av1BitstreamParser {
2191-
private static final String TAG = "Av1BitstreamParser";
2192-
2193-
public int colorDescriptionPresentFlag;
2194-
public int colorPrimaries;
2195-
public int transferCharacteristics;
2196-
public int matrixCoefficients;
2197-
public int colorRange;
2198-
2199-
private final ParsableByteArray data;
2200-
private int bitCount;
2201-
private int currentByte;
2202-
private boolean parseSequenceHeaderCalled;
2203-
2204-
public Av1BitstreamParser(ParsableByteArray data) {
2205-
this.data = data;
2206-
}
2207-
2208-
/**
2209-
* Parses a fixed-length unsigned number.
2210-
*
2211-
* <p>See AV1 bitstream spec 4.10.2. f(n): Unsigned n-bit number appearing directly in the
2212-
* bitstream. The bits are read from high to low order.
2213-
*
2214-
* @param length The length (in bits) of the number to decode.
2215-
* @return Parsed unsigned number.
2216-
*/
2217-
private int parseF(int length) {
2218-
int result = 0;
2219-
while (length > 0) {
2220-
if (bitCount == 0) {
2221-
currentByte = data.readUnsignedByte();
2222-
bitCount = 8;
2223-
}
2224-
int newBitCount = min(length, bitCount);
2225-
bitCount -= newBitCount;
2226-
length -= newBitCount;
2227-
result = (result << newBitCount) | ((currentByte >> bitCount) & ((1 << newBitCount) - 1));
2228-
}
2229-
return result;
2230-
}
2231-
2232-
/**
2233-
* Parses the AV1 sequence header.
2234-
*
2235-
* <p>See AV1 bitstream spec 5.5.1. This method can only be called once.
2236-
*/
2237-
public boolean parseSequenceHeader() {
2238-
if (parseSequenceHeaderCalled) {
2239-
return false;
2240-
}
2241-
parseSequenceHeaderCalled = true;
2242-
2243-
// 5.3.1. General OBU syntax
2244-
parseF(1); // obu_forbidden_bit
2245-
int obuType = parseF(4); // obu_type
2246-
if (obuType != 1) { // obu_type != OBU_SEQUENCE_HEADER
2247-
Log.e(TAG, "Unsupported obu_type: " + obuType);
2248-
return false;
2249-
} else if (parseF(1) != 0) { // obu_extension_flag
2250-
Log.e(TAG, "Unsupported obu_extension_flag");
2251-
return false;
2252-
}
2253-
int obuHasSizeField = parseF(1); // obu_has_size_field
2254-
parseF(1); // obu_reserved_1bit
2255-
if (obuHasSizeField == 1 && parseF(8) > 127) { // obu_size
2256-
Log.e(TAG, "Excessive obu_size");
2257-
return false;
2258-
}
2259-
// 5.5.1. General sequence header OBU syntax
2260-
int seqProfile = parseF(3); // seq_profile
2261-
parseF(1); // still_picture
2262-
if (parseF(1) != 0) { // reduced_still_picture_header
2263-
Log.e(TAG, "Unsupported reduced_still_picture_header");
2264-
return false;
2265-
} else if (parseF(1) != 0) { // timing_info_present_flag
2266-
Log.e(TAG, "Unsupported timing_info_present_flag");
2267-
return false;
2268-
} else if (parseF(1) != 0) { // initial_display_delay_present_flag
2269-
Log.e(TAG, "Unsupported initial_display_delay_present_flag");
2270-
return false;
2271-
}
2272-
int operatingPointsCntMinus1 = parseF(5); // operating_points_cnt_minus_1
2273-
for (int i = 0; i <= operatingPointsCntMinus1; i++) {
2274-
parseF(12); // operating_point_idc[i]
2275-
int seqLevelIdx = parseF(5); // seq_level_idx[i]
2276-
if (seqLevelIdx > 7) {
2277-
parseF(1); // seq_tier[i]
2278-
}
2279-
}
2280-
int frameWidthBitsMinus1 = parseF(4); // frame_width_bits_minus_1
2281-
int frameHeightBitsMinus1 = parseF(4); // frame_height_bits_minus_1
2282-
parseF(frameWidthBitsMinus1 + 1); // max_frame_width_minus_1
2283-
parseF(frameHeightBitsMinus1 + 1); // max_frame_height_minus_1
2284-
if (parseF(1) == 1) { // frame_id_numbers_present_flag
2285-
parseF(7); // delta_frame_id_length_minus_2, additional_frame_id_length_minus_1
2286-
}
2287-
parseF(7); // use_128x128_superblock...enable_dual_filter: 7 flags
2288-
int enableOrderHint = parseF(1); // enable_order_hint
2289-
if (enableOrderHint == 1) {
2290-
parseF(2); // enable_jnt_comp, enable_ref_frame_mvs
2291-
}
2292-
int seqForceScreenContentTools = 2; // SELECT_SCREEN_CONTENT_TOOLS
2293-
if (parseF(1) == 0) { // seq_choose_screen_content_tools
2294-
seqForceScreenContentTools = parseF(1); // seq_force_screen_content_tools
2295-
}
2296-
if (seqForceScreenContentTools > 0) {
2297-
if (parseF(1) == 0) { // seq_choose_integer_mv
2298-
parseF(1); // seq_force_integer_mv
2299-
}
2300-
}
2301-
if (enableOrderHint == 1) {
2302-
parseF(3); // order_hint_bits_minus_1
2303-
}
2304-
parseF(3); // enable_superres, enable_cdef, enable_restoration
2305-
// 5.5.2. Color config syntax
2306-
int highBitdepth = parseF(1); // high_bitdepth
2307-
if (seqProfile == 2 && highBitdepth == 1) {
2308-
parseF(1); // twelve_bit
2309-
}
2310-
int monoChrome = 0;
2311-
if (seqProfile != 1) {
2312-
monoChrome = parseF(1); // mono_chrome
2313-
}
2314-
colorDescriptionPresentFlag = parseF(1); // color_description_present_flag
2315-
if (colorDescriptionPresentFlag == 1) {
2316-
colorPrimaries = parseF(8); // color_primaries
2317-
transferCharacteristics = parseF(8); // transfer_characteristics
2318-
matrixCoefficients = parseF(8); // matrix_coefficients
2319-
if (monoChrome == 0
2320-
&& colorPrimaries == 1 // CP_BT_709
2321-
&& transferCharacteristics == 13 // TC_SRGB
2322-
&& matrixCoefficients == 0) { // MC_IDENTITY
2323-
colorRange = 1;
2324-
} else {
2325-
colorRange = parseF(1); // color_range
2326-
}
2327-
}
2328-
return true;
2329-
}
2330-
}
23312299
}

0 commit comments

Comments
 (0)