From 6cad61cbdbd50183747c850a96b0ee111ae18746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Via=C4=8Das=C5=82a=C5=AD=20Chalikin?= Date: Mon, 30 Dec 2024 21:50:58 +0300 Subject: [PATCH] Fix PulseAudio freeze * Fix freeze when close app/content after stopping/restarting pulse service * Fix pa->devicelist memleak * Logging improvements --- audio/drivers/alsa.c | 6 ++--- audio/drivers/pipewire.c | 15 +++++++----- audio/drivers/pulse.c | 49 +++++++++++++++++++++++++++------------- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/audio/drivers/alsa.c b/audio/drivers/alsa.c index 900bbcd5207d..9200d70e1f6f 100644 --- a/audio/drivers/alsa.c +++ b/audio/drivers/alsa.c @@ -254,10 +254,8 @@ void alsa_device_list_free(void *data, void *array_list_data) { struct string_list *s = (struct string_list*)array_list_data; - if (!s) - return; - - string_list_free(s); + if (s) + string_list_free(s); } audio_driver_t audio_alsa = { diff --git a/audio/drivers/pipewire.c b/audio/drivers/pipewire.c index c5eb12ca0fb2..872c24930fe7 100644 --- a/audio/drivers/pipewire.c +++ b/audio/drivers/pipewire.c @@ -132,11 +132,15 @@ static void stream_state_changed_cb(void *data, switch(state) { + case PW_STREAM_STATE_ERROR: + RARCH_ERR("[PipeWire]: Stream error\n"); + pw_thread_loop_signal(audio->pw->thread_loop, false); + break; case PW_STREAM_STATE_UNCONNECTED: + RARCH_WARN("[PipeWire]: Stream unconnected\n"); pw_thread_loop_stop(audio->pw->thread_loop); break; case PW_STREAM_STATE_STREAMING: - case PW_STREAM_STATE_ERROR: case PW_STREAM_STATE_PAUSED: pw_thread_loop_signal(audio->pw->thread_loop, false); break; @@ -192,7 +196,8 @@ static void registry_event_global(void *data, uint32_t id, media = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); if (media && strcmp(media, "Audio/Sink") == 0) { - if ((sink = spa_dict_lookup(props, PW_KEY_NODE_NAME)) != NULL) + sink = spa_dict_lookup(props, PW_KEY_NODE_NAME); + if (sink && pw->devicelist) { attr.i = id; string_list_append(pw->devicelist, sink, attr); @@ -231,11 +236,9 @@ static void *pipewire_init(const char *device, unsigned rate, if (!audio) goto error; - pw = audio->pw = (pipewire_core_t*)calloc(1, sizeof(*audio->pw)); + pw = audio->pw = (pipewire_core_t*)calloc(1, sizeof(*audio->pw)); pw->devicelist = string_list_new(); - if (!pw->devicelist) - goto error; if (!pipewire_core_init(pw, "audio_driver")) goto error; @@ -445,7 +448,7 @@ static void *pipewire_device_list_new(void *data) { pipewire_audio_t *audio = (pipewire_audio_t*)data; - if (audio && audio->pw->devicelist) + if (audio && audio->pw && audio->pw->devicelist) return string_list_clone(audio->pw->devicelist); return NULL; diff --git a/audio/drivers/pulse.c b/audio/drivers/pulse.c index 6cae2af73717..be49d1bcf0a0 100644 --- a/audio/drivers/pulse.c +++ b/audio/drivers/pulse.c @@ -37,6 +37,7 @@ typedef struct bool nonblock; bool success; bool is_paused; + bool is_ready; struct string_list *devicelist; } pa_t; @@ -65,6 +66,9 @@ static void pulse_free(void *data) if (pa->mainloop) pa_threaded_mainloop_free(pa->mainloop); + if (pa->devicelist) + string_list_free(pa->devicelist); + free(pa); } @@ -83,8 +87,15 @@ static void context_state_cb(pa_context *c, void *data) switch (pa_context_get_state(c)) { case PA_CONTEXT_READY: - case PA_CONTEXT_TERMINATED: + pa_threaded_mainloop_signal(pa->mainloop, 0); + break; case PA_CONTEXT_FAILED: + RARCH_ERR("[PulseAudio]: Connection failed\n"); + pa->is_ready = false; + pa_threaded_mainloop_signal(pa->mainloop, 0); + break; + case PA_CONTEXT_TERMINATED: + pa->is_ready = false; pa_threaded_mainloop_signal(pa->mainloop, 0); break; default: @@ -98,9 +109,7 @@ static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void * attr.i = 0; pa_t *pa = (pa_t*)data; - if (!pa->devicelist) - pa->devicelist = string_list_new(); - if (!pa->devicelist) + if (!pa || !pa->devicelist) return; /* If EOL is set to a positive number, @@ -119,8 +128,13 @@ static void stream_state_cb(pa_stream *s, void *data) switch (pa_stream_get_state(s)) { case PA_STREAM_READY: + pa->is_ready = true; + pa_threaded_mainloop_signal(pa->mainloop, 0); + break; + case PA_STREAM_UNCONNECTED: case PA_STREAM_FAILED: case PA_STREAM_TERMINATED: + pa->is_ready = false; pa_threaded_mainloop_signal(pa->mainloop, 0); break; default: @@ -180,6 +194,8 @@ static void *pulse_init(const char *device, unsigned rate, if (!pa) goto error; + pa->devicelist = string_list_new(); + pa->mainloop = pa_threaded_mainloop_new(); if (!pa->mainloop) goto error; @@ -203,7 +219,7 @@ static void *pulse_init(const char *device, unsigned rate, if (pa_context_get_state(pa->context) != PA_CONTEXT_READY) goto unlock_error; - pa_context_get_sink_info_list(pa->context,pa_sinklist_cb,pa); + pa_context_get_sink_info_list(pa->context, pa_sinklist_cb, pa); /* Checking device against sink list would be tricky due to callback, so it is just set. */ if (device) pa_context_set_default_sink(pa->context, device, NULL, NULL); @@ -249,6 +265,7 @@ static void *pulse_init(const char *device, unsigned rate, pa->buffer_size = buffer_attr.tlength; pa_threaded_mainloop_unlock(pa->mainloop); + pa->is_ready = true; return pa; @@ -299,11 +316,12 @@ static bool pulse_stop(void *data) { bool ret; pa_t *pa = (pa_t*)data; + + if (!pa->is_ready) + return false; if (pa->is_paused) return true; - RARCH_LOG("[PulseAudio]: Pausing.\n"); - pa->success = true; /* In case of spurious wakeup. Not critical. */ pa_threaded_mainloop_lock(pa->mainloop); pa_stream_cork(pa->stream, true, stream_success_cb, pa); @@ -318,7 +336,7 @@ static bool pulse_alive(void *data) { pa_t *pa = (pa_t*)data; - if (!pa) + if (!pa || !pa->is_ready) return false; return !pa->is_paused; } @@ -327,11 +345,12 @@ static bool pulse_start(void *data, bool is_shutdown) { bool ret; pa_t *pa = (pa_t*)data; + + if (!pa->is_ready) + return false; if (!pa->is_paused) return true; - RARCH_LOG("[PulseAudio]: Unpausing.\n"); - pa->success = true; /* In case of spurious wakeup. Not critical. */ pa_threaded_mainloop_lock(pa->mainloop); pa_stream_cork(pa->stream, false, stream_success_cb, pa); @@ -377,13 +396,11 @@ static size_t pulse_buffer_size(void *data) static void *pulse_device_list_new(void *data) { pa_t *pa = (pa_t*)data; - if (!pa) - return NULL; - struct string_list *s = pa->devicelist ? string_list_clone(pa->devicelist) : NULL; - if (!s) - return NULL; - return s; + if (pa && pa->devicelist) + return string_list_clone(pa->devicelist); + + return NULL; } static void pulse_device_list_free(void *data, void *array_list_data)