diff --git a/library/common/src/main/java/com/google/android/exoplayer2/ExoPlaybackException.java b/library/common/src/main/java/com/google/android/exoplayer2/ExoPlaybackException.java index 95edfdf6f4..93f66123fc 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/ExoPlaybackException.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/ExoPlaybackException.java @@ -102,13 +102,9 @@ public final class ExoPlaybackException extends Exception { @Nullable public final MediaPeriodId mediaPeriodId; /** - * Whether the error may be recoverable. - * - *

This is only used internally by ExoPlayer to try to recover from some errors and should not - * be used by apps. - * - *

If the {@link #type} is {@link #TYPE_RENDERER}, it may be possible to recover from the error - * by disabling and re-enabling the renderers. + * If {@link #type} is {@link #TYPE_RENDERER}, this field indicates whether the error may be + * recoverable by disabling and re-enabling (but not resetting) the renderers. For other + * {@link Type types} this field will always be {@code false}. */ /* package */ final boolean isRecoverable; @@ -282,6 +278,7 @@ public final class ExoPlaybackException extends Exception { long timestampMs, boolean isRecoverable) { super(message, cause); + Assertions.checkArgument(!isRecoverable || type == TYPE_RENDERER); this.type = type; this.cause = cause; this.rendererName = rendererName; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index 5a2c783a6f..62224cb65a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -145,7 +145,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_PLAYLIST_UPDATE_REQUESTED = 22; private static final int MSG_SET_PAUSE_AT_END_OF_WINDOW = 23; private static final int MSG_SET_OFFLOAD_SCHEDULING_ENABLED = 24; - private static final int MSG_ATTEMPT_ERROR_RECOVERY = 25; + private static final int MSG_ATTEMPT_RENDERER_ERROR_RECOVERY = 25; private static final int ACTIVE_INTERVAL_MS = 10; private static final int IDLE_INTERVAL_MS = 1000; @@ -202,7 +202,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private long rendererPositionUs; private int nextPendingMessageIndexHint; private boolean deliverPendingMessageAtStartPositionRequired; - @Nullable private ExoPlaybackException pendingRecoverableError; + @Nullable private ExoPlaybackException pendingRecoverableRendererError; private long setForegroundModeTimeoutMs; @@ -534,8 +534,8 @@ import java.util.concurrent.atomic.AtomicBoolean; case MSG_SET_OFFLOAD_SCHEDULING_ENABLED: setOffloadSchedulingEnabledInternal(msg.arg1 == 1); break; - case MSG_ATTEMPT_ERROR_RECOVERY: - attemptErrorRecovery((ExoPlaybackException) msg.obj); + case MSG_ATTEMPT_RENDERER_ERROR_RECOVERY: + attemptRendererErrorRecovery((ExoPlaybackException) msg.obj); break; case MSG_RELEASE: releaseInternal(); @@ -554,17 +554,16 @@ import java.util.concurrent.atomic.AtomicBoolean; e = e.copyWithMediaPeriodId(readingPeriod.info.id); } } - if (e.isRecoverable && pendingRecoverableError == null) { - Log.w(TAG, "Recoverable playback error", e); - pendingRecoverableError = e; - HandlerWrapper.Message message = handler.obtainMessage(MSG_ATTEMPT_ERROR_RECOVERY, e); + if (e.isRecoverable && pendingRecoverableRendererError == null) { + Log.w(TAG, "Recoverable renderer error", e); + pendingRecoverableRendererError = e; // Given that the player is now in an unhandled exception state, the error needs to be // recovered or the player stopped before any other message is handled. - message.getTarget().sendMessageAtFrontOfQueue(message); + handler.sendMessageAtFrontOfQueue( + handler.obtainMessage(MSG_ATTEMPT_RENDERER_ERROR_RECOVERY, e)); } else { - if (pendingRecoverableError != null) { - e.addSuppressed(pendingRecoverableError); - pendingRecoverableError = null; + if (pendingRecoverableRendererError != null) { + e.addSuppressed(pendingRecoverableRendererError); } Log.e(TAG, "Playback error", e); stopInternal(/* forceResetRenderers= */ true, /* acknowledgeStop= */ false); @@ -594,11 +593,9 @@ import java.util.concurrent.atomic.AtomicBoolean; // Private methods. - private void attemptErrorRecovery(ExoPlaybackException exceptionToRecoverFrom) + private void attemptRendererErrorRecovery(ExoPlaybackException exceptionToRecoverFrom) throws ExoPlaybackException { - Assertions.checkArgument( - exceptionToRecoverFrom.isRecoverable - && exceptionToRecoverFrom.type == ExoPlaybackException.TYPE_RENDERER); + Assertions.checkArgument(exceptionToRecoverFrom.isRecoverable); try { seekToCurrentPosition(/* sendDiscontinuity= */ true); } catch (Exception e) { @@ -985,7 +982,7 @@ import java.util.concurrent.atomic.AtomicBoolean; } else if (playbackInfo.playbackState == Player.STATE_BUFFERING && shouldTransitionToReadyState(renderersAllowPlayback)) { setState(Player.STATE_READY); - pendingRecoverableError = null; // Any pending error was successfully recovered from. + pendingRecoverableRendererError = null; // Any pending error was successfully recovered from. if (shouldPlayWhenReady()) { startRenderers(); } @@ -1345,6 +1342,7 @@ import java.util.concurrent.atomic.AtomicBoolean; boolean releaseMediaSourceList, boolean resetError) { handler.removeMessages(MSG_DO_SOME_WORK); + pendingRecoverableRendererError = null; isRebuffering = false; mediaClock.stop(); rendererPositionUs = 0; @@ -1413,7 +1411,6 @@ import java.util.concurrent.atomic.AtomicBoolean; if (releaseMediaSourceList) { mediaSourceList.release(); } - pendingRecoverableError = null; } private Pair getPlaceholderFirstMediaPeriodPosition(Timeline timeline) {