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 d3051cc5eb..6fbf4d4427 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Boxes.java @@ -131,7 +131,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull; MetadataCollector metadataCollector, long minInputPtsUs, boolean isFragmentedMp4, - @Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior) { + @Mp4Muxer.LastSampleDurationBehavior int lastSampleDurationBehavior) { // 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. @@ -157,7 +157,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull; track.writtenSamples(), minInputPtsUs, track.videoUnitTimebase(), - lastFrameDurationBehavior); + lastSampleDurationBehavior); long trackDurationInTrackUnitsVu = 0; for (int j = 0; j < sampleDurationsVu.size(); j++) { @@ -808,7 +808,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull; * Otherwise this should be equal to the presentation timestamp of first sample present in the * {@code samplesInfo} list. * @param videoUnitTimescale The timescale of the track. - * @param lastDurationBehavior The behaviour for the last sample duration. + * @param lastSampleDurationBehavior The behaviour for the last sample duration. * @return A list of all the sample durations. */ // TODO: b/280084657 - Add support for setting last sample duration. @@ -816,7 +816,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull; List samplesInfo, long firstSamplePresentationTimeUs, int videoUnitTimescale, - @Mp4Muxer.LastFrameDurationBehavior int lastDurationBehavior) { + @Mp4Muxer.LastSampleDurationBehavior int lastSampleDurationBehavior) { List presentationTimestampsUs = new ArrayList<>(samplesInfo.size()); List durationsVu = new ArrayList<>(samplesInfo.size()); @@ -855,7 +855,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull; // Default duration for the last sample. durationsVu.add(0); - adjustLastSampleDuration(durationsVu, lastDurationBehavior); + adjustLastSampleDuration(durationsVu, lastSampleDurationBehavior); return durationsVu; } @@ -1264,7 +1264,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull; // TODO: b/317117431 - Change this method to getLastSampleDuration(). /** Adjusts the duration of the very last sample if needed. */ private static void adjustLastSampleDuration( - List durationsToBeAdjustedVu, @Mp4Muxer.LastFrameDurationBehavior int behavior) { + List durationsToBeAdjustedVu, @Mp4Muxer.LastSampleDurationBehavior int behavior) { // Technically, MP4 file stores frame durations, not timestamps. If a frame starts at a // given timestamp then the duration of the last frame is not obvious. If samples follow each // other in roughly regular intervals (e.g. in a normal, 30 fps video), it can be safely assumed @@ -1278,14 +1278,14 @@ import org.checkerframework.checker.nullness.qual.PolyNull; } switch (behavior) { - case Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION: + case Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION: // This is the default MediaMuxer behavior: the last sample duration is a copy of the // previous sample duration. durationsToBeAdjustedVu.set( durationsToBeAdjustedVu.size() - 1, durationsToBeAdjustedVu.get(durationsToBeAdjustedVu.size() - 2)); break; - case Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME: + case Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE: // Keep the last sample duration as short as possible. checkState(Iterables.getLast(durationsToBeAdjustedVu) == 0L); break; diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Writer.java b/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Writer.java index b89cfdebba..14b07e293d 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Writer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Writer.java @@ -68,7 +68,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private final AnnexBToAvccConverter annexBToAvccConverter; private final long fragmentDurationUs; private final boolean sampleCopyEnabled; - private final @Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior; + private final @Mp4Muxer.LastSampleDurationBehavior int lastSampleDurationBehavior; private final List tracks; private @MonotonicNonNull Track videoTrack; @@ -100,7 +100,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; this.annexBToAvccConverter = annexBToAvccConverter; this.fragmentDurationUs = fragmentDurationMs * 1_000; this.sampleCopyEnabled = sampleCopyEnabled; - lastFrameDurationBehavior = Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION; + lastSampleDurationBehavior = Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION; tracks = new ArrayList<>(); minInputPresentationTimeUs = Long.MAX_VALUE; currentFragmentSequenceNumber = 1; @@ -207,7 +207,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; metadataCollector, /* minInputPtsUs= */ 0L, /* isFragmentedMp4= */ true, - lastFrameDurationBehavior)); + lastSampleDurationBehavior)); } private boolean shouldFlushPendingSamples( @@ -333,7 +333,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; ? minInputPresentationTimeUs : pendingSamplesBufferInfo.get(0).presentationTimeUs, track.videoUnitTimebase(), - Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION); + Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION); List sampleCompositionTimeOffsets = Boxes.calculateSampleCompositionTimeOffsets( 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 e5680edbc6..8fa20c6a1d 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java @@ -145,19 +145,19 @@ public final class Mp4Muxer implements Muxer { @Retention(RetentionPolicy.SOURCE) @Target(TYPE_USE) @IntDef({ - LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION, - LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME + LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE, + LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION, }) - public @interface LastFrameDurationBehavior {} + public @interface LastSampleDurationBehavior {} /** Insert a zero-length last sample. */ - public static final int LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME = 0; + public static final int LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE = 0; /** * Use the difference between the last timestamp and the one before that as the duration of the * last sample. */ - public static final int LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION = 1; + public static final int LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION = 1; /** The specific MP4 file format. */ @Documented @@ -183,7 +183,7 @@ public final class Mp4Muxer implements Muxer { public static final class Builder { private final FileOutputStream outputStream; - private @LastFrameDurationBehavior int lastFrameDurationBehavior; + private @LastSampleDurationBehavior int lastSampleDurationBehavior; @Nullable private AnnexBToAvccConverter annexBToAvccConverter; private boolean sampleCopyEnabled; private boolean attemptStreamableOutputEnabled; @@ -197,21 +197,21 @@ public final class Mp4Muxer implements Muxer { */ public Builder(FileOutputStream outputStream) { this.outputStream = outputStream; - lastFrameDurationBehavior = LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME; + lastSampleDurationBehavior = LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE; sampleCopyEnabled = true; attemptStreamableOutputEnabled = true; outputFileFormat = FILE_FORMAT_DEFAULT; } /** - * Sets the {@link LastFrameDurationBehavior} for the video track. + * Sets the {@link LastSampleDurationBehavior}. * - *

The default value is {@link #LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME}. + *

The default value is {@link #LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE}. */ @CanIgnoreReturnValue - public Mp4Muxer.Builder setLastFrameDurationBehavior( - @LastFrameDurationBehavior int lastFrameDurationBehavior) { - this.lastFrameDurationBehavior = lastFrameDurationBehavior; + public Mp4Muxer.Builder setLastSampleDurationBehavior( + @LastSampleDurationBehavior int lastSampleDurationBehavior) { + this.lastSampleDurationBehavior = lastSampleDurationBehavior; return this; } @@ -290,7 +290,7 @@ public final class Mp4Muxer implements Muxer { "EditablevideoParameters must be set for FILE_FORMAT_EDITABLE_VIDEO"); return new Mp4Muxer( outputStream, - lastFrameDurationBehavior, + lastSampleDurationBehavior, annexBToAvccConverter == null ? AnnexBToAvccConverter.DEFAULT : annexBToAvccConverter, sampleCopyEnabled, attemptStreamableOutputEnabled, @@ -303,7 +303,7 @@ public final class Mp4Muxer implements Muxer { private final FileOutputStream outputStream; private final FileChannel outputChannel; - private final @LastFrameDurationBehavior int lastFrameDurationBehavior; + private final @LastSampleDurationBehavior int lastSampleDurationBehavior; private final AnnexBToAvccConverter annexBToAvccConverter; private final boolean sampleCopyEnabled; private final boolean attemptStreamableOutputEnabled; @@ -320,7 +320,7 @@ public final class Mp4Muxer implements Muxer { private Mp4Muxer( FileOutputStream outputStream, - @LastFrameDurationBehavior int lastFrameDurationBehavior, + @LastSampleDurationBehavior int lastFrameDurationBehavior, AnnexBToAvccConverter annexBToAvccConverter, boolean sampleCopyEnabled, boolean attemptStreamableOutputEnabled, @@ -328,7 +328,7 @@ public final class Mp4Muxer implements Muxer { @Nullable EditableVideoParameters editableVideoParameters) { this.outputStream = outputStream; outputChannel = outputStream.getChannel(); - this.lastFrameDurationBehavior = lastFrameDurationBehavior; + this.lastSampleDurationBehavior = lastFrameDurationBehavior; this.annexBToAvccConverter = annexBToAvccConverter; this.sampleCopyEnabled = sampleCopyEnabled; this.attemptStreamableOutputEnabled = attemptStreamableOutputEnabled; @@ -504,7 +504,7 @@ public final class Mp4Muxer implements Muxer { cacheFileOutputStream.getChannel(), checkNotNull(editableVideoMetadataCollector), annexBToAvccConverter, - lastFrameDurationBehavior, + lastSampleDurationBehavior, sampleCopyEnabled, attemptStreamableOutputEnabled); } diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java index 424780cacf..9c47284900 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java @@ -49,7 +49,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private final FileChannel outputFileChannel; private final MetadataCollector metadataCollector; private final AnnexBToAvccConverter annexBToAvccConverter; - private final @Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior; + private final @Mp4Muxer.LastSampleDurationBehavior int lastSampleDurationBehavior; private final boolean sampleCopyEnabled; private final List tracks; private final List editableVideoTracks; @@ -77,8 +77,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * @param annexBToAvccConverter The {@link AnnexBToAvccConverter} to be used to convert H.264 and * H.265 NAL units from the Annex-B format (using start codes to delineate NAL units) to the * AVCC format (which uses length prefixes). - * @param lastFrameDurationBehavior The {@link Mp4Muxer.LastFrameDurationBehavior} for the video - * track. + * @param lastSampleDurationBehavior The {@link Mp4Muxer.LastSampleDurationBehavior}. * @param sampleCopyEnabled Whether sample copying is enabled. * @param attemptStreamableOutputEnabled Whether to attempt to write a streamable output. */ @@ -86,13 +85,13 @@ import java.util.concurrent.atomic.AtomicBoolean; FileChannel fileChannel, MetadataCollector metadataCollector, AnnexBToAvccConverter annexBToAvccConverter, - @Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior, + @Mp4Muxer.LastSampleDurationBehavior int lastSampleDurationBehavior, boolean sampleCopyEnabled, boolean attemptStreamableOutputEnabled) { this.outputFileChannel = fileChannel; this.metadataCollector = metadataCollector; this.annexBToAvccConverter = annexBToAvccConverter; - this.lastFrameDurationBehavior = lastFrameDurationBehavior; + this.lastSampleDurationBehavior = lastSampleDurationBehavior; this.sampleCopyEnabled = sampleCopyEnabled; tracks = new ArrayList<>(); editableVideoTracks = new ArrayList<>(); @@ -205,7 +204,7 @@ import java.util.concurrent.atomic.AtomicBoolean; editableVideoMetadataCollector, findMinimumPresentationTimestampUsAcrossTracks(editableVideoTracks), /* isFragmentedMp4= */ false, - lastFrameDurationBehavior); + lastSampleDurationBehavior); ByteBuffer edvdBoxHeader = getEdvdBoxHeader(/* payloadSize= */ ftypBox.remaining() + moovBox.remaining()); return BoxUtils.concatenateBuffers(edvdBoxHeader, ftypBox, moovBox); @@ -323,7 +322,7 @@ import java.util.concurrent.atomic.AtomicBoolean; metadataCollector, minInputPtsUs, /* isFragmentedMp4= */ false, - lastFrameDurationBehavior); + lastSampleDurationBehavior); } else { // Skip moov box, if there are no samples. moovHeader = ByteBuffer.allocate(0); 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 00d23635ff..8ca7dfa477 100644 --- a/libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java +++ b/libraries/muxer/src/test/java/androidx/media3/muxer/BoxesTest.java @@ -15,8 +15,8 @@ */ package androidx.media3.muxer; -import static androidx.media3.muxer.Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION; -import static androidx.media3.muxer.Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME; +import static androidx.media3.muxer.Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION; +import static androidx.media3.muxer.Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE; import static androidx.media3.muxer.MuxerTestUtil.FAKE_AUDIO_FORMAT; import static androidx.media3.muxer.MuxerTestUtil.FAKE_CSD_0; import static androidx.media3.muxer.MuxerTestUtil.FAKE_VIDEO_FORMAT; @@ -477,7 +477,7 @@ public class BoxesTest { sampleBufferInfos, /* firstSamplePresentationTimeUs= */ 0L, VU_TIMEBASE, - LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME); + LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE); assertThat(durationsVu).containsExactly(0); } @@ -493,7 +493,7 @@ public class BoxesTest { sampleBufferInfos, /* firstSamplePresentationTimeUs= */ 0L, VU_TIMEBASE, - LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME); + LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE); assertThat(durationsVu).containsExactly(0); } @@ -509,7 +509,7 @@ public class BoxesTest { sampleBufferInfos, /* firstSamplePresentationTimeUs= */ 0L, VU_TIMEBASE, - LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME); + LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE); assertThat(durationsVu).containsExactly(3_000, 5_000, 0); } @@ -525,7 +525,7 @@ public class BoxesTest { sampleBufferInfos, /* firstSamplePresentationTimeUs= */ 0L, VU_TIMEBASE, - LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION); + LAST_SAMPLE_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION); assertThat(durationsVu).containsExactly(3_000, 5_000, 5_000); } @@ -541,7 +541,7 @@ public class BoxesTest { sampleBufferInfos, /* firstSamplePresentationTimeUs= */ 0L, VU_TIMEBASE, - LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME); + LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE); assertThat(durationsVu).containsExactly(100, 100, 800, 100, 0); } @@ -595,7 +595,7 @@ public class BoxesTest { sampleBufferInfos, /* firstSamplePresentationTimeUs= */ 0L, VU_TIMEBASE, - LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME); + LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE); ByteBuffer cttsBox = Boxes.ctts(sampleBufferInfos, durationsVu, VU_TIMEBASE); @@ -612,7 +612,7 @@ public class BoxesTest { sampleBufferInfos, /* firstSamplePresentationTimeUs= */ 0L, VU_TIMEBASE, - LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME); + LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE); ByteBuffer cttsBox = Boxes.ctts(sampleBufferInfos, durationsVu, VU_TIMEBASE); @@ -631,7 +631,7 @@ public class BoxesTest { sampleBufferInfos, /* firstSamplePresentationTimeUs= */ 0L, VU_TIMEBASE, - LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME); + LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE); ByteBuffer cttsBox = Boxes.ctts(sampleBufferInfos, durationsVu, VU_TIMEBASE); @@ -651,7 +651,7 @@ public class BoxesTest { sampleBufferInfos, /* firstSamplePresentationTimeUs= */ 23698215060L, VU_TIMEBASE, - LAST_FRAME_DURATION_BEHAVIOR_INSERT_SHORT_FRAME); + LAST_SAMPLE_DURATION_BEHAVIOR_INSERT_SHORT_SAMPLE); ByteBuffer cttsBox = Boxes.ctts(sampleBufferInfos, durationsVu, VU_TIMEBASE);