diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/AdtsReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/AdtsReader.java index 553502b4c8d..52543a11e8d 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/AdtsReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/AdtsReader.java @@ -285,12 +285,18 @@ private void setCheckingAdtsHeaderState() { bytesRead = 0; } - /** Sets the state to STATE_READING_AAC_PCE. */ + /** + * Sets the state to STATE_READING_AAC_PCE. + * + * @param outputToUse TrackOutput object to write the sample to + * @param currentSampleDuration Duration of the sample to be read + * @param sampleSize Size of the sample + */ private void setReadingAacPceState( TrackOutput outputToUse, long currentSampleDuration, int sampleSize) { state = STATE_READING_AAC_PCE; bytesRead = 0; - currentOutput = outputToUse; + this.currentOutput = outputToUse; this.currentSampleDuration = currentSampleDuration; this.sampleSize = sampleSize; pceBuffer = new ParsableBitArray(new byte[min(sampleSize, AAC_PCE_MAX_SIZE)]); @@ -567,46 +573,52 @@ private void parseAdtsHeader() throws ParserException { @RequiresNonNull({"pendingOutputFormat", "pceBuffer"}) void readAacProgramConfigElement() { + // See ISO 13818-7 Advanced Audio Coding (2006) Table 36 for PCE tag encoding. if (pceBuffer.readBits(3) == 5 /* PCE tag */) { - // See `ISO 13818-7 Advanced Audio Coding (2006) Table 25` for format of a PCE - pceBuffer.skipBits(10); // Element instance tag, profile, sample frequency index + // See ISO 13818-7 Advanced Audio Coding (2006) Table 25 for syntax of a PCE. + pceBuffer.skipBits(10); // element_instance_tag(4), profile(2), element_instance_tag(4) int channelBits = 0; - channelBits += pceBuffer.readBits(4) * 5; // Front channel elements - channelBits += pceBuffer.readBits(4) * 5; // Side channel elements - channelBits += pceBuffer.readBits(4) * 5; // Back channel elements - channelBits += pceBuffer.readBits(2) * 4; // LFE channel elements - channelBits += pceBuffer.readBits(3) * 4; // Data elements - channelBits += pceBuffer.readBits(4) * 5; // Coupling channel elements - - if (pceBuffer.readBit()) { - pceBuffer.skipBits(4); // Mono mixdown + // num_front_channel_elements, front_element_is_cpe(1), front_element_tag_select(4) + channelBits += pceBuffer.readBits(4) * 5; + // num_side_channel_elements, side_element_is_cpe(1), side_element_tag_select(4) + channelBits += pceBuffer.readBits(4) * 5; + // num_back_channel_elements, back_element_is_cpe(1), back_element_tag_select(4) + channelBits += pceBuffer.readBits(4) * 5; + // num_lfe_channel_elements, lfe_element_tag_select(4) + channelBits += pceBuffer.readBits(2) * 4; + // num_assoc_data_elements, assoc_data_element_tag_select(4) + channelBits += pceBuffer.readBits(3) * 4; + // num_valid_cc_elements, cc_element_is_ind_sw(1), valid_cc_element_tag_select(4) + channelBits += pceBuffer.readBits(4) * 5; + + if (pceBuffer.readBit()) { // mono_mixdown_present + pceBuffer.skipBits(4); // mono_mixdown_element_number } - if (pceBuffer.readBit()) { - pceBuffer.skipBits(4); // Stereo mixdown + if (pceBuffer.readBit()) { // stereo_mixdown_present + pceBuffer.skipBits(4); // stereo_mixdown_element_number } - if (pceBuffer.readBit()) { - pceBuffer.skipBits(3); // Matrix mixdown + if (pceBuffer.readBit()) { // matrix_mixdown_idx_present + pceBuffer.skipBits(3); // matrix_mixdown_idx(2), matrix_mixdown_idx(1) } - // Beyond this point, pceBuffer may be empty, so check before consuming. + int numAlignmentBits = + 8 - (pceBuffer.getPosition() + channelBits + 7) % 8 - 1; // byte_alignment + int commentSizeBits = 8; // comment_field_bytes - int numAlignmentBits = 8 - (pceBuffer.getPosition() + channelBits + 7) % 8 - 1; - - if (pceBuffer.bitsLeft() >= channelBits + numAlignmentBits + 8) - { + // Beyond this point, pceBuffer may be empty, so check before consuming. + if (pceBuffer.bitsLeft() >= channelBits + numAlignmentBits + commentSizeBits) { pceBuffer.skipBits(channelBits); // Store PCE size excluding initial PCE tag, alignment bits and comment for later. int numPceBits = pceBuffer.getPosition() - 3 /* PCE tag */; pceBuffer.skipBits(numAlignmentBits); - int commentSize = pceBuffer.readBits(8); + int commentSize = pceBuffer.readBits(commentSizeBits); - if (sampleSize >= pceBuffer.getBytePosition() + commentSize) - { + if (sampleSize >= pceBuffer.getBytePosition() + commentSize) { // Append PCE to format's audio specific config. byte[] oldConfig = pendingOutputFormat.initializationData.get(0); diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/ts/AdtsReaderTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/ts/AdtsReaderTest.java index 14752fd40ca..617236523d4 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/ts/AdtsReaderTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/ts/AdtsReaderTest.java @@ -65,8 +65,7 @@ public class AdtsReaderTest { TestUtil.createByteArray(0xff, 0xf1, 0x50, 0x00, 0x02, 0x1f, 0xfc); private static final byte[] AAC_PCE_ADTS_CONTENT = - TestUtil.createByteArray( - 0xa0, 0x99, 0x01, 0x20, 0x00, 0x21, 0x19, 0x00, 0x00); + TestUtil.createByteArray(0xa0, 0x99, 0x01, 0x20, 0x00, 0x21, 0x19, 0x00, 0x00); private static final byte[] AAC_PCE_TEST_DATA = Bytes.concat(AAC_PCE_ADTS_HEADER, AAC_PCE_ADTS_CONTENT); @@ -202,16 +201,17 @@ public void adtsDataOnly() throws ParserException { @Test public void aacPceData() throws ParserException { data = new ParsableByteArray(AAC_PCE_TEST_DATA); + feed(); + assertSampleCounts(0, 1); adtsOutput.assertSample(0, AAC_PCE_ADTS_CONTENT, 0, C.BUFFER_FLAG_KEY_FRAME, null); } @Test(expected = IllegalStateException.class) public void aacPceDataFail() throws ParserException { - data = new ParsableByteArray(AAC_PCE_TEST_DATA); + data = new ParsableByteArray(Arrays.copyOf(AAC_PCE_TEST_DATA, AAC_PCE_TEST_DATA.length)); byte[] bytes = data.getData(); - // Remove PCE tag (first 3 bits of content). bytes[AAC_PCE_ADTS_HEADER.length] &= 0x1f; // Replace with CPE tag.