Pause and resume Android audio without taking device locks

The AAudio driver implemented pause/resume by dangerously locking the audio devices. If there was an audio hotplug event or a background thread tried to interact with the audio system, this could cause deadlocks.
This commit is contained in:
Sam Lantinga 2024-07-29 08:17:10 -07:00
parent 9d35f178a6
commit 5318e30ee5
4 changed files with 29 additions and 34 deletions

View File

@ -43,7 +43,6 @@ struct SDL_PrivateAudioData
size_t processed_bytes;
SDL_Semaphore *semaphore;
SDL_AtomicInt error_callback_triggered;
SDL_bool resume; // Resume device if it was paused automatically
};
// Debug
@ -165,7 +164,18 @@ static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize)
static int AAUDIO_WaitDevice(SDL_AudioDevice *device)
{
SDL_WaitSemaphore(device->hidden->semaphore);
while (!SDL_AtomicGet(&device->shutdown)) {
// this semaphore won't fire when the app is in the background (AAUDIO_PauseDevices was called).
const int rc = SDL_WaitSemaphoreTimeout(device->hidden->semaphore, 100);
if (rc == -1) { // uh, what?
return -1;
} else if (rc == 0) {
return 0; // semaphore was signaled, let's go!
} else {
SDL_assert(rc == SDL_MUTEX_TIMEDOUT);
}
// Still waiting on the semaphore (or the system), check other things then wait again.
}
return 0;
}
@ -439,9 +449,6 @@ static SDL_bool PauseOneDevice(SDL_AudioDevice *device, void *userdata)
LOGI("SDL Failed AAudioStream_requestPause %d", res);
SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
}
SDL_LockMutex(device->lock);
hidden->resume = SDL_TRUE;
}
}
return SDL_FALSE; // keep enumerating.
@ -460,11 +467,6 @@ static SDL_bool ResumeOneDevice(SDL_AudioDevice *device, void *userdata)
{
struct SDL_PrivateAudioData *hidden = device->hidden;
if (hidden) {
if (hidden->resume) {
hidden->resume = SDL_FALSE;
SDL_UnlockMutex(device->lock);
}
if (hidden->stream) {
aaudio_result_t res = ctx.AAudioStream_requestStart(hidden->stream);
if (res != AAUDIO_OK) {

View File

@ -23,16 +23,7 @@
#ifndef SDL_aaudio_h_
#define SDL_aaudio_h_
#ifdef SDL_AUDIO_DRIVER_AAUDIO
void AAUDIO_ResumeDevices(void);
void AAUDIO_PauseDevices(void);
#else
#define AAUDIO_ResumeDevices()
#define AAUDIO_PauseDevices()
#endif
extern void AAUDIO_ResumeDevices(void);
extern void AAUDIO_PauseDevices(void);
#endif // SDL_aaudio_h_

View File

@ -652,8 +652,19 @@ static int OPENSLES_WaitDevice(SDL_AudioDevice *device)
LOGV("OPENSLES_WaitDevice()");
// Wait for an audio chunk to finish
return SDL_WaitSemaphore(audiodata->playsem);
while (!SDL_AtomicGet(&device->shutdown)) {
// this semaphore won't fire when the app is in the background (OPENSLES_PauseDevices was called).
const int rc = SDL_WaitSemaphoreTimeout(audiodata->playsem, 100);
if (rc == -1) { // uh, what?
return -1;
} else if (rc == 0) {
return 0; // semaphore was signaled, let's go!
} else {
SDL_assert(rc == SDL_MUTEX_TIMEDOUT);
}
// Still waiting on the semaphore (or the system), check other things then wait again.
}
return 0;
}
static int OPENSLES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)

View File

@ -23,16 +23,7 @@
#ifndef SDL_openslesaudio_h_
#define SDL_openslesaudio_h_
#ifdef SDL_AUDIO_DRIVER_OPENSLES
void OPENSLES_ResumeDevices(void);
void OPENSLES_PauseDevices(void);
#else
static void OPENSLES_ResumeDevices(void) {}
static void OPENSLES_PauseDevices(void) {}
#endif
extern void OPENSLES_ResumeDevices(void);
extern void OPENSLES_PauseDevices(void);
#endif // SDL_openslesaudio_h_