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 cdea32d3ed..ecc171bcec 100644 --- a/libraries/muxer/src/androidTest/java/androidx/media3/muxer/Mp4MuxerEndToEndParameterizedAndroidTest.java +++ b/libraries/muxer/src/androidTest/java/androidx/media3/muxer/Mp4MuxerEndToEndParameterizedAndroidTest.java @@ -51,6 +51,7 @@ public class Mp4MuxerEndToEndParameterizedAndroidTest { private static final String H265_HDR10_MP4 = "hdr10-720p.mp4"; 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"; @Parameters(name = "{0}") public static ImmutableList mediaSamples() { @@ -60,7 +61,8 @@ public class Mp4MuxerEndToEndParameterizedAndroidTest { H264_WITH_PYRAMID_B_FRAMES_MP4, H265_HDR10_MP4, H265_WITH_METADATA_TRACK_MP4, - AV1_MP4); + AV1_MP4, + AMR_WB); } @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 6c13832f10..ca4f1087d4 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java @@ -21,6 +21,7 @@ import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.muxer.ColorUtils.MEDIAFORMAT_STANDARD_TO_PRIMARIES_AND_MATRIX; import static androidx.media3.muxer.ColorUtils.MEDIAFORMAT_TRANSFER_TO_MP4_TRANSFER; import static androidx.media3.muxer.Mp4Utils.UNSIGNED_INT_MAX_VALUE; +import static java.nio.charset.StandardCharsets.UTF_8; import android.media.MediaCodec; import android.media.MediaCodec.BufferInfo; @@ -541,6 +542,8 @@ import java.util.List; switch (mimeType) { case MimeTypes.AUDIO_AAC: return esdsBox(format); + case MimeTypes.AUDIO_AMR_WB: + return damrBox(/* mode= */ (short) 0x83FF); // mode set: all enabled case MimeTypes.VIDEO_H264: return avcCBox(format); case MimeTypes.VIDEO_H265: @@ -1317,6 +1320,8 @@ import java.util.List; switch (mimeType) { case MimeTypes.AUDIO_AAC: return "mp4a"; + case MimeTypes.AUDIO_AMR_WB: + return "sawb"; case MimeTypes.VIDEO_H264: return "avc1"; case MimeTypes.VIDEO_H265: @@ -1380,6 +1385,21 @@ import java.util.List; return BoxUtils.wrapIntoBox("esds", contents); } + /** Returns the audio damr box. */ + private static ByteBuffer damrBox(short mode) { + + ByteBuffer contents = ByteBuffer.allocate(MAX_FIXED_LEAF_BOX_SIZE); + + contents.put(" ".getBytes(UTF_8)); // vendor: 4 bytes + contents.put((byte) 0); // decoder version + contents.putShort(mode); + contents.put((byte) 0); // mode change period + contents.put((byte) 1); // frames per sample + + contents.flip(); + return BoxUtils.wrapIntoBox("damr", contents); + } + /** Packs a three-letter language code into a short, packing 3x5 bits. */ private static short languageCodeFromString(@Nullable String code) { if (code == null) { 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 44e74aac17..1b74bc912a 100644 --- a/libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java +++ b/libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java @@ -253,6 +253,27 @@ public class BoxesTest { context, dumpableBox, getExpectedDumpFilePath("audio_sample_entry_box_mp4a")); } + @Test + public void createAudioSampleEntryBox_forSawb_matchesExpected() throws Exception { + Format format = + new Format.Builder() + .setPeakBitrate(128000) + .setSampleRate(48000) + .setId(3) + .setSampleMimeType("audio/amr-wb") + .setChannelCount(2) + .setAverageBitrate(128000) + .setLanguage("```") + .setMaxInputSize(502) + .build(); + + ByteBuffer audioSampleEntryBox = Boxes.audioSampleEntry(format); + + DumpableMp4Box dumpableBox = new DumpableMp4Box(audioSampleEntryBox); + DumpFileAsserts.assertOutput( + context, dumpableBox, getExpectedDumpFilePath("audio_sample_entry_box_sawb")); + } + @Test public void createAudioSampleEntryBox_withUnknownAudioFormat_throws() { // The audio format contains an unknown MIME type. diff --git a/libraries/test_data/src/test/assets/media/mp4/bbb_mono_16kHz_23.05kbps_amrwb.3gp b/libraries/test_data/src/test/assets/media/mp4/bbb_mono_16kHz_23.05kbps_amrwb.3gp new file mode 100644 index 0000000000..6722f13270 Binary files /dev/null and b/libraries/test_data/src/test/assets/media/mp4/bbb_mono_16kHz_23.05kbps_amrwb.3gp differ diff --git a/libraries/test_data/src/test/assets/muxerdumps/audio_sample_entry_box_sawb.dump b/libraries/test_data/src/test/assets/muxerdumps/audio_sample_entry_box_sawb.dump new file mode 100644 index 0000000000..c14524ad3d --- /dev/null +++ b/libraries/test_data/src/test/assets/muxerdumps/audio_sample_entry_box_sawb.dump @@ -0,0 +1,2 @@ +sawb (53 bytes): + Data = length 45, hash 7F064471 diff --git a/libraries/test_data/src/test/assets/muxerdumps/bbb_mono_16kHz_23.05kbps_amrwb.3gp.dump b/libraries/test_data/src/test/assets/muxerdumps/bbb_mono_16kHz_23.05kbps_amrwb.3gp.dump new file mode 100644 index 0000000000..5412e4734a --- /dev/null +++ b/libraries/test_data/src/test/assets/muxerdumps/bbb_mono_16kHz_23.05kbps_amrwb.3gp.dump @@ -0,0 +1,620 @@ +seekMap: + isSeekable = true + duration = 2980000 + getPosition(0) = [[timeUs=0, position=400052]] + getPosition(1) = [[timeUs=1, position=400052]] + getPosition(1490000) = [[timeUs=1490000, position=404418]] + getPosition(2980000) = [[timeUs=2980000, position=408843]] +numberOfTracks = 1 +track 0: + total output bytes = 8850 + sample count = 150 + format 0: + id = 1 + sampleMimeType = audio/amr-wb + maxInputSize = 89 + channelCount = 1 + sampleRate = 16000 + language = und + metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000] + sample 0: + time = 0 + flags = 1 + data = length 59, hash 7CD1831D + sample 1: + time = 20000 + flags = 1 + data = length 59, hash D3770C4A + sample 2: + time = 40000 + flags = 1 + data = length 59, hash 9FE1F1DC + sample 3: + time = 60000 + flags = 1 + data = length 59, hash E01D2E94 + sample 4: + time = 80000 + flags = 1 + data = length 59, hash 9F0F1148 + sample 5: + time = 100000 + flags = 1 + data = length 59, hash 84844FC5 + sample 6: + time = 120000 + flags = 1 + data = length 59, hash E1BA9151 + sample 7: + time = 140000 + flags = 1 + data = length 59, hash 809C53C7 + sample 8: + time = 160000 + flags = 1 + data = length 59, hash CF7C326C + sample 9: + time = 180000 + flags = 1 + data = length 59, hash 9493FCC5 + sample 10: + time = 200000 + flags = 1 + data = length 59, hash 7E1C0B55 + sample 11: + time = 220000 + flags = 1 + data = length 59, hash 590CE70F + sample 12: + time = 240000 + flags = 1 + data = length 59, hash AE96EA + sample 13: + time = 260000 + flags = 1 + data = length 59, hash 101B6F1F + sample 14: + time = 280000 + flags = 1 + data = length 59, hash A7B53E8 + sample 15: + time = 300000 + flags = 1 + data = length 59, hash ADFF0186 + sample 16: + time = 320000 + flags = 1 + data = length 59, hash 62429AE2 + sample 17: + time = 340000 + flags = 1 + data = length 59, hash C0DE98C4 + sample 18: + time = 360000 + flags = 1 + data = length 59, hash 38248344 + sample 19: + time = 380000 + flags = 1 + data = length 59, hash 6FA9C78D + sample 20: + time = 400000 + flags = 1 + data = length 59, hash 70E62506 + sample 21: + time = 420000 + flags = 1 + data = length 59, hash C5852B4D + sample 22: + time = 440000 + flags = 1 + data = length 59, hash 3963835 + sample 23: + time = 460000 + flags = 1 + data = length 59, hash 12332285 + sample 24: + time = 480000 + flags = 1 + data = length 59, hash 6992A084 + sample 25: + time = 500000 + flags = 1 + data = length 59, hash 5F571FDF + sample 26: + time = 520000 + flags = 1 + data = length 59, hash 3897B324 + sample 27: + time = 540000 + flags = 1 + data = length 59, hash E55F2655 + sample 28: + time = 560000 + flags = 1 + data = length 59, hash 2EEF8B7B + sample 29: + time = 580000 + flags = 1 + data = length 59, hash 5C36A7D6 + sample 30: + time = 600000 + flags = 1 + data = length 59, hash EA18CD8F + sample 31: + time = 620000 + flags = 1 + data = length 59, hash 2C2702ED + sample 32: + time = 640000 + flags = 1 + data = length 59, hash 31438CAF + sample 33: + time = 660000 + flags = 1 + data = length 59, hash 5F8FD154 + sample 34: + time = 680000 + flags = 1 + data = length 59, hash 9D352097 + sample 35: + time = 700000 + flags = 1 + data = length 59, hash D7EA644 + sample 36: + time = 720000 + flags = 1 + data = length 59, hash 8905564B + sample 37: + time = 740000 + flags = 1 + data = length 59, hash 8FCB240C + sample 38: + time = 760000 + flags = 1 + data = length 59, hash B74A9E26 + sample 39: + time = 780000 + flags = 1 + data = length 59, hash EA79D25B + sample 40: + time = 800000 + flags = 1 + data = length 59, hash 798EC537 + sample 41: + time = 820000 + flags = 1 + data = length 59, hash F4D614DE + sample 42: + time = 840000 + flags = 1 + data = length 59, hash 9F3F5B91 + sample 43: + time = 860000 + flags = 1 + data = length 59, hash B04F058 + sample 44: + time = 880000 + flags = 1 + data = length 59, hash 8758EDC0 + sample 45: + time = 900000 + flags = 1 + data = length 59, hash DFBCAE27 + sample 46: + time = 920000 + flags = 1 + data = length 59, hash BF6AF6FD + sample 47: + time = 940000 + flags = 1 + data = length 59, hash 319B3149 + sample 48: + time = 960000 + flags = 1 + data = length 59, hash 8CA30C36 + sample 49: + time = 980000 + flags = 1 + data = length 59, hash 74CD82DA + sample 50: + time = 1000000 + flags = 1 + data = length 59, hash 8123D422 + sample 51: + time = 1020000 + flags = 1 + data = length 59, hash 697DFCCA + sample 52: + time = 1040000 + flags = 1 + data = length 59, hash 40EC178E + sample 53: + time = 1060000 + flags = 1 + data = length 59, hash C8418BE1 + sample 54: + time = 1080000 + flags = 1 + data = length 59, hash 7AC2CF25 + sample 55: + time = 1100000 + flags = 1 + data = length 59, hash BCCEFF19 + sample 56: + time = 1120000 + flags = 1 + data = length 59, hash 975A96 + sample 57: + time = 1140000 + flags = 1 + data = length 59, hash 8F187C30 + sample 58: + time = 1160000 + flags = 1 + data = length 59, hash D7797178 + sample 59: + time = 1180000 + flags = 1 + data = length 59, hash CE6E8CA6 + sample 60: + time = 1200000 + flags = 1 + data = length 59, hash 5A50B1A8 + sample 61: + time = 1220000 + flags = 1 + data = length 59, hash ADDECF9A + sample 62: + time = 1240000 + flags = 1 + data = length 59, hash C5F376FF + sample 63: + time = 1260000 + flags = 1 + data = length 59, hash 1A83DF9A + sample 64: + time = 1280000 + flags = 1 + data = length 59, hash AC44CC85 + sample 65: + time = 1300000 + flags = 1 + data = length 59, hash FE8D7E6D + sample 66: + time = 1320000 + flags = 1 + data = length 59, hash 50269ED4 + sample 67: + time = 1340000 + flags = 1 + data = length 59, hash 1CC6FC25 + sample 68: + time = 1360000 + flags = 1 + data = length 59, hash 6A4092A7 + sample 69: + time = 1380000 + flags = 1 + data = length 59, hash 68C46314 + sample 70: + time = 1400000 + flags = 1 + data = length 59, hash 5964275A + sample 71: + time = 1420000 + flags = 1 + data = length 59, hash 3130A785 + sample 72: + time = 1440000 + flags = 1 + data = length 59, hash 83F2D996 + sample 73: + time = 1460000 + flags = 1 + data = length 59, hash 515A062A + sample 74: + time = 1480000 + flags = 1 + data = length 59, hash 1D64DD8B + sample 75: + time = 1500000 + flags = 1 + data = length 59, hash 63587E3 + sample 76: + time = 1520000 + flags = 1 + data = length 59, hash 68DCB927 + sample 77: + time = 1540000 + flags = 1 + data = length 59, hash CDBA3C67 + sample 78: + time = 1560000 + flags = 1 + data = length 59, hash 12A67F90 + sample 79: + time = 1580000 + flags = 1 + data = length 59, hash 61159E34 + sample 80: + time = 1600000 + flags = 1 + data = length 59, hash 45A73FB9 + sample 81: + time = 1620000 + flags = 1 + data = length 59, hash 5A841075 + sample 82: + time = 1640000 + flags = 1 + data = length 59, hash EBCDC2CA + sample 83: + time = 1660000 + flags = 1 + data = length 59, hash F751E192 + sample 84: + time = 1680000 + flags = 1 + data = length 59, hash 5F8907C6 + sample 85: + time = 1700000 + flags = 1 + data = length 59, hash FC73C275 + sample 86: + time = 1720000 + flags = 1 + data = length 59, hash 2D4154EB + sample 87: + time = 1740000 + flags = 1 + data = length 59, hash ECA668E1 + sample 88: + time = 1760000 + flags = 1 + data = length 59, hash 8C27620A + sample 89: + time = 1780000 + flags = 1 + data = length 59, hash 8D8BC449 + sample 90: + time = 1800000 + flags = 1 + data = length 59, hash 332FDB24 + sample 91: + time = 1820000 + flags = 1 + data = length 59, hash 33C2F5AF + sample 92: + time = 1840000 + flags = 1 + data = length 59, hash E08089D5 + sample 93: + time = 1860000 + flags = 1 + data = length 59, hash A0A51B67 + sample 94: + time = 1880000 + flags = 1 + data = length 59, hash 14B84283 + sample 95: + time = 1900000 + flags = 1 + data = length 59, hash BB7F61C6 + sample 96: + time = 1920000 + flags = 1 + data = length 59, hash C870B629 + sample 97: + time = 1940000 + flags = 1 + data = length 59, hash DA557DDA + sample 98: + time = 1960000 + flags = 1 + data = length 59, hash 4E7D8042 + sample 99: + time = 1980000 + flags = 1 + data = length 59, hash 91060D4A + sample 100: + time = 2000000 + flags = 1 + data = length 59, hash 676F457A + sample 101: + time = 2020000 + flags = 1 + data = length 59, hash 8BE7A3E2 + sample 102: + time = 2040000 + flags = 1 + data = length 59, hash 8B5F28E4 + sample 103: + time = 2060000 + flags = 1 + data = length 59, hash 88288529 + sample 104: + time = 2080000 + flags = 1 + data = length 59, hash 52EF420D + sample 105: + time = 2100000 + flags = 1 + data = length 59, hash DA4DA53B + sample 106: + time = 2120000 + flags = 1 + data = length 59, hash CC3284E + sample 107: + time = 2140000 + flags = 1 + data = length 59, hash 371BB544 + sample 108: + time = 2160000 + flags = 1 + data = length 59, hash E40EE9CA + sample 109: + time = 2180000 + flags = 1 + data = length 59, hash C070679F + sample 110: + time = 2200000 + flags = 1 + data = length 59, hash F62B5F23 + sample 111: + time = 2220000 + flags = 1 + data = length 59, hash 1C265EC6 + sample 112: + time = 2240000 + flags = 1 + data = length 59, hash AF49562 + sample 113: + time = 2260000 + flags = 1 + data = length 59, hash F3E1441B + sample 114: + time = 2280000 + flags = 1 + data = length 59, hash 808BA758 + sample 115: + time = 2300000 + flags = 1 + data = length 59, hash 17931F4C + sample 116: + time = 2320000 + flags = 1 + data = length 59, hash 59EBFB2C + sample 117: + time = 2340000 + flags = 1 + data = length 59, hash 48CBD767 + sample 118: + time = 2360000 + flags = 1 + data = length 59, hash 37F0E68C + sample 119: + time = 2380000 + flags = 1 + data = length 59, hash 78D9C594 + sample 120: + time = 2400000 + flags = 1 + data = length 59, hash BC29DE1A + sample 121: + time = 2420000 + flags = 1 + data = length 59, hash 30D57FD8 + sample 122: + time = 2440000 + flags = 1 + data = length 59, hash 11EBFCA7 + sample 123: + time = 2460000 + flags = 1 + data = length 59, hash 4A073D0 + sample 124: + time = 2480000 + flags = 1 + data = length 59, hash AAE23356 + sample 125: + time = 2500000 + flags = 1 + data = length 59, hash 8CD60F62 + sample 126: + time = 2520000 + flags = 1 + data = length 59, hash 413DF0E5 + sample 127: + time = 2540000 + flags = 1 + data = length 59, hash E0A6E95 + sample 128: + time = 2560000 + flags = 1 + data = length 59, hash 3E41A79F + sample 129: + time = 2580000 + flags = 1 + data = length 59, hash 74E52392 + sample 130: + time = 2600000 + flags = 1 + data = length 59, hash 4730E2F8 + sample 131: + time = 2620000 + flags = 1 + data = length 59, hash F76800F3 + sample 132: + time = 2640000 + flags = 1 + data = length 59, hash 6EAFD6A7 + sample 133: + time = 2660000 + flags = 1 + data = length 59, hash F22E2D70 + sample 134: + time = 2680000 + flags = 1 + data = length 59, hash 70229933 + sample 135: + time = 2700000 + flags = 1 + data = length 59, hash 6FF33D1C + sample 136: + time = 2720000 + flags = 1 + data = length 59, hash 83EC00FE + sample 137: + time = 2740000 + flags = 1 + data = length 59, hash 292A2AFD + sample 138: + time = 2760000 + flags = 1 + data = length 59, hash A4ECB34E + sample 139: + time = 2780000 + flags = 1 + data = length 59, hash 7B1A9983 + sample 140: + time = 2800000 + flags = 1 + data = length 59, hash 486E9059 + sample 141: + time = 2820000 + flags = 1 + data = length 59, hash 2255918 + sample 142: + time = 2840000 + flags = 1 + data = length 59, hash 33DC9432 + sample 143: + time = 2860000 + flags = 1 + data = length 59, hash 41D0A458 + sample 144: + time = 2880000 + flags = 1 + data = length 59, hash DBA9CED6 + sample 145: + time = 2900000 + flags = 1 + data = length 59, hash FA055524 + sample 146: + time = 2920000 + flags = 1 + data = length 59, hash A4146B9E + sample 147: + time = 2940000 + flags = 1 + data = length 59, hash 2DC246A0 + sample 148: + time = 2960000 + flags = 1 + data = length 59, hash 4A8B5F2A + sample 149: + time = 2980000 + flags = 536870913 + data = length 59, hash D3223AAC +tracksEnded = true