From e01750020dcc9d49e6f32d513f1b40cd30148d91 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 12 Feb 2015 17:00:52 +0100 Subject: [PATCH] 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. --- audio/out/ao_pulse.c | 48 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/audio/out/ao_pulse.c b/audio/out/ao_pulse.c index d6e08de53d..fd5e006172 100644 --- a/audio/out/ao_pulse.c +++ b/audio/out/ao_pulse.c @@ -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) {