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

View File

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

View File

@ -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;
@ -272,6 +289,9 @@ static ssize_t pulse_write(void *data, const void *buf_, size_t size)
if (!pulse_start(pa, false))
return -1;
if (!pa->is_ready)
return 0;
pa_threaded_mainloop_lock(pa->mainloop);
while (size)
{
@ -299,11 +319,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 +339,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 +348,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);
@ -360,6 +382,9 @@ static size_t pulse_write_avail(void *data)
size_t _len;
pa_t *pa = (pa_t*)data;
if (!pa->is_ready)
return 0;
pa_threaded_mainloop_lock(pa->mainloop);
_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)
{
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)