Add structure to ExoPlaybackException.

- Add a top level failure type (source/renderer/unexpected),
  and convenience methods for retrieving the underlying cause
  without needing to cast.
- Also add renderer index in the case of renderer failures.
- setIndex/getIndex is a little . . . unclean, but alternatives
  involve either having the top line of the stack trace be a
  non-interesting line, or loads of try/catch blocks in
  ExoPlayerImplInternal.

Issue: #777
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=114763073
This commit is contained in:
olly 2016-02-16 09:52:22 -08:00 committed by Oliver Woodman
parent c48dd4f3e3
commit 0e60335064
7 changed files with 100 additions and 31 deletions

View File

@ -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.
* <p>
* 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}.
* <p>
* 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}.
* <p>
* Call {@link #getRendererException()} to retrieve the underlying cause.
*/
public static final int TYPE_RENDERER = 1;
/**
* The error was an unexpected {@link RuntimeException}.
* <p>
* 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();
}
}

View File

@ -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;
}

View File

@ -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.

View File

@ -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)) {

View File

@ -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

View File

@ -108,7 +108,7 @@ public final class MetadataTrackRenderer<T> 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;

View File

@ -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());
}
}