Skip to content

Commit

Permalink
Fix PulseAudio freeze
Browse files Browse the repository at this point in the history
* Fix freeze when close app/content after stopping/restarting
  pulse service
* Fix pa->devicelist memleak
* Logging improvements
  • Loading branch information
viachaslavic committed Dec 30, 2024
1 parent 1e65626 commit 6cad61c
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 26 deletions.
6 changes: 2 additions & 4 deletions audio/drivers/alsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
15 changes: 9 additions & 6 deletions audio/drivers/pipewire.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
49 changes: 33 additions & 16 deletions audio/drivers/pulse.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef struct
bool nonblock;
bool success;
bool is_paused;
bool is_ready;
struct string_list *devicelist;
} pa_t;

Expand Down Expand Up @@ -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);
}

Expand All @@ -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:
Expand All @@ -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,
Expand All @@ -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:
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 6cad61c

Please sign in to comment.