Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2251,9 +2251,18 @@ private static void parseAudioSampleEntry(
out.format =
Ac3Util.parseAc3AnnexFFormat(parent, Integer.toString(trackId), language, drmInitData);
} else if (childAtomType == Mp4Box.TYPE_dec3) {
// Preserve the raw EC3SpecificBox body as csd-0 so that downstream components (e.g. the
// muxer for stream-copy) can rewrite the dec3 box.
int childAtomBodySize = childAtomSize - Mp4Box.HEADER_SIZE;
byte[] initializationDataBytes = new byte[childAtomBodySize];
parent.setPosition(Mp4Box.HEADER_SIZE + childPosition);
parent.readBytes(initializationDataBytes, /* offset= */ 0, childAtomBodySize);
parent.setPosition(Mp4Box.HEADER_SIZE + childPosition);
out.format =
Ac3Util.parseEAc3AnnexFFormat(parent, Integer.toString(trackId), language, drmInitData);
Ac3Util.parseEAc3AnnexFFormat(parent, Integer.toString(trackId), language, drmInitData)
.buildUpon()
.setInitializationData(ImmutableList.of(initializationDataBytes))
.build();
} else if (childAtomType == Mp4Box.TYPE_dac4) {
parent.setPosition(Mp4Box.HEADER_SIZE + childPosition);
out.format =
Expand Down
21 changes: 21 additions & 0 deletions libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,9 @@ public static ByteBuffer codecSpecificBox(Format format) {
return dOpsBox(format);
case MimeTypes.AUDIO_IAMF:
return iacbBox(format);
case MimeTypes.AUDIO_E_AC3:
case MimeTypes.AUDIO_E_AC3_JOC:
return dec3Box(format);
case MimeTypes.AUDIO_RAW:
return ByteBuffer.allocate(0); // No codec specific box for raw audio.
case MimeTypes.VIDEO_H263:
Expand Down Expand Up @@ -1643,6 +1646,21 @@ private static ByteBuffer av1CBox(Format format) {
return BoxUtils.wrapIntoBox("av1C", ByteBuffer.wrap(csd0));
}

/**
* Returns the dec3 box (EC3SpecificBox) for E-AC-3 / E-AC-3 JOC (Dolby Atmos) audio.
*
* <p>The dec3 box body is the codec-specific configuration, carried verbatim in csd-0. Stream-copy
* callers must populate {@code format.initializationData} with the source track's raw dec3 payload;
* the MP4 extractor populates this from the source {@code dec3} box.
*/
private static ByteBuffer dec3Box(Format format) {
checkArgument(
!format.initializationData.isEmpty(), "csd-0 (dec3 payload) not found for dec3 box.");
byte[] csd0 = format.initializationData.get(0);
checkArgument(csd0.length > 0, "csd-0 is empty for dec3 box.");
return BoxUtils.wrapIntoBox("dec3", ByteBuffer.wrap(csd0));
}

/** Returns a dvcC/dvwC/dvvC vision box which will be included in dolby vision box. */
private static ByteBuffer doviBox(int profile, byte[] csd) {
checkArgument(csd.length > 0, "csd is empty for dovi box.");
Expand Down Expand Up @@ -1847,6 +1865,9 @@ private static String codecSpecificFourcc(Format format) {
return "Opus";
case MimeTypes.AUDIO_IAMF:
return "iamf";
case MimeTypes.AUDIO_E_AC3:
case MimeTypes.AUDIO_E_AC3_JOC:
return "ec-3";
case MimeTypes.AUDIO_RAW:
if (format.pcmEncoding == C.ENCODING_PCM_16BIT) {
return "sowt";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
* <li>Opus
* <li>Vorbis
* <li>Raw Audio
* <li>E-AC-3 (Dolby Digital Plus)
* <li>E-AC-3 JOC (Dolby Atmos)
* </ul>
* <li>Metadata
* </ul>
Expand Down Expand Up @@ -180,7 +182,9 @@ public FragmentedMp4Muxer build() {
MimeTypes.AUDIO_IAMF,
MimeTypes.AUDIO_OPUS,
MimeTypes.AUDIO_VORBIS,
MimeTypes.AUDIO_RAW);
MimeTypes.AUDIO_RAW,
MimeTypes.AUDIO_E_AC3,
MimeTypes.AUDIO_E_AC3_JOC);

// LINT.ThenChange(Boxes.java:codec_specific_boxes)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
* <li>Opus
* <li>Vorbis
* <li>Raw Audio
* <li>E-AC-3 (Dolby Digital Plus)
* <li>E-AC-3 JOC (Dolby Atmos)
* </ul>
* <li>Metadata
* </ul>
Expand Down Expand Up @@ -414,6 +416,8 @@ public Mp4Muxer build() {
MimeTypes.AUDIO_OPUS,
MimeTypes.AUDIO_VORBIS,
MimeTypes.AUDIO_RAW,
MimeTypes.AUDIO_E_AC3,
MimeTypes.AUDIO_E_AC3_JOC,
MimeTypes.AUDIO_IAMF);

// LINT.ThenChange(Boxes.java:codec_specific_boxes)
Expand Down
87 changes: 87 additions & 0 deletions libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,93 @@ public void createAudioSampleEntryBox_forVorbis_matchesExpected() throws Excepti
MuxerTestUtil.getExpectedMp4DumpFilePath("audio_sample_entry_box_vorbis"));
}

@Test
public void createCodecSpecificBox_forEAc3_wrapsDec3PayloadVerbatim() {
byte[] payload = new byte[] {0x0F, (byte) 0x80, 0x00};
Format format =
FAKE_AUDIO_FORMAT
.buildUpon()
.setSampleMimeType(MimeTypes.AUDIO_E_AC3)
.setInitializationData(ImmutableList.of(payload))
.build();

ByteBuffer box = Boxes.codecSpecificBox(format);

// Box layout: 4 bytes size + 4 bytes "dec3" type + payload bytes.
assertThat(box.remaining()).isEqualTo(8 + payload.length);
byte[] typeBytes = new byte[4];
box.position(4);
box.get(typeBytes);
assertThat(new String(typeBytes)).isEqualTo("dec3");
byte[] actual = new byte[payload.length];
box.get(actual);
assertThat(actual).isEqualTo(payload);
}

@Test
public void createCodecSpecificBox_forEAc3Joc_wrapsDec3PayloadVerbatim() {
byte[] payload = new byte[] {0x0F, (byte) 0x80, 0x00};
Format format =
FAKE_AUDIO_FORMAT
.buildUpon()
.setSampleMimeType(MimeTypes.AUDIO_E_AC3_JOC)
.setInitializationData(ImmutableList.of(payload))
.build();

ByteBuffer box = Boxes.codecSpecificBox(format);

assertThat(box.remaining()).isEqualTo(8 + payload.length);
byte[] typeBytes = new byte[4];
box.position(4);
box.get(typeBytes);
assertThat(new String(typeBytes)).isEqualTo("dec3");
byte[] actual = new byte[payload.length];
box.get(actual);
assertThat(actual).isEqualTo(payload);
}

@Test
public void createCodecSpecificBox_forEAc3WithNoCsd_throws() {
Format format =
FAKE_AUDIO_FORMAT
.buildUpon()
.setSampleMimeType(MimeTypes.AUDIO_E_AC3)
.setInitializationData(ImmutableList.of())
.build();

assertThrows(IllegalArgumentException.class, () -> Boxes.codecSpecificBox(format));
}

@Test
public void createCodecSpecificBox_forEAc3WithEmptyCsd_throws() {
Format format =
FAKE_AUDIO_FORMAT
.buildUpon()
.setSampleMimeType(MimeTypes.AUDIO_E_AC3)
.setInitializationData(ImmutableList.of(new byte[0]))
.build();

assertThrows(IllegalArgumentException.class, () -> Boxes.codecSpecificBox(format));
}

@Test
public void createAudioSampleEntryBox_forEAc3_usesEc3Fourcc() {
Format format =
FAKE_AUDIO_FORMAT
.buildUpon()
.setSampleMimeType(MimeTypes.AUDIO_E_AC3)
.setInitializationData(ImmutableList.of(new byte[] {0x0F, (byte) 0x80, 0x00}))
.build();

ByteBuffer box = Boxes.audioSampleEntry(format);

// Sample entry box layout: 4 bytes size + 4 bytes fourcc + ...
byte[] fourccBytes = new byte[4];
box.position(4);
box.get(fourccBytes);
assertThat(Util.fromUtf8Bytes(fourccBytes)).isEqualTo("ec-3");
}

@Test
public void createAudioSampleEntryBox_withUnknownAudioFormat_throws() {
// The audio format contains an unknown MIME type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public class Mp4MuxerEndToEndParameterizedTest {
private static final String OPUS_OGG = "ogg/bbb_6ch_8kHz_opus.ogg";
private static final String VORBIS_OGG = "ogg/bbb_1ch_16kHz_q10_vorbis.ogg";
private static final String RAW_WAV = "wav/bbb_2ch_44kHz.wav";
private static final String EAC3_MP4 = "mp4/sample_eac3.mp4";
private static final String EAC3_JOC_MP4 = "mp4/sample_eac3joc.mp4";

public static final String MEDIA_ASSET_DIRECTORY = "asset:///media/";

Expand All @@ -91,7 +93,9 @@ public static ImmutableList<String> mediaSamples() {
IAMF_OPUS_MP4,
OPUS_OGG,
VORBIS_OGG,
RAW_WAV);
RAW_WAV,
EAC3_MP4,
EAC3_JOC_MP4);
}

@Parameter public @MonotonicNonNull String inputFile;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000]
initializationData:
data = length 5, hash 38742EF
sample 0:
time = 0
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000]
initializationData:
data = length 5, hash 38742EF
sample 0:
time = 576000
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000]
initializationData:
data = length 5, hash 38742EF
sample 0:
time = 1152000
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000]
initializationData:
data = length 5, hash 38742EF
sample 0:
time = 1696000
flags = 536870913
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086526, modification time=3662086526, timescale=48000]
initializationData:
data = length 5, hash 38742EF
sample 0:
time = 0
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000]
initializationData:
data = length 5, hash 38742EF
sample 0:
time = 0
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000]
initializationData:
data = length 5, hash 38742EF
sample 0:
time = 576000
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000]
initializationData:
data = length 5, hash 38742EF
sample 0:
time = 1152000
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000]
initializationData:
data = length 5, hash 38742EF
sample 0:
time = 1696000
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086719, modification time=3662086719, timescale=1000]
initializationData:
data = length 5, hash 38742EF
sample 0:
time = 0
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000]
initializationData:
data = length 7, hash 8BA78FD3
sample 0:
time = 0
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000]
initializationData:
data = length 7, hash 8BA78FD3
sample 0:
time = 672000
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000]
initializationData:
data = length 7, hash 8BA78FD3
sample 0:
time = 1344000
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000]
initializationData:
data = length 7, hash 8BA78FD3
sample 0:
time = 2016000
flags = 536870913
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086940, modification time=3662086940, timescale=48000]
initializationData:
data = length 7, hash 8BA78FD3
sample 0:
time = 0
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000]
initializationData:
data = length 7, hash 8BA78FD3
sample 0:
time = 0
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000]
initializationData:
data = length 7, hash 8BA78FD3
sample 0:
time = 672000
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000]
initializationData:
data = length 7, hash 8BA78FD3
sample 0:
time = 1344000
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000]
initializationData:
data = length 7, hash 8BA78FD3
sample 0:
time = 2016000
flags = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ track 0:
sampleRate = 48000
language = und
metadata = entries=[Mp4Timestamp: creation time=3662086977, modification time=3662086977, timescale=1000]
initializationData:
data = length 7, hash 8BA78FD3
sample 0:
time = 0
flags = 1
Expand Down
Loading