diff --git a/libraries/muxer/src/androidTest/java/androidx/media3/muxer/Mp4MuxerEndToEndParameterizedAndroidTest.java b/libraries/muxer/src/androidTest/java/androidx/media3/muxer/Mp4MuxerEndToEndParameterizedAndroidTest.java index ecc171bcec..6ef69d9ec0 100644 --- a/libraries/muxer/src/androidTest/java/androidx/media3/muxer/Mp4MuxerEndToEndParameterizedAndroidTest.java +++ b/libraries/muxer/src/androidTest/java/androidx/media3/muxer/Mp4MuxerEndToEndParameterizedAndroidTest.java @@ -52,6 +52,7 @@ public class Mp4MuxerEndToEndParameterizedAndroidTest { private static final String H265_WITH_METADATA_TRACK_MP4 = "h265_with_metadata_track.mp4"; private static final String AV1_MP4 = "sample_av1.mp4"; private static final String AMR_WB = "bbb_mono_16kHz_23.05kbps_amrwb.3gp"; + private static final String MPEG4_MP4 = "bbb_176x144_192kbps_15fps_mpeg4.mp4"; @Parameters(name = "{0}") public static ImmutableList mediaSamples() { @@ -62,7 +63,8 @@ public class Mp4MuxerEndToEndParameterizedAndroidTest { H265_HDR10_MP4, H265_WITH_METADATA_TRACK_MP4, AV1_MP4, - AMR_WB); + AMR_WB, + MPEG4_MP4); } @Parameter public @MonotonicNonNull String inputFile; 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 9bc10cb89c..fe570c41a2 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java @@ -542,6 +542,7 @@ import java.util.List; String mimeType = checkNotNull(format.sampleMimeType); switch (mimeType) { case MimeTypes.AUDIO_AAC: + case MimeTypes.VIDEO_MP4V: return esdsBox(format); case MimeTypes.AUDIO_AMR_WB: return damrBox(/* mode= */ (short) 0x83FF); // mode set: all enabled @@ -1329,6 +1330,8 @@ import java.util.List; return "hvc1"; case MimeTypes.VIDEO_AV1: return "av01"; + case MimeTypes.VIDEO_MP4V: + return "mp4v-es"; default: throw new IllegalArgumentException("Unsupported format: " + mimeType); } @@ -1344,6 +1347,7 @@ import java.util.List; ByteBuffer csd0ByteBuffer = ByteBuffer.wrap(csd0); int peakBitrate = format.peakBitrate; int averageBitrate = format.averageBitrate; + boolean isVideo = MimeTypes.isVideo(format.sampleMimeType); int csd0Size = csd0ByteBuffer.limit(); ByteBuffer dsiSizeBuffer = getSizeBuffer(csd0Size); @@ -1358,17 +1362,18 @@ import java.util.List; contents.put(esdSizeBuffer); contents.putShort((short) 0x0000); // First 16 bits of ES_ID. - contents.put((byte) 0x00); // Last 8 bits of ES_ID. + contents.put(isVideo ? (byte) 0x1f : (byte) 0x00); // Last 8 bits of ES_ID. contents.put((byte) 0x04); // DecoderConfigDescrTag contents.put(dcdSizeBuffer); - contents.put((byte) 0x40); // objectTypeIndication + contents.put(isVideo ? (byte) 0x20 : (byte) 0x40); // objectTypeIndication // streamType (6 bits) | upStream (1 bit) | reserved = 1 (1 bit) - contents.put((byte) ((0x05 << 2) | 0x01)); // streamType AudioStream + contents.put((byte) ((isVideo ? (0x04 << 2) : (0x05 << 2)) | 0x01)); // streamType - contents.putShort((short) 0x03); // First 16 bits of buffer size (0x300). - contents.put((byte) 0x00); // Last 8 bits of buffer size (0x300). + int size = isVideo ? 0x017700 : 0x000300; + contents.putShort((short) ((size >> 8) & 0xFFFF)); // First 16 bits of buffer size. + contents.put((byte) 0x00); // Last 8 bits of buffer size. contents.putInt(peakBitrate != Format.NO_VALUE ? peakBitrate : 0); contents.putInt(averageBitrate != Format.NO_VALUE ? averageBitrate : 0); 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 1b74bc912a..7c8ce48960 100644 --- a/libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java +++ b/libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java @@ -30,6 +30,7 @@ import android.media.MediaCodec; import androidx.media3.common.C; import androidx.media3.common.ColorInfo; import androidx.media3.common.Format; +import androidx.media3.common.MimeTypes; import androidx.media3.common.util.Util; import androidx.media3.container.MdtaMetadataEntry; import androidx.media3.container.Mp4LocationData; @@ -396,6 +397,35 @@ public class BoxesTest { context, dumpableBox, MuxerTestUtil.getExpectedDumpFilePath("video_sample_entry_box_av1")); } + @Test + public void createVideoSampleEntryBox_forMPEG4_matchesExpected() throws IOException { + Format format = + new Format.Builder() + .setId(1) + .setSampleMimeType(MimeTypes.VIDEO_MP4V) + .setAverageBitrate(9200) + .setPeakBitrate(9200) + .setLanguage("und") + .setWidth(10) + .setMaxInputSize(49) + .setFrameRate(25) + .setHeight(12) + .setInitializationData( + ImmutableList.of( + BaseEncoding.base16() + .decode( + "000001B001000001B58913000001000000012000C48D88007D0584121443000001B24C61766335382E35342E313030"))) + .build(); + + ByteBuffer videoSampleEntryBox = Boxes.videoSampleEntry(format); + + DumpableMp4Box dumpableBox = new DumpableMp4Box(videoSampleEntryBox); + DumpFileAsserts.assertOutput( + context, + dumpableBox, + MuxerTestUtil.getExpectedDumpFilePath("video_sample_entry_box_mpeg4")); + } + @Test public void createVideoSampleEntryBox_withUnknownVideoFormat_throws() { // The video format contains an unknown MIME type. diff --git a/libraries/test_data/src/test/assets/media/mp4/bbb_176x144_192kbps_15fps_mpeg4.mp4 b/libraries/test_data/src/test/assets/media/mp4/bbb_176x144_192kbps_15fps_mpeg4.mp4 new file mode 100644 index 0000000000..53d0c09b14 Binary files /dev/null and b/libraries/test_data/src/test/assets/media/mp4/bbb_176x144_192kbps_15fps_mpeg4.mp4 differ diff --git a/libraries/test_data/src/test/assets/muxerdumps/bbb_176x144_192kbps_15fps_mpeg4.mp4.dump b/libraries/test_data/src/test/assets/muxerdumps/bbb_176x144_192kbps_15fps_mpeg4.mp4.dump new file mode 100644 index 0000000000..708f85f29b --- /dev/null +++ b/libraries/test_data/src/test/assets/muxerdumps/bbb_176x144_192kbps_15fps_mpeg4.mp4.dump @@ -0,0 +1,87 @@ +seekMap: + isSeekable = true + duration = 933300 + getPosition(0) = [[timeUs=0, position=400052]] + getPosition(1) = [[timeUs=0, position=400052]] + getPosition(466650) = [[timeUs=0, position=400052]] + getPosition(933300) = [[timeUs=0, position=400052]] +numberOfTracks = 1 +track 0: + total output bytes = 45694 + sample count = 15 + format 0: + averageBitrate = 365552 + peakBitrate = 365552 + id = 1 + sampleMimeType = video/mp4v-es + maxInputSize = 9256 + width = 176 + height = 144 + frameRate = 16.072002 + colorInfo: + lumaBitdepth = 8 + chromaBitdepth = 8 + metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] + initializationData: + data = length 47, hash DC4DD041 + sample 0: + time = 0 + flags = 1 + data = length 9226, hash 7C636E69 + sample 1: + time = 66655 + flags = 0 + data = length 2560, hash C7A69F47 + sample 2: + time = 133322 + flags = 0 + data = length 1720, hash 32B683D + sample 3: + time = 200000 + flags = 0 + data = length 2684, hash A77F20B0 + sample 4: + time = 266655 + flags = 0 + data = length 2966, hash CDB6D380 + sample 5: + time = 333322 + flags = 0 + data = length 3160, hash 5A1455FB + sample 6: + time = 400000 + flags = 0 + data = length 3128, hash 8F3EA8F2 + sample 7: + time = 466655 + flags = 0 + data = length 3164, hash 40A33F45 + sample 8: + time = 533322 + flags = 0 + data = length 3309, hash 4ACFEBD1 + sample 9: + time = 600000 + flags = 0 + data = length 3332, hash 7902F5A6 + sample 10: + time = 666655 + flags = 0 + data = length 3082, hash 57E4EBB9 + sample 11: + time = 733322 + flags = 0 + data = length 3210, hash FDA3CC0F + sample 12: + time = 800000 + flags = 0 + data = length 981, hash FA98FC90 + sample 13: + time = 866655 + flags = 0 + data = length 1611, hash 3F58EEE9 + sample 14: + time = 933322 + flags = 536870912 + data = length 1561, hash 24FA5A54 +tracksEnded = true diff --git a/libraries/test_data/src/test/assets/muxerdumps/video_sample_entry_box_mpeg4.dump b/libraries/test_data/src/test/assets/muxerdumps/video_sample_entry_box_mpeg4.dump new file mode 100644 index 0000000000..99f75b2061 --- /dev/null +++ b/libraries/test_data/src/test/assets/muxerdumps/video_sample_entry_box_mpeg4.dump @@ -0,0 +1,2 @@ +mp4v (186 bytes): + Data = length 178, hash DE771E7E