Add unified addMetadata() method in Mp4Muxer

PiperOrigin-RevId: 610710011
This commit is contained in:
sheenachhabra 2024-02-27 04:31:28 -08:00 committed by Copybara-Service
parent 0e0e1c4f1a
commit 94e0a27a81
8 changed files with 185 additions and 153 deletions

View File

@ -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;
*
* <p>This box contains a list of metadata keys.
*/
public static ByteBuffer keys(List<String> keyNames) {
// This should be an adaptive size here; we don't yet care since it's usually small.
public static ByteBuffer keys(List<MdtaMetadataEntry> 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;
*
* <p>This box contains a list of metadata values.
*/
public static ByteBuffer ilst(List<Object> values) {
// This should be an adaptive size here; we don't yet care since it's usually small.
public static ByteBuffer ilst(List<MdtaMetadataEntry> 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);

View File

@ -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<String, Object> metadataPairs;
public List<MdtaMetadataEntry> 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 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");
}
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;
}
}

View File

@ -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;

View File

@ -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.
*
* <p>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.
*
* <p>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}.
* <p>List of supported {@linkplain Metadata.Entry metadata entries}:
*
* <ul>
* <li>{@link Mp4LocationData}
* <li>{@link Mp4TimestampData}
* <li>{@link MdtaMetadataEntry}: Only {@linkplain MdtaMetadataEntry#TYPE_INDICATOR_STRING
* string type} or {@linkplain MdtaMetadataEntry#TYPE_INDICATOR_FLOAT32 float type} value is
* supported.
* <li>{@link XmpData}
* </ul>
*
* @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;
}
}

View File

@ -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<String> keyNames = ImmutableList.of("com.android.version", "com.android.capture.fps");
List<MdtaMetadataEntry> 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<Object> values = ImmutableList.of("11", 120.0f);
List<MdtaMetadataEntry> 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"));

View File

@ -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<ByteBuffer, BufferInfo> 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 {

View File

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

View File

@ -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 {
* <p>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.
*
* <p>List of supported {@linkplain Metadata.Entry metadata entries}:
*
* <ul>
* <li>{@link Mp4LocationData}
* <li>{@link XmpData}
* <li>{@link Mp4TimestampData}
* <li>{@link MdtaMetadataEntry}
* </ul>
* <p>For the list of supported metadata refer to {@link Mp4Muxer#addMetadata(Metadata.Entry)}.
*/
void updateMetadataEntries(Set<Metadata.Entry> 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);
}
}
}