SDL: audio: pipewire: Block while waiting on stream state info

From 5f9effaa7eed143f1a65f3a335428cd128f8bc31 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Sun, 28 Mar 2021 17:45:41 -0400
Subject: [PATCH] audio: pipewire: Block while waiting on stream state info

Initializing streams, particularly capture streams, can take many milliseconds, which is a bit much for a busy wait.  Use a blocking wait instead.
---
 src/audio/pipewire/SDL_pipewire.c | 38 +++++++++++++++++++++++--------
 src/audio/pipewire/SDL_pipewire.h |  5 ++--
 2 files changed, 31 insertions(+), 12 deletions(-)

diff --git a/src/audio/pipewire/SDL_pipewire.c b/src/audio/pipewire/SDL_pipewire.c
index 81e7946d0..811b0b210 100644
--- a/src/audio/pipewire/SDL_pipewire.c
+++ b/src/audio/pipewire/SDL_pipewire.c
@@ -983,8 +983,23 @@ input_callback(void *data)
     PIPEWIRE_pw_stream_queue_buffer(stream, pw_buf);
 }
 
-static const struct pw_stream_events stream_output_events = { PW_VERSION_STREAM_EVENTS, .process = output_callback };
-static const struct pw_stream_events stream_input_events  = { PW_VERSION_STREAM_EVENTS, .process = input_callback };
+static void
+stream_state_changed_callback(void *data, enum pw_stream_state old, enum pw_stream_state state, const char *error)
+{
+    _THIS = data;
+
+    if (state == PW_STREAM_STATE_STREAMING || state == PW_STREAM_STATE_ERROR) {
+        SDL_AtomicSet(&this->hidden->stream_initialized, 1);
+        PIPEWIRE_pw_thread_loop_signal(this->hidden->loop, false);
+    }
+}
+
+static const struct pw_stream_events stream_output_events = { PW_VERSION_STREAM_EVENTS,
+                                                              .state_changed = stream_state_changed_callback,
+                                                              .process       = output_callback };
+static const struct pw_stream_events stream_input_events  = { PW_VERSION_STREAM_EVENTS,
+                                                             .state_changed = stream_state_changed_callback,
+                                                             .process       = input_callback };
 
 static int
 PIPEWIRE_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
@@ -1005,7 +1020,7 @@ PIPEWIRE_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     const struct spa_pod *       params   = NULL;
     struct SDL_PrivateAudioData *priv;
     struct pw_properties *       props;
-    const char *                 app_name, *stream_name, *stream_role;
+    const char *                 app_name, *stream_name, *stream_role, *error;
     const Uint32                 node_id = this->handle == NULL ? PW_ID_ANY : PW_HANDLE_TO_ID(this->handle);
     enum pw_stream_state         state;
     int                          res;
@@ -1120,14 +1135,17 @@ PIPEWIRE_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     }
 
     /* Wait until the stream is either running or failed */
-    do {
-        const char *error;
-        state = PIPEWIRE_pw_stream_get_state(priv->stream, &error);
+    PIPEWIRE_pw_thread_loop_lock(priv->loop);
+    if (!SDL_AtomicGet(&priv->stream_initialized)) {
+        PIPEWIRE_pw_thread_loop_wait(priv->loop);
+    }
+    PIPEWIRE_pw_thread_loop_unlock(priv->loop);
 
-        if (state == PW_STREAM_STATE_ERROR) {
-            return SDL_SetError("Pipewire: Stream error: %s", error);
-        }
-    } while (state != PW_STREAM_STATE_STREAMING);
+    state = PIPEWIRE_pw_stream_get_state(priv->stream, &error);
+
+    if (state == PW_STREAM_STATE_ERROR) {
+        return SDL_SetError("Pipewire: Stream error: %s", error);
+    }
 
     return 0;
 }
diff --git a/src/audio/pipewire/SDL_pipewire.h b/src/audio/pipewire/SDL_pipewire.h
index 567fd04a6..473e83158 100644
--- a/src/audio/pipewire/SDL_pipewire.h
+++ b/src/audio/pipewire/SDL_pipewire.h
@@ -37,8 +37,9 @@ struct SDL_PrivateAudioData
     struct pw_context     *context;
     struct SDL_DataQueue  *buffer;
 
-    size_t buffer_period_size;
-    Sint32 stride; /* Bytes-per-frame */
+    size_t       buffer_period_size;
+    Sint32       stride; /* Bytes-per-frame */
+    SDL_atomic_t stream_initialized;
 };
 
 #endif /* SDL_pipewire_h_ */