|
19 | 19 | import static androidx.media3.common.util.Assertions.checkNotNull; |
20 | 20 | import static androidx.media3.common.util.Util.castNonNull; |
21 | 21 | import static java.lang.Math.max; |
22 | | -import static java.lang.Math.min; |
23 | 22 |
|
24 | 23 | import android.util.Pair; |
25 | 24 | import androidx.annotation.Nullable; |
|
33 | 32 | import androidx.media3.common.util.CodecSpecificDataUtil; |
34 | 33 | import androidx.media3.common.util.Log; |
35 | 34 | import androidx.media3.common.util.NullableType; |
| 35 | +import androidx.media3.common.util.ParsableBitArray; |
36 | 36 | import androidx.media3.common.util.ParsableByteArray; |
37 | 37 | import androidx.media3.common.util.Util; |
38 | 38 | import androidx.media3.container.Mp4LocationData; |
@@ -1222,32 +1222,15 @@ private static void parseVideoSampleEntry( |
1222 | 1222 | colorTransfer = |
1223 | 1223 | ColorInfo.isoTransferCharacteristicsToColorTransfer(transferCharacteristics); |
1224 | 1224 | } else if (childAtomType == Atom.TYPE_av1C) { |
1225 | | - ExtractorUtil.checkContainerInput(mimeType == null, /* message= */ null); |
1226 | 1225 | mimeType = MimeTypes.VIDEO_AV1; |
1227 | 1226 | 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; |
1251 | 1234 | } else if (childAtomType == Atom.TYPE_clli) { |
1252 | 1235 | if (hdrStaticInfo == null) { |
1253 | 1236 | hdrStaticInfo = allocateHdrStaticInfo(); |
@@ -1396,6 +1379,138 @@ private static void parseVideoSampleEntry( |
1396 | 1379 | out.format = formatBuilder.build(); |
1397 | 1380 | } |
1398 | 1381 |
|
| 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 | + |
1399 | 1514 | private static ByteBuffer allocateHdrStaticInfo() { |
1400 | 1515 | // For HDR static info, Android decoders expect a 25-byte array. The first byte is zero to |
1401 | 1516 | // represent Static Metadata Type 1, as per CTA-861-G:2017, Table 44. The following 24 bytes |
@@ -2181,151 +2296,4 @@ public int readNextSampleSize() { |
2181 | 2296 | } |
2182 | 2297 | } |
2183 | 2298 | } |
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 | | - } |
2331 | 2299 | } |
0 commit comments