This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch master
in repository efl.

View the commit online.

commit a8c1a7132d1f151186c60d99e4d0b626e3225cbe
Author: Carsten Haitzler <ras...@rasterman.com>
AuthorDate: Wed Sep 4 11:42:11 2024 +0100

    ecore audio - handle null context for pulse without pulse crashes
    
    fixes #70
    
    @fix
---
 src/lib/ecore_audio/ecore_audio_obj_out_pulse.c | 203 ++++++++++++++----------
 src/lib/ecore_audio/ecore_audio_pulse.c         |  17 +-
 2 files changed, 130 insertions(+), 90 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 463ecb6773..c225003c5f 100644
--- a/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c
+++ b/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c
@@ -54,11 +54,13 @@ _ecore_audio_out_pulse_ecore_audio_volume_set(Eo *eo_obj, Ecore_Audio_Out_Pulse_
 
   ecore_audio_obj_volume_set(efl_super(eo_obj, MY_CLASS), volume);
 
-  EINA_LIST_FOREACH(out_obj->inputs, input, in) {
+  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)(pd->context, idx, &pa_volume, NULL, NULL));
-  }
+      if (pd->context)
+        EPA_CALL(pa_operation_unref)(EPA_CALL(pa_context_set_sink_input_volume)(pd->context, idx, &pa_volume, NULL, NULL));
+    }
 }
 
 static void _write_cb(pa_stream *stream, size_t len, void *data)
@@ -103,7 +105,7 @@ static Eina_Bool _input_attach_internal(Eo *eo_obj, Eo *in)
   const char *name = NULL;
   pa_sample_spec ss;
   double speed = 0;
-  pa_stream *stream;
+  pa_stream *stream = NULL;
   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);
@@ -121,12 +123,13 @@ static Eina_Bool _input_attach_internal(Eo *eo_obj, Eo *in)
 
   ss.rate = ss.rate * speed;
 
-  stream = EPA_CALL(pa_stream_new)(pd->context, name, &ss, NULL);
-  if (!stream) {
+  if (pd->context) 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);
       return EINA_FALSE;
-  }
+    }
 
   efl_event_callback_add(in, ECORE_AUDIO_IN_EVENT_IN_SAMPLERATE_CHANGED, _update_samplerate_cb, eo_obj);
 
@@ -164,12 +167,15 @@ _ecore_audio_out_pulse_ecore_audio_out_input_attach(Eo *eo_obj, Ecore_Audio_Out_
 
   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 {
-    retval = _input_attach_internal(eo_obj, in);
-  }
+  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
+    {
+      retval = _input_attach_internal(eo_obj, in);
+    }
 
   return retval;
 }
@@ -240,27 +246,36 @@ static void _state_cb(pa_context *context, void *data)
    pd->state = state;
 
    //ref everything in the list to be sure...
-   EINA_LIST_FOREACH(pd->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) {
+   if (state == PA_CONTEXT_READY)
+    {
       DBG("PA context ready.");
-      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)) {
+      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(pd->outputs, out, eo_obj) {
-         efl_event_callback_call(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, NULL);
-      }
-   } else {
+      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(pd->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)
@@ -274,17 +289,20 @@ static void _state_job(void *data)
 
         DBG("PA context fail.");
         //ref everything in the list to be sure...
-        EINA_LIST_FOREACH(pd->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(pd->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(pd->outputs, out, tmp, eo_obj) {
+        EINA_LIST_FOREACH_SAFE(pd->outputs, out, tmp, eo_obj)
+         {
            efl_unref(eo_obj);
-        }
+         }
      }
    pd->state_job = NULL;
 }
@@ -296,6 +314,7 @@ _ecore_audio_out_pulse_efl_object_constructor(Eo *eo_obj, Ecore_Audio_Out_Pulse_
   char **argv, *disp = NULL;
   Ecore_Audio_Output *out_obj = efl_data_scope_get(eo_obj, ECORE_AUDIO_OUT_CLASS);
   static char *dispenv = NULL;
+  char *dispenv_free = NULL;
 
   if (!EPA_LOAD()) return NULL;
   eo_obj = efl_constructor(efl_super(eo_obj, MY_CLASS));
@@ -303,59 +322,71 @@ _ecore_audio_out_pulse_efl_object_constructor(Eo *eo_obj, Ecore_Audio_Out_Pulse_
 
   out_obj->need_writer = EINA_FALSE;
 
-  if (!pd->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");
-    // make a tmp copy of display locally as we'll overwrite this
-    if (disp) disp = strdup(disp);
-    // if we had a previously allocated env var buffer for DISPLAY then
-    // free it only if DISPLAY env var changed
-    if (dispenv) {
-      if (!((disp) && (!strcmp(dispenv + 8/*"DISPLAY="*/, disp)))) {
-        free(dispenv);
-        dispenv = NULL;
-      }
-    }
-    // no previous display env but we have a display, then allocate a buffer
-    // that stays around until the next time here with the evn var string
-    // but have space for disp string too
-    if ((!dispenv) && (disp)) {
-      dispenv = malloc(8/*"DISPLAY="*/ + strlen(disp) + 1);
-    }
-    // ensure env var is empty and to a putenv as pulse wants to use DISPLAY
-    // and if its non-empty it'll try connect to the xserver and we do not
-    // want this to happen in a wayland universe
-    if (dispenv) {
-      strcpy(dispenv, "DISPLAY=");
-      putenv(dispenv);
-    }
-    // now hopefully getenv("DISPLAY") inside pulse will return NULL or it
-    // will return an empty string "" which pulse thinsk is the same as NULL
+      // if we're in a wayland world rather than x11... but DISPLAY also set...
+      if (getenv("WAYLAND_DISPLAY")) disp = getenv("DISPLAY");
+      // make a tmp copy of display locally as we'll overwrite this
+      if (disp) disp = strdup(disp);
+      // if we had a previously allocated env var buffer for DISPLAY then
+      // free it only if DISPLAY env var changed
+      if (dispenv)
+        {
+          if (!((disp) && (!strcmp(dispenv + 8/*"DISPLAY="*/, disp))))
+            {
+              // queue this old disp env to be freed - don't do it yet
+              dispenv_free = dispenv;
+              dispenv = NULL;
+            }
+        }
+      // no previous display env but we have a display, then allocate a buffer
+      // that stays around until the next time here with the evn var string
+      // but have space for disp string too
+      if ((!dispenv) && (disp))
+        {
+          dispenv = malloc(8/*"DISPLAY="*/ + strlen(disp) + 1);
+        }
+      // ensure env var is empty and to a putenv as pulse wants to use DISPLAY
+      // and if its non-empty it'll try connect to the xserver and we do not
+      // want this to happen in a wayland universe
+      if (dispenv)
+        {
+          strcpy(dispenv, "DISPLAY=");
+          putenv(dispenv);
+        }
+      // now hopefully getenv("DISPLAY") inside pulse will return NULL or it
+      // will return an empty string "" which pulse thinsk is the same as NULL
 
-    ecore_app_args_get(&argc, &argv);
-    if (!argc) {
-        DBG("Could not get program name, pulse outputs will be named ecore_audio");
-       pd->context = EPA_CALL(pa_context_new)(pd->api, "ecore_audio");
-    } else {
-       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
-    // add to the env of the dispenv buffer, then putenv that back. as the
-    // buffer is malloced this will be safe, but as the displayenv is local
-    // and static we wont go allocating these buffers forever. just this one
-    // here and then replace/re-use it.
-    if ((disp) && (dispenv)) {
-      strcat(dispenv, disp);
-      putenv(dispenv);
-    }
-    // free up our temporary local DISPLAY env sring copy if we have it
-    if (disp) free(disp);
+      ecore_app_args_get(&argc, &argv);
+      if (!argc)
+        {
+          DBG("Could not get program name, pulse outputs will be named ecore_audio");
+          pd->context = EPA_CALL(pa_context_new)(pd->api, "ecore_audio");
+        }
+      else
+        {
+          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
+      // add to the env of the dispenv buffer, then putenv that back. as the
+      // buffer is malloced this will be safe, but as the displayenv is local
+      // and static we wont go allocating these buffers forever. just this one
+      // here and then replace/re-use it.
+      if ((disp) && (dispenv))
+        {
+          strcat(dispenv, disp);
+          putenv(dispenv);
+        }
+      // free up our temporary local DISPLAY env sring copy if we have it
+      if (disp) free(disp);
+      // free that old dispenv we don't need anymore
+      if (dispenv_free) free(dispenv_free);
 
-    EPA_CALL(pa_context_set_state_callback)(pd->context, _state_cb, pd);
-    EPA_CALL(pa_context_connect)(pd->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);
+    }
 
   pd->outputs = eina_list_append(pd->outputs, eo_obj);
   pd->state_job = ecore_job_add(_state_job, pd);
@@ -368,7 +399,11 @@ _ecore_audio_out_pulse_efl_object_destructor(Eo *eo_obj, Ecore_Audio_Out_Pulse_D
 {
   pd->outputs = eina_list_remove(pd->outputs, eo_obj);
   ecore_job_del(pd->state_job);
-  EPA_CALL(pa_context_unref)(pd->context);
+  if (pd->context)
+    {
+      EPA_CALL(pa_context_unref)(pd->context);
+      pd->context = NULL;
+    }
   efl_destructor(efl_super(eo_obj, MY_CLASS));
 }
 
diff --git a/src/lib/ecore_audio/ecore_audio_pulse.c b/src/lib/ecore_audio/ecore_audio_pulse.c
index 099491c1fd..6414a82866 100644
--- a/src/lib/ecore_audio/ecore_audio_pulse.c
+++ b/src/lib/ecore_audio/ecore_audio_pulse.c
@@ -368,8 +368,11 @@ _pulse_output_volume_set(Ecore_Audio_Object *output, double vol)
      {
         stream = in->obj_data;
         idx = pa_stream_get_index(stream);
-        op = pa_context_set_sink_input_volume(priv->context, idx, &volume, NULL, NULL);
-        pa_operation_unref(op);
+        if (priv->context)
+         {
+           op = pa_context_set_sink_input_volume(priv->context, idx, &volume, NULL, NULL);
+           pa_operation_unref(op);
+         }
      }
 }
 
@@ -399,15 +402,17 @@ _pulse_output_add_input(Ecore_Audio_Object *output, Ecore_Audio_Object *input)
    Ecore_Audio_Input *in = (Ecore_Audio_Input *)input;
    Ecore_Audio_Module *outmod = out->module;
    struct _Ecore_Audio_Pa_Private *priv = (struct _Ecore_Audio_Pa_Private *)outmod->priv;
-   pa_stream *stream;
+   pa_stream *stream = NULL;
 
-   pa_sample_spec ss = {
+   pa_sample_spec ss =
+    {
       .format = PA_SAMPLE_FLOAT32LE,
       .rate = in->samplerate * in->speed,
       .channels = in->channels,
-   };
+    };
 
-   stream = pa_stream_new(priv->context, in->name, &ss, NULL);
+   if (priv->context)
+     stream = pa_stream_new(priv->context, in->name, &ss, NULL);
    if (!stream)
      {
         ERR("Could not create stream");

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to