Skip to content

Commit

Permalink
Remove Mp4Writer interface
Browse files Browse the repository at this point in the history
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
  • Loading branch information
SheenaChhabra authored and Copybara-Service committed Mar 27, 2024
1 parent 5a318de commit 66547cc
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,8 @@
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;
Expand Down Expand Up @@ -78,23 +76,20 @@ public BasicMp4Writer(
lastMoovWritten = Range.closed(0L, 0L);
}

@Override
public TrackToken addTrack(int sortKey, Format format) {
Track track = new Track(format, sortKey);
tracks.add(track);
Collections.sort(tracks, (a, b) -> Integer.compare(a.sortKey, b.sortKey));
return track;
}

@Override
public void writeSampleData(TrackToken token, ByteBuffer byteBuffer, BufferInfo bufferInfo)
throws IOException {
checkArgument(token instanceof Track);
((Track) token).writeSampleData(byteBuffer, bufferInfo);
doInterleave();
}

@Override
public void close() throws IOException {
try {
for (int i = 0; i < tracks.size(); i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@
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;
Expand Down Expand Up @@ -88,7 +88,6 @@ public FragmentedMp4Writer(
currentFragmentSequenceNumber = 1;
}

@Override
public TrackToken addTrack(int sortKey, Format format) {
Track track = new Track(format);
tracks.add(track);
Expand All @@ -98,7 +97,6 @@ public TrackToken addTrack(int sortKey, Format format) {
return track;
}

@Override
public void writeSampleData(
TrackToken token, ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo)
throws IOException {
Expand All @@ -122,7 +120,6 @@ public void writeSampleData(
lastPendingSample.presentationTimeUs - firstPendingSample.presentationTimeUs);
}

@Override
public void close() throws IOException {
try {
createFragment();
Expand Down
129 changes: 0 additions & 129 deletions libraries/muxer/src/main/java/androidx/media3/muxer/Mp4Writer.java

This file was deleted.

118 changes: 118 additions & 0 deletions libraries/muxer/src/main/java/androidx/media3/muxer/Track.java
Original file line number Diff line number Diff line change
@@ -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<BufferInfo> writtenSamples;
public final List<Long> writtenChunkOffsets;
public final List<Integer> writtenChunkSampleCounts;
public final Deque<BufferInfo> pendingSamplesBufferInfo;
public final Deque<ByteBuffer> 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<BufferInfo> writtenSamples() {
return ImmutableList.copyOf(writtenSamples);
}

@Override
public ImmutableList<Long> writtenChunkOffsets() {
return ImmutableList.copyOf(writtenChunkOffsets);
}

@Override
public ImmutableList<Integer> writtenChunkSampleCounts() {
return ImmutableList.copyOf(writtenChunkSampleCounts);
}

@Override
public Format format() {
return format;
}
}

0 comments on commit 66547cc

Please sign in to comment.