From d097fe11d06369297e69d97aeb5242a043149431 Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Mon, 19 Feb 2024 05:28:28 -0800 Subject: [PATCH] Add E2E test for Mp4Muxer to verify overall box structure There is no super test which covers whole MP4 structure. In all the E2E test, it verified against `ExtractorOutput` which would remain same if there are changes in the box structure. PiperOrigin-RevId: 608310006 --- .../media3/muxer/Mp4MuxerEndToEndTest.java | 70 ++++++++++++++----- .../media3/muxer/Mp4MuxerMetadataTest.java | 4 +- .../androidx/media3/muxer/MuxerTestUtil.java | 2 + .../mp4_with_samples_and_metadata.mp4.dump | 44 ++++++++++++ 4 files changed, 100 insertions(+), 20 deletions(-) create mode 100644 libraries/test_data/src/test/assets/muxerdumps/mp4_with_samples_and_metadata.mp4.dump 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 43029dc257..ae69dd8aa8 100644 --- a/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java +++ b/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java @@ -16,9 +16,12 @@ package androidx.media3.muxer; import static androidx.media3.muxer.MuxerTestUtil.FAKE_VIDEO_FORMAT; +import static androidx.media3.muxer.MuxerTestUtil.XMP_SAMPLE_DATA; +import static androidx.media3.muxer.MuxerTestUtil.getFakeSampleAndSampleInfo; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; +import android.content.Context; import android.media.MediaCodec.BufferInfo; import android.util.Pair; import androidx.media3.container.Mp4TimestampData; @@ -43,6 +46,8 @@ import org.junit.runner.RunWith; public class Mp4MuxerEndToEndTest { @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); + private final Context context = ApplicationProvider.getApplicationContext(); + @Test public void createMp4File_addTrackAndMetadataButNoSamples_createsEmptyFile() throws IOException { String outputFilePath = temporaryFolder.newFile().getPath(); @@ -69,13 +74,13 @@ public class Mp4MuxerEndToEndTest { /* creationTimestampSeconds= */ 100_000_000L, /* modificationTimestampSeconds= */ 500_000_000L)); Pair track1Sample1 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L); Pair track1Sample2 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 200L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 200L); Pair track2Sample1 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L); Pair track2Sample2 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 300L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 300L); try { TrackToken track1 = mp4Muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); @@ -98,7 +103,7 @@ public class Mp4MuxerEndToEndTest { FakeExtractorOutput fakeExtractorOutput = TestUtil.extractAllSamplesFromFilePath(new Mp4Extractor(), outputFilePath); DumpFileAsserts.assertOutput( - ApplicationProvider.getApplicationContext(), + context, fakeExtractorOutput, MuxerTestUtil.getExpectedDumpFilePath("mp4_with_same_tracks_offset.mp4")); } @@ -112,13 +117,13 @@ public class Mp4MuxerEndToEndTest { /* creationTimestampSeconds= */ 100_000_000L, /* modificationTimestampSeconds= */ 500_000_000L)); Pair track1Sample1 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L); Pair track1Sample2 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L); Pair track2Sample1 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L); Pair track2Sample2 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 200L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 200L); try { TrackToken track1 = mp4Muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); @@ -137,7 +142,7 @@ public class Mp4MuxerEndToEndTest { FakeExtractorOutput fakeExtractorOutput = TestUtil.extractAllSamplesFromFilePath(new Mp4Extractor(), outputFilePath); DumpFileAsserts.assertOutput( - ApplicationProvider.getApplicationContext(), + context, fakeExtractorOutput, MuxerTestUtil.getExpectedDumpFilePath("mp4_with_different_tracks_offset.mp4")); } @@ -147,11 +152,11 @@ public class Mp4MuxerEndToEndTest { String outputFilePath = temporaryFolder.newFile().getPath(); Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Pair track1Sample1 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L); Pair track1Sample2 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 2000L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 2000L); Pair track1Sample3 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 1000L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 1000L); try { TrackToken track1 = mp4Muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); mp4Muxer.writeSampleData(track1, track1Sample1.first, track1Sample1.second); @@ -174,9 +179,9 @@ public class Mp4MuxerEndToEndTest { /* creationTimestampSeconds= */ 100_000_000L, /* modificationTimestampSeconds= */ 500_000_000L)); Pair track1Sample1 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L); Pair track1Sample2 = - MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L); + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L); try { TrackToken track1 = mp4Muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); @@ -194,8 +199,39 @@ public class Mp4MuxerEndToEndTest { new DumpableMp4Box(ByteBuffer.wrap(TestUtil.getByteArrayFromFilePath(outputFilePath))); // Output contains only one trak box. DumpFileAsserts.assertOutput( - ApplicationProvider.getApplicationContext(), + context, dumpableBox, MuxerTestUtil.getExpectedDumpFilePath("mp4_without_empty_track.mp4")); + } + + @Test + public void writeMp4File_withSampleAndMetadata_matchedExpectedBoxStructure() throws Exception { + String outputFilePath = temporaryFolder.newFile().getPath(); + Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); + Pair sampleAndSampleInfo = + getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L); + byte[] xmpBytes = TestUtil.getByteArray(context, XMP_SAMPLE_DATA); + ByteBuffer xmp = ByteBuffer.wrap(xmpBytes); + + try { + muxer.setOrientation(90); + muxer.setLocation(/* latitude= */ 33.0f, /* longitude= */ -120f); + muxer.setCaptureFps(120.0f); + muxer.setTimestampData( + new Mp4TimestampData( + /* creationTimestampSeconds= */ 1_000_000L, + /* modificationTimestampSeconds= */ 5_000_000L)); + muxer.addMetadata("StringKey1", "StringValue"); + muxer.addXmp(xmp); + TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); + muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second); + } finally { + muxer.close(); + } + + DumpableMp4Box dumpableBox = + new DumpableMp4Box(ByteBuffer.wrap(TestUtil.getByteArrayFromFilePath(outputFilePath))); + DumpFileAsserts.assertOutput( + context, dumpableBox, - MuxerTestUtil.getExpectedDumpFilePath("mp4_without_empty_track.mp4")); + MuxerTestUtil.getExpectedDumpFilePath("mp4_with_samples_and_metadata.mp4")); } } diff --git a/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerMetadataTest.java b/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerMetadataTest.java index ed5a1e7906..a766352542 100644 --- a/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerMetadataTest.java +++ b/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerMetadataTest.java @@ -16,6 +16,7 @@ package androidx.media3.muxer; import static androidx.media3.muxer.MuxerTestUtil.FAKE_VIDEO_FORMAT; +import static androidx.media3.muxer.MuxerTestUtil.XMP_SAMPLE_DATA; import android.content.Context; import android.media.MediaCodec.BufferInfo; @@ -41,9 +42,6 @@ import org.junit.runner.RunWith; public class Mp4MuxerMetadataTest { @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); - // Input files. - private static final String XMP_SAMPLE_DATA = "media/xmp/sample_datetime_xmp.xmp"; - private final Context context = ApplicationProvider.getApplicationContext(); private final Pair sampleAndSampleInfo = MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L); diff --git a/libraries/muxer/src/test/java/androidx/media3/muxer/MuxerTestUtil.java b/libraries/muxer/src/test/java/androidx/media3/muxer/MuxerTestUtil.java index 00055f21c6..7123b4dbab 100644 --- a/libraries/muxer/src/test/java/androidx/media3/muxer/MuxerTestUtil.java +++ b/libraries/muxer/src/test/java/androidx/media3/muxer/MuxerTestUtil.java @@ -46,6 +46,8 @@ import java.nio.ByteBuffer; .setInitializationData(ImmutableList.of(FAKE_CSD_0, FAKE_CSD_1)) .build(); + public static final String XMP_SAMPLE_DATA = "media/xmp/sample_datetime_xmp.xmp"; + private static final byte[] FAKE_H264_SAMPLE = BaseEncoding.base16() .decode( diff --git a/libraries/test_data/src/test/assets/muxerdumps/mp4_with_samples_and_metadata.mp4.dump b/libraries/test_data/src/test/assets/muxerdumps/mp4_with_samples_and_metadata.mp4.dump new file mode 100644 index 0000000000..39988d902a --- /dev/null +++ b/libraries/test_data/src/test/assets/muxerdumps/mp4_with_samples_and_metadata.mp4.dump @@ -0,0 +1,44 @@ +ftyp (28 bytes): + Data = length 20, hash EF896440 +mdat (71 bytes): + Data = length 55, hash 6B19F4A7 +moov (873 bytes): + mvhd (108 bytes): + Data = length 100, hash 2613A5C + udta (38 bytes): + Data = length 30, hash 25372BB9 + meta (177 bytes): + hdlr (33 bytes): + Data = length 25, hash C39D0F5B + keys (65 bytes): + Data = length 57, hash E97C2148 + ilst (71 bytes): + Data = length 63, hash D81D174 + trak (542 bytes): + tkhd (92 bytes): + Data = length 84, hash 4ACEE52E + mdia (442 bytes): + mdhd (32 bytes): + Data = length 24, hash 41542D81 + hdlr (44 bytes): + Data = length 36, hash A0852FF2 + minf (358 bytes): + vmhd (20 bytes): + Data = length 12, hash EE830681 + dinf (36 bytes): + Data = length 28, hash D535436B + stbl (294 bytes): + stsd (166 bytes): + Data = length 158, hash 11532063 + stts (24 bytes): + Data = length 16, hash E4FC6483 + stsz (24 bytes): + Data = length 16, hash 50B7F5B9 + stsc (28 bytes): + Data = length 20, hash 8F6E8285 + co64 (24 bytes): + Data = length 16, hash E4EE4D2E + stss (20 bytes): + Data = length 12, hash EE911E03 +uuid (2853 bytes): + Data = length 2845, hash 52AF0F9D