summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2014-10-09 21:21:31 +0200
committerwm4 <wm4@nowhere>2014-10-09 21:21:31 +0200
commit35649a990a5d468b6e9539c9e362d1e5a351c9c4 (patch)
tree31b1f76c5c219cfeaa0360c09122bfc45c4b90b7
parent89890192711e1478e7626df6f8eb2a7ecb117342 (diff)
downloadmpv-35649a990a5d468b6e9539c9e362d1e5a351c9c4.tar.bz2
mpv-35649a990a5d468b6e9539c9e362d1e5a351c9c4.tar.xz
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.
-rw-r--r--DOCS/man/options.rst17
-rw-r--r--audio/out/ao.c83
-rw-r--r--audio/out/ao.h13
-rw-r--r--audio/out/internal.h14
-rw-r--r--options/options.c2
-rw-r--r--options/options.h1
-rw-r--r--player/main.c4
7 files changed, 128 insertions, 6 deletions
diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst
index a59e1b8060..1d0efacb79 100644
--- a/DOCS/man/options.rst
+++ b/DOCS/man/options.rst
@@ -763,6 +763,23 @@ Audio
inserts the ``scaletempo`` audio filter. For details, see audio filter
section.
+``--audio-device=<name>``
+ Use the given audio device. This consists of the audio output name, e.g.
+ ``alsa``, followed by ``/``, followed by the audio output specific device
+ name.
+
+ You can list audio devices with ``--audio-device=help``.
+
+ The default value for this option is ``auto``, which tries every audio
+ output in preference order with the default device.
+
+ Note that many AOs have a ``device`` sub-option, which overrides the
+ device selection of this option (but not the audio output selection).
+ Likewise, forcing an AO with ``--ao`` will override the audio output
+ selection of ``--audio-device`` (but not the device selecton).
+
+ Currently not implemented for most AOs.
+
``--ao=<driver1[:suboption1[=value]:...],driver2,...[,]>``
Specify a priority list of audio output drivers to be used. For
interactive use one would normally specify a single one to use, but in
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
diff --git a/options/options.c b/options/options.c
index af30a584b9..39b562ee80 100644
--- a/options/options.c
+++ b/options/options.c
@@ -350,6 +350,7 @@ const m_option_t mp_opts[] = {
OPT_SETTINGSLIST("vo-defaults", vo.vo_defs, 0, &vo_obj_list),
OPT_SETTINGSLIST("ao", audio_driver_list, 0, &ao_obj_list),
OPT_SETTINGSLIST("ao-defaults", ao_defs, 0, &ao_obj_list),
+ OPT_STRING("audio-device", audio_device, 0),
OPT_FLAG("fixed-vo", fixed_vo, CONF_GLOBAL),
OPT_FLAG("force-window", force_vo, CONF_GLOBAL),
OPT_FLAG("ontop", vo.ontop, M_OPT_FIXED),
@@ -562,6 +563,7 @@ const struct MPOpts mp_default_opts = {
.mixer_init_mute = -1,
.gapless_audio = -1,
.audio_buffer = 0.2,
+ .audio_device = "auto",
.vo = {
.video_driver_list = NULL,
.monitor_pixel_aspect = 1.0,
diff --git a/options/options.h b/options/options.h
index 08af7b7fc2..189df712fd 100644
--- a/options/options.h
+++ b/options/options.h
@@ -64,6 +64,7 @@ typedef struct MPOpts {
int auto_load_scripts;
struct m_obj_settings *audio_driver_list, *ao_defs;
+ char *audio_device;
int fixed_vo;
int force_vo;
int softvol;
diff --git a/player/main.c b/player/main.c
index af1d3427dc..bf14b25340 100644
--- a/player/main.c
+++ b/player/main.c
@@ -229,6 +229,10 @@ static bool handle_help_options(struct MPContext *mpctx)
MP_INFO(mpctx, "\n");
opt_exit = 1;
}
+ if (opts->audio_device && strcmp(opts->audio_device, "help") == 0) {
+ ao_print_devices(log);
+ opt_exit = 1;
+ }
#if HAVE_ENCODING
if (encode_lavc_showhelp(log, opts->encode_opts))
opt_exit = 1;