forked from enlightenment/efl
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
This commit is contained in:
parent
1af46ef302
commit
9324bcc361
|
@ -25,27 +25,19 @@ extern pa_mainloop_api functable;
|
||||||
#define MY_CLASS ECORE_AUDIO_OUT_PULSE_CLASS
|
#define MY_CLASS ECORE_AUDIO_OUT_PULSE_CLASS
|
||||||
#define MY_CLASS_NAME "Ecore_Audio_Out_Pulse"
|
#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
|
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;
|
typedef struct _Ecore_Audio_Out_Pulse_Data Ecore_Audio_Out_Pulse_Data;
|
||||||
|
|
||||||
EOLIAN static void
|
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;
|
Eo *in;
|
||||||
pa_stream *stream = NULL;
|
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) {
|
EINA_LIST_FOREACH(out_obj->inputs, input, in) {
|
||||||
stream = efl_key_data_get(in, "pulse_data");
|
stream = efl_key_data_get(in, "pulse_data");
|
||||||
idx = EPA_CALL(pa_stream_get_index)(stream);
|
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;
|
pa_stream *stream;
|
||||||
Eina_Bool ret = EINA_FALSE;
|
Eina_Bool ret = EINA_FALSE;
|
||||||
Ecore_Audio_Object *ea_obj = efl_data_scope_get(eo_obj, ECORE_AUDIO_CLASS);
|
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;
|
if (!EPA_LOAD()) return EINA_FALSE;
|
||||||
ret = ecore_audio_obj_out_input_attach(efl_super(eo_obj, MY_CLASS), in);
|
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;
|
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) {
|
if (!stream) {
|
||||||
ERR("Could not create stream");
|
ERR("Could not create stream");
|
||||||
ecore_audio_obj_out_input_detach(efl_super(eo_obj, MY_CLASS), in);
|
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)
|
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, data);
|
||||||
efl_event_callback_del(event->object, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, _delayed_attach_cb, in);
|
|
||||||
|
|
||||||
_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
|
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;
|
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.");
|
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);
|
efl_event_callback_add(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, _delayed_attach_cb, in);
|
||||||
} else {
|
} else {
|
||||||
|
@ -180,16 +182,32 @@ static void _drain_cb(pa_stream *stream, int success EINA_UNUSED, void *data EIN
|
||||||
}
|
}
|
||||||
|
|
||||||
EOLIAN static Eina_Bool
|
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;
|
pa_stream *stream = NULL;
|
||||||
Eina_Bool ret2 = EINA_FALSE;
|
Eina_Bool ret2 = EINA_FALSE;
|
||||||
pa_operation *op;
|
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);
|
ret2 = ecore_audio_obj_out_input_detach(efl_super(eo_obj, MY_CLASS), in);
|
||||||
if (!ret2)
|
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");
|
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);
|
op = EPA_CALL(pa_stream_drain) (stream, _drain_cb, NULL);
|
||||||
if (!op)
|
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;
|
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;
|
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;
|
Eina_List *out, *tmp;
|
||||||
Eo *eo_obj;
|
Eo *eo_obj;
|
||||||
pa_context_state_t state;
|
pa_context_state_t state;
|
||||||
|
Ecore_Audio_Out_Pulse_Data *pd = data;
|
||||||
|
|
||||||
if (!EPA_LOAD()) return;
|
if (!EPA_LOAD()) return;
|
||||||
state = EPA_CALL(pa_context_get_state)(context);
|
state = EPA_CALL(pa_context_get_state)(context);
|
||||||
class_vars.state = state;
|
pd->state = state;
|
||||||
|
|
||||||
//ref everything in the list to be sure...
|
//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);
|
efl_ref(eo_obj);
|
||||||
}
|
}
|
||||||
// the callback here can delete things in the list..
|
// the callback here can delete things in the list..
|
||||||
if (state == PA_CONTEXT_READY) {
|
if (state == PA_CONTEXT_READY) {
|
||||||
DBG("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);
|
efl_event_callback_call(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, NULL);
|
||||||
}
|
}
|
||||||
} else if ((state == PA_CONTEXT_FAILED) || (state == PA_CONTEXT_TERMINATED)) {
|
} else if ((state == PA_CONTEXT_FAILED) || (state == PA_CONTEXT_TERMINATED)) {
|
||||||
DBG("PA context fail.");
|
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);
|
efl_event_callback_call(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, NULL);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DBG("Connection state %i", state);
|
DBG("Connection state %i", state);
|
||||||
}
|
}
|
||||||
// now unref everything safely
|
// 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);
|
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) ||
|
Ecore_Audio_Out_Pulse_Data *pd = data;
|
||||||
(class_vars.state == PA_CONTEXT_TERMINATED))
|
if ((pd->state == PA_CONTEXT_FAILED) ||
|
||||||
|
(pd->state == PA_CONTEXT_TERMINATED))
|
||||||
{
|
{
|
||||||
Eo *eo_obj;
|
Eo *eo_obj;
|
||||||
Eina_List *out, *tmp;
|
Eina_List *out, *tmp;
|
||||||
|
|
||||||
DBG("PA context fail.");
|
DBG("PA context fail.");
|
||||||
//ref everything in the list to be sure...
|
//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);
|
efl_ref(eo_obj);
|
||||||
}
|
}
|
||||||
// the callback here can delete things in the list..
|
// 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);
|
efl_event_callback_call(eo_obj, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_FAIL, NULL);
|
||||||
}
|
}
|
||||||
// now unref everything safely
|
// 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);
|
efl_unref(eo_obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class_vars.state_job = NULL;
|
pd->state_job = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
EOLIAN static Eo *
|
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;
|
int argc;
|
||||||
char **argv, *disp = NULL;
|
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;
|
if (!EPA_LOAD()) return NULL;
|
||||||
eo_obj = efl_constructor(efl_super(eo_obj, MY_CLASS));
|
eo_obj = efl_constructor(efl_super(eo_obj, MY_CLASS));
|
||||||
|
pd->api = &functable;
|
||||||
|
|
||||||
out_obj->need_writer = EINA_FALSE;
|
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 we're in a wayland world rather than x11... but DISPLAY also set...
|
||||||
if (getenv("WAYLAND_DISPLAY")) disp = getenv("DISPLAY");
|
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);
|
ecore_app_args_get(&argc, &argv);
|
||||||
if (!argc) {
|
if (!argc) {
|
||||||
DBG("Could not get program name, pulse outputs will be named ecore_audio");
|
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 {
|
} 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
|
// 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
|
// 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
|
// free up our temporary local DISPLAY env sring copy if we have it
|
||||||
if (disp) free(disp);
|
if (disp) free(disp);
|
||||||
|
|
||||||
EPA_CALL(pa_context_set_state_callback)(class_vars.context, _state_cb, NULL);
|
EPA_CALL(pa_context_set_state_callback)(pd->context, _state_cb, pd);
|
||||||
EPA_CALL(pa_context_connect)(class_vars.context, NULL, PA_CONTEXT_NOFLAGS, NULL);
|
EPA_CALL(pa_context_connect)(pd->context, NULL, PA_CONTEXT_NOFLAGS, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
class_vars.outputs = eina_list_append(class_vars.outputs, eo_obj);
|
pd->outputs = eina_list_append(pd->outputs, eo_obj);
|
||||||
if (class_vars.state_job) ecore_job_del(class_vars.state_job);
|
pd->state_job = ecore_job_add(_state_job, pd);
|
||||||
class_vars.state_job = ecore_job_add(_state_job, NULL);
|
|
||||||
|
|
||||||
return eo_obj;
|
return eo_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
EOLIAN static void
|
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));
|
efl_destructor(efl_super(eo_obj, MY_CLASS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue