Skip to content

Commit

Permalink
Allow renderer recovery with disabling offload if failed at first write
Browse files Browse the repository at this point in the history
If offload fails at first write with [ERROR_DEAD_OBJECT](https://developer.android.com/reference/android/media/AudioTrack#ERROR_DEAD_OBJECT), then try disabling offload mode and try again. This allows recovery in a state where AudioTrack succeeds init in offload mode but writing is failing.

Issue: androidx#627
PiperOrigin-RevId: 564402181
  • Loading branch information
microkatz committed Sep 21, 2023
1 parent b24a569 commit cc4ada8
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 7 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
* Add `media3.extractor.heif.HeifExtractor`.
* Audio:
* Add support for Opus gapless metadata during offload playback.
* Allow renderer recovery by disabling offload if failed at first write
([#627](https://github.com/androidx/media/issues/627)).
* Video:
* Text:
* Metadata:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,10 @@ public class PlaybackException extends Exception implements Bundleable {
/** Caused by an AudioTrack write operation failure. */
public static final int ERROR_CODE_AUDIO_TRACK_WRITE_FAILED = 5002;

// TODO(b/299907254): Stabilize error code, remove @UnstableApi annotation, and add to IntDef
/** Caused by an AudioTrack write operation failure in offload mode. */
@UnstableApi public static final int ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED = 5003;

// DRM errors (6xxx).

/** Caused by an unspecified error related to DRM protection. */
Expand Down Expand Up @@ -324,6 +328,8 @@ public static String getErrorCodeName(@ErrorCode int errorCode) {
return "ERROR_CODE_AUDIO_TRACK_INIT_FAILED";
case ERROR_CODE_AUDIO_TRACK_WRITE_FAILED:
return "ERROR_CODE_AUDIO_TRACK_WRITE_FAILED";
case ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED:
return "ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED";
case ERROR_CODE_DRM_UNSPECIFIED:
return "ERROR_CODE_DRM_UNSPECIFIED";
case ERROR_CODE_DRM_SCHEME_UNSUPPORTED:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -618,9 +618,19 @@ public boolean handleMessage(Message msg) {
e = e.copyWithMediaPeriodId(readingPeriod.info.id);
}
}
if (e.isRecoverable && pendingRecoverableRendererError == null) {
if (e.isRecoverable
&& (pendingRecoverableRendererError == null
|| e.errorCode == PlaybackException.ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED)) {
// If pendingRecoverableRendererError != null and error was
// ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED then upon retry, renderer will attempt with
// offload disabled.
Log.w(TAG, "Recoverable renderer error", e);
pendingRecoverableRendererError = e;
if (pendingRecoverableRendererError != null) {
pendingRecoverableRendererError.addSuppressed(e);
e = pendingRecoverableRendererError;
} else {
pendingRecoverableRendererError = 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.
handler.sendMessageAtFrontOfQueue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1163,9 +1163,17 @@ private void writeBuffer(ByteBuffer buffer, long avSyncPresentationTimeUs) throw
int error = bytesWrittenOrError;

// Treat a write error on a previously successful offload channel as recoverable
// without disabling offload. Offload will be disabled when a new AudioTrack is created,
// if no longer supported.
boolean isRecoverable = isAudioTrackDeadObject(error) && getWrittenFrames() > 0;
// without disabling offload. Offload will be disabled if offload channel was not successfully
// written to or when a new AudioTrack is created, if no longer supported.
boolean isRecoverable = false;
if (isAudioTrackDeadObject(error)) {
if (getWrittenFrames() > 0) {
isRecoverable = true;
} else if (isOffloadedPlayback(audioTrack)) {
maybeDisableOffload();
isRecoverable = true;
}
}

WriteException e = new WriteException(error, configuration.inputFormat, isRecoverable);
if (listener != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,13 @@ protected boolean processOutputBuffer(
e, inputFormat, e.isRecoverable, PlaybackException.ERROR_CODE_AUDIO_TRACK_INIT_FAILED);
} catch (WriteException e) {
throw createRendererException(
e, format, e.isRecoverable, PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED);
e,
format,
e.isRecoverable,
isBypassEnabled()
&& getConfiguration().offloadModePreferred != AudioSink.OFFLOAD_MODE_DISABLED
? PlaybackException.ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED
: PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED);
}

if (fullyConsumed) {
Expand All @@ -774,7 +780,12 @@ protected void renderToEndOfStream() throws ExoPlaybackException {
audioSink.playToEndOfStream();
} catch (AudioSink.WriteException e) {
throw createRendererException(
e, e.format, e.isRecoverable, PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED);
e,
e.format,
e.isRecoverable,
isBypassEnabled()
? PlaybackException.ERROR_CODE_AUDIO_TRACK_OFFLOAD_WRITE_FAILED
: PlaybackException.ERROR_CODE_AUDIO_TRACK_WRITE_FAILED);
}
}

Expand Down

0 comments on commit cc4ada8

Please sign in to comment.