summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Blumenkrantz <zmike@samsung.com>2020-03-17 12:06:13 -0400
committerMarcel Hollerbach <mail@marcel-hollerbach.de>2020-03-25 22:13:12 +0100
commit9324bcc3617caf76ef50a6eefb7e51b6d987a0b4 (patch)
treebc7630771727dacf4948b789fc717cd55182aca1
parent1af46ef30251c47480f93b2774ab87146021cede (diff)
ecore/audio: rewrite pulseaudio output to not use global variables
this breaks down immediately when calling init/shutdown in quick succession due to the async nature of pulseaudio. we have object-based private data, so we can just use that instead Reviewed-by: Marcel Hollerbach <mail@marcel-hollerbach.de> Differential Revision: https://phab.enlightenment.org/D11531
-rw-r--r--src/lib/ecore_audio/ecore_audio_obj_out_pulse.c125
1 files changed, 76 insertions, 49 deletions
diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c b/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c
index 5d61e0eb3e..edcc924acb 100644
--- a/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c
+++ b/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c
@@ -25,27 +25,19 @@ extern pa_mainloop_api functable;
25#define MY_CLASS ECORE_AUDIO_OUT_PULSE_CLASS 25#define MY_CLASS ECORE_AUDIO_OUT_PULSE_CLASS
26#define MY_CLASS_NAME "Ecore_Audio_Out_Pulse" 26#define MY_CLASS_NAME "Ecore_Audio_Out_Pulse"
27 27
28struct _Ecore_Audio_Pulse_Class {
29 pa_mainloop_api *api;
30 pa_context *context;
31 pa_context_state_t state;
32 Ecore_Job *state_job;
33 Eina_List *outputs;
34};
35
36static struct _Ecore_Audio_Pulse_Class class_vars = {
37 .api = &functable,
38};
39
40struct _Ecore_Audio_Out_Pulse_Data 28struct _Ecore_Audio_Out_Pulse_Data
41{ 29{
42 char *foo; 30 pa_mainloop_api *api;
31 pa_context *context;
32 pa_context_state_t state;
33 Ecore_Job *state_job;
34 Eina_List *outputs;
43}; 35};
44 36
45typedef struct _Ecore_Audio_Out_Pulse_Data Ecore_Audio_Out_Pulse_Data; 37typedef struct _Ecore_Audio_Out_Pulse_Data Ecore_Audio_Out_Pulse_Data;
46 38
47EOLIAN static void 39EOLIAN static void
48_ecore_audio_out_pulse_ecore_audio_volume_set(Eo *eo_obj, Ecore_Audio_Out_Pulse_Data *_pd EINA_UNUSED, double volume) 40_ecore_audio_out_pulse_ecore_audio_volume_set(Eo *eo_obj, Ecore_Audio_Out_Pulse_Data *pd, double volume)
49{ 41{
50 Eo *in; 42 Eo *in;
51 pa_stream *stream = NULL; 43 pa_stream *stream = NULL;
@@ -65,7 +57,7 @@ _ecore_audio_out_pulse_ecore_audio_volume_set(Eo *eo_obj, Ecore_Audio_Out_Pulse_
65 EINA_LIST_FOREACH(out_obj->inputs, input, in) { 57 EINA_LIST_FOREACH(out_obj->inputs, input, in) {
66 stream = efl_key_data_get(in, "pulse_data"); 58 stream = efl_key_data_get(in, "pulse_data");
67 idx = EPA_CALL(pa_stream_get_index)(stream); 59 idx = EPA_CALL(pa_stream_get_index)(stream);
68 EPA_CALL(pa_operation_unref)(EPA_CALL(pa_context_set_sink_input_volume)(class_vars.context, idx, &pa_volume, NULL, NULL)); 60 EPA_CALL(pa_operation_unref)(EPA_CALL(pa_context_set_sink_input_volume)(pd->context, idx, &pa_volume, NULL, NULL));
69 } 61 }
70} 62}
71 63
@@ -114,6 +106,7 @@ static Eina_Bool _input_attach_internal(Eo *eo_obj, Eo *in)
114 pa_stream *stream; 106 pa_stream *stream;
115 Eina_Bool ret = EINA_FALSE; 107 Eina_Bool ret = EINA_FALSE;
116 Ecore_Audio_Object *ea_obj = efl_data_scope_get(eo_obj, ECORE_AUDIO_CLASS); 108 Ecore_Audio_Object *ea_obj = efl_data_scope_get(eo_obj, ECORE_AUDIO_CLASS);
109 Ecore_Audio_Out_Pulse_Data *pd = efl_data_scope_get(eo_obj, MY_CLASS);
117 110
118 if (!EPA_LOAD()) return EINA_FALSE; 111 if (!EPA_LOAD()) return EINA_FALSE;
119 ret = ecore_audio_obj_out_input_attach(efl_super(eo_obj, MY_CLASS), in); 112 ret = ecore_audio_obj_out_input_attach(efl_super(eo_obj, MY_CLASS), in);
@@ -128,7 +121,7 @@ static Eina_Bool _input_attach_internal(Eo *eo_obj, Eo *in)
128 121
129 ss.rate = ss.rate * speed; 122 ss.rate = ss.rate * speed;
130 123
131 stream = EPA_CALL(pa_stream_new)(class_vars.context, name, &ss, NULL); 124 stream = EPA_CALL(pa_stream_new)(pd->context, name, &ss, NULL);
132 if (!stream) { 125 if (!stream) {
133 ERR("Could not create stream"); 126 ERR("Could not create stream");
134 ecore_audio_obj_out_input_detach(efl_super(eo_obj, MY_CLASS), in); 127 ecore_audio_obj_out_input_detach(efl_super(eo_obj, MY_CLASS), in);
@@ -151,18 +144,27 @@ static Eina_Bool _input_attach_internal(Eo *eo_obj, Eo *in)
151 144
152static void _delayed_attach_cb(void *data, const Efl_Event *event) 145static void _delayed_attach_cb(void *data, const Efl_Event *event)
153{ 146{
154 Eo *in = data; 147 efl_event_callback_del(event->object, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, _delayed_attach_cb, data);
155 efl_event_callback_del(event->object, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, _delayed_attach_cb, in);
156 148
157 _input_attach_internal(event->object, in); 149 _input_attach_internal(event->object, data);
150}
151
152static Eina_Bool
153_is_input_attached(Eo *eo_obj, Eo *in)
154{
155 Ecore_Audio_Output *out_obj = efl_data_scope_get(eo_obj, ECORE_AUDIO_OUT_CLASS);
156 if (!out_obj->inputs) return EINA_FALSE;
157 return !!eina_list_data_find(out_obj->inputs, in);
158} 158}
159 159
160EOLIAN static Eina_Bool 160EOLIAN static Eina_Bool
161_ecore_audio_out_pulse_ecore_audio_out_input_attach(Eo *eo_obj, Ecore_Audio_Out_Pulse_Data *_pd EINA_UNUSED, Eo *in) 161_ecore_audio_out_pulse_ecore_audio_out_input_attach(Eo *eo_obj, Ecore_Audio_Out_Pulse_Data *pd, Eo *in)
162{ 162{
163 Eina_Bool retval = EINA_TRUE; 163 Eina_Bool retval = EINA_TRUE;
164 164
165 if (class_vars.state != PA_CONTEXT_READY) { 165 if (_is_input_attached(eo_obj, in)) return EINA_TRUE;
166
167 if (pd->state != PA_CONTEXT_READY) {
166 DBG("Delaying input_attach because PA context is not ready."); 168 DBG("Delaying input_attach because PA context is not ready.");
167 efl_event_callback_add(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, _delayed_attach_cb, in); 169 efl_event_callback_add(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, _delayed_attach_cb, in);
168 } else { 170 } else {
@@ -180,16 +182,32 @@ static void _drain_cb(pa_stream *stream, int success EINA_UNUSED, void *data EIN
180} 182}
181 183
182EOLIAN static Eina_Bool 184EOLIAN static Eina_Bool
183_ecore_audio_out_pulse_ecore_audio_out_input_detach(Eo *eo_obj, Ecore_Audio_Out_Pulse_Data *_pd EINA_UNUSED, Eo *in) 185_ecore_audio_out_pulse_ecore_audio_out_input_detach(Eo *eo_obj, Ecore_Audio_Out_Pulse_Data *pd, Eo *in)
184{ 186{
185 pa_stream *stream = NULL; 187 pa_stream *stream = NULL;
186 Eina_Bool ret2 = EINA_FALSE; 188 Eina_Bool ret2 = EINA_FALSE;
187 pa_operation *op; 189 pa_operation *op;
188 190
189 if (!EPA_LOAD()) return EINA_FALSE; 191 if (!EPA_LOAD())
192 {
193 ERR("Failed to load PA!");
194 return EINA_FALSE;
195 }
196 if (!_is_input_attached(eo_obj, in))
197 {
198 ERR("Input object passed is not currently attached to this output!");
199 return EINA_FALSE;
200 }
201 if (pd->state != PA_CONTEXT_READY)
202 efl_event_callback_del(in, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, _delayed_attach_cb, pd);
203 else
204 efl_event_callback_del(in, ECORE_AUDIO_IN_EVENT_IN_SAMPLERATE_CHANGED, _update_samplerate_cb, eo_obj);
190 ret2 = ecore_audio_obj_out_input_detach(efl_super(eo_obj, MY_CLASS), in); 205 ret2 = ecore_audio_obj_out_input_detach(efl_super(eo_obj, MY_CLASS), in);
191 if (!ret2) 206 if (!ret2)
192 return EINA_FALSE; 207 {
208 ERR("Super call failed for ecore_audio_obj_out_input_detach!");
209 return EINA_FALSE;
210 }
193 211
194 stream = efl_key_data_get(in, "pulse_data"); 212 stream = efl_key_data_get(in, "pulse_data");
195 213
@@ -197,7 +215,12 @@ _ecore_audio_out_pulse_ecore_audio_out_input_detach(Eo *eo_obj, Ecore_Audio_Out_
197 op = EPA_CALL(pa_stream_drain) (stream, _drain_cb, NULL); 215 op = EPA_CALL(pa_stream_drain) (stream, _drain_cb, NULL);
198 if (!op) 216 if (!op)
199 { 217 {
200 ERR("Failed to drain PulseAudio stream."); 218 op = EPA_CALL(pa_stream_flush)(stream, _drain_cb, NULL);
219 if (!op)
220 {
221 EPA_CALL(pa_stream_disconnect)(stream);
222 EPA_CALL(pa_stream_unref)(stream);
223 }
201 return EINA_FALSE; 224 return EINA_FALSE;
202 } 225 }
203 226
@@ -205,67 +228,69 @@ _ecore_audio_out_pulse_ecore_audio_out_input_detach(Eo *eo_obj, Ecore_Audio_Out_
205 return EINA_TRUE; 228 return EINA_TRUE;
206} 229}
207 230
208static void _state_cb(pa_context *context, void *data EINA_UNUSED) 231static void _state_cb(pa_context *context, void *data)
209{ 232{
210 Eina_List *out, *tmp; 233 Eina_List *out, *tmp;
211 Eo *eo_obj; 234 Eo *eo_obj;
212 pa_context_state_t state; 235 pa_context_state_t state;
236 Ecore_Audio_Out_Pulse_Data *pd = data;
213 237
214 if (!EPA_LOAD()) return; 238 if (!EPA_LOAD()) return;
215 state = EPA_CALL(pa_context_get_state)(context); 239 state = EPA_CALL(pa_context_get_state)(context);
216 class_vars.state = state; 240 pd->state = state;
217 241
218 //ref everything in the list to be sure... 242 //ref everything in the list to be sure...
219 EINA_LIST_FOREACH(class_vars.outputs, out, eo_obj) { 243 EINA_LIST_FOREACH(pd->outputs, out, eo_obj) {
220 efl_ref(eo_obj); 244 efl_ref(eo_obj);
221 } 245 }
222 // the callback here can delete things in the list.. 246 // the callback here can delete things in the list..
223 if (state == PA_CONTEXT_READY) { 247 if (state == PA_CONTEXT_READY) {
224 DBG("PA context ready."); 248 DBG("PA context ready.");
225 EINA_LIST_FOREACH(class_vars.outputs, out, eo_obj) { 249 EINA_LIST_FOREACH(pd->outputs, out, eo_obj) {
226 efl_event_callback_call(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, NULL); 250 efl_event_callback_call(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, NULL);
227 } 251 }
228 } else if ((state == PA_CONTEXT_FAILED) || (state == PA_CONTEXT_TERMINATED)) { 252 } else if ((state == PA_CONTEXT_FAILED) || (state == PA_CONTEXT_TERMINATED)) {
229 DBG("PA context fail."); 253 DBG("PA context fail.");
230 EINA_LIST_FOREACH(class_vars.outputs, out, eo_obj) { 254 EINA_LIST_FOREACH(pd->outputs, out, eo_obj) {
231 efl_event_callback_call(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, NULL); 255 efl_event_callback_call(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, NULL);
232 } 256 }
233 } else { 257 } else {
234 DBG("Connection state %i", state); 258 DBG("Connection state %i", state);
235 } 259 }
236 // now unref everything safely 260 // now unref everything safely
237 EINA_LIST_FOREACH_SAFE(class_vars.outputs, out, tmp, eo_obj) { 261 EINA_LIST_FOREACH_SAFE(pd->outputs, out, tmp, eo_obj) {
238 efl_unref(eo_obj); 262 efl_unref(eo_obj);
239 } 263 }
240} 264}
241 265
242static void _state_job(void *data EINA_UNUSED) 266static void _state_job(void *data)
243{ 267{
244 if ((class_vars.state == PA_CONTEXT_FAILED) || 268 Ecore_Audio_Out_Pulse_Data *pd = data;
245 (class_vars.state == PA_CONTEXT_TERMINATED)) 269 if ((pd->state == PA_CONTEXT_FAILED) ||
270 (pd->state == PA_CONTEXT_TERMINATED))
246 { 271 {
247 Eo *eo_obj; 272 Eo *eo_obj;
248 Eina_List *out, *tmp; 273 Eina_List *out, *tmp;
249 274
250 DBG("PA context fail."); 275 DBG("PA context fail.");
251 //ref everything in the list to be sure... 276 //ref everything in the list to be sure...
252 EINA_LIST_FOREACH(class_vars.outputs, out, eo_obj) { 277 EINA_LIST_FOREACH(pd->outputs, out, eo_obj) {
253 efl_ref(eo_obj); 278 efl_ref(eo_obj);
254 } 279 }
255 // the callback here can delete things in the list.. 280 // the callback here can delete things in the list..
256 EINA_LIST_FOREACH(class_vars.outputs, out, eo_obj) { 281 EINA_LIST_FOREACH(pd->outputs, out, eo_obj) {
257 efl_event_callback_call(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, NULL); 282 efl_event_callback_call(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, NULL);
258 } 283 }
259 // now unref everything safely 284 // now unref everything safely
260 EINA_LIST_FOREACH_SAFE(class_vars.outputs, out, tmp, eo_obj) { 285 EINA_LIST_FOREACH_SAFE(pd->outputs, out, tmp, eo_obj) {
261 efl_unref(eo_obj); 286 efl_unref(eo_obj);
262 } 287 }
263 } 288 }
264 class_vars.state_job = NULL; 289 pd->state_job = NULL;
265} 290}
266 291
267EOLIAN static Eo * 292EOLIAN static Eo *
268_ecore_audio_out_pulse_efl_object_constructor(Eo *eo_obj, Ecore_Audio_Out_Pulse_Data *_pd EINA_UNUSED) 293_ecore_audio_out_pulse_efl_object_constructor(Eo *eo_obj, Ecore_Audio_Out_Pulse_Data *pd)
269{ 294{
270 int argc; 295 int argc;
271 char **argv, *disp = NULL; 296 char **argv, *disp = NULL;
@@ -274,10 +299,11 @@ _ecore_audio_out_pulse_efl_object_constructor(Eo *eo_obj, Ecore_Audio_Out_Pulse_
274 299
275 if (!EPA_LOAD()) return NULL; 300 if (!EPA_LOAD()) return NULL;
276 eo_obj = efl_constructor(efl_super(eo_obj, MY_CLASS)); 301 eo_obj = efl_constructor(efl_super(eo_obj, MY_CLASS));
302 pd->api = &functable;
277 303
278 out_obj->need_writer = EINA_FALSE; 304 out_obj->need_writer = EINA_FALSE;
279 305
280 if (!class_vars.context) { 306 if (!pd->context) {
281 307
282 // if we're in a wayland world rather than x11... but DISPLAY also set... 308 // if we're in a wayland world rather than x11... but DISPLAY also set...
283 if (getenv("WAYLAND_DISPLAY")) disp = getenv("DISPLAY"); 309 if (getenv("WAYLAND_DISPLAY")) disp = getenv("DISPLAY");
@@ -310,9 +336,9 @@ _ecore_audio_out_pulse_efl_object_constructor(Eo *eo_obj, Ecore_Audio_Out_Pulse_
310 ecore_app_args_get(&argc, &argv); 336 ecore_app_args_get(&argc, &argv);
311 if (!argc) { 337 if (!argc) {
312 DBG("Could not get program name, pulse outputs will be named ecore_audio"); 338 DBG("Could not get program name, pulse outputs will be named ecore_audio");
313 class_vars.context = EPA_CALL(pa_context_new)(class_vars.api, "ecore_audio"); 339 pd->context = EPA_CALL(pa_context_new)(pd->api, "ecore_audio");
314 } else { 340 } else {
315 class_vars.context = EPA_CALL(pa_context_new)(class_vars.api, basename(argv[0])); 341 pd->context = EPA_CALL(pa_context_new)(pd->api, basename(argv[0]));
316 } 342 }
317 // if we had a display value and a displayenv buffer then let's restore 343 // if we had a display value and a displayenv buffer then let's restore
318 // the previous value content of DISPLAY as we duplicated it above and 344 // the previous value content of DISPLAY as we duplicated it above and
@@ -327,21 +353,22 @@ _ecore_audio_out_pulse_efl_object_constructor(Eo *eo_obj, Ecore_Audio_Out_Pulse_
327 // free up our temporary local DISPLAY env sring copy if we have it 353 // free up our temporary local DISPLAY env sring copy if we have it
328 if (disp) free(disp); 354 if (disp) free(disp);
329 355
330 EPA_CALL(pa_context_set_state_callback)(class_vars.context, _state_cb, NULL); 356 EPA_CALL(pa_context_set_state_callback)(pd->context, _state_cb, pd);
331 EPA_CALL(pa_context_connect)(class_vars.context, NULL, PA_CONTEXT_NOFLAGS, NULL); 357 EPA_CALL(pa_context_connect)(pd->context, NULL, PA_CONTEXT_NOFLAGS, NULL);
332 } 358 }
333 359
334 class_vars.outputs = eina_list_append(class_vars.outputs, eo_obj); 360 pd->outputs = eina_list_append(pd->outputs, eo_obj);
335 if (class_vars.state_job) ecore_job_del(class_vars.state_job); 361 pd->state_job = ecore_job_add(_state_job, pd);
336 class_vars.state_job = ecore_job_add(_state_job, NULL);
337 362
338 return eo_obj; 363 return eo_obj;
339} 364}
340 365
341EOLIAN static void 366EOLIAN static void
342_ecore_audio_out_pulse_efl_object_destructor(Eo *eo_obj, Ecore_Audio_Out_Pulse_Data *_pd EINA_UNUSED) 367_ecore_audio_out_pulse_efl_object_destructor(Eo *eo_obj, Ecore_Audio_Out_Pulse_Data *pd)
343{ 368{
344 class_vars.outputs = eina_list_remove(class_vars.outputs, eo_obj); 369 pd->outputs = eina_list_remove(pd->outputs, eo_obj);
370 ecore_job_del(pd->state_job);
371 EPA_CALL(pa_context_unref)(pd->context);
345 efl_destructor(efl_super(eo_obj, MY_CLASS)); 372 efl_destructor(efl_super(eo_obj, MY_CLASS));
346} 373}
347 374