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 572781309b..01fc9a0beb 100644
--- a/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java
+++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java
@@ -415,7 +415,12 @@ import java.util.Locale;
*
This box contains a list of metadata keys.
*/
public static ByteBuffer keys(List mdtaMetadataEntries) {
- ByteBuffer contents = ByteBuffer.allocate(MAX_FIXED_LEAF_BOX_SIZE);
+ int totalSizeToStoreKeys = 0;
+ for (int i = 0; i < mdtaMetadataEntries.size(); i++) {
+ // Add header size to wrap each key into a "mdta" box.
+ totalSizeToStoreKeys += mdtaMetadataEntries.get(i).key.length() + BOX_HEADER_SIZE;
+ }
+ ByteBuffer contents = ByteBuffer.allocate(2 * BYTES_PER_INTEGER + totalSizeToStoreKeys);
contents.putInt(0x0); // version and flags
contents.putInt(mdtaMetadataEntries.size()); // Entry count
@@ -434,7 +439,15 @@ import java.util.Locale;
* This box contains a list of metadata values.
*/
public static ByteBuffer ilst(List mdtaMetadataEntries) {
- ByteBuffer contents = ByteBuffer.allocate(MAX_FIXED_LEAF_BOX_SIZE);
+ int totalSizeToStoreValues = 0;
+ for (int i = 0; i < mdtaMetadataEntries.size(); i++) {
+ // Add additional 16 bytes for writing metadata associated to each value.
+ // Add header size to wrap each value into a "data" box.
+ totalSizeToStoreValues +=
+ mdtaMetadataEntries.get(i).value.length + 4 * BYTES_PER_INTEGER + BOX_HEADER_SIZE;
+ }
+
+ ByteBuffer contents = ByteBuffer.allocate(totalSizeToStoreValues);
for (int i = 0; i < mdtaMetadataEntries.size(); i++) {
int keyId = i + 1;
@@ -448,7 +461,7 @@ import java.util.Locale;
valueContents.flip();
ByteBuffer valueBox = BoxUtils.wrapIntoBox("data", valueContents);
- contents.putInt(valueBox.remaining() + 8);
+ contents.putInt(valueBox.remaining() + BOX_HEADER_SIZE);
contents.putInt(keyId);
contents.put(valueBox);
}
diff --git a/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerMetadataTest.java b/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerMetadataTest.java
index e71cf8bfac..1419c892f3 100644
--- a/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerMetadataTest.java
+++ b/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerMetadataTest.java
@@ -20,6 +20,7 @@ import static androidx.media3.container.MdtaMetadataEntry.TYPE_INDICATOR_FLOAT32
import static androidx.media3.container.MdtaMetadataEntry.TYPE_INDICATOR_STRING;
import static androidx.media3.muxer.MuxerTestUtil.FAKE_VIDEO_FORMAT;
import static androidx.media3.muxer.MuxerTestUtil.XMP_SAMPLE_DATA;
+import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.media.MediaCodec.BufferInfo;
@@ -277,6 +278,27 @@ public class Mp4MuxerMetadataTest {
MuxerTestUtil.getExpectedDumpFilePath("mp4_with_string_metadata.mp4"));
}
+ @Test
+ public void writeMp4File_addManyLargeStringMetadata_doesNotThrow() throws Exception {
+ String outputFilePath = temporaryFolder.newFile().getPath();
+ Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
+
+ String metadataKey = "SomeStringKey";
+ byte[] metadataValue = Util.getUtf8Bytes("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ for (int i = 0; i < 100; i++) {
+ muxer.addMetadataEntry(
+ new MdtaMetadataEntry(metadataKey, metadataValue, TYPE_INDICATOR_STRING));
+ }
+ TrackToken token = muxer.addTrack(FAKE_VIDEO_FORMAT);
+
+ try {
+ muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
+ assertThat(sampleAndSampleInfo.first.remaining()).isEqualTo(0);
+ } finally {
+ muxer.close();
+ }
+ }
+
@Test
public void writeMp4File_addFloatMetadata_matchesExpected() throws Exception {
String outputFilePath = temporaryFolder.newFile().getPath();