Work around hang in AAudioStream_write() during extended shared object loading while running in a debugger. Observed on a OnePlus 8T (KB2005) running Oxygen OS 11.0.10.10.KB05AA. The observed behavior is that any nonzero timeout value would hang until the device was paused and resumed. And a zero timeout value would always return 0 frames written even when audio fragments could be heard. Making a manual timeout system unworkable. None of the straightforward systems imply that there's a detectable problem before the call to AAudioStream_write(). And the callback set within AAudioStreamBuilder_setErrorCallback() does not get called as we enter the hang state. I've found that AAudioStream_getTimestamp() will report an error state from another thread. So this change codifies that behavior a bit until a better fix or more root cause can be found.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
diff --git a/src/audio/aaudio/SDL_aaudio.c b/src/audio/aaudio/SDL_aaudio.c
index b9cad6b..91bd114 100644
--- a/src/audio/aaudio/SDL_aaudio.c
+++ b/src/audio/aaudio/SDL_aaudio.c
@@ -62,6 +62,12 @@ static int aaudio_LoadFunctions(AAUDIO_Data *data)
return 0;
}
+void aaudio_errorCallback( AAudioStream *stream, void *userData, aaudio_result_t error );
+void aaudio_errorCallback( AAudioStream *stream, void *userData, aaudio_result_t error )
+{
+ LOGI( "SDL aaudio_errorCallback: %d - %s", error, ctx.AAudio_convertResultToText( error ) );
+}
+
#define LIB_AAUDIO_SO "libaaudio.so"
static int
@@ -109,6 +115,8 @@ aaudio_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
ctx.AAudioStreamBuilder_setFormat(ctx.builder, format);
}
+ ctx.AAudioStreamBuilder_setErrorCallback( ctx.builder, aaudio_errorCallback, private );
+
LOGI("AAudio Try to open %u hz %u bit chan %u %s samples %u",
this->spec.freq, SDL_AUDIO_BITSIZE(this->spec.format),
this->spec.channels, (this->spec.format & 0x1000) ? "BE" : "LE", this->spec.samples);
@@ -412,6 +420,33 @@ void aaudio_ResumeDevices(void)
}
}
+/*
+ We can sometimes get into a state where AAudioStream_write() will just block forever until we pause and unpause.
+ None of the standard state queries indicate any problem in my testing. And the error callback doesn't actually get called.
+ But, AAudioStream_getTimestamp() does return AAUDIO_ERROR_INVALID_STATE
+*/
+SDL_bool aaudio_DetectBrokenPlayState( void )
+{
+ if ( !audioDevice || !audioDevice->hidden ) {
+ return SDL_FALSE;
+ }
+
+ struct SDL_PrivateAudioData *private = audioDevice->hidden;
+
+ int64_t framePosition, timeNanoseconds;
+ aaudio_result_t res = ctx.AAudioStream_getTimestamp( private->stream, CLOCK_MONOTONIC, &framePosition, &timeNanoseconds );
+ if ( res == AAUDIO_ERROR_INVALID_STATE ) {
+ aaudio_stream_state_t currentState = ctx.AAudioStream_getState( private->stream );
+ /* AAudioStream_getTimestamp() will also return AAUDIO_ERROR_INVALID_STATE while the stream is still initially starting. But we only care if it silently went invalid while playing. */
+ if ( currentState == AAUDIO_STREAM_STATE_STARTED ) {
+ LOGI( "SDL aaudio_DetectBrokenPlayState: detected invalid audio device state: AAudioStream_getTimestamp result=%d, framePosition=%lld, timeNanoseconds=%lld, getState=%d", (int)res, (long long)framePosition, (long long)timeNanoseconds, (int)currentState );
+ return SDL_TRUE;
+ }
+ }
+
+ return SDL_FALSE;
+}
+
#endif /* SDL_AUDIO_DRIVER_AAUDIO */
/* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/audio/aaudio/SDL_aaudio.h b/src/audio/aaudio/SDL_aaudio.h
index 34c2f21..2b16fb1 100644
--- a/src/audio/aaudio/SDL_aaudio.h
+++ b/src/audio/aaudio/SDL_aaudio.h
@@ -44,6 +44,7 @@ struct SDL_PrivateAudioData
void aaudio_ResumeDevices(void);
void aaudio_PauseDevices(void);
+SDL_bool aaudio_DetectBrokenPlayState(void);
#endif /* _SDL_aaudio_h */
diff --git a/src/audio/aaudio/SDL_aaudiofuncs.h b/src/audio/aaudio/SDL_aaudiofuncs.h
index a563d18..d482d00 100644
--- a/src/audio/aaudio/SDL_aaudiofuncs.h
+++ b/src/audio/aaudio/SDL_aaudiofuncs.h
@@ -22,7 +22,7 @@
#define SDL_PROC_UNUSED(ret,func,params)
SDL_PROC(const char *, AAudio_convertResultToText, (aaudio_result_t returnCode))
-SDL_PROC_UNUSED(const char *, AAudio_convertStreamStateToText, (aaudio_stream_state_t state))
+SDL_PROC(const char *, AAudio_convertStreamStateToText, (aaudio_stream_state_t state))
SDL_PROC(aaudio_result_t, AAudio_createStreamBuilder, (AAudioStreamBuilder** builder))
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setDeviceId, (AAudioStreamBuilder* builder, int32_t deviceId))
SDL_PROC(void, AAudioStreamBuilder_setSampleRate, (AAudioStreamBuilder* builder, int32_t sampleRate))
@@ -41,7 +41,7 @@ SDL_PROC_UNUSED(void, AAudioStreamBuilder_setSessionId, (AAudioStreamBuilder* bu
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setPrivacySensitive, (AAudioStreamBuilder* builder, bool privacySensitive)) /* API 30 */
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setDataCallback, (AAudioStreamBuilder* builder, AAudioStream_dataCallback callback, void *userData))
SDL_PROC_UNUSED(void, AAudioStreamBuilder_setFramesPerDataCallback, (AAudioStreamBuilder* builder, int32_t numFrames))
-SDL_PROC_UNUSED(void, AAudioStreamBuilder_setErrorCallback, (AAudioStreamBuilder* builder, AAudioStream_errorCallback callback, void *userData))
+SDL_PROC(void, AAudioStreamBuilder_setErrorCallback, (AAudioStreamBuilder* builder, AAudioStream_errorCallback callback, void *userData))
SDL_PROC(aaudio_result_t , AAudioStreamBuilder_openStream, (AAudioStreamBuilder* builder, AAudioStream** stream))
SDL_PROC(aaudio_result_t , AAudioStreamBuilder_delete, (AAudioStreamBuilder* builder))
SDL_PROC_UNUSED(aaudio_result_t , AAudioStream_release, (AAudioStream* stream)) /* API 30 */
@@ -50,7 +50,7 @@ SDL_PROC(aaudio_result_t , AAudioStream_requestStart, (AAudioStream* stream))
SDL_PROC(aaudio_result_t , AAudioStream_requestPause, (AAudioStream* stream))
SDL_PROC_UNUSED(aaudio_result_t , AAudioStream_requestFlush, (AAudioStream* stream))
SDL_PROC(aaudio_result_t , AAudioStream_requestStop, (AAudioStream* stream))
-SDL_PROC_UNUSED(aaudio_stream_state_t, AAudioStream_getState, (AAudioStream* stream))
+SDL_PROC(aaudio_stream_state_t, AAudioStream_getState, (AAudioStream* stream))
SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_waitForStateChange, (AAudioStream* stream, aaudio_stream_state_t inputState, aaudio_stream_state_t *nextState, int64_t timeoutNanoseconds))
SDL_PROC(aaudio_result_t, AAudioStream_read, (AAudioStream* stream, void *buffer, int32_t numFrames, int64_t timeoutNanoseconds))
SDL_PROC(aaudio_result_t, AAudioStream_write, (AAudioStream* stream, const void *buffer, int32_t numFrames, int64_t timeoutNanoseconds))
@@ -71,7 +71,7 @@ SDL_PROC_UNUSED(aaudio_direction_t, AAudioStream_getDirection, (AAudioStream* st
SDL_PROC_UNUSED(int64_t, AAudioStream_getFramesWritten, (AAudioStream* stream))
SDL_PROC_UNUSED(int64_t, AAudioStream_getFramesRead, (AAudioStream* stream))
SDL_PROC_UNUSED(aaudio_session_id_t, AAudioStream_getSessionId, (AAudioStream* stream)) /* API 28 */
-SDL_PROC_UNUSED(aaudio_result_t, AAudioStream_getTimestamp, (AAudioStream* stream, clockid_t clockid, int64_t *framePosition, int64_t *timeNanoseconds))
+SDL_PROC(aaudio_result_t, AAudioStream_getTimestamp, (AAudioStream* stream, clockid_t clockid, int64_t *framePosition, int64_t *timeNanoseconds))
SDL_PROC_UNUSED(aaudio_usage_t, AAudioStream_getUsage, (AAudioStream* stream)) /* API 28 */
SDL_PROC_UNUSED(aaudio_content_type_t, AAudioStream_getContentType, (AAudioStream* stream)) /* API 28 */
SDL_PROC_UNUSED(aaudio_input_preset_t, AAudioStream_getInputPreset, (AAudioStream* stream)) /* API 28 */
diff --git a/src/video/android/SDL_androidevents.c b/src/video/android/SDL_androidevents.c
index 4124cac..0a04b75 100644
--- a/src/video/android/SDL_androidevents.c
+++ b/src/video/android/SDL_androidevents.c
@@ -51,9 +51,11 @@ static void openslES_PauseDevices(void) {}
#if !SDL_AUDIO_DISABLED && SDL_AUDIO_DRIVER_AAUDIO
extern void aaudio_ResumeDevices(void);
extern void aaudio_PauseDevices(void);
+SDL_bool aaudio_DetectBrokenPlayState( void );
#else
static void aaudio_ResumeDevices(void) {}
static void aaudio_PauseDevices(void) {}
+static SDL_bool aaudio_DetectBrokenPlayState( void ) { return SDL_FALSE; }
#endif
@@ -168,6 +170,11 @@ Android_PumpEvents_Blocking(_THIS)
}
}
}
+
+ if ( aaudio_DetectBrokenPlayState() ) {
+ aaudio_PauseDevices();
+ aaudio_ResumeDevices();
+ }
}
void
@@ -246,6 +253,11 @@ Android_PumpEvents_NonBlocking(_THIS)
}
}
}
+
+ if ( aaudio_DetectBrokenPlayState() ) {
+ aaudio_PauseDevices();
+ aaudio_ResumeDevices();
+ }
}
#endif /* SDL_VIDEO_DRIVER_ANDROID */