Commit d97af762 by krocard Committed by kim-vde

Retry after offload playback failure

Do that by adding a recoverable state to
the ExoPlaybackException marking when
it is needed to recreate the renderers.

PiperOrigin-RevId: 333507849
parent cf30ee50
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
([#7866](https://github.com/google/ExoPlayer/issues/7866)). ([#7866](https://github.com/google/ExoPlayer/issues/7866)).
* Text: * Text:
* Add support for `\h` SSA/ASS style override code (non-breaking space). * Add support for `\h` SSA/ASS style override code (non-breaking space).
* Audio:
* Retry playback after some types of `AudioTrack` error.
### 2.12.0 (2020-09-11) ### ### 2.12.0 (2020-09-11) ###
......
...@@ -341,6 +341,19 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { ...@@ -341,6 +341,19 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
*/ */
protected final ExoPlaybackException createRendererException( protected final ExoPlaybackException createRendererException(
Exception cause, @Nullable Format format) { Exception cause, @Nullable Format format) {
return createRendererException(cause, format, /* isRecoverable= */ false);
}
/**
* Creates an {@link ExoPlaybackException} of type {@link ExoPlaybackException#TYPE_RENDERER} for
* this renderer.
*
* @param cause The cause of the exception.
* @param format The current format used by the renderer. May be null.
* @param isRecoverable If the error is recoverable by disabling and re-enabling the renderer.
*/
protected final ExoPlaybackException createRendererException(
Exception cause, @Nullable Format format, boolean isRecoverable) {
@FormatSupport int formatSupport = RendererCapabilities.FORMAT_HANDLED; @FormatSupport int formatSupport = RendererCapabilities.FORMAT_HANDLED;
if (format != null && !throwRendererExceptionIsExecuting) { if (format != null && !throwRendererExceptionIsExecuting) {
// Prevent recursive re-entry from subclass supportsFormat implementations. // Prevent recursive re-entry from subclass supportsFormat implementations.
...@@ -354,7 +367,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities { ...@@ -354,7 +367,7 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
} }
} }
return ExoPlaybackException.createForRenderer( return ExoPlaybackException.createForRenderer(
cause, getName(), getIndex(), format, formatSupport); cause, getName(), getIndex(), format, formatSupport, isRecoverable);
} }
/** /**
......
...@@ -29,9 +29,7 @@ import java.lang.annotation.Retention; ...@@ -29,9 +29,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
/** /** Thrown when a non locally recoverable playback failure occurs. */
* Thrown when a non-recoverable playback failure occurs.
*/
public final class ExoPlaybackException extends Exception { public final class ExoPlaybackException extends Exception {
/** /**
...@@ -138,6 +136,17 @@ public final class ExoPlaybackException extends Exception { ...@@ -138,6 +136,17 @@ public final class ExoPlaybackException extends Exception {
*/ */
@Nullable public final MediaSource.MediaPeriodId mediaPeriodId; @Nullable public final MediaSource.MediaPeriodId mediaPeriodId;
/**
* Whether the error may be recoverable.
*
* <p>This is only used internally by ExoPlayer to try to recover from some errors and should not
* be used by apps.
*
* <p>If the {@link #type} is {@link #TYPE_RENDERER}, it may be possible to recover from the error
* by disabling and re-enabling the renderers.
*/
/* package */ final boolean isRecoverable;
@Nullable private final Throwable cause; @Nullable private final Throwable cause;
/** /**
...@@ -167,6 +176,34 @@ public final class ExoPlaybackException extends Exception { ...@@ -167,6 +176,34 @@ public final class ExoPlaybackException extends Exception {
int rendererIndex, int rendererIndex,
@Nullable Format rendererFormat, @Nullable Format rendererFormat,
@FormatSupport int rendererFormatSupport) { @FormatSupport int rendererFormatSupport) {
return createForRenderer(
cause,
rendererName,
rendererIndex,
rendererFormat,
rendererFormatSupport,
/* isRecoverable= */ false);
}
/**
* Creates an instance of type {@link #TYPE_RENDERER}.
*
* @param cause The cause of the failure.
* @param rendererIndex The index of the renderer in which the failure occurred.
* @param rendererFormat The {@link Format} the renderer was using at the time of the exception,
* or null if the renderer wasn't using a {@link Format}.
* @param rendererFormatSupport The {@link FormatSupport} of the renderer for {@code
* rendererFormat}. Ignored if {@code rendererFormat} is null.
* @param isRecoverable If the failure can be recovered by disabling and re-enabling the renderer.
* @return The created instance.
*/
public static ExoPlaybackException createForRenderer(
Exception cause,
String rendererName,
int rendererIndex,
@Nullable Format rendererFormat,
@FormatSupport int rendererFormatSupport,
boolean isRecoverable) {
return new ExoPlaybackException( return new ExoPlaybackException(
TYPE_RENDERER, TYPE_RENDERER,
cause, cause,
...@@ -175,7 +212,8 @@ public final class ExoPlaybackException extends Exception { ...@@ -175,7 +212,8 @@ public final class ExoPlaybackException extends Exception {
rendererIndex, rendererIndex,
rendererFormat, rendererFormat,
rendererFormat == null ? RendererCapabilities.FORMAT_HANDLED : rendererFormatSupport, rendererFormat == null ? RendererCapabilities.FORMAT_HANDLED : rendererFormatSupport,
TIMEOUT_OPERATION_UNDEFINED); TIMEOUT_OPERATION_UNDEFINED,
isRecoverable);
} }
/** /**
...@@ -225,7 +263,8 @@ public final class ExoPlaybackException extends Exception { ...@@ -225,7 +263,8 @@ public final class ExoPlaybackException extends Exception {
/* rendererIndex= */ C.INDEX_UNSET, /* rendererIndex= */ C.INDEX_UNSET,
/* rendererFormat= */ null, /* rendererFormat= */ null,
/* rendererFormatSupport= */ RendererCapabilities.FORMAT_HANDLED, /* rendererFormatSupport= */ RendererCapabilities.FORMAT_HANDLED,
timeoutOperation); timeoutOperation,
/* isRecoverable= */ false);
} }
private ExoPlaybackException(@Type int type, Throwable cause) { private ExoPlaybackException(@Type int type, Throwable cause) {
...@@ -237,7 +276,8 @@ public final class ExoPlaybackException extends Exception { ...@@ -237,7 +276,8 @@ public final class ExoPlaybackException extends Exception {
/* rendererIndex= */ C.INDEX_UNSET, /* rendererIndex= */ C.INDEX_UNSET,
/* rendererFormat= */ null, /* rendererFormat= */ null,
/* rendererFormatSupport= */ RendererCapabilities.FORMAT_HANDLED, /* rendererFormatSupport= */ RendererCapabilities.FORMAT_HANDLED,
TIMEOUT_OPERATION_UNDEFINED); TIMEOUT_OPERATION_UNDEFINED,
/* isRecoverable= */ false);
} }
private ExoPlaybackException(@Type int type, String message) { private ExoPlaybackException(@Type int type, String message) {
...@@ -249,7 +289,8 @@ public final class ExoPlaybackException extends Exception { ...@@ -249,7 +289,8 @@ public final class ExoPlaybackException extends Exception {
/* rendererIndex= */ C.INDEX_UNSET, /* rendererIndex= */ C.INDEX_UNSET,
/* rendererFormat= */ null, /* rendererFormat= */ null,
/* rendererFormatSupport= */ RendererCapabilities.FORMAT_HANDLED, /* rendererFormatSupport= */ RendererCapabilities.FORMAT_HANDLED,
/* timeoutOperation= */ TIMEOUT_OPERATION_UNDEFINED); /* timeoutOperation= */ TIMEOUT_OPERATION_UNDEFINED,
/* isRecoverable= */ false);
} }
private ExoPlaybackException( private ExoPlaybackException(
...@@ -260,7 +301,8 @@ public final class ExoPlaybackException extends Exception { ...@@ -260,7 +301,8 @@ public final class ExoPlaybackException extends Exception {
int rendererIndex, int rendererIndex,
@Nullable Format rendererFormat, @Nullable Format rendererFormat,
@FormatSupport int rendererFormatSupport, @FormatSupport int rendererFormatSupport,
@TimeoutOperation int timeoutOperation) { @TimeoutOperation int timeoutOperation,
boolean isRecoverable) {
this( this(
deriveMessage( deriveMessage(
type, type,
...@@ -277,7 +319,8 @@ public final class ExoPlaybackException extends Exception { ...@@ -277,7 +319,8 @@ public final class ExoPlaybackException extends Exception {
rendererFormatSupport, rendererFormatSupport,
/* mediaPeriodId= */ null, /* mediaPeriodId= */ null,
timeoutOperation, timeoutOperation,
/* timestampMs= */ SystemClock.elapsedRealtime()); /* timestampMs= */ SystemClock.elapsedRealtime(),
isRecoverable);
} }
private ExoPlaybackException( private ExoPlaybackException(
...@@ -290,7 +333,8 @@ public final class ExoPlaybackException extends Exception { ...@@ -290,7 +333,8 @@ public final class ExoPlaybackException extends Exception {
@FormatSupport int rendererFormatSupport, @FormatSupport int rendererFormatSupport,
@Nullable MediaSource.MediaPeriodId mediaPeriodId, @Nullable MediaSource.MediaPeriodId mediaPeriodId,
@TimeoutOperation int timeoutOperation, @TimeoutOperation int timeoutOperation,
long timestampMs) { long timestampMs,
boolean isRecoverable) {
super(message, cause); super(message, cause);
this.type = type; this.type = type;
this.cause = cause; this.cause = cause;
...@@ -301,6 +345,7 @@ public final class ExoPlaybackException extends Exception { ...@@ -301,6 +345,7 @@ public final class ExoPlaybackException extends Exception {
this.mediaPeriodId = mediaPeriodId; this.mediaPeriodId = mediaPeriodId;
this.timeoutOperation = timeoutOperation; this.timeoutOperation = timeoutOperation;
this.timestampMs = timestampMs; this.timestampMs = timestampMs;
this.isRecoverable = isRecoverable;
} }
/** /**
...@@ -360,7 +405,7 @@ public final class ExoPlaybackException extends Exception { ...@@ -360,7 +405,7 @@ public final class ExoPlaybackException extends Exception {
* @return The copied exception. * @return The copied exception.
*/ */
@CheckResult @CheckResult
/* package= */ ExoPlaybackException copyWithMediaPeriodId( /* package */ ExoPlaybackException copyWithMediaPeriodId(
@Nullable MediaSource.MediaPeriodId mediaPeriodId) { @Nullable MediaSource.MediaPeriodId mediaPeriodId) {
return new ExoPlaybackException( return new ExoPlaybackException(
getMessage(), getMessage(),
...@@ -372,7 +417,8 @@ public final class ExoPlaybackException extends Exception { ...@@ -372,7 +417,8 @@ public final class ExoPlaybackException extends Exception {
rendererFormatSupport, rendererFormatSupport,
mediaPeriodId, mediaPeriodId,
timeoutOperation, timeoutOperation,
timestampMs); timestampMs,
isRecoverable);
} }
@Nullable @Nullable
......
...@@ -142,6 +142,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -142,6 +142,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int MSG_PLAYLIST_UPDATE_REQUESTED = 22; private static final int MSG_PLAYLIST_UPDATE_REQUESTED = 22;
private static final int MSG_SET_PAUSE_AT_END_OF_WINDOW = 23; private static final int MSG_SET_PAUSE_AT_END_OF_WINDOW = 23;
private static final int MSG_SET_OFFLOAD_SCHEDULING_ENABLED = 24; private static final int MSG_SET_OFFLOAD_SCHEDULING_ENABLED = 24;
private static final int MSG_ATTEMPT_ERROR_RECOVERY = 25;
private static final int ACTIVE_INTERVAL_MS = 10; private static final int ACTIVE_INTERVAL_MS = 10;
private static final int IDLE_INTERVAL_MS = 1000; private static final int IDLE_INTERVAL_MS = 1000;
...@@ -196,6 +197,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -196,6 +197,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private long rendererPositionUs; private long rendererPositionUs;
private int nextPendingMessageIndexHint; private int nextPendingMessageIndexHint;
private boolean deliverPendingMessageAtStartPositionRequired; private boolean deliverPendingMessageAtStartPositionRequired;
@Nullable private ExoPlaybackException pendingRecoverableError;
private long releaseTimeoutMs; private long releaseTimeoutMs;
private boolean throwWhenStuckBuffering; private boolean throwWhenStuckBuffering;
...@@ -525,6 +527,9 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -525,6 +527,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
case MSG_SET_OFFLOAD_SCHEDULING_ENABLED: case MSG_SET_OFFLOAD_SCHEDULING_ENABLED:
setOffloadSchedulingEnabledInternal(msg.arg1 == 1); setOffloadSchedulingEnabledInternal(msg.arg1 == 1);
break; break;
case MSG_ATTEMPT_ERROR_RECOVERY:
attemptErrorRecovery((ExoPlaybackException) msg.obj);
break;
case MSG_RELEASE: case MSG_RELEASE:
releaseInternal(); releaseInternal();
// Return immediately to not send playback info updates after release. // Return immediately to not send playback info updates after release.
...@@ -542,9 +547,22 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -542,9 +547,22 @@ import java.util.concurrent.atomic.AtomicBoolean;
e = e.copyWithMediaPeriodId(readingPeriod.info.id); e = e.copyWithMediaPeriodId(readingPeriod.info.id);
} }
} }
Log.e(TAG, "Playback error", e); if (e.isRecoverable && pendingRecoverableError == null) {
stopInternal(/* forceResetRenderers= */ true, /* acknowledgeStop= */ false); Log.w(TAG, "Recoverable playback error", e);
playbackInfo = playbackInfo.copyWithPlaybackError(e); pendingRecoverableError = e;
Message message = handler.obtainMessage(MSG_ATTEMPT_ERROR_RECOVERY, e);
// Given that the player is now in an unhandled exception state, the error needs to be
// recovered or the player stopped before any other message is handled.
message.getTarget().sendMessageAtFrontOfQueue(message);
} else {
if (pendingRecoverableError != null) {
e.addSuppressed(pendingRecoverableError);
pendingRecoverableError = null;
}
Log.e(TAG, "Playback error", e);
stopInternal(/* forceResetRenderers= */ true, /* acknowledgeStop= */ false);
playbackInfo = playbackInfo.copyWithPlaybackError(e);
}
maybeNotifyPlaybackInfoChanged(); maybeNotifyPlaybackInfoChanged();
} catch (IOException e) { } catch (IOException e) {
ExoPlaybackException error = ExoPlaybackException.createForSource(e); ExoPlaybackException error = ExoPlaybackException.createForSource(e);
...@@ -572,6 +590,19 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -572,6 +590,19 @@ import java.util.concurrent.atomic.AtomicBoolean;
// Private methods. // Private methods.
private void attemptErrorRecovery(ExoPlaybackException exceptionToRecoverFrom)
throws ExoPlaybackException {
Assertions.checkArgument(
exceptionToRecoverFrom.isRecoverable
&& exceptionToRecoverFrom.type == ExoPlaybackException.TYPE_RENDERER);
try {
seekToCurrentPosition(/* sendDiscontinuity= */ true);
} catch (Exception e) {
exceptionToRecoverFrom.addSuppressed(e);
throw exceptionToRecoverFrom;
}
}
/** /**
* Blocks the current thread until a condition becomes true. * Blocks the current thread until a condition becomes true.
* *
...@@ -929,6 +960,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -929,6 +960,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
} else if (playbackInfo.playbackState == Player.STATE_BUFFERING } else if (playbackInfo.playbackState == Player.STATE_BUFFERING
&& shouldTransitionToReadyState(renderersAllowPlayback)) { && shouldTransitionToReadyState(renderersAllowPlayback)) {
setState(Player.STATE_READY); setState(Player.STATE_READY);
pendingRecoverableError = null; // Any pending error was successfully recovered from.
if (shouldPlayWhenReady()) { if (shouldPlayWhenReady()) {
startRenderers(); startRenderers();
} }
...@@ -1318,6 +1350,7 @@ import java.util.concurrent.atomic.AtomicBoolean; ...@@ -1318,6 +1350,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
if (releaseMediaSourceList) { if (releaseMediaSourceList) {
mediaSourceList.release(); mediaSourceList.release();
} }
pendingRecoverableError = null;
} }
private Pair<MediaPeriodId, Long> getPlaceholderFirstMediaPeriodPosition(Timeline timeline) { private Pair<MediaPeriodId, Long> getPlaceholderFirstMediaPeriodPosition(Timeline timeline) {
......
...@@ -136,34 +136,41 @@ public interface AudioSink { ...@@ -136,34 +136,41 @@ public interface AudioSink {
} }
/** /** Thrown when a failure occurs initializing the sink. */
* Thrown when a failure occurs initializing the sink.
*/
final class InitializationException extends Exception { final class InitializationException extends Exception {
/** /** The underlying {@link AudioTrack}'s state. */
* The underlying {@link AudioTrack}'s state, if applicable.
*/
public final int audioTrackState; public final int audioTrackState;
/** If the exception can be recovered by recreating the sink. */
public final boolean isRecoverable;
/** /**
* @param audioTrackState The underlying {@link AudioTrack}'s state, if applicable.
* @param sampleRate The requested sample rate in Hz. * @param sampleRate The requested sample rate in Hz.
* @param channelConfig The requested channel configuration. * @param channelConfig The requested channel configuration.
* @param bufferSize The requested buffer size in bytes. * @param bufferSize The requested buffer size in bytes.
* @param audioTrackException Exception thrown during the creation of the {@link AudioTrack}.
*/ */
public InitializationException(int audioTrackState, int sampleRate, int channelConfig, public InitializationException(
int bufferSize) { int audioTrackState,
super("AudioTrack init failed: " + audioTrackState + ", Config(" + sampleRate + ", " int sampleRate,
+ channelConfig + ", " + bufferSize + ")"); int channelConfig,
int bufferSize,
boolean isRecoverable,
@Nullable Exception audioTrackException) {
super(
"AudioTrack init failed "
+ audioTrackState
+ " "
+ ("Config(" + sampleRate + ", " + channelConfig + ", " + bufferSize + ")")
+ (isRecoverable ? " (recoverable)" : ""),
audioTrackException);
this.audioTrackState = audioTrackState; this.audioTrackState = audioTrackState;
this.isRecoverable = isRecoverable;
} }
} }
/** /** Thrown when a failure occurs writing to the sink. */
* Thrown when a failure occurs writing to the sink.
*/
final class WriteException extends Exception { final class WriteException extends Exception {
/** /**
...@@ -173,12 +180,13 @@ public interface AudioSink { ...@@ -173,12 +180,13 @@ public interface AudioSink {
* Otherwise, the meaning of the error code depends on the sink implementation. * Otherwise, the meaning of the error code depends on the sink implementation.
*/ */
public final int errorCode; public final int errorCode;
/** If the exception can be recovered by recreating the sink. */
public final boolean isRecoverable;
/** /** @param errorCode The error value returned from the sink implementation. */
* @param errorCode The error value returned from the sink implementation. public WriteException(int errorCode, boolean isRecoverable) {
*/
public WriteException(int errorCode) {
super("AudioTrack write failed: " + errorCode); super("AudioTrack write failed: " + errorCode);
this.isRecoverable = isRecoverable;
this.errorCode = errorCode; this.errorCode = errorCode;
} }
......
...@@ -257,7 +257,7 @@ public abstract class DecoderAudioRenderer< ...@@ -257,7 +257,7 @@ public abstract class DecoderAudioRenderer<
try { try {
audioSink.playToEndOfStream(); audioSink.playToEndOfStream();
} catch (AudioSink.WriteException e) { } catch (AudioSink.WriteException e) {
throw createRendererException(e, inputFormat); throw createRendererException(e, inputFormat, e.isRecoverable);
} }
return; return;
} }
...@@ -296,11 +296,12 @@ public abstract class DecoderAudioRenderer< ...@@ -296,11 +296,12 @@ public abstract class DecoderAudioRenderer<
while (drainOutputBuffer()) {} while (drainOutputBuffer()) {}
while (feedInputBuffer()) {} while (feedInputBuffer()) {}
TraceUtil.endSection(); TraceUtil.endSection();
} catch (DecoderException } catch (DecoderException | AudioSink.ConfigurationException e) {
| AudioSink.ConfigurationException
| AudioSink.InitializationException
| AudioSink.WriteException e) {
throw createRendererException(e, inputFormat); throw createRendererException(e, inputFormat);
} catch (AudioSink.InitializationException e) {
throw createRendererException(e, inputFormat, e.isRecoverable);
} catch (AudioSink.WriteException e) {
throw createRendererException(e, inputFormat, e.isRecoverable);
} }
decoderCounters.ensureUpdated(); decoderCounters.ensureUpdated();
} }
...@@ -383,7 +384,7 @@ public abstract class DecoderAudioRenderer< ...@@ -383,7 +384,7 @@ public abstract class DecoderAudioRenderer<
try { try {
processEndOfStream(); processEndOfStream();
} catch (AudioSink.WriteException e) { } catch (AudioSink.WriteException e) {
throw createRendererException(e, getOutputFormat(decoder)); throw createRendererException(e, getOutputFormat(decoder), e.isRecoverable);
} }
} }
return false; return false;
......
...@@ -908,7 +908,7 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -908,7 +908,7 @@ public final class DefaultAudioSink implements AudioSink {
if (isRecoverable) { if (isRecoverable) {
maybeDisableOffload(); maybeDisableOffload();
} }
throw new WriteException(bytesWritten); throw new WriteException(bytesWritten, isRecoverable);
} }
if (isOffloadedPlayback(audioTrack)) { if (isOffloadedPlayback(audioTrack)) {
...@@ -1883,9 +1883,14 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -1883,9 +1883,14 @@ public final class DefaultAudioSink implements AudioSink {
AudioTrack audioTrack; AudioTrack audioTrack;
try { try {
audioTrack = createAudioTrack(tunneling, audioAttributes, audioSessionId); audioTrack = createAudioTrack(tunneling, audioAttributes, audioSessionId);
} catch (UnsupportedOperationException e) { } catch (UnsupportedOperationException | IllegalArgumentException e) {
throw new InitializationException( throw new InitializationException(
AudioTrack.STATE_UNINITIALIZED, outputSampleRate, outputChannelConfig, bufferSize); AudioTrack.STATE_UNINITIALIZED,
outputSampleRate,
outputChannelConfig,
bufferSize,
/* isRecoverable= */ outputModeIsOffload(),
e);
} }
int state = audioTrack.getState(); int state = audioTrack.getState();
...@@ -1896,7 +1901,13 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -1896,7 +1901,13 @@ public final class DefaultAudioSink implements AudioSink {
// The track has already failed to initialize, so it wouldn't be that surprising if // The track has already failed to initialize, so it wouldn't be that surprising if
// release were to fail too. Swallow the exception. // release were to fail too. Swallow the exception.
} }
throw new InitializationException(state, outputSampleRate, outputChannelConfig, bufferSize); throw new InitializationException(
state,
outputSampleRate,
outputChannelConfig,
bufferSize,
/* isRecoverable= */ outputModeIsOffload(),
/* audioTrackException= */ null);
} }
return audioTrack; return audioTrack;
} }
......
...@@ -37,6 +37,8 @@ import com.google.android.exoplayer2.PlaybackParameters; ...@@ -37,6 +37,8 @@ import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.PlayerMessage.Target; import com.google.android.exoplayer2.PlayerMessage.Target;
import com.google.android.exoplayer2.RendererCapabilities; import com.google.android.exoplayer2.RendererCapabilities;
import com.google.android.exoplayer2.audio.AudioRendererEventListener.EventDispatcher; import com.google.android.exoplayer2.audio.AudioRendererEventListener.EventDispatcher;
import com.google.android.exoplayer2.audio.AudioSink.InitializationException;
import com.google.android.exoplayer2.audio.AudioSink.WriteException;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.mediacodec.MediaCodecAdapter; import com.google.android.exoplayer2.mediacodec.MediaCodecAdapter;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo; import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
...@@ -616,8 +618,10 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -616,8 +618,10 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
boolean fullyConsumed; boolean fullyConsumed;
try { try {
fullyConsumed = audioSink.handleBuffer(buffer, bufferPresentationTimeUs, sampleCount); fullyConsumed = audioSink.handleBuffer(buffer, bufferPresentationTimeUs, sampleCount);
} catch (AudioSink.InitializationException | AudioSink.WriteException e) { } catch (InitializationException e) {
throw createRendererException(e, format); throw createRendererException(e, format, e.isRecoverable);
} catch (WriteException e) {
throw createRendererException(e, format, e.isRecoverable);
} }
if (fullyConsumed) { if (fullyConsumed) {
...@@ -637,7 +641,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media ...@@ -637,7 +641,8 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
audioSink.playToEndOfStream(); audioSink.playToEndOfStream();
} catch (AudioSink.WriteException e) { } catch (AudioSink.WriteException e) {
@Nullable Format outputFormat = getOutputFormat(); @Nullable Format outputFormat = getOutputFormat();
throw createRendererException(e, outputFormat != null ? outputFormat : getInputFormat()); throw createRendererException(
e, outputFormat != null ? outputFormat : getInputFormat(), e.isRecoverable);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment