mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Convert PlaybackExceptions to TransformationExceptions.
Transformer uses ExoPlayer for reading input. Apps using Transformer do not need to know this. So, PlaybackExceptions are converted to TransformationExceptions with the same message, cause and error code. The corresponding IO error codes are copied from PlaybackException. PiperOrigin-RevId: 416793741
This commit is contained in:
parent
9d463725fb
commit
f2d337c33d
3 changed files with 177 additions and 106 deletions
|
|
@ -25,10 +25,12 @@ import android.os.SystemClock;
|
|||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.Format;
|
||||
import com.google.android.exoplayer2.PlaybackException;
|
||||
import com.google.android.exoplayer2.audio.AudioProcessor;
|
||||
import com.google.android.exoplayer2.audio.AudioProcessor.AudioFormat;
|
||||
import com.google.android.exoplayer2.util.Clock;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.common.collect.ImmutableBiMap;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
|
@ -37,6 +39,167 @@ import java.lang.annotation.Target;
|
|||
/** Thrown when a non-locally recoverable transformation failure occurs. */
|
||||
public final class TransformationException extends Exception {
|
||||
|
||||
/**
|
||||
* Codes that identify causes of {@link Transformer} errors.
|
||||
*
|
||||
* <p>This list of errors may be extended in future versions. The underlying values may also
|
||||
* change, so it is best to avoid relying on them directly without using the constants.
|
||||
*/
|
||||
// TODO(b/209469847): Update the javadoc once the underlying values are fixed.
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
|
||||
@IntDef(
|
||||
open = true,
|
||||
value = {
|
||||
ERROR_CODE_UNSPECIFIED,
|
||||
ERROR_CODE_FAILED_RUNTIME_CHECK,
|
||||
ERROR_CODE_IO_UNSPECIFIED,
|
||||
ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
|
||||
ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT,
|
||||
ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE,
|
||||
ERROR_CODE_IO_BAD_HTTP_STATUS,
|
||||
ERROR_CODE_IO_FILE_NOT_FOUND,
|
||||
ERROR_CODE_IO_NO_PERMISSION,
|
||||
ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED,
|
||||
ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE,
|
||||
ERROR_CODE_DECODER_INIT_FAILED,
|
||||
ERROR_CODE_DECODING_FAILED,
|
||||
ERROR_CODE_DECODING_FORMAT_UNSUPPORTED,
|
||||
ERROR_CODE_ENCODER_INIT_FAILED,
|
||||
ERROR_CODE_ENCODING_FAILED,
|
||||
ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED,
|
||||
ERROR_CODE_GL_INIT_FAILED,
|
||||
ERROR_CODE_GL_PROCESSING_FAILED,
|
||||
ERROR_CODE_AUDIO_PROCESSOR_INIT_FAILED,
|
||||
})
|
||||
public @interface ErrorCode {}
|
||||
|
||||
// Miscellaneous errors (1xxx).
|
||||
|
||||
/** Caused by an error whose cause could not be identified. */
|
||||
public static final int ERROR_CODE_UNSPECIFIED = 1000;
|
||||
/**
|
||||
* Caused by a failed runtime check.
|
||||
*
|
||||
* <p>This can happen when transformer reaches an invalid state.
|
||||
*/
|
||||
public static final int ERROR_CODE_FAILED_RUNTIME_CHECK = 1001;
|
||||
|
||||
// Input/Output errors (2xxx).
|
||||
|
||||
/** Caused by an Input/Output error which could not be identified. */
|
||||
public static final int ERROR_CODE_IO_UNSPECIFIED = 2000;
|
||||
/**
|
||||
* Caused by a network connection failure.
|
||||
*
|
||||
* <p>The following is a non-exhaustive list of possible reasons:
|
||||
*
|
||||
* <ul>
|
||||
* <li>There is no network connectivity.
|
||||
* <li>The URL's domain is misspelled or does not exist.
|
||||
* <li>The target host is unreachable.
|
||||
* <li>The server unexpectedly closes the connection.
|
||||
* </ul>
|
||||
*/
|
||||
public static final int ERROR_CODE_IO_NETWORK_CONNECTION_FAILED = 2001;
|
||||
/** Caused by a network timeout, meaning the server is taking too long to fulfill a request. */
|
||||
public static final int ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT = 2002;
|
||||
/**
|
||||
* Caused by a server returning a resource with an invalid "Content-Type" HTTP header value.
|
||||
*
|
||||
* <p>For example, this can happen when the player is expecting a piece of media, but the server
|
||||
* returns a paywall HTML page, with content type "text/html".
|
||||
*/
|
||||
public static final int ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE = 2003;
|
||||
/** Caused by an HTTP server returning an unexpected HTTP response status code. */
|
||||
public static final int ERROR_CODE_IO_BAD_HTTP_STATUS = 2004;
|
||||
/** Caused by a non-existent file. */
|
||||
public static final int ERROR_CODE_IO_FILE_NOT_FOUND = 2005;
|
||||
/**
|
||||
* Caused by lack of permission to perform an IO operation. For example, lack of permission to
|
||||
* access internet or external storage.
|
||||
*/
|
||||
public static final int ERROR_CODE_IO_NO_PERMISSION = 2006;
|
||||
/**
|
||||
* Caused by the player trying to access cleartext HTTP traffic (meaning http:// rather than
|
||||
* https://) when the app's Network Security Configuration does not permit it.
|
||||
*/
|
||||
public static final int ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED = 2007;
|
||||
/** Caused by reading data out of the data bound. */
|
||||
public static final int ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE = 2008;
|
||||
|
||||
// Decoding errors (3xxx).
|
||||
|
||||
/** Caused by a decoder initialization failure. */
|
||||
public static final int ERROR_CODE_DECODER_INIT_FAILED = 3001;
|
||||
/** Caused by a failure while trying to decode media samples. */
|
||||
public static final int ERROR_CODE_DECODING_FAILED = 3002;
|
||||
/** Caused by trying to decode content whose format is not supported. */
|
||||
public static final int ERROR_CODE_DECODING_FORMAT_UNSUPPORTED = 3003;
|
||||
|
||||
// Encoding errors (4xxx).
|
||||
|
||||
/** Caused by an encoder initialization failure. */
|
||||
public static final int ERROR_CODE_ENCODER_INIT_FAILED = 4001;
|
||||
/** Caused by a failure while trying to encode media samples. */
|
||||
public static final int ERROR_CODE_ENCODING_FAILED = 4002;
|
||||
/** Caused by requesting to encode content in a format that is not supported by the device. */
|
||||
public static final int ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED = 4003;
|
||||
|
||||
// Video editing errors (5xxx).
|
||||
|
||||
/** Caused by a GL initialization failure. */
|
||||
public static final int ERROR_CODE_GL_INIT_FAILED = 5001;
|
||||
/** Caused by a failure while using or releasing a GL program. */
|
||||
public static final int ERROR_CODE_GL_PROCESSING_FAILED = 5002;
|
||||
|
||||
// Audio editing errors (6xxx).
|
||||
|
||||
/** Caused by an audio processor initialization failure. */
|
||||
public static final int ERROR_CODE_AUDIO_PROCESSOR_INIT_FAILED = 6001;
|
||||
|
||||
private static final ImmutableBiMap<String, @ErrorCode Integer> NAME_TO_ERROR_CODE =
|
||||
new ImmutableBiMap.Builder<String, @ErrorCode Integer>()
|
||||
.put("ERROR_CODE_FAILED_RUNTIME_CHECK", ERROR_CODE_FAILED_RUNTIME_CHECK)
|
||||
.put("ERROR_CODE_IO_UNSPECIFIED", ERROR_CODE_IO_UNSPECIFIED)
|
||||
.put("ERROR_CODE_IO_NETWORK_CONNECTION_FAILED", ERROR_CODE_IO_NETWORK_CONNECTION_FAILED)
|
||||
.put("ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT", ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT)
|
||||
.put("ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE", ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE)
|
||||
.put("ERROR_CODE_IO_BAD_HTTP_STATUS", ERROR_CODE_IO_BAD_HTTP_STATUS)
|
||||
.put("ERROR_CODE_IO_FILE_NOT_FOUND", ERROR_CODE_IO_FILE_NOT_FOUND)
|
||||
.put("ERROR_CODE_IO_NO_PERMISSION", ERROR_CODE_IO_NO_PERMISSION)
|
||||
.put("ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED", ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED)
|
||||
.put("ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE", ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE)
|
||||
.put("ERROR_CODE_DECODER_INIT_FAILED", ERROR_CODE_DECODER_INIT_FAILED)
|
||||
.put("ERROR_CODE_DECODING_FAILED", ERROR_CODE_DECODING_FAILED)
|
||||
.put("ERROR_CODE_DECODING_FORMAT_UNSUPPORTED", ERROR_CODE_DECODING_FORMAT_UNSUPPORTED)
|
||||
.put("ERROR_CODE_ENCODER_INIT_FAILED", ERROR_CODE_ENCODER_INIT_FAILED)
|
||||
.put("ERROR_CODE_ENCODING_FAILED", ERROR_CODE_ENCODING_FAILED)
|
||||
.put("ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED", ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED)
|
||||
.put("ERROR_CODE_GL_INIT_FAILED", ERROR_CODE_GL_INIT_FAILED)
|
||||
.put("ERROR_CODE_GL_PROCESSING_FAILED", ERROR_CODE_GL_PROCESSING_FAILED)
|
||||
.put("ERROR_CODE_AUDIO_PROCESSOR_INIT_FAILED", ERROR_CODE_AUDIO_PROCESSOR_INIT_FAILED)
|
||||
.buildOrThrow();
|
||||
|
||||
/** Returns the {@code errorCode} for a given name. */
|
||||
private static @ErrorCode int getErrorCodeForName(String errorCodeName) {
|
||||
return NAME_TO_ERROR_CODE.getOrDefault(errorCodeName, ERROR_CODE_UNSPECIFIED);
|
||||
}
|
||||
|
||||
/** Returns the name of a given {@code errorCode}. */
|
||||
public static String getErrorCodeName(@ErrorCode int errorCode) {
|
||||
return NAME_TO_ERROR_CODE.inverse().getOrDefault(errorCode, "invalid error code");
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to {@link TransformationException#getErrorCodeName(int)
|
||||
* TransformationException.getErrorCodeName(this.errorCode)}.
|
||||
*/
|
||||
public final String getErrorCodeName() {
|
||||
return getErrorCodeName(errorCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance for a decoder or encoder related exception.
|
||||
*
|
||||
|
|
@ -70,8 +233,8 @@ public final class TransformationException extends Exception {
|
|||
/**
|
||||
* Creates an instance for an unexpected exception.
|
||||
*
|
||||
* <p>If the exception is a runtime exception, error code {@link ERROR_CODE_FAILED_RUNTIME_CHECK}
|
||||
* is used. Otherwise, the created instance has error code {@link ERROR_CODE_UNSPECIFIED}.
|
||||
* <p>If the exception is a runtime exception, error code {@link #ERROR_CODE_FAILED_RUNTIME_CHECK}
|
||||
* is used. Otherwise, the created instance has error code {@link #ERROR_CODE_UNSPECIFIED}.
|
||||
*
|
||||
* @param cause The cause of the failure.
|
||||
* @return The created instance.
|
||||
|
|
@ -85,107 +248,17 @@ public final class TransformationException extends Exception {
|
|||
}
|
||||
|
||||
/**
|
||||
* Codes that identify causes of {@link Transformer} errors.
|
||||
* Converts a {@link PlaybackException} to a {@code TransformationException}.
|
||||
*
|
||||
* <p>This list of errors may be extended in future versions.
|
||||
* <p>If no corresponding error code exists, the created instance will have {@link
|
||||
* #ERROR_CODE_UNSPECIFIED}.
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target({FIELD, METHOD, PARAMETER, LOCAL_VARIABLE, TYPE_USE})
|
||||
@IntDef(
|
||||
open = true,
|
||||
value = {
|
||||
ERROR_CODE_UNSPECIFIED,
|
||||
ERROR_CODE_FAILED_RUNTIME_CHECK,
|
||||
ERROR_CODE_DECODER_INIT_FAILED,
|
||||
ERROR_CODE_DECODING_FAILED,
|
||||
ERROR_CODE_DECODING_FORMAT_UNSUPPORTED,
|
||||
ERROR_CODE_ENCODER_INIT_FAILED,
|
||||
ERROR_CODE_ENCODING_FAILED,
|
||||
ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED,
|
||||
ERROR_CODE_GL_INIT_FAILED,
|
||||
ERROR_CODE_GL_PROCESSING_FAILED,
|
||||
ERROR_CODE_AUDIO_PROCESSOR_INIT_FAILED,
|
||||
})
|
||||
public @interface ErrorCode {}
|
||||
|
||||
// Miscellaneous errors (1xxx).
|
||||
|
||||
/** Caused by an error whose cause could not be identified. */
|
||||
public static final int ERROR_CODE_UNSPECIFIED = 1000;
|
||||
/**
|
||||
* Caused by a failed runtime check.
|
||||
*
|
||||
* <p>This can happen when transformer reaches an invalid state.
|
||||
*/
|
||||
public static final int ERROR_CODE_FAILED_RUNTIME_CHECK = 1001;
|
||||
|
||||
// Decoding errors (2xxx).
|
||||
|
||||
/** Caused by a decoder initialization failure. */
|
||||
public static final int ERROR_CODE_DECODER_INIT_FAILED = 2001;
|
||||
/** Caused by a failure while trying to decode media samples. */
|
||||
public static final int ERROR_CODE_DECODING_FAILED = 2002;
|
||||
/** Caused by trying to decode content whose format is not supported. */
|
||||
public static final int ERROR_CODE_DECODING_FORMAT_UNSUPPORTED = 2003;
|
||||
|
||||
// Encoding errors (3xxx).
|
||||
|
||||
/** Caused by an encoder initialization failure. */
|
||||
public static final int ERROR_CODE_ENCODER_INIT_FAILED = 3001;
|
||||
/** Caused by a failure while trying to encode media samples. */
|
||||
public static final int ERROR_CODE_ENCODING_FAILED = 3002;
|
||||
/** Caused by requesting to encode content in a format that is not supported by the device. */
|
||||
public static final int ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED = 3003;
|
||||
|
||||
// Video editing errors (4xxx).
|
||||
|
||||
/** Caused by a GL initialization failure. */
|
||||
public static final int ERROR_CODE_GL_INIT_FAILED = 4001;
|
||||
/** Caused by a failure while using or releasing a GL program. */
|
||||
public static final int ERROR_CODE_GL_PROCESSING_FAILED = 4002;
|
||||
|
||||
// Audio editing errors (5xxx).
|
||||
|
||||
/** Caused by an audio processor initialization failure. */
|
||||
public static final int ERROR_CODE_AUDIO_PROCESSOR_INIT_FAILED = 5001;
|
||||
|
||||
/** Returns the name of a given {@code errorCode}. */
|
||||
public static String getErrorCodeName(@ErrorCode int errorCode) {
|
||||
switch (errorCode) {
|
||||
case ERROR_CODE_UNSPECIFIED:
|
||||
return "ERROR_CODE_UNSPECIFIED";
|
||||
case ERROR_CODE_FAILED_RUNTIME_CHECK:
|
||||
return "ERROR_CODE_FAILED_RUNTIME_CHECK";
|
||||
case ERROR_CODE_DECODER_INIT_FAILED:
|
||||
return "ERROR_CODE_DECODER_INIT_FAILED";
|
||||
case ERROR_CODE_DECODING_FAILED:
|
||||
return "ERROR_CODE_DECODING_FAILED";
|
||||
case ERROR_CODE_DECODING_FORMAT_UNSUPPORTED:
|
||||
return "ERROR_CODE_DECODING_FORMAT_UNSUPPORTED";
|
||||
case ERROR_CODE_ENCODER_INIT_FAILED:
|
||||
return "ERROR_CODE_ENCODER_INIT_FAILED";
|
||||
case ERROR_CODE_ENCODING_FAILED:
|
||||
return "ERROR_CODE_ENCODING_FAILED";
|
||||
case ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED:
|
||||
return "ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED";
|
||||
case ERROR_CODE_GL_INIT_FAILED:
|
||||
return "ERROR_CODE_GL_INIT_FAILED";
|
||||
case ERROR_CODE_GL_PROCESSING_FAILED:
|
||||
return "ERROR_CODE_GL_PROCESSING_FAILED";
|
||||
case ERROR_CODE_AUDIO_PROCESSOR_INIT_FAILED:
|
||||
return "ERROR_CODE_AUDIO_PROCESSOR_INIT_FAILED";
|
||||
default:
|
||||
return "invalid error code";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent to {@link TransformationException#getErrorCodeName(int)
|
||||
* TransformationException.getErrorCodeName(this.errorCode)}.
|
||||
*/
|
||||
public final String getErrorCodeName() {
|
||||
return getErrorCodeName(errorCode);
|
||||
/* package */ static TransformationException createForPlaybackException(
|
||||
PlaybackException exception) {
|
||||
return new TransformationException(
|
||||
exception.getMessage(),
|
||||
exception.getCause(),
|
||||
getErrorCodeForName(exception.getErrorCodeName()));
|
||||
}
|
||||
|
||||
/** An error code which identifies the cause of the transformation failure. */
|
||||
|
|
|
|||
|
|
@ -845,7 +845,7 @@ public final class Transformer {
|
|||
handleTransformationEnded(
|
||||
cause instanceof TransformationException
|
||||
? (TransformationException) cause
|
||||
: TransformationException.createForUnexpected(error));
|
||||
: TransformationException.createForPlaybackException(error));
|
||||
}
|
||||
|
||||
private void handleTransformationEnded(@Nullable TransformationException exception) {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ import android.view.Surface;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.ExoPlaybackException;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.testutil.DumpFileAsserts;
|
||||
import com.google.android.exoplayer2.testutil.FakeClock;
|
||||
|
|
@ -295,15 +294,14 @@ public final class TransformerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void startTransformation_withPlayerError_completesWithError() throws Exception {
|
||||
public void startTransformation_withIoError_completesWithError() throws Exception {
|
||||
Transformer transformer = new Transformer.Builder(context).setClock(clock).build();
|
||||
MediaItem mediaItem = MediaItem.fromUri("asset:///non-existing-path.mp4");
|
||||
|
||||
transformer.startTransformation(mediaItem, outputPath);
|
||||
TransformationException exception = TransformerTestRunner.runUntilError(transformer);
|
||||
|
||||
assertThat(exception).hasCauseThat().isInstanceOf(ExoPlaybackException.class);
|
||||
assertThat(exception).hasCauseThat().hasCauseThat().isInstanceOf(IOException.class);
|
||||
assertThat(exception).hasCauseThat().isInstanceOf(IOException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Reference in a new issue