summaryrefslogtreecommitdiffstats
path: root/audio/out/ao.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-02-12 16:53:56 +0100
committerwm4 <wm4@nowhere>2015-02-12 17:17:41 +0100
commitf061befb3338c5cd6837701b4232dad48340ab6c (patch)
tree13d8526248cc5bf68fb5d563fbf4cdd6e5dcc2aa /audio/out/ao.c
parentc152c590843a192761cff585a84ce57385d83d40 (diff)
downloadmpv-f061befb3338c5cd6837701b4232dad48340ab6c.tar.bz2
mpv-f061befb3338c5cd6837701b4232dad48340ab6c.tar.xz
audio: add device change notification for hotplugging
Not very important for the command line player; but GUI applications will want to know about this. This only adds the internal API; support for specific audio outputs comes later. This reuses the ao struct as context for the hotplug event listener, similar to how the "old" device listing API did. This is probably a bit unclean and confusing. One argument got reusing it is that otherwise rewriting parts of ao_pulse would be required (because the PulseAudio API requires so damn much boilerplate). Another is that --ao-defaults is applied to the hotplug dummy ao struct, which automatically applies such defaults even to the hotplug context. Notification works through the property observation mechanism in the client API. The notification chain is a bit complicated: the AO notifies the player, which in turn notifies the clients, which in turn will actually retrieve the device list. (It still has the advantage that it's slightly cleaner, since the AO stuff doesn't need to know about client API issues.) The weird handling of atomic flags in ao.c is because we still don't require real atomics from the compiler. Otherwise we'd just use atomic bitwise operations.
Diffstat (limited to 'audio/out/ao.c')
-rw-r--r--audio/out/ao.c112
1 files changed, 98 insertions, 14 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c
index ccb39f7f9e..a2fa2fb104 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -386,14 +386,25 @@ int ao_query_and_reset_events(struct ao *ao, int events)
int actual_events = 0;
if (atomic_load(&ao->request_reload)) // don't need to reset it
actual_events |= AO_EVENT_RELOAD;
+ if (atomic_load(&ao->request_hotplug))
+ actual_events |= AO_EVENT_HOTPLUG;
return actual_events & events;
}
-// Request that the player core destroys and recreates the AO.
+// Request that the player core destroys and recreates the AO. Fully thread-safe.
void ao_request_reload(struct ao *ao)
{
atomic_store(&ao->request_reload, true);
- mp_input_wakeup(ao->input_ctx);
+ if (ao->input_ctx)
+ mp_input_wakeup(ao->input_ctx);
+}
+
+// Notify the player that the device list changed. Fully thread-safe.
+void ao_hotplug_event(struct ao *ao)
+{
+ atomic_store(&ao->request_hotplug, true);
+ if (ao->input_ctx)
+ mp_input_wakeup(ao->input_ctx);
}
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
@@ -444,26 +455,88 @@ const char *ao_get_detected_device(struct ao *ao)
return ao->detected_device;
}
-struct ao_device_list *ao_get_device_list(struct mpv_global *global)
+// ---
+
+struct ao_hotplug {
+ struct mpv_global *global;
+ struct input_ctx *input_ctx;
+ // A single AO instance is used to listen to hotplug events. It wouldn't
+ // make much sense to allow multiple AO drivers; all sane platforms have
+ // a single such audio API.
+ // This is _not_ the same AO instance as used for playing audio.
+ struct ao *ao;
+ // cached
+ struct ao_device_list *list;
+ bool needs_update;
+};
+
+struct ao_hotplug *ao_hotplug_create(struct mpv_global *global,
+ struct input_ctx *input_ctx)
+{
+ struct ao_hotplug *hp = talloc_ptrtype(NULL, hp);
+ *hp = (struct ao_hotplug){
+ .global = global,
+ .input_ctx = input_ctx,
+ .needs_update = true,
+ };
+ return hp;
+}
+
+static void get_devices(struct ao *ao, struct ao_device_list *list)
+{
+ int num = list->num_devices;
+ if (ao->driver->list_devs)
+ ao->driver->list_devs(ao, list);
+ // Add at least a default entry
+ if (list->num_devices == num)
+ ao_device_list_add(list, ao, &(struct ao_device_desc){"", "Default"});
+}
+
+bool ao_hotplug_check_update(struct ao_hotplug *hp)
+{
+ if (hp->ao && ao_query_and_reset_events(hp->ao, AO_EVENT_HOTPLUG)) {
+ hp->needs_update = true;
+ atomic_store(&hp->ao->request_hotplug, false);
+ return true;
+ }
+ return false;
+}
+
+// The return value is valid until the next call to this API.
+struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp)
{
- struct ao_device_list *list = talloc_zero(NULL, struct ao_device_list);
+ if (hp->list && !hp->needs_update)
+ return hp->list;
+
+ talloc_free(hp->list);
+ struct ao_device_list *list = talloc_zero(hp, struct ao_device_list);
+ hp->list = list;
+
MP_TARRAY_APPEND(list, list->devices, list->num_devices,
(struct ao_device_desc){"auto", "Autoselect device"});
+
for (int n = 0; audio_out_drivers[n]; n++) {
const struct ao_driver *d = audio_out_drivers[n];
if (d == &audio_out_null)
break; // don't add unsafe/special entries
- struct ao *ao = ao_alloc(true, global, NULL, (char *)d->name, NULL);
+
+ struct ao *ao = ao_alloc(true, hp->global, hp->input_ctx,
+ (char *)d->name, NULL);
if (!ao)
continue;
- int num = list->num_devices;
- if (d->list_devs)
- d->list_devs(ao, list);
- // Add at least a default entry
- if (list->num_devices == num)
- ao_device_list_add(list, ao, &(struct ao_device_desc){"", "Default"});
- talloc_free(ao);
+
+ if (ao->driver->hotplug_init) {
+ if (!hp->ao && ao->driver->hotplug_init(ao) >= 0)
+ hp->ao = ao; // keep this one
+ if (hp->ao && hp->ao->driver == d)
+ get_devices(hp->ao, list);
+ } else {
+ get_devices(ao, list);
+ }
+ if (ao != hp->ao)
+ talloc_free(ao);
}
+ hp->needs_update = false;
return list;
}
@@ -478,13 +551,24 @@ void ao_device_list_add(struct ao_device_list *list, struct ao *ao,
MP_TARRAY_APPEND(list, list->devices, list->num_devices, c);
}
+void ao_hotplug_destroy(struct ao_hotplug *hp)
+{
+ if (!hp)
+ return;
+ if (hp->ao && hp->ao->driver->hotplug_uninit)
+ hp->ao->driver->hotplug_uninit(hp->ao);
+ talloc_free(hp->ao);
+ talloc_free(hp);
+}
+
void ao_print_devices(struct mpv_global *global, struct mp_log *log)
{
- struct ao_device_list *list = ao_get_device_list(global);
+ struct ao_hotplug *hp = ao_hotplug_create(global, NULL);
+ struct ao_device_list *list = ao_hotplug_get_device_list(hp);
mp_info(log, "List of detected audio devices:\n");
for (int n = 0; n < list->num_devices; n++) {
struct ao_device_desc *desc = &list->devices[n];
mp_info(log, " '%s' (%s)\n", desc->name, desc->desc);
}
- talloc_free(list);
+ ao_hotplug_destroy(hp);
}