From 94271bc6fd823a3d1a6955fb800716246fbfb4c7 Mon Sep 17 00:00:00 2001 From: Rex Jin Date: Thu, 2 Jul 2026 11:26:57 -0700 Subject: [PATCH] Add E-AC-3 and Dolby Atmos (E-AC-3 JOC) support in extractor and muxer Introduce FormatSpecificTransmuxingData, a typed Metadata.Entry that carries a raw MP4 box payload (the box body, without header) so it can be reproduced when transmuxing without re-encoding. Extractor: when parsing the MP4 dec3 (EC3SpecificBox), attach its payload to Format.metadata as a FormatSpecificTransmuxingData("dec3", ...) entry instead of Format.initializationData. This keeps E-AC-3's decode path unchanged (no csd-0 is injected) while preserving the bytes for muxing. Muxer: implement the dec3 box and ec-3 fourcc in Boxes.java, reading the payload from the FormatSpecificTransmuxingData metadata entry, and add AUDIO_E_AC3 / AUDIO_E_AC3_JOC to the supported audio MIME types in Mp4Muxer and FragmentedMp4Muxer. The default Transformer can now stream-copy (transmux) E-AC-3 / Dolby Atmos audio. Tests: - BoxesTest: dec3 box wrapping for AUDIO_E_AC3 and AUDIO_E_AC3_JOC, the ec-3 fourcc, and guards for missing / empty dec3 payload. - Mp4MuxerEndToEndTest and FragmentedMp4MuxerEndToEndTest: Mp4Extractor- driven transmux of sample_eac3.mp4 and sample_eac3joc.mp4 (reads via Mp4Extractor so Format.metadata is preserved). - Extractor golden dumps regenerated to carry the dec3 metadata entry. --- .../FormatSpecificTransmuxingData.java | 72 +++++ .../media3/extractor/mp4/BoxParser.java | 19 +- .../java/androidx/media3/muxer/Boxes.java | 44 +++ .../media3/muxer/FragmentedMp4Muxer.java | 6 +- .../java/androidx/media3/muxer/Mp4Muxer.java | 4 + .../java/androidx/media3/muxer/BoxesTest.java | 87 ++++++ .../muxer/FragmentedMp4MuxerEndToEndTest.java | 64 ++++ .../media3/muxer/Mp4MuxerEndToEndTest.java | 56 ++++ .../extractordumps/mp4/sample_eac3.mp4.0.dump | 2 +- .../extractordumps/mp4/sample_eac3.mp4.1.dump | 2 +- .../extractordumps/mp4/sample_eac3.mp4.2.dump | 2 +- .../extractordumps/mp4/sample_eac3.mp4.3.dump | 2 +- .../mp4/sample_eac3.mp4.unknown_length.dump | 2 +- .../mp4/sample_eac3_fragmented.mp4.0.dump | 2 +- .../mp4/sample_eac3_fragmented.mp4.1.dump | 2 +- .../mp4/sample_eac3_fragmented.mp4.2.dump | 2 +- .../mp4/sample_eac3_fragmented.mp4.3.dump | 2 +- ...le_eac3_fragmented.mp4.unknown_length.dump | 2 +- .../mp4/sample_eac3joc.mp4.0.dump | 2 +- .../mp4/sample_eac3joc.mp4.1.dump | 2 +- .../mp4/sample_eac3joc.mp4.2.dump | 2 +- .../mp4/sample_eac3joc.mp4.3.dump | 2 +- .../sample_eac3joc.mp4.unknown_length.dump | 2 +- .../mp4/sample_eac3joc_fragmented.mp4.0.dump | 2 +- .../mp4/sample_eac3joc_fragmented.mp4.1.dump | 2 +- .../mp4/sample_eac3joc_fragmented.mp4.2.dump | 2 +- .../mp4/sample_eac3joc_fragmented.mp4.3.dump | 2 +- ...eac3joc_fragmented.mp4.unknown_length.dump | 2 +- .../muxerdumps/mp4/sample_eac3.mp4.dump | 240 +++++++++++++++ .../mp4/sample_eac3.mp4_fragmented.dump | 234 +++++++++++++++ .../muxerdumps/mp4/sample_eac3joc.mp4.dump | 280 ++++++++++++++++++ .../mp4/sample_eac3joc.mp4_fragmented.dump | 274 +++++++++++++++++ 32 files changed, 1398 insertions(+), 22 deletions(-) create mode 100644 libraries/container/src/main/java/androidx/media3/container/FormatSpecificTransmuxingData.java create mode 100644 libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3.mp4.dump create mode 100644 libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3.mp4_fragmented.dump create mode 100644 libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3joc.mp4.dump create mode 100644 libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3joc.mp4_fragmented.dump diff --git a/libraries/container/src/main/java/androidx/media3/container/FormatSpecificTransmuxingData.java b/libraries/container/src/main/java/androidx/media3/container/FormatSpecificTransmuxingData.java new file mode 100644 index 00000000000..abd1f66325f --- /dev/null +++ b/libraries/container/src/main/java/androidx/media3/container/FormatSpecificTransmuxingData.java @@ -0,0 +1,72 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.container; + +import androidx.annotation.Nullable; +import androidx.media3.common.Metadata; +import androidx.media3.common.util.UnstableApi; +import androidx.media3.common.util.Util; +import java.util.Arrays; + +/** + * Stores the raw payload of a format-specific box so it can be re-written during transmuxing (stream + * copy) without re-encoding. + * + *

Some codecs store codec configuration in a container box whose bytes are not otherwise exposed + * on {@link androidx.media3.common.Format} (for example the E-AC-3 {@code dec3} / EC3SpecificBox). + * The extractor preserves that box's payload here so a muxer can reproduce the box verbatim. + * + *

The {@link #data} holds the box body only, i.e. excluding the 4-byte size and 4-byte type + * header, and {@link #boxType} identifies which box the payload belongs to (for example {@code + * dec3}). + */ +@UnstableApi +public final class FormatSpecificTransmuxingData implements Metadata.Entry { + + /** The four-character type of the box the {@link #data} belongs to, for example {@code dec3}. */ + public final String boxType; + + /** The raw box payload (the box body, excluding its 4-byte size and 4-byte type header). */ + public final byte[] data; + + /** Creates an instance. */ + public FormatSpecificTransmuxingData(String boxType, byte[] data) { + this.boxType = boxType; + this.data = data; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + FormatSpecificTransmuxingData other = (FormatSpecificTransmuxingData) obj; + return boxType.equals(other.boxType) && Arrays.equals(data, other.data); + } + + @Override + public int hashCode() { + return 31 * boxType.hashCode() + Arrays.hashCode(data); + } + + @Override + public String toString() { + return "FormatSpecificTransmuxingData: boxType=" + boxType; + } +} diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/BoxParser.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/BoxParser.java index c994e35a441..f20e79928a7 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/BoxParser.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/BoxParser.java @@ -41,6 +41,7 @@ import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import androidx.media3.container.DolbyVisionConfig; +import androidx.media3.container.FormatSpecificTransmuxingData; import androidx.media3.container.Mp4AlternateGroupData; import androidx.media3.container.Mp4Box; import androidx.media3.container.Mp4Box.LeafBox; @@ -2251,9 +2252,25 @@ private static void parseAudioSampleEntry( out.format = Ac3Util.parseAc3AnnexFFormat(parent, Integer.toString(trackId), language, drmInitData); } else if (childAtomType == Mp4Box.TYPE_dec3) { + // Preserve the raw EC3SpecificBox body as format-specific transmuxing metadata so that + // downstream components (e.g. the muxer for stream-copy) can rewrite the dec3 box. + int childAtomBodySize = childAtomSize - Mp4Box.HEADER_SIZE; + byte[] ec3SpecificBoxPayload = new byte[childAtomBodySize]; parent.setPosition(Mp4Box.HEADER_SIZE + childPosition); - out.format = + parent.readBytes(ec3SpecificBoxPayload, /* offset= */ 0, childAtomBodySize); + parent.setPosition(Mp4Box.HEADER_SIZE + childPosition); + Format eac3Format = Ac3Util.parseEAc3AnnexFFormat(parent, Integer.toString(trackId), language, drmInitData); + Metadata.Entry dec3TransmuxingData = + new FormatSpecificTransmuxingData("dec3", ec3SpecificBoxPayload); + out.format = + eac3Format + .buildUpon() + .setMetadata( + eac3Format.metadata != null + ? eac3Format.metadata.copyWithAppendedEntries(dec3TransmuxingData) + : new Metadata(dec3TransmuxingData)) + .build(); } else if (childAtomType == Mp4Box.TYPE_dac4) { parent.setPosition(Mp4Box.HEADER_SIZE + childPosition); out.format = diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java b/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java index c60ad3a19f0..4f659b005bd 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java @@ -30,10 +30,12 @@ import androidx.media3.common.C; import androidx.media3.common.ColorInfo; import androidx.media3.common.Format; +import androidx.media3.common.Metadata; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.CodecSpecificDataUtil; import androidx.media3.common.util.Log; import androidx.media3.common.util.Util; +import androidx.media3.container.FormatSpecificTransmuxingData; import androidx.media3.container.MdtaMetadataEntry; import androidx.media3.container.Mp4LocationData; import androidx.media3.container.NalUnitUtil; @@ -781,6 +783,9 @@ public static ByteBuffer codecSpecificBox(Format format) { return dOpsBox(format); case MimeTypes.AUDIO_IAMF: return iacbBox(format); + case MimeTypes.AUDIO_E_AC3: + case MimeTypes.AUDIO_E_AC3_JOC: + return dec3Box(format); case MimeTypes.AUDIO_RAW: return ByteBuffer.allocate(0); // No codec specific box for raw audio. case MimeTypes.VIDEO_H263: @@ -1643,6 +1648,42 @@ private static ByteBuffer av1CBox(Format format) { return BoxUtils.wrapIntoBox("av1C", ByteBuffer.wrap(csd0)); } + /** + * Returns the dec3 box (EC3SpecificBox) for E-AC-3 / E-AC-3 JOC (Dolby Atmos) audio. + * + *

The dec3 box body is carried verbatim in a {@link FormatSpecificTransmuxingData} entry (with + * {@linkplain FormatSpecificTransmuxingData#boxType boxType} {@code "dec3"}) on {@link + * Format#metadata}. Stream-copy callers must populate it with the source track's raw dec3 payload; + * the MP4 extractor populates this from the source {@code dec3} box. + */ + private static ByteBuffer dec3Box(Format format) { + byte[] dec3Payload = getTransmuxingBoxPayload(format, "dec3"); + checkArgument( + dec3Payload != null && dec3Payload.length > 0, "dec3 payload not found for dec3 box."); + return BoxUtils.wrapIntoBox("dec3", ByteBuffer.wrap(dec3Payload)); + } + + /** + * Returns the raw payload of the {@link FormatSpecificTransmuxingData} entry on {@code + * format.metadata} whose {@linkplain FormatSpecificTransmuxingData#boxType boxType} matches {@code + * boxType}, or {@code null} if there is no such entry. + */ + @Nullable + private static byte[] getTransmuxingBoxPayload(Format format, String boxType) { + @Nullable Metadata metadata = format.metadata; + if (metadata == null) { + return null; + } + for (int i = 0; i < metadata.length(); i++) { + Metadata.Entry entry = metadata.get(i); + if (entry instanceof FormatSpecificTransmuxingData + && ((FormatSpecificTransmuxingData) entry).boxType.equals(boxType)) { + return ((FormatSpecificTransmuxingData) entry).data; + } + } + return null; + } + /** Returns a dvcC/dvwC/dvvC vision box which will be included in dolby vision box. */ private static ByteBuffer doviBox(int profile, byte[] csd) { checkArgument(csd.length > 0, "csd is empty for dovi box."); @@ -1847,6 +1888,9 @@ private static String codecSpecificFourcc(Format format) { return "Opus"; case MimeTypes.AUDIO_IAMF: return "iamf"; + case MimeTypes.AUDIO_E_AC3: + case MimeTypes.AUDIO_E_AC3_JOC: + return "ec-3"; case MimeTypes.AUDIO_RAW: if (format.pcmEncoding == C.ENCODING_PCM_16BIT) { return "sowt"; diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Muxer.java b/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Muxer.java index f64e1f3da53..cca077012ee 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Muxer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Muxer.java @@ -62,6 +62,8 @@ *

  • Opus *
  • Vorbis *
  • Raw Audio + *
  • E-AC-3 (Dolby Digital Plus) + *
  • E-AC-3 JOC (Dolby Atmos) * *
  • Metadata * @@ -180,7 +182,9 @@ public FragmentedMp4Muxer build() { MimeTypes.AUDIO_IAMF, MimeTypes.AUDIO_OPUS, MimeTypes.AUDIO_VORBIS, - MimeTypes.AUDIO_RAW); + MimeTypes.AUDIO_RAW, + MimeTypes.AUDIO_E_AC3, + MimeTypes.AUDIO_E_AC3_JOC); // LINT.ThenChange(Boxes.java:codec_specific_boxes) diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java index ee0421739e8..15062d108e8 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java @@ -84,6 +84,8 @@ *
  • Opus *
  • Vorbis *
  • Raw Audio + *
  • E-AC-3 (Dolby Digital Plus) + *
  • E-AC-3 JOC (Dolby Atmos) * *
  • Metadata * @@ -414,6 +416,8 @@ public Mp4Muxer build() { MimeTypes.AUDIO_OPUS, MimeTypes.AUDIO_VORBIS, MimeTypes.AUDIO_RAW, + MimeTypes.AUDIO_E_AC3, + MimeTypes.AUDIO_E_AC3_JOC, MimeTypes.AUDIO_IAMF); // LINT.ThenChange(Boxes.java:codec_specific_boxes) diff --git a/libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java b/libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java index 1f4e414f19f..a936963a8c2 100644 --- a/libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java +++ b/libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java @@ -29,8 +29,10 @@ import androidx.media3.common.C; import androidx.media3.common.ColorInfo; import androidx.media3.common.Format; +import androidx.media3.common.Metadata; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.Util; +import androidx.media3.container.FormatSpecificTransmuxingData; import androidx.media3.container.MdtaMetadataEntry; import androidx.media3.container.Mp4LocationData; import androidx.media3.muxer.FragmentedMp4Writer.SampleMetadata; @@ -403,6 +405,91 @@ public void createAudioSampleEntryBox_forVorbis_matchesExpected() throws Excepti MuxerTestUtil.getExpectedMp4DumpFilePath("audio_sample_entry_box_vorbis")); } + @Test + public void createCodecSpecificBox_forEAc3_wrapsDec3PayloadVerbatim() { + byte[] payload = new byte[] {0x0F, (byte) 0x80, 0x00}; + Format format = + FAKE_AUDIO_FORMAT + .buildUpon() + .setSampleMimeType(MimeTypes.AUDIO_E_AC3) + .setMetadata(new Metadata(new FormatSpecificTransmuxingData("dec3", payload))) + .build(); + + ByteBuffer box = Boxes.codecSpecificBox(format); + + // Box layout: 4 bytes size + 4 bytes "dec3" type + payload bytes. + assertThat(box.remaining()).isEqualTo(8 + payload.length); + byte[] typeBytes = new byte[4]; + box.position(4); + box.get(typeBytes); + assertThat(Util.fromUtf8Bytes(typeBytes)).isEqualTo("dec3"); + byte[] actual = new byte[payload.length]; + box.get(actual); + assertThat(actual).isEqualTo(payload); + } + + @Test + public void createCodecSpecificBox_forEAc3Joc_wrapsDec3PayloadVerbatim() { + byte[] payload = new byte[] {0x0F, (byte) 0x80, 0x00}; + Format format = + FAKE_AUDIO_FORMAT + .buildUpon() + .setSampleMimeType(MimeTypes.AUDIO_E_AC3_JOC) + .setMetadata(new Metadata(new FormatSpecificTransmuxingData("dec3", payload))) + .build(); + + ByteBuffer box = Boxes.codecSpecificBox(format); + + assertThat(box.remaining()).isEqualTo(8 + payload.length); + byte[] typeBytes = new byte[4]; + box.position(4); + box.get(typeBytes); + assertThat(Util.fromUtf8Bytes(typeBytes)).isEqualTo("dec3"); + byte[] actual = new byte[payload.length]; + box.get(actual); + assertThat(actual).isEqualTo(payload); + } + + @Test + public void createCodecSpecificBox_forEAc3WithNoTransmuxingData_throws() { + // No FormatSpecificTransmuxingData entry on the format's metadata. + Format format = FAKE_AUDIO_FORMAT.buildUpon().setSampleMimeType(MimeTypes.AUDIO_E_AC3).build(); + + assertThrows(IllegalArgumentException.class, () -> Boxes.codecSpecificBox(format)); + } + + @Test + public void createCodecSpecificBox_forEAc3WithEmptyPayload_throws() { + Format format = + FAKE_AUDIO_FORMAT + .buildUpon() + .setSampleMimeType(MimeTypes.AUDIO_E_AC3) + .setMetadata(new Metadata(new FormatSpecificTransmuxingData("dec3", new byte[0]))) + .build(); + + assertThrows(IllegalArgumentException.class, () -> Boxes.codecSpecificBox(format)); + } + + @Test + public void createAudioSampleEntryBox_forEAc3_usesEc3Fourcc() { + Format format = + FAKE_AUDIO_FORMAT + .buildUpon() + .setSampleMimeType(MimeTypes.AUDIO_E_AC3) + .setMetadata( + new Metadata( + new FormatSpecificTransmuxingData("dec3", new byte[] {0x0F, (byte) 0x80, 0x00}))) + .build(); + + ByteBuffer box = Boxes.audioSampleEntry(format); + + // Sample entry box layout: 4 bytes size + 4 bytes fourcc + ... + byte[] fourccBytes = new byte[4]; + box.position(4); + box.get(fourccBytes); + assertThat(Util.fromUtf8Bytes(fourccBytes)).isEqualTo("ec-3"); + } + @Test public void createAudioSampleEntryBox_withUnknownAudioFormat_throws() { // The audio format contains an unknown MIME type. diff --git a/libraries/muxer/src/test/java/androidx/media3/muxer/FragmentedMp4MuxerEndToEndTest.java b/libraries/muxer/src/test/java/androidx/media3/muxer/FragmentedMp4MuxerEndToEndTest.java index bbac80f42f0..bde4fca3f62 100644 --- a/libraries/muxer/src/test/java/androidx/media3/muxer/FragmentedMp4MuxerEndToEndTest.java +++ b/libraries/muxer/src/test/java/androidx/media3/muxer/FragmentedMp4MuxerEndToEndTest.java @@ -19,17 +19,21 @@ import static com.google.common.base.Preconditions.checkNotNull; import android.content.Context; +import androidx.media3.common.C; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; import androidx.media3.container.Mp4TimestampData; import androidx.media3.extractor.mp4.FragmentedMp4Extractor; +import androidx.media3.extractor.mp4.Mp4Extractor; import androidx.media3.extractor.text.DefaultSubtitleParserFactory; import androidx.media3.test.utils.DumpFileAsserts; import androidx.media3.test.utils.DumpableMp4Box; import androidx.media3.test.utils.FakeExtractorOutput; +import androidx.media3.test.utils.FakeTrackOutput; import androidx.media3.test.utils.TestUtil; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.collect.Iterables; import java.io.FileOutputStream; import java.nio.ByteBuffer; import org.junit.Rule; @@ -243,4 +247,64 @@ public void createFragmentedMp4File_withSomeUnknownTrack_writesAsTextMetadataTra dumpableMp4Box, MuxerTestUtil.getExpectedMp4DumpFilePath("fragmented_mp4_with_unknown_track.mp4")); } + + @Test + public void writeFragmentedMp4File_fromEAc3Input_matchesExpected() throws Exception { + transmuxToFragmentedAndAssert(/* inputFileName= */ "mp4/sample_eac3.mp4"); + } + + @Test + public void writeFragmentedMp4File_fromEAc3JocInput_matchesExpected() throws Exception { + transmuxToFragmentedAndAssert(/* inputFileName= */ "mp4/sample_eac3joc.mp4"); + } + + /** + * Stream-copies an MP4 audio track through {@link FragmentedMp4Muxer} and asserts the re-extracted + * output against the expected dump. + * + *

    The input is read with {@link Mp4Extractor} (rather than {@code MediaExtractorCompat}) so that + * {@link Format#metadata} — which carries the E-AC-3 {@code dec3} payload as a {@code + * FormatSpecificTransmuxingData} entry — is preserved and passed to the muxer. + */ + private void transmuxToFragmentedAndAssert(String inputFileName) throws Exception { + FakeExtractorOutput inputExtractorOutput = + TestUtil.extractAllSamplesFromFile(new Mp4Extractor(), context, "media/" + inputFileName); + FakeTrackOutput audioTrackOutput = + Iterables.getOnlyElement(inputExtractorOutput.getTrackOutputsForType(C.TRACK_TYPE_AUDIO)); + Format audioFormat = checkNotNull(audioTrackOutput.lastFormat); + String outputFilePath = temporaryFolder.newFile().getPath(); + + try (FragmentedMp4Muxer muxer = + new FragmentedMp4Muxer.Builder(new FileOutputStream(outputFilePath).getChannel()).build()) { + // Use fixed timestamps so the output (and its dump) is deterministic. + muxer.addMetadataEntry( + new Mp4TimestampData( + /* creationTimestampSeconds= */ 100_000_000L, + /* modificationTimestampSeconds= */ 500_000_000L)); + int trackId = muxer.addTrack(audioFormat); + for (int i = 0; i < audioTrackOutput.getSampleCount(); i++) { + byte[] sampleData = audioTrackOutput.getSampleData(i); + ByteBuffer sampleBuffer = ByteBuffer.allocateDirect(sampleData.length); + sampleBuffer.put(sampleData); + sampleBuffer.rewind(); + muxer.writeSampleData( + trackId, + sampleBuffer, + new BufferInfo( + audioTrackOutput.getSampleTimeUs(i), + sampleData.length, + audioTrackOutput.getSampleFlags(i))); + } + } + + FakeExtractorOutput outputExtractorOutput = + TestUtil.extractAllSamplesFromFilePath( + new FragmentedMp4Extractor(new DefaultSubtitleParserFactory()), + checkNotNull(outputFilePath)); + DumpFileAsserts.assertOutput( + context, + outputExtractorOutput, + MuxerTestUtil.getExpectedDumpFilePath( + MuxerTestUtil.getSubstitutedPath(inputFileName, MuxerTestUtil.MP4) + "_fragmented")); + } } diff --git a/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java b/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java index 09b81a33755..5d1cacdd8e8 100644 --- a/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java +++ b/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java @@ -43,6 +43,7 @@ import androidx.media3.test.utils.DumpFileAsserts; import androidx.media3.test.utils.DumpableMp4Box; import androidx.media3.test.utils.FakeExtractorOutput; +import androidx.media3.test.utils.FakeTrackOutput; import androidx.media3.test.utils.TestUtil; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -105,6 +106,61 @@ public void writeMp4File_withSampleAndMetadata_matchedExpectedBoxStructure() thr MuxerTestUtil.getExpectedMp4DumpFilePath("mp4_with_samples_and_metadata.mp4")); } + @Test + public void writeMp4File_fromEAc3Input_matchesExpected() throws Exception { + transmuxAndAssert(/* inputFileName= */ "sample_eac3.mp4"); + } + + @Test + public void writeMp4File_fromEAc3JocInput_matchesExpected() throws Exception { + transmuxAndAssert(/* inputFileName= */ "sample_eac3joc.mp4"); + } + + /** + * Stream-copies an MP4 audio track through {@link Mp4Muxer} and asserts the re-extracted output + * against the expected dump. + * + *

    The input is read with {@link Mp4Extractor} (rather than {@code MediaExtractorCompat}) so that + * {@link Format#metadata} — which carries the E-AC-3 {@code dec3} payload as a {@code + * FormatSpecificTransmuxingData} entry — is preserved and passed to the muxer. + */ + private void transmuxAndAssert(String inputFileName) throws Exception { + FakeExtractorOutput inputExtractorOutput = + TestUtil.extractAllSamplesFromFile( + new Mp4Extractor(), context, "media/mp4/" + inputFileName); + FakeTrackOutput audioTrackOutput = + Iterables.getOnlyElement(inputExtractorOutput.getTrackOutputsForType(C.TRACK_TYPE_AUDIO)); + Format audioFormat = checkNotNull(audioTrackOutput.lastFormat); + String outputFilePath = temporaryFolder.newFile().getPath(); + + try (Mp4Muxer muxer = new Mp4Muxer.Builder(SeekableMuxerOutput.of(outputFilePath)).build()) { + // Use fixed timestamps so the output (and its dump) is deterministic. + muxer.addMetadataEntry( + new Mp4TimestampData( + /* creationTimestampSeconds= */ 100_000_000L, + /* modificationTimestampSeconds= */ 500_000_000L)); + int trackId = muxer.addTrack(audioFormat); + for (int i = 0; i < audioTrackOutput.getSampleCount(); i++) { + byte[] sampleData = audioTrackOutput.getSampleData(i); + ByteBuffer sampleBuffer = ByteBuffer.allocateDirect(sampleData.length); + sampleBuffer.put(sampleData); + sampleBuffer.rewind(); + muxer.writeSampleData( + trackId, + sampleBuffer, + new BufferInfo( + audioTrackOutput.getSampleTimeUs(i), + sampleData.length, + audioTrackOutput.getSampleFlags(i))); + } + } + + FakeExtractorOutput outputExtractorOutput = + TestUtil.extractAllSamplesFromFilePath(new Mp4Extractor(), outputFilePath); + DumpFileAsserts.assertOutput( + context, outputExtractorOutput, MuxerTestUtil.getExpectedMp4DumpFilePath(inputFileName)); + } + @Test public void createMp4File_addTrackAndMetadataButNoSamples_createsEmptyFile() throws Exception { String outputFilePath = temporaryFolder.newFile().getPath(); diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.0.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.0.dump index 18be5b4841c..06e031c84f5 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.0.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.0.dump @@ -20,7 +20,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000] sample 0: time = 0 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.1.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.1.dump index 5553d6aacc5..36641422acb 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.1.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.1.dump @@ -20,7 +20,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000] sample 0: time = 576000 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.2.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.2.dump index 70065cd5b70..6baa2a18e81 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.2.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.2.dump @@ -20,7 +20,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000] sample 0: time = 1152000 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.3.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.3.dump index debb18e351b..43ab7b846d3 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.3.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.3.dump @@ -20,7 +20,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000] sample 0: time = 1696000 flags = 536870913 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.unknown_length.dump index 18be5b4841c..06e031c84f5 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.unknown_length.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3.mp4.unknown_length.dump @@ -20,7 +20,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000] sample 0: time = 0 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.0.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.0.dump index d667a5b6bd3..b928c6b22d1 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.0.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.0.dump @@ -18,7 +18,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000] sample 0: time = 0 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.1.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.1.dump index b2fe4e15817..b81b87b36f5 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.1.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.1.dump @@ -18,7 +18,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000] sample 0: time = 576000 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.2.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.2.dump index 9b79dddd878..a68a51bd1ec 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.2.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.2.dump @@ -18,7 +18,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000] sample 0: time = 1152000 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.3.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.3.dump index e05add6a366..029b35752cc 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.3.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.3.dump @@ -18,7 +18,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000] sample 0: time = 1696000 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.unknown_length.dump index d667a5b6bd3..b928c6b22d1 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.unknown_length.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3_fragmented.mp4.unknown_length.dump @@ -18,7 +18,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000] sample 0: time = 0 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.0.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.0.dump index 722eacf3946..3d07a8f8b0f 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.0.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.0.dump @@ -20,7 +20,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000] sample 0: time = 0 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.1.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.1.dump index 0c4e9324c26..a823a6612f3 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.1.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.1.dump @@ -20,7 +20,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000] sample 0: time = 672000 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.2.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.2.dump index 8391f766571..5027d42ea81 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.2.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.2.dump @@ -20,7 +20,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000] sample 0: time = 1344000 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.3.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.3.dump index adac41cf3e0..96322d89825 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.3.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.3.dump @@ -20,7 +20,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000] sample 0: time = 2016000 flags = 536870913 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.unknown_length.dump index 722eacf3946..3d07a8f8b0f 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.unknown_length.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc.mp4.unknown_length.dump @@ -20,7 +20,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000] sample 0: time = 0 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.0.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.0.dump index 37ca0ee67ae..4e4c63626d3 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.0.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.0.dump @@ -18,7 +18,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000] sample 0: time = 0 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.1.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.1.dump index a95b4b0033a..f7505977d74 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.1.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.1.dump @@ -18,7 +18,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000] sample 0: time = 672000 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.2.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.2.dump index 3f5ff4f7381..f95bb5ccffd 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.2.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.2.dump @@ -18,7 +18,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000] sample 0: time = 1344000 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.3.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.3.dump index e4372fd8373..93c7963349b 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.3.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.3.dump @@ -18,7 +18,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000] sample 0: time = 2016000 flags = 1 diff --git a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.unknown_length.dump b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.unknown_length.dump index 37ca0ee67ae..4e4c63626d3 100644 --- a/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.unknown_length.dump +++ b/libraries/test_data/src/test/assets/extractordumps/mp4/sample_eac3joc_fragmented.mp4.unknown_length.dump @@ -18,7 +18,7 @@ track 0: channelCount = 6 sampleRate = 48000 language = und - metadata = entries=[Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000] + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000] sample 0: time = 0 flags = 1 diff --git a/libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3.mp4.dump b/libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3.mp4.dump new file mode 100644 index 00000000000..b42bd69b386 --- /dev/null +++ b/libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3.mp4.dump @@ -0,0 +1,240 @@ +seekMap: + isSeekable = true + duration = 1728000 + getPosition(0) = [[timeUs=0, position=400052]] + getPosition(1) = [[timeUs=1, position=400052]] + getPosition(864000) = [[timeUs=864000, position=508052]] + getPosition(1728000) = [[timeUs=1728000, position=612052]] +numberOfTracks = 1 +track 0: + total output bytes = 216000 + sample count = 54 + track duration = 1728000 + format 0: + averageBitrate = 1000000 + peakBitrate = 1000000 + id = 1 + containerMimeType = audio/mp4 + sampleMimeType = audio/eac3 + maxInputSize = 4030 + channelCount = 6 + sampleRate = 48000 + language = und + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] + sample 0: + time = 0 + flags = 1 + data = length 4000, hash BAEAFB2A + sample 1: + time = 32000 + flags = 1 + data = length 4000, hash E3C5EBF0 + sample 2: + time = 64000 + flags = 1 + data = length 4000, hash 32E0F957 + sample 3: + time = 96000 + flags = 1 + data = length 4000, hash 5354CC5D + sample 4: + time = 128000 + flags = 1 + data = length 4000, hash FF834906 + sample 5: + time = 160000 + flags = 1 + data = length 4000, hash 6F571E61 + sample 6: + time = 192000 + flags = 1 + data = length 4000, hash 5C931F6B + sample 7: + time = 224000 + flags = 1 + data = length 4000, hash B1FB2E57 + sample 8: + time = 256000 + flags = 1 + data = length 4000, hash C71240EB + sample 9: + time = 288000 + flags = 1 + data = length 4000, hash C3E302EE + sample 10: + time = 320000 + flags = 1 + data = length 4000, hash 7994C27B + sample 11: + time = 352000 + flags = 1 + data = length 4000, hash 1ED4E6F3 + sample 12: + time = 384000 + flags = 1 + data = length 4000, hash 1D5E6AAC + sample 13: + time = 416000 + flags = 1 + data = length 4000, hash 30058F51 + sample 14: + time = 448000 + flags = 1 + data = length 4000, hash 15DD0E4A + sample 15: + time = 480000 + flags = 1 + data = length 4000, hash 37BE7C15 + sample 16: + time = 512000 + flags = 1 + data = length 4000, hash 7CFDD34B + sample 17: + time = 544000 + flags = 1 + data = length 4000, hash 27F20D29 + sample 18: + time = 576000 + flags = 1 + data = length 4000, hash 6F565894 + sample 19: + time = 608000 + flags = 1 + data = length 4000, hash A6F07C4A + sample 20: + time = 640000 + flags = 1 + data = length 4000, hash 3A0CA15C + sample 21: + time = 672000 + flags = 1 + data = length 4000, hash DB365414 + sample 22: + time = 704000 + flags = 1 + data = length 4000, hash 31E08469 + sample 23: + time = 736000 + flags = 1 + data = length 4000, hash 315F5C28 + sample 24: + time = 768000 + flags = 1 + data = length 4000, hash CC65DF80 + sample 25: + time = 800000 + flags = 1 + data = length 4000, hash 503FB64C + sample 26: + time = 832000 + flags = 1 + data = length 4000, hash 817CF735 + sample 27: + time = 864000 + flags = 1 + data = length 4000, hash 37391ADA + sample 28: + time = 896000 + flags = 1 + data = length 4000, hash 37391ADA + sample 29: + time = 928000 + flags = 1 + data = length 4000, hash 64DBF751 + sample 30: + time = 960000 + flags = 1 + data = length 4000, hash 81AE828E + sample 31: + time = 992000 + flags = 1 + data = length 4000, hash 767D6C98 + sample 32: + time = 1024000 + flags = 1 + data = length 4000, hash A5F6D4E + sample 33: + time = 1056000 + flags = 1 + data = length 4000, hash EABC6B0D + sample 34: + time = 1088000 + flags = 1 + data = length 4000, hash F47EF742 + sample 35: + time = 1120000 + flags = 1 + data = length 4000, hash 9B2549DA + sample 36: + time = 1152000 + flags = 1 + data = length 4000, hash A12733C9 + sample 37: + time = 1184000 + flags = 1 + data = length 4000, hash 95F62E99 + sample 38: + time = 1216000 + flags = 1 + data = length 4000, hash A4D858 + sample 39: + time = 1248000 + flags = 1 + data = length 4000, hash A4D858 + sample 40: + time = 1280000 + flags = 1 + data = length 4000, hash 22C1A129 + sample 41: + time = 1312000 + flags = 1 + data = length 4000, hash 2C51E4A1 + sample 42: + time = 1344000 + flags = 1 + data = length 4000, hash 3782E8BB + sample 43: + time = 1376000 + flags = 1 + data = length 4000, hash 2C51E4A1 + sample 44: + time = 1408000 + flags = 1 + data = length 4000, hash BDB3D129 + sample 45: + time = 1440000 + flags = 1 + data = length 4000, hash F642A55 + sample 46: + time = 1472000 + flags = 1 + data = length 4000, hash 32F259F4 + sample 47: + time = 1504000 + flags = 1 + data = length 4000, hash 4C987B7C + sample 48: + time = 1536000 + flags = 1 + data = length 4000, hash 57C98E1C + sample 49: + time = 1568000 + flags = 1 + data = length 4000, hash 4C987B7C + sample 50: + time = 1600000 + flags = 1 + data = length 4000, hash 4C987B7C + sample 51: + time = 1632000 + flags = 1 + data = length 4000, hash 4C987B7C + sample 52: + time = 1664000 + flags = 1 + data = length 4000, hash 4C987B7C + sample 53: + time = 1696000 + flags = 536870913 + data = length 4000, hash 4C987B7C +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3.mp4_fragmented.dump b/libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3.mp4_fragmented.dump new file mode 100644 index 00000000000..66ee04d96f2 --- /dev/null +++ b/libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3.mp4_fragmented.dump @@ -0,0 +1,234 @@ +seekMap: + isSeekable = false + duration = UNSET TIME + getPosition(0) = [[timeUs=0, position=569]] +numberOfTracks = 1 +track 0: + total output bytes = 216000 + sample count = 54 + format 0: + peakBitrate = 1000000 + id = 1 + containerMimeType = audio/mp4 + sampleMimeType = audio/eac3 + channelCount = 6 + sampleRate = 48000 + language = und + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] + sample 0: + time = 0 + flags = 1 + data = length 4000, hash BAEAFB2A + sample 1: + time = 32000 + flags = 1 + data = length 4000, hash E3C5EBF0 + sample 2: + time = 64000 + flags = 1 + data = length 4000, hash 32E0F957 + sample 3: + time = 96000 + flags = 1 + data = length 4000, hash 5354CC5D + sample 4: + time = 128000 + flags = 1 + data = length 4000, hash FF834906 + sample 5: + time = 160000 + flags = 1 + data = length 4000, hash 6F571E61 + sample 6: + time = 192000 + flags = 1 + data = length 4000, hash 5C931F6B + sample 7: + time = 224000 + flags = 1 + data = length 4000, hash B1FB2E57 + sample 8: + time = 256000 + flags = 1 + data = length 4000, hash C71240EB + sample 9: + time = 288000 + flags = 1 + data = length 4000, hash C3E302EE + sample 10: + time = 320000 + flags = 1 + data = length 4000, hash 7994C27B + sample 11: + time = 352000 + flags = 1 + data = length 4000, hash 1ED4E6F3 + sample 12: + time = 384000 + flags = 1 + data = length 4000, hash 1D5E6AAC + sample 13: + time = 416000 + flags = 1 + data = length 4000, hash 30058F51 + sample 14: + time = 448000 + flags = 1 + data = length 4000, hash 15DD0E4A + sample 15: + time = 480000 + flags = 1 + data = length 4000, hash 37BE7C15 + sample 16: + time = 512000 + flags = 1 + data = length 4000, hash 7CFDD34B + sample 17: + time = 544000 + flags = 1 + data = length 4000, hash 27F20D29 + sample 18: + time = 576000 + flags = 1 + data = length 4000, hash 6F565894 + sample 19: + time = 608000 + flags = 1 + data = length 4000, hash A6F07C4A + sample 20: + time = 640000 + flags = 1 + data = length 4000, hash 3A0CA15C + sample 21: + time = 672000 + flags = 1 + data = length 4000, hash DB365414 + sample 22: + time = 704000 + flags = 1 + data = length 4000, hash 31E08469 + sample 23: + time = 736000 + flags = 1 + data = length 4000, hash 315F5C28 + sample 24: + time = 768000 + flags = 1 + data = length 4000, hash CC65DF80 + sample 25: + time = 800000 + flags = 1 + data = length 4000, hash 503FB64C + sample 26: + time = 832000 + flags = 1 + data = length 4000, hash 817CF735 + sample 27: + time = 864000 + flags = 1 + data = length 4000, hash 37391ADA + sample 28: + time = 896000 + flags = 1 + data = length 4000, hash 37391ADA + sample 29: + time = 928000 + flags = 1 + data = length 4000, hash 64DBF751 + sample 30: + time = 960000 + flags = 1 + data = length 4000, hash 81AE828E + sample 31: + time = 992000 + flags = 1 + data = length 4000, hash 767D6C98 + sample 32: + time = 1024000 + flags = 1 + data = length 4000, hash A5F6D4E + sample 33: + time = 1056000 + flags = 1 + data = length 4000, hash EABC6B0D + sample 34: + time = 1088000 + flags = 1 + data = length 4000, hash F47EF742 + sample 35: + time = 1120000 + flags = 1 + data = length 4000, hash 9B2549DA + sample 36: + time = 1152000 + flags = 1 + data = length 4000, hash A12733C9 + sample 37: + time = 1184000 + flags = 1 + data = length 4000, hash 95F62E99 + sample 38: + time = 1216000 + flags = 1 + data = length 4000, hash A4D858 + sample 39: + time = 1248000 + flags = 1 + data = length 4000, hash A4D858 + sample 40: + time = 1280000 + flags = 1 + data = length 4000, hash 22C1A129 + sample 41: + time = 1312000 + flags = 1 + data = length 4000, hash 2C51E4A1 + sample 42: + time = 1344000 + flags = 1 + data = length 4000, hash 3782E8BB + sample 43: + time = 1376000 + flags = 1 + data = length 4000, hash 2C51E4A1 + sample 44: + time = 1408000 + flags = 1 + data = length 4000, hash BDB3D129 + sample 45: + time = 1440000 + flags = 1 + data = length 4000, hash F642A55 + sample 46: + time = 1472000 + flags = 1 + data = length 4000, hash 32F259F4 + sample 47: + time = 1504000 + flags = 1 + data = length 4000, hash 4C987B7C + sample 48: + time = 1536000 + flags = 1 + data = length 4000, hash 57C98E1C + sample 49: + time = 1568000 + flags = 1 + data = length 4000, hash 4C987B7C + sample 50: + time = 1600000 + flags = 1 + data = length 4000, hash 4C987B7C + sample 51: + time = 1632000 + flags = 1 + data = length 4000, hash 4C987B7C + sample 52: + time = 1664000 + flags = 1 + data = length 4000, hash 4C987B7C + sample 53: + time = 1696000 + flags = 1 + data = length 4000, hash 4C987B7C +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3joc.mp4.dump b/libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3joc.mp4.dump new file mode 100644 index 00000000000..55db1ad6067 --- /dev/null +++ b/libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3joc.mp4.dump @@ -0,0 +1,280 @@ +seekMap: + isSeekable = true + duration = 2048000 + getPosition(0) = [[timeUs=0, position=400052]] + getPosition(1) = [[timeUs=1, position=400052]] + getPosition(1024000) = [[timeUs=1024000, position=481972]] + getPosition(2048000) = [[timeUs=2048000, position=561332]] +numberOfTracks = 1 +track 0: + total output bytes = 163840 + sample count = 64 + track duration = 2048000 + format 0: + averageBitrate = 640000 + peakBitrate = 640000 + id = 1 + containerMimeType = audio/mp4 + sampleMimeType = audio/eac3-joc + maxInputSize = 2590 + channelCount = 6 + sampleRate = 48000 + language = und + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] + sample 0: + time = 0 + flags = 1 + data = length 2560, hash 882594AD + sample 1: + time = 32000 + flags = 1 + data = length 2560, hash 41EC8B22 + sample 2: + time = 64000 + flags = 1 + data = length 2560, hash 67E6EFD4 + sample 3: + time = 96000 + flags = 1 + data = length 2560, hash A7E66AFD + sample 4: + time = 128000 + flags = 1 + data = length 2560, hash 3924116 + sample 5: + time = 160000 + flags = 1 + data = length 2560, hash 64DCE40B + sample 6: + time = 192000 + flags = 1 + data = length 2560, hash F2E0DA64 + sample 7: + time = 224000 + flags = 1 + data = length 2560, hash C156258B + sample 8: + time = 256000 + flags = 1 + data = length 2560, hash D8DBDCDE + sample 9: + time = 288000 + flags = 1 + data = length 2560, hash C11B2F25 + sample 10: + time = 320000 + flags = 1 + data = length 2560, hash B3C5612 + sample 11: + time = 352000 + flags = 1 + data = length 2560, hash A94B15D0 + sample 12: + time = 384000 + flags = 1 + data = length 2560, hash 12E4E306 + sample 13: + time = 416000 + flags = 1 + data = length 2560, hash 11CB959F + sample 14: + time = 448000 + flags = 1 + data = length 2560, hash B6433844 + sample 15: + time = 480000 + flags = 1 + data = length 2560, hash EA6DEB89 + sample 16: + time = 512000 + flags = 1 + data = length 2560, hash 6D65CBD9 + sample 17: + time = 544000 + flags = 1 + data = length 2560, hash A5D635C5 + sample 18: + time = 576000 + flags = 1 + data = length 2560, hash 992E36AB + sample 19: + time = 608000 + flags = 1 + data = length 2560, hash 1EC4E5AF + sample 20: + time = 640000 + flags = 1 + data = length 2560, hash DCFEB7D2 + sample 21: + time = 672000 + flags = 1 + data = length 2560, hash 45EFC639 + sample 22: + time = 704000 + flags = 1 + data = length 2560, hash F598673 + sample 23: + time = 736000 + flags = 1 + data = length 2560, hash 89E4E5EC + sample 24: + time = 768000 + flags = 1 + data = length 2560, hash FBE2532B + sample 25: + time = 800000 + flags = 1 + data = length 2560, hash 9CE5F83B + sample 26: + time = 832000 + flags = 1 + data = length 2560, hash 6ED49E2C + sample 27: + time = 864000 + flags = 1 + data = length 2560, hash BC52F8F3 + sample 28: + time = 896000 + flags = 1 + data = length 2560, hash 759203E2 + sample 29: + time = 928000 + flags = 1 + data = length 2560, hash D5D31AE9 + sample 30: + time = 960000 + flags = 1 + data = length 2560, hash 640A24ED + sample 31: + time = 992000 + flags = 1 + data = length 2560, hash 19B52B8B + sample 32: + time = 1024000 + flags = 1 + data = length 2560, hash 5DA977C3 + sample 33: + time = 1056000 + flags = 1 + data = length 2560, hash 982879DD + sample 34: + time = 1088000 + flags = 1 + data = length 2560, hash A7656B9C + sample 35: + time = 1120000 + flags = 1 + data = length 2560, hash 445CCC67 + sample 36: + time = 1152000 + flags = 1 + data = length 2560, hash ACD5CB5C + sample 37: + time = 1184000 + flags = 1 + data = length 2560, hash 175BBF26 + sample 38: + time = 1216000 + flags = 1 + data = length 2560, hash DBCBEB0 + sample 39: + time = 1248000 + flags = 1 + data = length 2560, hash DA39D991 + sample 40: + time = 1280000 + flags = 1 + data = length 2560, hash F08CC8E2 + sample 41: + time = 1312000 + flags = 1 + data = length 2560, hash 6B0842D7 + sample 42: + time = 1344000 + flags = 1 + data = length 2560, hash 9FE87594 + sample 43: + time = 1376000 + flags = 1 + data = length 2560, hash 8E62CE19 + sample 44: + time = 1408000 + flags = 1 + data = length 2560, hash 5FDC4084 + sample 45: + time = 1440000 + flags = 1 + data = length 2560, hash C32DAEE1 + sample 46: + time = 1472000 + flags = 1 + data = length 2560, hash BBEFB568 + sample 47: + time = 1504000 + flags = 1 + data = length 2560, hash 20504279 + sample 48: + time = 1536000 + flags = 1 + data = length 2560, hash 3B8192D2 + sample 49: + time = 1568000 + flags = 1 + data = length 2560, hash 4206B48 + sample 50: + time = 1600000 + flags = 1 + data = length 2560, hash B195AB53 + sample 51: + time = 1632000 + flags = 1 + data = length 2560, hash 3AA8E25F + sample 52: + time = 1664000 + flags = 1 + data = length 2560, hash BC227D7B + sample 53: + time = 1696000 + flags = 1 + data = length 2560, hash 6A34F7EA + sample 54: + time = 1728000 + flags = 1 + data = length 2560, hash F1E731C4 + sample 55: + time = 1760000 + flags = 1 + data = length 2560, hash 9CC406 + sample 56: + time = 1792000 + flags = 1 + data = length 2560, hash A1532233 + sample 57: + time = 1824000 + flags = 1 + data = length 2560, hash 98E49039 + sample 58: + time = 1856000 + flags = 1 + data = length 2560, hash 3F8B6DC0 + sample 59: + time = 1888000 + flags = 1 + data = length 2560, hash 4E7BF79F + sample 60: + time = 1920000 + flags = 1 + data = length 2560, hash 6DD6F2D7 + sample 61: + time = 1952000 + flags = 1 + data = length 2560, hash A05C0EC2 + sample 62: + time = 1984000 + flags = 1 + data = length 2560, hash 10C62F30 + sample 63: + time = 2016000 + flags = 536870913 + data = length 2560, hash EE4F848A +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3joc.mp4_fragmented.dump b/libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3joc.mp4_fragmented.dump new file mode 100644 index 00000000000..5b0867221d7 --- /dev/null +++ b/libraries/test_data/src/test/assets/muxerdumps/mp4/sample_eac3joc.mp4_fragmented.dump @@ -0,0 +1,274 @@ +seekMap: + isSeekable = false + duration = UNSET TIME + getPosition(0) = [[timeUs=0, position=571]] +numberOfTracks = 1 +track 0: + total output bytes = 163840 + sample count = 64 + format 0: + peakBitrate = 640000 + id = 1 + containerMimeType = audio/mp4 + sampleMimeType = audio/eac3-joc + channelCount = 6 + sampleRate = 48000 + language = und + metadata = entries=[FormatSpecificTransmuxingData: boxType=dec3, Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] + sample 0: + time = 0 + flags = 1 + data = length 2560, hash 882594AD + sample 1: + time = 32000 + flags = 1 + data = length 2560, hash 41EC8B22 + sample 2: + time = 64000 + flags = 1 + data = length 2560, hash 67E6EFD4 + sample 3: + time = 96000 + flags = 1 + data = length 2560, hash A7E66AFD + sample 4: + time = 128000 + flags = 1 + data = length 2560, hash 3924116 + sample 5: + time = 160000 + flags = 1 + data = length 2560, hash 64DCE40B + sample 6: + time = 192000 + flags = 1 + data = length 2560, hash F2E0DA64 + sample 7: + time = 224000 + flags = 1 + data = length 2560, hash C156258B + sample 8: + time = 256000 + flags = 1 + data = length 2560, hash D8DBDCDE + sample 9: + time = 288000 + flags = 1 + data = length 2560, hash C11B2F25 + sample 10: + time = 320000 + flags = 1 + data = length 2560, hash B3C5612 + sample 11: + time = 352000 + flags = 1 + data = length 2560, hash A94B15D0 + sample 12: + time = 384000 + flags = 1 + data = length 2560, hash 12E4E306 + sample 13: + time = 416000 + flags = 1 + data = length 2560, hash 11CB959F + sample 14: + time = 448000 + flags = 1 + data = length 2560, hash B6433844 + sample 15: + time = 480000 + flags = 1 + data = length 2560, hash EA6DEB89 + sample 16: + time = 512000 + flags = 1 + data = length 2560, hash 6D65CBD9 + sample 17: + time = 544000 + flags = 1 + data = length 2560, hash A5D635C5 + sample 18: + time = 576000 + flags = 1 + data = length 2560, hash 992E36AB + sample 19: + time = 608000 + flags = 1 + data = length 2560, hash 1EC4E5AF + sample 20: + time = 640000 + flags = 1 + data = length 2560, hash DCFEB7D2 + sample 21: + time = 672000 + flags = 1 + data = length 2560, hash 45EFC639 + sample 22: + time = 704000 + flags = 1 + data = length 2560, hash F598673 + sample 23: + time = 736000 + flags = 1 + data = length 2560, hash 89E4E5EC + sample 24: + time = 768000 + flags = 1 + data = length 2560, hash FBE2532B + sample 25: + time = 800000 + flags = 1 + data = length 2560, hash 9CE5F83B + sample 26: + time = 832000 + flags = 1 + data = length 2560, hash 6ED49E2C + sample 27: + time = 864000 + flags = 1 + data = length 2560, hash BC52F8F3 + sample 28: + time = 896000 + flags = 1 + data = length 2560, hash 759203E2 + sample 29: + time = 928000 + flags = 1 + data = length 2560, hash D5D31AE9 + sample 30: + time = 960000 + flags = 1 + data = length 2560, hash 640A24ED + sample 31: + time = 992000 + flags = 1 + data = length 2560, hash 19B52B8B + sample 32: + time = 1024000 + flags = 1 + data = length 2560, hash 5DA977C3 + sample 33: + time = 1056000 + flags = 1 + data = length 2560, hash 982879DD + sample 34: + time = 1088000 + flags = 1 + data = length 2560, hash A7656B9C + sample 35: + time = 1120000 + flags = 1 + data = length 2560, hash 445CCC67 + sample 36: + time = 1152000 + flags = 1 + data = length 2560, hash ACD5CB5C + sample 37: + time = 1184000 + flags = 1 + data = length 2560, hash 175BBF26 + sample 38: + time = 1216000 + flags = 1 + data = length 2560, hash DBCBEB0 + sample 39: + time = 1248000 + flags = 1 + data = length 2560, hash DA39D991 + sample 40: + time = 1280000 + flags = 1 + data = length 2560, hash F08CC8E2 + sample 41: + time = 1312000 + flags = 1 + data = length 2560, hash 6B0842D7 + sample 42: + time = 1344000 + flags = 1 + data = length 2560, hash 9FE87594 + sample 43: + time = 1376000 + flags = 1 + data = length 2560, hash 8E62CE19 + sample 44: + time = 1408000 + flags = 1 + data = length 2560, hash 5FDC4084 + sample 45: + time = 1440000 + flags = 1 + data = length 2560, hash C32DAEE1 + sample 46: + time = 1472000 + flags = 1 + data = length 2560, hash BBEFB568 + sample 47: + time = 1504000 + flags = 1 + data = length 2560, hash 20504279 + sample 48: + time = 1536000 + flags = 1 + data = length 2560, hash 3B8192D2 + sample 49: + time = 1568000 + flags = 1 + data = length 2560, hash 4206B48 + sample 50: + time = 1600000 + flags = 1 + data = length 2560, hash B195AB53 + sample 51: + time = 1632000 + flags = 1 + data = length 2560, hash 3AA8E25F + sample 52: + time = 1664000 + flags = 1 + data = length 2560, hash BC227D7B + sample 53: + time = 1696000 + flags = 1 + data = length 2560, hash 6A34F7EA + sample 54: + time = 1728000 + flags = 1 + data = length 2560, hash F1E731C4 + sample 55: + time = 1760000 + flags = 1 + data = length 2560, hash 9CC406 + sample 56: + time = 1792000 + flags = 1 + data = length 2560, hash A1532233 + sample 57: + time = 1824000 + flags = 1 + data = length 2560, hash 98E49039 + sample 58: + time = 1856000 + flags = 1 + data = length 2560, hash 3F8B6DC0 + sample 59: + time = 1888000 + flags = 1 + data = length 2560, hash 4E7BF79F + sample 60: + time = 1920000 + flags = 1 + data = length 2560, hash 6DD6F2D7 + sample 61: + time = 1952000 + flags = 1 + data = length 2560, hash A05C0EC2 + sample 62: + time = 1984000 + flags = 1 + data = length 2560, hash 10C62F30 + sample 63: + time = 2016000 + flags = 1 + data = length 2560, hash EE4F848A +tracksEnded = true