Parcourir la source

pipewire: i/o callbacks should avoid higher-level iteration during device open.

Sometimes these callbacks will fire while we're still waiting on state to
settle down in PIPEWIRE_OpenDevice, which means we're holding the device lock,
but then the i/o callback will fire from a background thread and also try to
grab the device lock, but can't, because PIPEWIRE_OpenDevice is holding it and
waiting for this i/o callback to finish...hence, a deadlock.

So now, if the device is still opening, output callbacks will write silence
and input callbacks will just flush the buffer, without calling the main
iterate function, and thus avoid obtaining the lock.
Ryan C. Gordon il y a 5 mois
Parent
commit
32cc92dceb
1 fichiers modifiés avec 25 ajouts et 2 suppressions
  1. 25 2
      src/audio/pipewire/SDL_pipewire.c

+ 25 - 2
src/audio/pipewire/SDL_pipewire.c

@@ -939,7 +939,21 @@ static bool PIPEWIRE_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, in
 
 static void output_callback(void *data)
 {
-    SDL_PlaybackAudioThreadIterate((SDL_AudioDevice *)data);
+    SDL_AudioDevice *device = (SDL_AudioDevice *) data;
+
+    // this callback can fire in a background thread during OpenDevice, while we're still blocking
+    // _with the device lock_ until the stream is ready, causing a deadlock. Write silence in this case.
+    if (device->hidden->stream_init_status != PW_READY_FLAG_ALL_BITS) {
+        int bufsize = 0;
+        Uint8 *buf = PIPEWIRE_GetDeviceBuf(device, &bufsize);
+        if (buf && bufsize) {
+            SDL_memset(buf, device->silence_value, bufsize);
+        }
+        PIPEWIRE_PlayDevice(device, buf, bufsize);
+        return;
+    }
+
+    SDL_PlaybackAudioThreadIterate(device);
 }
 
 static void PIPEWIRE_FlushRecording(SDL_AudioDevice *device)
@@ -980,7 +994,16 @@ static int PIPEWIRE_RecordDevice(SDL_AudioDevice *device, void *buffer, int bufl
 
 static void input_callback(void *data)
 {
-    SDL_RecordingAudioThreadIterate((SDL_AudioDevice *)data);
+    SDL_AudioDevice *device = (SDL_AudioDevice *) data;
+
+    // this callback can fire in a background thread during OpenDevice, while we're still blocking
+    // _with the device lock_ until the stream is ready, causing a deadlock. Drop data in this case.
+    if (device->hidden->stream_init_status != PW_READY_FLAG_ALL_BITS) {
+        PIPEWIRE_FlushRecording(device);
+        return;
+    }
+
+    SDL_RecordingAudioThreadIterate(device);
 }
 
 static void stream_add_buffer_callback(void *data, struct pw_buffer *buffer)