From 66547cc331f4aa465848ae3e290ad32d655634c7 Mon Sep 17 00:00:00 2001 From: sheenachhabra Date: Wed, 27 Mar 2024 04:24:20 -0700 Subject: [PATCH] Remove Mp4Writer interface Since the public class has already been split into `Mp4Muxer` and `FragmentedMp4Muxer`, there is no need for having common interface for internal implementation. In the follow up CL `BasicMp4Writer` will be renamed to `Mp4Writer` which is more appropriate and aligns with public class names. PiperOrigin-RevId: 619486876 --- .../androidx/media3/muxer/BasicMp4Writer.java | 9 +- .../media3/muxer/FragmentedMp4Writer.java | 9 +- .../java/androidx/media3/muxer/Mp4Writer.java | 129 ------------------ .../java/androidx/media3/muxer/Track.java | 118 ++++++++++++++++ 4 files changed, 123 insertions(+), 142 deletions(-) delete mode 100644 libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java create mode 100644 libraries/muxer/src/main/java/androidx/media3/muxer/Track.java diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/BasicMp4Writer.java b/libraries/muxer/src/main/java/androidx/media3/muxer/BasicMp4Writer.java index 47c12fa0cd..2022fdf0b8 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/BasicMp4Writer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/BasicMp4Writer.java @@ -36,10 +36,8 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -/** - * The basic implementation of {@link Mp4Writer} which writes all the samples in a single mdat box. - */ -/* package */ final class BasicMp4Writer implements Mp4Writer { +/** Writes all media samples into a single mdat box. */ +/* package */ final class BasicMp4Writer { private static final long INTERLEAVE_DURATION_US = 1_000_000L; private final FileOutputStream outputStream; @@ -78,7 +76,6 @@ import java.util.concurrent.atomic.AtomicBoolean; lastMoovWritten = Range.closed(0L, 0L); } - @Override public TrackToken addTrack(int sortKey, Format format) { Track track = new Track(format, sortKey); tracks.add(track); @@ -86,7 +83,6 @@ import java.util.concurrent.atomic.AtomicBoolean; return track; } - @Override public void writeSampleData(TrackToken token, ByteBuffer byteBuffer, BufferInfo bufferInfo) throws IOException { checkArgument(token instanceof Track); @@ -94,7 +90,6 @@ import java.util.concurrent.atomic.AtomicBoolean; doInterleave(); } - @Override public void close() throws IOException { try { for (int i = 0; i < tracks.size(); i++) { 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 36c9bd8fb2..ad86a02412 100644 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Writer.java +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/FragmentedMp4Writer.java @@ -43,10 +43,10 @@ import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** - * An {@link Mp4Writer} implementation which writes samples into multiple fragments as per the - * fragmented MP4 (ISO/IEC 14496-12) standard. + * Writes media samples into multiple fragments as per the fragmented MP4 (ISO/IEC 14496-12) + * standard. */ -/* package */ final class FragmentedMp4Writer implements Mp4Writer { +/* package */ final class FragmentedMp4Writer { /** Provides a limited set of sample metadata. */ public static class SampleMetadata { public final long durationVu; @@ -88,7 +88,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; currentFragmentSequenceNumber = 1; } - @Override public TrackToken addTrack(int sortKey, Format format) { Track track = new Track(format); tracks.add(track); @@ -98,7 +97,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; return track; } - @Override public void writeSampleData( TrackToken token, ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo) throws IOException { @@ -122,7 +120,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; lastPendingSample.presentationTimeUs - firstPendingSample.presentationTimeUs); } - @Override public void close() throws IOException { try { createFragment(); diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java b/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java deleted file mode 100644 index 9b0c1091cd..0000000000 --- a/libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2022 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.muxer; - -import static androidx.media3.common.util.Assertions.checkArgument; - -import android.media.MediaCodec; -import android.media.MediaCodec.BufferInfo; -import androidx.media3.common.C; -import androidx.media3.common.Format; -import androidx.media3.common.MimeTypes; -import androidx.media3.muxer.Muxer.TrackToken; -import com.google.common.collect.ImmutableList; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.List; - -/** Writes MP4 data to the disk. */ -/* package */ interface Mp4Writer { - - TrackToken addTrack(int sortKey, Format format); - - void writeSampleData(Mp4Muxer.TrackToken token, ByteBuffer byteBuffer, BufferInfo bufferInfo) - throws IOException; - - void close() throws IOException; - - class Track implements TrackToken, Mp4MoovStructure.TrackMetadataProvider { - public final Format format; - public final int sortKey; - public final List writtenSamples; - public final List writtenChunkOffsets; - public final List writtenChunkSampleCounts; - public final Deque pendingSamplesBufferInfo; - public final Deque pendingSamplesByteBuffer; - public boolean hadKeyframe; - - private long lastSamplePresentationTimeUs; - - /** Creates an instance with {@code sortKey} set to 1. */ - public Track(Format format) { - this(format, /* sortKey= */ 1); - } - - /** - * Creates an instance. - * - * @param format The {@link Format} for the track. - * @param sortKey The key used for sorting the track list. - */ - public Track(Format format, int sortKey) { - this.format = format; - this.sortKey = sortKey; - writtenSamples = new ArrayList<>(); - writtenChunkOffsets = new ArrayList<>(); - writtenChunkSampleCounts = new ArrayList<>(); - pendingSamplesBufferInfo = new ArrayDeque<>(); - pendingSamplesByteBuffer = new ArrayDeque<>(); - lastSamplePresentationTimeUs = C.TIME_UNSET; - } - - public void writeSampleData(ByteBuffer byteBuffer, BufferInfo bufferInfo) throws IOException { - checkArgument( - bufferInfo.presentationTimeUs > lastSamplePresentationTimeUs, - "Out of order B-frames are not supported"); - // TODO: b/279931840 - Confirm whether muxer should throw when writing empty samples. - // Skip empty samples. - if (bufferInfo.size == 0 || byteBuffer.remaining() == 0) { - return; - } - - if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) > 0) { - hadKeyframe = true; - } - - // The video track must start with a key frame. - if (!hadKeyframe && MimeTypes.isVideo(format.sampleMimeType)) { - return; - } - - pendingSamplesBufferInfo.addLast(bufferInfo); - pendingSamplesByteBuffer.addLast(byteBuffer); - lastSamplePresentationTimeUs = bufferInfo.presentationTimeUs; - } - - @Override - public int videoUnitTimebase() { - return MimeTypes.isAudio(format.sampleMimeType) - ? 48_000 // TODO: b/270583563 - Update these with actual values from mediaFormat. - : 90_000; - } - - @Override - public ImmutableList writtenSamples() { - return ImmutableList.copyOf(writtenSamples); - } - - @Override - public ImmutableList writtenChunkOffsets() { - return ImmutableList.copyOf(writtenChunkOffsets); - } - - @Override - public ImmutableList writtenChunkSampleCounts() { - return ImmutableList.copyOf(writtenChunkSampleCounts); - } - - @Override - public Format format() { - return format; - } - } -} diff --git a/libraries/muxer/src/main/java/androidx/media3/muxer/Track.java b/libraries/muxer/src/main/java/androidx/media3/muxer/Track.java new file mode 100644 index 0000000000..24d0130d3c --- /dev/null +++ b/libraries/muxer/src/main/java/androidx/media3/muxer/Track.java @@ -0,0 +1,118 @@ +/* + * Copyright 2022 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.muxer; + +import static androidx.media3.common.util.Assertions.checkArgument; + +import android.media.MediaCodec; +import android.media.MediaCodec.BufferInfo; +import androidx.media3.common.C; +import androidx.media3.common.Format; +import androidx.media3.common.MimeTypes; +import androidx.media3.muxer.Muxer.TrackToken; +import com.google.common.collect.ImmutableList; +import java.nio.ByteBuffer; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; + +/** Represents a single track (audio, video, metadata etc.). */ +/* package */ final class Track implements TrackToken, Mp4MoovStructure.TrackMetadataProvider { + public final Format format; + public final int sortKey; + public final List writtenSamples; + public final List writtenChunkOffsets; + public final List writtenChunkSampleCounts; + public final Deque pendingSamplesBufferInfo; + public final Deque pendingSamplesByteBuffer; + public boolean hadKeyframe; + + private long lastSamplePresentationTimeUs; + + /** Creates an instance with {@code sortKey} set to 1. */ + public Track(Format format) { + this(format, /* sortKey= */ 1); + } + + /** + * Creates an instance. + * + * @param format The {@link Format} for the track. + * @param sortKey The key used for sorting the track list. + */ + public Track(Format format, int sortKey) { + this.format = format; + this.sortKey = sortKey; + writtenSamples = new ArrayList<>(); + writtenChunkOffsets = new ArrayList<>(); + writtenChunkSampleCounts = new ArrayList<>(); + pendingSamplesBufferInfo = new ArrayDeque<>(); + pendingSamplesByteBuffer = new ArrayDeque<>(); + lastSamplePresentationTimeUs = C.TIME_UNSET; + } + + public void writeSampleData(ByteBuffer byteBuffer, BufferInfo bufferInfo) { + checkArgument( + bufferInfo.presentationTimeUs > lastSamplePresentationTimeUs, + "Out of order B-frames are not supported"); + // TODO: b/279931840 - Confirm whether muxer should throw when writing empty samples. + // Skip empty samples. + if (bufferInfo.size == 0 || byteBuffer.remaining() == 0) { + return; + } + + if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) > 0) { + hadKeyframe = true; + } + + // The video track must start with a key frame. + if (!hadKeyframe && MimeTypes.isVideo(format.sampleMimeType)) { + return; + } + + pendingSamplesBufferInfo.addLast(bufferInfo); + pendingSamplesByteBuffer.addLast(byteBuffer); + lastSamplePresentationTimeUs = bufferInfo.presentationTimeUs; + } + + @Override + public int videoUnitTimebase() { + return MimeTypes.isAudio(format.sampleMimeType) + ? 48_000 // TODO: b/270583563 - Update these with actual values from mediaFormat. + : 90_000; + } + + @Override + public ImmutableList writtenSamples() { + return ImmutableList.copyOf(writtenSamples); + } + + @Override + public ImmutableList writtenChunkOffsets() { + return ImmutableList.copyOf(writtenChunkOffsets); + } + + @Override + public ImmutableList writtenChunkSampleCounts() { + return ImmutableList.copyOf(writtenChunkSampleCounts); + } + + @Override + public Format format() { + return format; + } +}