Replace setModificationTime API with setTimestampData in Mp4Muxer

The new API will take both `creation time` and `modification time`.

Till now, Mp4Muxer wrote `modification time` in both
`creation time` and `modification time` field, which was
incorrect.

PiperOrigin-RevId: 605590623
This commit is contained in:
sheenachhabra 2024-02-09 04:13:28 -08:00 committed by Copybara-Service
parent a31a384393
commit 8a758c2ed7
36 changed files with 193 additions and 147 deletions

View File

@ -18,7 +18,6 @@ package androidx.media3.container;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Metadata; import androidx.media3.common.Metadata;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import com.google.common.primitives.Longs; import com.google.common.primitives.Longs;
@ -29,6 +28,13 @@ public final class Mp4TimestampData implements Metadata.Entry {
/** Represents an unset or unknown timescale. */ /** Represents an unset or unknown timescale. */
public static final int TIMESCALE_UNSET = -1; public static final int TIMESCALE_UNSET = -1;
/**
* The delta between a Unix epoch timestamp (in milliseconds since midnight, January 1, 1970) and
* an MP4 timestamp (in seconds since midnight, January 1, 1904).
*/
private static final int UNIX_EPOCH_TO_MP4_TIME_DELTA_SECONDS =
((1970 - 1904) * 365 + 17 /* leap year */) * (24 * 60 * 60);
/** The creation timestamp. */ /** The creation timestamp. */
public final long creationTimestampSeconds; public final long creationTimestampSeconds;
@ -41,13 +47,16 @@ public final class Mp4TimestampData implements Metadata.Entry {
/** /**
* Creates an instance. * Creates an instance.
* *
* <p>The {@link #timescale} is set to {@link Mp4TimestampData#TIMESCALE_UNSET}.
*
* @param creationTimestampSeconds The creation time UTC in seconds since midnight, January 1, * @param creationTimestampSeconds The creation time UTC in seconds since midnight, January 1,
* 1904. The {@link #modificationTimestampSeconds} is set to {@link C#TIME_UNSET} and {@link * 1904.
* #timescale} is set to {@link Mp4TimestampData#TIMESCALE_UNSET}. * @param modificationTimestampSeconds The modification time UTC in seconds since midnight,
* January 1, 1904.
*/ */
public Mp4TimestampData(long creationTimestampSeconds) { public Mp4TimestampData(long creationTimestampSeconds, long modificationTimestampSeconds) {
this.creationTimestampSeconds = creationTimestampSeconds; this.creationTimestampSeconds = creationTimestampSeconds;
this.modificationTimestampSeconds = C.TIME_UNSET; this.modificationTimestampSeconds = modificationTimestampSeconds;
this.timescale = TIMESCALE_UNSET; this.timescale = TIMESCALE_UNSET;
} }
@ -73,6 +82,14 @@ public final class Mp4TimestampData implements Metadata.Entry {
this.timescale = in.readLong(); this.timescale = in.readLong();
} }
/**
* Returns an MP4 timestamp (in seconds since midnight, January 1, 1904) from a Unix epoch
* timestamp (in milliseconds since midnight, January 1, 1970).
*/
public static long unixTimeToMp4TimeSeconds(long unixTimestampMs) {
return (unixTimestampMs / 1_000L) + UNIX_EPOCH_TO_MP4_TIME_DELTA_SECONDS;
}
@Override @Override
public boolean equals(@Nullable Object obj) { public boolean equals(@Nullable Object obj) {
if (this == obj) { if (this == obj) {

View File

@ -1,43 +0,0 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.media3.container;
import androidx.media3.common.util.UnstableApi;
/** Utilities for MP4 container. */
@UnstableApi
public final class Mp4Util {
private static final int UNIX_EPOCH_TO_MP4_TIME_DELTA_SECONDS =
((1970 - 1904) * 365 + 17 /* leap year */) * (24 * 60 * 60);
private Mp4Util() {}
/**
* Returns an MP4 timestamp (in seconds since midnight, January 1, 1904) from a Unix epoch
* timestamp (in milliseconds since midnight, January 1, 1970).
*/
public static long unixTimeToMp4TimeSeconds(long unixTimestampMs) {
return (unixTimestampMs / 1000L + UNIX_EPOCH_TO_MP4_TIME_DELTA_SECONDS);
}
/**
* Returns a Unix epoch timestamp (in milliseconds since midnight, January 1, 1970) from an MP4
* timestamp (in seconds since midnight, January 1, 1904).
*/
public static long mp4TimeToUnixTimeMs(long mp4TimestampSeconds) {
return (mp4TimestampSeconds - UNIX_EPOCH_TO_MP4_TIME_DELTA_SECONDS) * 1000L;
}
}

View File

@ -23,6 +23,7 @@ import android.media.MediaCodec;
import android.media.MediaExtractor; import android.media.MediaExtractor;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.util.MediaFormatUtil; import androidx.media3.common.util.MediaFormatUtil;
import androidx.media3.container.Mp4TimestampData;
import androidx.media3.extractor.mp4.FragmentedMp4Extractor; import androidx.media3.extractor.mp4.FragmentedMp4Extractor;
import androidx.media3.extractor.mp4.Mp4Extractor; import androidx.media3.extractor.mp4.Mp4Extractor;
import androidx.media3.test.utils.DumpFileAsserts; import androidx.media3.test.utils.DumpFileAsserts;
@ -85,7 +86,10 @@ public class Mp4MuxerEndToEndTest {
try { try {
mp4Muxer = new Mp4Muxer.Builder(checkNotNull(outputStream)).build(); mp4Muxer = new Mp4Muxer.Builder(checkNotNull(outputStream)).build();
mp4Muxer.setModificationTime(/* timestampMs= */ 500_000_000L); mp4Muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 100_000_000L,
/* modificationTimestampSeconds= */ 500_000_000L));
feedInputDataToMuxer(mp4Muxer, checkNotNull(inputFile)); feedInputDataToMuxer(mp4Muxer, checkNotNull(inputFile));
} finally { } finally {
if (mp4Muxer != null) { if (mp4Muxer != null) {
@ -106,7 +110,10 @@ public class Mp4MuxerEndToEndTest {
// ensure some data has been written after taking all the inputs but before closing the muxer. // ensure some data has been written after taking all the inputs but before closing the muxer.
assumeTrue(checkNotNull(inputFile).equals(H265_HDR10_MP4)); assumeTrue(checkNotNull(inputFile).equals(H265_HDR10_MP4));
Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(checkNotNull(outputStream)).build(); Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(checkNotNull(outputStream)).build();
mp4Muxer.setModificationTime(/* timestampMs= */ 500_000_000L); mp4Muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 100_000_000L,
/* modificationTimestampSeconds= */ 500_000_000L));
feedInputDataToMuxer(mp4Muxer, inputFile); feedInputDataToMuxer(mp4Muxer, inputFile);
// Muxer not closed. // Muxer not closed.
@ -132,7 +139,10 @@ public class Mp4MuxerEndToEndTest {
try { try {
mp4Muxer = mp4Muxer =
new Mp4Muxer.Builder(checkNotNull(outputStream)).setFragmentedMp4Enabled(true).build(); new Mp4Muxer.Builder(checkNotNull(outputStream)).setFragmentedMp4Enabled(true).build();
mp4Muxer.setModificationTime(/* timestampMs= */ 500_000_000L); mp4Muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 100_000_000L,
/* modificationTimestampSeconds= */ 500_000_000L));
feedInputDataToMuxer(mp4Muxer, inputFile); feedInputDataToMuxer(mp4Muxer, inputFile);
} finally { } finally {
if (mp4Muxer != null) { if (mp4Muxer != null) {
@ -160,7 +170,10 @@ public class Mp4MuxerEndToEndTest {
try { try {
mp4Muxer = mp4Muxer =
new Mp4Muxer.Builder(checkNotNull(outputStream)).setFragmentedMp4Enabled(true).build(); new Mp4Muxer.Builder(checkNotNull(outputStream)).setFragmentedMp4Enabled(true).build();
mp4Muxer.setModificationTime(/* timestampMs= */ 500_000_000L); mp4Muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 100_000_000L,
/* modificationTimestampSeconds= */ 500_000_000L));
feedInputDataToMuxer(mp4Muxer, inputFile); feedInputDataToMuxer(mp4Muxer, inputFile);
} finally { } finally {
if (mp4Muxer != null) { if (mp4Muxer != null) {

View File

@ -93,14 +93,15 @@ import java.util.Locale;
public static ByteBuffer tkhd( public static ByteBuffer tkhd(
int trackId, int trackId,
int trackDurationVu, int trackDurationVu,
int creationTimestampSeconds,
int modificationTimestampSeconds, int modificationTimestampSeconds,
int orientation, int orientation,
Format format) { Format format) {
ByteBuffer contents = ByteBuffer.allocate(Mp4Utils.MAX_FIXED_LEAF_BOX_SIZE); ByteBuffer contents = ByteBuffer.allocate(Mp4Utils.MAX_FIXED_LEAF_BOX_SIZE);
contents.putInt(0x00000007); // version and flags; allow presentation, etc. contents.putInt(0x00000007); // version and flags; allow presentation, etc.
contents.putInt(modificationTimestampSeconds); // creation_time contents.putInt(creationTimestampSeconds); // creation_time; unsigned int(32)
contents.putInt(modificationTimestampSeconds); // modification_time contents.putInt(modificationTimestampSeconds); // modification_time; unsigned int(32)
contents.putInt(trackId); contents.putInt(trackId);
contents.putInt(0); // reserved contents.putInt(0); // reserved
@ -132,12 +133,15 @@ import java.util.Locale;
* <p>This is the movie header for the entire MP4 file. * <p>This is the movie header for the entire MP4 file.
*/ */
public static ByteBuffer mvhd( public static ByteBuffer mvhd(
int nextEmptyTrackId, int modificationTimestampSeconds, long videoDurationUs) { int nextEmptyTrackId,
int creationTimestampSeconds,
int modificationTimestampSeconds,
long videoDurationUs) {
ByteBuffer contents = ByteBuffer.allocate(Mp4Utils.MAX_FIXED_LEAF_BOX_SIZE); ByteBuffer contents = ByteBuffer.allocate(Mp4Utils.MAX_FIXED_LEAF_BOX_SIZE);
contents.putInt(0); // version and flags contents.putInt(0); // version and flags
contents.putInt(modificationTimestampSeconds); // creation_time contents.putInt(creationTimestampSeconds); // creation_time; unsigned int(32)
contents.putInt(modificationTimestampSeconds); // modification_time contents.putInt(modificationTimestampSeconds); // modification_time; unsigned int(32)
contents.putInt((int) MVHD_TIMEBASE); // The per-track timescales might be different. contents.putInt((int) MVHD_TIMEBASE); // The per-track timescales might be different.
contents.putInt( contents.putInt(
(int) Mp4Utils.vuFromUs(videoDurationUs, MVHD_TIMEBASE)); // Duration of the entire video. (int) Mp4Utils.vuFromUs(videoDurationUs, MVHD_TIMEBASE)); // Duration of the entire video.
@ -175,13 +179,14 @@ import java.util.Locale;
public static ByteBuffer mdhd( public static ByteBuffer mdhd(
long trackDurationVu, long trackDurationVu,
int videoUnitTimebase, int videoUnitTimebase,
int creationTimestampSeconds,
int modificationTimestampSeconds, int modificationTimestampSeconds,
@Nullable String languageCode) { @Nullable String languageCode) {
ByteBuffer contents = ByteBuffer.allocate(Mp4Utils.MAX_FIXED_LEAF_BOX_SIZE); ByteBuffer contents = ByteBuffer.allocate(Mp4Utils.MAX_FIXED_LEAF_BOX_SIZE);
contents.putInt(0x0); // version and flags contents.putInt(0x0); // version and flags
contents.putInt(modificationTimestampSeconds); // creation_time contents.putInt(creationTimestampSeconds); // creation_time; unsigned int(32)
contents.putInt(modificationTimestampSeconds); // modification_time contents.putInt(modificationTimestampSeconds); // modification_time; unsigned int(32)
contents.putInt(videoUnitTimebase); contents.putInt(videoUnitTimebase);

View File

@ -15,11 +15,10 @@
*/ */
package androidx.media3.muxer; package androidx.media3.muxer;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.muxer.Mp4Utils.UNSIGNED_INT_MAX_VALUE; import static androidx.media3.container.Mp4TimestampData.unixTimeToMp4TimeSeconds;
import androidx.media3.container.Mp4Util; import androidx.media3.container.Mp4TimestampData;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -30,14 +29,17 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
public int orientation; public int orientation;
public @MonotonicNonNull Mp4Location location; public @MonotonicNonNull Mp4Location location;
public Map<String, Object> metadataPairs; public Map<String, Object> metadataPairs;
public int modificationTimestampSeconds; public Mp4TimestampData timestampData;
public @MonotonicNonNull ByteBuffer xmpData; public @MonotonicNonNull ByteBuffer xmpData;
public MetadataCollector() { public MetadataCollector() {
orientation = 0; orientation = 0;
metadataPairs = new LinkedHashMap<>(); metadataPairs = new LinkedHashMap<>();
modificationTimestampSeconds = long currentTimeInMp4TimeSeconds = unixTimeToMp4TimeSeconds(System.currentTimeMillis());
(int) Mp4Util.unixTimeToMp4TimeSeconds(System.currentTimeMillis()); timestampData =
new Mp4TimestampData(
/* creationTimestampSeconds= */ currentTimeInMp4TimeSeconds,
/* modificationTimestampSeconds= */ currentTimeInMp4TimeSeconds);
} }
public void addXmp(ByteBuffer xmpData) { public void addXmp(ByteBuffer xmpData) {
@ -61,10 +63,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
metadataPairs.put(key, value); metadataPairs.put(key, value);
} }
public void setModificationTime(long unixTimestampMs) { public void setTimestampData(Mp4TimestampData timestampData) {
long timestampSeconds = Mp4Util.unixTimeToMp4TimeSeconds(unixTimestampMs); this.timestampData = timestampData;
checkArgument(
timestampSeconds <= UNSIGNED_INT_MAX_VALUE, "Only 32-bit long timestamp supported");
this.modificationTimestampSeconds = (int) timestampSeconds;
} }
} }

View File

@ -60,6 +60,12 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
@SuppressWarnings("InlinedApi") @SuppressWarnings("InlinedApi")
public ByteBuffer moovMetadataHeader( public ByteBuffer moovMetadataHeader(
List<? extends TrackMetadataProvider> tracks, long minInputPtsUs, boolean isFragmentedMp4) { List<? extends TrackMetadataProvider> tracks, long minInputPtsUs, boolean isFragmentedMp4) {
// The timestamp will always fit into a 32-bit integer. This is already validated in the
// Mp4Muxer.setTimestampData() API. The value after type casting might be negative, but it is
// still valid because it is meant to be read as an unsigned integer.
int creationTimestampSeconds = (int) metadataCollector.timestampData.creationTimestampSeconds;
int modificationTimestampSeconds =
(int) metadataCollector.timestampData.modificationTimestampSeconds;
List<ByteBuffer> trakBoxes = new ArrayList<>(); List<ByteBuffer> trakBoxes = new ArrayList<>();
List<ByteBuffer> trexBoxes = new ArrayList<>(); List<ByteBuffer> trexBoxes = new ArrayList<>();
@ -149,14 +155,16 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
// Using the time base of the entire file, not that of the track; otherwise, // Using the time base of the entire file, not that of the track; otherwise,
// Quicktime will stretch the audio accordingly, see b/158120042. // Quicktime will stretch the audio accordingly, see b/158120042.
(int) Mp4Utils.vuFromUs(trackDurationUs, MVHD_TIMEBASE), (int) Mp4Utils.vuFromUs(trackDurationUs, MVHD_TIMEBASE),
metadataCollector.modificationTimestampSeconds, creationTimestampSeconds,
modificationTimestampSeconds,
metadataCollector.orientation, metadataCollector.orientation,
format), format),
Boxes.mdia( Boxes.mdia(
Boxes.mdhd( Boxes.mdhd(
trackDurationInTrackUnitsVu, trackDurationInTrackUnitsVu,
track.videoUnitTimebase(), track.videoUnitTimebase(),
metadataCollector.modificationTimestampSeconds, creationTimestampSeconds,
modificationTimestampSeconds,
languageCode), languageCode),
Boxes.hdlr(handlerType, handlerName), Boxes.hdlr(handlerType, handlerName),
Boxes.minf(mhdBox, Boxes.dinf(Boxes.dref(Boxes.localUrl())), stblBox))); Boxes.minf(mhdBox, Boxes.dinf(Boxes.dref(Boxes.localUrl())), stblBox)));
@ -168,7 +176,8 @@ import org.checkerframework.checker.nullness.qual.PolyNull;
} }
ByteBuffer mvhdBox = ByteBuffer mvhdBox =
Boxes.mvhd(nextTrackId, metadataCollector.modificationTimestampSeconds, videoDurationUs); Boxes.mvhd(
nextTrackId, creationTimestampSeconds, modificationTimestampSeconds, videoDurationUs);
ByteBuffer udtaBox = Boxes.udta(metadataCollector.location); ByteBuffer udtaBox = Boxes.udta(metadataCollector.location);
ByteBuffer metaBox = ByteBuffer metaBox =
metadataCollector.metadataPairs.isEmpty() metadataCollector.metadataPairs.isEmpty()

View File

@ -15,7 +15,9 @@
*/ */
package androidx.media3.muxer; package androidx.media3.muxer;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.muxer.Mp4Utils.UNSIGNED_INT_MAX_VALUE;
import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.ElementType.TYPE_USE;
import android.media.MediaCodec.BufferInfo; import android.media.MediaCodec.BufferInfo;
@ -25,6 +27,7 @@ import androidx.annotation.Nullable;
import androidx.media3.common.Format; import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes; import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.container.Mp4TimestampData;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -233,12 +236,17 @@ public final class Mp4Muxer {
} }
/** /**
* Sets the file modification time. * Sets the timestamp data (creation time and modification time) for the output file.
* *
* @param timestampMs The modification time UTC in milliseconds since the Unix epoch. * <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}.
*/ */
public void setModificationTime(long timestampMs) { public void setTimestampData(Mp4TimestampData timestampData) {
metadataCollector.setModificationTime(timestampMs); checkArgument(
timestampData.creationTimestampSeconds <= UNSIGNED_INT_MAX_VALUE
&& timestampData.modificationTimestampSeconds <= UNSIGNED_INT_MAX_VALUE,
"Only 32-bit long timestamp is supported");
metadataCollector.setTimestampData(timestampData);
} }
/** /**

View File

@ -66,7 +66,8 @@ public class BoxesTest {
Boxes.tkhd( Boxes.tkhd(
/* trackId= */ 1, /* trackId= */ 1,
/* trackDurationVu= */ 5_000_000, /* trackDurationVu= */ 5_000_000,
/* modificationTimestampSeconds= */ 1_000_000_000, /* creationTimestampSeconds= */ 1_000_000_000,
/* modificationTimestampSeconds= */ 2_000_000_000,
/* orientation= */ 90, /* orientation= */ 90,
FAKE_VIDEO_FORMAT); FAKE_VIDEO_FORMAT);
@ -81,7 +82,8 @@ public class BoxesTest {
Boxes.tkhd( Boxes.tkhd(
/* trackId= */ 1, /* trackId= */ 1,
/* trackDurationVu= */ 5_000_000, /* trackDurationVu= */ 5_000_000,
/* modificationTimestampSeconds= */ 1_000_000_000, /* creationTimestampSeconds= */ 1_000_000_000,
/* modificationTimestampSeconds= */ 2_000_000_000,
/* orientation= */ 90, /* orientation= */ 90,
FAKE_AUDIO_FORMAT); FAKE_AUDIO_FORMAT);
@ -95,7 +97,8 @@ public class BoxesTest {
ByteBuffer mvhdBox = ByteBuffer mvhdBox =
Boxes.mvhd( Boxes.mvhd(
/* nextEmptyTrackId= */ 3, /* nextEmptyTrackId= */ 3,
/* modificationTimestampSeconds= */ 1_000_000_000, /* creationTimestampSeconds= */ 1_000_000_000,
/* modificationTimestampSeconds= */ 2_000_000_000,
/* videoDurationUs= */ 5_000_000); /* videoDurationUs= */ 5_000_000);
DumpableMp4Box dumpableBox = new DumpableMp4Box(mvhdBox); DumpableMp4Box dumpableBox = new DumpableMp4Box(mvhdBox);
@ -108,7 +111,8 @@ public class BoxesTest {
Boxes.mdhd( Boxes.mdhd(
/* trackDurationVu= */ 5_000_000, /* trackDurationVu= */ 5_000_000,
VU_TIMEBASE, VU_TIMEBASE,
/* modificationTimestampSeconds= */ 1_000_000_000, /* creationTimestampSeconds= */ 1_000_000_000,
/* modificationTimestampSeconds= */ 2_000_000_000,
/* languageCode= */ "und"); /* languageCode= */ "und");
DumpableMp4Box dumpableBox = new DumpableMp4Box(mdhdBox); DumpableMp4Box dumpableBox = new DumpableMp4Box(mdhdBox);

View File

@ -21,6 +21,7 @@ import static org.junit.Assert.assertThrows;
import android.media.MediaCodec.BufferInfo; import android.media.MediaCodec.BufferInfo;
import android.util.Pair; import android.util.Pair;
import androidx.media3.container.Mp4TimestampData;
import androidx.media3.extractor.mp4.Mp4Extractor; import androidx.media3.extractor.mp4.Mp4Extractor;
import androidx.media3.muxer.Mp4Muxer.TrackToken; import androidx.media3.muxer.Mp4Muxer.TrackToken;
import androidx.media3.test.utils.DumpFileAsserts; import androidx.media3.test.utils.DumpFileAsserts;
@ -63,7 +64,10 @@ public class Mp4MuxerEndToEndTest {
public void createMp4File_withSameTracksOffset_matchesExpected() throws IOException { public void createMp4File_withSameTracksOffset_matchesExpected() throws IOException {
String outputFilePath = temporaryFolder.newFile().getPath(); String outputFilePath = temporaryFolder.newFile().getPath();
Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
mp4Muxer.setModificationTime(/* timestampMs= */ 500_000_000L); mp4Muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 100_000_000L,
/* modificationTimestampSeconds= */ 500_000_000L));
Pair<ByteBuffer, BufferInfo> track1Sample1 = Pair<ByteBuffer, BufferInfo> track1Sample1 =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L); MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L);
Pair<ByteBuffer, BufferInfo> track1Sample2 = Pair<ByteBuffer, BufferInfo> track1Sample2 =
@ -103,7 +107,10 @@ public class Mp4MuxerEndToEndTest {
public void createMp4File_withDifferentTracksOffset_matchesExpected() throws IOException { public void createMp4File_withDifferentTracksOffset_matchesExpected() throws IOException {
String outputFilePath = temporaryFolder.newFile().getPath(); String outputFilePath = temporaryFolder.newFile().getPath();
Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
mp4Muxer.setModificationTime(/* timestampMs= */ 500_000_000L); mp4Muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 100_000_000L,
/* modificationTimestampSeconds= */ 500_000_000L));
Pair<ByteBuffer, BufferInfo> track1Sample1 = Pair<ByteBuffer, BufferInfo> track1Sample1 =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L); MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L);
Pair<ByteBuffer, BufferInfo> track1Sample2 = Pair<ByteBuffer, BufferInfo> track1Sample2 =
@ -162,7 +169,10 @@ public class Mp4MuxerEndToEndTest {
public void createMp4File_withOneTrackEmpty_doesNotWriteEmptyTrack() throws Exception { public void createMp4File_withOneTrackEmpty_doesNotWriteEmptyTrack() throws Exception {
String outputFilePath = temporaryFolder.newFile().getPath(); String outputFilePath = temporaryFolder.newFile().getPath();
Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
mp4Muxer.setModificationTime(/* timestampMs= */ 500_000_000L); mp4Muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 100_000_000L,
/* modificationTimestampSeconds= */ 500_000_000L));
Pair<ByteBuffer, BufferInfo> track1Sample1 = Pair<ByteBuffer, BufferInfo> track1Sample1 =
MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L); MuxerTestUtil.getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 0L);
Pair<ByteBuffer, BufferInfo> track1Sample2 = Pair<ByteBuffer, BufferInfo> track1Sample2 =

View File

@ -20,6 +20,7 @@ import static androidx.media3.muxer.MuxerTestUtil.FAKE_VIDEO_FORMAT;
import android.content.Context; import android.content.Context;
import android.media.MediaCodec.BufferInfo; import android.media.MediaCodec.BufferInfo;
import android.util.Pair; import android.util.Pair;
import androidx.media3.container.Mp4TimestampData;
import androidx.media3.extractor.mp4.Mp4Extractor; import androidx.media3.extractor.mp4.Mp4Extractor;
import androidx.media3.muxer.Mp4Muxer.TrackToken; import androidx.media3.muxer.Mp4Muxer.TrackToken;
import androidx.media3.test.utils.DumpFileAsserts; import androidx.media3.test.utils.DumpFileAsserts;
@ -53,7 +54,10 @@ public class Mp4MuxerMetadataTest {
Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
try { try {
muxer.setModificationTime(/* timestampMs= */ 5_000_000L); muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second); muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
} finally { } finally {
@ -75,7 +79,10 @@ public class Mp4MuxerMetadataTest {
Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
try { try {
muxer.setModificationTime(/* timestampMs= */ 5_000_000L); muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second); muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
@ -99,7 +106,10 @@ public class Mp4MuxerMetadataTest {
Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
try { try {
muxer.setModificationTime(/* timestampMs= */ 5_000_000L); muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second); muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
@ -123,7 +133,10 @@ public class Mp4MuxerMetadataTest {
Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
try { try {
muxer.setModificationTime(/* timestampMs= */ 5_000_000L); muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second); muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
@ -147,7 +160,10 @@ public class Mp4MuxerMetadataTest {
Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
try { try {
muxer.setModificationTime(/* timestampMs= */ 5_000_000L); muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second); muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
muxer.setLocation(33.0f, -120f); muxer.setLocation(33.0f, -120f);
@ -170,7 +186,10 @@ public class Mp4MuxerMetadataTest {
Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
try { try {
muxer.setModificationTime(/* timestampMs= */ 5_000_000L); muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second); muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
} finally { } finally {
@ -192,7 +211,10 @@ public class Mp4MuxerMetadataTest {
Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
try { try {
muxer.setModificationTime(/* timestampMs= */ 5_000_000L); muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
muxer.setCaptureFps(120.0f); muxer.setCaptureFps(120.0f);
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second); muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
@ -215,7 +237,10 @@ public class Mp4MuxerMetadataTest {
Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
try { try {
muxer.setModificationTime(/* timestampMs= */ 5_000_000L); muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
muxer.addMetadata("SomeStringKey", "Some Random String"); muxer.addMetadata("SomeStringKey", "Some Random String");
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second); muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
@ -238,7 +263,10 @@ public class Mp4MuxerMetadataTest {
Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
try { try {
muxer.setModificationTime(/* timestampMs= */ 5_000_000L); muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
muxer.addMetadata("SomeStringKey", 10.0f); muxer.addMetadata("SomeStringKey", 10.0f);
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT); TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second); muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
@ -261,7 +289,10 @@ public class Mp4MuxerMetadataTest {
Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build(); Mp4Muxer muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
try { try {
muxer.setModificationTime(/* timestampMs= */ 5_000_000L); muxer.setTimestampData(
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
Context context = ApplicationProvider.getApplicationContext(); Context context = ApplicationProvider.getApplicationContext();
byte[] xmpBytes = TestUtil.getByteArray(context, XMP_SAMPLE_DATA); byte[] xmpBytes = TestUtil.getByteArray(context, XMP_SAMPLE_DATA);
ByteBuffer xmp = ByteBuffer.wrap(xmpBytes); ByteBuffer xmp = ByteBuffer.wrap(xmpBytes);

View File

@ -1,2 +1,2 @@
tkhd (92 bytes): tkhd (92 bytes):
Data = length 84, hash 8F9E5354 Data = length 84, hash C3AC4BE9

View File

@ -13,7 +13,7 @@ track 0:
id = 1 id = 1
sampleMimeType = application/meta sampleMimeType = application/meta
maxInputSize = 161 maxInputSize = 161
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
sample 0: sample 0:
time = 0 time = 0
flags = 1 flags = 1
@ -39,7 +39,7 @@ track 1:
channelCount = 2 channelCount = 2
sampleRate = 48000 sampleRate = 48000
language = ``` language = ```
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 2, hash 560 data = length 2, hash 560
sample 0: sample 0:
@ -71,7 +71,7 @@ track 2:
colorTransfer = 3 colorTransfer = 3
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 85, hash 6F3CAA16 data = length 85, hash 6F3CAA16
sample 0: sample 0:

View File

@ -23,7 +23,7 @@ track 0:
colorTransfer = 6 colorTransfer = 6
lumaBitdepth = 10 lumaBitdepth = 10
chromaBitdepth = 10 chromaBitdepth = 10
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 99, hash 99842E5A data = length 99, hash 99842E5A
sample 0: sample 0:
@ -547,7 +547,7 @@ track 1:
channelCount = 2 channelCount = 2
sampleRate = 48000 sampleRate = 48000
language = ``` language = ```
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 2, hash 560 data = length 2, hash 560
sample 0: sample 0:

View File

@ -2,13 +2,13 @@ ftyp (28 bytes):
Data = length 20, hash EF896440 Data = length 20, hash EF896440
moov (1209 bytes): moov (1209 bytes):
mvhd (108 bytes): mvhd (108 bytes):
Data = length 100, hash 5CF3AC6F Data = length 100, hash 2FE65289
trak (610 bytes): trak (610 bytes):
tkhd (92 bytes): tkhd (92 bytes):
Data = length 84, hash 112173F4 Data = length 84, hash 7E478E0E
mdia (510 bytes): mdia (510 bytes):
mdhd (32 bytes): mdhd (32 bytes):
Data = length 24, hash 42753A93 Data = length 24, hash 87E287AD
hdlr (44 bytes): hdlr (44 bytes):
Data = length 36, hash A0852FF2 Data = length 36, hash A0852FF2
minf (426 bytes): minf (426 bytes):
@ -31,10 +31,10 @@ moov (1209 bytes):
Data = length 8, hash 94446F01 Data = length 8, hash 94446F01
trak (411 bytes): trak (411 bytes):
tkhd (92 bytes): tkhd (92 bytes):
Data = length 84, hash 34D7906B Data = length 84, hash A1FDAA85
mdia (311 bytes): mdia (311 bytes):
mdhd (32 bytes): mdhd (32 bytes):
Data = length 24, hash EA3D1FE6 Data = length 24, hash 2FAA6D00
hdlr (44 bytes): hdlr (44 bytes):
Data = length 36, hash 49FC755F Data = length 36, hash 49FC755F
minf (227 bytes): minf (227 bytes):

View File

@ -1,2 +1,2 @@
mdhd (32 bytes): mdhd (32 bytes):
Data = length 24, hash D0792E76 Data = length 24, hash 71B9FC8B

View File

@ -20,7 +20,7 @@ track 0:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=2082849800, modification time=2082849800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=1000000, modification time=5000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682

View File

@ -21,7 +21,7 @@ track 0:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=2082849800, modification time=2082849800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=1000000, modification time=5000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682

View File

@ -21,7 +21,7 @@ track 0:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=2082849800, modification time=2082849800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=1000000, modification time=5000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682

View File

@ -21,7 +21,7 @@ track 0:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=2082849800, modification time=2082849800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=1000000, modification time=5000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682

View File

@ -21,7 +21,7 @@ track 0:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682
@ -48,7 +48,7 @@ track 1:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682

View File

@ -20,7 +20,7 @@ track 0:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[mdta: key=SomeStringKey, value=10.0, Mp4Timestamp: creation time=2082849800, modification time=2082849800, timescale=10000] metadata = entries=[mdta: key=SomeStringKey, value=10.0, Mp4Timestamp: creation time=1000000, modification time=5000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682

View File

@ -20,7 +20,7 @@ track 0:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[mdta: key=com.android.capture.fps, value=120.0, Mp4Timestamp: creation time=2082849800, modification time=2082849800, timescale=10000] metadata = entries=[mdta: key=com.android.capture.fps, value=120.0, Mp4Timestamp: creation time=1000000, modification time=5000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682

View File

@ -20,7 +20,7 @@ track 0:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[xyz: latitude=33.0, longitude=-120.0, Mp4Timestamp: creation time=2082849800, modification time=2082849800, timescale=10000] metadata = entries=[xyz: latitude=33.0, longitude=-120.0, Mp4Timestamp: creation time=1000000, modification time=5000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682

View File

@ -20,7 +20,7 @@ track 0:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=2082849800, modification time=2082849800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=1000000, modification time=5000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682

View File

@ -21,7 +21,7 @@ track 0:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682
@ -48,7 +48,7 @@ track 1:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682

View File

@ -20,7 +20,7 @@ track 0:
colorRange = 1 colorRange = 1
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[mdta: key=SomeStringKey, value=Some Random String, Mp4Timestamp: creation time=2082849800, modification time=2082849800, timescale=10000] metadata = entries=[mdta: key=SomeStringKey, value=Some Random String, Mp4Timestamp: creation time=1000000, modification time=5000000, timescale=10000]
initializationData: initializationData:
data = length 28, hash 410B510 data = length 28, hash 410B510
data = length 9, hash FBADD682 data = length 9, hash FBADD682

View File

@ -4,13 +4,13 @@ mdat (71 bytes):
Data = length 55, hash 6B19F4A7 Data = length 55, hash 6B19F4A7
moov (658 bytes): moov (658 bytes):
mvhd (108 bytes): mvhd (108 bytes):
Data = length 100, hash A5ADE288 Data = length 100, hash 2613A5C
trak (542 bytes): trak (542 bytes):
tkhd (92 bytes): tkhd (92 bytes):
Data = length 84, hash 8893F5BB Data = length 84, hash 3D79758F
mdia (442 bytes): mdia (442 bytes):
mdhd (32 bytes): mdhd (32 bytes):
Data = length 24, hash 50217AD Data = length 24, hash 41542D81
hdlr (44 bytes): hdlr (44 bytes):
Data = length 36, hash A0852FF2 Data = length 36, hash A0852FF2
minf (358 bytes): minf (358 bytes):

View File

@ -4,13 +4,13 @@ mdat (126 bytes):
Data = length 110, hash 48173D41 Data = length 110, hash 48173D41
moov (674 bytes): moov (674 bytes):
mvhd (108 bytes): mvhd (108 bytes):
Data = length 100, hash 3D6D026F Data = length 100, hash 105FA889
trak (558 bytes): trak (558 bytes):
tkhd (92 bytes): tkhd (92 bytes):
Data = length 84, hash 3EFBEC22 Data = length 84, hash AC22063C
mdia (458 bytes): mdia (458 bytes):
mdhd (32 bytes): mdhd (32 bytes):
Data = length 24, hash 42F40E1C Data = length 24, hash 88615B36
hdlr (44 bytes): hdlr (44 bytes):
Data = length 36, hash A0852FF2 Data = length 36, hash A0852FF2
minf (374 bytes): minf (374 bytes):

View File

@ -1,2 +1,2 @@
mvhd (108 bytes): mvhd (108 bytes):
Data = length 100, hash 22E47B06 Data = length 100, hash 1EE0A99B

View File

@ -23,7 +23,7 @@ track 0:
colorTransfer = 6 colorTransfer = 6
lumaBitdepth = 10 lumaBitdepth = 10
chromaBitdepth = 10 chromaBitdepth = 10
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 99, hash 99842E5A data = length 99, hash 99842E5A
sample 0: sample 0:
@ -415,7 +415,7 @@ track 1:
channelCount = 2 channelCount = 2
sampleRate = 48000 sampleRate = 48000
language = ``` language = ```
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 2, hash 560 data = length 2, hash 560
sample 0: sample 0:

View File

@ -19,7 +19,7 @@ track 0:
colorInfo: colorInfo:
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
sample 0: sample 0:
time = 0 time = 0
flags = 1 flags = 1
@ -153,7 +153,7 @@ track 1:
channelCount = 1 channelCount = 1
sampleRate = 44100 sampleRate = 44100
language = und language = und
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 5, hash 2B7623A data = length 5, hash 2B7623A
sample 0: sample 0:

View File

@ -17,7 +17,7 @@ track 0:
channelCount = 1 channelCount = 1
sampleRate = 44100 sampleRate = 44100
language = und language = und
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 2, hash 5F7 data = length 2, hash 5F7
sample 0: sample 0:
@ -217,7 +217,7 @@ track 1:
colorTransfer = 3 colorTransfer = 3
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[Mp4Timestamp: creation time=2083344800, modification time=2083344800, timescale=10000] metadata = entries=[Mp4Timestamp: creation time=100000000, modification time=500000000, timescale=10000]
initializationData: initializationData:
data = length 23, hash 33E412EE data = length 23, hash 33E412EE
data = length 9, hash FBAFBC0C data = length 9, hash FBAFBC0C

View File

@ -1,2 +1,2 @@
tkhd (92 bytes): tkhd (92 bytes):
Data = length 84, hash D09E9E0B Data = length 84, hash 4AC96A0

View File

@ -23,7 +23,7 @@ track 0:
colorTransfer = 3 colorTransfer = 3
lumaBitdepth = 8 lumaBitdepth = 8
chromaBitdepth = 8 chromaBitdepth = 8
metadata = entries=[mdta: key=com.android.version, value=13, xyz: latitude=40.68, longitude=-74.4999, Mp4Timestamp: creation time=2000000000, modification time=2000000000, timescale=10000] metadata = entries=[mdta: key=com.android.version, value=13, xyz: latitude=40.68, longitude=-74.4999, Mp4Timestamp: creation time=3000000000, modification time=4000000000, timescale=10000]
initializationData: initializationData:
data = length 23, hash 33E412EE data = length 23, hash 33E412EE
data = length 9, hash FBAFBC0C data = length 9, hash FBAFBC0C
@ -158,7 +158,7 @@ track 1:
channelCount = 1 channelCount = 1
sampleRate = 44100 sampleRate = 44100
language = und language = und
metadata = entries=[mdta: key=com.android.version, value=13, xyz: latitude=40.68, longitude=-74.4999, Mp4Timestamp: creation time=2000000000, modification time=2000000000, timescale=10000] metadata = entries=[mdta: key=com.android.version, value=13, xyz: latitude=40.68, longitude=-74.4999, Mp4Timestamp: creation time=3000000000, modification time=4000000000, timescale=10000]
initializationData: initializationData:
data = length 2, hash 5F7 data = length 2, hash 5F7
sample 0: sample 0:

View File

@ -28,7 +28,6 @@ import androidx.media3.common.util.Util;
import androidx.media3.container.MdtaMetadataEntry; import androidx.media3.container.MdtaMetadataEntry;
import androidx.media3.container.Mp4LocationData; import androidx.media3.container.Mp4LocationData;
import androidx.media3.container.Mp4TimestampData; import androidx.media3.container.Mp4TimestampData;
import androidx.media3.container.Mp4Util;
import androidx.media3.container.XmpData; import androidx.media3.container.XmpData;
import androidx.media3.muxer.Mp4Muxer; import androidx.media3.muxer.Mp4Muxer;
import androidx.media3.muxer.Mp4Muxer.TrackToken; import androidx.media3.muxer.Mp4Muxer.TrackToken;
@ -290,9 +289,7 @@ public final class InAppMuxer implements Muxer {
} else if (entry instanceof XmpData) { } else if (entry instanceof XmpData) {
mp4Muxer.addXmp(ByteBuffer.wrap(((XmpData) entry).data)); mp4Muxer.addXmp(ByteBuffer.wrap(((XmpData) entry).data));
} else if (entry instanceof Mp4TimestampData) { } else if (entry instanceof Mp4TimestampData) {
// TODO: b/285281716 - Use creation time specific API. mp4Muxer.setTimestampData((Mp4TimestampData) entry);
mp4Muxer.setModificationTime(
Mp4Util.mp4TimeToUnixTimeMs(((Mp4TimestampData) entry).creationTimestampSeconds));
} else if (entry instanceof MdtaMetadataEntry) { } else if (entry instanceof MdtaMetadataEntry) {
MdtaMetadataEntry mdtaMetadataEntry = (MdtaMetadataEntry) entry; MdtaMetadataEntry mdtaMetadataEntry = (MdtaMetadataEntry) entry;
if (mdtaMetadataEntry.key.equals(MdtaMetadataEntry.KEY_ANDROID_CAPTURE_FPS)) { if (mdtaMetadataEntry.key.equals(MdtaMetadataEntry.KEY_ANDROID_CAPTURE_FPS)) {

View File

@ -16,7 +16,6 @@
package androidx.media3.transformer; package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.container.Mp4TimestampData.TIMESCALE_UNSET;
import static androidx.media3.test.utils.FileUtil.retrieveTrackFormat; import static androidx.media3.test.utils.FileUtil.retrieveTrackFormat;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
@ -70,9 +69,8 @@ public class TransformerWithInAppMuxerEndToEndTest {
// Add timestamp to make output file deterministic. // Add timestamp to make output file deterministic.
metadataEntries.add( metadataEntries.add(
new Mp4TimestampData( new Mp4TimestampData(
/* creationTimestampSeconds= */ 2_000_000_000L, /* creationTimestampSeconds= */ 3_000_000_000L,
/* modificationTimestampSeconds= */ 2_000_000_000L, /* modificationTimestampSeconds= */ 4_000_000_000L)))
TIMESCALE_UNSET)))
.build(); .build();
Transformer transformer = Transformer transformer =
@ -183,12 +181,10 @@ public class TransformerWithInAppMuxerEndToEndTest {
@Test @Test
public void transmux_withTimestampData_writesSameTimestampData() throws Exception { public void transmux_withTimestampData_writesSameTimestampData() throws Exception {
// TODO: b/285281716 - Use different value for modification timestamp.
Mp4TimestampData expectedTimestampData = Mp4TimestampData expectedTimestampData =
new Mp4TimestampData( new Mp4TimestampData(
/* creationTimestampSeconds= */ 2_000_000_000L, /* creationTimestampSeconds= */ 3_000_000_000L,
/* modificationTimestampSeconds= */ 2_000_000_000L, /* modificationTimestampSeconds= */ 4_000_000_000L);
TIMESCALE_UNSET);
Muxer.Factory inAppMuxerFactory = Muxer.Factory inAppMuxerFactory =
new InAppMuxer.Factory.Builder() new InAppMuxer.Factory.Builder()
.setMetadataProvider(metadataEntries -> metadataEntries.add(expectedTimestampData)) .setMetadataProvider(metadataEntries -> metadataEntries.add(expectedTimestampData))