From 35649a990a5d468b6e9539c9e362d1e5a351c9c4 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 9 Oct 2014 21:21:31 +0200 Subject: audio: add device selection & listing with --audio-device Not sure how good of an idea this is. This commit doesn't add support for this to any AO yet; the AO implementations will follow later. --- audio/out/ao.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++---- audio/out/ao.h | 13 ++++++++ audio/out/internal.h | 14 +++++++++ 3 files changed, 104 insertions(+), 6 deletions(-) (limited to 'audio') diff --git a/audio/out/ao.c b/audio/out/ao.c index e87fc52d0e..4da77c6356 100644 --- a/audio/out/ao.c +++ b/audio/out/ao.c @@ -128,12 +128,37 @@ const struct m_obj_list ao_obj_list = { .allow_trailer = true, }; +// Return true if the ao with the given name matches the opt (--audio-device +// contents). If opt is empty, matching always succeeds. If out_device is not +// NULL, it will be set to the implied audio device (or NULL). +static bool match_ao_driver(const char *ao_name, char *opt, char **out_device) +{ + bstr ao_nameb = bstr0(ao_name); + bstr optb = bstr0(opt); + + char *dummy; + if (!out_device) + out_device = &dummy; + *out_device = NULL; + + if (!optb.len || bstr_equals0(optb, "auto")) + return true; + if (!bstr_startswith(optb, ao_nameb)) + return false; + if (optb.len > ao_nameb.len && optb.start[ao_nameb.len] != '/') + return false; + char *split = strchr(opt, '/'); + *out_device = split ? split + 1 : NULL; + return true; +} + static struct ao *ao_create(bool probing, struct mpv_global *global, struct input_ctx *input_ctx, struct encode_lavc_context *encode_lavc_ctx, int samplerate, int format, struct mp_chmap channels, char *name, char **args) { + struct MPOpts *opts = global->opts; struct mp_log *log = mp_log_new(NULL, global->log, "ao"); struct m_obj_desc desc; if (!m_obj_list_find(&desc, &ao_obj_list, bstr0(name))) { @@ -152,17 +177,20 @@ static struct ao *ao_create(bool probing, struct mpv_global *global, .channels = channels, .format = format, .log = mp_log_new(ao, log, name), - .def_buffer = global->opts->audio_buffer, + .def_buffer = opts->audio_buffer, }; if (ao->driver->encode != !!ao->encode_lavc_ctx) goto error; struct m_config *config = m_config_from_obj_desc(ao, ao->log, &desc); - if (m_config_apply_defaults(config, name, global->opts->ao_defs) < 0) + if (m_config_apply_defaults(config, name, opts->ao_defs) < 0) goto error; if (m_config_set_obj_params(config, args) < 0) goto error; ao->priv = config->optstruct; + match_ao_driver(ao->driver->name, opts->audio_device, &ao->device); + ao->device = talloc_strdup(ao, ao->device); + char *chmap = mp_chmap_to_str(&ao->channels); MP_VERBOSE(ao, "requested format: %d Hz, %s channels, %s\n", ao->samplerate, chmap, af_fmt_to_str(ao->format)); @@ -205,9 +233,10 @@ struct ao *ao_init_best(struct mpv_global *global, struct encode_lavc_context *encode_lavc_ctx, int samplerate, int format, struct mp_chmap channels) { + struct MPOpts *opts = global->opts; struct mp_log *log = mp_log_new(NULL, global->log, "ao"); struct ao *ao = NULL; - struct m_obj_settings *ao_list = global->opts->audio_driver_list; + struct m_obj_settings *ao_list = opts->audio_driver_list; if (ao_list && ao_list[0].name) { for (int n = 0; ao_list[n].name; n++) { if (strlen(ao_list[n].name) == 0) @@ -224,15 +253,21 @@ struct ao *ao_init_best(struct mpv_global *global, } goto done; } -autoprobe: +autoprobe: ; // now try the rest... + bool matched_dev = false; for (int i = 0; audio_out_drivers[i]; i++) { + char *name = (char *)audio_out_drivers[i]->name; + if (!match_ao_driver(name, opts->audio_device, NULL)) + continue; + matched_dev = true; ao = ao_create(true, global, input_ctx, encode_lavc_ctx, - samplerate, format, channels, - (char *)audio_out_drivers[i]->name, NULL); + samplerate, format, channels, name, NULL); if (ao) goto done; } + if (!matched_dev) + mp_err(log, "--audio-device option refers to missing output driver.\n"); done: talloc_free(log); return ao; @@ -352,3 +387,39 @@ bool ao_untimed(struct ao *ao) { return ao->untimed; } + +struct ao_device_list *ao_get_device_list(void) +{ + struct ao_device_list *list = talloc_zero(NULL, struct ao_device_list); + for (int n = 0; audio_out_drivers[n]; n++) { + const struct ao_driver *d = audio_out_drivers[n]; + int num = list->num_devices; + if (d->list_devs) + d->list_devs(d, list); + // Add at least a default entry + if (list->num_devices == num) + ao_device_list_add(list, d, &(struct ao_device_desc){"", ""}); + } + return list; +} + +void ao_device_list_add(struct ao_device_list *list, const struct ao_driver *d, + struct ao_device_desc *e) +{ + struct ao_device_desc c = *e; + c.name = c.name[0] ? talloc_asprintf(list, "%s/%s", d->name, c.name) + : talloc_strdup(list, d->name); + c.desc = talloc_strdup(list, c.desc); + MP_TARRAY_APPEND(list, list->devices, list->num_devices, c); +} + +void ao_print_devices(struct mp_log *log) +{ + struct ao_device_list *list = ao_get_device_list(); + 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); +} diff --git a/audio/out/ao.h b/audio/out/ao.h index 7badf90aef..32b6611afc 100644 --- a/audio/out/ao.h +++ b/audio/out/ao.h @@ -51,6 +51,16 @@ typedef struct ao_control_vol { float right; } ao_control_vol_t; +struct ao_device_desc { + char *name; // symbolic name; will be set on ao->device + char *desc; // verbose human readable name +}; + +struct ao_device_list { + struct ao_device_desc *devices; + int num_devices; +}; + struct ao; struct mpv_global; struct input_ctx; @@ -76,4 +86,7 @@ void ao_resume(struct ao *ao); void ao_drain(struct ao *ao); bool ao_eof_reached(struct ao *ao); +struct ao_device_list *ao_get_device_list(void); +void ao_print_devices(struct mp_log *log); + #endif /* MPLAYER_AUDIO_OUT_H */ diff --git a/audio/out/internal.h b/audio/out/internal.h index a17aa10f02..399a8ee8b4 100644 --- a/audio/out/internal.h +++ b/audio/out/internal.h @@ -50,6 +50,11 @@ struct ao { struct input_ctx *input_ctx; struct mp_log *log; // Using e.g. "[ao/coreaudio]" as prefix + // The device as selected by the user, usually using ao_device_desc.name + // from an entry from the list returned by driver->list_devices. If the + // default device should be used, this is set to NULL. + char *device; + int buffer; double def_buffer; void *api_priv; @@ -147,6 +152,11 @@ struct ao_driver { // In combination with wait(). Lock may or may not be held. void (*wakeup)(struct ao *ao); + // 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). + void (*list_devs)(const struct ao_driver *d, struct ao_device_list *list); + // For option parsing (see vo.h) int priv_size; const void *priv_defaults; @@ -167,4 +177,8 @@ bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s, bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s, struct mp_chmap *map, int num); +// Add a deep copy of e to the list. +void ao_device_list_add(struct ao_device_list *list, const struct ao_driver *d, + struct ao_device_desc *e); + #endif -- cgit v1.2.3