diff --git a/library/src/main/java/com/google/android/exoplayer/ExoPlaybackException.java b/library/src/main/java/com/google/android/exoplayer/ExoPlaybackException.java index 3e391a1fd8..d20aca4518 100644 --- a/library/src/main/java/com/google/android/exoplayer/ExoPlaybackException.java +++ b/library/src/main/java/com/google/android/exoplayer/ExoPlaybackException.java @@ -15,37 +15,85 @@ */ package com.google.android.exoplayer; +import com.google.android.exoplayer.util.Assertions; + +import java.io.IOException; + /** * Thrown when a non-recoverable playback failure occurs. - *
- * Where possible, the cause returned by {@link #getCause()} will indicate the reason for failure. */ public final class ExoPlaybackException extends Exception { /** - * True if the cause (i.e. the {@link Throwable} returned by {@link #getCause()}) was only caught - * by a fail-safe at the top level of the player. False otherwise. + * The error occurred loading data from a {@link SampleSource}. + *
+ * Call {@link #getSourceException()} to retrieve the underlying cause. */ - public final boolean caughtAtTopLevel; + public static final int TYPE_SOURCE = 0; + /** + * The error occurred in a {@link TrackRenderer}. + *
+ * Call {@link #getRendererException()} to retrieve the underlying cause. + */ + public static final int TYPE_RENDERER = 1; + /** + * The error was an unexpected {@link RuntimeException}. + *
+ * Call {@link #getUnexpectedException()} to retrieve the underlying cause.
+ */
+ public static final int TYPE_UNEXPECTED = 2;
- public ExoPlaybackException(String message) {
- super(message);
- caughtAtTopLevel = false;
+ /**
+ * The type of the playback failure. One of {@link #TYPE_SOURCE}, {@link #TYPE_RENDERER} and
+ * {@link #TYPE_UNEXPECTED}.
+ */
+ public final int type;
+
+ /**
+ * If {@link #type} is {@link #TYPE_RENDERER}, this is the index of the renderer.
+ */
+ public final int rendererIndex;
+
+ public static ExoPlaybackException createForRenderer(Exception cause, int rendererIndex) {
+ return new ExoPlaybackException(TYPE_RENDERER, null, cause, rendererIndex);
}
- public ExoPlaybackException(Throwable cause) {
- super(cause);
- caughtAtTopLevel = false;
+ public static ExoPlaybackException createForSource(IOException cause) {
+ return new ExoPlaybackException(TYPE_SOURCE, null, cause, -1);
}
- public ExoPlaybackException(String message, Throwable cause) {
+ /* package */ static ExoPlaybackException createForUnexpected(RuntimeException cause) {
+ return new ExoPlaybackException(TYPE_UNEXPECTED, null, cause, -1);
+ }
+
+ private ExoPlaybackException(int type, String message, Throwable cause, int rendererIndex) {
super(message, cause);
- caughtAtTopLevel = false;
+ this.type = type;
+ this.rendererIndex = rendererIndex;
}
- /* package */ ExoPlaybackException(Throwable cause, boolean caughtAtTopLevel) {
- super(cause);
- this.caughtAtTopLevel = caughtAtTopLevel;
+ /**
+ * Retrieves the underlying error when {@link #type} is {@link #TYPE_SOURCE}.
+ */
+ public IOException getSourceException() {
+ Assertions.checkState(type == TYPE_SOURCE);
+ return (IOException) getCause();
+ }
+
+ /**
+ * Retrieves the underlying error when {@link #type} is {@link #TYPE_RENDERER}.
+ */
+ public Exception getRendererException() {
+ Assertions.checkState(type == TYPE_RENDERER);
+ return (Exception) getCause();
+ }
+
+ /**
+ * Retrieves the underlying error when {@link #type} is {@link #TYPE_UNEXPECTED}.
+ */
+ public RuntimeException getUnexpectedException() {
+ Assertions.checkState(type == TYPE_UNEXPECTED);
+ return (RuntimeException) getCause();
}
}
diff --git a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java
index 9d267de4b1..48233b41a8 100644
--- a/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java
+++ b/library/src/main/java/com/google/android/exoplayer/ExoPlayerImplInternal.java
@@ -42,7 +42,6 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
// TODO[REFACTOR]: Make sure renderer errors that will prevent prepare from being called again are
// always propagated properly.
-// TODO[REFACTOR]: Distinguish source and renderer errors in ExoPlaybackException.
/* package */ final class ExoPlayerImplInternal implements Handler.Callback {
private static final String TAG = "ExoPlayerImplInternal";
@@ -113,6 +112,7 @@ import java.util.concurrent.atomic.AtomicInteger;
MediaClock rendererMediaClock = null;
TrackRenderer rendererMediaClockSource = null;
for (int i = 0; i < renderers.length; i++) {
+ renderers[i].setIndex(i);
MediaClock mediaClock = renderers[i].getMediaClock();
if (mediaClock != null) {
Assertions.checkState(rendererMediaClock == null);
@@ -266,12 +266,13 @@ import java.util.concurrent.atomic.AtomicInteger;
return true;
} catch (IOException e) {
Log.e(TAG, "Source track renderer error.", e);
- eventHandler.obtainMessage(MSG_ERROR, new ExoPlaybackException(e)).sendToTarget();
+ eventHandler.obtainMessage(MSG_ERROR, ExoPlaybackException.createForSource(e)).sendToTarget();
stopInternal();
return true;
} catch (RuntimeException e) {
Log.e(TAG, "Internal runtime error.", e);
- eventHandler.obtainMessage(MSG_ERROR, new ExoPlaybackException(e, true)).sendToTarget();
+ eventHandler.obtainMessage(MSG_ERROR, ExoPlaybackException.createForUnexpected(e))
+ .sendToTarget();
stopInternal();
return true;
}
diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java
index 1e7b1588f4..6b4b622800 100644
--- a/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java
+++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecAudioTrackRenderer.java
@@ -353,7 +353,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
audioTrackHasData = false;
} catch (AudioTrack.InitializationException e) {
notifyAudioTrackInitializationError(e);
- throw new ExoPlaybackException(e);
+ throw ExoPlaybackException.createForRenderer(e, getIndex());
}
if (getState() == TrackRenderer.STATE_STARTED) {
audioTrack.play();
@@ -377,7 +377,7 @@ public class MediaCodecAudioTrackRenderer extends MediaCodecTrackRenderer implem
lastFeedElapsedRealtimeMs = SystemClock.elapsedRealtime();
} catch (AudioTrack.WriteException e) {
notifyAudioTrackWriteError(e);
- throw new ExoPlaybackException(e);
+ throw ExoPlaybackException.createForRenderer(e, getIndex());
}
// If we are out of sync, allow currentPositionUs to jump backwards.
diff --git a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java
index 48e042ad2a..aeaf399b6c 100644
--- a/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java
+++ b/library/src/main/java/com/google/android/exoplayer/MediaCodecTrackRenderer.java
@@ -273,7 +273,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
return decoderInfo != null && decoderInfo.adaptive ? TrackRenderer.ADAPTIVE_SEAMLESS
: TrackRenderer.ADAPTIVE_NOT_SEAMLESS;
} catch (DecoderQueryException e) {
- throw new ExoPlaybackException(e);
+ throw ExoPlaybackException.createForRenderer(e, getIndex());
}
}
@@ -282,7 +282,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
try {
return supportsFormat(mediaCodecSelector, format);
} catch (DecoderQueryException e) {
- throw new ExoPlaybackException(e);
+ throw ExoPlaybackException.createForRenderer(e, getIndex());
}
}
@@ -335,7 +335,8 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
boolean requiresSecureDecoder = false;
if (drmInitData != null) {
if (drmSessionManager == null) {
- throw new ExoPlaybackException("Media requires a DrmSessionManager");
+ throw ExoPlaybackException.createForRenderer(
+ new IllegalStateException("Media requires a DrmSessionManager"), getIndex());
}
if (!openedDrmSession) {
drmSessionManager.open(drmInitData);
@@ -343,7 +344,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
}
int drmSessionState = drmSessionManager.getState();
if (drmSessionState == DrmSessionManager.STATE_ERROR) {
- throw new ExoPlaybackException(drmSessionManager.getError());
+ throw ExoPlaybackException.createForRenderer(drmSessionManager.getError(), getIndex());
} else if (drmSessionState == DrmSessionManager.STATE_OPENED
|| drmSessionState == DrmSessionManager.STATE_OPENED_WITH_KEYS) {
mediaCrypto = drmSessionManager.getMediaCrypto();
@@ -403,7 +404,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
private void notifyAndThrowDecoderInitError(DecoderInitializationException e)
throws ExoPlaybackException {
notifyDecoderInitializationError(e);
- throw new ExoPlaybackException(e);
+ throw ExoPlaybackException.createForRenderer(e, getIndex());
}
protected boolean shouldInitCodec() {
@@ -636,7 +637,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
}
} catch (CryptoException e) {
notifyCryptoError(e);
- throw new ExoPlaybackException(e);
+ throw ExoPlaybackException.createForRenderer(e, getIndex());
}
return false;
}
@@ -678,7 +679,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
codecReconfigurationState = RECONFIGURATION_STATE_NONE;
} catch (CryptoException e) {
notifyCryptoError(e);
- throw new ExoPlaybackException(e);
+ throw ExoPlaybackException.createForRenderer(e, getIndex());
}
return true;
}
@@ -705,7 +706,7 @@ public abstract class MediaCodecTrackRenderer extends SampleSourceTrackRenderer
}
int drmManagerState = drmSessionManager.getState();
if (drmManagerState == DrmSessionManager.STATE_ERROR) {
- throw new ExoPlaybackException(drmSessionManager.getError());
+ throw ExoPlaybackException.createForRenderer(drmSessionManager.getError(), getIndex());
}
if (drmManagerState != DrmSessionManager.STATE_OPENED_WITH_KEYS &&
(sampleEncrypted || !playClearSamplesWithoutKeys)) {
diff --git a/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java
index e3c17098b3..8a204c9570 100644
--- a/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java
+++ b/library/src/main/java/com/google/android/exoplayer/TrackRenderer.java
@@ -88,8 +88,27 @@ public abstract class TrackRenderer implements ExoPlayerComponent {
*/
protected static final int STATE_STARTED = 2;
+ private int index;
private int state;
+ /**
+ * Sets the index of this renderer within the player.
+ *
+ * @param index The renderer index.
+ */
+ /* package */ final void setIndex(int index) {
+ this.index = index;
+ }
+
+ /**
+ * Returns the index of the renderer within the player.
+ *
+ * @return The index of the renderer within the player.
+ */
+ protected final int getIndex() {
+ return index;
+ }
+
/**
* If the renderer advances its own playback position then this method returns a corresponding
* {@link MediaClock}. If provided, the player will use the returned {@link MediaClock} as its
diff --git a/library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java
index 4d0771bb57..60bcf1cef0 100644
--- a/library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java
+++ b/library/src/main/java/com/google/android/exoplayer/metadata/MetadataTrackRenderer.java
@@ -108,7 +108,7 @@ public final class MetadataTrackRenderer