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 26f4f64e85..0dbecf2657 100644
--- a/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java
+++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java
@@ -685,6 +685,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
}
/** Returns a codec specific box. */
+ @SuppressWarnings("MergeCases")
public static ByteBuffer codecSpecificBox(Format format) {
String mimeType = checkNotNull(format.sampleMimeType);
switch (mimeType) {
@@ -697,6 +698,8 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
return damrBox(/* mode= */ (short) 0x83FF); // mode set: all enabled for AMR-WB
case MimeTypes.AUDIO_OPUS:
return dOpsBox(format);
+ case MimeTypes.AUDIO_RAW:
+ return ByteBuffer.allocate(0); // No codec specific box for raw audio.
case MimeTypes.VIDEO_H263:
return d263Box(format);
case MimeTypes.VIDEO_H264:
@@ -1690,6 +1693,14 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
return "s263";
case MimeTypes.AUDIO_OPUS:
return "Opus";
+ case MimeTypes.AUDIO_RAW:
+ if (format.pcmEncoding == C.ENCODING_PCM_16BIT) {
+ return "sowt";
+ } else if (format.pcmEncoding == C.ENCODING_PCM_16BIT_BIG_ENDIAN) {
+ return "twos";
+ } else {
+ throw new IllegalArgumentException("Unsupported PCM encoding: " + format.pcmEncoding);
+ }
case MimeTypes.VIDEO_H264:
return "avc1";
case MimeTypes.VIDEO_H265:
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 0b7b053b50..613e6f3cca 100644
--- a/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Muxer.java
+++ b/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Muxer.java
@@ -58,6 +58,7 @@ import java.nio.ByteBuffer;
*
AMR-WB (Wideband AMR)
* Opus
* Vorbis
+ * Raw Audio
*
* Metadata
*
@@ -158,7 +159,8 @@ public final class FragmentedMp4Muxer implements AutoCloseable {
MimeTypes.AUDIO_AMR_NB,
MimeTypes.AUDIO_AMR_WB,
MimeTypes.AUDIO_OPUS,
- MimeTypes.AUDIO_VORBIS);
+ MimeTypes.AUDIO_VORBIS,
+ MimeTypes.AUDIO_RAW);
private final FragmentedMp4Writer fragmentedMp4Writer;
private final MetadataCollector metadataCollector;
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 8bf6215231..4973e223ec 100644
--- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java
+++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java
@@ -82,6 +82,7 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
* AMR-WB (Wideband AMR)
* Opus
* Vorbis
+ * Raw Audio
*
* Metadata
*
@@ -361,7 +362,8 @@ public final class Mp4Muxer implements AutoCloseable {
MimeTypes.AUDIO_AMR_NB,
MimeTypes.AUDIO_AMR_WB,
MimeTypes.AUDIO_OPUS,
- MimeTypes.AUDIO_VORBIS);
+ MimeTypes.AUDIO_VORBIS,
+ MimeTypes.AUDIO_RAW);
private static final String TAG = "Mp4Muxer";
diff --git a/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndParameterizedTest.java b/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndParameterizedTest.java
index 51bf44eed5..f8c94ec0ab 100644
--- a/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndParameterizedTest.java
+++ b/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndParameterizedTest.java
@@ -73,6 +73,7 @@ public class Mp4MuxerEndToEndParameterizedTest {
private static final String AMR_WB_3GP = "bbb_mono_16kHz_23.05kbps_amrwb.3gp";
private static final String OPUS_OGG = "bbb_6ch_8kHz_opus.ogg";
private static final String VORBIS_OGG = "bbb_1ch_16kHz_q10_vorbis.ogg";
+ private static final String RAW_WAV = "bbb_2ch_44kHz.wav";
@Parameters(name = "{0}")
public static ImmutableList mediaSamples() {
@@ -92,7 +93,8 @@ public class Mp4MuxerEndToEndParameterizedTest {
AMR_NB_3GP,
AMR_WB_3GP,
OPUS_OGG,
- VORBIS_OGG);
+ VORBIS_OGG,
+ RAW_WAV);
}
@Parameter public @MonotonicNonNull String inputFile;
diff --git a/libraries/test_data/src/test/assets/media/mp4/bbb_2ch_44kHz.wav b/libraries/test_data/src/test/assets/media/mp4/bbb_2ch_44kHz.wav
new file mode 100644
index 0000000000..2565aa2ada
Binary files /dev/null and b/libraries/test_data/src/test/assets/media/mp4/bbb_2ch_44kHz.wav differ
diff --git a/libraries/test_data/src/test/assets/muxerdumps/bbb_2ch_44kHz.wav.dump b/libraries/test_data/src/test/assets/muxerdumps/bbb_2ch_44kHz.wav.dump
new file mode 100644
index 0000000000..3b71cf704c
--- /dev/null
+++ b/libraries/test_data/src/test/assets/muxerdumps/bbb_2ch_44kHz.wav.dump
@@ -0,0 +1,63 @@
+seekMap:
+ isSeekable = true
+ duration = 1000000
+ getPosition(0) = [[timeUs=0, position=400052]]
+ getPosition(1) = [[timeUs=1, position=400052]]
+ getPosition(500000) = [[timeUs=500000, position=488252]]
+ getPosition(1000000) = [[timeUs=1000000, position=558812]]
+numberOfTracks = 1
+track 0:
+ total output bytes = 40
+ sample count = 10
+ track duration = 1000000
+ format 0:
+ id = 1
+ containerMimeType = audio/mp4
+ sampleMimeType = audio/raw
+ maxInputSize = 34
+ channelCount = 2
+ sampleRate = 44100
+ pcmEncoding = 2
+ language = ```
+ metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
+ sample 0:
+ time = 0
+ flags = 1
+ data = length 4, hash F0FD1
+ sample 1:
+ time = 100000
+ flags = 1
+ data = length 4, hash FFDB3408
+ sample 2:
+ time = 200000
+ flags = 1
+ data = length 4, hash 47510F
+ sample 3:
+ time = 300000
+ flags = 1
+ data = length 4, hash 35B613
+ sample 4:
+ time = 400000
+ flags = 1
+ data = length 4, hash 333956
+ sample 5:
+ time = 500000
+ flags = 1
+ data = length 4, hash FFF14579
+ sample 6:
+ time = 600000
+ flags = 1
+ data = length 4, hash 3620FE
+ sample 7:
+ time = 700000
+ flags = 1
+ data = length 4, hash C488D
+ sample 8:
+ time = 800000
+ flags = 1
+ data = length 4, hash FFF6FBD8
+ sample 9:
+ time = 900000
+ flags = 536870913
+ data = length 4, hash FFDD4C2A
+tracksEnded = true