diff options
Diffstat (limited to 'audio/out')
-rw-r--r-- | audio/out/ao.c | 112 | ||||
-rw-r--r-- | audio/out/ao.h | 10 | ||||
-rw-r--r-- | audio/out/internal.h | 23 |
3 files changed, 120 insertions, 25 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); } diff --git a/audio/out/ao.h b/audio/out/ao.h index dbbed24873..7b85ec80ba 100644 --- a/audio/out/ao.h +++ b/audio/out/ao.h @@ -48,6 +48,7 @@ enum aocontrol { enum { AO_EVENT_RELOAD = 1, + AO_EVENT_HOTPLUG = 2, }; typedef struct ao_control_vol { @@ -92,8 +93,15 @@ void ao_drain(struct ao *ao); bool ao_eof_reached(struct ao *ao); int ao_query_and_reset_events(struct ao *ao, int events); void ao_request_reload(struct ao *ao); +void ao_hotplug_event(struct ao *ao); + +struct ao_hotplug; +struct ao_hotplug *ao_hotplug_create(struct mpv_global *global, + struct input_ctx *input_ctx); +void ao_hotplug_destroy(struct ao_hotplug *hp); +bool ao_hotplug_check_update(struct ao_hotplug *hp); +struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp); -struct ao_device_list *ao_get_device_list(struct mpv_global *global); void ao_print_devices(struct mpv_global *global, struct mp_log *log); #endif /* MPLAYER_AUDIO_OUT_H */ diff --git a/audio/out/internal.h b/audio/out/internal.h index 9414208923..19402861a2 100644 --- a/audio/out/internal.h +++ b/audio/out/internal.h @@ -59,7 +59,8 @@ struct ao { // Used during init: if init fails, redirect to this ao char *redirect; - atomic_bool request_reload; + // Internal events (use ao_request_reload(), ao_hotplug_event()) + atomic_bool request_reload, request_hotplug; int buffer; double def_buffer; @@ -161,17 +162,19 @@ struct ao_driver { // Return the list of devices currently available in the system. Use // ao_device_list_add() to add entries. The selected device will be set as // ao->device (using ao_device_desc.name). - // Warning: the ao struct passed doesn't necessarily have ao_driver->init() - // called on it - in this case, ->uninit() won't be called either - // after this function. The idea is that list_devs can be called - // both when no audio or when audio is active. the latter can - // happen if the audio config change at runtime, and in this case - // we don't want to force a new connection to the audio server - // just to update the device list. For runtime updates, ->init() - // will have been called. In both cases, ao->priv is properly - // allocated. (Runtime updates are not used/supported yet.) + // Warning: the ao struct passed is not initialized with ao_driver->init(). + // Instead, hotplug_init/hotplug_uninit is called. If these + // callbacks are not set, no driver initialization call is done + // on the ao struct. void (*list_devs)(struct ao *ao, struct ao_device_list *list); + // If set, these are called before/after ao_driver->list_devs is called. + // It is also assumed that the driver can do hotplugging - which means + // it is expected to call ao_hotplug_event(ao) whenever the system's + // audio device list changes. The player will then call list_devs() again. + int (*hotplug_init)(struct ao *ao); + void (*hotplug_uninit)(struct ao *ao); + // For option parsing (see vo.h) int priv_size; const void *priv_defaults; |