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();