From a0460c3bd7128d5358a4a445f4f38733836e52b4 Mon Sep 17 00:00:00 2001 From: tonihei Date: Tue, 12 Jan 2021 11:34:17 +0000 Subject: [PATCH] Remove ExoPlaybackException.TYPE_TIMEOUT. The ExoPlaybackException types are locations from where the exception is coming from and not the type of exception itself, which should be denoted by different exception classes. To avoid a mixture of error types and class checks, the timeout exceptions should use their own class and be of type RENDERER as this is where the timeout actually happens. PiperOrigin-RevId: 351337699 --- .../exoplayer2/ExoPlaybackException.java | 108 +++++------------- .../android/exoplayer2/ExoPlayerImpl.java | 14 +-- .../exoplayer2/ExoTimeoutException.java | 79 +++++++++++++ .../android/exoplayer2/SimpleExoPlayer.java | 6 +- 4 files changed, 116 insertions(+), 91 deletions(-) create mode 100644 library/core/src/main/java/com/google/android/exoplayer2/ExoTimeoutException.java 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 dd1591bc78..95edfdf6f4 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 @@ -27,19 +27,18 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.concurrent.TimeoutException; /** Thrown when a non locally recoverable playback failure occurs. */ public final class ExoPlaybackException extends Exception { /** * The type of source that produced the error. One of {@link #TYPE_SOURCE}, {@link #TYPE_RENDERER} - * {@link #TYPE_UNEXPECTED}, {@link #TYPE_REMOTE} or {@link #TYPE_TIMEOUT}. Note that new types - * may be added in the future and error handling should handle unknown type values. + * {@link #TYPE_UNEXPECTED} or {@link #TYPE_REMOTE}. Note that new types may be added in the + * future and error handling should handle unknown type values. */ @Documented @Retention(RetentionPolicy.SOURCE) - @IntDef({TYPE_SOURCE, TYPE_RENDERER, TYPE_UNEXPECTED, TYPE_REMOTE, TYPE_TIMEOUT}) + @IntDef({TYPE_SOURCE, TYPE_RENDERER, TYPE_UNEXPECTED, TYPE_REMOTE}) public @interface Type {} /** * The error occurred loading data from a {@code MediaSource}. @@ -68,43 +67,19 @@ public final class ExoPlaybackException extends Exception { */ public static final int TYPE_REMOTE = 3; - /** The error was a {@link TimeoutException}. */ - public static final int TYPE_TIMEOUT = 4; - /** The {@link Type} of the playback failure. */ @Type public final int type; /** - * The operation which produced the timeout error. One of {@link #TIMEOUT_OPERATION_RELEASE}, - * {@link #TIMEOUT_OPERATION_SET_FOREGROUND_MODE}, {@link #TIMEOUT_OPERATION_DETACH_SURFACE} or - * {@link #TIMEOUT_OPERATION_UNDEFINED}. Note that new operations may be added in the future and - * error handling should handle unknown operation values. + * If {@link #type} is {@link #TYPE_RENDERER}, this is the name of the renderer, or null if + * unknown. */ - @Documented - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - TIMEOUT_OPERATION_UNDEFINED, - TIMEOUT_OPERATION_RELEASE, - TIMEOUT_OPERATION_SET_FOREGROUND_MODE, - TIMEOUT_OPERATION_DETACH_SURFACE - }) - public @interface TimeoutOperation {} - - /** The operation where this error occurred is not defined. */ - public static final int TIMEOUT_OPERATION_UNDEFINED = 0; - // TODO(b/172315872) Change back @code to @link when the Player is in common. - /** The error occurred in {@code Player#release}. */ - public static final int TIMEOUT_OPERATION_RELEASE = 1; - /** The error occurred in {@code ExoPlayer#setForegroundMode}. */ - // TODO(b/172315872) Set foregroundMode is an ExoPlayer method, NOT a player one. - public static final int TIMEOUT_OPERATION_SET_FOREGROUND_MODE = 2; - /** The error occurred while detaching a surface from the player. */ - public static final int TIMEOUT_OPERATION_DETACH_SURFACE = 3; - - /** If {@link #type} is {@link #TYPE_RENDERER}, this is the name of the renderer. */ @Nullable public final String rendererName; - /** If {@link #type} is {@link #TYPE_RENDERER}, this is the index of the renderer. */ + /** + * If {@link #type} is {@link #TYPE_RENDERER}, this is the index of the renderer, or {@link + * C#INDEX_UNSET} if unknown. + */ public final int rendererIndex; /** @@ -120,11 +95,6 @@ public final class ExoPlaybackException extends Exception { */ @FormatSupport public final int rendererFormatSupport; - /** - * If {@link #type} is {@link #TYPE_TIMEOUT}, this is the operation where the timeout happened. - */ - @TimeoutOperation public final int timeoutOperation; - /** The value of {@link SystemClock#elapsedRealtime()} when this exception was created. */ public final long timestampMs; @@ -154,6 +124,24 @@ public final class ExoPlaybackException extends Exception { return new ExoPlaybackException(TYPE_SOURCE, cause); } + /** + * Creates an instance of type {@link #TYPE_RENDERER} for an unknown renderer. + * + * @param cause The cause of the failure. + * @return The created instance. + */ + public static ExoPlaybackException createForRenderer(Exception cause) { + return new ExoPlaybackException( + TYPE_RENDERER, + cause, + /* customMessage= */ null, + /* rendererName */ null, + /* rendererIndex= */ C.INDEX_UNSET, + /* rendererFormat= */ null, + /* rendererFormatSupport= */ C.FORMAT_HANDLED, + /* isRecoverable= */ false); + } + /** * Creates an instance of type {@link #TYPE_RENDERER}. * @@ -207,7 +195,6 @@ public final class ExoPlaybackException extends Exception { rendererIndex, rendererFormat, rendererFormat == null ? C.FORMAT_HANDLED : rendererFormatSupport, - TIMEOUT_OPERATION_UNDEFINED, isRecoverable); } @@ -231,27 +218,6 @@ public final class ExoPlaybackException extends Exception { return new ExoPlaybackException(TYPE_REMOTE, message); } - /** - * Creates an instance of type {@link #TYPE_TIMEOUT}. - * - * @param cause The cause of the failure. - * @param timeoutOperation The operation that caused this timeout. - * @return The created instance. - */ - public static ExoPlaybackException createForTimeout( - TimeoutException cause, @TimeoutOperation int timeoutOperation) { - return new ExoPlaybackException( - TYPE_TIMEOUT, - cause, - /* customMessage= */ null, - /* rendererName= */ null, - /* rendererIndex= */ C.INDEX_UNSET, - /* rendererFormat= */ null, - /* rendererFormatSupport= */ C.FORMAT_HANDLED, - timeoutOperation, - /* isRecoverable= */ false); - } - private ExoPlaybackException(@Type int type, Throwable cause) { this( type, @@ -261,7 +227,6 @@ public final class ExoPlaybackException extends Exception { /* rendererIndex= */ C.INDEX_UNSET, /* rendererFormat= */ null, /* rendererFormatSupport= */ C.FORMAT_HANDLED, - TIMEOUT_OPERATION_UNDEFINED, /* isRecoverable= */ false); } @@ -274,7 +239,6 @@ public final class ExoPlaybackException extends Exception { /* rendererIndex= */ C.INDEX_UNSET, /* rendererFormat= */ null, /* rendererFormatSupport= */ C.FORMAT_HANDLED, - /* timeoutOperation= */ TIMEOUT_OPERATION_UNDEFINED, /* isRecoverable= */ false); } @@ -286,7 +250,6 @@ public final class ExoPlaybackException extends Exception { int rendererIndex, @Nullable Format rendererFormat, @FormatSupport int rendererFormatSupport, - @TimeoutOperation int timeoutOperation, boolean isRecoverable) { this( deriveMessage( @@ -303,7 +266,6 @@ public final class ExoPlaybackException extends Exception { rendererFormat, rendererFormatSupport, /* mediaPeriodId= */ null, - timeoutOperation, /* timestampMs= */ SystemClock.elapsedRealtime(), isRecoverable); } @@ -317,7 +279,6 @@ public final class ExoPlaybackException extends Exception { @Nullable Format rendererFormat, @FormatSupport int rendererFormatSupport, @Nullable MediaPeriodId mediaPeriodId, - @TimeoutOperation int timeoutOperation, long timestampMs, boolean isRecoverable) { super(message, cause); @@ -328,7 +289,6 @@ public final class ExoPlaybackException extends Exception { this.rendererFormat = rendererFormat; this.rendererFormatSupport = rendererFormatSupport; this.mediaPeriodId = mediaPeriodId; - this.timeoutOperation = timeoutOperation; this.timestampMs = timestampMs; this.isRecoverable = isRecoverable; } @@ -363,16 +323,6 @@ public final class ExoPlaybackException extends Exception { return (RuntimeException) Assertions.checkNotNull(cause); } - /** - * Retrieves the underlying error when {@link #type} is {@link #TYPE_TIMEOUT}. - * - * @throws IllegalStateException If {@link #type} is not {@link #TYPE_TIMEOUT}. - */ - public TimeoutException getTimeoutException() { - Assertions.checkState(type == TYPE_TIMEOUT); - return (TimeoutException) Assertions.checkNotNull(cause); - } - /** * Returns a copy of this exception with the provided {@link MediaPeriodId}. * @@ -390,7 +340,6 @@ public final class ExoPlaybackException extends Exception { rendererFormat, rendererFormatSupport, mediaPeriodId, - timeoutOperation, timestampMs, isRecoverable); } @@ -422,9 +371,6 @@ public final class ExoPlaybackException extends Exception { case TYPE_REMOTE: message = "Remote error"; break; - case TYPE_TIMEOUT: - message = "Timeout error"; - break; case TYPE_UNEXPECTED: default: message = "Unexpected runtime error"; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index b2dd249acf..d8e6affb2b 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -48,7 +48,6 @@ import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.TimeoutException; /** * An {@link ExoPlayer} implementation. Instances can be obtained from {@link ExoPlayer.Builder}. @@ -674,11 +673,12 @@ import java.util.concurrent.TimeoutException; if (this.foregroundMode != foregroundMode) { this.foregroundMode = foregroundMode; if (!internalPlayer.setForegroundMode(foregroundMode)) { + // One of the renderers timed out releasing its resources. stop( /* reset= */ false, - ExoPlaybackException.createForTimeout( - new TimeoutException("Setting foreground mode timed out."), - ExoPlaybackException.TIMEOUT_OPERATION_SET_FOREGROUND_MODE)); + ExoPlaybackException.createForRenderer( + new ExoTimeoutException( + ExoTimeoutException.TIMEOUT_OPERATION_SET_FOREGROUND_MODE))); } } } @@ -728,13 +728,13 @@ import java.util.concurrent.TimeoutException; + ExoPlayerLibraryInfo.VERSION_SLASHY + "] [" + Util.DEVICE_DEBUG_INFO + "] [" + ExoPlayerLibraryInfo.registeredModules() + "]"); if (!internalPlayer.release()) { + // One of the renderers timed out releasing its resources. listeners.sendEvent( Player.EVENT_PLAYER_ERROR, listener -> listener.onPlayerError( - ExoPlaybackException.createForTimeout( - new TimeoutException("Player release timed out."), - ExoPlaybackException.TIMEOUT_OPERATION_RELEASE))); + ExoPlaybackException.createForRenderer( + new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_RELEASE)))); } listeners.release(); playbackInfoUpdateHandler.removeCallbacksAndMessages(null); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoTimeoutException.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoTimeoutException.java new file mode 100644 index 0000000000..a6af2d193c --- /dev/null +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoTimeoutException.java @@ -0,0 +1,79 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.android.exoplayer2; + +import androidx.annotation.IntDef; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** A timeout of an operation on the ExoPlayer playback thread. */ +public final class ExoTimeoutException extends Exception { + + /** + * The operation which produced the timeout error. One of {@link #TIMEOUT_OPERATION_RELEASE}, + * {@link #TIMEOUT_OPERATION_SET_FOREGROUND_MODE}, {@link #TIMEOUT_OPERATION_DETACH_SURFACE} or + * {@link #TIMEOUT_OPERATION_UNDEFINED}. Note that new operations may be added in the future and + * error handling should handle unknown operation values. + */ + @Documented + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + TIMEOUT_OPERATION_UNDEFINED, + TIMEOUT_OPERATION_RELEASE, + TIMEOUT_OPERATION_SET_FOREGROUND_MODE, + TIMEOUT_OPERATION_DETACH_SURFACE + }) + public @interface TimeoutOperation {} + + /** The operation where this error occurred is not defined. */ + public static final int TIMEOUT_OPERATION_UNDEFINED = 0; + // TODO(b/172315872) Change back @code to @link when the Player is in common. + /** The error occurred in {@code Player#release}. */ + public static final int TIMEOUT_OPERATION_RELEASE = 1; + /** The error occurred in {@code ExoPlayer#setForegroundMode}. */ + // TODO(b/172315872) Set foregroundMode is an ExoPlayer method, NOT a player one. + public static final int TIMEOUT_OPERATION_SET_FOREGROUND_MODE = 2; + /** The error occurred while detaching a surface from the player. */ + public static final int TIMEOUT_OPERATION_DETACH_SURFACE = 3; + + /** The operation on the ExoPlayer playback thread that timed out. */ + @TimeoutOperation public final int timeoutOperation; + + /** + * Creates the timeout exception. + * + * @param timeoutOperation The {@link TimeoutOperation operation} that produced the timeout. + */ + public ExoTimeoutException(@TimeoutOperation int timeoutOperation) { + super(getErrorMessage(timeoutOperation)); + this.timeoutOperation = timeoutOperation; + } + + private static String getErrorMessage(@TimeoutOperation int timeoutOperation) { + switch (timeoutOperation) { + case TIMEOUT_OPERATION_RELEASE: + return "Player release timed out."; + case TIMEOUT_OPERATION_SET_FOREGROUND_MODE: + return "Setting foreground mode timed out."; + case TIMEOUT_OPERATION_DETACH_SURFACE: + return "Detaching surface timed out."; + case TIMEOUT_OPERATION_UNDEFINED: + default: + return "Undefined timeout."; + } + } +} diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index 08032cc5a2..53babe2709 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -2061,11 +2061,11 @@ public class SimpleExoPlayer extends BasePlayer } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (TimeoutException e) { + // One of the renderers timed out releasing its resources. player.stop( /* reset= */ false, - ExoPlaybackException.createForTimeout( - new TimeoutException("Detaching surface timed out."), - ExoPlaybackException.TIMEOUT_OPERATION_DETACH_SURFACE)); + ExoPlaybackException.createForRenderer( + new ExoTimeoutException(ExoTimeoutException.TIMEOUT_OPERATION_DETACH_SURFACE))); } // If we created the previous surface, we are responsible for releasing it. if (this.ownsSurface) {