diff --git a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java index 6e1b9a706b..7704149fdc 100644 --- a/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java +++ b/demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java @@ -32,7 +32,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.Player; @@ -424,7 +423,7 @@ public class PlayerActivity extends AppCompatActivity } @Override - public void onPlayerError(@NonNull ExoPlaybackException e) { + public void onPlayerError(@NonNull PlaybackException e) { if (e.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { player.seekToDefaultPosition(); player.prepare(); diff --git a/docs/listening-to-player-events.md b/docs/listening-to-player-events.md index 58dbb7fe75..de23a32c2b 100644 --- a/docs/listening-to-player-events.md +++ b/docs/listening-to-player-events.md @@ -67,35 +67,36 @@ public void onIsPlayingChanged(boolean isPlaying) { ### Playback errors ### Errors that cause playback to fail can be received by implementing -`onPlayerError(ExoPlaybackException error)` in a registered +`onPlayerError(PlaybackException error)` in a registered `Player.Listener`. When a failure occurs, this method will be called immediately before the playback state transitions to `Player.STATE_IDLE`. Failed or stopped playbacks can be retried by calling `ExoPlayer.retry`. -[`ExoPlaybackException`][] has a `type` field, as well as corresponding getter -methods that return cause exceptions providing more information about the -failure. The example below shows how to detect when a playback has failed due to -a HTTP networking issue. +Note that some [`Player`][] implementations pass instances of subclasses of +`PlaybackException` to provide additional information about the failure. For +example, [`ExoPlayer`][] passes [`ExoPlaybackException`][] which has `type`, +`rendererIndex` and other ExoPlayer-specific fields. + +The following example shows how to detect when a playback has failed due to an +HTTP networking issue: ~~~ @Override -public void onPlayerError(ExoPlaybackException error) { - if (error.type == ExoPlaybackException.TYPE_SOURCE) { - IOException cause = error.getSourceException(); - if (cause instanceof HttpDataSourceException) { - // An HTTP error occurred. - HttpDataSourceException httpError = (HttpDataSourceException) cause; - // This is the request for which the error occurred. - DataSpec requestDataSpec = httpError.dataSpec; - // It's possible to find out more about the error both by casting and by - // querying the cause. - if (httpError instanceof HttpDataSource.InvalidResponseCodeException) { - // Cast to InvalidResponseCodeException and retrieve the response code, - // message and headers. - } else { - // Try calling httpError.getCause() to retrieve the underlying cause, - // although note that it may be null. - } +public void onPlayerError(PlaybackException error) { + Throwable cause = error.getCause(); + if (cause instanceof HttpDataSourceException) { + // An HTTP error occurred. + HttpDataSourceException httpError = (HttpDataSourceException) cause; + // This is the request for which the error occurred. + DataSpec requestDataSpec = httpError.dataSpec; + // It's possible to find out more about the error both by casting and by + // querying the cause. + if (httpError instanceof HttpDataSource.InvalidResponseCodeException) { + // Cast to InvalidResponseCodeException and retrieve the response code, + // message and headers. + } else { + // Try calling httpError.getCause() to retrieve the underlying cause, + // although note that it may be null. } } } @@ -228,6 +229,8 @@ player [`Player.Listener`]: {{ site.exo_sdk }}/Player.Listener.html [Javadoc]: {{ site.exo_sdk }}/Player.Listener.html [`Individual callbacks vs onEvents`]: #individual-callbacks-vs-onevents +[`Player`]: {{ site.exo_sdk }}/Player.html +[`ExoPlayer`]: {{ site.exo_sdk }}/ExoPlayer.html [`ExoPlaybackException`]: {{ site.exo_sdk }}/ExoPlaybackException.html [log output]: event-logger.html [`Parameters`]: {{ site.exo_sdk }}/trackselection/DefaultTrackSelector.Parameters.html diff --git a/docs/live-streaming.md b/docs/live-streaming.md index 419bb0c9ac..77090a0ad5 100644 --- a/docs/live-streaming.md +++ b/docs/live-streaming.md @@ -142,7 +142,7 @@ the demo app exemplifies this approach. ~~~ @Override -public void onPlayerError(ExoPlaybackException e) { +public void onPlayerError(PlaybackException e) { if (e.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW) { // Re-initialize player at the current live window default position. player.seekToDefaultPosition(); diff --git a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java index a52f8088b3..c2cfc4547d 100644 --- a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java +++ b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacPlaybackTest.java @@ -23,8 +23,8 @@ import android.os.Looper; 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.PlaybackException; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.RenderersFactory; @@ -96,7 +96,7 @@ public class FlacPlaybackTest { private final AudioSink audioSink; @Nullable private SimpleExoPlayer player; - @Nullable private ExoPlaybackException playbackException; + @Nullable private PlaybackException playbackException; public TestPlaybackRunnable(Uri uri, Context context, AudioSink audioSink) { this.uri = uri; @@ -129,7 +129,7 @@ public class FlacPlaybackTest { } @Override - public void onPlayerError(ExoPlaybackException error) { + public void onPlayerError(PlaybackException error) { playbackException = error; } diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java index 0f44011a67..dd71015fbb 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/AdTagLoader.java @@ -51,8 +51,8 @@ import com.google.ads.interactivemedia.v3.api.player.ContentProgressProvider; import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer; import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate; import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayerLibraryInfo; +import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.ads.AdPlaybackState; @@ -514,7 +514,7 @@ import java.util.Map; } @Override - public void onPlayerError(ExoPlaybackException error) { + public void onPlayerError(PlaybackException error) { if (imaAdState != IMA_AD_STATE_NONE) { AdMediaInfo adMediaInfo = checkNotNull(imaAdMediaInfo); for (int i = 0; i < adCallbacks.size(); i++) { diff --git a/extensions/leanback/src/main/java/com/google/android/exoplayer2/ext/leanback/LeanbackPlayerAdapter.java b/extensions/leanback/src/main/java/com/google/android/exoplayer2/ext/leanback/LeanbackPlayerAdapter.java index 9ca5ab7f7e..55b37bf729 100644 --- a/extensions/leanback/src/main/java/com/google/android/exoplayer2/ext/leanback/LeanbackPlayerAdapter.java +++ b/extensions/leanback/src/main/java/com/google/android/exoplayer2/ext/leanback/LeanbackPlayerAdapter.java @@ -255,15 +255,16 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab } @Override - public void onPlayerError(ExoPlaybackException exception) { + public void onPlayerError(PlaybackException exception) { Callback callback = getCallback(); if (errorMessageProvider != null) { Pair errorMessage = errorMessageProvider.getErrorMessage(exception); callback.onError(LeanbackPlayerAdapter.this, errorMessage.first, errorMessage.second); } else { - // TODO: Conditionally assign the rendererIndex depending on whether the exception is an - // ExoPlaybackException once onPlayerError takes a PlaybackException. - int rendererIndex = exception.rendererIndex; + int rendererIndex = C.INDEX_UNSET; + if (exception instanceof ExoPlaybackException) { + rendererIndex = ((ExoPlaybackException) exception).rendererIndex; + } callback.onError( LeanbackPlayerAdapter.this, exception.errorCode, diff --git a/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java b/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java index f4d934bcf9..a9e08beefb 100644 --- a/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java +++ b/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java @@ -27,8 +27,8 @@ import androidx.media2.common.SessionPlayer; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ControlDispatcher; import com.google.android.exoplayer2.DefaultControlDispatcher; -import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Timeline; @@ -597,7 +597,7 @@ import java.util.List; } @Override - public void onPlayerError(ExoPlaybackException error) { + public void onPlayerError(PlaybackException error) { updateSessionPlayerState(); } diff --git a/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java b/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java index 7522b8048e..d5d1adf899 100644 --- a/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java +++ b/extensions/opus/src/androidTest/java/com/google/android/exoplayer2/ext/opus/OpusPlaybackTest.java @@ -23,8 +23,8 @@ import android.os.Looper; 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.PlaybackException; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.RenderersFactory; @@ -79,7 +79,7 @@ public class OpusPlaybackTest { private final Uri uri; @Nullable private SimpleExoPlayer player; - @Nullable private ExoPlaybackException playbackException; + @Nullable private PlaybackException playbackException; public TestPlaybackRunnable(Uri uri, Context context) { this.uri = uri; @@ -109,7 +109,7 @@ public class OpusPlaybackTest { } @Override - public void onPlayerError(ExoPlaybackException error) { + public void onPlayerError(PlaybackException error) { playbackException = error; } diff --git a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java index 59471296b4..b9d841ccf8 100644 --- a/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java +++ b/extensions/vp9/src/androidTest/java/com/google/android/exoplayer2/ext/vp9/VpxPlaybackTest.java @@ -24,8 +24,8 @@ import android.os.Looper; 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.PlaybackException; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.RenderersFactory; @@ -107,7 +107,7 @@ public class VpxPlaybackTest { private final Uri uri; @Nullable private SimpleExoPlayer player; - @Nullable private ExoPlaybackException playbackException; + @Nullable private PlaybackException playbackException; public TestPlaybackRunnable(Uri uri, Context context) { this.uri = uri; @@ -144,7 +144,7 @@ public class VpxPlaybackTest { } @Override - public void onPlayerError(ExoPlaybackException error) { + public void onPlayerError(PlaybackException error) { playbackException = error; } diff --git a/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java b/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java index 59f6efe674..9c1d3e117a 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/ForwardingPlayer.java @@ -681,7 +681,7 @@ public class ForwardingPlayer implements Player { } @Override - public void onPlayerError(ExoPlaybackException error) { + public void onPlayerError(PlaybackException error) { eventListener.onPlayerError(error); } diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Player.java b/library/common/src/main/java/com/google/android/exoplayer2/Player.java index f4e861c571..01cd606f13 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/Player.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/Player.java @@ -267,9 +267,12 @@ public interface Player { *

{@link #onEvents(Player, Events)} will also be called to report this event along with * other events that happen in the same {@link Looper} message queue iteration. * + *

Implementations of Player may pass an instance of a subclass of {@link PlaybackException} + * to this method in order to include more information about the error. + * * @param error The error. */ - default void onPlayerError(ExoPlaybackException error) {} + default void onPlayerError(PlaybackException error) {} /** * @deprecated Use {@link #onPositionDiscontinuity(PositionInfo, PositionInfo, int)} instead. @@ -1488,14 +1491,14 @@ public interface Player { /** * Returns the error that caused playback to fail. This is the same error that will have been - * reported via {@link Listener#onPlayerError(ExoPlaybackException)} at the time of failure. It - * can be queried using this method until the player is re-prepared. + * reported via {@link Listener#onPlayerError(PlaybackException)} at the time of failure. It can + * be queried using this method until the player is re-prepared. * *

Note that this method will always return {@code null} if {@link #getPlaybackState()} is not * {@link #STATE_IDLE}. * * @return The error, or {@code null}. - * @see Listener#onPlayerError(ExoPlaybackException) + * @see Listener#onPlayerError(PlaybackException) */ @Nullable PlaybackException getPlayerError(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java b/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java index 57c70cd5ca..bbcf163ec6 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/PlayerMessage.java @@ -245,8 +245,7 @@ public final class PlayerMessage { /** * Sends the message. If the target throws an {@link ExoPlaybackException} then it is propagated - * out of the player as an error using {@link - * Player.Listener#onPlayerError(ExoPlaybackException)}. + * out of the player as an error using {@link Player.Listener#onPlayerError(PlaybackException)}. * * @return This message. * @throws IllegalStateException If this message has already been sent. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java index 3814b8471c..279d8b1874 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java @@ -26,6 +26,7 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaMetadata; +import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player.PlaybackSuppressionReason; @@ -708,15 +709,22 @@ public class AnalyticsCollector } @Override - public final void onPlayerError(ExoPlaybackException error) { - EventTime eventTime = - error.mediaPeriodId != null - ? generateEventTime(new MediaPeriodId(error.mediaPeriodId)) - : generateCurrentPlayerMediaPeriodEventTime(); + public final void onPlayerError(PlaybackException error) { + EventTime eventTime = null; + if (error instanceof ExoPlaybackException) { + ExoPlaybackException exoError = (ExoPlaybackException) error; + if (exoError.mediaPeriodId != null) { + eventTime = generateEventTime(new MediaPeriodId(exoError.mediaPeriodId)); + } + } + if (eventTime == null) { + eventTime = generateCurrentPlayerMediaPeriodEventTime(); + } + EventTime finalEventTime = eventTime; sendEvent( eventTime, AnalyticsListener.EVENT_PLAYER_ERROR, - listener -> listener.onPlayerError(eventTime, error)); + listener -> listener.onPlayerError(finalEventTime, error)); } // Calling deprecated callback. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java index 1d11e33462..817050940f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsListener.java @@ -650,6 +650,9 @@ public interface AnalyticsListener { /** * Called when a fatal player error occurred. * + *

Implementations of {@link Player} may pass an instance of a subclass of {@link + * PlaybackException} to this method in order to include more information about the error. + * * @param eventTime The event time. * @param error The error. */ diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java index 6df52551c3..8f38dacf5d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java @@ -21,6 +21,7 @@ import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; +import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; import java.lang.annotation.Documented; @@ -122,7 +123,7 @@ public interface AudioSink { * wishes to do so. * *

Fatal errors that cannot be recovered will be reported wrapped in a {@link - * ExoPlaybackException} by {@link Player.Listener#onPlayerError(ExoPlaybackException)}. + * ExoPlaybackException} by {@link Player.Listener#onPlayerError(PlaybackException)}. * * @param audioSinkError The error that occurred. Typically an {@link InitializationException}, * a {@link WriteException}, or an {@link UnexpectedDiscontinuityException}. diff --git a/library/rtsp/src/test/java/com/google/android/exoplayer2/source/rtsp/RtspPlaybackTest.java b/library/rtsp/src/test/java/com/google/android/exoplayer2/source/rtsp/RtspPlaybackTest.java index c5f80cf673..389a544a1c 100644 --- a/library/rtsp/src/test/java/com/google/android/exoplayer2/source/rtsp/RtspPlaybackTest.java +++ b/library/rtsp/src/test/java/com/google/android/exoplayer2/source/rtsp/RtspPlaybackTest.java @@ -20,8 +20,8 @@ import static com.google.common.truth.Truth.assertThat; import android.net.Uri; 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.PlaybackException; import com.google.android.exoplayer2.Player.Listener; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.robolectric.RobolectricUtil; @@ -95,7 +95,7 @@ public final class RtspPlaybackTest { player.addListener( new Listener() { @Override - public void onPlayerError(ExoPlaybackException error) { + public void onPlayerError(PlaybackException error) { playbackError.set(error); } }); diff --git a/robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java b/robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java index 9e0e891f8b..2f477ee7db 100644 --- a/robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java +++ b/robolectricutils/src/main/java/com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.java @@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat; import android.os.Looper; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.ExoPlayer; +import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.Timeline; @@ -199,8 +200,9 @@ public class TestPlayerRunHelper { Player.Listener listener = new Player.Listener() { @Override - public void onPlayerError(ExoPlaybackException error) { - receivedError.set(error); + public void onPlayerError(PlaybackException error) { + // ExoPlayer is guaranteed to throw an ExoPlaybackException. + receivedError.set((ExoPlaybackException) error); player.removeListener(this); } }; diff --git a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java index 208007f0d4..b302d571d8 100644 --- a/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java +++ b/testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java @@ -29,6 +29,7 @@ import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.LoadControl; import com.google.android.exoplayer2.MediaItem; +import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Renderer; import com.google.android.exoplayer2.RenderersFactory; @@ -692,7 +693,7 @@ public final class ExoPlayerTestRunner implements Player.Listener, ActionSchedul } @Override - public void onPlayerError(ExoPlaybackException error) { + public void onPlayerError(PlaybackException error) { handleException(error); }