Add error code and exception type for muxing failures.
Exceptions thrown by MediaMuxer are converted MuxerExceptions and later to TransformationExceptions with ERROR_CODE_MUXING_FAILED. PiperOrigin-RevId: 421033721
This commit is contained in:
parent
b208d6d26e
commit
6070b200ae
@ -127,7 +127,7 @@ import java.nio.ByteBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addTrack(Format format) {
|
||||
public int addTrack(Format format) throws MuxerException {
|
||||
String sampleMimeType = checkNotNull(format.sampleMimeType);
|
||||
MediaFormat mediaFormat;
|
||||
if (MimeTypes.isAudio(sampleMimeType)) {
|
||||
@ -137,29 +137,56 @@ import java.nio.ByteBuffer;
|
||||
} else {
|
||||
mediaFormat =
|
||||
MediaFormat.createVideoFormat(castNonNull(sampleMimeType), format.width, format.height);
|
||||
try {
|
||||
mediaMuxer.setOrientationHint(format.rotationDegrees);
|
||||
} catch (RuntimeException e) {
|
||||
throw new MuxerException(
|
||||
"Failed to set orientation hint with rotationDegrees=" + format.rotationDegrees, e);
|
||||
}
|
||||
}
|
||||
MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData);
|
||||
return mediaMuxer.addTrack(mediaFormat);
|
||||
int trackIndex;
|
||||
try {
|
||||
trackIndex = mediaMuxer.addTrack(mediaFormat);
|
||||
} catch (RuntimeException e) {
|
||||
throw new MuxerException("Failed to add track with format=" + format, e);
|
||||
}
|
||||
return trackIndex;
|
||||
}
|
||||
|
||||
@SuppressLint("WrongConstant") // C.BUFFER_FLAG_KEY_FRAME equals MediaCodec.BUFFER_FLAG_KEY_FRAME.
|
||||
@Override
|
||||
public void writeSampleData(
|
||||
int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) {
|
||||
int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs)
|
||||
throws MuxerException {
|
||||
if (!isStarted) {
|
||||
isStarted = true;
|
||||
try {
|
||||
mediaMuxer.start();
|
||||
} catch (RuntimeException e) {
|
||||
throw new MuxerException("Failed to start the muxer", e);
|
||||
}
|
||||
}
|
||||
int offset = data.position();
|
||||
int size = data.limit() - offset;
|
||||
int flags = isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0;
|
||||
bufferInfo.set(offset, size, presentationTimeUs, flags);
|
||||
try {
|
||||
mediaMuxer.writeSampleData(trackIndex, data, bufferInfo);
|
||||
} catch (RuntimeException e) {
|
||||
throw new MuxerException(
|
||||
"Failed to write sample for trackIndex="
|
||||
+ trackIndex
|
||||
+ ", presentationTimeUs="
|
||||
+ presentationTimeUs
|
||||
+ ", size="
|
||||
+ size,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(boolean forCancellation) {
|
||||
public void release(boolean forCancellation) throws MuxerException {
|
||||
if (!isStarted) {
|
||||
mediaMuxer.release();
|
||||
return;
|
||||
@ -168,7 +195,7 @@ import java.nio.ByteBuffer;
|
||||
isStarted = false;
|
||||
try {
|
||||
mediaMuxer.stop();
|
||||
} catch (IllegalStateException e) {
|
||||
} catch (RuntimeException e) {
|
||||
if (SDK_INT < 30) {
|
||||
// Set the muxer state to stopped even if mediaMuxer.stop() failed so that
|
||||
// mediaMuxer.release() doesn't attempt to stop the muxer and therefore doesn't throw the
|
||||
@ -187,7 +214,7 @@ import java.nio.ByteBuffer;
|
||||
}
|
||||
// It doesn't matter that stopping the muxer throws if the transformation is being cancelled.
|
||||
if (!forCancellation) {
|
||||
throw e;
|
||||
throw new MuxerException("Failed to stop the muxer", e);
|
||||
}
|
||||
} finally {
|
||||
mediaMuxer.release();
|
||||
|
@ -36,6 +36,19 @@ import java.nio.ByteBuffer;
|
||||
*/
|
||||
/* package */ interface Muxer {
|
||||
|
||||
/** Thrown when a muxing failure occurs. */
|
||||
/* package */ 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 {
|
||||
/**
|
||||
@ -83,8 +96,11 @@ import java.nio.ByteBuffer;
|
||||
/**
|
||||
* Adds a track with the specified format, and returns its index (to be passed in subsequent calls
|
||||
* to {@link #writeSampleData(int, ByteBuffer, boolean, long)}).
|
||||
*
|
||||
* @param format The {@link Format} of the track.
|
||||
* @throws MuxerException If the muxer encounters a problem while adding the track.
|
||||
*/
|
||||
int addTrack(Format format);
|
||||
int addTrack(Format format) throws MuxerException;
|
||||
|
||||
/**
|
||||
* Writes the specified sample.
|
||||
@ -93,15 +109,18 @@ import java.nio.ByteBuffer;
|
||||
* @param data Buffer containing the sample data to write to the container.
|
||||
* @param isKeyFrame Whether the sample is a key frame.
|
||||
* @param presentationTimeUs The presentation time of the sample in microseconds.
|
||||
* @throws MuxerException If the muxer fails to start or an error occurs while writing the sample.
|
||||
*/
|
||||
void writeSampleData(
|
||||
int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs);
|
||||
void writeSampleData(int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs)
|
||||
throws MuxerException;
|
||||
|
||||
/**
|
||||
* Releases any resources associated with muxing.
|
||||
*
|
||||
* @param forCancellation Whether the reason for releasing the resources is the transformation
|
||||
* cancellation.
|
||||
* @throws MuxerException If the muxer fails to stop or release resources and {@code
|
||||
* forCancellation} is false.
|
||||
*/
|
||||
void release(boolean forCancellation);
|
||||
void release(boolean forCancellation) throws MuxerException;
|
||||
}
|
||||
|
@ -103,8 +103,10 @@ import java.nio.ByteBuffer;
|
||||
* @param format The {@link Format} to be added.
|
||||
* @throws IllegalStateException If the format is unsupported or if there is already a track
|
||||
* format of the same type (audio or video).
|
||||
* @throws Muxer.MuxerException If the underlying muxer encounters a problem while adding the
|
||||
* track.
|
||||
*/
|
||||
public void addTrackFormat(Format format) {
|
||||
public void addTrackFormat(Format format) throws Muxer.MuxerException {
|
||||
checkState(trackCount > 0, "All tracks should be registered before the formats are added.");
|
||||
checkState(trackFormatCount < trackCount, "All track formats have already been added.");
|
||||
@Nullable String sampleMimeType = format.sampleMimeType;
|
||||
@ -138,9 +140,11 @@ import java.nio.ByteBuffer;
|
||||
* good interleaving.
|
||||
* @throws IllegalStateException If the muxer doesn't have any {@link #endTrack(int) non-ended}
|
||||
* track of the given track type.
|
||||
* @throws Muxer.MuxerException If the underlying muxer fails to write the sample.
|
||||
*/
|
||||
public boolean writeSample(
|
||||
@C.TrackType int trackType, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) {
|
||||
@C.TrackType int trackType, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs)
|
||||
throws Muxer.MuxerException {
|
||||
int trackIndex = trackTypeToIndex.get(trackType, /* valueIfKeyNotFound= */ C.INDEX_UNSET);
|
||||
checkState(
|
||||
trackIndex != C.INDEX_UNSET,
|
||||
@ -174,8 +178,10 @@ import java.nio.ByteBuffer;
|
||||
*
|
||||
* @param forCancellation Whether the reason for releasing the resources is the transformation
|
||||
* cancellation.
|
||||
* @throws Muxer.MuxerException If the underlying muxer fails to stop and to release resources and
|
||||
* {@code forCancellation} is false.
|
||||
*/
|
||||
public void release(boolean forCancellation) {
|
||||
public void release(boolean forCancellation) throws Muxer.MuxerException {
|
||||
isReady = false;
|
||||
muxer.release(forCancellation);
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ public final class TransformationException extends Exception {
|
||||
ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED,
|
||||
ERROR_CODE_GL_INIT_FAILED,
|
||||
ERROR_CODE_GL_PROCESSING_FAILED,
|
||||
ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED,
|
||||
ERROR_CODE_MUXING_FAILED,
|
||||
})
|
||||
public @interface ErrorCode {}
|
||||
|
||||
@ -164,6 +166,8 @@ public final class TransformationException extends Exception {
|
||||
* TransformationRequest.Builder#setVideoMimeType(String)} to transcode to a supported MIME type.
|
||||
*/
|
||||
public static final int ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED = 6001;
|
||||
/** Caused by a failure while muxing media samples. */
|
||||
public static final int ERROR_CODE_MUXING_FAILED = 6002;
|
||||
|
||||
private static final ImmutableBiMap<String, @ErrorCode Integer> NAME_TO_ERROR_CODE =
|
||||
new ImmutableBiMap.Builder<String, @ErrorCode Integer>()
|
||||
@ -188,6 +192,7 @@ public final class TransformationException extends Exception {
|
||||
.put(
|
||||
"ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED",
|
||||
ERROR_CODE_MUXER_SAMPLE_MIME_TYPE_UNSUPPORTED)
|
||||
.put("ERROR_CODE_MUXING_FAILED", ERROR_CODE_MUXING_FAILED)
|
||||
.buildOrThrow();
|
||||
|
||||
/** Returns the {@code errorCode} for a given name. */
|
||||
|
@ -672,7 +672,11 @@ public final class Transformer {
|
||||
* @throws IllegalStateException If this method is called from the wrong thread.
|
||||
*/
|
||||
public void cancel() {
|
||||
try {
|
||||
releaseResources(/* forCancellation= */ true);
|
||||
} catch (TransformationException impossible) {
|
||||
throw new IllegalStateException(impossible);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -681,17 +685,22 @@ public final class Transformer {
|
||||
* @param forCancellation Whether the reason for releasing the resources is the transformation
|
||||
* cancellation.
|
||||
* @throws IllegalStateException If this method is called from the wrong thread.
|
||||
* @throws IllegalStateException If the muxer is in the wrong state and {@code forCancellation} is
|
||||
* false.
|
||||
* @throws TransformationException If the muxer is in the wrong state and {@code forCancellation}
|
||||
* is false.
|
||||
*/
|
||||
private void releaseResources(boolean forCancellation) {
|
||||
private void releaseResources(boolean forCancellation) throws TransformationException {
|
||||
verifyApplicationThread();
|
||||
if (player != null) {
|
||||
player.release();
|
||||
player = null;
|
||||
}
|
||||
if (muxerWrapper != null) {
|
||||
try {
|
||||
muxerWrapper.release(forCancellation);
|
||||
} catch (Muxer.MuxerException e) {
|
||||
throw TransformationException.createForMuxer(
|
||||
e, TransformationException.ERROR_CODE_MUXING_FAILED);
|
||||
}
|
||||
muxerWrapper = null;
|
||||
}
|
||||
progressState = PROGRESS_STATE_NO_TRANSFORMATION;
|
||||
@ -826,9 +835,9 @@ public final class Transformer {
|
||||
@Nullable TransformationException resourceReleaseException = null;
|
||||
try {
|
||||
releaseResources(/* forCancellation= */ false);
|
||||
} catch (IllegalStateException e) {
|
||||
// TODO(internal b/209469847): Use a more specific error code when the IllegalStateException
|
||||
// is caused by the muxer.
|
||||
} catch (TransformationException e) {
|
||||
resourceReleaseException = e;
|
||||
} catch (RuntimeException e) {
|
||||
resourceReleaseException = TransformationException.createForUnexpected(e);
|
||||
}
|
||||
|
||||
|
@ -97,6 +97,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
while (feedMuxerFromPipeline() || samplePipeline.processData() || feedPipelineFromInput()) {}
|
||||
} catch (TransformationException e) {
|
||||
throw wrapTransformationException(e);
|
||||
} catch (Muxer.MuxerException e) {
|
||||
throw wrapTransformationException(
|
||||
TransformationException.createForMuxer(
|
||||
e, TransformationException.ERROR_CODE_MUXING_FAILED));
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,7 +149,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
* @return Whether it may be possible to write more data immediately by calling this method again.
|
||||
*/
|
||||
@RequiresNonNull("samplePipeline")
|
||||
private boolean feedMuxerFromPipeline() {
|
||||
private boolean feedMuxerFromPipeline() throws Muxer.MuxerException {
|
||||
if (!muxerWrapperTrackAdded) {
|
||||
@Nullable Format samplePipelineOutputFormat = samplePipeline.getOutputFormat();
|
||||
if (samplePipelineOutputFormat == null) {
|
||||
|
@ -45,7 +45,7 @@ public final class TestMuxer implements Muxer, Dumper.Dumpable {
|
||||
// Muxer implementation.
|
||||
|
||||
@Override
|
||||
public int addTrack(Format format) {
|
||||
public int addTrack(Format format) throws MuxerException {
|
||||
int trackIndex = muxer.addTrack(format);
|
||||
dumpables.add(new DumpableFormat(format, trackIndex));
|
||||
return trackIndex;
|
||||
@ -53,13 +53,14 @@ public final class TestMuxer implements Muxer, Dumper.Dumpable {
|
||||
|
||||
@Override
|
||||
public void writeSampleData(
|
||||
int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs) {
|
||||
int trackIndex, ByteBuffer data, boolean isKeyFrame, long presentationTimeUs)
|
||||
throws MuxerException {
|
||||
dumpables.add(new DumpableSample(trackIndex, data, isKeyFrame, presentationTimeUs));
|
||||
muxer.writeSampleData(trackIndex, data, isKeyFrame, presentationTimeUs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(boolean forCancellation) {
|
||||
public void release(boolean forCancellation) throws MuxerException {
|
||||
dumpables.add(dumper -> dumper.add("released", true));
|
||||
muxer.release(forCancellation);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user