From a52df6d29e74187cf6a7372869704f6a93943357 Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Thu, 18 Jul 2024 06:47:56 -0700 Subject: [PATCH] Make Mp4MoovStructure.moov() method static Creating a moov box is same as creating any other box so there is no particular need to have a separate class for this. In a follow up CL the method will be moved into Boxes.java along with other box creation methods. Made nit changes in the final fields ordering to match with constructor parameter ordering. PiperOrigin-RevId: 653602558 --- .../media3/muxer/FragmentedMp4Muxer.java | 5 +--- .../media3/muxer/FragmentedMp4Writer.java | 24 ++++++++++++------- .../media3/muxer/Mp4MoovStructure.java | 20 +++++++--------- .../java/androidx/media3/muxer/Mp4Muxer.java | 10 ++++---- .../java/androidx/media3/muxer/Mp4Writer.java | 24 +++++++++++++------ 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Muxer.java b/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Muxer.java index fc56869723c..b0137b4271f 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Muxer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Muxer.java @@ -124,13 +124,10 @@ private FragmentedMp4Muxer( FileOutputStream fileOutputStream, long fragmentDurationMs, boolean sampleCopyEnabled) { checkNotNull(fileOutputStream); metadataCollector = new MetadataCollector(); - Mp4MoovStructure moovStructure = - new Mp4MoovStructure( - metadataCollector, Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION); fragmentedMp4Writer = new FragmentedMp4Writer( fileOutputStream, - moovStructure, + metadataCollector, AnnexBToAvccConverter.DEFAULT, fragmentDurationMs, sampleCopyEnabled); 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 474133b2f9e..2f7f1485e3d 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Writer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Writer.java @@ -64,11 +64,12 @@ public SampleMetadata(int durationsVu, int size, int flags, int compositionTimeO private final FileOutputStream outputStream; private final FileChannel output; - private final Mp4MoovStructure moovGenerator; + private final MetadataCollector metadataCollector; private final AnnexBToAvccConverter annexBToAvccConverter; - private final List tracks; private final long fragmentDurationUs; private final boolean sampleCopyEnabled; + private final @Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior; + private final List tracks; private @MonotonicNonNull Track videoTrack; private int currentFragmentSequenceNumber; @@ -80,7 +81,7 @@ public SampleMetadata(int durationsVu, int size, int flags, int compositionTimeO * Creates an instance. * * @param outputStream The {@link FileOutputStream} to write the data to. - * @param moovGenerator An {@link Mp4MoovStructure} instance to generate the moov box. + * @param metadataCollector A {@link MetadataCollector}. * @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). @@ -89,17 +90,18 @@ public SampleMetadata(int durationsVu, int size, int flags, int compositionTimeO */ public FragmentedMp4Writer( FileOutputStream outputStream, - Mp4MoovStructure moovGenerator, + MetadataCollector metadataCollector, AnnexBToAvccConverter annexBToAvccConverter, long fragmentDurationMs, boolean sampleCopyEnabled) { this.outputStream = outputStream; - this.output = outputStream.getChannel(); - this.moovGenerator = moovGenerator; + output = outputStream.getChannel(); + this.metadataCollector = metadataCollector; this.annexBToAvccConverter = annexBToAvccConverter; + this.fragmentDurationUs = fragmentDurationMs * 1_000; this.sampleCopyEnabled = sampleCopyEnabled; + lastFrameDurationBehavior = Mp4Muxer.LAST_FRAME_DURATION_BEHAVIOR_DUPLICATE_PREV_DURATION; tracks = new ArrayList<>(); - this.fragmentDurationUs = fragmentDurationMs * 1_000; minInputPresentationTimeUs = Long.MAX_VALUE; currentFragmentSequenceNumber = 1; } @@ -200,8 +202,12 @@ private void createHeader() throws IOException { output.write(Boxes.ftyp()); // The minInputPtsUs is actually ignored as there are no pending samples to write. output.write( - moovGenerator.moovMetadataHeader( - tracks, /* minInputPtsUs= */ 0L, /* isFragmentedMp4= */ true)); + Mp4MoovStructure.moov( + tracks, + metadataCollector, + /* minInputPtsUs= */ 0L, + /* isFragmentedMp4= */ true, + lastFrameDurationBehavior)); } private boolean shouldFlushPendingSamples( diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4MoovStructure.java b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4MoovStructure.java index 0e03392eeb9..054b72ce094 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4MoovStructure.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4MoovStructure.java @@ -30,7 +30,7 @@ import org.checkerframework.checker.nullness.qual.PolyNull; /** Builds the moov box structure of an MP4 file. */ -/* package */ class Mp4MoovStructure { +/* package */ final class Mp4MoovStructure { /** Provides track's metadata like media format, written samples. */ public interface TrackMetadataProvider { Format format(); @@ -44,20 +44,16 @@ public interface TrackMetadataProvider { ImmutableList writtenChunkSampleCounts(); } - private final MetadataCollector metadataCollector; - private final @Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior; + private Mp4MoovStructure() {} - public Mp4MoovStructure( + /** Returns the moov box. */ + @SuppressWarnings("InlinedApi") + public static ByteBuffer moov( + List tracks, MetadataCollector metadataCollector, + long minInputPtsUs, + boolean isFragmentedMp4, @Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior) { - this.metadataCollector = metadataCollector; - this.lastFrameDurationBehavior = lastFrameDurationBehavior; - } - - /** Generates a mdat header. */ - @SuppressWarnings("InlinedApi") - public ByteBuffer moovMetadataHeader( - List 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. 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 2877a9eac73..3ed66cf536e 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Muxer.java @@ -296,13 +296,12 @@ private Mp4Muxer( this.outputFileFormat = outputFileFormat; this.cacheFileProvider = cacheFileProvider; metadataCollector = new MetadataCollector(); - Mp4MoovStructure moovStructure = - new Mp4MoovStructure(metadataCollector, lastFrameDurationBehavior); mp4Writer = new Mp4Writer( outputChannel, - moovStructure, + metadataCollector, annexBToAvccConverter, + lastFrameDurationBehavior, sampleCopyEnabled, attemptStreamableOutputEnabled); editableVideoTracks = new ArrayList<>(); @@ -465,13 +464,12 @@ private void ensureSetupForEditableVideoTracks() throws FileNotFoundException { cacheFilePath = checkNotNull(cacheFileProvider).getCacheFilePath(); cacheFileOutputStream = new FileOutputStream(cacheFilePath); editableVideoMetadataCollector = new MetadataCollector(); - Mp4MoovStructure mp4MoovStructure = - new Mp4MoovStructure(editableVideoMetadataCollector, lastFrameDurationBehavior); editableVideoMp4Writer = new Mp4Writer( cacheFileOutputStream.getChannel(), - mp4MoovStructure, + checkNotNull(editableVideoMetadataCollector), annexBToAvccConverter, + lastFrameDurationBehavior, 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 03f7f57232c..949ba92ffd1 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java @@ -43,11 +43,12 @@ private static final String FREE_BOX_TYPE = "free"; private final FileChannel outputFileChannel; - private final Mp4MoovStructure moovGenerator; + private final MetadataCollector metadataCollector; private final AnnexBToAvccConverter annexBToAvccConverter; + private final @Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior; + private final boolean sampleCopyEnabled; private final List tracks; private final AtomicBoolean hasWrittenSamples; - private final boolean sampleCopyEnabled; // Stores location of the space reserved for the moov box at the beginning of the file (after ftyp // box) @@ -67,26 +68,30 @@ * @param fileChannel The {@link FileChannel} to write the data to. The {@link FileChannel} can be * closed after {@linkplain #finishWritingSamplesAndFinalizeMoovBox() finishing writing * samples}. - * @param moovGenerator An {@link Mp4MoovStructure} instance to generate the moov box. + * @param metadataCollector A {@link MetadataCollector}. * @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 sampleCopyEnabled Whether sample copying is enabled. * @param attemptStreamableOutputEnabled Whether to attempt to write a streamable output. */ public Mp4Writer( FileChannel fileChannel, - Mp4MoovStructure moovGenerator, + MetadataCollector metadataCollector, AnnexBToAvccConverter annexBToAvccConverter, + @Mp4Muxer.LastFrameDurationBehavior int lastFrameDurationBehavior, boolean sampleCopyEnabled, boolean attemptStreamableOutputEnabled) { this.outputFileChannel = fileChannel; - this.moovGenerator = moovGenerator; + this.metadataCollector = metadataCollector; this.annexBToAvccConverter = annexBToAvccConverter; + this.lastFrameDurationBehavior = lastFrameDurationBehavior; this.sampleCopyEnabled = sampleCopyEnabled; - canWriteMoovAtStart = attemptStreamableOutputEnabled; tracks = new ArrayList<>(); hasWrittenSamples = new AtomicBoolean(false); + canWriteMoovAtStart = attemptStreamableOutputEnabled; lastMoovWritten = Range.closed(0L, 0L); } @@ -239,7 +244,12 @@ private ByteBuffer assembleCurrentMoovData() { ByteBuffer moovHeader; if (minInputPtsUs != Long.MAX_VALUE) { moovHeader = - moovGenerator.moovMetadataHeader(tracks, minInputPtsUs, /* isFragmentedMp4= */ false); + Mp4MoovStructure.moov( + tracks, + metadataCollector, + minInputPtsUs, + /* isFragmentedMp4= */ false, + lastFrameDurationBehavior); } else { // Skip moov box, if there are no samples. moovHeader = ByteBuffer.allocate(0);