ao_pulse: listen for hotplug events

This requires jumping through multiple hoops on fire. Since the
PulseAudio API is virtually undocumented, I'm not sure if this is
correct either. We only react to sink events, and only to the NEW/REMOVE
events. CHANGE events are ignored, because PulseAudio fires them far too
often - even if the system is completely idle! If pa_sink_info.name can
change, we're in trouble. pa_sink_info.description is not so important,
but it'd also be a bit un-nice if it can change, and we don't update it.

The weird way how the actual AO and the hotplug context share the same
struct (ao) comes in handy here, although context_success_cb() still had
to be duplicated from success_cb() - the unused argument has a different
type.
This commit is contained in:
wm4 2015-02-12 17:00:52 +01:00
parent f061befb33
commit e01750020d
1 changed files with 41 additions and 7 deletions

View File

@ -81,6 +81,27 @@ static void context_state_cb(pa_context *c, void *userdata)
}
}
static void subscribe_cb(pa_context *c, pa_subscription_event_type_t t,
uint32_t idx, void *userdata)
{
struct ao *ao = userdata;
int type = t & PA_SUBSCRIPTION_MASK_SINK;
int fac = t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
if ((type == PA_SUBSCRIPTION_EVENT_NEW || type == PA_SUBSCRIPTION_EVENT_REMOVE)
&& fac == PA_SUBSCRIPTION_EVENT_SINK)
{
ao_hotplug_event(ao);
}
}
static void context_success_cb(pa_context *c, int success, void *userdata)
{
struct ao *ao = userdata;
struct priv *priv = ao->priv;
priv->retval = success;
pa_threaded_mainloop_signal(priv->mainloop, 0);
}
static void stream_state_cb(pa_stream *s, void *userdata)
{
struct ao *ao = userdata;
@ -323,6 +344,7 @@ static int pa_init_boilerplate(struct ao *ao)
(long)pa_context_get_server_protocol_version(priv->context));
pa_context_set_state_callback(priv->context, context_state_cb, ao);
pa_context_set_subscribe_callback(priv->context, subscribe_cb, ao);
if (pa_context_connect(priv->context, host, 0, NULL) < 0)
goto fail;
@ -751,22 +773,32 @@ static void sink_info_cb(pa_context *c, const pa_sink_info *i, int eol, void *ud
ao_device_list_add(ctx->list, ctx->ao, &entry);
}
static int hotplug_init(struct ao *ao)
{
struct priv *priv = ao->priv;
if (pa_init_boilerplate(ao) < 0)
return -1;
pa_threaded_mainloop_lock(priv->mainloop);
waitop(priv, pa_context_subscribe(priv->context, PA_SUBSCRIPTION_MASK_SINK,
context_success_cb, ao));
return 0;
}
static void list_devs(struct ao *ao, struct ao_device_list *list)
{
struct priv *priv = ao->priv;
bool need_uninit = !priv->mainloop;
struct sink_cb_ctx ctx = {ao, list};
if (need_uninit && pa_init_boilerplate(ao) < 0)
return;
pa_threaded_mainloop_lock(priv->mainloop);
waitop(priv, pa_context_get_sink_info_list(priv->context, sink_info_cb, &ctx));
if (need_uninit)
uninit(ao);
}
static void hotplug_uninit(struct ao *ao)
{
uninit(ao);
}
#define OPT_BASE_STRUCT struct priv
@ -785,6 +817,8 @@ const struct ao_driver audio_out_pulse = {
.drain = drain,
.wait = wait_audio,
.wakeup = wakeup,
.hotplug_init = hotplug_init,
.hotplug_uninit = hotplug_uninit,
.list_devs = list_devs,
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {