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
This commit is contained in:
parent
56cb327f1e
commit
7dfdde9246
@ -246,6 +246,12 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
*/
|
||||
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";
|
||||
|
||||
/**
|
||||
@ -279,6 +285,9 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
private final boolean enableAudioTrackPlaybackParams;
|
||||
private final boolean enableOffload;
|
||||
@MonotonicNonNull private StreamEventCallbackV29 offloadStreamEventCallbackV29;
|
||||
private final PendingExceptionHolder<InitializationException>
|
||||
initializationExceptionPendingExceptionHolder;
|
||||
private final PendingExceptionHolder<WriteException> writeExceptionPendingExceptionHolder;
|
||||
|
||||
@Nullable private Listener listener;
|
||||
/**
|
||||
@ -425,6 +434,10 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
activeAudioProcessors = new AudioProcessor[0];
|
||||
outputBuffers = new ByteBuffer[0];
|
||||
mediaPositionParametersCheckpoints = new ArrayDeque<>();
|
||||
initializationExceptionPendingExceptionHolder =
|
||||
new PendingExceptionHolder<>(AUDIO_TRACK_RETRY_DURATION_MS);
|
||||
writeExceptionPendingExceptionHolder =
|
||||
new PendingExceptionHolder<>(AUDIO_TRACK_RETRY_DURATION_MS);
|
||||
}
|
||||
|
||||
// AudioSink implementation.
|
||||
@ -710,8 +723,17 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
}
|
||||
|
||||
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) {
|
||||
startMediaTimeUs = max(0, presentationTimeUs);
|
||||
@ -929,8 +951,14 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
if (listener != null) {
|
||||
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;
|
||||
|
||||
if (isOffloadedPlayback(audioTrack)) {
|
||||
@ -1182,6 +1210,8 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
writeExceptionPendingExceptionHolder.clear();
|
||||
initializationExceptionPendingExceptionHolder.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1193,6 +1223,9 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
return;
|
||||
}
|
||||
|
||||
writeExceptionPendingExceptionHolder.clear();
|
||||
initializationExceptionPendingExceptionHolder.clear();
|
||||
|
||||
if (!isAudioTrackInitialized()) {
|
||||
return;
|
||||
}
|
||||
@ -2065,4 +2098,37 @@ public final class DefaultAudioSink implements AudioSink {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user