From cbe17bcd0aa8569717eb3187498a7bd1f87a6be7 Mon Sep 17 00:00:00 2001 From: Rohit Singh Date: Tue, 16 Apr 2024 14:51:47 +0100 Subject: [PATCH] Changes based on internal review --- .../androidx/media3/extractor/MpeghUtil.java | 33 ++++++++-- .../media3/extractor/ts/MpeghReader.java | 60 +++++++++++-------- .../media3/extractor/ts/TsExtractorTest.java | 48 +++++++++++---- 3 files changed, 98 insertions(+), 43 deletions(-) diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/MpeghUtil.java b/libraries/extractor/src/main/java/androidx/media3/extractor/MpeghUtil.java index bfa91766ad..ff7794f24d 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/MpeghUtil.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/MpeghUtil.java @@ -245,10 +245,10 @@ public final class MpeghUtil { // accordingly. double resamplingRatio = getResamplingRatio(usacSamplingFrequency); int samplingFrequency = (int) (usacSamplingFrequency * resamplingRatio); - int standardFrameSamples = (int) (outputFrameLength * resamplingRatio); + int standardFrameLength = (int) (outputFrameLength * resamplingRatio); return new Mpegh3daConfig( - profileLevelIndication, samplingFrequency, standardFrameSamples, compatibleProfileLevelSet); + profileLevelIndication, samplingFrequency, standardFrameLength, compatibleProfileLevelSet); } /** @@ -558,6 +558,10 @@ public final class MpeghUtil { private MpeghUtil() {} + /** + * Represents the header of an MHAS (MPEG-H 3D Audio System) packet. This header provides + * essential information to identify and parse the packet's contents. + */ public static class MhasPacketHeader { /** MHAS packet types. See ISO_IEC_23008-3;2022, 14.4. */ @@ -611,11 +615,19 @@ public final class MpeghUtil { public static final int PACTYPE_PCMDATA = 21; public static final int PACTYP_LOUDNESS = 22; + /** The payload type in the actual packet. */ public final @Type int packetType; + + /** A label indicating which packets belong together. */ public final long packetLabel; + + /** The length of MHAS packet in bytes. */ public final int packetLength; + + /** The length of MHAS packet header in bytes. */ public final int headerLength; + /** Creates an instance. */ public MhasPacketHeader( @Type int packetType, long packetLabel, int packetLength, int headerLength) { this.packetType = packetType; @@ -625,21 +637,32 @@ public final class MpeghUtil { } } + /** Represents an MPEG-H 3D audio configuration. */ public static class Mpegh3daConfig { + /** The MPEG-H 3D audio profile and level indication. */ public final int profileLevelIndication; + + /** The sampling frequency of the MPEG-H 3D Audio stream. */ public final int samplingFrequency; - public final int standardFrameSamples; + + /** The standard frame length in samples. */ + public final int standardFrameLength; + + /** + * A list of MPEG-H 3D audio profile-level set that are compatible with the current + * configuration. + */ @Nullable public final byte[] compatibleProfileLevelSet; private Mpegh3daConfig( int profileLevelIndication, int samplingFrequency, - int standardFrameSamples, + int standardFrameLength, @Nullable byte[] compatibleProfileLevelSet) { this.profileLevelIndication = profileLevelIndication; this.samplingFrequency = samplingFrequency; - this.standardFrameSamples = standardFrameSamples; + this.standardFrameLength = standardFrameLength; this.compatibleProfileLevelSet = compatibleProfileLevelSet; } } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/MpeghReader.java b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/MpeghReader.java index 19567c3010..25c6e9af47 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/ts/MpeghReader.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/ts/MpeghReader.java @@ -209,26 +209,10 @@ public final class MpeghReader implements ElementaryStreamReader { ParsableByteArray source, ParsableByteArray target, int targetLength) { int bytesToRead = min(source.bytesLeft(), targetLength - target.getPosition()); source.readBytes(target.getData(), target.getPosition(), bytesToRead); - target.setPosition(target.getPosition() + bytesToRead); + target.skipBytes(bytesToRead); return target.getPosition() == targetLength; } - /** - * Copies data from the provided {@code source} into a given {@code target} without progressing - * the position of the {@code source}. - * - * @param source The source from which to read. - * @param target The target into which data is to be read. - * @param targetLength The target length of the read. - */ - private void copyData(ParsableByteArray source, ParsableByteArray target, int targetLength) { - int sourcePosition = source.getPosition(); - int bytesToRead = min(source.bytesLeft(), targetLength - target.getPosition()); - source.readBytes(target.getData(), target.getPosition(), bytesToRead); - target.setPosition(target.getPosition() + bytesToRead); - source.setPosition(sourcePosition); - } - /** * Locates the next SYNC value in the buffer, advancing the position to the byte that immediately * follows it. If SYNC was not located, the position is advanced to the limit. @@ -249,7 +233,6 @@ public final class MpeghReader implements ElementaryStreamReader { syncBytes <<= C.BITS_PER_BYTE; syncBytes |= pesBuffer.readUnsignedByte(); if (MpeghUtil.isSyncWord(syncBytes)) { - pesBuffer.setPosition(pesBuffer.getPosition() - MpeghUtil.MHAS_SYNC_WORD_LENGTH); syncBytes = 0; return true; } @@ -275,12 +258,9 @@ public final class MpeghReader implements ElementaryStreamReader { payloadBytesRead = 0; frameBytes += header.packetLength + header.headerLength; - if (header.packetType == MpeghUtil.MhasPacketHeader.PACTYP_AUDIOTRUNCATION - || header.packetType == MpeghUtil.MhasPacketHeader.PACTYP_MPEGH3DACFG) { + if (shouldParsePacket(header.packetType)) { // prepare data scratch buffer - dataScratchBytes.ensureCapacity(header.packetLength); - dataScratchBytes.setPosition(0); - dataScratchBytes.setLimit(header.packetLength); + dataScratchBytes.reset(header.packetLength); } headerDataFinished = true; } @@ -308,8 +288,7 @@ public final class MpeghReader implements ElementaryStreamReader { * not be changed. */ private void maybeCopyToDataScratchBuffer(ParsableByteArray data) { - if (header.packetType == MpeghUtil.MhasPacketHeader.PACTYP_MPEGH3DACFG - || header.packetType == MpeghUtil.MhasPacketHeader.PACTYP_AUDIOTRUNCATION) { + if (shouldParsePacket(header.packetType)) { // read bytes from header scratch buffer into the data scratch buffer if (headerScratchBytes.getPosition() != MpeghUtil.MAX_MHAS_PACKET_HEADER_SIZE) { copyData(headerScratchBytes, dataScratchBytes, header.packetLength); @@ -319,6 +298,35 @@ public final class MpeghReader implements ElementaryStreamReader { } } + /** + * Copies data from the provided {@code source} into a given {@code target} without progressing + * the position of the {@code source}. + * + * @param source The source from which to read. + * @param target The target into which data is to be read. + * @param targetLength The target length of the read. + */ + private void copyData(ParsableByteArray source, ParsableByteArray target, int targetLength) { + int sourcePosition = source.getPosition(); + int bytesToRead = min(source.bytesLeft(), targetLength - target.getPosition()); + source.readBytes(target.getData(), target.getPosition(), bytesToRead); + target.skipBytes(bytesToRead); + source.setPosition(sourcePosition); + } + + /** + * Determines whether a packet should be parsed based on its type. + * + * @param packetType The {@link MpeghUtil.MhasPacketHeader.Type} of the MHAS packet header. + * @return {@code true} if the packet type is either {@link + * MpeghUtil.MhasPacketHeader#PACTYP_MPEGH3DACFG} or {@link + * MpeghUtil.MhasPacketHeader#PACTYP_AUDIOTRUNCATION}, {@code false} otherwise. + */ + private boolean shouldParsePacket(@MpeghUtil.MhasPacketHeader.Type int packetType) { + return packetType == MpeghUtil.MhasPacketHeader.PACTYP_MPEGH3DACFG + || packetType == MpeghUtil.MhasPacketHeader.PACTYP_AUDIOTRUNCATION; + } + /** * Writes sample data to the output. * @@ -348,7 +356,7 @@ public final class MpeghReader implements ElementaryStreamReader { private void parseConfig(ParsableBitArray bitArray) throws ParserException { MpeghUtil.Mpegh3daConfig config = MpeghUtil.parseMpegh3daConfig(bitArray); samplingRate = config.samplingFrequency; - standardFrameLength = config.standardFrameSamples; + standardFrameLength = config.standardFrameLength; if (mainStreamLabel != header.packetLabel) { mainStreamLabel = header.packetLabel; // set the output format diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/ts/TsExtractorTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/ts/TsExtractorTest.java index 575f5cef99..05d2840204 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/ts/TsExtractorTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/ts/TsExtractorTest.java @@ -248,73 +248,97 @@ public final class TsExtractorTest { @Test public void sampleWithMpeghBlCicp1Single() throws Exception { ExtractorAsserts.assertBehavior( - TsExtractor::new, "media/ts/sample_mpegh_bl_cicp1_single.ts", simulationConfig); + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/ts/sample_mpegh_bl_cicp1_single.ts", + simulationConfig); } @Test public void sampleWithMpeghLcBlCicp1Single() throws Exception { ExtractorAsserts.assertBehavior( - TsExtractor::new, "media/ts/sample_mpegh_lcbl_cicp1_single.ts", simulationConfig); + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/ts/sample_mpegh_lcbl_cicp1_single.ts", + simulationConfig); } @Test public void sampleWithMpeghBlConfigChangeSingle() throws Exception { ExtractorAsserts.assertBehavior( - TsExtractor::new, "media/ts/sample_mpegh_bl_configchange_single.ts", simulationConfig); + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/ts/sample_mpegh_bl_configchange_single.ts", + simulationConfig); } @Test public void sampleWithMpeghLcBlConfigChangeSingle() throws Exception { ExtractorAsserts.assertBehavior( - TsExtractor::new, "media/ts/sample_mpegh_lcbl_configchange_single.ts", simulationConfig); + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/ts/sample_mpegh_lcbl_configchange_single.ts", + simulationConfig); } @Test public void sampleWithMpeghBlCicp1Multi() throws Exception { ExtractorAsserts.assertBehavior( - TsExtractor::new, "media/ts/sample_mpegh_bl_cicp1_multi.ts", simulationConfig); + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/ts/sample_mpegh_bl_cicp1_multi.ts", + simulationConfig); } @Test public void sampleWithMpeghLcBlCicp1Multi() throws Exception { ExtractorAsserts.assertBehavior( - TsExtractor::new, "media/ts/sample_mpegh_lcbl_cicp1_multi.ts", simulationConfig); + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/ts/sample_mpegh_lcbl_cicp1_multi.ts", + simulationConfig); } @Test public void sampleWithMpeghBlConfigChangeMulti() throws Exception { ExtractorAsserts.assertBehavior( - TsExtractor::new, "media/ts/sample_mpegh_bl_configchange_multi.ts", simulationConfig); + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/ts/sample_mpegh_bl_configchange_multi.ts", + simulationConfig); } @Test public void sampleWithMpeghLcBlConfigChangeMulti() throws Exception { ExtractorAsserts.assertBehavior( - TsExtractor::new, "media/ts/sample_mpegh_lcbl_configchange_multi.ts", simulationConfig); + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/ts/sample_mpegh_lcbl_configchange_multi.ts", + simulationConfig); } @Test public void sampleWithMpeghBlCicp1Cont() throws Exception { ExtractorAsserts.assertBehavior( - TsExtractor::new, "media/ts/sample_mpegh_bl_cicp1_cont.ts", simulationConfig); + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/ts/sample_mpegh_bl_cicp1_cont.ts", + simulationConfig); } @Test public void sampleWithMpeghLcBlCicp1Cont() throws Exception { ExtractorAsserts.assertBehavior( - TsExtractor::new, "media/ts/sample_mpegh_lcbl_cicp1_cont.ts", simulationConfig); + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/ts/sample_mpegh_lcbl_cicp1_cont.ts", + simulationConfig); } @Test public void sampleWithMpeghBlConfigChangeCont() throws Exception { ExtractorAsserts.assertBehavior( - TsExtractor::new, "media/ts/sample_mpegh_bl_configchange_cont.ts", simulationConfig); + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/ts/sample_mpegh_bl_configchange_cont.ts", + simulationConfig); } @Test public void sampleWithMpeghLcBlConfigChangeCont() throws Exception { ExtractorAsserts.assertBehavior( - TsExtractor::new, "media/ts/sample_mpegh_lcbl_configchange_cont.ts", simulationConfig); + getExtractorFactory(subtitlesParsedDuringExtraction), + "media/ts/sample_mpegh_lcbl_configchange_cont.ts", + simulationConfig); } @Test