Skip to content

Commit 3a43302

Browse files
authored
Fix PulseAudio freeze (#17316)
* Fix freeze when close app/content after stopping/restarting pulse service * Fix pa->devicelist memleak * Logging improvements
1 parent 6be18bf commit 3a43302

3 files changed

Lines changed: 50 additions & 26 deletions

File tree

audio/drivers/alsa.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,10 +254,8 @@ void alsa_device_list_free(void *data, void *array_list_data)
254254
{
255255
struct string_list *s = (struct string_list*)array_list_data;
256256

257-
if (!s)
258-
return;
259-
260-
string_list_free(s);
257+
if (s)
258+
string_list_free(s);
261259
}
262260

263261
audio_driver_t audio_alsa = {

audio/drivers/pipewire.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,15 @@ static void stream_state_changed_cb(void *data,
132132

133133
switch(state)
134134
{
135+
case PW_STREAM_STATE_ERROR:
136+
RARCH_ERR("[PipeWire]: Stream error\n");
137+
pw_thread_loop_signal(audio->pw->thread_loop, false);
138+
break;
135139
case PW_STREAM_STATE_UNCONNECTED:
140+
RARCH_WARN("[PipeWire]: Stream unconnected\n");
136141
pw_thread_loop_stop(audio->pw->thread_loop);
137142
break;
138143
case PW_STREAM_STATE_STREAMING:
139-
case PW_STREAM_STATE_ERROR:
140144
case PW_STREAM_STATE_PAUSED:
141145
pw_thread_loop_signal(audio->pw->thread_loop, false);
142146
break;
@@ -192,7 +196,8 @@ static void registry_event_global(void *data, uint32_t id,
192196
media = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS);
193197
if (media && strcmp(media, "Audio/Sink") == 0)
194198
{
195-
if ((sink = spa_dict_lookup(props, PW_KEY_NODE_NAME)) != NULL)
199+
sink = spa_dict_lookup(props, PW_KEY_NODE_NAME);
200+
if (sink && pw->devicelist)
196201
{
197202
attr.i = id;
198203
string_list_append(pw->devicelist, sink, attr);
@@ -231,11 +236,9 @@ static void *pipewire_init(const char *device, unsigned rate,
231236

232237
if (!audio)
233238
goto error;
234-
pw = audio->pw = (pipewire_core_t*)calloc(1, sizeof(*audio->pw));
235239

240+
pw = audio->pw = (pipewire_core_t*)calloc(1, sizeof(*audio->pw));
236241
pw->devicelist = string_list_new();
237-
if (!pw->devicelist)
238-
goto error;
239242

240243
if (!pipewire_core_init(pw, "audio_driver"))
241244
goto error;
@@ -445,7 +448,7 @@ static void *pipewire_device_list_new(void *data)
445448
{
446449
pipewire_audio_t *audio = (pipewire_audio_t*)data;
447450

448-
if (audio && audio->pw->devicelist)
451+
if (audio && audio->pw && audio->pw->devicelist)
449452
return string_list_clone(audio->pw->devicelist);
450453

451454
return NULL;

audio/drivers/pulse.c

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ typedef struct
3737
bool nonblock;
3838
bool success;
3939
bool is_paused;
40+
bool is_ready;
4041
struct string_list *devicelist;
4142
} pa_t;
4243

@@ -65,6 +66,9 @@ static void pulse_free(void *data)
6566
if (pa->mainloop)
6667
pa_threaded_mainloop_free(pa->mainloop);
6768

69+
if (pa->devicelist)
70+
string_list_free(pa->devicelist);
71+
6872
free(pa);
6973
}
7074

@@ -83,8 +87,15 @@ static void context_state_cb(pa_context *c, void *data)
8387
switch (pa_context_get_state(c))
8488
{
8589
case PA_CONTEXT_READY:
86-
case PA_CONTEXT_TERMINATED:
90+
pa_threaded_mainloop_signal(pa->mainloop, 0);
91+
break;
8792
case PA_CONTEXT_FAILED:
93+
RARCH_ERR("[PulseAudio]: Connection failed\n");
94+
pa->is_ready = false;
95+
pa_threaded_mainloop_signal(pa->mainloop, 0);
96+
break;
97+
case PA_CONTEXT_TERMINATED:
98+
pa->is_ready = false;
8899
pa_threaded_mainloop_signal(pa->mainloop, 0);
89100
break;
90101
default:
@@ -98,9 +109,7 @@ static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *
98109
attr.i = 0;
99110
pa_t *pa = (pa_t*)data;
100111

101-
if (!pa->devicelist)
102-
pa->devicelist = string_list_new();
103-
if (!pa->devicelist)
112+
if (!pa || !pa->devicelist)
104113
return;
105114

106115
/* If EOL is set to a positive number,
@@ -119,8 +128,13 @@ static void stream_state_cb(pa_stream *s, void *data)
119128
switch (pa_stream_get_state(s))
120129
{
121130
case PA_STREAM_READY:
131+
pa->is_ready = true;
132+
pa_threaded_mainloop_signal(pa->mainloop, 0);
133+
break;
134+
case PA_STREAM_UNCONNECTED:
122135
case PA_STREAM_FAILED:
123136
case PA_STREAM_TERMINATED:
137+
pa->is_ready = false;
124138
pa_threaded_mainloop_signal(pa->mainloop, 0);
125139
break;
126140
default:
@@ -180,6 +194,8 @@ static void *pulse_init(const char *device, unsigned rate,
180194
if (!pa)
181195
goto error;
182196

197+
pa->devicelist = string_list_new();
198+
183199
pa->mainloop = pa_threaded_mainloop_new();
184200
if (!pa->mainloop)
185201
goto error;
@@ -203,7 +219,7 @@ static void *pulse_init(const char *device, unsigned rate,
203219
if (pa_context_get_state(pa->context) != PA_CONTEXT_READY)
204220
goto unlock_error;
205221

206-
pa_context_get_sink_info_list(pa->context,pa_sinklist_cb,pa);
222+
pa_context_get_sink_info_list(pa->context, pa_sinklist_cb, pa);
207223
/* Checking device against sink list would be tricky due to callback, so it is just set. */
208224
if (device)
209225
pa_context_set_default_sink(pa->context, device, NULL, NULL);
@@ -249,6 +265,7 @@ static void *pulse_init(const char *device, unsigned rate,
249265
pa->buffer_size = buffer_attr.tlength;
250266

251267
pa_threaded_mainloop_unlock(pa->mainloop);
268+
pa->is_ready = true;
252269

253270
return pa;
254271

@@ -272,6 +289,9 @@ static ssize_t pulse_write(void *data, const void *buf_, size_t size)
272289
if (!pulse_start(pa, false))
273290
return -1;
274291

292+
if (!pa->is_ready)
293+
return 0;
294+
275295
pa_threaded_mainloop_lock(pa->mainloop);
276296
while (size)
277297
{
@@ -299,11 +319,12 @@ static bool pulse_stop(void *data)
299319
{
300320
bool ret;
301321
pa_t *pa = (pa_t*)data;
322+
323+
if (!pa->is_ready)
324+
return false;
302325
if (pa->is_paused)
303326
return true;
304327

305-
RARCH_LOG("[PulseAudio]: Pausing.\n");
306-
307328
pa->success = true; /* In case of spurious wakeup. Not critical. */
308329
pa_threaded_mainloop_lock(pa->mainloop);
309330
pa_stream_cork(pa->stream, true, stream_success_cb, pa);
@@ -318,7 +339,7 @@ static bool pulse_alive(void *data)
318339
{
319340
pa_t *pa = (pa_t*)data;
320341

321-
if (!pa)
342+
if (!pa || !pa->is_ready)
322343
return false;
323344
return !pa->is_paused;
324345
}
@@ -327,11 +348,12 @@ static bool pulse_start(void *data, bool is_shutdown)
327348
{
328349
bool ret;
329350
pa_t *pa = (pa_t*)data;
351+
352+
if (!pa->is_ready)
353+
return false;
330354
if (!pa->is_paused)
331355
return true;
332356

333-
RARCH_LOG("[PulseAudio]: Unpausing.\n");
334-
335357
pa->success = true; /* In case of spurious wakeup. Not critical. */
336358
pa_threaded_mainloop_lock(pa->mainloop);
337359
pa_stream_cork(pa->stream, false, stream_success_cb, pa);
@@ -360,6 +382,9 @@ static size_t pulse_write_avail(void *data)
360382
size_t _len;
361383
pa_t *pa = (pa_t*)data;
362384

385+
if (!pa->is_ready)
386+
return 0;
387+
363388
pa_threaded_mainloop_lock(pa->mainloop);
364389
_len = pa_stream_writable_size(pa->stream);
365390

@@ -377,13 +402,11 @@ static size_t pulse_buffer_size(void *data)
377402
static void *pulse_device_list_new(void *data)
378403
{
379404
pa_t *pa = (pa_t*)data;
380-
if (!pa)
381-
return NULL;
382405

383-
struct string_list *s = pa->devicelist ? string_list_clone(pa->devicelist) : NULL;
384-
if (!s)
385-
return NULL;
386-
return s;
406+
if (pa && pa->devicelist)
407+
return string_list_clone(pa->devicelist);
408+
409+
return NULL;
387410
}
388411

389412
static void pulse_device_list_free(void *data, void *array_list_data)

0 commit comments

Comments
 (0)