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
This commit is contained in:
sheenachhabra 2024-02-19 05:28:28 -08:00 committed by Copybara-Service
parent 2cd0cb30cd
commit d097fe11d0
4 changed files with 100 additions and 20 deletions

View file

@ -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<ByteBuffer, BufferInfo> track1Sample1 =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L);
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L);
Pair<ByteBuffer, BufferInfo> track1Sample2 =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 200L);
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 200L);
Pair<ByteBuffer, BufferInfo> track2Sample1 =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L);
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L);
Pair<ByteBuffer, BufferInfo> 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<ByteBuffer, BufferInfo> track1Sample1 =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L);
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L);
Pair<ByteBuffer, BufferInfo> track1Sample2 =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L);
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L);
Pair<ByteBuffer, BufferInfo> track2Sample1 =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L);
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L);
Pair<ByteBuffer, BufferInfo> 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<ByteBuffer, BufferInfo> track1Sample1 =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L);
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L);
Pair<ByteBuffer, BufferInfo> track1Sample2 =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 2000L);
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 2000L);
Pair<ByteBuffer, BufferInfo> 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<ByteBuffer, BufferInfo> track1Sample1 =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L);
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L);
Pair<ByteBuffer, BufferInfo> 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<ByteBuffer, BufferInfo> 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"));
}
}

View file

@ -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<ByteBuffer, BufferInfo> sampleAndSampleInfo =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L);

View file

@ -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(

View file

@ -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