Remove TrackToken and replace with int trackId

This is to make API simpler and to have parity with `MediaMuxer`

PiperOrigin-RevId: 712954669
This commit is contained in:
sheenachhabra 2025-01-07 10:13:42 -08:00 committed by Copybara-Service
parent 8dca430e42
commit bcae7abde9
17 changed files with 227 additions and 232 deletions

View File

@ -38,6 +38,8 @@
* Muxers:
* Renamed `setSampleCopyEnabled()` method to `setSampleCopyingEnabled()`
in both `Mp4Muxer.Builder` and `FragmentedMp4Muxer.Builder`.
* `Mp4Muxer.addTrack()` and `FragmentedMp4Muxer.addTrack()` now return an
`int` track id instead of a `TrackToken`.
* IMA extension:
* Session:
* Fix bug where calling a `Player` method on a `MediaController` connected

View File

@ -44,11 +44,11 @@ import java.util.List;
context.getResources().getAssets().openFd(MP4_FILE_ASSET_DIRECTORY + inputFileName);
extractor.setDataSource(fd);
List<Muxer.TrackToken> addedTracks = new ArrayList<>();
List<Integer> addedTracks = new ArrayList<>();
for (int i = 0; i < extractor.getTrackCount(); i++) {
Muxer.TrackToken trackToken =
int trackId =
muxer.addTrack(MediaFormatUtil.createFormatFromMediaFormat(extractor.getTrackFormat(i)));
addedTracks.add(trackToken);
addedTracks.add(trackId);
extractor.selectTrack(i);
}

View File

@ -19,6 +19,7 @@ import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull;
import android.media.MediaCodec.BufferInfo;
import android.util.SparseArray;
import androidx.media3.common.Format;
import androidx.media3.common.Metadata;
import androidx.media3.common.util.UnstableApi;
@ -64,10 +65,9 @@ import java.nio.ByteBuffer;
* <p>To create a fragmented MP4 file, the caller must:
*
* <ul>
* <li>Add tracks using {@link #addTrack(Format)} which will return a {@link Mp4Muxer.TrackToken}.
* <li>Use the associated {@link Mp4Muxer.TrackToken} when {@linkplain
* #writeSampleData(Mp4Muxer.TrackToken, ByteBuffer, BufferInfo) writing samples} for that
* track.
* <li>Add tracks using {@link #addTrack(Format)} which will return a track id.
* <li>Use the associated track id when {@linkplain #writeSampleData(int, ByteBuffer, BufferInfo)
* writing samples} for that track.
* <li>{@link #close} the muxer when all data has been written.
* </ul>
*
@ -76,8 +76,8 @@ import java.nio.ByteBuffer;
* <ul>
* <li>All tracks must be added before writing any samples.
* <li>The caller is responsible for ensuring that samples of different track types are well
* interleaved by calling {@link #writeSampleData(Mp4Muxer.TrackToken, ByteBuffer,
* BufferInfo)} in an order that interleaves samples from different tracks.
* interleaved by calling {@link #writeSampleData(int, ByteBuffer, BufferInfo)} in an order
* that interleaves samples from different tracks.
* </ul>
*/
@UnstableApi
@ -121,10 +121,10 @@ public final class FragmentedMp4Muxer implements Muxer {
/**
* Sets whether to enable the sample copy.
*
* <p>If the sample copy is enabled, {@link #writeSampleData(TrackToken, ByteBuffer,
* BufferInfo)} copies the input {@link ByteBuffer} and {@link BufferInfo} before it returns, so
* it is safe to reuse them immediately. Otherwise, the muxer takes ownership of the {@link
* ByteBuffer} and the {@link BufferInfo} and the caller must not modify them.
* <p>If the sample copy is enabled, {@link #writeSampleData(int, ByteBuffer, BufferInfo)}
* copies the input {@link ByteBuffer} and {@link BufferInfo} before it returns, so it is safe
* to reuse them immediately. Otherwise, the muxer takes ownership of the {@link ByteBuffer} and
* the {@link BufferInfo} and the caller must not modify them.
*
* <p>The default value is {@code true}.
*/
@ -142,6 +142,7 @@ public final class FragmentedMp4Muxer implements Muxer {
private final FragmentedMp4Writer fragmentedMp4Writer;
private final MetadataCollector metadataCollector;
private final SparseArray<Track> trackIdToTrack;
private FragmentedMp4Muxer(
OutputStream outputStream, long fragmentDurationMs, boolean sampleCopyEnabled) {
@ -154,11 +155,14 @@ public final class FragmentedMp4Muxer implements Muxer {
AnnexBToAvccConverter.DEFAULT,
fragmentDurationMs,
sampleCopyEnabled);
trackIdToTrack = new SparseArray<>();
}
@Override
public TrackToken addTrack(Format format) {
return fragmentedMp4Writer.addTrack(/* sortKey= */ 1, format);
public int addTrack(Format format) {
Track track = fragmentedMp4Writer.addTrack(/* sortKey= */ 1, format);
trackIdToTrack.append(track.id, track);
return track.id;
}
/**
@ -171,7 +175,7 @@ public final class FragmentedMp4Muxer implements Muxer {
*
* <p>Note: Out of order B-frames are currently not supported.
*
* @param trackToken The {@link TrackToken} for which this sample is being written.
* @param trackId The track id for which this sample is being written.
* @param byteBuffer The encoded sample. The muxer takes ownership of the buffer if {@link
* Builder#setSampleCopyingEnabled(boolean) sample copying} is disabled. Otherwise, the
* position of the buffer is updated but the caller retains ownership.
@ -179,10 +183,10 @@ public final class FragmentedMp4Muxer implements Muxer {
* @throws MuxerException If there is any error while writing data to the disk.
*/
@Override
public void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
public void writeSampleData(int trackId, ByteBuffer byteBuffer, BufferInfo bufferInfo)
throws MuxerException {
try {
fragmentedMp4Writer.writeSampleData(trackToken, byteBuffer, bufferInfo);
fragmentedMp4Writer.writeSampleData(trackIdToTrack.get(trackId), byteBuffer, bufferInfo);
} catch (IOException e) {
throw new MuxerException(
"Failed to write sample for presentationTimeUs="

View File

@ -33,7 +33,6 @@ import android.media.MediaCodec.BufferInfo;
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Util;
import androidx.media3.muxer.Muxer.TrackToken;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.OutputStream;
@ -122,6 +121,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private boolean headerCreated;
private long minInputPresentationTimeUs;
private long maxTrackDurationUs;
private int nextTrackId;
/**
* Creates an instance.
@ -153,8 +153,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
currentFragmentSequenceNumber = 1;
}
public TrackToken addTrack(int sortKey, Format format) {
Track track = new Track(format, sampleCopyEnabled);
public Track addTrack(int sortKey, Format format) {
Track track = new Track(nextTrackId++, format, sampleCopyEnabled);
tracks.add(track);
if (MimeTypes.isVideo(format.sampleMimeType)) {
videoTrack = track;
@ -162,15 +162,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
return track;
}
public void writeSampleData(
TrackToken token, ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo)
public void writeSampleData(Track track, ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo)
throws IOException {
checkArgument(token instanceof Track);
if (!headerCreated) {
createHeader();
headerCreated = true;
}
Track track = (Track) token;
if (shouldFlushPendingSamples(track, bufferInfo)) {
createFragment();
}

View File

@ -89,9 +89,9 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
* <p>To create an MP4 container file, the caller must:
*
* <ul>
* <li>Add tracks using {@link #addTrack(int, Format)} which will return a {@link TrackToken}.
* <li>Use the associated {@link TrackToken} when {@linkplain #writeSampleData(TrackToken,
* ByteBuffer, BufferInfo) writing samples} for that track.
* <li>Add tracks using {@link #addTrack(int, Format)} which will return a track id.
* <li>Use the associated track id when {@linkplain #writeSampleData(int, ByteBuffer, BufferInfo)
* writing samples} for that track.
* <li>{@link #close} the muxer when all data has been written.
* </ul>
*
@ -100,8 +100,8 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
* <ul>
* <li>Tracks can be added at any point, even after writing some samples to other tracks.
* <li>The caller is responsible for ensuring that samples of different track types are well
* interleaved by calling {@link #writeSampleData(TrackToken, ByteBuffer, BufferInfo)} in an
* order that interleaves samples from different tracks.
* interleaved by calling {@link #writeSampleData(int, ByteBuffer, BufferInfo)} in an order
* that interleaves samples from different tracks.
* <li>When writing a file, if an error occurs and the muxer is not closed, then the output MP4
* file may still have some partial data.
* </ul>
@ -252,10 +252,10 @@ public final class Mp4Muxer implements Muxer {
/**
* Sets whether to enable the sample copy.
*
* <p>If the sample copy is enabled, {@link #writeSampleData(TrackToken, ByteBuffer,
* BufferInfo)} copies the input {@link ByteBuffer} and {@link BufferInfo} before it returns, so
* it is safe to reuse them immediately. Otherwise, the muxer takes ownership of the {@link
* ByteBuffer} and the {@link BufferInfo} and the caller must not modify them.
* <p>If the sample copy is enabled, {@link #writeSampleData(int, ByteBuffer, BufferInfo)}
* copies the input {@link ByteBuffer} and {@link BufferInfo} before it returns, so it is safe
* to reuse them immediately. Otherwise, the muxer takes ownership of the {@link ByteBuffer} and
* the {@link BufferInfo} and the caller must not modify them.
*
* <p>The default value is {@code true}.
*/
@ -269,7 +269,7 @@ public final class Mp4Muxer implements Muxer {
* Sets whether to enable sample batching.
*
* <p>If sample batching is enabled, samples are written in batches for each track, otherwise
* samples are written as they {@linkplain #writeSampleData(TrackToken, ByteBuffer, BufferInfo)
* samples are written as they {@linkplain #writeSampleData(int, ByteBuffer, BufferInfo)
* arrive}.
*
* <p>The default value is {@code true}.
@ -348,6 +348,7 @@ public final class Mp4Muxer implements Muxer {
@Nullable private final Mp4AtFileParameters mp4AtFileParameters;
private final MetadataCollector metadataCollector;
private final Mp4Writer mp4Writer;
private final List<Track> trackIdToTrack;
private final List<Track> auxiliaryTracks;
@Nullable private String cacheFilePath;
@ -355,6 +356,8 @@ public final class Mp4Muxer implements Muxer {
@Nullable private MetadataCollector auxiliaryTracksMetadataCollector;
@Nullable private Mp4Writer auxiliaryTracksMp4Writer;
private int nextTrackId;
private Mp4Muxer(
FileOutputStream outputStream,
@LastSampleDurationBehavior int lastFrameDurationBehavior,
@ -383,6 +386,7 @@ public final class Mp4Muxer implements Muxer {
sampleCopyEnabled,
sampleBatchingEnabled,
attemptStreamableOutputEnabled);
trackIdToTrack = new ArrayList<>();
auxiliaryTracks = new ArrayList<>();
}
@ -395,11 +399,12 @@ public final class Mp4Muxer implements Muxer {
* <p>The order of tracks remains same in which they are added.
*
* @param format The {@link Format} for the track.
* @return A unique {@link TrackToken}. It should be used in {@link #writeSampleData}.
* @return A unique track id. The track id is non-negative. It should be used in {@link
* #writeSampleData}.
* @throws MuxerException If an error occurs while adding track.
*/
@Override
public TrackToken addTrack(Format format) throws MuxerException {
public int addTrack(Format format) throws MuxerException {
return addTrack(/* sortKey= */ 1, format);
}
@ -410,31 +415,37 @@ public final class Mp4Muxer implements Muxer {
* other tracks.
*
* <p>The final order of tracks is determined by the provided sort key. Tracks with a lower sort
* key will always have a lower track id than tracks with a higher sort key. Ordering between
* tracks with the same sort key is not specified.
* key will be written before tracks with a higher sort key. Ordering between tracks with the same
* sort key is not specified.
*
* @param sortKey The key used for sorting the track list.
* @param format The {@link Format} for the track.
* @return A unique {@link TrackToken}. It should be used in {@link #writeSampleData}.
* @return A unique track id. The track id is non-negative. It should be used in {@link
* #writeSampleData}.
* @throws MuxerException If an error occurs while adding track.
*/
public TrackToken addTrack(int sortKey, Format format) throws MuxerException {
public int addTrack(int sortKey, Format format) throws MuxerException {
Track track;
if (outputFileFormat == FILE_FORMAT_MP4_WITH_AUXILIARY_TRACKS_EXTENSION
&& isAuxiliaryTrack(format)) {
if (checkNotNull(mp4AtFileParameters).shouldInterleaveSamples) {
// Auxiliary tracks are handled by the primary Mp4Writer.
return mp4Writer.addAuxiliaryTrack(sortKey, format);
track = mp4Writer.addAuxiliaryTrack(nextTrackId++, sortKey, format);
} else {
// Auxiliary tracks are handled by the auxiliary tracks Mp4Writer.
try {
ensureSetupForAuxiliaryTracks();
} catch (FileNotFoundException e) {
throw new MuxerException("Cache file not found", e);
}
track = auxiliaryTracksMp4Writer.addTrack(nextTrackId++, sortKey, format);
auxiliaryTracks.add(track);
}
try {
ensureSetupForAuxiliaryTracks();
} catch (FileNotFoundException e) {
throw new MuxerException("Cache file not found", e);
}
Track track = auxiliaryTracksMp4Writer.addTrack(sortKey, format);
auxiliaryTracks.add(track);
return track;
} else {
track = mp4Writer.addTrack(nextTrackId++, sortKey, format);
}
return mp4Writer.addTrack(sortKey, format);
trackIdToTrack.add(track);
return track.id;
}
/**
@ -442,13 +453,13 @@ public final class Mp4Muxer implements Muxer {
*
* <p>When sample batching is {@linkplain Mp4Muxer.Builder#setSampleBatchingEnabled(boolean)
* enabled}, provide sample data ({@link ByteBuffer}, {@link BufferInfo}) that won't be modified
* after calling the {@link #writeSampleData(TrackToken, ByteBuffer, BufferInfo)} method, unless
* sample copying is also {@linkplain Mp4Muxer.Builder#setSampleCopyingEnabled(boolean) enabled}.
* This ensures data integrity within the batch. If sample copying is {@linkplain
* after calling the {@link #writeSampleData(int, ByteBuffer, BufferInfo)} method, unless sample
* copying is also {@linkplain Mp4Muxer.Builder#setSampleCopyingEnabled(boolean) enabled}. This
* ensures data integrity within the batch. If sample copying is {@linkplain
* Mp4Muxer.Builder#setSampleCopyingEnabled(boolean) enabled}, it's safe to modify the data after
* the method returns, as the muxer internally creates a sample copy.
*
* @param trackToken The {@link TrackToken} for which this sample is being written.
* @param trackId The track id for which this sample is being written.
* @param byteBuffer The encoded sample. The muxer takes ownership of the buffer if {@link
* Builder#setSampleCopyingEnabled(boolean) sample copying} is disabled. Otherwise, the
* position of the buffer is updated but the caller retains ownership.
@ -456,12 +467,11 @@ public final class Mp4Muxer implements Muxer {
* @throws MuxerException If an error occurs while writing data to the output file.
*/
@Override
public void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
public void writeSampleData(int trackId, ByteBuffer byteBuffer, BufferInfo bufferInfo)
throws MuxerException {
checkState(trackToken instanceof Track);
Track track = (Track) trackToken;
Track track = trackIdToTrack.get(trackId);
try {
if (auxiliaryTracks.contains(trackToken)) {
if (auxiliaryTracks.contains(track)) {
checkNotNull(auxiliaryTracksMp4Writer).writeSampleData(track, byteBuffer, bufferInfo);
} else {
mp4Writer.writeSampleData(track, byteBuffer, bufferInfo);

View File

@ -111,12 +111,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
/**
* Adds a track of the given {@link Format}.
*
* @param trackId The track id for the track.
* @param sortKey The key used for sorting the track list.
* @param format The {@link Format} for the track.
* @return A unique {@link Track}. It should be used in {@link #writeSampleData}.
*/
public Track addTrack(int sortKey, Format format) {
Track track = new Track(format, sortKey, sampleCopyEnabled);
public Track addTrack(int trackId, int sortKey, Format format) {
Track track = new Track(trackId, format, sortKey, sampleCopyEnabled);
tracks.add(track);
Collections.sort(tracks, (a, b) -> Integer.compare(a.sortKey, b.sortKey));
return track;
@ -127,12 +128,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
*
* <p>See {@link MuxerUtil#isAuxiliaryTrack(Format)} for auxiliary tracks.
*
* @param trackId The track id for the track.
* @param sortKey The key used for sorting the track list.
* @param format The {@link Format} for the track.
* @return A unique {@link Track}. It should be used in {@link #writeSampleData}.
*/
public Track addAuxiliaryTrack(int sortKey, Format format) {
Track track = new Track(format, sortKey, sampleCopyEnabled);
public Track addAuxiliaryTrack(int trackId, int sortKey, Format format) {
Track track = new Track(trackId, format, sortKey, sampleCopyEnabled);
auxiliaryTracks.add(track);
Collections.sort(auxiliaryTracks, (a, b) -> Integer.compare(a.sortKey, b.sortKey));
return track;

View File

@ -44,29 +44,24 @@ public interface Muxer {
ImmutableList<String> getSupportedSampleMimeTypes(@C.TrackType int trackType);
}
/** A token representing an added track. */
interface TrackToken {}
/**
* Adds a track of the given media format.
*
* @param format The {@link Format} of the track.
* @return The {@link TrackToken} for this track, which should be passed to {@link
* #writeSampleData}.
* @return A track id for this track, which should be passed to {@link #writeSampleData}.
* @throws MuxerException If the muxer encounters a problem while adding the track.
*/
TrackToken addTrack(Format format) throws MuxerException;
int addTrack(Format format) throws MuxerException;
/**
* Writes encoded sample data.
*
* @param trackToken The {@link TrackToken} of the track, previously returned by {@link
* #addTrack(Format)}.
* @param trackId The track id, previously returned by {@link #addTrack(Format)}.
* @param byteBuffer A buffer containing the sample data to write to the container.
* @param bufferInfo The {@link BufferInfo} of the sample.
* @throws MuxerException If the muxer fails to write the sample.
*/
void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
void writeSampleData(int trackId, ByteBuffer byteBuffer, BufferInfo bufferInfo)
throws MuxerException;
/** Adds {@linkplain Metadata.Entry metadata} about the output file. */

View File

@ -22,7 +22,6 @@ 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 java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.ArrayList;
@ -30,7 +29,8 @@ import java.util.Deque;
import java.util.List;
/** Represents a single track (audio, video, metadata etc.). */
/* package */ final class Track implements TrackToken {
/* package */ final class Track {
public final int id;
public final Format format;
public final int sortKey;
public final List<BufferInfo> writtenSamples;
@ -44,18 +44,20 @@ import java.util.List;
private final boolean sampleCopyEnabled;
/** Creates an instance with {@code sortKey} set to 1. */
public Track(Format format, boolean sampleCopyEnabled) {
this(format, /* sortKey= */ 1, sampleCopyEnabled);
public Track(int trackId, Format format, boolean sampleCopyEnabled) {
this(trackId, format, /* sortKey= */ 1, sampleCopyEnabled);
}
/**
* Creates an instance.
*
* @param trackId A unique id for the track.
* @param format The {@link Format} for the track.
* @param sortKey The key used for sorting the track list.
* @param sampleCopyEnabled Whether sample copying is enabled.
*/
public Track(Format format, int sortKey, boolean sampleCopyEnabled) {
public Track(int trackId, Format format, int sortKey, boolean sampleCopyEnabled) {
id = trackId;
this.format = format;
this.sortKey = sortKey;
this.sampleCopyEnabled = sampleCopyEnabled;

View File

@ -35,7 +35,6 @@ import androidx.media3.container.Mp4TimestampData;
import androidx.media3.container.XmpData;
import androidx.media3.extractor.mp4.Mp4Extractor;
import androidx.media3.extractor.text.DefaultSubtitleParserFactory;
import androidx.media3.muxer.Muxer.TrackToken;
import androidx.media3.test.utils.DumpFileAsserts;
import androidx.media3.test.utils.DumpableMp4Box;
import androidx.media3.test.utils.FakeExtractorOutput;
@ -83,8 +82,8 @@ public class Mp4MuxerEndToEndTest {
/* value= */ Util.getUtf8Bytes("StringValue"),
MdtaMetadataEntry.TYPE_INDICATOR_STRING));
muxer.addMetadataEntry(new XmpData(xmpBytes));
TrackToken token = muxer.addTrack(FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
int trackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
} finally {
muxer.close();
}
@ -136,12 +135,12 @@ public class Mp4MuxerEndToEndTest {
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 300L);
try {
TrackToken track1 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
int track1 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
mp4Muxer.writeSampleData(track1, track1Sample1.first, track1Sample1.second);
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
// Add same track again but with different samples.
TrackToken track2 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
int track2 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
mp4Muxer.writeSampleData(track2, track2Sample1.first, track2Sample1.second);
mp4Muxer.writeSampleData(track2, track2Sample2.first, track2Sample2.second);
} finally {
@ -180,12 +179,12 @@ public class Mp4MuxerEndToEndTest {
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 200L);
try {
TrackToken track1 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
int track1 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
mp4Muxer.writeSampleData(track1, track1Sample1.first, track1Sample1.second);
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
// Add same track again but with different samples.
TrackToken track2 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
int track2 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
mp4Muxer.writeSampleData(track2, track2Sample1.first, track2Sample1.second);
mp4Muxer.writeSampleData(track2, track2Sample2.first, track2Sample2.second);
} finally {
@ -218,7 +217,7 @@ public class Mp4MuxerEndToEndTest {
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 300L);
try {
TrackToken track1 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
int track1 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
mp4Muxer.writeSampleData(track1, track1Sample1.first, track1Sample1.second);
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
mp4Muxer.writeSampleData(track1, track1Sample3.first, track1Sample3.second);
@ -257,7 +256,7 @@ public class Mp4MuxerEndToEndTest {
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 2_000L);
try {
TrackToken track1 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
int track1 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
mp4Muxer.writeSampleData(track1, track1Sample1.first, track1Sample1.second);
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
mp4Muxer.writeSampleData(track1, track1Sample3.first, track1Sample3.second);
@ -301,12 +300,12 @@ public class Mp4MuxerEndToEndTest {
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 23_001_033_192L);
try {
TrackToken track1 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
int track1 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
mp4Muxer.writeSampleData(track1, track1Sample1.first, track1Sample1.second);
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
mp4Muxer.writeSampleData(track1, track1Sample3.first, track1Sample3.second);
mp4Muxer.writeSampleData(track1, track1Sample4.first, track1Sample4.second);
TrackToken track2 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
int track2 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
mp4Muxer.writeSampleData(track2, track2Sample1.first, track2Sample1.second);
mp4Muxer.writeSampleData(track2, track2Sample2.first, track2Sample2.second);
mp4Muxer.writeSampleData(track2, track2Sample3.first, track2Sample3.second);
@ -337,7 +336,7 @@ public class Mp4MuxerEndToEndTest {
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L);
try {
TrackToken track1 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
int track1 = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
mp4Muxer.writeSampleData(track1, track1Sample1.first, track1Sample1.second);
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
// Add same track again but without any samples.
@ -365,11 +364,11 @@ public class Mp4MuxerEndToEndTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(FAKE_VIDEO_FORMAT);
int trackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
for (int i = 0; i < 50_000; i++) {
Pair<ByteBuffer, BufferInfo> sampleAndSampleInfo =
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ i);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
}
} finally {
muxer.close();
@ -397,11 +396,11 @@ public class Mp4MuxerEndToEndTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(FAKE_VIDEO_FORMAT);
int trackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
for (int i = 0; i < 1_000; i++) {
Pair<ByteBuffer, BufferInfo> sampleAndSampleInfo =
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ i);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
}
} finally {
muxer.close();
@ -448,24 +447,24 @@ public class Mp4MuxerEndToEndTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken primaryVideoTrackToken = muxer.addTrack(FAKE_VIDEO_FORMAT);
TrackToken sharpVideoTrackToken =
int primaryVideoTrackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
int sharpVideoTrackId =
muxer.addTrack(
FAKE_VIDEO_FORMAT
.buildUpon()
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
.build());
TrackToken depthLinearVideoTrackToken =
int depthLinearVideoTrackId =
muxer.addTrack(
FAKE_VIDEO_FORMAT
.buildUpon()
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
.build());
writeFakeSamples(muxer, primaryVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, sharpVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, depthLinearVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, primaryVideoTrackId, /* sampleCount= */ 5);
writeFakeSamples(muxer, sharpVideoTrackId, /* sampleCount= */ 5);
writeFakeSamples(muxer, depthLinearVideoTrackId, /* sampleCount= */ 5);
} finally {
muxer.close();
}
@ -490,24 +489,24 @@ public class Mp4MuxerEndToEndTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken primaryVideoTrackToken = muxer.addTrack(FAKE_VIDEO_FORMAT);
TrackToken sharpVideoTrackToken =
int primaryVideoTrackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
int sharpVideoTrackId =
muxer.addTrack(
FAKE_VIDEO_FORMAT
.buildUpon()
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
.build());
TrackToken depthLinearVideoTrackToken =
int depthLinearVideoTrackId =
muxer.addTrack(
FAKE_VIDEO_FORMAT
.buildUpon()
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
.build());
writeFakeSamples(muxer, primaryVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, sharpVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, depthLinearVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, primaryVideoTrackId, /* sampleCount= */ 5);
writeFakeSamples(muxer, sharpVideoTrackId, /* sampleCount= */ 5);
writeFakeSamples(muxer, depthLinearVideoTrackId, /* sampleCount= */ 5);
} finally {
muxer.close();
}
@ -540,24 +539,24 @@ public class Mp4MuxerEndToEndTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken primaryVideoTrackToken = muxer.addTrack(FAKE_VIDEO_FORMAT);
TrackToken sharpVideoTrackToken =
int primaryVideoTrackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
int sharpVideoTrackId =
muxer.addTrack(
FAKE_VIDEO_FORMAT
.buildUpon()
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
.build());
TrackToken depthLinearVideoTrackToken =
int depthLinearVideoTrackId =
muxer.addTrack(
FAKE_VIDEO_FORMAT
.buildUpon()
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
.build());
writeFakeSamples(muxer, primaryVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, sharpVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, depthLinearVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, primaryVideoTrackId, /* sampleCount= */ 5);
writeFakeSamples(muxer, sharpVideoTrackId, /* sampleCount= */ 5);
writeFakeSamples(muxer, depthLinearVideoTrackId, /* sampleCount= */ 5);
} finally {
muxer.close();
}
@ -591,24 +590,24 @@ public class Mp4MuxerEndToEndTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken primaryVideoTrackToken = muxer.addTrack(FAKE_VIDEO_FORMAT);
TrackToken sharpVideoTrackToken =
int primaryVideoTrackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
int sharpVideoTrackId =
muxer.addTrack(
FAKE_VIDEO_FORMAT
.buildUpon()
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
.build());
TrackToken depthLinearVideoTrackToken =
int depthLinearVideoTrackId =
muxer.addTrack(
FAKE_VIDEO_FORMAT
.buildUpon()
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
.build());
writeFakeSamples(muxer, primaryVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, sharpVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, depthLinearVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, primaryVideoTrackId, /* sampleCount= */ 5);
writeFakeSamples(muxer, sharpVideoTrackId, /* sampleCount= */ 5);
writeFakeSamples(muxer, depthLinearVideoTrackId, /* sampleCount= */ 5);
} finally {
muxer.close();
}
@ -642,24 +641,24 @@ public class Mp4MuxerEndToEndTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken primaryVideoTrackToken = muxer.addTrack(FAKE_VIDEO_FORMAT);
TrackToken sharpVideoTrackToken =
int primaryVideoTrackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
int sharpVideoTrackId =
muxer.addTrack(
FAKE_VIDEO_FORMAT
.buildUpon()
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
.build());
TrackToken depthLinearVideoTrackToken =
int depthLinearVideoTrackId =
muxer.addTrack(
FAKE_VIDEO_FORMAT
.buildUpon()
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
.build());
writeFakeSamples(muxer, primaryVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, sharpVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, depthLinearVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, primaryVideoTrackId, /* sampleCount= */ 5);
writeFakeSamples(muxer, sharpVideoTrackId, /* sampleCount= */ 5);
writeFakeSamples(muxer, depthLinearVideoTrackId, /* sampleCount= */ 5);
} finally {
muxer.close();
}
@ -693,24 +692,24 @@ public class Mp4MuxerEndToEndTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken primaryVideoTrackToken = muxer.addTrack(FAKE_VIDEO_FORMAT);
TrackToken sharpVideoTrackToken =
int primaryVideoTrackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
int sharpVideoTrackId =
muxer.addTrack(
FAKE_VIDEO_FORMAT
.buildUpon()
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
.build());
TrackToken depthLinearVideoTrackToken =
int depthLinearVideoTrackId =
muxer.addTrack(
FAKE_VIDEO_FORMAT
.buildUpon()
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
.build());
writeFakeSamples(muxer, primaryVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, sharpVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, depthLinearVideoTrackToken, /* sampleCount= */ 5);
writeFakeSamples(muxer, primaryVideoTrackId, /* sampleCount= */ 5);
writeFakeSamples(muxer, sharpVideoTrackId, /* sampleCount= */ 5);
writeFakeSamples(muxer, depthLinearVideoTrackId, /* sampleCount= */ 5);
} finally {
muxer.close();
}
@ -751,7 +750,7 @@ public class Mp4MuxerEndToEndTest {
long expectedDurationUs = 1_000L;
try {
TrackToken track = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
int track = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
mp4Muxer.writeSampleData(track, sample1.first, sample1.second);
mp4Muxer.writeSampleData(track, sample2.first, sample2.second);
mp4Muxer.writeSampleData(track, sample3.first, sample3.second);
@ -798,7 +797,7 @@ public class Mp4MuxerEndToEndTest {
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 300L);
try {
TrackToken track = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
int track = mp4Muxer.addTrack(FAKE_VIDEO_FORMAT);
mp4Muxer.writeSampleData(track, sample1.first, sample1.second);
mp4Muxer.writeSampleData(track, sample2.first, sample2.second);
mp4Muxer.writeSampleData(track, sample3.first, sample3.second);
@ -814,12 +813,12 @@ public class Mp4MuxerEndToEndTest {
assertThat(fakeExtractorOutput.seekMap.getDurationUs()).isEqualTo(400L);
}
private static void writeFakeSamples(Mp4Muxer muxer, TrackToken trackToken, int sampleCount)
private static void writeFakeSamples(Mp4Muxer muxer, int trackId, int sampleCount)
throws MuxerException {
for (int i = 0; i < sampleCount; i++) {
Pair<ByteBuffer, BufferInfo> sampleAndSampleInfo =
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ i);
muxer.writeSampleData(trackToken, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
}
}
}

View File

@ -33,7 +33,6 @@ import androidx.media3.container.Mp4TimestampData;
import androidx.media3.container.XmpData;
import androidx.media3.extractor.mp4.Mp4Extractor;
import androidx.media3.extractor.text.DefaultSubtitleParserFactory;
import androidx.media3.muxer.Muxer.TrackToken;
import androidx.media3.test.utils.DumpFileAsserts;
import androidx.media3.test.utils.DumpableMp4Box;
import androidx.media3.test.utils.FakeExtractorOutput;
@ -66,8 +65,8 @@ public class Mp4MuxerMetadataTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
} finally {
muxer.close();
}
@ -92,8 +91,8 @@ public class Mp4MuxerMetadataTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
muxer.addMetadataEntry(new Mp4OrientationData(/* orientation= */ 90));
} finally {
@ -120,8 +119,8 @@ public class Mp4MuxerMetadataTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
muxer.addMetadataEntry(new Mp4OrientationData(/* orientation= */ 180));
} finally {
@ -148,8 +147,8 @@ public class Mp4MuxerMetadataTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
muxer.addMetadataEntry(new Mp4OrientationData(/* orientation= */ 270));
} finally {
@ -176,8 +175,8 @@ public class Mp4MuxerMetadataTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
muxer.addMetadataEntry(new Mp4LocationData(/* latitude= */ 33.0f, /* longitude= */ -120f));
} finally {
muxer.close();
@ -203,8 +202,8 @@ public class Mp4MuxerMetadataTest {
new Mp4TimestampData(
/* creationTimestampSeconds= */ 1_000_000L,
/* modificationTimestampSeconds= */ 5_000_000L));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
} finally {
muxer.close();
}
@ -233,8 +232,8 @@ public class Mp4MuxerMetadataTest {
muxer.addMetadataEntry(
new MdtaMetadataEntry(
KEY_ANDROID_CAPTURE_FPS, Util.toByteArray(captureFps), TYPE_INDICATOR_FLOAT32));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
} finally {
muxer.close();
}
@ -262,8 +261,8 @@ public class Mp4MuxerMetadataTest {
muxer.addMetadataEntry(
new MdtaMetadataEntry(
"SomeStringKey", Util.getUtf8Bytes("Some Random String"), TYPE_INDICATOR_STRING));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
} finally {
muxer.close();
}
@ -289,10 +288,10 @@ public class Mp4MuxerMetadataTest {
muxer.addMetadataEntry(
new MdtaMetadataEntry(metadataKey, metadataValue, TYPE_INDICATOR_STRING));
}
TrackToken token = muxer.addTrack(FAKE_VIDEO_FORMAT);
int trackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
try {
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
assertThat(sampleAndSampleInfo.first.remaining()).isEqualTo(0);
} finally {
muxer.close();
@ -313,8 +312,8 @@ public class Mp4MuxerMetadataTest {
muxer.addMetadataEntry(
new MdtaMetadataEntry(
"SomeStringKey", Util.toByteArray(floatValue), TYPE_INDICATOR_FLOAT32));
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
} finally {
muxer.close();
}
@ -342,8 +341,8 @@ public class Mp4MuxerMetadataTest {
Context context = ApplicationProvider.getApplicationContext();
byte[] xmpBytes = TestUtil.getByteArray(context, XMP_SAMPLE_DATA);
muxer.addMetadataEntry(new XmpData(xmpBytes));
TrackToken token = muxer.addTrack(0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
int trackId = muxer.addTrack(0, FAKE_VIDEO_FORMAT);
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
} finally {
muxer.close();
}

View File

@ -24,7 +24,6 @@ import static org.junit.Assume.assumeFalse;
import android.content.Context;
import android.media.MediaCodec.BufferInfo;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Effect;
import androidx.media3.common.Format;
@ -448,7 +447,7 @@ public class TransformerPauseResumeTest {
private final FrameBlockingMuxer.Listener listener;
private boolean notifiedListener;
@Nullable private TrackToken videoTrackToken;
private int videoTrackId;
private FrameBlockingMuxer(Muxer wrappedMuxer, FrameBlockingMuxer.Listener listener) {
this.wrappedMuxer = wrappedMuxer;
@ -456,18 +455,18 @@ public class TransformerPauseResumeTest {
}
@Override
public TrackToken addTrack(Format format) throws MuxerException {
TrackToken trackToken = wrappedMuxer.addTrack(format);
public int addTrack(Format format) throws MuxerException {
int trackId = wrappedMuxer.addTrack(format);
if (MimeTypes.isVideo(format.sampleMimeType)) {
videoTrackToken = trackToken;
videoTrackId = trackId;
}
return trackToken;
return trackId;
}
@Override
public void writeSampleData(TrackToken trackToken, ByteBuffer data, BufferInfo bufferInfo)
public void writeSampleData(int trackId, ByteBuffer data, BufferInfo bufferInfo)
throws MuxerException {
if (trackToken == videoTrackToken
if (trackId == videoTrackId
&& bufferInfo.presentationTimeUs >= DEFAULT_PRESENTATION_TIME_US_TO_BLOCK_FRAME) {
if (!notifiedListener) {
listener.onFrameBlocked();
@ -475,7 +474,7 @@ public class TransformerPauseResumeTest {
}
return;
}
wrappedMuxer.writeSampleData(trackToken, data, bufferInfo);
wrappedMuxer.writeSampleData(trackId, data, bufferInfo);
}
@Override

View File

@ -84,14 +84,14 @@ public final class DefaultMuxer implements Muxer {
}
@Override
public TrackToken addTrack(Format format) throws MuxerException {
public int addTrack(Format format) throws MuxerException {
return muxer.addTrack(format);
}
@Override
public void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
public void writeSampleData(int trackId, ByteBuffer byteBuffer, BufferInfo bufferInfo)
throws MuxerException {
muxer.writeSampleData(trackToken, byteBuffer, bufferInfo);
muxer.writeSampleData(trackId, byteBuffer, bufferInfo);
}
@Override

View File

@ -24,7 +24,7 @@ import android.annotation.SuppressLint;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import androidx.annotation.Nullable;
import android.util.SparseArray;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.Metadata;
@ -40,9 +40,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/** {@link Muxer} implementation that uses a {@link MediaMuxer}. */
/* package */ final class FrameworkMuxer implements Muxer {
@ -105,13 +103,14 @@ import java.util.Map;
private static final ImmutableList<String> SUPPORTED_AUDIO_SAMPLE_MIME_TYPES =
ImmutableList.of(MimeTypes.AUDIO_AAC, MimeTypes.AUDIO_AMR_NB, MimeTypes.AUDIO_AMR_WB);
private static final String TAG = "FrameworkMuxer";
private static final int TRACK_ID_UNSET = -1;
private final MediaMuxer mediaMuxer;
private final long videoDurationUs;
private final Map<TrackToken, Long> trackTokenToLastPresentationTimeUs;
private final Map<TrackToken, Long> trackTokenToPresentationTimeOffsetUs;
private final SparseArray<Long> trackIdToLastPresentationTimeUs;
private final SparseArray<Long> trackIdToPresentationTimeOffsetUs;
@Nullable private TrackToken videoTrackToken;
private int videoTrackId;
private boolean isStarted;
private boolean isReleased;
@ -119,12 +118,13 @@ import java.util.Map;
private FrameworkMuxer(MediaMuxer mediaMuxer, long videoDurationUs) {
this.mediaMuxer = mediaMuxer;
this.videoDurationUs = videoDurationUs;
trackTokenToLastPresentationTimeUs = new HashMap<>();
trackTokenToPresentationTimeOffsetUs = new HashMap<>();
trackIdToLastPresentationTimeUs = new SparseArray<>();
trackIdToPresentationTimeOffsetUs = new SparseArray<>();
videoTrackId = TRACK_ID_UNSET;
}
@Override
public TrackToken addTrack(Format format) throws MuxerException {
public int addTrack(Format format) throws MuxerException {
String sampleMimeType = checkNotNull(format.sampleMimeType);
MediaFormat mediaFormat;
boolean isVideo = MimeTypes.isVideo(sampleMimeType);
@ -150,20 +150,19 @@ import java.util.Map;
throw new MuxerException("Failed to add track with format=" + format, e);
}
TrackToken trackToken = new TrackTokenImpl(trackIndex);
if (isVideo) {
videoTrackToken = trackToken;
videoTrackId = trackIndex;
}
return trackToken;
return trackIndex;
}
@Override
public void writeSampleData(TrackToken trackToken, ByteBuffer data, BufferInfo bufferInfo)
public void writeSampleData(int trackId, ByteBuffer data, BufferInfo bufferInfo)
throws MuxerException {
long presentationTimeUs = bufferInfo.presentationTimeUs;
if (videoDurationUs != C.TIME_UNSET
&& trackToken == videoTrackToken
&& trackId == videoTrackId
&& presentationTimeUs > videoDurationUs) {
Log.w(
TAG,
@ -176,20 +175,18 @@ import java.util.Map;
}
if (!isStarted) {
if (Util.SDK_INT < 30 && presentationTimeUs < 0) {
trackTokenToPresentationTimeOffsetUs.put(trackToken, -presentationTimeUs);
trackIdToPresentationTimeOffsetUs.put(trackId, -presentationTimeUs);
}
startMuxer();
}
long presentationTimeOffsetUs =
trackTokenToPresentationTimeOffsetUs.containsKey(trackToken)
? trackTokenToPresentationTimeOffsetUs.get(trackToken)
: 0;
trackIdToPresentationTimeOffsetUs.get(trackId, /* valueIfKeyNotFound= */ 0L);
presentationTimeUs += presentationTimeOffsetUs;
long lastSamplePresentationTimeUs =
trackTokenToLastPresentationTimeUs.containsKey(trackToken)
? trackTokenToLastPresentationTimeUs.get(trackToken)
Util.contains(trackIdToLastPresentationTimeUs, trackId)
? trackIdToLastPresentationTimeUs.get(trackId)
: 0;
// writeSampleData blocks on old API versions, so check here to avoid calling the method.
checkState(
@ -199,7 +196,7 @@ import java.util.Map;
+ " < "
+ lastSamplePresentationTimeUs
+ ") unsupported on this API version");
trackTokenToLastPresentationTimeUs.put(trackToken, presentationTimeUs);
trackIdToLastPresentationTimeUs.put(trackId, presentationTimeUs);
checkState(
presentationTimeOffsetUs == 0 || presentationTimeUs >= 0,
@ -212,8 +209,8 @@ import java.util.Map;
bufferInfo.set(bufferInfo.offset, bufferInfo.size, presentationTimeUs, bufferInfo.flags);
try {
checkState(trackToken instanceof TrackTokenImpl);
mediaMuxer.writeSampleData(((TrackTokenImpl) trackToken).trackIndex, data, bufferInfo);
mediaMuxer.writeSampleData(trackId, data, bufferInfo);
} catch (RuntimeException e) {
throw new MuxerException(
"Failed to write sample for presentationTimeUs="
@ -244,14 +241,14 @@ import java.util.Map;
startMuxer();
}
if (videoDurationUs != C.TIME_UNSET && videoTrackToken != null) {
if (videoDurationUs != C.TIME_UNSET && videoTrackId != TRACK_ID_UNSET) {
BufferInfo bufferInfo = new BufferInfo();
bufferInfo.set(
/* newOffset= */ 0,
/* newSize= */ 0,
videoDurationUs,
TransformerUtil.getMediaCodecFlags(C.BUFFER_FLAG_END_OF_STREAM));
writeSampleData(checkNotNull(videoTrackToken), ByteBuffer.allocateDirect(0), bufferInfo);
writeSampleData(videoTrackId, ByteBuffer.allocateDirect(0), bufferInfo);
}
isStarted = false;
@ -314,12 +311,4 @@ import java.util.Map;
}
return supportedMimeTypes.build();
}
private static class TrackTokenImpl implements TrackToken {
public final int trackIndex;
public TrackTokenImpl(int trackIndex) {
this.trackIndex = trackIndex;
}
}
}

View File

@ -15,7 +15,6 @@
*/
package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.muxer.Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_SET_FROM_END_OF_STREAM_BUFFER_OR_DUPLICATE_PREVIOUS;
import android.media.MediaCodec;
@ -207,13 +206,14 @@ public final class InAppMuxer implements Muxer {
}
private static final String TAG = "InAppMuxer";
private static final int TRACK_ID_UNSET = -1;
private final Muxer muxer;
@Nullable private final MetadataProvider metadataProvider;
private final long videoDurationUs;
private final Set<Metadata.Entry> metadataEntries;
@Nullable private TrackToken videoTrackToken;
private int videoTrackId;
private InAppMuxer(
Muxer muxer, @Nullable MetadataProvider metadataProvider, long videoDurationUs) {
@ -221,23 +221,24 @@ public final class InAppMuxer implements Muxer {
this.metadataProvider = metadataProvider;
this.videoDurationUs = videoDurationUs;
metadataEntries = new LinkedHashSet<>();
videoTrackId = TRACK_ID_UNSET;
}
@Override
public TrackToken addTrack(Format format) throws MuxerException {
TrackToken trackToken = muxer.addTrack(format);
public int addTrack(Format format) throws MuxerException {
int trackId = muxer.addTrack(format);
if (MimeTypes.isVideo(format.sampleMimeType)) {
muxer.addMetadataEntry(new Mp4OrientationData(format.rotationDegrees));
videoTrackToken = trackToken;
videoTrackId = trackId;
}
return trackToken;
return trackId;
}
@Override
public void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
public void writeSampleData(int trackId, ByteBuffer byteBuffer, BufferInfo bufferInfo)
throws MuxerException {
if (videoDurationUs != C.TIME_UNSET
&& trackToken == videoTrackToken
&& trackId == videoTrackId
&& bufferInfo.presentationTimeUs > videoDurationUs) {
Log.w(
TAG,
@ -248,7 +249,7 @@ public final class InAppMuxer implements Muxer {
videoDurationUs));
return;
}
muxer.writeSampleData(trackToken, byteBuffer, bufferInfo);
muxer.writeSampleData(trackId, byteBuffer, bufferInfo);
}
@Override
@ -260,14 +261,14 @@ public final class InAppMuxer implements Muxer {
@Override
public void close() throws MuxerException {
if (videoDurationUs != C.TIME_UNSET && videoTrackToken != null) {
if (videoDurationUs != C.TIME_UNSET && videoTrackId != TRACK_ID_UNSET) {
BufferInfo bufferInfo = new BufferInfo();
bufferInfo.set(
/* newOffset= */ 0,
/* newSize= */ 0,
videoDurationUs,
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
writeSampleData(checkNotNull(videoTrackToken), ByteBuffer.allocateDirect(0), bufferInfo);
writeSampleData(videoTrackId, ByteBuffer.allocateDirect(0), bufferInfo);
}
writeMetadata();
muxer.close();

View File

@ -48,7 +48,6 @@ import androidx.media3.common.util.Util;
import androidx.media3.container.NalUnitUtil;
import androidx.media3.effect.DebugTraceUtil;
import androidx.media3.muxer.Muxer;
import androidx.media3.muxer.Muxer.TrackToken;
import androidx.media3.muxer.MuxerException;
import com.google.common.collect.ImmutableList;
import java.io.File;
@ -580,7 +579,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
data.remaining(),
presentationTimeUs,
TransformerUtil.getMediaCodecFlags(isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0));
muxer.writeSampleData(trackInfo.trackToken, data, bufferInfo);
muxer.writeSampleData(trackInfo.trackId, data, bufferInfo);
DebugTraceUtil.logEvent(
COMPONENT_MUXER,
@ -749,16 +748,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private static final class TrackInfo {
public final Format format;
public final TrackToken trackToken;
public final int trackId;
public long startTimeUs;
public long bytesWritten;
public int sampleCount;
public long timeUs;
public TrackInfo(Format format, TrackToken trackToken) {
public TrackInfo(Format format, int trackId) {
this.format = format;
this.trackToken = trackToken;
this.trackId = trackId;
}
/**

View File

@ -63,13 +63,12 @@ import java.nio.ByteBuffer;
}
@Override
public TrackToken addTrack(Format format) {
return new TrackToken() {};
public int addTrack(Format format) {
return 0;
}
@Override
public void writeSampleData(
TrackToken trackToken, ByteBuffer data, MediaCodec.BufferInfo bufferInfo) {}
public void writeSampleData(int trackId, ByteBuffer data, MediaCodec.BufferInfo bufferInfo) {}
@Override
public void addMetadataEntry(Metadata.Entry metadataEntry) {}

View File

@ -38,8 +38,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
/**
* A {@link Dumpable} {@link Muxer} implementation that supports dumping information about all
@ -88,7 +86,7 @@ public final class CapturingMuxer implements Muxer, Dumpable {
private final boolean handleAudioAsPcm;
private final SparseArray<DumpableFormat> dumpableFormatByTrackType;
private final SparseArray<DumpableStream> dumpableStreamByTrackType;
private final Map<TrackToken, Integer> trackTokenToType;
private final SparseArray<Integer> trackIdToType;
private final ArrayList<Metadata.Entry> metadataList;
private boolean released;
@ -98,18 +96,18 @@ public final class CapturingMuxer implements Muxer, Dumpable {
this.handleAudioAsPcm = handleAudioAsPcm;
dumpableFormatByTrackType = new SparseArray<>();
dumpableStreamByTrackType = new SparseArray<>();
trackTokenToType = new HashMap<>();
trackIdToType = new SparseArray<>();
metadataList = new ArrayList<>();
}
// Muxer implementation.
@Override
public TrackToken addTrack(Format format) throws MuxerException {
TrackToken trackToken = wrappedMuxer.addTrack(format);
public int addTrack(Format format) throws MuxerException {
int trackId = wrappedMuxer.addTrack(format);
@C.TrackType int trackType = getProcessedTrackType(format.sampleMimeType);
trackTokenToType.put(trackToken, trackType);
trackIdToType.put(trackId, trackType);
dumpableFormatByTrackType.append(
trackType, new DumpableFormat(format, /* tag= */ Util.getTrackTypeString(trackType)));
@ -120,20 +118,20 @@ public final class CapturingMuxer implements Muxer, Dumpable {
? new DumpablePcmAudioStream(trackType)
: new DumpableStream(trackType));
return trackToken;
return trackId;
}
@Override
public void writeSampleData(TrackToken trackToken, ByteBuffer data, BufferInfo bufferInfo)
public void writeSampleData(int trackId, ByteBuffer data, BufferInfo bufferInfo)
throws MuxerException {
@C.TrackType int trackType = checkNotNull(trackTokenToType.get(trackToken));
@C.TrackType int trackType = checkNotNull(trackIdToType.get(trackId));
dumpableStreamByTrackType
.get(trackType)
.addSample(
data,
(bufferInfo.flags & C.BUFFER_FLAG_KEY_FRAME) == C.BUFFER_FLAG_KEY_FRAME,
bufferInfo.presentationTimeUs);
wrappedMuxer.writeSampleData(trackToken, data, bufferInfo);
wrappedMuxer.writeSampleData(trackId, data, bufferInfo);
}
@Override