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

View File

@ -61,6 +61,7 @@ public class PlaybackException extends Exception implements Bundleable {
ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED, ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED,
ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED, ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED,
ERROR_CODE_DECODER_INIT_FAILED, ERROR_CODE_DECODER_INIT_FAILED,
ERROR_CODE_DECODER_QUERY_FAILED,
ERROR_CODE_DECODING_FAILED, ERROR_CODE_DECODING_FAILED,
ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES, ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES,
ERROR_CODE_DECODING_FORMAT_UNSUPPORTED, ERROR_CODE_DECODING_FORMAT_UNSUPPORTED,
@ -144,12 +145,14 @@ public class PlaybackException extends Exception implements Bundleable {
/** Caused by a decoder initialization failure. */ /** Caused by a decoder initialization failure. */
public static final int ERROR_CODE_DECODER_INIT_FAILED = 4001; 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. */ /** 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. */ /** 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. */ /** 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). // AudioTrack errors (5xxx).
@ -223,6 +226,8 @@ public class PlaybackException extends Exception implements Bundleable {
return "ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED"; return "ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED";
case ERROR_CODE_DECODER_INIT_FAILED: case ERROR_CODE_DECODER_INIT_FAILED:
return "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: case ERROR_CODE_DECODING_FAILED:
return "ERROR_CODE_DECODING_FAILED"; return "ERROR_CODE_DECODING_FAILED";
case ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES: case ERROR_CODE_DECODING_FORMAT_EXCEEDS_CAPABILITIES:

View File

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

View File

@ -78,7 +78,7 @@ public class PlaybackExceptionTest {
Bundle bundle = exception.toBundle(); Bundle bundle = exception.toBundle();
assertThat(bundle.getString("0")).isEqualTo(PlaybackException.class.getName()); 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.getLong("2")).isEqualTo(2000); // Timestamp.
assertThat(bundle.getString("3")).isEqualTo("message"); assertThat(bundle.getString("3")).isEqualTo("message");
assertThat(bundle.getString("4")).isEqualTo(cause.getClass().getName()); 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 * Creates an {@link ExoPlaybackException} of type {@link ExoPlaybackException#TYPE_RENDERER} for
* this renderer. * this renderer.
* *
* @param cause The cause of the exception. * <p>Equivalent to {@link #createRendererException(Throwable, Format, int)
* @param format The current format used by the renderer. May be null. * createRendererException(cause, format, PlaybackException.ERROR_CODE_UNSPECIFIED)}.
*/ */
protected final ExoPlaybackException createRendererException( protected final ExoPlaybackException createRendererException(
Throwable cause, @Nullable Format format) { 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 cause The cause of the exception.
* @param format The current format used by the renderer. May be null. * @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 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( 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; @C.FormatSupport int formatSupport = C.FORMAT_HANDLED;
if (format != null && !throwRendererExceptionIsExecuting) { if (format != null && !throwRendererExceptionIsExecuting) {
// Prevent recursive re-entry from subclass supportsFormat implementations. // Prevent recursive re-entry from subclass supportsFormat implementations.
@ -367,7 +389,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
} }
} }
return ExoPlaybackException.createForRenderer( 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.ExoPlayer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.PlayerMessage.Target; import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
@ -258,7 +259,8 @@ public abstract class DecoderAudioRenderer<
try { try {
audioSink.playToEndOfStream(); audioSink.playToEndOfStream();
} catch (AudioSink.WriteException e) { } 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; return;
} }
@ -278,7 +280,8 @@ public abstract class DecoderAudioRenderer<
try { try {
processEndOfStream(); processEndOfStream();
} catch (AudioSink.WriteException e) { } catch (AudioSink.WriteException e) {
throw createRendererException(e, /* format= */ null); throw createRendererException(
e, /* format= */ null, PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED);
} }
return; return;
} else { } else {
@ -298,15 +301,19 @@ public abstract class DecoderAudioRenderer<
while (feedInputBuffer()) {} while (feedInputBuffer()) {}
TraceUtil.endSection(); TraceUtil.endSection();
} catch (DecoderException e) { } catch (DecoderException e) {
// Can happen with dequeueOutputBuffer, dequeueInputBuffer, queueInputBuffer
Log.e(TAG, "Audio codec error", e); Log.e(TAG, "Audio codec error", e);
eventDispatcher.audioCodecError(e); eventDispatcher.audioCodecError(e);
throw createRendererException(e, inputFormat); throw createRendererException(e, inputFormat, PlaybackException.ERROR_CODE_DECODING_FAILED);
} catch (AudioSink.ConfigurationException e) { } 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) { } 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) { } 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(); decoderCounters.ensureUpdated();
} }
@ -382,7 +389,8 @@ public abstract class DecoderAudioRenderer<
try { try {
processEndOfStream(); processEndOfStream();
} catch (AudioSink.WriteException e) { } 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; return false;
@ -624,9 +632,11 @@ public abstract class DecoderAudioRenderer<
} catch (DecoderException e) { } catch (DecoderException e) {
Log.e(TAG, "Audio codec error", e); Log.e(TAG, "Audio codec error", e);
eventDispatcher.audioCodecError(e); eventDispatcher.audioCodecError(e);
throw createRendererException(e, inputFormat); throw createRendererException(
e, inputFormat, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
} catch (OutOfMemoryError e) { } 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.ExoPlayer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.PlayerMessage.Target; import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
@ -477,7 +478,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
try { try {
audioSink.configure(audioSinkInputFormat, /* specifiedBufferSize= */ 0, channelMap); audioSink.configure(audioSinkInputFormat, /* specifiedBufferSize= */ 0, channelMap);
} catch (AudioSink.ConfigurationException e) { } 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 { try {
fullyConsumed = audioSink.handleBuffer(buffer, bufferPresentationTimeUs, sampleCount); fullyConsumed = audioSink.handleBuffer(buffer, bufferPresentationTimeUs, sampleCount);
} catch (InitializationException e) { } 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) { } catch (WriteException e) {
throw createRendererException(e, format, e.isRecoverable); throw createRendererException(
e, format, e.isRecoverable, PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED);
} }
if (fullyConsumed) { if (fullyConsumed) {
@ -657,7 +661,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
try { try {
audioSink.playToEndOfStream(); audioSink.playToEndOfStream();
} catch (AudioSink.WriteException e) { } 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.ExoPlaybackException;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; 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.DecoderCounters;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer.InsufficientCapacityException; import com.google.android.exoplayer2.decoder.DecoderInputBuffer.InsufficientCapacityException;
@ -493,7 +494,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
try { try {
return supportsFormat(mediaCodecSelector, format); return supportsFormat(mediaCodecSelector, format);
} catch (DecoderQueryException e) { } 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 { try {
maybeInitCodecWithFallback(mediaCrypto, mediaCryptoRequiresSecureDecoder); maybeInitCodecWithFallback(mediaCrypto, mediaCryptoRequiresSecureDecoder);
} catch (DecoderInitializationException e) { } 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(); releaseCodec();
} }
throw createRendererException( throw createRendererException(
createDecoderException(e, getCodecInfo()), inputFormat, isRecoverable); createDecoderException(e, getCodecInfo()),
inputFormat,
isRecoverable,
PlaybackException.ERROR_CODE_DECODING_FAILED);
} }
throw e; throw e;
} }
@ -1257,7 +1262,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
return true; return true;
} else { } else {
throw createRendererException( 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 (newFormat.sampleMimeType == null) {
// If the new format is invalid, it is either a media bug or it is not intended to be played. // 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. // 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); setSourceDrmSession(formatHolder.drmSession);
inputFormat = newFormat; 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.ExoPlayer;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder; import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.PlayerMessage.Target; import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.decoder.Decoder; import com.google.android.exoplayer2.decoder.Decoder;
import com.google.android.exoplayer2.decoder.DecoderCounters; import com.google.android.exoplayer2.decoder.DecoderCounters;
@ -211,7 +212,7 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
} catch (DecoderException e) { } catch (DecoderException e) {
Log.e(TAG, "Video codec error", e); Log.e(TAG, "Video codec error", e);
eventDispatcher.videoCodecError(e); eventDispatcher.videoCodecError(e);
throw createRendererException(e, inputFormat); throw createRendererException(e, inputFormat, PlaybackException.ERROR_CODE_DECODING_FAILED);
} }
decoderCounters.ensureUpdated(); decoderCounters.ensureUpdated();
} }
@ -691,9 +692,11 @@ public abstract class DecoderVideoRenderer extends BaseRenderer {
} catch (DecoderException e) { } catch (DecoderException e) {
Log.e(TAG, "Video codec error", e); Log.e(TAG, "Video codec error", e);
eventDispatcher.videoCodecError(e); eventDispatcher.videoCodecError(e);
throw createRendererException(e, inputFormat); throw createRendererException(
e, inputFormat, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
} catch (OutOfMemoryError e) { } catch (OutOfMemoryError e) {
throw createRendererException(e, inputFormat); throw createRendererException(
e, inputFormat, PlaybackException.ERROR_CODE_DECODER_INIT_FAILED);
} }
} }