bu5hm4n pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=9324bcc3617caf76ef50a6eefb7e51b6d987a0b4

commit 9324bcc3617caf76ef50a6eefb7e51b6d987a0b4
Author: Mike Blumenkrantz <zm...@samsung.com>
Date:   Tue Mar 17 12:06:13 2020 -0400

    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 <m...@marcel-hollerbach.de>
    Differential Revision: https://phab.enlightenment.org/D11531
---
 src/lib/ecore_audio/ecore_audio_obj_out_pulse.c | 125 ++++++++++++++----------
 1 file 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;
 #define MY_CLASS ECORE_AUDIO_OUT_PULSE_CLASS
 #define MY_CLASS_NAME "Ecore_Audio_Out_Pulse"
 
-struct _Ecore_Audio_Pulse_Class {
-  pa_mainloop_api *api;
-  pa_context *context;
-  pa_context_state_t state;
-  Ecore_Job *state_job;
-  Eina_List *outputs;
-};
-
-static struct _Ecore_Audio_Pulse_Class class_vars = {
-    .api = &functable,
-};
-
 struct _Ecore_Audio_Out_Pulse_Data
 {
-  char *foo;
+   pa_mainloop_api *api;
+   pa_context *context;
+   pa_context_state_t state;
+   Ecore_Job *state_job;
+   Eina_List *outputs;
 };
 
 typedef struct _Ecore_Audio_Out_Pulse_Data Ecore_Audio_Out_Pulse_Data;
 
 EOLIAN static void
-_ecore_audio_out_pulse_ecore_audio_volume_set(Eo *eo_obj, 
Ecore_Audio_Out_Pulse_Data *_pd EINA_UNUSED, double volume)
+_ecore_audio_out_pulse_ecore_audio_volume_set(Eo *eo_obj, 
Ecore_Audio_Out_Pulse_Data *pd, double volume)
 {
   Eo *in;
   pa_stream *stream = NULL;
@@ -65,7 +57,7 @@ _ecore_audio_out_pulse_ecore_audio_volume_set(Eo *eo_obj, 
Ecore_Audio_Out_Pulse_
   EINA_LIST_FOREACH(out_obj->inputs, input, in) {
       stream = efl_key_data_get(in, "pulse_data");
       idx = EPA_CALL(pa_stream_get_index)(stream);
-      
EPA_CALL(pa_operation_unref)(EPA_CALL(pa_context_set_sink_input_volume)(class_vars.context,
 idx, &pa_volume, NULL, NULL));
+      
EPA_CALL(pa_operation_unref)(EPA_CALL(pa_context_set_sink_input_volume)(pd->context,
 idx, &pa_volume, NULL, NULL));
   }
 }
 
@@ -114,6 +106,7 @@ static Eina_Bool _input_attach_internal(Eo *eo_obj, Eo *in)
   pa_stream *stream;
   Eina_Bool ret = EINA_FALSE;
   Ecore_Audio_Object *ea_obj = efl_data_scope_get(eo_obj, ECORE_AUDIO_CLASS);
+  Ecore_Audio_Out_Pulse_Data *pd = efl_data_scope_get(eo_obj, MY_CLASS);
 
   if (!EPA_LOAD()) return EINA_FALSE;
   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)
 
   ss.rate = ss.rate * speed;
 
-  stream = EPA_CALL(pa_stream_new)(class_vars.context, name, &ss, NULL);
+  stream = EPA_CALL(pa_stream_new)(pd->context, name, &ss, NULL);
   if (!stream) {
       ERR("Could not create stream");
       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)
 
 static void _delayed_attach_cb(void *data, const Efl_Event *event)
 {
-  Eo *in = data;
-  efl_event_callback_del(event->object, 
ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, _delayed_attach_cb, in);
+  efl_event_callback_del(event->object, 
ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, _delayed_attach_cb, data);
 
-  _input_attach_internal(event->object, in);
+  _input_attach_internal(event->object, data);
+}
+
+static Eina_Bool
+_is_input_attached(Eo *eo_obj, Eo *in)
+{
+   Ecore_Audio_Output *out_obj = efl_data_scope_get(eo_obj, 
ECORE_AUDIO_OUT_CLASS);
+   if (!out_obj->inputs) return EINA_FALSE;
+   return !!eina_list_data_find(out_obj->inputs, in);
 }
 
 EOLIAN static Eina_Bool
-_ecore_audio_out_pulse_ecore_audio_out_input_attach(Eo *eo_obj, 
Ecore_Audio_Out_Pulse_Data *_pd EINA_UNUSED, Eo *in)
+_ecore_audio_out_pulse_ecore_audio_out_input_attach(Eo *eo_obj, 
Ecore_Audio_Out_Pulse_Data *pd, Eo *in)
 {
   Eina_Bool retval = EINA_TRUE;
 
-  if (class_vars.state != PA_CONTEXT_READY) {
+  if (_is_input_attached(eo_obj, in)) return EINA_TRUE;
+
+  if (pd->state != PA_CONTEXT_READY) {
     DBG("Delaying input_attach because PA context is not ready.");
     efl_event_callback_add(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, 
_delayed_attach_cb, in);
   } else {
@@ -180,16 +182,32 @@ static void _drain_cb(pa_stream *stream, int success 
EINA_UNUSED, void *data EIN
 }
 
 EOLIAN static Eina_Bool
-_ecore_audio_out_pulse_ecore_audio_out_input_detach(Eo *eo_obj, 
Ecore_Audio_Out_Pulse_Data *_pd EINA_UNUSED, Eo *in)
+_ecore_audio_out_pulse_ecore_audio_out_input_detach(Eo *eo_obj, 
Ecore_Audio_Out_Pulse_Data *pd, Eo *in)
 {
   pa_stream *stream = NULL;
   Eina_Bool ret2 = EINA_FALSE;
   pa_operation *op;
 
-  if (!EPA_LOAD()) return EINA_FALSE;
+  if (!EPA_LOAD())
+    {
+       ERR("Failed to load PA!");
+       return EINA_FALSE;
+    }
+  if (!_is_input_attached(eo_obj, in))
+    {
+       ERR("Input object passed is not currently attached to this output!");
+       return EINA_FALSE;
+    }
+  if (pd->state != PA_CONTEXT_READY)
+    efl_event_callback_del(in, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, 
_delayed_attach_cb, pd);
+  else
+    efl_event_callback_del(in, ECORE_AUDIO_IN_EVENT_IN_SAMPLERATE_CHANGED, 
_update_samplerate_cb, eo_obj);
   ret2 = ecore_audio_obj_out_input_detach(efl_super(eo_obj, MY_CLASS), in);
   if (!ret2)
-    return EINA_FALSE;
+    {
+       ERR("Super call failed for ecore_audio_obj_out_input_detach!");
+       return EINA_FALSE;
+    }
 
   stream = efl_key_data_get(in, "pulse_data");
 
@@ -197,7 +215,12 @@ _ecore_audio_out_pulse_ecore_audio_out_input_detach(Eo 
*eo_obj, Ecore_Audio_Out_
   op = EPA_CALL(pa_stream_drain) (stream, _drain_cb, NULL);
   if (!op)
     {
-       ERR("Failed to drain PulseAudio stream.");
+       op = EPA_CALL(pa_stream_flush)(stream, _drain_cb, NULL);
+       if (!op)
+         {
+            EPA_CALL(pa_stream_disconnect)(stream);
+            EPA_CALL(pa_stream_unref)(stream);
+         }
        return EINA_FALSE;
     }
 
@@ -205,67 +228,69 @@ _ecore_audio_out_pulse_ecore_audio_out_input_detach(Eo 
*eo_obj, Ecore_Audio_Out_
   return EINA_TRUE;
 }
 
-static void _state_cb(pa_context *context, void *data EINA_UNUSED)
+static void _state_cb(pa_context *context, void *data)
 {
    Eina_List *out, *tmp;
    Eo *eo_obj;
    pa_context_state_t state;
+   Ecore_Audio_Out_Pulse_Data *pd = data;
    
    if (!EPA_LOAD()) return;
    state = EPA_CALL(pa_context_get_state)(context);
-   class_vars.state = state;
+   pd->state = state;
    
    //ref everything in the list to be sure...
-   EINA_LIST_FOREACH(class_vars.outputs, out, eo_obj) {
+   EINA_LIST_FOREACH(pd->outputs, out, eo_obj) {
       efl_ref(eo_obj);
    }
    // the callback here can delete things in the list..
    if (state == PA_CONTEXT_READY) {
       DBG("PA context ready.");
-      EINA_LIST_FOREACH(class_vars.outputs, out, eo_obj) {
+      EINA_LIST_FOREACH(pd->outputs, out, eo_obj) {
          efl_event_callback_call(eo_obj, 
ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, NULL);
       }
    } else if ((state == PA_CONTEXT_FAILED) || (state == 
PA_CONTEXT_TERMINATED)) {
       DBG("PA context fail.");
-      EINA_LIST_FOREACH(class_vars.outputs, out, eo_obj) {
+      EINA_LIST_FOREACH(pd->outputs, out, eo_obj) {
          efl_event_callback_call(eo_obj, 
ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, NULL);
       }
    } else {
       DBG("Connection state %i", state);
    }
    // now unref everything safely
-   EINA_LIST_FOREACH_SAFE(class_vars.outputs, out, tmp, eo_obj) {
+   EINA_LIST_FOREACH_SAFE(pd->outputs, out, tmp, eo_obj) {
       efl_unref(eo_obj);
    }
 }
 
-static void _state_job(void *data EINA_UNUSED)
+static void _state_job(void *data)
 {
-   if ((class_vars.state == PA_CONTEXT_FAILED) ||
-       (class_vars.state == PA_CONTEXT_TERMINATED))
+   Ecore_Audio_Out_Pulse_Data *pd = data;
+   if ((pd->state == PA_CONTEXT_FAILED) ||
+       (pd->state == PA_CONTEXT_TERMINATED))
      {
         Eo *eo_obj;
         Eina_List *out, *tmp;
         
         DBG("PA context fail.");
         //ref everything in the list to be sure...
-        EINA_LIST_FOREACH(class_vars.outputs, out, eo_obj) {
+        EINA_LIST_FOREACH(pd->outputs, out, eo_obj) {
            efl_ref(eo_obj);
         }
         // the callback here can delete things in the list..
-        EINA_LIST_FOREACH(class_vars.outputs, out, eo_obj) {
+        EINA_LIST_FOREACH(pd->outputs, out, eo_obj) {
            efl_event_callback_call(eo_obj, 
ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, NULL);
         }
         // now unref everything safely
-        EINA_LIST_FOREACH_SAFE(class_vars.outputs, out, tmp, eo_obj) {
+        EINA_LIST_FOREACH_SAFE(pd->outputs, out, tmp, eo_obj) {
            efl_unref(eo_obj);
         }
      }
-   class_vars.state_job = NULL;
+   pd->state_job = NULL;
 }
 
 EOLIAN static Eo *
-_ecore_audio_out_pulse_efl_object_constructor(Eo *eo_obj, 
Ecore_Audio_Out_Pulse_Data *_pd EINA_UNUSED)
+_ecore_audio_out_pulse_efl_object_constructor(Eo *eo_obj, 
Ecore_Audio_Out_Pulse_Data *pd)
 {
   int argc;
   char **argv, *disp = NULL;
@@ -274,10 +299,11 @@ _ecore_audio_out_pulse_efl_object_constructor(Eo *eo_obj, 
Ecore_Audio_Out_Pulse_
 
   if (!EPA_LOAD()) return NULL;
   eo_obj = efl_constructor(efl_super(eo_obj, MY_CLASS));
+  pd->api = &functable;
 
   out_obj->need_writer = EINA_FALSE;
 
-  if (!class_vars.context) {
+  if (!pd->context) {
 
     // if we're in a wayland world rather than x11... but DISPLAY also set...
     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_
     ecore_app_args_get(&argc, &argv);
     if (!argc) {
         DBG("Could not get program name, pulse outputs will be named 
ecore_audio");
-       class_vars.context = EPA_CALL(pa_context_new)(class_vars.api, 
"ecore_audio");
+       pd->context = EPA_CALL(pa_context_new)(pd->api, "ecore_audio");
     } else {
-       class_vars.context = EPA_CALL(pa_context_new)(class_vars.api, 
basename(argv[0]));
+       pd->context = EPA_CALL(pa_context_new)(pd->api, basename(argv[0]));
     }
     // if we had a display value and a displayenv buffer then let's restore
     // 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_
     // free up our temporary local DISPLAY env sring copy if we have it
     if (disp) free(disp);
 
-    EPA_CALL(pa_context_set_state_callback)(class_vars.context, _state_cb, 
NULL);
-    EPA_CALL(pa_context_connect)(class_vars.context, NULL, PA_CONTEXT_NOFLAGS, 
NULL);
+    EPA_CALL(pa_context_set_state_callback)(pd->context, _state_cb, pd);
+    EPA_CALL(pa_context_connect)(pd->context, NULL, PA_CONTEXT_NOFLAGS, NULL);
   }
 
-  class_vars.outputs = eina_list_append(class_vars.outputs, eo_obj);
-  if (class_vars.state_job) ecore_job_del(class_vars.state_job);
-  class_vars.state_job = ecore_job_add(_state_job, NULL);
+  pd->outputs = eina_list_append(pd->outputs, eo_obj);
+  pd->state_job = ecore_job_add(_state_job, pd);
 
   return eo_obj;
 }
 
 EOLIAN static void
-_ecore_audio_out_pulse_efl_object_destructor(Eo *eo_obj, 
Ecore_Audio_Out_Pulse_Data *_pd EINA_UNUSED)
+_ecore_audio_out_pulse_efl_object_destructor(Eo *eo_obj, 
Ecore_Audio_Out_Pulse_Data *pd)
 {
-  class_vars.outputs = eina_list_remove(class_vars.outputs, eo_obj);
+  pd->outputs = eina_list_remove(pd->outputs, eo_obj);
+  ecore_job_del(pd->state_job);
+  EPA_CALL(pa_context_unref)(pd->context);
   efl_destructor(efl_super(eo_obj, MY_CLASS));
 }
 

-- 


Reply via email to