Commit 7dfdde92 by krocard Committed by kim-vde

Retry AudioTrack init and write before throwing

Retry AudioTrack init and write for 100ms before
giving up and aborting playback.

This was tested by throwing every 2 init/write and
making sure playback did not stopped.

#exo-offload

PiperOrigin-RevId: 333536841
parent 56cb327f
...@@ -246,6 +246,12 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -246,6 +246,12 @@ public final class DefaultAudioSink implements AudioSink {
*/ */
private static final int ERROR_NATIVE_DEAD_OBJECT = -32; private static final int ERROR_NATIVE_DEAD_OBJECT = -32;
/**
* The duration for which failed attempts to initialize or write to the audio track may be retried
* before throwing an exception, in milliseconds.
*/
private static final int AUDIO_TRACK_RETRY_DURATION_MS = 100;
private static final String TAG = "AudioTrack"; private static final String TAG = "AudioTrack";
/** /**
...@@ -279,6 +285,9 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -279,6 +285,9 @@ public final class DefaultAudioSink implements AudioSink {
private final boolean enableAudioTrackPlaybackParams; private final boolean enableAudioTrackPlaybackParams;
private final boolean enableOffload; private final boolean enableOffload;
@MonotonicNonNull private StreamEventCallbackV29 offloadStreamEventCallbackV29; @MonotonicNonNull private StreamEventCallbackV29 offloadStreamEventCallbackV29;
private final PendingExceptionHolder<InitializationException>
initializationExceptionPendingExceptionHolder;
private final PendingExceptionHolder<WriteException> writeExceptionPendingExceptionHolder;
@Nullable private Listener listener; @Nullable private Listener listener;
/** /**
...@@ -425,6 +434,10 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -425,6 +434,10 @@ public final class DefaultAudioSink implements AudioSink {
activeAudioProcessors = new AudioProcessor[0]; activeAudioProcessors = new AudioProcessor[0];
outputBuffers = new ByteBuffer[0]; outputBuffers = new ByteBuffer[0];
mediaPositionParametersCheckpoints = new ArrayDeque<>(); mediaPositionParametersCheckpoints = new ArrayDeque<>();
initializationExceptionPendingExceptionHolder =
new PendingExceptionHolder<>(AUDIO_TRACK_RETRY_DURATION_MS);
writeExceptionPendingExceptionHolder =
new PendingExceptionHolder<>(AUDIO_TRACK_RETRY_DURATION_MS);
} }
// AudioSink implementation. // AudioSink implementation.
...@@ -710,8 +723,17 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -710,8 +723,17 @@ public final class DefaultAudioSink implements AudioSink {
} }
if (!isAudioTrackInitialized()) { if (!isAudioTrackInitialized()) {
initializeAudioTrack(); try {
initializeAudioTrack();
} catch (InitializationException e) {
if (e.isRecoverable) {
throw e; // Do not delay the exception if it can be recovered at higher level.
}
initializationExceptionPendingExceptionHolder.throwExceptionIfDeadlineIsReached(e);
return false;
}
} }
initializationExceptionPendingExceptionHolder.clear();
if (startMediaTimeUsNeedsInit) { if (startMediaTimeUsNeedsInit) {
startMediaTimeUs = max(0, presentationTimeUs); startMediaTimeUs = max(0, presentationTimeUs);
...@@ -929,8 +951,14 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -929,8 +951,14 @@ public final class DefaultAudioSink implements AudioSink {
if (listener != null) { if (listener != null) {
listener.onAudioSinkError(e); listener.onAudioSinkError(e);
} }
throw e; if (e.isRecoverable) {
throw e; // Do not delay the exception if it can be recovered at higher level.
}
writeExceptionPendingExceptionHolder.throwExceptionIfDeadlineIsReached(e);
return;
} }
writeExceptionPendingExceptionHolder.clear();
int bytesWritten = bytesWrittenOrError; int bytesWritten = bytesWrittenOrError;
if (isOffloadedPlayback(audioTrack)) { if (isOffloadedPlayback(audioTrack)) {
...@@ -1182,6 +1210,8 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -1182,6 +1210,8 @@ public final class DefaultAudioSink implements AudioSink {
} }
}.start(); }.start();
} }
writeExceptionPendingExceptionHolder.clear();
initializationExceptionPendingExceptionHolder.clear();
} }
@Override @Override
...@@ -1193,6 +1223,9 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -1193,6 +1223,9 @@ public final class DefaultAudioSink implements AudioSink {
return; return;
} }
writeExceptionPendingExceptionHolder.clear();
initializationExceptionPendingExceptionHolder.clear();
if (!isAudioTrackInitialized()) { if (!isAudioTrackInitialized()) {
return; return;
} }
...@@ -2065,4 +2098,37 @@ public final class DefaultAudioSink implements AudioSink { ...@@ -2065,4 +2098,37 @@ public final class DefaultAudioSink implements AudioSink {
return outputMode == OUTPUT_MODE_OFFLOAD; return outputMode == OUTPUT_MODE_OFFLOAD;
} }
} }
private static final class PendingExceptionHolder<T extends Exception> {
private final long throwDelayMs;
@Nullable private T pendingException;
private long throwDeadlineMs;
public PendingExceptionHolder(long throwDelayMs) {
this.throwDelayMs = throwDelayMs;
}
public void throwExceptionIfDeadlineIsReached(T exception) throws T {
long nowMs = SystemClock.elapsedRealtime();
if (pendingException == null) {
pendingException = exception;
throwDeadlineMs = nowMs + throwDelayMs;
}
if (nowMs >= throwDeadlineMs) {
if (pendingException != exception) {
// All retry exception are probably the same, thus only save the last one to save memory.
pendingException.addSuppressed(exception);
}
T pendingException = this.pendingException;
clear();
throw pendingException;
}
}
public void clear() {
pendingException = null;
}
}
} }
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