Replace Transformer.Muxer interface with Muxer.Muxer
PiperOrigin-RevId: 633193701
This commit is contained in:
parent
5950e884f6
commit
3a3145521b
@ -32,6 +32,9 @@
|
||||
at stereo when handling PCM input.
|
||||
* When selecting tracks in `ExoPlayerAssetLoader`, ignore audio channel
|
||||
count constraints as they only apply for playback.
|
||||
* Replace `androidx.media3.transformer.Muxer` interface with
|
||||
`androidx.media3.muxer.Muxer` and remove
|
||||
`androidx.media3.transformer.Muxer`.
|
||||
* Track Selection:
|
||||
* Extractors:
|
||||
* MPEG-TS: Roll forward the change ensuring the last frame is rendered by
|
||||
|
@ -83,6 +83,7 @@ dependencies {
|
||||
implementation project(modulePrefix + 'lib-exoplayer')
|
||||
implementation project(modulePrefix + 'lib-exoplayer-dash')
|
||||
implementation project(modulePrefix + 'lib-transformer')
|
||||
implementation project(modulePrefix + 'lib-muxer')
|
||||
implementation project(modulePrefix + 'lib-ui')
|
||||
|
||||
// For MediaPipe and its dependencies:
|
||||
|
@ -80,6 +80,7 @@ import androidx.media3.effect.TextureOverlay;
|
||||
import androidx.media3.exoplayer.ExoPlayer;
|
||||
import androidx.media3.exoplayer.audio.SilenceSkippingAudioProcessor;
|
||||
import androidx.media3.exoplayer.util.DebugTextViewHelper;
|
||||
import androidx.media3.muxer.Muxer;
|
||||
import androidx.media3.transformer.Composition;
|
||||
import androidx.media3.transformer.DefaultEncoderFactory;
|
||||
import androidx.media3.transformer.DefaultMuxer;
|
||||
@ -90,7 +91,6 @@ import androidx.media3.transformer.ExportException;
|
||||
import androidx.media3.transformer.ExportResult;
|
||||
import androidx.media3.transformer.InAppMuxer;
|
||||
import androidx.media3.transformer.JsonUtil;
|
||||
import androidx.media3.transformer.Muxer;
|
||||
import androidx.media3.transformer.ProgressHolder;
|
||||
import androidx.media3.transformer.Transformer;
|
||||
import androidx.media3.ui.AspectRatioFrameLayout;
|
||||
|
@ -19,6 +19,7 @@ import android.content.Context;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaExtractor;
|
||||
import androidx.media3.common.util.MediaFormatUtil;
|
||||
import androidx.media3.muxer.Muxer.MuxerException;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
@ -37,7 +38,7 @@ import java.util.List;
|
||||
}
|
||||
|
||||
public static void feedInputDataToMuxer(Context context, Muxer muxer, String inputFileName)
|
||||
throws IOException {
|
||||
throws IOException, MuxerException {
|
||||
MediaExtractor extractor = new MediaExtractor();
|
||||
extractor.setDataSource(
|
||||
context.getResources().getAssets().openFd(MP4_FILE_ASSET_DIRECTORY + inputFileName));
|
||||
|
@ -62,7 +62,7 @@ public class FragmentedMp4MuxerEndToEndAndroidTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createFragmentedMp4File_fromInputFileSampleData_matchesExpected() throws IOException {
|
||||
public void createFragmentedMp4File_fromInputFileSampleData_matchesExpected() throws Exception {
|
||||
@Nullable Muxer fragmentedMp4Muxer = null;
|
||||
|
||||
try {
|
||||
@ -89,7 +89,7 @@ public class FragmentedMp4MuxerEndToEndAndroidTest {
|
||||
|
||||
@Test
|
||||
public void createFragmentedMp4File_fromInputFileSampleData_matchesExpectedBoxStructure()
|
||||
throws IOException {
|
||||
throws Exception {
|
||||
@Nullable Muxer fragmentedMp4Muxer = null;
|
||||
|
||||
try {
|
||||
|
@ -73,7 +73,7 @@ public class Mp4MuxerEndToEndAndroidTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMp4File_fromInputFileSampleData_matchesExpected() throws IOException {
|
||||
public void createMp4File_fromInputFileSampleData_matchesExpected() throws Exception {
|
||||
@Nullable Mp4Muxer mp4Muxer = null;
|
||||
|
||||
try {
|
||||
@ -96,7 +96,7 @@ public class Mp4MuxerEndToEndAndroidTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMp4File_muxerNotClosed_createsPartiallyWrittenValidFile() throws IOException {
|
||||
public void createMp4File_muxerNotClosed_createsPartiallyWrittenValidFile() throws Exception {
|
||||
// Skip for all parameter values except when the input is a large file. The muxer writes samples
|
||||
// in batches (and flushes data only when it's closed), so a large input file is needed to
|
||||
// ensure some data has been written after taking all the inputs but before closing the muxer.
|
||||
|
@ -156,12 +156,21 @@ public final class FragmentedMp4Muxer implements Muxer {
|
||||
* Builder#setSampleCopyEnabled(boolean) sample copying} is disabled. Otherwise, the position
|
||||
* of the buffer is updated but the caller retains ownership.
|
||||
* @param bufferInfo The {@link BufferInfo} related to this sample.
|
||||
* @throws IOException 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
|
||||
public void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
|
||||
throws IOException {
|
||||
fragmentedMp4Writer.writeSampleData(trackToken, byteBuffer, bufferInfo);
|
||||
throws MuxerException {
|
||||
try {
|
||||
fragmentedMp4Writer.writeSampleData(trackToken, byteBuffer, bufferInfo);
|
||||
} catch (IOException e) {
|
||||
throw new MuxerException(
|
||||
"Failed to write sample for presentationTimeUs="
|
||||
+ bufferInfo.presentationTimeUs
|
||||
+ ", size="
|
||||
+ bufferInfo.size,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -190,7 +199,11 @@ public final class FragmentedMp4Muxer implements Muxer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
fragmentedMp4Writer.close();
|
||||
public void close() throws MuxerException {
|
||||
try {
|
||||
fragmentedMp4Writer.close();
|
||||
} catch (IOException e) {
|
||||
throw new MuxerException("Failed to close the muxer", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,12 +221,21 @@ public final class Mp4Muxer implements Muxer {
|
||||
* Builder#setSampleCopyEnabled(boolean) sample copying} is disabled. Otherwise, the position
|
||||
* of the buffer is updated but the caller retains ownership.
|
||||
* @param bufferInfo The {@link BufferInfo} related to this sample.
|
||||
* @throws IOException 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
|
||||
public void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
|
||||
throws IOException {
|
||||
mp4Writer.writeSampleData(trackToken, byteBuffer, bufferInfo);
|
||||
throws MuxerException {
|
||||
try {
|
||||
mp4Writer.writeSampleData(trackToken, byteBuffer, bufferInfo);
|
||||
} catch (IOException e) {
|
||||
throw new MuxerException(
|
||||
"Failed to write sample for presentationTimeUs="
|
||||
+ bufferInfo.presentationTimeUs
|
||||
+ ", size="
|
||||
+ bufferInfo.size,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -255,7 +264,11 @@ public final class Mp4Muxer implements Muxer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
mp4Writer.close();
|
||||
public void close() throws MuxerException {
|
||||
try {
|
||||
mp4Writer.close();
|
||||
} catch (IOException e) {
|
||||
throw new MuxerException("Failed to close the muxer", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,28 +16,81 @@
|
||||
package androidx.media3.muxer;
|
||||
|
||||
import android.media.MediaCodec.BufferInfo;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.Metadata;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import java.io.IOException;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/** The muxer for producing media container files. */
|
||||
@UnstableApi
|
||||
public interface Muxer {
|
||||
/** Thrown when a muxer failure occurs. */
|
||||
final class MuxerException extends Exception {
|
||||
/**
|
||||
* Creates an instance.
|
||||
*
|
||||
* @param message See {@link #getMessage()}.
|
||||
* @param cause See {@link #getCause()}.
|
||||
*/
|
||||
public MuxerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
/** Factory for muxers. */
|
||||
interface Factory {
|
||||
/**
|
||||
* Returns a new {@link Muxer}.
|
||||
*
|
||||
* @param path The path to the output file.
|
||||
* @throws MuxerException If an error occurs opening the output file for writing.
|
||||
*/
|
||||
Muxer create(String path) throws MuxerException;
|
||||
|
||||
/**
|
||||
* Returns the supported sample {@linkplain MimeTypes MIME types} for the given {@link
|
||||
* C.TrackType}.
|
||||
*/
|
||||
ImmutableList<String> getSupportedSampleMimeTypes(@C.TrackType int trackType);
|
||||
}
|
||||
|
||||
/** A token representing an added track. */
|
||||
interface TrackToken {}
|
||||
|
||||
/** Adds a track of the given media format. */
|
||||
TrackToken addTrack(Format format);
|
||||
/**
|
||||
* 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}.
|
||||
* @throws MuxerException If the muxer encounters a problem while adding the track.
|
||||
*/
|
||||
TrackToken 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
|
||||
* #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)
|
||||
throws IOException;
|
||||
throws MuxerException;
|
||||
|
||||
/** Adds {@linkplain Metadata.Entry metadata} about the output file. */
|
||||
void addMetadataEntry(Metadata.Entry metadataEntry);
|
||||
|
||||
/** Closes the file. */
|
||||
void close() throws IOException;
|
||||
/**
|
||||
* Closes the file.
|
||||
*
|
||||
* <p>The muxer cannot be used anymore once this method returns.
|
||||
*
|
||||
* @throws MuxerException If the muxer fails to finish writing the output.
|
||||
*/
|
||||
void close() throws MuxerException;
|
||||
}
|
||||
|
@ -39,7 +39,6 @@ import androidx.media3.test.utils.TestUtil;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@ -54,7 +53,7 @@ public class Mp4MuxerEndToEndTest {
|
||||
private final Context context = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Test
|
||||
public void createMp4File_addTrackAndMetadataButNoSamples_createsEmptyFile() throws IOException {
|
||||
public void createMp4File_addTrackAndMetadataButNoSamples_createsEmptyFile() throws Exception {
|
||||
String outputFilePath = temporaryFolder.newFile().getPath();
|
||||
Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
|
||||
|
||||
@ -75,7 +74,7 @@ public class Mp4MuxerEndToEndTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMp4File_withSameTracksOffset_matchesExpected() throws IOException {
|
||||
public void createMp4File_withSameTracksOffset_matchesExpected() throws Exception {
|
||||
String outputFilePath = temporaryFolder.newFile().getPath();
|
||||
Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
|
||||
mp4Muxer.addMetadataEntry(
|
||||
@ -118,7 +117,7 @@ public class Mp4MuxerEndToEndTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMp4File_withDifferentTracksOffset_matchesExpected() throws IOException {
|
||||
public void createMp4File_withDifferentTracksOffset_matchesExpected() throws Exception {
|
||||
String outputFilePath = temporaryFolder.newFile().getPath();
|
||||
Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
|
||||
mp4Muxer.addMetadataEntry(
|
||||
@ -157,7 +156,7 @@ public class Mp4MuxerEndToEndTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeSampleData_withOutOfOrderSampleTimestamps_throws() throws IOException {
|
||||
public void writeSampleData_withOutOfOrderSampleTimestamps_throws() throws Exception {
|
||||
String outputFilePath = temporaryFolder.newFile().getPath();
|
||||
Mp4Muxer mp4Muxer = new Mp4Muxer.Builder(new FileOutputStream(outputFilePath)).build();
|
||||
Pair<ByteBuffer, BufferInfo> track1Sample1 =
|
||||
|
@ -36,7 +36,8 @@ import androidx.media3.common.audio.AudioProcessor;
|
||||
import androidx.media3.common.audio.SonicAudioProcessor;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.effect.RgbFilter;
|
||||
import androidx.media3.muxer.Muxer.TrackToken;
|
||||
import androidx.media3.muxer.Muxer;
|
||||
import androidx.media3.muxer.Muxer.MuxerException;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import com.google.common.base.Ascii;
|
||||
@ -418,7 +419,7 @@ public class TransformerPauseResumeTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Muxer create(String path) throws Muxer.MuxerException {
|
||||
public Muxer create(String path) throws MuxerException {
|
||||
return new FrameBlockingMuxer(wrappedMuxerFactory.create(path), listener);
|
||||
}
|
||||
|
||||
@ -473,8 +474,8 @@ public class TransformerPauseResumeTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() throws MuxerException {
|
||||
wrappedMuxer.release();
|
||||
public void close() throws MuxerException {
|
||||
wrappedMuxer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import androidx.media3.common.C;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.Metadata;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.muxer.Muxer;
|
||||
import androidx.media3.muxer.Muxer.TrackToken;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.nio.ByteBuffer;
|
||||
@ -71,9 +72,9 @@ public final class DefaultMuxer implements Muxer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSampleData(TrackToken trackToken, ByteBuffer data, BufferInfo bufferInfo)
|
||||
public void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
|
||||
throws MuxerException {
|
||||
muxer.writeSampleData(trackToken, data, bufferInfo);
|
||||
muxer.writeSampleData(trackToken, byteBuffer, bufferInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -82,7 +83,7 @@ public final class DefaultMuxer implements Muxer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() throws MuxerException {
|
||||
muxer.release();
|
||||
public void close() throws MuxerException {
|
||||
muxer.close();
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.util.MediaFormatUtil;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.container.Mp4LocationData;
|
||||
import androidx.media3.muxer.Muxer.TrackToken;
|
||||
import androidx.media3.muxer.Muxer;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
@ -199,7 +199,7 @@ import java.util.Map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() throws MuxerException {
|
||||
public void close() throws MuxerException {
|
||||
if (isReleased) {
|
||||
return;
|
||||
}
|
||||
|
@ -25,12 +25,12 @@ import androidx.media3.container.Mp4OrientationData;
|
||||
import androidx.media3.muxer.FragmentedMp4Muxer;
|
||||
import androidx.media3.muxer.Mp4Muxer;
|
||||
import androidx.media3.muxer.Mp4Utils;
|
||||
import androidx.media3.muxer.Muxer;
|
||||
import androidx.media3.muxer.Muxer.TrackToken;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
@ -170,7 +170,7 @@ public final class InAppMuxer implements Muxer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackToken addTrack(Format format) {
|
||||
public TrackToken addTrack(Format format) throws MuxerException {
|
||||
TrackToken trackToken = muxer.addTrack(format);
|
||||
if (MimeTypes.isVideo(format.sampleMimeType)) {
|
||||
muxer.addMetadataEntry(new Mp4OrientationData(format.rotationDegrees));
|
||||
@ -179,19 +179,9 @@ public final class InAppMuxer implements Muxer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeSampleData(TrackToken trackToken, ByteBuffer data, BufferInfo bufferInfo)
|
||||
public void writeSampleData(TrackToken trackToken, ByteBuffer byteBuffer, BufferInfo bufferInfo)
|
||||
throws MuxerException {
|
||||
|
||||
try {
|
||||
muxer.writeSampleData(trackToken, data, bufferInfo);
|
||||
} catch (IOException e) {
|
||||
throw new MuxerException(
|
||||
"Failed to write sample for presentationTimeUs="
|
||||
+ bufferInfo.presentationTimeUs
|
||||
+ ", size="
|
||||
+ bufferInfo.size,
|
||||
e);
|
||||
}
|
||||
muxer.writeSampleData(trackToken, byteBuffer, bufferInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -202,14 +192,9 @@ public final class InAppMuxer implements Muxer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() throws MuxerException {
|
||||
public void close() throws MuxerException {
|
||||
writeMetadata();
|
||||
|
||||
try {
|
||||
muxer.close();
|
||||
} catch (IOException e) {
|
||||
throw new MuxerException("Error closing muxer", e);
|
||||
}
|
||||
muxer.close();
|
||||
}
|
||||
|
||||
private void writeMetadata() {
|
||||
|
@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.media3.transformer;
|
||||
|
||||
import android.media.MediaCodec.BufferInfo;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.Metadata;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.muxer.Muxer.TrackToken;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Abstracts media muxing operations.
|
||||
*
|
||||
* <p>Query whether {@linkplain Factory#getSupportedSampleMimeTypes(int) sample MIME types} are
|
||||
* supported and {@linkplain #addTrack(Format) add all tracks}, then {@linkplain #writeSampleData
|
||||
* write sample data} to mux samples. Once any sample data has been written, it is not possible to
|
||||
* add tracks. After writing all sample data, {@linkplain #release() release} the instance to finish
|
||||
* writing to the output and return any resources to the system.
|
||||
*/
|
||||
@UnstableApi
|
||||
// TODO: b/330695864 - Replace with the Muxer interface from the Muxer module.
|
||||
public interface Muxer {
|
||||
|
||||
/** Thrown when a muxing failure occurs. */
|
||||
final class MuxerException extends Exception {
|
||||
/**
|
||||
* Creates an instance.
|
||||
*
|
||||
* @param message See {@link #getMessage()}.
|
||||
* @param cause See {@link #getCause()}.
|
||||
*/
|
||||
public MuxerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
/** Factory for muxers. */
|
||||
interface Factory {
|
||||
/**
|
||||
* Returns a new muxer writing to a file.
|
||||
*
|
||||
* @param path The path to the output file.
|
||||
* @throws IllegalArgumentException If the path is invalid.
|
||||
* @throws MuxerException If an error occurs opening the output file for writing.
|
||||
*/
|
||||
Muxer create(String path) throws MuxerException;
|
||||
|
||||
/**
|
||||
* Returns the supported sample {@linkplain MimeTypes MIME types} for the given {@link
|
||||
* C.TrackType}.
|
||||
*/
|
||||
ImmutableList<String> getSupportedSampleMimeTypes(@C.TrackType int trackType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a track with the specified format.
|
||||
*
|
||||
* @param format The {@link Format} of the track.
|
||||
* @return The {@link TrackToken} 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;
|
||||
|
||||
/**
|
||||
* Writes the specified sample.
|
||||
*
|
||||
* @param trackToken The {@link TrackToken} of the track, previously returned by {@link
|
||||
* #addTrack(Format)}.
|
||||
* @param data 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 data, BufferInfo bufferInfo)
|
||||
throws MuxerException;
|
||||
|
||||
/** Adds {@linkplain Metadata.Entry metadata} about the output file. */
|
||||
void addMetadataEntry(Metadata.Entry metadataEntry);
|
||||
|
||||
/**
|
||||
* Finishes writing the output and releases any resources associated with muxing.
|
||||
*
|
||||
* <p>The muxer cannot be used anymore once this method has been called.
|
||||
*
|
||||
* @throws MuxerException If the muxer fails to finish writing the output and {@code
|
||||
* forCancellation} is false.
|
||||
*/
|
||||
void release() throws MuxerException;
|
||||
}
|
@ -48,6 +48,8 @@ import androidx.media3.common.MimeTypes;
|
||||
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.MuxerException;
|
||||
import androidx.media3.muxer.Muxer.TrackToken;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.io.File;
|
||||
@ -374,11 +376,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
* @throws IllegalStateException If the number of formats added exceeds the {@linkplain
|
||||
* #setTrackCount track count}, if {@link #setTrackCount(int)} has not been called or if there
|
||||
* is already a track of that {@link C.TrackType}.
|
||||
* @throws Muxer.MuxerException If the underlying {@link Muxer} encounters a problem while adding
|
||||
* the track.
|
||||
* @throws MuxerException If the underlying {@link Muxer} encounters a problem while adding the
|
||||
* track.
|
||||
*/
|
||||
public void addTrackFormat(Format format)
|
||||
throws AppendTrackFormatException, Muxer.MuxerException {
|
||||
public void addTrackFormat(Format format) throws AppendTrackFormatException, MuxerException {
|
||||
@Nullable String sampleMimeType = format.sampleMimeType;
|
||||
@C.TrackType int trackType = MimeTypes.getTrackType(sampleMimeType);
|
||||
checkArgument(
|
||||
@ -521,11 +522,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
* received a format} for every {@linkplain #setTrackCount(int) track}.
|
||||
* @throws IllegalArgumentException If the muxer doesn't have a {@linkplain #endTrack(int)
|
||||
* non-ended} track of the given {@link C.TrackType}.
|
||||
* @throws Muxer.MuxerException If the underlying {@link Muxer} fails to write the sample.
|
||||
* @throws MuxerException If the underlying {@link Muxer} fails to write the sample.
|
||||
*/
|
||||
public boolean writeSample(
|
||||
@C.TrackType int trackType, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs)
|
||||
throws Muxer.MuxerException {
|
||||
throws MuxerException {
|
||||
checkArgument(contains(trackTypeToInfo, trackType));
|
||||
TrackInfo trackInfo = trackTypeToInfo.get(trackType);
|
||||
boolean canWriteSample = canWriteSample(trackType, presentationTimeUs);
|
||||
@ -653,11 +654,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
* #MUXER_RELEASE_REASON_CANCELLED} or {@link #MUXER_RELEASE_REASON_ERROR}.
|
||||
*
|
||||
* @param releaseReason The reason to release the muxer.
|
||||
* @throws Muxer.MuxerException If the underlying {@link Muxer} fails to finish writing the output
|
||||
* and the {@code releaseReason} is not {@link #MUXER_RELEASE_REASON_CANCELLED}.
|
||||
* @throws MuxerException If the underlying {@link Muxer} fails to finish writing the output and
|
||||
* the {@code releaseReason} is not {@link #MUXER_RELEASE_REASON_CANCELLED}.
|
||||
*/
|
||||
public void finishWritingAndMaybeRelease(@MuxerReleaseReason int releaseReason)
|
||||
throws Muxer.MuxerException {
|
||||
throws MuxerException {
|
||||
if (releaseReason == MUXER_RELEASE_REASON_COMPLETED && muxerMode == MUXER_MODE_MUX_PARTIAL) {
|
||||
return;
|
||||
}
|
||||
@ -665,8 +666,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
abortScheduledExecutorService.shutdownNow();
|
||||
if (muxer != null) {
|
||||
try {
|
||||
muxer.release();
|
||||
} catch (Muxer.MuxerException e) {
|
||||
muxer.close();
|
||||
} catch (MuxerException e) {
|
||||
if (releaseReason == MUXER_RELEASE_REASON_CANCELLED
|
||||
&& checkNotNull(e.getMessage())
|
||||
.equals(FrameworkMuxer.MUXER_STOPPING_FAILED_ERROR_MESSAGE)) {
|
||||
@ -736,7 +737,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
}
|
||||
|
||||
@EnsuresNonNull("muxer")
|
||||
private void ensureMuxerInitialized() throws Muxer.MuxerException {
|
||||
private void ensureMuxerInitialized() throws MuxerException {
|
||||
if (muxer == null) {
|
||||
muxer = muxerFactory.create(outputPath);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import androidx.media3.common.Format;
|
||||
import androidx.media3.common.Metadata;
|
||||
import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.decoder.DecoderInputBuffer;
|
||||
import androidx.media3.muxer.Muxer.MuxerException;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.util.List;
|
||||
@ -110,7 +111,7 @@ import java.util.List;
|
||||
}
|
||||
try {
|
||||
muxerWrapper.addTrackFormat(inputFormat);
|
||||
} catch (Muxer.MuxerException e) {
|
||||
} catch (MuxerException e) {
|
||||
throw ExportException.createForMuxer(e, ExportException.ERROR_CODE_MUXING_FAILED);
|
||||
} catch (MuxerWrapper.AppendTrackFormatException e) {
|
||||
throw ExportException.createForMuxer(e, ExportException.ERROR_CODE_MUXING_APPEND);
|
||||
@ -136,7 +137,7 @@ import java.util.List;
|
||||
muxerInputBuffer.timeUs)) {
|
||||
return false;
|
||||
}
|
||||
} catch (Muxer.MuxerException e) {
|
||||
} catch (MuxerException e) {
|
||||
throw ExportException.createForMuxer(e, ExportException.ERROR_CODE_MUXING_FAILED);
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,7 @@ import androidx.media3.effect.DebugTraceUtil;
|
||||
import androidx.media3.effect.DefaultVideoFrameProcessor;
|
||||
import androidx.media3.effect.Presentation;
|
||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||
import androidx.media3.muxer.Muxer;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
|
@ -62,6 +62,7 @@ import androidx.media3.common.util.Clock;
|
||||
import androidx.media3.common.util.ConditionVariable;
|
||||
import androidx.media3.common.util.HandlerWrapper;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.muxer.Muxer.MuxerException;
|
||||
import androidx.media3.transformer.AssetLoader.CompositionSettings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.lang.annotation.Documented;
|
||||
@ -436,7 +437,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
}
|
||||
try {
|
||||
muxerWrapper.finishWritingAndMaybeRelease(getMuxerReleaseReason(endReason));
|
||||
} catch (Muxer.MuxerException e) {
|
||||
} catch (MuxerException e) {
|
||||
if (releaseExportException == null) {
|
||||
releaseExportException = ExportException.createForMuxer(e, ERROR_CODE_MUXING_FAILED);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ import androidx.media3.common.C;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.Metadata;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.muxer.Muxer.TrackToken;
|
||||
import androidx.media3.muxer.Muxer;
|
||||
import androidx.media3.test.utils.DumpableFormat;
|
||||
import androidx.media3.test.utils.Dumper;
|
||||
import androidx.media3.test.utils.Dumper.Dumpable;
|
||||
@ -72,7 +72,7 @@ public final class CapturingMuxer implements Muxer, Dumpable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Muxer create(String path) throws Muxer.MuxerException {
|
||||
public Muxer create(String path) throws MuxerException {
|
||||
muxer = new CapturingMuxer(wrappedFactory.create(path), handleAudioAsPcm);
|
||||
return muxer;
|
||||
}
|
||||
@ -142,9 +142,9 @@ public final class CapturingMuxer implements Muxer, Dumpable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() throws MuxerException {
|
||||
public void close() throws MuxerException {
|
||||
released = true;
|
||||
wrappedMuxer.release();
|
||||
wrappedMuxer.close();
|
||||
}
|
||||
|
||||
// Dumper.Dumpable implementation.
|
||||
|
@ -30,6 +30,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.ColorInfo;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.muxer.Muxer.MuxerException;
|
||||
import androidx.media3.test.utils.DumpFileAsserts;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
@ -74,7 +75,7 @@ public class MuxerWrapperTest {
|
||||
@Nullable private MuxerWrapper muxerWrapper;
|
||||
|
||||
@After
|
||||
public void tearDown() throws Muxer.MuxerException {
|
||||
public void tearDown() throws MuxerException {
|
||||
if (muxerWrapper != null) {
|
||||
// Release with reason cancellation so that underlying resources are always released.
|
||||
muxerWrapper.finishWritingAndMaybeRelease(MuxerWrapper.MUXER_RELEASE_REASON_CANCELLED);
|
||||
|
@ -24,6 +24,7 @@ import androidx.media3.common.audio.ChannelMixingMatrix;
|
||||
import androidx.media3.common.audio.SonicAudioProcessor;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.muxer.Muxer;
|
||||
import androidx.media3.test.utils.FakeClock;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
@ -34,6 +34,7 @@ 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;
|
||||
import androidx.media3.test.utils.DumpFileAsserts;
|
||||
import androidx.media3.test.utils.FakeClock;
|
||||
import androidx.media3.test.utils.FakeExtractorOutput;
|
||||
|
Loading…
x
Reference in New Issue
Block a user