summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-02-12 17:00:52 +0100
committerwm4 <wm4@nowhere>2015-02-12 17:18:43 +0100
commite01750020dcc9d49e6f32d513f1b40cd30148d91 (patch)
treeaf94cd563feab2068d041e44b466fffd32d2765d
parentf061befb3338c5cd6837701b4232dad48340ab6c (diff)
downloadmpv-e01750020dcc9d49e6f32d513f1b40cd30148d91.tar.bz2
mpv-e01750020dcc9d49e6f32d513f1b40cd30148d91.tar.xz
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.
-rw-r--r--audio/out/ao_pulse.c48
1 files 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) {