mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
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:
parent
8dca430e42
commit
bcae7abde9
@ -38,6 +38,8 @@
|
|||||||
* Muxers:
|
* Muxers:
|
||||||
* Renamed `setSampleCopyEnabled()` method to `setSampleCopyingEnabled()`
|
* Renamed `setSampleCopyEnabled()` method to `setSampleCopyingEnabled()`
|
||||||
in both `Mp4Muxer.Builder` and `FragmentedMp4Muxer.Builder`.
|
in both `Mp4Muxer.Builder` and `FragmentedMp4Muxer.Builder`.
|
||||||
|
* `Mp4Muxer.addTrack()` and `FragmentedMp4Muxer.addTrack()` now return an
|
||||||
|
`int` track id instead of a `TrackToken`.
|
||||||
* IMA extension:
|
* IMA extension:
|
||||||
* Session:
|
* Session:
|
||||||
* Fix bug where calling a `Player` method on a `MediaController` connected
|
* Fix bug where calling a `Player` method on a `MediaController` connected
|
||||||
|
@ -44,11 +44,11 @@ import java.util.List;
|
|||||||
context.getResources().getAssets().openFd(MP4_FILE_ASSET_DIRECTORY + inputFileName);
|
context.getResources().getAssets().openFd(MP4_FILE_ASSET_DIRECTORY + inputFileName);
|
||||||
extractor.setDataSource(fd);
|
extractor.setDataSource(fd);
|
||||||
|
|
||||||
List<Muxer.TrackToken> addedTracks = new ArrayList<>();
|
List<Integer> addedTracks = new ArrayList<>();
|
||||||
for (int i = 0; i < extractor.getTrackCount(); i++) {
|
for (int i = 0; i < extractor.getTrackCount(); i++) {
|
||||||
Muxer.TrackToken trackToken =
|
int trackId =
|
||||||
muxer.addTrack(MediaFormatUtil.createFormatFromMediaFormat(extractor.getTrackFormat(i)));
|
muxer.addTrack(MediaFormatUtil.createFormatFromMediaFormat(extractor.getTrackFormat(i)));
|
||||||
addedTracks.add(trackToken);
|
addedTracks.add(trackId);
|
||||||
extractor.selectTrack(i);
|
extractor.selectTrack(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import static androidx.media3.common.util.Assertions.checkArgument;
|
|||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
|
||||||
import android.media.MediaCodec.BufferInfo;
|
import android.media.MediaCodec.BufferInfo;
|
||||||
|
import android.util.SparseArray;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.Metadata;
|
import androidx.media3.common.Metadata;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@ -64,10 +65,9 @@ import java.nio.ByteBuffer;
|
|||||||
* <p>To create a fragmented MP4 file, the caller must:
|
* <p>To create a fragmented MP4 file, the caller must:
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Add tracks using {@link #addTrack(Format)} which will return a {@link Mp4Muxer.TrackToken}.
|
* <li>Add tracks using {@link #addTrack(Format)} which will return a track id.
|
||||||
* <li>Use the associated {@link Mp4Muxer.TrackToken} when {@linkplain
|
* <li>Use the associated track id when {@linkplain #writeSampleData(int, ByteBuffer, BufferInfo)
|
||||||
* #writeSampleData(Mp4Muxer.TrackToken, ByteBuffer, BufferInfo) writing samples} for that
|
* writing samples} for that track.
|
||||||
* track.
|
|
||||||
* <li>{@link #close} the muxer when all data has been written.
|
* <li>{@link #close} the muxer when all data has been written.
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
@ -76,8 +76,8 @@ import java.nio.ByteBuffer;
|
|||||||
* <ul>
|
* <ul>
|
||||||
* <li>All tracks must be added before writing any samples.
|
* <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
|
* <li>The caller is responsible for ensuring that samples of different track types are well
|
||||||
* interleaved by calling {@link #writeSampleData(Mp4Muxer.TrackToken, ByteBuffer,
|
* interleaved by calling {@link #writeSampleData(int, ByteBuffer, BufferInfo)} in an order
|
||||||
* BufferInfo)} in an order that interleaves samples from different tracks.
|
* that interleaves samples from different tracks.
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@ -121,10 +121,10 @@ public final class FragmentedMp4Muxer implements Muxer {
|
|||||||
/**
|
/**
|
||||||
* Sets whether to enable the sample copy.
|
* Sets whether to enable the sample copy.
|
||||||
*
|
*
|
||||||
* <p>If the sample copy is enabled, {@link #writeSampleData(TrackToken, ByteBuffer,
|
* <p>If the sample copy is enabled, {@link #writeSampleData(int, ByteBuffer, BufferInfo)}
|
||||||
* BufferInfo)} copies the input {@link ByteBuffer} and {@link BufferInfo} before it returns, so
|
* copies the input {@link ByteBuffer} and {@link BufferInfo} before it returns, so it is safe
|
||||||
* it is safe to reuse them immediately. Otherwise, the muxer takes ownership of the {@link
|
* to reuse them immediately. Otherwise, the muxer takes ownership of the {@link ByteBuffer} and
|
||||||
* ByteBuffer} and the {@link BufferInfo} and the caller must not modify them.
|
* the {@link BufferInfo} and the caller must not modify them.
|
||||||
*
|
*
|
||||||
* <p>The default value is {@code true}.
|
* <p>The default value is {@code true}.
|
||||||
*/
|
*/
|
||||||
@ -142,6 +142,7 @@ public final class FragmentedMp4Muxer implements Muxer {
|
|||||||
|
|
||||||
private final FragmentedMp4Writer fragmentedMp4Writer;
|
private final FragmentedMp4Writer fragmentedMp4Writer;
|
||||||
private final MetadataCollector metadataCollector;
|
private final MetadataCollector metadataCollector;
|
||||||
|
private final SparseArray<Track> trackIdToTrack;
|
||||||
|
|
||||||
private FragmentedMp4Muxer(
|
private FragmentedMp4Muxer(
|
||||||
OutputStream outputStream, long fragmentDurationMs, boolean sampleCopyEnabled) {
|
OutputStream outputStream, long fragmentDurationMs, boolean sampleCopyEnabled) {
|
||||||
@ -154,11 +155,14 @@ public final class FragmentedMp4Muxer implements Muxer {
|
|||||||
AnnexBToAvccConverter.DEFAULT,
|
AnnexBToAvccConverter.DEFAULT,
|
||||||
fragmentDurationMs,
|
fragmentDurationMs,
|
||||||
sampleCopyEnabled);
|
sampleCopyEnabled);
|
||||||
|
trackIdToTrack = new SparseArray<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackToken addTrack(Format format) {
|
public int addTrack(Format format) {
|
||||||
return fragmentedMp4Writer.addTrack(/* sortKey= */ 1, 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.
|
* <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
|
* @param byteBuffer The encoded sample. The muxer takes ownership of the buffer if {@link
|
||||||
* Builder#setSampleCopyingEnabled(boolean) sample copying} is disabled. Otherwise, the
|
* Builder#setSampleCopyingEnabled(boolean) sample copying} is disabled. Otherwise, the
|
||||||
* position of the buffer is updated but the caller retains ownership.
|
* 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.
|
* @throws MuxerException If there is any error while writing data to the disk.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
|
public void writeSampleData(int trackId, ByteBuffer byteBuffer, BufferInfo bufferInfo)
|
||||||
throws MuxerException {
|
throws MuxerException {
|
||||||
try {
|
try {
|
||||||
fragmentedMp4Writer.writeSampleData(trackToken, byteBuffer, bufferInfo);
|
fragmentedMp4Writer.writeSampleData(trackIdToTrack.get(trackId), byteBuffer, bufferInfo);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new MuxerException(
|
throw new MuxerException(
|
||||||
"Failed to write sample for presentationTimeUs="
|
"Failed to write sample for presentationTimeUs="
|
||||||
|
@ -33,7 +33,6 @@ import android.media.MediaCodec.BufferInfo;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.muxer.Muxer.TrackToken;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
@ -122,6 +121,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private boolean headerCreated;
|
private boolean headerCreated;
|
||||||
private long minInputPresentationTimeUs;
|
private long minInputPresentationTimeUs;
|
||||||
private long maxTrackDurationUs;
|
private long maxTrackDurationUs;
|
||||||
|
private int nextTrackId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
@ -153,8 +153,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
currentFragmentSequenceNumber = 1;
|
currentFragmentSequenceNumber = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TrackToken addTrack(int sortKey, Format format) {
|
public Track addTrack(int sortKey, Format format) {
|
||||||
Track track = new Track(format, sampleCopyEnabled);
|
Track track = new Track(nextTrackId++, format, sampleCopyEnabled);
|
||||||
tracks.add(track);
|
tracks.add(track);
|
||||||
if (MimeTypes.isVideo(format.sampleMimeType)) {
|
if (MimeTypes.isVideo(format.sampleMimeType)) {
|
||||||
videoTrack = track;
|
videoTrack = track;
|
||||||
@ -162,15 +162,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeSampleData(
|
public void writeSampleData(Track track, ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo)
|
||||||
TrackToken token, ByteBuffer byteBuffer, MediaCodec.BufferInfo bufferInfo)
|
|
||||||
throws IOException {
|
throws IOException {
|
||||||
checkArgument(token instanceof Track);
|
|
||||||
if (!headerCreated) {
|
if (!headerCreated) {
|
||||||
createHeader();
|
createHeader();
|
||||||
headerCreated = true;
|
headerCreated = true;
|
||||||
}
|
}
|
||||||
Track track = (Track) token;
|
|
||||||
if (shouldFlushPendingSamples(track, bufferInfo)) {
|
if (shouldFlushPendingSamples(track, bufferInfo)) {
|
||||||
createFragment();
|
createFragment();
|
||||||
}
|
}
|
||||||
|
@ -89,9 +89,9 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
|||||||
* <p>To create an MP4 container file, the caller must:
|
* <p>To create an MP4 container file, the caller must:
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>Add tracks using {@link #addTrack(int, Format)} which will return a {@link TrackToken}.
|
* <li>Add tracks using {@link #addTrack(int, Format)} which will return a track id.
|
||||||
* <li>Use the associated {@link TrackToken} when {@linkplain #writeSampleData(TrackToken,
|
* <li>Use the associated track id when {@linkplain #writeSampleData(int, ByteBuffer, BufferInfo)
|
||||||
* ByteBuffer, BufferInfo) writing samples} for that track.
|
* writing samples} for that track.
|
||||||
* <li>{@link #close} the muxer when all data has been written.
|
* <li>{@link #close} the muxer when all data has been written.
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
@ -100,8 +100,8 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
|||||||
* <ul>
|
* <ul>
|
||||||
* <li>Tracks can be added at any point, even after writing some samples to other tracks.
|
* <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
|
* <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
|
* interleaved by calling {@link #writeSampleData(int, ByteBuffer, BufferInfo)} in an order
|
||||||
* order that interleaves samples from different tracks.
|
* 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
|
* <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.
|
* file may still have some partial data.
|
||||||
* </ul>
|
* </ul>
|
||||||
@ -252,10 +252,10 @@ public final class Mp4Muxer implements Muxer {
|
|||||||
/**
|
/**
|
||||||
* Sets whether to enable the sample copy.
|
* Sets whether to enable the sample copy.
|
||||||
*
|
*
|
||||||
* <p>If the sample copy is enabled, {@link #writeSampleData(TrackToken, ByteBuffer,
|
* <p>If the sample copy is enabled, {@link #writeSampleData(int, ByteBuffer, BufferInfo)}
|
||||||
* BufferInfo)} copies the input {@link ByteBuffer} and {@link BufferInfo} before it returns, so
|
* copies the input {@link ByteBuffer} and {@link BufferInfo} before it returns, so it is safe
|
||||||
* it is safe to reuse them immediately. Otherwise, the muxer takes ownership of the {@link
|
* to reuse them immediately. Otherwise, the muxer takes ownership of the {@link ByteBuffer} and
|
||||||
* ByteBuffer} and the {@link BufferInfo} and the caller must not modify them.
|
* the {@link BufferInfo} and the caller must not modify them.
|
||||||
*
|
*
|
||||||
* <p>The default value is {@code true}.
|
* <p>The default value is {@code true}.
|
||||||
*/
|
*/
|
||||||
@ -269,7 +269,7 @@ public final class Mp4Muxer implements Muxer {
|
|||||||
* Sets whether to enable sample batching.
|
* Sets whether to enable sample batching.
|
||||||
*
|
*
|
||||||
* <p>If sample batching is enabled, samples are written in batches for each track, otherwise
|
* <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}.
|
* arrive}.
|
||||||
*
|
*
|
||||||
* <p>The default value is {@code true}.
|
* <p>The default value is {@code true}.
|
||||||
@ -348,6 +348,7 @@ public final class Mp4Muxer implements Muxer {
|
|||||||
@Nullable private final Mp4AtFileParameters mp4AtFileParameters;
|
@Nullable private final Mp4AtFileParameters mp4AtFileParameters;
|
||||||
private final MetadataCollector metadataCollector;
|
private final MetadataCollector metadataCollector;
|
||||||
private final Mp4Writer mp4Writer;
|
private final Mp4Writer mp4Writer;
|
||||||
|
private final List<Track> trackIdToTrack;
|
||||||
private final List<Track> auxiliaryTracks;
|
private final List<Track> auxiliaryTracks;
|
||||||
|
|
||||||
@Nullable private String cacheFilePath;
|
@Nullable private String cacheFilePath;
|
||||||
@ -355,6 +356,8 @@ public final class Mp4Muxer implements Muxer {
|
|||||||
@Nullable private MetadataCollector auxiliaryTracksMetadataCollector;
|
@Nullable private MetadataCollector auxiliaryTracksMetadataCollector;
|
||||||
@Nullable private Mp4Writer auxiliaryTracksMp4Writer;
|
@Nullable private Mp4Writer auxiliaryTracksMp4Writer;
|
||||||
|
|
||||||
|
private int nextTrackId;
|
||||||
|
|
||||||
private Mp4Muxer(
|
private Mp4Muxer(
|
||||||
FileOutputStream outputStream,
|
FileOutputStream outputStream,
|
||||||
@LastSampleDurationBehavior int lastFrameDurationBehavior,
|
@LastSampleDurationBehavior int lastFrameDurationBehavior,
|
||||||
@ -383,6 +386,7 @@ public final class Mp4Muxer implements Muxer {
|
|||||||
sampleCopyEnabled,
|
sampleCopyEnabled,
|
||||||
sampleBatchingEnabled,
|
sampleBatchingEnabled,
|
||||||
attemptStreamableOutputEnabled);
|
attemptStreamableOutputEnabled);
|
||||||
|
trackIdToTrack = new ArrayList<>();
|
||||||
auxiliaryTracks = 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.
|
* <p>The order of tracks remains same in which they are added.
|
||||||
*
|
*
|
||||||
* @param format The {@link Format} for the track.
|
* @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.
|
* @throws MuxerException If an error occurs while adding track.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public TrackToken addTrack(Format format) throws MuxerException {
|
public int addTrack(Format format) throws MuxerException {
|
||||||
return addTrack(/* sortKey= */ 1, format);
|
return addTrack(/* sortKey= */ 1, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,31 +415,37 @@ public final class Mp4Muxer implements Muxer {
|
|||||||
* other tracks.
|
* other tracks.
|
||||||
*
|
*
|
||||||
* <p>The final order of tracks is determined by the provided sort key. Tracks with a lower sort
|
* <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
|
* key will be written before tracks with a higher sort key. Ordering between tracks with the same
|
||||||
* tracks with the same sort key is not specified.
|
* sort key is not specified.
|
||||||
*
|
*
|
||||||
* @param sortKey The key used for sorting the track list.
|
* @param sortKey The key used for sorting the track list.
|
||||||
* @param format The {@link Format} for the track.
|
* @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.
|
* @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
|
if (outputFileFormat == FILE_FORMAT_MP4_WITH_AUXILIARY_TRACKS_EXTENSION
|
||||||
&& isAuxiliaryTrack(format)) {
|
&& isAuxiliaryTrack(format)) {
|
||||||
if (checkNotNull(mp4AtFileParameters).shouldInterleaveSamples) {
|
if (checkNotNull(mp4AtFileParameters).shouldInterleaveSamples) {
|
||||||
// Auxiliary tracks are handled by the primary Mp4Writer.
|
// 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 {
|
} else {
|
||||||
ensureSetupForAuxiliaryTracks();
|
track = mp4Writer.addTrack(nextTrackId++, sortKey, format);
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
throw new MuxerException("Cache file not found", e);
|
|
||||||
}
|
|
||||||
Track track = auxiliaryTracksMp4Writer.addTrack(sortKey, format);
|
|
||||||
auxiliaryTracks.add(track);
|
|
||||||
return track;
|
|
||||||
}
|
}
|
||||||
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)
|
* <p>When sample batching is {@linkplain Mp4Muxer.Builder#setSampleBatchingEnabled(boolean)
|
||||||
* enabled}, provide sample data ({@link ByteBuffer}, {@link BufferInfo}) that won't be modified
|
* enabled}, provide sample data ({@link ByteBuffer}, {@link BufferInfo}) that won't be modified
|
||||||
* after calling the {@link #writeSampleData(TrackToken, ByteBuffer, BufferInfo)} method, unless
|
* after calling the {@link #writeSampleData(int, ByteBuffer, BufferInfo)} method, unless sample
|
||||||
* sample copying is also {@linkplain Mp4Muxer.Builder#setSampleCopyingEnabled(boolean) enabled}.
|
* copying is also {@linkplain Mp4Muxer.Builder#setSampleCopyingEnabled(boolean) enabled}. This
|
||||||
* This ensures data integrity within the batch. If sample copying is {@linkplain
|
* ensures data integrity within the batch. If sample copying is {@linkplain
|
||||||
* Mp4Muxer.Builder#setSampleCopyingEnabled(boolean) enabled}, it's safe to modify the data after
|
* Mp4Muxer.Builder#setSampleCopyingEnabled(boolean) enabled}, it's safe to modify the data after
|
||||||
* the method returns, as the muxer internally creates a sample copy.
|
* 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
|
* @param byteBuffer The encoded sample. The muxer takes ownership of the buffer if {@link
|
||||||
* Builder#setSampleCopyingEnabled(boolean) sample copying} is disabled. Otherwise, the
|
* Builder#setSampleCopyingEnabled(boolean) sample copying} is disabled. Otherwise, the
|
||||||
* position of the buffer is updated but the caller retains ownership.
|
* 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.
|
* @throws MuxerException If an error occurs while writing data to the output file.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
|
public void writeSampleData(int trackId, ByteBuffer byteBuffer, BufferInfo bufferInfo)
|
||||||
throws MuxerException {
|
throws MuxerException {
|
||||||
checkState(trackToken instanceof Track);
|
Track track = trackIdToTrack.get(trackId);
|
||||||
Track track = (Track) trackToken;
|
|
||||||
try {
|
try {
|
||||||
if (auxiliaryTracks.contains(trackToken)) {
|
if (auxiliaryTracks.contains(track)) {
|
||||||
checkNotNull(auxiliaryTracksMp4Writer).writeSampleData(track, byteBuffer, bufferInfo);
|
checkNotNull(auxiliaryTracksMp4Writer).writeSampleData(track, byteBuffer, bufferInfo);
|
||||||
} else {
|
} else {
|
||||||
mp4Writer.writeSampleData(track, byteBuffer, bufferInfo);
|
mp4Writer.writeSampleData(track, byteBuffer, bufferInfo);
|
||||||
|
@ -111,12 +111,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
/**
|
/**
|
||||||
* Adds a track of the given {@link Format}.
|
* 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 sortKey The key used for sorting the track list.
|
||||||
* @param format The {@link Format} for the track.
|
* @param format The {@link Format} for the track.
|
||||||
* @return A unique {@link Track}. It should be used in {@link #writeSampleData}.
|
* @return A unique {@link Track}. It should be used in {@link #writeSampleData}.
|
||||||
*/
|
*/
|
||||||
public Track addTrack(int sortKey, Format format) {
|
public Track addTrack(int trackId, int sortKey, Format format) {
|
||||||
Track track = new Track(format, sortKey, sampleCopyEnabled);
|
Track track = new Track(trackId, format, sortKey, sampleCopyEnabled);
|
||||||
tracks.add(track);
|
tracks.add(track);
|
||||||
Collections.sort(tracks, (a, b) -> Integer.compare(a.sortKey, b.sortKey));
|
Collections.sort(tracks, (a, b) -> Integer.compare(a.sortKey, b.sortKey));
|
||||||
return track;
|
return track;
|
||||||
@ -127,12 +128,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
*
|
*
|
||||||
* <p>See {@link MuxerUtil#isAuxiliaryTrack(Format)} for auxiliary tracks.
|
* <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 sortKey The key used for sorting the track list.
|
||||||
* @param format The {@link Format} for the track.
|
* @param format The {@link Format} for the track.
|
||||||
* @return A unique {@link Track}. It should be used in {@link #writeSampleData}.
|
* @return A unique {@link Track}. It should be used in {@link #writeSampleData}.
|
||||||
*/
|
*/
|
||||||
public Track addAuxiliaryTrack(int sortKey, Format format) {
|
public Track addAuxiliaryTrack(int trackId, int sortKey, Format format) {
|
||||||
Track track = new Track(format, sortKey, sampleCopyEnabled);
|
Track track = new Track(trackId, format, sortKey, sampleCopyEnabled);
|
||||||
auxiliaryTracks.add(track);
|
auxiliaryTracks.add(track);
|
||||||
Collections.sort(auxiliaryTracks, (a, b) -> Integer.compare(a.sortKey, b.sortKey));
|
Collections.sort(auxiliaryTracks, (a, b) -> Integer.compare(a.sortKey, b.sortKey));
|
||||||
return track;
|
return track;
|
||||||
|
@ -44,29 +44,24 @@ public interface Muxer {
|
|||||||
ImmutableList<String> getSupportedSampleMimeTypes(@C.TrackType int trackType);
|
ImmutableList<String> getSupportedSampleMimeTypes(@C.TrackType int trackType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A token representing an added track. */
|
|
||||||
interface TrackToken {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a track of the given media format.
|
* Adds a track of the given media format.
|
||||||
*
|
*
|
||||||
* @param format The {@link Format} of the track.
|
* @param format The {@link Format} of the track.
|
||||||
* @return The {@link TrackToken} for this track, which should be passed to {@link
|
* @return A track id for this track, which should be passed to {@link #writeSampleData}.
|
||||||
* #writeSampleData}.
|
|
||||||
* @throws MuxerException If the muxer encounters a problem while adding the track.
|
* @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.
|
* Writes encoded sample data.
|
||||||
*
|
*
|
||||||
* @param trackToken The {@link TrackToken} of the track, previously returned by {@link
|
* @param trackId The track id, previously returned by {@link #addTrack(Format)}.
|
||||||
* #addTrack(Format)}.
|
|
||||||
* @param byteBuffer A buffer containing the sample data to write to the container.
|
* @param byteBuffer A buffer containing the sample data to write to the container.
|
||||||
* @param bufferInfo The {@link BufferInfo} of the sample.
|
* @param bufferInfo The {@link BufferInfo} of the sample.
|
||||||
* @throws MuxerException If the muxer fails to write 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;
|
throws MuxerException;
|
||||||
|
|
||||||
/** Adds {@linkplain Metadata.Entry metadata} about the output file. */
|
/** Adds {@linkplain Metadata.Entry metadata} about the output file. */
|
||||||
|
@ -22,7 +22,6 @@ import android.media.MediaCodec.BufferInfo;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.muxer.Muxer.TrackToken;
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -30,7 +29,8 @@ import java.util.Deque;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Represents a single track (audio, video, metadata etc.). */
|
/** 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 Format format;
|
||||||
public final int sortKey;
|
public final int sortKey;
|
||||||
public final List<BufferInfo> writtenSamples;
|
public final List<BufferInfo> writtenSamples;
|
||||||
@ -44,18 +44,20 @@ import java.util.List;
|
|||||||
private final boolean sampleCopyEnabled;
|
private final boolean sampleCopyEnabled;
|
||||||
|
|
||||||
/** Creates an instance with {@code sortKey} set to 1. */
|
/** Creates an instance with {@code sortKey} set to 1. */
|
||||||
public Track(Format format, boolean sampleCopyEnabled) {
|
public Track(int trackId, Format format, boolean sampleCopyEnabled) {
|
||||||
this(format, /* sortKey= */ 1, sampleCopyEnabled);
|
this(trackId, format, /* sortKey= */ 1, sampleCopyEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
*
|
*
|
||||||
|
* @param trackId A unique id for the track.
|
||||||
* @param format The {@link Format} for the track.
|
* @param format The {@link Format} for the track.
|
||||||
* @param sortKey The key used for sorting the track list.
|
* @param sortKey The key used for sorting the track list.
|
||||||
* @param sampleCopyEnabled Whether sample copying is enabled.
|
* @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.format = format;
|
||||||
this.sortKey = sortKey;
|
this.sortKey = sortKey;
|
||||||
this.sampleCopyEnabled = sampleCopyEnabled;
|
this.sampleCopyEnabled = sampleCopyEnabled;
|
||||||
|
@ -35,7 +35,6 @@ import androidx.media3.container.Mp4TimestampData;
|
|||||||
import androidx.media3.container.XmpData;
|
import androidx.media3.container.XmpData;
|
||||||
import androidx.media3.extractor.mp4.Mp4Extractor;
|
import androidx.media3.extractor.mp4.Mp4Extractor;
|
||||||
import androidx.media3.extractor.text.DefaultSubtitleParserFactory;
|
import androidx.media3.extractor.text.DefaultSubtitleParserFactory;
|
||||||
import androidx.media3.muxer.Muxer.TrackToken;
|
|
||||||
import androidx.media3.test.utils.DumpFileAsserts;
|
import androidx.media3.test.utils.DumpFileAsserts;
|
||||||
import androidx.media3.test.utils.DumpableMp4Box;
|
import androidx.media3.test.utils.DumpableMp4Box;
|
||||||
import androidx.media3.test.utils.FakeExtractorOutput;
|
import androidx.media3.test.utils.FakeExtractorOutput;
|
||||||
@ -83,8 +82,8 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
/* value= */ Util.getUtf8Bytes("StringValue"),
|
/* value= */ Util.getUtf8Bytes("StringValue"),
|
||||||
MdtaMetadataEntry.TYPE_INDICATOR_STRING));
|
MdtaMetadataEntry.TYPE_INDICATOR_STRING));
|
||||||
muxer.addMetadataEntry(new XmpData(xmpBytes));
|
muxer.addMetadataEntry(new XmpData(xmpBytes));
|
||||||
TrackToken token = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
int trackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
@ -136,12 +135,12 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 300L);
|
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 300L);
|
||||||
|
|
||||||
try {
|
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, track1Sample1.first, track1Sample1.second);
|
||||||
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
|
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
|
||||||
|
|
||||||
// Add same track again but with different samples.
|
// 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, track2Sample1.first, track2Sample1.second);
|
||||||
mp4Muxer.writeSampleData(track2, track2Sample2.first, track2Sample2.second);
|
mp4Muxer.writeSampleData(track2, track2Sample2.first, track2Sample2.second);
|
||||||
} finally {
|
} finally {
|
||||||
@ -180,12 +179,12 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 200L);
|
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 200L);
|
||||||
|
|
||||||
try {
|
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, track1Sample1.first, track1Sample1.second);
|
||||||
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
|
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
|
||||||
|
|
||||||
// Add same track again but with different samples.
|
// 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, track2Sample1.first, track2Sample1.second);
|
||||||
mp4Muxer.writeSampleData(track2, track2Sample2.first, track2Sample2.second);
|
mp4Muxer.writeSampleData(track2, track2Sample2.first, track2Sample2.second);
|
||||||
} finally {
|
} finally {
|
||||||
@ -218,7 +217,7 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 300L);
|
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 300L);
|
||||||
|
|
||||||
try {
|
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, track1Sample1.first, track1Sample1.second);
|
||||||
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
|
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
|
||||||
mp4Muxer.writeSampleData(track1, track1Sample3.first, track1Sample3.second);
|
mp4Muxer.writeSampleData(track1, track1Sample3.first, track1Sample3.second);
|
||||||
@ -257,7 +256,7 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 2_000L);
|
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 2_000L);
|
||||||
|
|
||||||
try {
|
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, track1Sample1.first, track1Sample1.second);
|
||||||
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
|
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
|
||||||
mp4Muxer.writeSampleData(track1, track1Sample3.first, track1Sample3.second);
|
mp4Muxer.writeSampleData(track1, track1Sample3.first, track1Sample3.second);
|
||||||
@ -301,12 +300,12 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 23_001_033_192L);
|
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 23_001_033_192L);
|
||||||
|
|
||||||
try {
|
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, track1Sample1.first, track1Sample1.second);
|
||||||
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
|
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
|
||||||
mp4Muxer.writeSampleData(track1, track1Sample3.first, track1Sample3.second);
|
mp4Muxer.writeSampleData(track1, track1Sample3.first, track1Sample3.second);
|
||||||
mp4Muxer.writeSampleData(track1, track1Sample4.first, track1Sample4.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, track2Sample1.first, track2Sample1.second);
|
||||||
mp4Muxer.writeSampleData(track2, track2Sample2.first, track2Sample2.second);
|
mp4Muxer.writeSampleData(track2, track2Sample2.first, track2Sample2.second);
|
||||||
mp4Muxer.writeSampleData(track2, track2Sample3.first, track2Sample3.second);
|
mp4Muxer.writeSampleData(track2, track2Sample3.first, track2Sample3.second);
|
||||||
@ -337,7 +336,7 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L);
|
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 100L);
|
||||||
|
|
||||||
try {
|
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, track1Sample1.first, track1Sample1.second);
|
||||||
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
|
mp4Muxer.writeSampleData(track1, track1Sample2.first, track1Sample2.second);
|
||||||
// Add same track again but without any samples.
|
// Add same track again but without any samples.
|
||||||
@ -365,11 +364,11 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_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++) {
|
for (int i = 0; i < 50_000; i++) {
|
||||||
Pair<ByteBuffer, BufferInfo> sampleAndSampleInfo =
|
Pair<ByteBuffer, BufferInfo> sampleAndSampleInfo =
|
||||||
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ i);
|
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ i);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
@ -397,11 +396,11 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_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++) {
|
for (int i = 0; i < 1_000; i++) {
|
||||||
Pair<ByteBuffer, BufferInfo> sampleAndSampleInfo =
|
Pair<ByteBuffer, BufferInfo> sampleAndSampleInfo =
|
||||||
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ i);
|
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ i);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
@ -448,24 +447,24 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_000_000L));
|
/* modificationTimestampSeconds= */ 5_000_000L));
|
||||||
TrackToken primaryVideoTrackToken = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
int primaryVideoTrackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
||||||
TrackToken sharpVideoTrackToken =
|
int sharpVideoTrackId =
|
||||||
muxer.addTrack(
|
muxer.addTrack(
|
||||||
FAKE_VIDEO_FORMAT
|
FAKE_VIDEO_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
||||||
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
|
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
|
||||||
.build());
|
.build());
|
||||||
TrackToken depthLinearVideoTrackToken =
|
int depthLinearVideoTrackId =
|
||||||
muxer.addTrack(
|
muxer.addTrack(
|
||||||
FAKE_VIDEO_FORMAT
|
FAKE_VIDEO_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
||||||
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
|
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
|
||||||
.build());
|
.build());
|
||||||
writeFakeSamples(muxer, primaryVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, primaryVideoTrackId, /* sampleCount= */ 5);
|
||||||
writeFakeSamples(muxer, sharpVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, sharpVideoTrackId, /* sampleCount= */ 5);
|
||||||
writeFakeSamples(muxer, depthLinearVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, depthLinearVideoTrackId, /* sampleCount= */ 5);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
@ -490,24 +489,24 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_000_000L));
|
/* modificationTimestampSeconds= */ 5_000_000L));
|
||||||
TrackToken primaryVideoTrackToken = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
int primaryVideoTrackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
||||||
TrackToken sharpVideoTrackToken =
|
int sharpVideoTrackId =
|
||||||
muxer.addTrack(
|
muxer.addTrack(
|
||||||
FAKE_VIDEO_FORMAT
|
FAKE_VIDEO_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
||||||
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
|
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
|
||||||
.build());
|
.build());
|
||||||
TrackToken depthLinearVideoTrackToken =
|
int depthLinearVideoTrackId =
|
||||||
muxer.addTrack(
|
muxer.addTrack(
|
||||||
FAKE_VIDEO_FORMAT
|
FAKE_VIDEO_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
||||||
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
|
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
|
||||||
.build());
|
.build());
|
||||||
writeFakeSamples(muxer, primaryVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, primaryVideoTrackId, /* sampleCount= */ 5);
|
||||||
writeFakeSamples(muxer, sharpVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, sharpVideoTrackId, /* sampleCount= */ 5);
|
||||||
writeFakeSamples(muxer, depthLinearVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, depthLinearVideoTrackId, /* sampleCount= */ 5);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
@ -540,24 +539,24 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_000_000L));
|
/* modificationTimestampSeconds= */ 5_000_000L));
|
||||||
TrackToken primaryVideoTrackToken = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
int primaryVideoTrackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
||||||
TrackToken sharpVideoTrackToken =
|
int sharpVideoTrackId =
|
||||||
muxer.addTrack(
|
muxer.addTrack(
|
||||||
FAKE_VIDEO_FORMAT
|
FAKE_VIDEO_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
||||||
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
|
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
|
||||||
.build());
|
.build());
|
||||||
TrackToken depthLinearVideoTrackToken =
|
int depthLinearVideoTrackId =
|
||||||
muxer.addTrack(
|
muxer.addTrack(
|
||||||
FAKE_VIDEO_FORMAT
|
FAKE_VIDEO_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
||||||
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
|
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
|
||||||
.build());
|
.build());
|
||||||
writeFakeSamples(muxer, primaryVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, primaryVideoTrackId, /* sampleCount= */ 5);
|
||||||
writeFakeSamples(muxer, sharpVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, sharpVideoTrackId, /* sampleCount= */ 5);
|
||||||
writeFakeSamples(muxer, depthLinearVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, depthLinearVideoTrackId, /* sampleCount= */ 5);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
@ -591,24 +590,24 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_000_000L));
|
/* modificationTimestampSeconds= */ 5_000_000L));
|
||||||
TrackToken primaryVideoTrackToken = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
int primaryVideoTrackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
||||||
TrackToken sharpVideoTrackToken =
|
int sharpVideoTrackId =
|
||||||
muxer.addTrack(
|
muxer.addTrack(
|
||||||
FAKE_VIDEO_FORMAT
|
FAKE_VIDEO_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
||||||
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
|
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
|
||||||
.build());
|
.build());
|
||||||
TrackToken depthLinearVideoTrackToken =
|
int depthLinearVideoTrackId =
|
||||||
muxer.addTrack(
|
muxer.addTrack(
|
||||||
FAKE_VIDEO_FORMAT
|
FAKE_VIDEO_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
||||||
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
|
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
|
||||||
.build());
|
.build());
|
||||||
writeFakeSamples(muxer, primaryVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, primaryVideoTrackId, /* sampleCount= */ 5);
|
||||||
writeFakeSamples(muxer, sharpVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, sharpVideoTrackId, /* sampleCount= */ 5);
|
||||||
writeFakeSamples(muxer, depthLinearVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, depthLinearVideoTrackId, /* sampleCount= */ 5);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
@ -642,24 +641,24 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_000_000L));
|
/* modificationTimestampSeconds= */ 5_000_000L));
|
||||||
TrackToken primaryVideoTrackToken = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
int primaryVideoTrackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
||||||
TrackToken sharpVideoTrackToken =
|
int sharpVideoTrackId =
|
||||||
muxer.addTrack(
|
muxer.addTrack(
|
||||||
FAKE_VIDEO_FORMAT
|
FAKE_VIDEO_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
||||||
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
|
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
|
||||||
.build());
|
.build());
|
||||||
TrackToken depthLinearVideoTrackToken =
|
int depthLinearVideoTrackId =
|
||||||
muxer.addTrack(
|
muxer.addTrack(
|
||||||
FAKE_VIDEO_FORMAT
|
FAKE_VIDEO_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
||||||
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
|
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
|
||||||
.build());
|
.build());
|
||||||
writeFakeSamples(muxer, primaryVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, primaryVideoTrackId, /* sampleCount= */ 5);
|
||||||
writeFakeSamples(muxer, sharpVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, sharpVideoTrackId, /* sampleCount= */ 5);
|
||||||
writeFakeSamples(muxer, depthLinearVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, depthLinearVideoTrackId, /* sampleCount= */ 5);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
@ -693,24 +692,24 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_000_000L));
|
/* modificationTimestampSeconds= */ 5_000_000L));
|
||||||
TrackToken primaryVideoTrackToken = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
int primaryVideoTrackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
||||||
TrackToken sharpVideoTrackToken =
|
int sharpVideoTrackId =
|
||||||
muxer.addTrack(
|
muxer.addTrack(
|
||||||
FAKE_VIDEO_FORMAT
|
FAKE_VIDEO_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
||||||
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
|
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_ORIGINAL)
|
||||||
.build());
|
.build());
|
||||||
TrackToken depthLinearVideoTrackToken =
|
int depthLinearVideoTrackId =
|
||||||
muxer.addTrack(
|
muxer.addTrack(
|
||||||
FAKE_VIDEO_FORMAT
|
FAKE_VIDEO_FORMAT
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
.setRoleFlags(C.ROLE_FLAG_AUXILIARY)
|
||||||
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
|
.setAuxiliaryTrackType(C.AUXILIARY_TRACK_TYPE_DEPTH_LINEAR)
|
||||||
.build());
|
.build());
|
||||||
writeFakeSamples(muxer, primaryVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, primaryVideoTrackId, /* sampleCount= */ 5);
|
||||||
writeFakeSamples(muxer, sharpVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, sharpVideoTrackId, /* sampleCount= */ 5);
|
||||||
writeFakeSamples(muxer, depthLinearVideoTrackToken, /* sampleCount= */ 5);
|
writeFakeSamples(muxer, depthLinearVideoTrackId, /* sampleCount= */ 5);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
@ -751,7 +750,7 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
|
|
||||||
long expectedDurationUs = 1_000L;
|
long expectedDurationUs = 1_000L;
|
||||||
try {
|
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, sample1.first, sample1.second);
|
||||||
mp4Muxer.writeSampleData(track, sample2.first, sample2.second);
|
mp4Muxer.writeSampleData(track, sample2.first, sample2.second);
|
||||||
mp4Muxer.writeSampleData(track, sample3.first, sample3.second);
|
mp4Muxer.writeSampleData(track, sample3.first, sample3.second);
|
||||||
@ -798,7 +797,7 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 300L);
|
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ 300L);
|
||||||
|
|
||||||
try {
|
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, sample1.first, sample1.second);
|
||||||
mp4Muxer.writeSampleData(track, sample2.first, sample2.second);
|
mp4Muxer.writeSampleData(track, sample2.first, sample2.second);
|
||||||
mp4Muxer.writeSampleData(track, sample3.first, sample3.second);
|
mp4Muxer.writeSampleData(track, sample3.first, sample3.second);
|
||||||
@ -814,12 +813,12 @@ public class Mp4MuxerEndToEndTest {
|
|||||||
assertThat(fakeExtractorOutput.seekMap.getDurationUs()).isEqualTo(400L);
|
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 {
|
throws MuxerException {
|
||||||
for (int i = 0; i < sampleCount; i++) {
|
for (int i = 0; i < sampleCount; i++) {
|
||||||
Pair<ByteBuffer, BufferInfo> sampleAndSampleInfo =
|
Pair<ByteBuffer, BufferInfo> sampleAndSampleInfo =
|
||||||
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ i);
|
getFakeSampleAndSampleInfo(/* presentationTimeUs= */ i);
|
||||||
muxer.writeSampleData(trackToken, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ import androidx.media3.container.Mp4TimestampData;
|
|||||||
import androidx.media3.container.XmpData;
|
import androidx.media3.container.XmpData;
|
||||||
import androidx.media3.extractor.mp4.Mp4Extractor;
|
import androidx.media3.extractor.mp4.Mp4Extractor;
|
||||||
import androidx.media3.extractor.text.DefaultSubtitleParserFactory;
|
import androidx.media3.extractor.text.DefaultSubtitleParserFactory;
|
||||||
import androidx.media3.muxer.Muxer.TrackToken;
|
|
||||||
import androidx.media3.test.utils.DumpFileAsserts;
|
import androidx.media3.test.utils.DumpFileAsserts;
|
||||||
import androidx.media3.test.utils.DumpableMp4Box;
|
import androidx.media3.test.utils.DumpableMp4Box;
|
||||||
import androidx.media3.test.utils.FakeExtractorOutput;
|
import androidx.media3.test.utils.FakeExtractorOutput;
|
||||||
@ -66,8 +65,8 @@ public class Mp4MuxerMetadataTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_000_000L));
|
/* modificationTimestampSeconds= */ 5_000_000L));
|
||||||
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
@ -92,8 +91,8 @@ public class Mp4MuxerMetadataTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_000_000L));
|
/* modificationTimestampSeconds= */ 5_000_000L));
|
||||||
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
|
|
||||||
muxer.addMetadataEntry(new Mp4OrientationData(/* orientation= */ 90));
|
muxer.addMetadataEntry(new Mp4OrientationData(/* orientation= */ 90));
|
||||||
} finally {
|
} finally {
|
||||||
@ -120,8 +119,8 @@ public class Mp4MuxerMetadataTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_000_000L));
|
/* modificationTimestampSeconds= */ 5_000_000L));
|
||||||
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
|
|
||||||
muxer.addMetadataEntry(new Mp4OrientationData(/* orientation= */ 180));
|
muxer.addMetadataEntry(new Mp4OrientationData(/* orientation= */ 180));
|
||||||
} finally {
|
} finally {
|
||||||
@ -148,8 +147,8 @@ public class Mp4MuxerMetadataTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_000_000L));
|
/* modificationTimestampSeconds= */ 5_000_000L));
|
||||||
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
|
|
||||||
muxer.addMetadataEntry(new Mp4OrientationData(/* orientation= */ 270));
|
muxer.addMetadataEntry(new Mp4OrientationData(/* orientation= */ 270));
|
||||||
} finally {
|
} finally {
|
||||||
@ -176,8 +175,8 @@ public class Mp4MuxerMetadataTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_000_000L));
|
/* modificationTimestampSeconds= */ 5_000_000L));
|
||||||
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
muxer.addMetadataEntry(new Mp4LocationData(/* latitude= */ 33.0f, /* longitude= */ -120f));
|
muxer.addMetadataEntry(new Mp4LocationData(/* latitude= */ 33.0f, /* longitude= */ -120f));
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
@ -203,8 +202,8 @@ public class Mp4MuxerMetadataTest {
|
|||||||
new Mp4TimestampData(
|
new Mp4TimestampData(
|
||||||
/* creationTimestampSeconds= */ 1_000_000L,
|
/* creationTimestampSeconds= */ 1_000_000L,
|
||||||
/* modificationTimestampSeconds= */ 5_000_000L));
|
/* modificationTimestampSeconds= */ 5_000_000L));
|
||||||
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
@ -233,8 +232,8 @@ public class Mp4MuxerMetadataTest {
|
|||||||
muxer.addMetadataEntry(
|
muxer.addMetadataEntry(
|
||||||
new MdtaMetadataEntry(
|
new MdtaMetadataEntry(
|
||||||
KEY_ANDROID_CAPTURE_FPS, Util.toByteArray(captureFps), TYPE_INDICATOR_FLOAT32));
|
KEY_ANDROID_CAPTURE_FPS, Util.toByteArray(captureFps), TYPE_INDICATOR_FLOAT32));
|
||||||
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
@ -262,8 +261,8 @@ public class Mp4MuxerMetadataTest {
|
|||||||
muxer.addMetadataEntry(
|
muxer.addMetadataEntry(
|
||||||
new MdtaMetadataEntry(
|
new MdtaMetadataEntry(
|
||||||
"SomeStringKey", Util.getUtf8Bytes("Some Random String"), TYPE_INDICATOR_STRING));
|
"SomeStringKey", Util.getUtf8Bytes("Some Random String"), TYPE_INDICATOR_STRING));
|
||||||
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
@ -289,10 +288,10 @@ public class Mp4MuxerMetadataTest {
|
|||||||
muxer.addMetadataEntry(
|
muxer.addMetadataEntry(
|
||||||
new MdtaMetadataEntry(metadataKey, metadataValue, TYPE_INDICATOR_STRING));
|
new MdtaMetadataEntry(metadataKey, metadataValue, TYPE_INDICATOR_STRING));
|
||||||
}
|
}
|
||||||
TrackToken token = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
int trackId = muxer.addTrack(FAKE_VIDEO_FORMAT);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
assertThat(sampleAndSampleInfo.first.remaining()).isEqualTo(0);
|
assertThat(sampleAndSampleInfo.first.remaining()).isEqualTo(0);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
@ -313,8 +312,8 @@ public class Mp4MuxerMetadataTest {
|
|||||||
muxer.addMetadataEntry(
|
muxer.addMetadataEntry(
|
||||||
new MdtaMetadataEntry(
|
new MdtaMetadataEntry(
|
||||||
"SomeStringKey", Util.toByteArray(floatValue), TYPE_INDICATOR_FLOAT32));
|
"SomeStringKey", Util.toByteArray(floatValue), TYPE_INDICATOR_FLOAT32));
|
||||||
TrackToken token = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
int trackId = muxer.addTrack(/* sortKey= */ 0, FAKE_VIDEO_FORMAT);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
@ -342,8 +341,8 @@ public class Mp4MuxerMetadataTest {
|
|||||||
Context context = ApplicationProvider.getApplicationContext();
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
byte[] xmpBytes = TestUtil.getByteArray(context, XMP_SAMPLE_DATA);
|
byte[] xmpBytes = TestUtil.getByteArray(context, XMP_SAMPLE_DATA);
|
||||||
muxer.addMetadataEntry(new XmpData(xmpBytes));
|
muxer.addMetadataEntry(new XmpData(xmpBytes));
|
||||||
TrackToken token = muxer.addTrack(0, FAKE_VIDEO_FORMAT);
|
int trackId = muxer.addTrack(0, FAKE_VIDEO_FORMAT);
|
||||||
muxer.writeSampleData(token, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
muxer.writeSampleData(trackId, sampleAndSampleInfo.first, sampleAndSampleInfo.second);
|
||||||
} finally {
|
} finally {
|
||||||
muxer.close();
|
muxer.close();
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import static org.junit.Assume.assumeFalse;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.MediaCodec.BufferInfo;
|
import android.media.MediaCodec.BufferInfo;
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Effect;
|
import androidx.media3.common.Effect;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
@ -448,7 +447,7 @@ public class TransformerPauseResumeTest {
|
|||||||
private final FrameBlockingMuxer.Listener listener;
|
private final FrameBlockingMuxer.Listener listener;
|
||||||
|
|
||||||
private boolean notifiedListener;
|
private boolean notifiedListener;
|
||||||
@Nullable private TrackToken videoTrackToken;
|
private int videoTrackId;
|
||||||
|
|
||||||
private FrameBlockingMuxer(Muxer wrappedMuxer, FrameBlockingMuxer.Listener listener) {
|
private FrameBlockingMuxer(Muxer wrappedMuxer, FrameBlockingMuxer.Listener listener) {
|
||||||
this.wrappedMuxer = wrappedMuxer;
|
this.wrappedMuxer = wrappedMuxer;
|
||||||
@ -456,18 +455,18 @@ public class TransformerPauseResumeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackToken addTrack(Format format) throws MuxerException {
|
public int addTrack(Format format) throws MuxerException {
|
||||||
TrackToken trackToken = wrappedMuxer.addTrack(format);
|
int trackId = wrappedMuxer.addTrack(format);
|
||||||
if (MimeTypes.isVideo(format.sampleMimeType)) {
|
if (MimeTypes.isVideo(format.sampleMimeType)) {
|
||||||
videoTrackToken = trackToken;
|
videoTrackId = trackId;
|
||||||
}
|
}
|
||||||
return trackToken;
|
return trackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeSampleData(TrackToken trackToken, ByteBuffer data, BufferInfo bufferInfo)
|
public void writeSampleData(int trackId, ByteBuffer data, BufferInfo bufferInfo)
|
||||||
throws MuxerException {
|
throws MuxerException {
|
||||||
if (trackToken == videoTrackToken
|
if (trackId == videoTrackId
|
||||||
&& bufferInfo.presentationTimeUs >= DEFAULT_PRESENTATION_TIME_US_TO_BLOCK_FRAME) {
|
&& bufferInfo.presentationTimeUs >= DEFAULT_PRESENTATION_TIME_US_TO_BLOCK_FRAME) {
|
||||||
if (!notifiedListener) {
|
if (!notifiedListener) {
|
||||||
listener.onFrameBlocked();
|
listener.onFrameBlocked();
|
||||||
@ -475,7 +474,7 @@ public class TransformerPauseResumeTest {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
wrappedMuxer.writeSampleData(trackToken, data, bufferInfo);
|
wrappedMuxer.writeSampleData(trackId, data, bufferInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -84,14 +84,14 @@ public final class DefaultMuxer implements Muxer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackToken addTrack(Format format) throws MuxerException {
|
public int addTrack(Format format) throws MuxerException {
|
||||||
return muxer.addTrack(format);
|
return muxer.addTrack(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
|
public void writeSampleData(int trackId, ByteBuffer byteBuffer, BufferInfo bufferInfo)
|
||||||
throws MuxerException {
|
throws MuxerException {
|
||||||
muxer.writeSampleData(trackToken, byteBuffer, bufferInfo);
|
muxer.writeSampleData(trackId, byteBuffer, bufferInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -24,7 +24,7 @@ import android.annotation.SuppressLint;
|
|||||||
import android.media.MediaCodec.BufferInfo;
|
import android.media.MediaCodec.BufferInfo;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.media.MediaMuxer;
|
import android.media.MediaMuxer;
|
||||||
import androidx.annotation.Nullable;
|
import android.util.SparseArray;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.Metadata;
|
import androidx.media3.common.Metadata;
|
||||||
@ -40,9 +40,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/** {@link Muxer} implementation that uses a {@link MediaMuxer}. */
|
/** {@link Muxer} implementation that uses a {@link MediaMuxer}. */
|
||||||
/* package */ final class FrameworkMuxer implements Muxer {
|
/* package */ final class FrameworkMuxer implements Muxer {
|
||||||
@ -105,13 +103,14 @@ import java.util.Map;
|
|||||||
private static final ImmutableList<String> SUPPORTED_AUDIO_SAMPLE_MIME_TYPES =
|
private static final ImmutableList<String> SUPPORTED_AUDIO_SAMPLE_MIME_TYPES =
|
||||||
ImmutableList.of(MimeTypes.AUDIO_AAC, MimeTypes.AUDIO_AMR_NB, MimeTypes.AUDIO_AMR_WB);
|
ImmutableList.of(MimeTypes.AUDIO_AAC, MimeTypes.AUDIO_AMR_NB, MimeTypes.AUDIO_AMR_WB);
|
||||||
private static final String TAG = "FrameworkMuxer";
|
private static final String TAG = "FrameworkMuxer";
|
||||||
|
private static final int TRACK_ID_UNSET = -1;
|
||||||
|
|
||||||
private final MediaMuxer mediaMuxer;
|
private final MediaMuxer mediaMuxer;
|
||||||
private final long videoDurationUs;
|
private final long videoDurationUs;
|
||||||
private final Map<TrackToken, Long> trackTokenToLastPresentationTimeUs;
|
private final SparseArray<Long> trackIdToLastPresentationTimeUs;
|
||||||
private final Map<TrackToken, Long> trackTokenToPresentationTimeOffsetUs;
|
private final SparseArray<Long> trackIdToPresentationTimeOffsetUs;
|
||||||
|
|
||||||
@Nullable private TrackToken videoTrackToken;
|
private int videoTrackId;
|
||||||
|
|
||||||
private boolean isStarted;
|
private boolean isStarted;
|
||||||
private boolean isReleased;
|
private boolean isReleased;
|
||||||
@ -119,12 +118,13 @@ import java.util.Map;
|
|||||||
private FrameworkMuxer(MediaMuxer mediaMuxer, long videoDurationUs) {
|
private FrameworkMuxer(MediaMuxer mediaMuxer, long videoDurationUs) {
|
||||||
this.mediaMuxer = mediaMuxer;
|
this.mediaMuxer = mediaMuxer;
|
||||||
this.videoDurationUs = videoDurationUs;
|
this.videoDurationUs = videoDurationUs;
|
||||||
trackTokenToLastPresentationTimeUs = new HashMap<>();
|
trackIdToLastPresentationTimeUs = new SparseArray<>();
|
||||||
trackTokenToPresentationTimeOffsetUs = new HashMap<>();
|
trackIdToPresentationTimeOffsetUs = new SparseArray<>();
|
||||||
|
videoTrackId = TRACK_ID_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackToken addTrack(Format format) throws MuxerException {
|
public int addTrack(Format format) throws MuxerException {
|
||||||
String sampleMimeType = checkNotNull(format.sampleMimeType);
|
String sampleMimeType = checkNotNull(format.sampleMimeType);
|
||||||
MediaFormat mediaFormat;
|
MediaFormat mediaFormat;
|
||||||
boolean isVideo = MimeTypes.isVideo(sampleMimeType);
|
boolean isVideo = MimeTypes.isVideo(sampleMimeType);
|
||||||
@ -150,20 +150,19 @@ import java.util.Map;
|
|||||||
throw new MuxerException("Failed to add track with format=" + format, e);
|
throw new MuxerException("Failed to add track with format=" + format, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackToken trackToken = new TrackTokenImpl(trackIndex);
|
|
||||||
if (isVideo) {
|
if (isVideo) {
|
||||||
videoTrackToken = trackToken;
|
videoTrackId = trackIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
return trackToken;
|
return trackIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeSampleData(TrackToken trackToken, ByteBuffer data, BufferInfo bufferInfo)
|
public void writeSampleData(int trackId, ByteBuffer data, BufferInfo bufferInfo)
|
||||||
throws MuxerException {
|
throws MuxerException {
|
||||||
long presentationTimeUs = bufferInfo.presentationTimeUs;
|
long presentationTimeUs = bufferInfo.presentationTimeUs;
|
||||||
if (videoDurationUs != C.TIME_UNSET
|
if (videoDurationUs != C.TIME_UNSET
|
||||||
&& trackToken == videoTrackToken
|
&& trackId == videoTrackId
|
||||||
&& presentationTimeUs > videoDurationUs) {
|
&& presentationTimeUs > videoDurationUs) {
|
||||||
Log.w(
|
Log.w(
|
||||||
TAG,
|
TAG,
|
||||||
@ -176,20 +175,18 @@ import java.util.Map;
|
|||||||
}
|
}
|
||||||
if (!isStarted) {
|
if (!isStarted) {
|
||||||
if (Util.SDK_INT < 30 && presentationTimeUs < 0) {
|
if (Util.SDK_INT < 30 && presentationTimeUs < 0) {
|
||||||
trackTokenToPresentationTimeOffsetUs.put(trackToken, -presentationTimeUs);
|
trackIdToPresentationTimeOffsetUs.put(trackId, -presentationTimeUs);
|
||||||
}
|
}
|
||||||
startMuxer();
|
startMuxer();
|
||||||
}
|
}
|
||||||
|
|
||||||
long presentationTimeOffsetUs =
|
long presentationTimeOffsetUs =
|
||||||
trackTokenToPresentationTimeOffsetUs.containsKey(trackToken)
|
trackIdToPresentationTimeOffsetUs.get(trackId, /* valueIfKeyNotFound= */ 0L);
|
||||||
? trackTokenToPresentationTimeOffsetUs.get(trackToken)
|
|
||||||
: 0;
|
|
||||||
presentationTimeUs += presentationTimeOffsetUs;
|
presentationTimeUs += presentationTimeOffsetUs;
|
||||||
|
|
||||||
long lastSamplePresentationTimeUs =
|
long lastSamplePresentationTimeUs =
|
||||||
trackTokenToLastPresentationTimeUs.containsKey(trackToken)
|
Util.contains(trackIdToLastPresentationTimeUs, trackId)
|
||||||
? trackTokenToLastPresentationTimeUs.get(trackToken)
|
? trackIdToLastPresentationTimeUs.get(trackId)
|
||||||
: 0;
|
: 0;
|
||||||
// writeSampleData blocks on old API versions, so check here to avoid calling the method.
|
// writeSampleData blocks on old API versions, so check here to avoid calling the method.
|
||||||
checkState(
|
checkState(
|
||||||
@ -199,7 +196,7 @@ import java.util.Map;
|
|||||||
+ " < "
|
+ " < "
|
||||||
+ lastSamplePresentationTimeUs
|
+ lastSamplePresentationTimeUs
|
||||||
+ ") unsupported on this API version");
|
+ ") unsupported on this API version");
|
||||||
trackTokenToLastPresentationTimeUs.put(trackToken, presentationTimeUs);
|
trackIdToLastPresentationTimeUs.put(trackId, presentationTimeUs);
|
||||||
|
|
||||||
checkState(
|
checkState(
|
||||||
presentationTimeOffsetUs == 0 || presentationTimeUs >= 0,
|
presentationTimeOffsetUs == 0 || presentationTimeUs >= 0,
|
||||||
@ -212,8 +209,8 @@ import java.util.Map;
|
|||||||
bufferInfo.set(bufferInfo.offset, bufferInfo.size, presentationTimeUs, bufferInfo.flags);
|
bufferInfo.set(bufferInfo.offset, bufferInfo.size, presentationTimeUs, bufferInfo.flags);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
checkState(trackToken instanceof TrackTokenImpl);
|
|
||||||
mediaMuxer.writeSampleData(((TrackTokenImpl) trackToken).trackIndex, data, bufferInfo);
|
mediaMuxer.writeSampleData(trackId, data, bufferInfo);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
throw new MuxerException(
|
throw new MuxerException(
|
||||||
"Failed to write sample for presentationTimeUs="
|
"Failed to write sample for presentationTimeUs="
|
||||||
@ -244,14 +241,14 @@ import java.util.Map;
|
|||||||
startMuxer();
|
startMuxer();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (videoDurationUs != C.TIME_UNSET && videoTrackToken != null) {
|
if (videoDurationUs != C.TIME_UNSET && videoTrackId != TRACK_ID_UNSET) {
|
||||||
BufferInfo bufferInfo = new BufferInfo();
|
BufferInfo bufferInfo = new BufferInfo();
|
||||||
bufferInfo.set(
|
bufferInfo.set(
|
||||||
/* newOffset= */ 0,
|
/* newOffset= */ 0,
|
||||||
/* newSize= */ 0,
|
/* newSize= */ 0,
|
||||||
videoDurationUs,
|
videoDurationUs,
|
||||||
TransformerUtil.getMediaCodecFlags(C.BUFFER_FLAG_END_OF_STREAM));
|
TransformerUtil.getMediaCodecFlags(C.BUFFER_FLAG_END_OF_STREAM));
|
||||||
writeSampleData(checkNotNull(videoTrackToken), ByteBuffer.allocateDirect(0), bufferInfo);
|
writeSampleData(videoTrackId, ByteBuffer.allocateDirect(0), bufferInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
isStarted = false;
|
isStarted = false;
|
||||||
@ -314,12 +311,4 @@ import java.util.Map;
|
|||||||
}
|
}
|
||||||
return supportedMimeTypes.build();
|
return supportedMimeTypes.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class TrackTokenImpl implements TrackToken {
|
|
||||||
public final int trackIndex;
|
|
||||||
|
|
||||||
public TrackTokenImpl(int trackIndex) {
|
|
||||||
this.trackIndex = trackIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.transformer;
|
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 static androidx.media3.muxer.Mp4Muxer.LAST_SAMPLE_DURATION_BEHAVIOR_SET_FROM_END_OF_STREAM_BUFFER_OR_DUPLICATE_PREVIOUS;
|
||||||
|
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
@ -207,13 +206,14 @@ public final class InAppMuxer implements Muxer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static final String TAG = "InAppMuxer";
|
private static final String TAG = "InAppMuxer";
|
||||||
|
private static final int TRACK_ID_UNSET = -1;
|
||||||
|
|
||||||
private final Muxer muxer;
|
private final Muxer muxer;
|
||||||
@Nullable private final MetadataProvider metadataProvider;
|
@Nullable private final MetadataProvider metadataProvider;
|
||||||
private final long videoDurationUs;
|
private final long videoDurationUs;
|
||||||
private final Set<Metadata.Entry> metadataEntries;
|
private final Set<Metadata.Entry> metadataEntries;
|
||||||
|
|
||||||
@Nullable private TrackToken videoTrackToken;
|
private int videoTrackId;
|
||||||
|
|
||||||
private InAppMuxer(
|
private InAppMuxer(
|
||||||
Muxer muxer, @Nullable MetadataProvider metadataProvider, long videoDurationUs) {
|
Muxer muxer, @Nullable MetadataProvider metadataProvider, long videoDurationUs) {
|
||||||
@ -221,23 +221,24 @@ public final class InAppMuxer implements Muxer {
|
|||||||
this.metadataProvider = metadataProvider;
|
this.metadataProvider = metadataProvider;
|
||||||
this.videoDurationUs = videoDurationUs;
|
this.videoDurationUs = videoDurationUs;
|
||||||
metadataEntries = new LinkedHashSet<>();
|
metadataEntries = new LinkedHashSet<>();
|
||||||
|
videoTrackId = TRACK_ID_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackToken addTrack(Format format) throws MuxerException {
|
public int addTrack(Format format) throws MuxerException {
|
||||||
TrackToken trackToken = muxer.addTrack(format);
|
int trackId = muxer.addTrack(format);
|
||||||
if (MimeTypes.isVideo(format.sampleMimeType)) {
|
if (MimeTypes.isVideo(format.sampleMimeType)) {
|
||||||
muxer.addMetadataEntry(new Mp4OrientationData(format.rotationDegrees));
|
muxer.addMetadataEntry(new Mp4OrientationData(format.rotationDegrees));
|
||||||
videoTrackToken = trackToken;
|
videoTrackId = trackId;
|
||||||
}
|
}
|
||||||
return trackToken;
|
return trackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
|
public void writeSampleData(int trackId, ByteBuffer byteBuffer, BufferInfo bufferInfo)
|
||||||
throws MuxerException {
|
throws MuxerException {
|
||||||
if (videoDurationUs != C.TIME_UNSET
|
if (videoDurationUs != C.TIME_UNSET
|
||||||
&& trackToken == videoTrackToken
|
&& trackId == videoTrackId
|
||||||
&& bufferInfo.presentationTimeUs > videoDurationUs) {
|
&& bufferInfo.presentationTimeUs > videoDurationUs) {
|
||||||
Log.w(
|
Log.w(
|
||||||
TAG,
|
TAG,
|
||||||
@ -248,7 +249,7 @@ public final class InAppMuxer implements Muxer {
|
|||||||
videoDurationUs));
|
videoDurationUs));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
muxer.writeSampleData(trackToken, byteBuffer, bufferInfo);
|
muxer.writeSampleData(trackId, byteBuffer, bufferInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -260,14 +261,14 @@ public final class InAppMuxer implements Muxer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws MuxerException {
|
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 bufferInfo = new BufferInfo();
|
||||||
bufferInfo.set(
|
bufferInfo.set(
|
||||||
/* newOffset= */ 0,
|
/* newOffset= */ 0,
|
||||||
/* newSize= */ 0,
|
/* newSize= */ 0,
|
||||||
videoDurationUs,
|
videoDurationUs,
|
||||||
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
|
||||||
writeSampleData(checkNotNull(videoTrackToken), ByteBuffer.allocateDirect(0), bufferInfo);
|
writeSampleData(videoTrackId, ByteBuffer.allocateDirect(0), bufferInfo);
|
||||||
}
|
}
|
||||||
writeMetadata();
|
writeMetadata();
|
||||||
muxer.close();
|
muxer.close();
|
||||||
|
@ -48,7 +48,6 @@ import androidx.media3.common.util.Util;
|
|||||||
import androidx.media3.container.NalUnitUtil;
|
import androidx.media3.container.NalUnitUtil;
|
||||||
import androidx.media3.effect.DebugTraceUtil;
|
import androidx.media3.effect.DebugTraceUtil;
|
||||||
import androidx.media3.muxer.Muxer;
|
import androidx.media3.muxer.Muxer;
|
||||||
import androidx.media3.muxer.Muxer.TrackToken;
|
|
||||||
import androidx.media3.muxer.MuxerException;
|
import androidx.media3.muxer.MuxerException;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -580,7 +579,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
data.remaining(),
|
data.remaining(),
|
||||||
presentationTimeUs,
|
presentationTimeUs,
|
||||||
TransformerUtil.getMediaCodecFlags(isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0));
|
TransformerUtil.getMediaCodecFlags(isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0));
|
||||||
muxer.writeSampleData(trackInfo.trackToken, data, bufferInfo);
|
muxer.writeSampleData(trackInfo.trackId, data, bufferInfo);
|
||||||
|
|
||||||
DebugTraceUtil.logEvent(
|
DebugTraceUtil.logEvent(
|
||||||
COMPONENT_MUXER,
|
COMPONENT_MUXER,
|
||||||
@ -749,16 +748,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
private static final class TrackInfo {
|
private static final class TrackInfo {
|
||||||
public final Format format;
|
public final Format format;
|
||||||
public final TrackToken trackToken;
|
public final int trackId;
|
||||||
|
|
||||||
public long startTimeUs;
|
public long startTimeUs;
|
||||||
public long bytesWritten;
|
public long bytesWritten;
|
||||||
public int sampleCount;
|
public int sampleCount;
|
||||||
public long timeUs;
|
public long timeUs;
|
||||||
|
|
||||||
public TrackInfo(Format format, TrackToken trackToken) {
|
public TrackInfo(Format format, int trackId) {
|
||||||
this.format = format;
|
this.format = format;
|
||||||
this.trackToken = trackToken;
|
this.trackId = trackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,13 +63,12 @@ import java.nio.ByteBuffer;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackToken addTrack(Format format) {
|
public int addTrack(Format format) {
|
||||||
return new TrackToken() {};
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeSampleData(
|
public void writeSampleData(int trackId, ByteBuffer data, MediaCodec.BufferInfo bufferInfo) {}
|
||||||
TrackToken trackToken, ByteBuffer data, MediaCodec.BufferInfo bufferInfo) {}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addMetadataEntry(Metadata.Entry metadataEntry) {}
|
public void addMetadataEntry(Metadata.Entry metadataEntry) {}
|
||||||
|
@ -38,8 +38,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Dumpable} {@link Muxer} implementation that supports dumping information about all
|
* 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 boolean handleAudioAsPcm;
|
||||||
private final SparseArray<DumpableFormat> dumpableFormatByTrackType;
|
private final SparseArray<DumpableFormat> dumpableFormatByTrackType;
|
||||||
private final SparseArray<DumpableStream> dumpableStreamByTrackType;
|
private final SparseArray<DumpableStream> dumpableStreamByTrackType;
|
||||||
private final Map<TrackToken, Integer> trackTokenToType;
|
private final SparseArray<Integer> trackIdToType;
|
||||||
private final ArrayList<Metadata.Entry> metadataList;
|
private final ArrayList<Metadata.Entry> metadataList;
|
||||||
private boolean released;
|
private boolean released;
|
||||||
|
|
||||||
@ -98,18 +96,18 @@ public final class CapturingMuxer implements Muxer, Dumpable {
|
|||||||
this.handleAudioAsPcm = handleAudioAsPcm;
|
this.handleAudioAsPcm = handleAudioAsPcm;
|
||||||
dumpableFormatByTrackType = new SparseArray<>();
|
dumpableFormatByTrackType = new SparseArray<>();
|
||||||
dumpableStreamByTrackType = new SparseArray<>();
|
dumpableStreamByTrackType = new SparseArray<>();
|
||||||
trackTokenToType = new HashMap<>();
|
trackIdToType = new SparseArray<>();
|
||||||
metadataList = new ArrayList<>();
|
metadataList = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Muxer implementation.
|
// Muxer implementation.
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TrackToken addTrack(Format format) throws MuxerException {
|
public int addTrack(Format format) throws MuxerException {
|
||||||
TrackToken trackToken = wrappedMuxer.addTrack(format);
|
int trackId = wrappedMuxer.addTrack(format);
|
||||||
@C.TrackType int trackType = getProcessedTrackType(format.sampleMimeType);
|
@C.TrackType int trackType = getProcessedTrackType(format.sampleMimeType);
|
||||||
|
|
||||||
trackTokenToType.put(trackToken, trackType);
|
trackIdToType.put(trackId, trackType);
|
||||||
|
|
||||||
dumpableFormatByTrackType.append(
|
dumpableFormatByTrackType.append(
|
||||||
trackType, new DumpableFormat(format, /* tag= */ Util.getTrackTypeString(trackType)));
|
trackType, new DumpableFormat(format, /* tag= */ Util.getTrackTypeString(trackType)));
|
||||||
@ -120,20 +118,20 @@ public final class CapturingMuxer implements Muxer, Dumpable {
|
|||||||
? new DumpablePcmAudioStream(trackType)
|
? new DumpablePcmAudioStream(trackType)
|
||||||
: new DumpableStream(trackType));
|
: new DumpableStream(trackType));
|
||||||
|
|
||||||
return trackToken;
|
return trackId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeSampleData(TrackToken trackToken, ByteBuffer data, BufferInfo bufferInfo)
|
public void writeSampleData(int trackId, ByteBuffer data, BufferInfo bufferInfo)
|
||||||
throws MuxerException {
|
throws MuxerException {
|
||||||
@C.TrackType int trackType = checkNotNull(trackTokenToType.get(trackToken));
|
@C.TrackType int trackType = checkNotNull(trackIdToType.get(trackId));
|
||||||
dumpableStreamByTrackType
|
dumpableStreamByTrackType
|
||||||
.get(trackType)
|
.get(trackType)
|
||||||
.addSample(
|
.addSample(
|
||||||
data,
|
data,
|
||||||
(bufferInfo.flags & C.BUFFER_FLAG_KEY_FRAME) == C.BUFFER_FLAG_KEY_FRAME,
|
(bufferInfo.flags & C.BUFFER_FLAG_KEY_FRAME) == C.BUFFER_FLAG_KEY_FRAME,
|
||||||
bufferInfo.presentationTimeUs);
|
bufferInfo.presentationTimeUs);
|
||||||
wrappedMuxer.writeSampleData(trackToken, data, bufferInfo);
|
wrappedMuxer.writeSampleData(trackId, data, bufferInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
x
Reference in New Issue
Block a user