Assign PlaybackException.ErrorCode to renderer failures.

PiperOrigin-RevId: 381852092
This commit is contained in:
claincly 2021-06-28 14:39:33 +01:00 committed by Oliver Woodman
parent 7aaba1ffe5
commit ffbec2234d
9 changed files with 96 additions and 34 deletions

View File

@ -170,7 +170,8 @@ public final class ExoPlaybackException extends PlaybackException {
rendererIndex,
rendererFormat,
rendererFormatSupport,
/* isRecoverable= */ false);
/* isRecoverable= */ false,
ERROR_CODE_UNSPECIFIED);
}
/**
@ -183,6 +184,7 @@ public final class ExoPlaybackException extends PlaybackException {
* @param rendererFormatSupport The {@link FormatSupport} of the renderer for {@code
* rendererFormat}. Ignored if {@code rendererFormat} is null.
* @param isRecoverable If the failure can be recovered by disabling and re-enabling the renderer.
* @param errorCode See {@link #errorCode}.
* @return The created instance.
*/
public static ExoPlaybackException createForRenderer(
@ -191,12 +193,14 @@ public final class ExoPlaybackException extends PlaybackException {
int rendererIndex,
@Nullable Format rendererFormat,
@FormatSupport int rendererFormatSupport,
boolean isRecoverable) {
boolean isRecoverable,
@ErrorCode int errorCode) {
return new ExoPlaybackException(
TYPE_RENDERER,
cause,
/* customMessage= */ null,
ERROR_CODE_UNSPECIFIED,
errorCode,
rendererName,
rendererIndex,
rendererFormat,

View File

@ -61,6 +61,7 @@ public class PlaybackException extends Exception implements Bundleable {
ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED,
ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED,
ERROR_CODE_DECODER_INIT_FAILED,
ERROR_CODE_DECODER_QUERY_FAILED,
ERROR_CODE_DECODING_FAILED,
ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES,
ERROR_CODE_DECODING_FORMAT_UNSUPPORTED,
@ -144,12 +145,14 @@ public class PlaybackException extends Exception implements Bundleable {
/** Caused by a decoder initialization failure. */
public static final int ERROR_CODE_DECODER_INIT_FAILED = 4001;
/** Caused by a decoder query failure. */
public static final int ERROR_CODE_DECODER_QUERY_FAILED = 4002;
/** Caused by a failure while trying to decode media samples. */
public static final int ERROR_CODE_DECODING_FAILED = 4002;
public static final int ERROR_CODE_DECODING_FAILED = 4003;
/** Caused by trying to decode content whose format exceeds the capabilities of the device. */
public static final int ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES = 4003;
public static final int ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES = 4004;
/** Caused by trying to decode content whose format is not supported. */
public static final int ERROR_CODE_DECODING_FORMAT_UNSUPPORTED = 4004;
public static final int ERROR_CODE_DECODING_FORMAT_UNSUPPORTED = 4005;
// AudioTrack errors (5xxx).
@ -223,6 +226,8 @@ public class PlaybackException extends Exception implements Bundleable {
return "ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED";
case ERROR_CODE_DECODER_INIT_FAILED:
return "ERROR_CODE_DECODER_INIT_FAILED";
case ERROR_CODE_DECODER_QUERY_FAILED:
return "ERROR_CODE_DECODER_QUERY_FAILED";
case ERROR_CODE_DECODING_FAILED:
return "ERROR_CODE_DECODING_FAILED";
case ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES:

View File

@ -51,7 +51,8 @@ public class ExoPlaybackExceptionTest {
/* rendererIndex= */ 123,
/* rendererFormat= */ new Format.Builder().setCodecs("anyCodec").build(),
/* rendererFormatSupport= */ C.FORMAT_UNSUPPORTED_SUBTYPE,
/* isRecoverable= */ true);
/* isRecoverable= */ true,
/* errorCode= */ PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
ExoPlaybackException after = ExoPlaybackException.CREATOR.fromBundle(before.toBundle());
assertThat(areExoPlaybackExceptionsEqual(before, after)).isTrue();

View File

@ -78,7 +78,7 @@ public class PlaybackExceptionTest {
Bundle bundle = exception.toBundle();
assertThat(bundle.getString("0")).isEqualTo(PlaybackException.class.getName());
assertThat(bundle.getInt("1")).isEqualTo(4002); // Error code.
assertThat(bundle.getInt("1")).isEqualTo(4003); // Error code.
assertThat(bundle.getLong("2")).isEqualTo(2000); // Timestamp.
assertThat(bundle.getString("3")).isEqualTo("message");
assertThat(bundle.getString("4")).isEqualTo(cause.getClass().getName());

View File

@ -336,12 +336,28 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
* Creates an {@link ExoPlaybackException} of type {@link ExoPlaybackException#TYPE_RENDERER} for
* this renderer.
*
* @param cause The cause of the exception.
* @param format The current format used by the renderer. May be null.
* <p>Equivalent to {@link #createRendererException(Throwable, Format, int)
* createRendererException(cause, format, PlaybackException.ERROR_CODE_UNSPECIFIED)}.
*/
protected final ExoPlaybackException createRendererException(
Throwable cause, @Nullable Format format) {
return createRendererException(cause, format, /* isRecoverable= */ false);
return createRendererException(cause, format, PlaybackException.ERROR_CODE_UNSPECIFIED);
}
/**
* Creates an {@link ExoPlaybackException} of type {@link ExoPlaybackException#TYPE_RENDERER} for
* this renderer.
*
* @param cause The cause of the exception.
* @param format The current format used by the renderer. May be null.
* @param errorCode A {@link PlaybackException.ErrorCode} to identify the cause of the playback
* failure.
* @return The created instance, in which {@link ExoPlaybackException#isRecoverable} is {@code
* false}.
*/
protected final ExoPlaybackException createRendererException(
Throwable cause, @Nullable Format format, @PlaybackException.ErrorCode int errorCode) {
return createRendererException(cause, format, /* isRecoverable= */ false, errorCode);
}
/**
@ -351,9 +367,15 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
* @param cause The cause of the exception.
* @param format The current format used by the renderer. May be null.
* @param isRecoverable If the error is recoverable by disabling and re-enabling the renderer.
* @param errorCode A {@link PlaybackException.ErrorCode} to identify the cause of the playback
* failure.
* @return The created instance.
*/
protected final ExoPlaybackException createRendererException(
Throwable cause, @Nullable Format format, boolean isRecoverable) {
Throwable cause,
@Nullable Format format,
boolean isRecoverable,
@PlaybackException.ErrorCode int errorCode) {
@C.FormatSupport int formatSupport = C.FORMAT_HANDLED;
if (format != null && !throwRendererExceptionIsExecuting) {
// Prevent recursive re-entry from subclass supportsFormat implementations.
@ -367,7 +389,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
}
}
return ExoPlaybackException.createForRenderer(
cause, getName(), getIndex(), format, formatSupport, isRecoverable);
cause, getName(), getIndex(), format, formatSupport, isRecoverable, errorCode);
}
/**

View File

@ -32,6 +32,7 @@ import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.RendererCapabilities;
@ -258,7 +259,8 @@ public abstract class DecoderAudioRenderer<
try {
audioSink.playToEndOfStream();
} catch (AudioSink.WriteException e) {
throw createRendererException(e, e.format, e.isRecoverable);
throw createRendererException(
e, e.format, e.isRecoverable, PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED);
}
return;
}
@ -278,7 +280,8 @@ public abstract class DecoderAudioRenderer<
try {
processEndOfStream();
} catch (AudioSink.WriteException e) {
throw createRendererException(e, /* format= */ null);
throw createRendererException(
e, /* format= */ null, PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED);
}
return;
} else {
@ -298,15 +301,19 @@ public abstract class DecoderAudioRenderer<
while (feedInputBuffer()) {}
TraceUtil.endSection();
} catch (DecoderException e) {
// Can happen with dequeueOutputBuffer, dequeueInputBuffer, queueInputBuffer
Log.e(TAG, "Audio codec error", e);
eventDispatcher.audioCodecError(e);
throw createRendererException(e, inputFormat);
throw createRendererException(e, inputFormat, PlaybackException.ERROR_CODE_DECODING_FAILED);
} catch (AudioSink.ConfigurationException e) {
throw createRendererException(e, e.format);
throw createRendererException(
e, e.format, PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED);
} catch (AudioSink.InitializationException e) {
throw createRendererException(e, e.format, e.isRecoverable);
throw createRendererException(
e, e.format, e.isRecoverable, PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED);
} catch (AudioSink.WriteException e) {
throw createRendererException(e, e.format, e.isRecoverable);
throw createRendererException(
e, e.format, e.isRecoverable, PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED);
}
decoderCounters.ensureUpdated();
}
@ -382,7 +389,8 @@ public abstract class DecoderAudioRenderer<
try {
processEndOfStream();
} catch (AudioSink.WriteException e) {
throw createRendererException(e, e.format, e.isRecoverable);
throw createRendererException(
e, e.format, e.isRecoverable, PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED);
}
}
return false;
@ -624,9 +632,11 @@ public abstract class DecoderAudioRenderer<
} catch (DecoderException e) {
Log.e(TAG, "Audio codec error", e);
eventDispatcher.audioCodecError(e);
throw createRendererException(e, inputFormat);
throw createRendererException(
e, inputFormat, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
} catch (OutOfMemoryError e) {
throw createRendererException(e, inputFormat);
throw createRendererException(
e, inputFormat, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
}
}

View File

@ -34,6 +34,7 @@ import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.RendererCapabilities;
@ -477,7 +478,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
try {
audioSink.configure(audioSinkInputFormat, /* specifiedBufferSize= */ 0, channelMap);
} catch (AudioSink.ConfigurationException e) {
throw createRendererException(e, e.format);
throw createRendererException(
e, e.format, PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED);
}
}
@ -636,9 +638,11 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
try {
fullyConsumed = audioSink.handleBuffer(buffer, bufferPresentationTimeUs, sampleCount);
} catch (InitializationException e) {
throw createRendererException(e, e.format, e.isRecoverable);
throw createRendererException(
e, e.format, e.isRecoverable, PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED);
} catch (WriteException e) {
throw createRendererException(e, format, e.isRecoverable);
throw createRendererException(
e, format, e.isRecoverable, PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED);
}
if (fullyConsumed) {
@ -657,7 +661,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
try {
audioSink.playToEndOfStream();
} catch (AudioSink.WriteException e) {
throw createRendererException(e, e.format, e.isRecoverable);
throw createRendererException(
e, e.format, e.isRecoverable, PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED);
}
}

View File

@ -49,6 +49,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.decoder.DecoderCounters;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer.InsufficientCapacityException;
@ -493,7 +494,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
try {
return supportsFormat(mediaCodecSelector, format);
} catch (DecoderQueryException e) {
throw createRendererException(e, format);
throw createRendererException(e, format, PlaybackException.ERROR_CODE_DECODER_QUERY_FAILED);
}
}
@ -592,7 +593,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
try {
maybeInitCodecWithFallback(mediaCrypto, mediaCryptoRequiresSecureDecoder);
} catch (DecoderInitializationException e) {
throw createRendererException(e, inputFormat);
throw createRendererException(
e, inputFormat, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
}
}
@ -848,7 +850,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
releaseCodec();
}
throw createRendererException(
createDecoderException(e, getCodecInfo()), inputFormat, isRecoverable);
createDecoderException(e, getCodecInfo()),
inputFormat,
isRecoverable,
PlaybackException.ERROR_CODE_DECODING_FAILED);
}
throw e;
}
@ -1257,7 +1262,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
return true;
} else {
throw createRendererException(
createDecoderException(e, getCodecInfo()), inputFormat, /* isRecoverable= */ false);
createDecoderException(e, getCodecInfo()),
inputFormat,
/* isRecoverable= */ false,
PlaybackException.ERROR_CODE_DECODING_FAILED);
}
}
@ -1442,7 +1450,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
if (newFormat.sampleMimeType == null) {
// If the new format is invalid, it is either a media bug or it is not intended to be played.
// See also https://github.com/google/ExoPlayer/issues/8283.
throw createRendererException(new IllegalArgumentException(), newFormat);
throw createRendererException(
new IllegalArgumentException(),
newFormat,
PlaybackException.ERROR_CODE_DECODING_FORMAT_UNSUPPORTED);
}
setSourceDrmSession(formatHolder.drmSession);
inputFormat = newFormat;

View File

@ -34,6 +34,7 @@ import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.decoder.Decoder;
import com.google.android.exoplayer2.decoder.DecoderCounters;
@ -211,7 +212,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
} catch (DecoderException e) {
Log.e(TAG, "Video codec error", e);
eventDispatcher.videoCodecError(e);
throw createRendererException(e, inputFormat);
throw createRendererException(e, inputFormat, PlaybackException.ERROR_CODE_DECODING_FAILED);
}
decoderCounters.ensureUpdated();
}
@ -691,9 +692,11 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
} catch (DecoderException e) {
Log.e(TAG, "Video codec error", e);
eventDispatcher.videoCodecError(e);
throw createRendererException(e, inputFormat);
throw createRendererException(
e, inputFormat, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
} catch (OutOfMemoryError e) {
throw createRendererException(e, inputFormat);
throw createRendererException(
e, inputFormat, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
}
}