Fix PulseAudio freeze (#17316)

* Fix freeze when close app/content after stopping/restarting
  pulse service
* Fix pa->devicelist memleak
* Logging improvements
This commit is contained in:
Viačasłaŭ 2024-12-31 01:08:45 +03:00 committed by GitHub
parent 6be18bfee9
commit 3a4330238a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 50 additions and 26 deletions

View File

@ -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; struct string_list *s = (struct string_list*)array_list_data;
if (!s) if (s)
return; string_list_free(s);
string_list_free(s);
} }
audio_driver_t audio_alsa = { audio_driver_t audio_alsa = {

View File

@ -132,11 +132,15 @@ static void stream_state_changed_cb(void *data,
switch(state) 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: case PW_STREAM_STATE_UNCONNECTED:
RARCH_WARN("[PipeWire]: Stream unconnected\n");
pw_thread_loop_stop(audio->pw->thread_loop); pw_thread_loop_stop(audio->pw->thread_loop);
break; break;
case PW_STREAM_STATE_STREAMING: case PW_STREAM_STATE_STREAMING:
case PW_STREAM_STATE_ERROR:
case PW_STREAM_STATE_PAUSED: case PW_STREAM_STATE_PAUSED:
pw_thread_loop_signal(audio->pw->thread_loop, false); pw_thread_loop_signal(audio->pw->thread_loop, false);
break; break;
@ -192,7 +196,8 @@ static void registry_event_global(void *data, uint32_t id,
media = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS); media = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS);
if (media && strcmp(media, "Audio/Sink") == 0) 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; attr.i = id;
string_list_append(pw->devicelist, sink, attr); string_list_append(pw->devicelist, sink, attr);
@ -231,11 +236,9 @@ static void *pipewire_init(const char *device, unsigned rate,
if (!audio) if (!audio)
goto error; 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(); pw->devicelist = string_list_new();
if (!pw->devicelist)
goto error;
if (!pipewire_core_init(pw, "audio_driver")) if (!pipewire_core_init(pw, "audio_driver"))
goto error; goto error;
@ -445,7 +448,7 @@ static void *pipewire_device_list_new(void *data)
{ {
pipewire_audio_t *audio = (pipewire_audio_t*)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 string_list_clone(audio->pw->devicelist);
return NULL; return NULL;

View File

@ -37,6 +37,7 @@ typedef struct
bool nonblock; bool nonblock;
bool success; bool success;
bool is_paused; bool is_paused;
bool is_ready;
struct string_list *devicelist; struct string_list *devicelist;
} pa_t; } pa_t;
@ -65,6 +66,9 @@ static void pulse_free(void *data)
if (pa->mainloop) if (pa->mainloop)
pa_threaded_mainloop_free(pa->mainloop); pa_threaded_mainloop_free(pa->mainloop);
if (pa->devicelist)
string_list_free(pa->devicelist);
free(pa); free(pa);
} }
@ -83,8 +87,15 @@ static void context_state_cb(pa_context *c, void *data)
switch (pa_context_get_state(c)) switch (pa_context_get_state(c))
{ {
case PA_CONTEXT_READY: case PA_CONTEXT_READY:
case PA_CONTEXT_TERMINATED: pa_threaded_mainloop_signal(pa->mainloop, 0);
break;
case PA_CONTEXT_FAILED: 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); pa_threaded_mainloop_signal(pa->mainloop, 0);
break; break;
default: default:
@ -98,9 +109,7 @@ static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *
attr.i = 0; attr.i = 0;
pa_t *pa = (pa_t*)data; pa_t *pa = (pa_t*)data;
if (!pa->devicelist) if (!pa || !pa->devicelist)
pa->devicelist = string_list_new();
if (!pa->devicelist)
return; return;
/* If EOL is set to a positive number, /* 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)) switch (pa_stream_get_state(s))
{ {
case PA_STREAM_READY: 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_FAILED:
case PA_STREAM_TERMINATED: case PA_STREAM_TERMINATED:
pa->is_ready = false;
pa_threaded_mainloop_signal(pa->mainloop, 0); pa_threaded_mainloop_signal(pa->mainloop, 0);
break; break;
default: default:
@ -180,6 +194,8 @@ static void *pulse_init(const char *device, unsigned rate,
if (!pa) if (!pa)
goto error; goto error;
pa->devicelist = string_list_new();
pa->mainloop = pa_threaded_mainloop_new(); pa->mainloop = pa_threaded_mainloop_new();
if (!pa->mainloop) if (!pa->mainloop)
goto error; 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) if (pa_context_get_state(pa->context) != PA_CONTEXT_READY)
goto unlock_error; 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. */ /* Checking device against sink list would be tricky due to callback, so it is just set. */
if (device) if (device)
pa_context_set_default_sink(pa->context, device, NULL, NULL); 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->buffer_size = buffer_attr.tlength;
pa_threaded_mainloop_unlock(pa->mainloop); pa_threaded_mainloop_unlock(pa->mainloop);
pa->is_ready = true;
return pa; return pa;
@ -272,6 +289,9 @@ static ssize_t pulse_write(void *data, const void *buf_, size_t size)
if (!pulse_start(pa, false)) if (!pulse_start(pa, false))
return -1; return -1;
if (!pa->is_ready)
return 0;
pa_threaded_mainloop_lock(pa->mainloop); pa_threaded_mainloop_lock(pa->mainloop);
while (size) while (size)
{ {
@ -299,11 +319,12 @@ static bool pulse_stop(void *data)
{ {
bool ret; bool ret;
pa_t *pa = (pa_t*)data; pa_t *pa = (pa_t*)data;
if (!pa->is_ready)
return false;
if (pa->is_paused) if (pa->is_paused)
return true; return true;
RARCH_LOG("[PulseAudio]: Pausing.\n");
pa->success = true; /* In case of spurious wakeup. Not critical. */ pa->success = true; /* In case of spurious wakeup. Not critical. */
pa_threaded_mainloop_lock(pa->mainloop); pa_threaded_mainloop_lock(pa->mainloop);
pa_stream_cork(pa->stream, true, stream_success_cb, pa); pa_stream_cork(pa->stream, true, stream_success_cb, pa);
@ -318,7 +339,7 @@ static bool pulse_alive(void *data)
{ {
pa_t *pa = (pa_t*)data; pa_t *pa = (pa_t*)data;
if (!pa) if (!pa || !pa->is_ready)
return false; return false;
return !pa->is_paused; return !pa->is_paused;
} }
@ -327,11 +348,12 @@ static bool pulse_start(void *data, bool is_shutdown)
{ {
bool ret; bool ret;
pa_t *pa = (pa_t*)data; pa_t *pa = (pa_t*)data;
if (!pa->is_ready)
return false;
if (!pa->is_paused) if (!pa->is_paused)
return true; return true;
RARCH_LOG("[PulseAudio]: Unpausing.\n");
pa->success = true; /* In case of spurious wakeup. Not critical. */ pa->success = true; /* In case of spurious wakeup. Not critical. */
pa_threaded_mainloop_lock(pa->mainloop); pa_threaded_mainloop_lock(pa->mainloop);
pa_stream_cork(pa->stream, false, stream_success_cb, pa); pa_stream_cork(pa->stream, false, stream_success_cb, pa);
@ -360,6 +382,9 @@ static size_t pulse_write_avail(void *data)
size_t _len; size_t _len;
pa_t *pa = (pa_t*)data; pa_t *pa = (pa_t*)data;
if (!pa->is_ready)
return 0;
pa_threaded_mainloop_lock(pa->mainloop); pa_threaded_mainloop_lock(pa->mainloop);
_len = pa_stream_writable_size(pa->stream); _len = pa_stream_writable_size(pa->stream);
@ -377,13 +402,11 @@ static size_t pulse_buffer_size(void *data)
static void *pulse_device_list_new(void *data) static void *pulse_device_list_new(void *data)
{ {
pa_t *pa = (pa_t*)data; pa_t *pa = (pa_t*)data;
if (!pa)
return NULL;
struct string_list *s = pa->devicelist ? string_list_clone(pa->devicelist) : NULL; if (pa && pa->devicelist)
if (!s) return string_list_clone(pa->devicelist);
return NULL;
return s; return NULL;
} }
static void pulse_device_list_free(void *data, void *array_list_data) static void pulse_device_list_free(void *data, void *array_list_data)