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 extends SampleSourceTrackRenderer im try { pendingMetadata = metadataParser.parse(sampleHolder.data.array(), sampleHolder.size); } catch (IOException e) { - throw new ExoPlaybackException(e); + throw ExoPlaybackException.createForRenderer(e, getIndex()); } } else if (result == TrackStream.END_OF_STREAM) { inputStreamEnded = true; diff --git a/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java b/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java index 3a37ec108a..484b0dd3dd 100644 --- a/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java +++ b/library/src/main/java/com/google/android/exoplayer/text/TextTrackRenderer.java @@ -188,7 +188,7 @@ public final class TextTrackRenderer extends SampleSourceTrackRenderer implement try { nextSubtitle = parserHelper.getAndClearResult(); } catch (IOException e) { - throw new ExoPlaybackException(e); + throw ExoPlaybackException.createForRenderer(e, getIndex()); } }