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 90edc5622f..f741a51c56 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java @@ -32,6 +32,7 @@ import androidx.media3.common.ColorInfo; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.Util; +import androidx.media3.container.MdtaMetadataEntry; import androidx.media3.container.Mp4LocationData; import androidx.media3.container.NalUnitUtil; import androidx.media3.muxer.FragmentedMp4Writer.SampleMetadata; @@ -395,14 +396,13 @@ import java.util.Locale; * *

This box contains a list of metadata keys. */ - public static ByteBuffer keys(List keyNames) { - // This should be an adaptive size here; we don't yet care since it's usually small. + public static ByteBuffer keys(List mdtaMetadataEntries) { ByteBuffer contents = ByteBuffer.allocate(Mp4Utils.MAX_FIXED_LEAF_BOX_SIZE); - contents.putInt(0x0); // version and flags. - contents.putInt(keyNames.size()); // num of entries + contents.putInt(0x0); // version and flags + contents.putInt(mdtaMetadataEntries.size()); // Entry count - for (int i = 0; i < keyNames.size(); i++) { - ByteBuffer keyNameBuffer = ByteBuffer.wrap(Util.getUtf8Bytes(keyNames.get(i))); + for (int i = 0; i < mdtaMetadataEntries.size(); i++) { + ByteBuffer keyNameBuffer = ByteBuffer.wrap(Util.getUtf8Bytes(mdtaMetadataEntries.get(i).key)); contents.put(BoxUtils.wrapIntoBox("mdta", keyNameBuffer)); } @@ -415,31 +415,18 @@ import java.util.Locale; * *

This box contains a list of metadata values. */ - public static ByteBuffer ilst(List values) { - // This should be an adaptive size here; we don't yet care since it's usually small. + public static ByteBuffer ilst(List mdtaMetadataEntries) { ByteBuffer contents = ByteBuffer.allocate(Mp4Utils.MAX_FIXED_LEAF_BOX_SIZE); - for (int i = 0; i < values.size(); i++) { + for (int i = 0; i < mdtaMetadataEntries.size(); i++) { int keyId = i + 1; - Object value = values.get(i); + MdtaMetadataEntry currentMdtaMetadataEntry = mdtaMetadataEntries.get(i); - ByteBuffer valueContents; - if (value instanceof String) { - String valueString = (String) value; - byte[] valueBytes = Util.getUtf8Bytes(valueString); - valueContents = ByteBuffer.allocate(valueBytes.length + 8); - valueContents.putInt(1); // type code for UTF-8 string - valueContents.putInt(0); // default country / language - valueContents.put(valueBytes); - - } else if (value instanceof Float) { - valueContents = ByteBuffer.allocate(12); - valueContents.putInt(23); // float32 - valueContents.putInt(0); // language / country - valueContents.putFloat((float) value); - } else { - throw new IllegalArgumentException("Unknown metadata type: " + value.getClass()); - } + ByteBuffer valueContents = + ByteBuffer.allocate(2 * BYTES_PER_INTEGER + currentMdtaMetadataEntry.value.length); + valueContents.putInt(currentMdtaMetadataEntry.typeIndicator); + valueContents.putInt(currentMdtaMetadataEntry.localeIndicator); + valueContents.put(currentMdtaMetadataEntry.value); valueContents.flip(); ByteBuffer valueBox = BoxUtils.wrapIntoBox("data", valueContents); diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/MetadataCollector.java b/libraries/muxer/src/main/java/androidx/media3/muxer/MetadataCollector.java index 66021a31d8..b4ecad0631 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/MetadataCollector.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/MetadataCollector.java @@ -15,27 +15,28 @@ */ package androidx.media3.muxer; -import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.container.Mp4TimestampData.unixTimeToMp4TimeSeconds; +import androidx.media3.common.Metadata; +import androidx.media3.container.MdtaMetadataEntry; import androidx.media3.container.Mp4LocationData; import androidx.media3.container.Mp4TimestampData; -import java.nio.ByteBuffer; -import java.util.LinkedHashMap; -import java.util.Map; +import androidx.media3.container.XmpData; +import java.util.ArrayList; +import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** Collects and provides metadata: location, FPS, XMP data, etc. */ /* package */ final class MetadataCollector { public int orientation; public @MonotonicNonNull Mp4LocationData locationData; - public Map metadataPairs; + public List metadataEntries; public Mp4TimestampData timestampData; - public @MonotonicNonNull ByteBuffer xmpData; + public @MonotonicNonNull XmpData xmpData; public MetadataCollector() { orientation = 0; - metadataPairs = new LinkedHashMap<>(); + metadataEntries = new ArrayList<>(); long currentTimeInMp4TimeSeconds = unixTimeToMp4TimeSeconds(System.currentTimeMillis()); timestampData = new Mp4TimestampData( @@ -43,28 +44,21 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /* modificationTimestampSeconds= */ currentTimeInMp4TimeSeconds); } - public void addXmp(ByteBuffer xmpData) { - checkState(this.xmpData == null); - this.xmpData = xmpData; - } - public void setOrientation(int orientation) { this.orientation = orientation; } - public void setLocation(float latitude, float longitude) { - locationData = new Mp4LocationData(latitude, longitude); - } - - public void setCaptureFps(float captureFps) { - metadataPairs.put("com.android.capture.fps", captureFps); - } - - public void addMetadata(String key, Object value) { - metadataPairs.put(key, value); - } - - public void setTimestampData(Mp4TimestampData timestampData) { - this.timestampData = timestampData; + public void addMetadata(Metadata.Entry metadata) { + if (metadata instanceof Mp4LocationData) { + locationData = (Mp4LocationData) metadata; + } else if (metadata instanceof Mp4TimestampData) { + timestampData = (Mp4TimestampData) metadata; + } else if (metadata instanceof MdtaMetadataEntry) { + metadataEntries.add((MdtaMetadataEntry) metadata); + } else if (metadata instanceof XmpData) { + xmpData = (XmpData) metadata; + } else { + throw new IllegalArgumentException("Unsupported metadata"); + } } } diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4MoovStructure.java b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4MoovStructure.java index cf154a9c48..ec71e235cc 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4MoovStructure.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4MoovStructure.java @@ -180,12 +180,12 @@ import org.checkerframework.checker.nullness.qual.PolyNull; nextTrackId, creationTimestampSeconds, modificationTimestampSeconds, videoDurationUs); ByteBuffer udtaBox = Boxes.udta(metadataCollector.locationData); ByteBuffer metaBox = - metadataCollector.metadataPairs.isEmpty() + metadataCollector.metadataEntries.isEmpty() ? ByteBuffer.allocate(0) : Boxes.meta( Boxes.hdlr(/* handlerType= */ "mdta", /* handlerName= */ ""), - Boxes.keys(Lists.newArrayList(metadataCollector.metadataPairs.keySet())), - Boxes.ilst(Lists.newArrayList(metadataCollector.metadataPairs.values()))); + Boxes.keys(Lists.newArrayList(metadataCollector.metadataEntries)), + Boxes.ilst(Lists.newArrayList(metadataCollector.metadataEntries))); ByteBuffer moovBox; moovBox = @@ -199,7 +199,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull; // Also add XMP if needed if (metadataCollector.xmpData != null) { return BoxUtils.concatenateBuffers( - moovBox, Boxes.uuid(Boxes.XMP_UUID, metadataCollector.xmpData.duplicate())); + moovBox, Boxes.uuid(Boxes.XMP_UUID, ByteBuffer.wrap(metadataCollector.xmpData.data))); } else { // No need for another copy if there is no XMP to be appended. return moovBox; 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 577261310c..47096b6484 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java @@ -25,9 +25,14 @@ import androidx.annotation.FloatRange; import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.Format; +import androidx.media3.common.Metadata; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.UnstableApi; +import androidx.media3.common.util.Util; +import androidx.media3.container.MdtaMetadataEntry; +import androidx.media3.container.Mp4LocationData; import androidx.media3.container.Mp4TimestampData; +import androidx.media3.container.XmpData; import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.FileOutputStream; @@ -205,6 +210,20 @@ public final class Mp4Muxer { this.metadataCollector = metadataCollector; } + /** + * Returns whether a given {@link Metadata.Entry metadata} is supported. + * + *

For the list of supported metadata refer to {@link Mp4Muxer#addMetadata(Metadata.Entry)}. + */ + public static boolean isMetadataSupported(Metadata.Entry metadata) { + return metadata instanceof Mp4LocationData + || (metadata instanceof Mp4TimestampData + && isMp4TimestampDataSupported((Mp4TimestampData) metadata)) + || (metadata instanceof MdtaMetadataEntry + && isMdtaMetadataEntrySupported((MdtaMetadataEntry) metadata)) + || metadata instanceof XmpData; + } + /** * Sets the orientation hint for the video playback. * @@ -215,57 +234,85 @@ public final class Mp4Muxer { } /** - * Sets the location. - * - * @param latitude The latitude, in degrees. Its value must be in the range [-90, 90]. - * @param longitude The longitude, in degrees. Its value must be in the range [-180, 180]. + * @deprecated Use {@link #addMetadata(Metadata.Entry)} with {@link Mp4LocationData} instead. */ + @Deprecated public void setLocation( @FloatRange(from = -90.0, to = 90.0) float latitude, @FloatRange(from = -180.0, to = 180.0) float longitude) { - metadataCollector.setLocation(latitude, longitude); + addMetadata(new Mp4LocationData(latitude, longitude)); } /** - * Sets the capture frame rate. - * - * @param captureFps The frame rate. + * @deprecated Use {@link #addMetadata(Metadata.Entry)} with {@link MdtaMetadataEntry} instead. */ + @Deprecated public void setCaptureFps(float captureFps) { - metadataCollector.setCaptureFps(captureFps); + addMetadata( + new MdtaMetadataEntry( + MdtaMetadataEntry.KEY_ANDROID_CAPTURE_FPS, + Util.toByteArray(captureFps), + MdtaMetadataEntry.TYPE_INDICATOR_FLOAT32)); } /** - * Sets the timestamp data (creation time and modification time) for the output file. - * - *

If this method is not called, the file creation time and modification time will be when the - * {@link Mp4Muxer} was {@linkplain Builder#build() created}. + * @deprecated Use {@link #addMetadata(Metadata.Entry)} with {@link Mp4TimestampData} instead. */ + @Deprecated public void setTimestampData(Mp4TimestampData timestampData) { - checkArgument( - timestampData.creationTimestampSeconds <= UNSIGNED_INT_MAX_VALUE - && timestampData.modificationTimestampSeconds <= UNSIGNED_INT_MAX_VALUE, - "Only 32-bit long timestamp is supported"); - metadataCollector.setTimestampData(timestampData); + addMetadata(timestampData); } /** - * Adds custom metadata. - * - * @param key The metadata key in {@link String} format. - * @param value The metadata value in {@link String} or {@link Float} format. + * @deprecated Use {@link #addMetadata(Metadata.Entry)} with {@link MdtaMetadataEntry} instead. */ + @Deprecated public void addMetadata(String key, Object value) { - metadataCollector.addMetadata(key, value); + MdtaMetadataEntry mdtaMetadataEntry = null; + if (value instanceof String) { + mdtaMetadataEntry = + new MdtaMetadataEntry( + key, Util.getUtf8Bytes((String) value), MdtaMetadataEntry.TYPE_INDICATOR_STRING); + } else if (value instanceof Float) { + mdtaMetadataEntry = + new MdtaMetadataEntry( + key, Util.toByteArray((Float) value), MdtaMetadataEntry.TYPE_INDICATOR_FLOAT32); + } else { + throw new IllegalArgumentException("Unsupported metadata"); + } + addMetadata(mdtaMetadataEntry); } /** - * Adds xmp data. + * Adds metadata for the output file. * - * @param xmp The xmp {@link ByteBuffer}. + *

List of supported {@linkplain Metadata.Entry metadata entries}: + * + *

    + *
  • {@link Mp4LocationData} + *
  • {@link Mp4TimestampData} + *
  • {@link MdtaMetadataEntry}: Only {@linkplain MdtaMetadataEntry#TYPE_INDICATOR_STRING + * string type} or {@linkplain MdtaMetadataEntry#TYPE_INDICATOR_FLOAT32 float type} value is + * supported. + *
  • {@link XmpData} + *
+ * + * @param metadata The {@linkplain Metadata.Entry metadata}. An {@link IllegalArgumentException} + * is throw if the {@linkplain Metadata.Entry metadata} is not supported. */ + public void addMetadata(Metadata.Entry metadata) { + checkArgument(isMetadataSupported(metadata), "Unsupported metadata"); + metadataCollector.addMetadata(metadata); + } + + /** + * @deprecated Use {@link #addMetadata(Metadata.Entry)} with {@link XmpData} instead. + */ + @Deprecated public void addXmp(ByteBuffer xmp) { - metadataCollector.addXmp(xmp); + byte[] xmpData = new byte[xmp.remaining()]; + xmp.get(xmpData, 0, xmpData.length); + addMetadata(new XmpData(xmpData)); } /** @@ -308,4 +355,14 @@ public final class Mp4Muxer { public void close() throws IOException { mp4Writer.close(); } + + private static boolean isMdtaMetadataEntrySupported(MdtaMetadataEntry mdtaMetadataEntry) { + return mdtaMetadataEntry.typeIndicator == MdtaMetadataEntry.TYPE_INDICATOR_STRING + || mdtaMetadataEntry.typeIndicator == MdtaMetadataEntry.TYPE_INDICATOR_FLOAT32; + } + + private static boolean isMp4TimestampDataSupported(Mp4TimestampData timestampData) { + return timestampData.creationTimestampSeconds <= UNSIGNED_INT_MAX_VALUE + && timestampData.modificationTimestampSeconds <= UNSIGNED_INT_MAX_VALUE; + } } 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 7bf34c440f..f84e55d01f 100644 --- a/libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java +++ b/libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java @@ -30,6 +30,8 @@ import android.media.MediaCodec; import androidx.media3.common.C; import androidx.media3.common.ColorInfo; import androidx.media3.common.Format; +import androidx.media3.common.util.Util; +import androidx.media3.container.MdtaMetadataEntry; import androidx.media3.container.Mp4LocationData; import androidx.media3.muxer.FragmentedMp4Writer.SampleMetadata; import androidx.media3.test.utils.DumpFileAsserts; @@ -173,9 +175,19 @@ public class BoxesTest { @Test public void createKeysBox_matchesExpected() throws Exception { - List keyNames = ImmutableList.of("com.android.version", "com.android.capture.fps"); + List metadataEntries = new ArrayList<>(); + metadataEntries.add( + new MdtaMetadataEntry( + "com.android.version", + Util.getUtf8Bytes("11"), + MdtaMetadataEntry.TYPE_INDICATOR_STRING)); + metadataEntries.add( + new MdtaMetadataEntry( + "com.android.capture.fps", + Util.toByteArray(120.0f), + MdtaMetadataEntry.TYPE_INDICATOR_FLOAT32)); - ByteBuffer keysBox = Boxes.keys(keyNames); + ByteBuffer keysBox = Boxes.keys(metadataEntries); DumpableMp4Box dumpableBox = new DumpableMp4Box(keysBox); DumpFileAsserts.assertOutput(context, dumpableBox, getExpectedDumpFilePath("keys_box")); @@ -183,9 +195,19 @@ public class BoxesTest { @Test public void createIlstBox_matchesExpected() throws Exception { - List values = ImmutableList.of("11", 120.0f); + List metadataEntries = new ArrayList<>(); + metadataEntries.add( + new MdtaMetadataEntry( + "com.android.version", + Util.getUtf8Bytes("11"), + MdtaMetadataEntry.TYPE_INDICATOR_STRING)); + metadataEntries.add( + new MdtaMetadataEntry( + "com.android.capture.fps", + Util.toByteArray(120.0f), + MdtaMetadataEntry.TYPE_INDICATOR_FLOAT32)); - ByteBuffer ilstBox = Boxes.ilst(values); + ByteBuffer ilstBox = Boxes.ilst(metadataEntries); DumpableMp4Box dumpableBox = new DumpableMp4Box(ilstBox); DumpFileAsserts.assertOutput(context, dumpableBox, getExpectedDumpFilePath("ilst_box")); diff --git a/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java b/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java index ae69dd8aa8..daaef7a59c 100644 --- a/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java +++ b/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerEndToEndTest.java @@ -24,7 +24,11 @@ import static org.junit.Assert.assertThrows; import android.content.Context; import android.media.MediaCodec.BufferInfo; import android.util.Pair; +import androidx.media3.common.util.Util; +import androidx.media3.container.MdtaMetadataEntry; +import androidx.media3.container.Mp4LocationData; import androidx.media3.container.Mp4TimestampData; +import androidx.media3.container.XmpData; import androidx.media3.extractor.mp4.Mp4Extractor; import androidx.media3.muxer.Mp4Muxer.TrackToken; import androidx.media3.test.utils.DumpFileAsserts; @@ -56,7 +60,11 @@ public class Mp4MuxerEndToEndTest { try { mp4Muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); mp4Muxer.setOrientation(90); - mp4Muxer.addMetadata("key", "value"); + mp4Muxer.addMetadata( + new MdtaMetadataEntry( + "key", + /* value= */ Util.getUtf8Bytes("value"), + MdtaMetadataEntry.TYPE_INDICATOR_STRING)); } finally { mp4Muxer.close(); } @@ -69,7 +77,7 @@ public class Mp4MuxerEndToEndTest { public void createMp4File_withSameTracksOffset_matchesExpected() throws IOException { String outputFilePath = temporaryFolder.newFile().getPath(); Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); - mp4Muxer.setTimestampData( + mp4Muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 100_000_000L, /* modificationTimestampSeconds= */ 500_000_000L)); @@ -112,7 +120,7 @@ public class Mp4MuxerEndToEndTest { public void createMp4File_withDifferentTracksOffset_matchesExpected() throws IOException { String outputFilePath = temporaryFolder.newFile().getPath(); Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); - mp4Muxer.setTimestampData( + mp4Muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 100_000_000L, /* modificationTimestampSeconds= */ 500_000_000L)); @@ -174,7 +182,7 @@ public class Mp4MuxerEndToEndTest { public void createMp4File_withOneTrackEmpty_doesNotWriteEmptyTrack() throws Exception { String outputFilePath = temporaryFolder.newFile().getPath(); Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); - mp4Muxer.setTimestampData( + mp4Muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 100_000_000L, /* modificationTimestampSeconds= */ 500_000_000L)); @@ -209,18 +217,26 @@ public class Mp4MuxerEndToEndTest { Pair sampleAndSampleInfo = getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L); byte[] xmpBytes = TestUtil.getByteArray(context, XMP_SAMPLE_DATA); - ByteBuffer xmp = ByteBuffer.wrap(xmpBytes); try { muxer.setOrientation(90); - muxer.setLocation(/* latitude= */ 33.0f, /* longitude= */ -120f); - muxer.setCaptureFps(120.0f); - muxer.setTimestampData( + muxer.addMetadata(new Mp4LocationData(/* latitude= */ 33.0f, /* longitude= */ -120f)); + float captureFps = 120.0f; + muxer.addMetadata( + new MdtaMetadataEntry( + MdtaMetadataEntry.KEY_ANDROID_CAPTURE_FPS, + /* value= */ Util.toByteArray(captureFps), + MdtaMetadataEntry.TYPE_INDICATOR_FLOAT32)); + muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 1_000_000L, /* modificationTimestampSeconds= */ 5_000_000L)); - muxer.addMetadata("StringKey1", "StringValue"); - muxer.addXmp(xmp); + muxer.addMetadata( + new MdtaMetadataEntry( + "StringKey1", + /* value= */ Util.getUtf8Bytes("StringValue"), + MdtaMetadataEntry.TYPE_INDICATOR_STRING)); + muxer.addMetadata(new XmpData(xmpBytes)); TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second); } finally { 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 a766352542..dc51f165ae 100644 --- a/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerMetadataTest.java +++ b/libraries/muxer/src/test/java/androidx/media3/muxer/Mp4MuxerMetadataTest.java @@ -52,7 +52,7 @@ public class Mp4MuxerMetadataTest { Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); try { - muxer.setTimestampData( + muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 1_000_000L, /* modificationTimestampSeconds= */ 5_000_000L)); @@ -77,7 +77,7 @@ public class Mp4MuxerMetadataTest { Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); try { - muxer.setTimestampData( + muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 1_000_000L, /* modificationTimestampSeconds= */ 5_000_000L)); @@ -104,7 +104,7 @@ public class Mp4MuxerMetadataTest { Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); try { - muxer.setTimestampData( + muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 1_000_000L, /* modificationTimestampSeconds= */ 5_000_000L)); @@ -131,7 +131,7 @@ public class Mp4MuxerMetadataTest { Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); try { - muxer.setTimestampData( + muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 1_000_000L, /* modificationTimestampSeconds= */ 5_000_000L)); @@ -158,7 +158,7 @@ public class Mp4MuxerMetadataTest { Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); try { - muxer.setTimestampData( + muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 1_000_000L, /* modificationTimestampSeconds= */ 5_000_000L)); @@ -184,7 +184,7 @@ public class Mp4MuxerMetadataTest { Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); try { - muxer.setTimestampData( + muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 1_000_000L, /* modificationTimestampSeconds= */ 5_000_000L)); @@ -209,7 +209,7 @@ public class Mp4MuxerMetadataTest { Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); try { - muxer.setTimestampData( + muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 1_000_000L, /* modificationTimestampSeconds= */ 5_000_000L)); @@ -235,7 +235,7 @@ public class Mp4MuxerMetadataTest { Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); try { - muxer.setTimestampData( + muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 1_000_000L, /* modificationTimestampSeconds= */ 5_000_000L)); @@ -261,7 +261,7 @@ public class Mp4MuxerMetadataTest { Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); try { - muxer.setTimestampData( + muxer.addMetadata( new Mp4TimestampData( /* creationTimestampSeconds= */ 1_000_000L, /* modificationTimestampSeconds= */ 5_000_000L)); diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java b/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java index 8759071fb2..8ad031574a 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/InAppMuxer.java @@ -24,11 +24,6 @@ import androidx.media3.common.Format; import androidx.media3.common.Metadata; import androidx.media3.common.MimeTypes; import androidx.media3.common.util.UnstableApi; -import androidx.media3.common.util.Util; -import androidx.media3.container.MdtaMetadataEntry; -import androidx.media3.container.Mp4LocationData; -import androidx.media3.container.Mp4TimestampData; -import androidx.media3.container.XmpData; import androidx.media3.muxer.Mp4Muxer; import androidx.media3.muxer.Mp4Muxer.TrackToken; import com.google.common.collect.ImmutableList; @@ -56,14 +51,7 @@ public final class InAppMuxer implements Muxer { *

A {@link Metadata.Entry} can be added or removed. To modify an existing {@link * Metadata.Entry}, first remove it and then add a new one. * - *

List of supported {@linkplain Metadata.Entry metadata entries}: - * - *

    - *
  • {@link Mp4LocationData} - *
  • {@link XmpData} - *
  • {@link Mp4TimestampData} - *
  • {@link MdtaMetadataEntry} - *
+ *

For the list of supported metadata refer to {@link Mp4Muxer#addMetadata(Metadata.Entry)}. */ void updateMetadataEntries(Set metadataEntries); } @@ -241,17 +229,7 @@ public final class InAppMuxer implements Muxer { public void addMetadata(Metadata metadata) { for (int i = 0; i < metadata.length(); i++) { Metadata.Entry entry = metadata.get(i); - // Keep only supported metadata. - // LINT.IfChange(added_metadata) - if (entry instanceof Mp4LocationData - || entry instanceof XmpData - || entry instanceof Mp4TimestampData - || (entry instanceof MdtaMetadataEntry - && (((MdtaMetadataEntry) entry).key.equals(MdtaMetadataEntry.KEY_ANDROID_CAPTURE_FPS) - || ((MdtaMetadataEntry) entry).typeIndicator - == MdtaMetadataEntry.TYPE_INDICATOR_STRING - || ((MdtaMetadataEntry) entry).typeIndicator - == MdtaMetadataEntry.TYPE_INDICATOR_FLOAT32))) { + if (Mp4Muxer.isMetadataSupported(entry)) { metadataEntries.add(entry); } } @@ -282,29 +260,7 @@ public final class InAppMuxer implements Muxer { } for (Metadata.Entry entry : metadataEntries) { - // LINT.IfChange(written_metadata) - if (entry instanceof Mp4LocationData) { - mp4Muxer.setLocation( - ((Mp4LocationData) entry).latitude, ((Mp4LocationData) entry).longitude); - } else if (entry instanceof XmpData) { - mp4Muxer.addXmp(ByteBuffer.wrap(((XmpData) entry).data)); - } else if (entry instanceof Mp4TimestampData) { - mp4Muxer.setTimestampData((Mp4TimestampData) entry); - } else if (entry instanceof MdtaMetadataEntry) { - MdtaMetadataEntry mdtaMetadataEntry = (MdtaMetadataEntry) entry; - if (mdtaMetadataEntry.key.equals(MdtaMetadataEntry.KEY_ANDROID_CAPTURE_FPS)) { - byte[] captureFps = mdtaMetadataEntry.value; - mp4Muxer.setCaptureFps(ByteBuffer.wrap(captureFps).getFloat()); - } else if (mdtaMetadataEntry.typeIndicator == MdtaMetadataEntry.TYPE_INDICATOR_STRING) { - mp4Muxer.addMetadata(mdtaMetadataEntry.key, Util.fromUtf8Bytes(mdtaMetadataEntry.value)); - } else if (mdtaMetadataEntry.typeIndicator == MdtaMetadataEntry.TYPE_INDICATOR_FLOAT32) { - mp4Muxer.addMetadata(mdtaMetadataEntry.key, Util.toFloat(mdtaMetadataEntry.value)); - } else { - throw new IllegalStateException("Unsupported MdtaMetadataEntry " + mdtaMetadataEntry.key); - } - } else { - throw new IllegalStateException("Unsupported Metadata.Entry " + entry.getClass().getName()); - } + mp4Muxer.addMetadata(entry); } } }