Browse Source

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.
Sam Lantinga 8 months ago
parent
commit
5318e30ee5

+ 12 - 10
src/audio/aaudio/SDL_aaudio.c

@@ -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) {

+ 2 - 11
src/audio/aaudio/SDL_aaudio.h

@@ -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_

+ 13 - 2
src/audio/openslES/SDL_openslES.c

@@ -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)

+ 2 - 11
src/audio/openslES/SDL_openslES.h

@@ -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_