summaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorMartin Herkt <lachs0r@srsfckn.biz>2016-10-19 19:09:00 +0200
committerMartin Herkt <lachs0r@srsfckn.biz>2016-10-19 19:09:00 +0200
commit5d74fa7dc0fa1617d1d11b69769423b1387ec785 (patch)
tree882752015b3500c91b8ab3a4d669696f23feb817 /audio
parentc226bc7616ab2ae9de6172660e9cf727f07dc374 (diff)
parenta2fa0d0b68ce200598122145254f56ae1adff91e (diff)
downloadmpv-5d74fa7dc0fa1617d1d11b69769423b1387ec785.tar.bz2
mpv-5d74fa7dc0fa1617d1d11b69769423b1387ec785.tar.xz
Merge branch 'master' into release/current
Diffstat (limited to 'audio')
-rw-r--r--audio/decode/ad_lavc.c8
-rw-r--r--audio/decode/dec_audio.c6
-rw-r--r--audio/filter/af.c8
-rw-r--r--audio/filter/af_pan.c244
-rw-r--r--audio/filter/af_rubberband.c26
-rw-r--r--audio/out/ao.c56
-rw-r--r--audio/out/ao.h7
-rw-r--r--audio/out/ao_alsa.c98
-rw-r--r--audio/out/ao_coreaudio.c6
-rw-r--r--audio/out/ao_coreaudio_exclusive.c2
-rw-r--r--audio/out/ao_jack.c64
-rw-r--r--audio/out/ao_null.c1
-rw-r--r--audio/out/ao_openal.c20
-rw-r--r--audio/out/ao_opensles.c1
-rw-r--r--audio/out/ao_oss.c3
-rw-r--r--audio/out/ao_pcm.c3
-rw-r--r--audio/out/ao_pulse.c3
-rw-r--r--audio/out/ao_rsound.c7
-rw-r--r--audio/out/ao_sdl.c1
-rw-r--r--audio/out/ao_sndio.c4
-rw-r--r--audio/out/ao_wasapi.c8
-rw-r--r--audio/out/ao_wasapi.h2
-rw-r--r--audio/out/ao_wasapi_changenotify.c1
-rw-r--r--audio/out/ao_wasapi_utils.c1
-rw-r--r--audio/out/internal.h11
-rw-r--r--audio/out/pull.c4
-rw-r--r--audio/out/push.c8
27 files changed, 377 insertions, 226 deletions
diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c
index 78d0cec807..e28558414d 100644
--- a/audio/decode/ad_lavc.c
+++ b/audio/decode/ad_lavc.c
@@ -103,7 +103,10 @@ static int init(struct dec_audio *da, const char *decoder)
lavc_context->refcounted_frames = 1;
lavc_context->codec_type = AVMEDIA_TYPE_AUDIO;
lavc_context->codec_id = lavc_codec->id;
- lavc_context->time_base = ctx->codec_timebase;
+
+#if LIBAVCODEC_VERSION_MICRO >= 100
+ lavc_context->pkt_timebase = ctx->codec_timebase;
+#endif
if (opts->downmix && mpopts->audio_output_channels.num_chmaps == 1) {
lavc_context->request_channel_layout =
@@ -242,7 +245,8 @@ static int decode_packet(struct dec_audio *da, struct demux_packet *mpkt,
if (!got_frame)
return 0;
- double out_pts = mp_pts_from_av(priv->avframe->pkt_pts, &priv->codec_timebase);
+ double out_pts = mp_pts_from_av(MP_AVFRAME_DEC_PTS(priv->avframe),
+ &priv->codec_timebase);
struct mp_audio *mpframe = mp_audio_from_avframe(priv->avframe);
if (!mpframe)
diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c
index d455770a74..3028d9988e 100644
--- a/audio/decode/dec_audio.c
+++ b/audio/decode/dec_audio.c
@@ -180,10 +180,14 @@ static void fix_audio_pts(struct dec_audio *da)
if (da->current_frame->pts != MP_NOPTS_VALUE) {
double newpts = da->current_frame->pts;
+
+ if (da->pts != MP_NOPTS_VALUE)
+ MP_STATS(da, "value %f audio-pts-err", da->pts - newpts);
+
// Keep the interpolated timestamp if it doesn't deviate more
// than 1 ms from the real one. (MKV rounded timestamps.)
if (da->pts == MP_NOPTS_VALUE || fabs(da->pts - newpts) > 0.001)
- da->pts = da->current_frame->pts;
+ da->pts = newpts;
}
if (da->pts == MP_NOPTS_VALUE && da->header->missing_timestamps)
diff --git a/audio/filter/af.c b/audio/filter/af.c
index f380459747..e6f19b3e37 100644
--- a/audio/filter/af.c
+++ b/audio/filter/af.c
@@ -166,10 +166,10 @@ static struct af_instance *af_create(struct af_stream *s, char *name,
.replaygain_data = s->replaygain_data,
.out_pool = mp_audio_pool_create(af),
};
- struct m_config *config = m_config_from_obj_desc(af, s->log, &desc);
- if (m_config_apply_defaults(config, name, s->opts->af_defs) < 0)
- goto error;
- if (m_config_set_obj_params(config, args) < 0)
+ struct m_config *config =
+ m_config_from_obj_desc_and_args(af, s->log, NULL, &desc,
+ name, s->opts->af_defs, args);
+ if (!config)
goto error;
af->priv = config->optstruct;
diff --git a/audio/filter/af_pan.c b/audio/filter/af_pan.c
index 900d10cc74..de2adf790c 100644
--- a/audio/filter/af_pan.c
+++ b/audio/filter/af_pan.c
@@ -28,12 +28,11 @@
#include "af.h"
// Data for specific instances of this filter
-typedef struct af_pan_s
-{
- int nch; // Number of output channels; zero means same as input
- float level[AF_NCH][AF_NCH]; // Gain level for each channel
- char *matrixstr;
-}af_pan_t;
+typedef struct af_pan_s {
+ int nch; // Number of output channels; zero means same as input
+ float level[AF_NCH][AF_NCH]; // Gain level for each channel
+ char *matrixstr;
+} af_pan_t;
static void set_channels(struct mp_audio *mpa, int num)
{
@@ -44,136 +43,151 @@ static void set_channels(struct mp_audio *mpa, int num)
mp_audio_set_channels(mpa, &map);
}
-// Initialization and runtime control
-static int control(struct af_instance* af, int cmd, void* arg)
+static void parse_matrix(struct af_instance *af, const char *cp)
{
- af_pan_t* s = af->priv;
-
- switch(cmd){
- case AF_CONTROL_REINIT:
- // Sanity check
- if(!arg) return AF_ERROR;
+ af_pan_t *s = af->priv;
+ int j = 0, k = 0, n;
+ while (*cp && k < AF_NCH) {
+ sscanf(cp, "%f%n" , &s->level[j][k], &n);
+ MP_VERBOSE(af, "Pan level from channel %i to"
+ " channel %i = %f\n", k, j, s->level[j][k]);
+ cp = &cp[n];
+ j++;
+ if (j >= s->nch) {
+ j = 0;
+ k++;
+ }
+ if (*cp != ',')
+ break;
+ cp++;
+ }
- af->data->rate = ((struct mp_audio*)arg)->rate;
- mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
- set_channels(af->data, s->nch ? s->nch: ((struct mp_audio*)arg)->nch);
+}
- if((af->data->format != ((struct mp_audio*)arg)->format) ||
- (af->data->bps != ((struct mp_audio*)arg)->bps)){
- mp_audio_set_format((struct mp_audio*)arg, af->data->format);
- return AF_FALSE;
+// Initialization and runtime control
+static int control(struct af_instance *af, int cmd, void *arg)
+{
+ af_pan_t* s = af->priv;
+
+ switch(cmd){
+ case AF_CONTROL_REINIT:
+ // Sanity check
+ if (!arg)
+ return AF_ERROR;
+
+ af->data->rate = ((struct mp_audio*)arg)->rate;
+ mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
+ set_channels(af->data, s->nch ? s->nch : ((struct mp_audio*)arg)->nch);
+
+ if ((af->data->format != ((struct mp_audio*)arg)->format) ||
+ (af->data->bps != ((struct mp_audio*)arg)->bps)) {
+ mp_audio_set_format((struct mp_audio*)arg, af->data->format);
+ return AF_FALSE;
+ }
+ return AF_OK;
+ case AF_CONTROL_SET_PAN_LEVEL: {
+ int i;
+ int ch = ((af_control_ext_t*)arg)->ch;
+ float *level = ((af_control_ext_t*)arg)->arg;
+ if (ch >= AF_NCH)
+ return AF_FALSE;
+ for (i = 0; i < AF_NCH; i++)
+ s->level[ch][i] = level[i];
+ return AF_OK;
}
- return AF_OK;
- case AF_CONTROL_SET_PAN_LEVEL:{
- int i;
- int ch = ((af_control_ext_t*)arg)->ch;
- float* level = ((af_control_ext_t*)arg)->arg;
- if (ch >= AF_NCH)
- return AF_FALSE;
- for(i=0;i<AF_NCH;i++)
- s->level[ch][i] = level[i];
- return AF_OK;
- }
- case AF_CONTROL_SET_PAN_NOUT:
- // Reinit must be called after this function has been called
-
- // Sanity check
- if(((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH){
- MP_ERR(af, "The number of output channels must be"
- " between 1 and %i. Current value is %i\n",AF_NCH,((int*)arg)[0]);
- return AF_ERROR;
+ case AF_CONTROL_SET_PAN_NOUT:
+ // Reinit must be called after this function has been called
+ // Sanity check
+ if (((int*)arg)[0] <= 0 || ((int*)arg)[0] > AF_NCH) {
+ MP_ERR(af, "The number of output channels must be"
+ " between 1 and %i. Current value is %i\n",
+ AF_NCH, ((int*)arg)[0]);
+ return AF_ERROR;
+ }
+ s->nch = ((int*)arg)[0];
+ return AF_OK;
+ case AF_CONTROL_SET_PAN_BALANCE: {
+ float val = *(float*)arg;
+ if (s->nch)
+ return AF_ERROR;
+ if (af->data->nch >= 2) {
+ s->level[0][0] = MPMIN(1.f, 1.f - val);
+ s->level[0][1] = MPMAX(0.f, val);
+ s->level[1][0] = MPMAX(0.f, -val);
+ s->level[1][1] = MPMIN(1.f, 1.f + val);
+ }
+ return AF_OK;
}
- s->nch=((int*)arg)[0];
- return AF_OK;
- case AF_CONTROL_SET_PAN_BALANCE:{
- float val = *(float*)arg;
- if (s->nch)
- return AF_ERROR;
- if (af->data->nch >= 2) {
- s->level[0][0] = MPMIN(1.f, 1.f - val);
- s->level[0][1] = MPMAX(0.f, val);
- s->level[1][0] = MPMAX(0.f, -val);
- s->level[1][1] = MPMIN(1.f, 1.f + val);
+ case AF_CONTROL_GET_PAN_BALANCE:
+ if (s->nch)
+ return AF_ERROR;
+ *(float*)arg = s->level[0][1] - s->level[1][0];
+ return AF_OK;
+ case AF_CONTROL_COMMAND: {
+ char **args = arg;
+ if (!strcmp(args[0], "set-matrix")) {
+ parse_matrix(af, args[1]);
+ return CONTROL_OK;
+ } else {
+ return CONTROL_ERROR;
+ }
}
- return AF_OK;
- }
- case AF_CONTROL_GET_PAN_BALANCE:
- if (s->nch)
- return AF_ERROR;
- *(float*)arg = s->level[0][1] - s->level[1][0];
- return AF_OK;
- }
- return AF_UNKNOWN;
+ }
+ return AF_UNKNOWN;
}
static int filter_frame(struct af_instance *af, struct mp_audio *c)
{
- if (!c)
- return 0;
- struct mp_audio *l = mp_audio_pool_get(af->out_pool, &af->fmt_out, c->samples);
- if (!l) {
- talloc_free(c);
- return -1;
- }
- mp_audio_copy_attributes(l, c);
-
- af_pan_t* s = af->priv; // Setup for this instance
- float* in = c->planes[0]; // Input audio data
- float* out = NULL; // Output audio data
- float* end = in+c->samples*c->nch; // End of loop
- int nchi = c->nch; // Number of input channels
- int ncho = l->nch; // Number of output channels
- register int j,k;
-
- out = l->planes[0];
- // Execute panning
- // FIXME: Too slow
- while(in < end){
- for(j=0;j<ncho;j++){
- register float x = 0.0;
- register float* tin = in;
- for(k=0;k<nchi;k++)
- x += tin[k] * s->level[j][k];
- out[j] = x;
+ if (!c)
+ return 0;
+ struct mp_audio *l = mp_audio_pool_get(af->out_pool, &af->fmt_out, c->samples);
+ if (!l) {
+ talloc_free(c);
+ return -1;
+ }
+ mp_audio_copy_attributes(l, c);
+
+ af_pan_t* s = af->priv; // Setup for this instance
+ float *in = c->planes[0]; // Input audio data
+ float *out = NULL; // Output audio data
+ float *end = in+c->samples * c->nch; // End of loop
+ int nchi = c->nch; // Number of input channels
+ int ncho = l->nch; // Number of output channels
+ register int j, k;
+
+ out = l->planes[0];
+ // Execute panning
+ // FIXME: Too slow
+ while (in < end) {
+ for (j = 0; j < ncho; j++) {
+ register float x = 0.0;
+ register float *tin = in;
+ for (k = 0; k < nchi; k++)
+ x += tin[k] * s->level[j][k];
+ out[j] = x;
+ }
+ out += ncho;
+ in += nchi;
}
- out+= ncho;
- in+= nchi;
- }
- talloc_free(c);
- af_add_output_frame(af, l);
- return 0;
+ talloc_free(c);
+ af_add_output_frame(af, l);
+ return 0;
}
// Allocate memory and set function pointers
-static int af_open(struct af_instance* af){
- af->control=control;
+static int af_open(struct af_instance *af)
+{
+ af->control = control;
af->filter_frame = filter_frame;
af_pan_t *s = af->priv;
- int n = 0;
- int j,k;
-
int nch = s->nch;
- if(nch && AF_OK != control(af,AF_CONTROL_SET_PAN_NOUT, &nch))
+ if (nch && AF_OK != control(af, AF_CONTROL_SET_PAN_NOUT, &nch))
return AF_ERROR;
// Read pan values
- char *cp = s->matrixstr;
- j = 0; k = 0;
- while(cp && k < AF_NCH){
- sscanf(cp, "%f%n" , &s->level[j][k], &n);
- MP_VERBOSE(af, "Pan level from channel %i to"
- " channel %i = %f\n",k,j,s->level[j][k]);
- cp =&cp[n];
- j++;
- if(j>=nch){
- j = 0;
- k++;
- }
- if (*cp != ',')
- break;
- cp++;
- }
+ if (s->matrixstr)
+ parse_matrix(af, s->matrixstr);
return AF_OK;
}
diff --git a/audio/filter/af_rubberband.c b/audio/filter/af_rubberband.c
index 48bb510679..a4deb3d48c 100644
--- a/audio/filter/af_rubberband.c
+++ b/audio/filter/af_rubberband.c
@@ -26,6 +26,7 @@
struct priv {
RubberBandState rubber;
double speed;
+ double pitch;
struct mp_audio *pending;
bool needs_reset;
// Estimate how much librubberband has buffered internally.
@@ -44,6 +45,14 @@ static void update_speed(struct af_instance *af, double new_speed)
rubberband_set_time_ratio(p->rubber, 1.0 / p->speed);
}
+static void update_pitch(struct af_instance *af, double new_pitch)
+{
+ struct priv *p = af->priv;
+
+ p->pitch = new_pitch;
+ rubberband_set_pitch_scale(p->rubber, p->pitch);
+}
+
static int control(struct af_instance *af, int cmd, void *arg)
{
struct priv *p = af->priv;
@@ -72,6 +81,7 @@ static int control(struct af_instance *af, int cmd, void *arg)
}
update_speed(af, p->speed);
+ update_pitch(af, p->pitch);
control(af, AF_CONTROL_RESET, NULL);
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
@@ -87,6 +97,19 @@ static int control(struct af_instance *af, int cmd, void *arg)
p->pending = NULL;
p->rubber_delay = 0;
return AF_OK;
+ case AF_CONTROL_COMMAND: {
+ char **args = arg;
+ if (!strcmp(args[0], "set-pitch")) {
+ char *endptr;
+ double pitch = strtod(args[1], &endptr);
+ if (*endptr || pitch < 0.01 || pitch > 100.0)
+ return CONTROL_ERROR;
+ update_pitch(af, pitch);
+ return CONTROL_OK;
+ } else {
+ return CONTROL_ERROR;
+ }
+ }
}
return AF_UNKNOWN;
}
@@ -187,9 +210,11 @@ const struct af_info af_info_rubberband = {
.priv_size = sizeof(struct priv),
.priv_defaults = &(const struct priv) {
.speed = 1.0,
+ .pitch = 1.0,
.opt_pitch = RubberBandOptionPitchHighConsistency,
.opt_transients = RubberBandOptionTransientsMixed,
.opt_formant = RubberBandOptionFormantPreserved,
+ .opt_channels = RubberBandOptionChannelsTogether,
},
.options = (const struct m_option[]) {
OPT_CHOICE("transients", opt_transients, 0,
@@ -220,6 +245,7 @@ const struct af_info af_info_rubberband = {
OPT_CHOICE("channels", opt_channels, 0,
({"apart", RubberBandOptionChannelsApart},
{"together", RubberBandOptionChannelsTogether})),
+ OPT_DOUBLE("pitch-scale", pitch, M_OPT_RANGE, .min = 0.01, .max = 100),
{0}
},
};
diff --git a/audio/out/ao.c b/audio/out/ao.c
index cf66e0c64b..15c0f8139e 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -28,7 +28,6 @@
#include "audio/format.h"
#include "audio/audio.h"
-#include "input/input.h"
#include "options/options.h"
#include "options/m_config.h"
#include "common/msg.h"
@@ -109,6 +108,8 @@ static bool get_desc(struct m_obj_desc *dst, int index)
.priv_size = ao->priv_size,
.priv_defaults = ao->priv_defaults,
.options = ao->options,
+ .global_opts = ao->global_opts,
+ .legacy_prefix = ao->legacy_prefix,
.hidden = ao->encode,
.p = ao,
};
@@ -121,11 +122,15 @@ const struct m_obj_list ao_obj_list = {
.description = "audio outputs",
.allow_unknown_entries = true,
.allow_trailer = true,
+ .disallow_positional_parameters = true,
};
static struct ao *ao_alloc(bool probing, struct mpv_global *global,
- struct input_ctx *input_ctx, char *name, char **args)
+ void (*wakeup_cb)(void *ctx), void *wakeup_ctx,
+ char *name, char **args)
{
+ assert(wakeup_cb);
+
struct MPOpts *opts = global->opts;
struct mp_log *log = mp_log_new(NULL, global->log, "ao");
struct m_obj_desc desc;
@@ -139,15 +144,17 @@ static struct ao *ao_alloc(bool probing, struct mpv_global *global,
*ao = (struct ao) {
.driver = desc.p,
.probing = probing,
- .input_ctx = input_ctx,
+ .global = global,
+ .wakeup_cb = wakeup_cb,
+ .wakeup_ctx = wakeup_ctx,
.log = mp_log_new(ao, log, name),
.def_buffer = opts->audio_buffer,
.client_name = talloc_strdup(ao, opts->audio_client_name),
};
- struct m_config *config = m_config_from_obj_desc(ao, ao->log, &desc);
- if (m_config_apply_defaults(config, name, opts->ao_defs) < 0)
- goto error;
- if (m_config_set_obj_params(config, args) < 0)
+ struct m_config *config =
+ m_config_from_obj_desc_and_args(ao, ao->log, global, &desc,
+ name, opts->ao_defs, args);
+ if (!config)
goto error;
ao->priv = config->optstruct;
return ao;
@@ -157,12 +164,12 @@ error:
}
static struct ao *ao_init(bool probing, struct mpv_global *global,
- struct input_ctx *input_ctx,
+ void (*wakeup_cb)(void *ctx), void *wakeup_ctx,
struct encode_lavc_context *encode_lavc_ctx, int flags,
int samplerate, int format, struct mp_chmap channels,
char *dev, char *name, char **args)
{
- struct ao *ao = ao_alloc(probing, global, input_ctx, name, args);
+ struct ao *ao = ao_alloc(probing, global, wakeup_cb, wakeup_ctx, name, args);
if (!ao)
return NULL;
ao->samplerate = samplerate;
@@ -193,8 +200,9 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
snprintf(redirect, sizeof(redirect), "%s", ao->redirect);
snprintf(rdevice, sizeof(rdevice), "%s", ao->device ? ao->device : "");
talloc_free(ao);
- return ao_init(probing, global, input_ctx, encode_lavc_ctx, flags,
- samplerate, format, channels, rdevice, redirect, NULL);
+ return ao_init(probing, global, wakeup_cb, wakeup_ctx,
+ encode_lavc_ctx, flags, samplerate, format, channels,
+ rdevice, redirect, NULL);
}
goto fail;
}
@@ -244,7 +252,7 @@ static void split_ao_device(void *tmp, char *opt, char **out_ao, char **out_dev)
struct ao *ao_init_best(struct mpv_global *global,
int init_flags,
- struct input_ctx *input_ctx,
+ void (*wakeup_cb)(void *ctx), void *wakeup_ctx,
struct encode_lavc_context *encode_lavc_ctx,
int samplerate, int format, struct mp_chmap channels)
{
@@ -300,8 +308,8 @@ struct ao *ao_init_best(struct mpv_global *global,
dev = pref_dev;
mp_verbose(log, "Using preferred device '%s'\n", dev);
}
- ao = ao_init(probing, global, input_ctx, encode_lavc_ctx, init_flags,
- samplerate, format, channels, dev,
+ ao = ao_init(probing, global, wakeup_cb, wakeup_ctx, encode_lavc_ctx,
+ init_flags, samplerate, format, channels, dev,
entry->name, entry->attribs);
if (ao)
break;
@@ -405,8 +413,7 @@ int ao_query_and_reset_events(struct ao *ao, int events)
static void ao_add_events(struct ao *ao, int events)
{
atomic_fetch_or(&ao->events_, events);
- if (ao->input_ctx)
- mp_input_wakeup(ao->input_ctx);
+ ao->wakeup_cb(ao->wakeup_ctx);
}
// Request that the player core destroys and recreates the AO. Fully thread-safe.
@@ -490,7 +497,8 @@ bool ao_untimed(struct ao *ao)
struct ao_hotplug {
struct mpv_global *global;
- struct input_ctx *input_ctx;
+ void (*wakeup_cb)(void *ctx);
+ void *wakeup_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.
@@ -502,12 +510,14 @@ struct ao_hotplug {
};
struct ao_hotplug *ao_hotplug_create(struct mpv_global *global,
- struct input_ctx *input_ctx)
+ void (*wakeup_cb)(void *ctx),
+ void *wakeup_ctx)
{
struct ao_hotplug *hp = talloc_ptrtype(NULL, hp);
*hp = (struct ao_hotplug){
.global = global,
- .input_ctx = input_ctx,
+ .wakeup_cb = wakeup_cb,
+ .wakeup_ctx = wakeup_ctx,
.needs_update = true,
};
return hp;
@@ -560,7 +570,7 @@ struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp)
if (d == &audio_out_null)
break; // don't add unsafe/special entries
- struct ao *ao = ao_alloc(true, hp->global, hp->input_ctx,
+ struct ao *ao = ao_alloc(true, hp->global, hp->wakeup_cb, hp->wakeup_ctx,
(char *)d->name, NULL);
if (!ao)
continue;
@@ -601,9 +611,13 @@ void ao_hotplug_destroy(struct ao_hotplug *hp)
talloc_free(hp);
}
+static void dummy_wakeup(void *ctx)
+{
+}
+
void ao_print_devices(struct mpv_global *global, struct mp_log *log)
{
- struct ao_hotplug *hp = ao_hotplug_create(global, NULL);
+ struct ao_hotplug *hp = ao_hotplug_create(global, dummy_wakeup, 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++) {
diff --git a/audio/out/ao.h b/audio/out/ao.h
index 3b187e7355..4a4d433c2b 100644
--- a/audio/out/ao.h
+++ b/audio/out/ao.h
@@ -58,6 +58,8 @@ enum {
AO_INIT_SAFE_MULTICHANNEL_ONLY = 1 << 1,
// Stream silence as long as no audio is playing.
AO_INIT_STREAM_SILENCE = 1 << 2,
+ // Force exclusive mode, i.e. lock out the system mixer.
+ AO_INIT_EXCLUSIVE = 1 << 3,
};
typedef struct ao_control_vol {
@@ -83,7 +85,7 @@ struct mp_audio;
struct ao *ao_init_best(struct mpv_global *global,
int init_flags,
- struct input_ctx *input_ctx,
+ void (*wakeup_cb)(void *ctx), void *wakeup_ctx,
struct encode_lavc_context *encode_lavc_ctx,
int samplerate, int format, struct mp_chmap channels);
void ao_uninit(struct ao *ao);
@@ -106,7 +108,8 @@ 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 (*wakeup_cb)(void *ctx),
+ void *wakeup_ctx);
void ao_hotplug_destroy(struct ao_hotplug *hp);
bool ao_hotplug_check_update(struct ao_hotplug *hp);
const char *ao_hotplug_get_detected_device(struct ao_hotplug *hp);
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index c6248d302b..90250c9855 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -35,6 +35,7 @@
#include "config.h"
#include "options/options.h"
+#include "options/m_config.h"
#include "options/m_option.h"
#include "common/msg.h"
#include "osdep/endian.h"
@@ -48,6 +49,37 @@
#include "internal.h"
#include "audio/format.h"
+struct ao_alsa_opts {
+ char *device;
+ char *mixer_device;
+ char *mixer_name;
+ int mixer_index;
+ int resample;
+ int ni;
+ int ignore_chmap;
+};
+
+#define OPT_BASE_STRUCT struct ao_alsa_opts
+static const struct m_sub_options ao_alsa_conf = {
+ .opts = (const struct m_option[]) {
+ OPT_STRING("alsa-device", device, 0, DEVICE_OPT_DEPRECATION),
+ OPT_FLAG("alsa-resample", resample, 0),
+ OPT_STRING("alsa-mixer-device", mixer_device, 0),
+ OPT_STRING("alsa-mixer-name", mixer_name, 0),
+ OPT_INTRANGE("alsa-mixer-index", mixer_index, 0, 0, 99),
+ OPT_FLAG("alsa-non-interleaved", ni, 0),
+ OPT_FLAG("alsa-ignore-chmap", ignore_chmap, 0),
+ {0}
+ },
+ .defaults = &(const struct ao_alsa_opts) {
+ .mixer_device = "default",
+ .mixer_name = "Master",
+ .mixer_index = 0,
+ .ni = 0,
+ },
+ .size = sizeof(struct ao_alsa_opts),
+};
+
struct priv {
snd_pcm_t *alsa;
bool device_lost;
@@ -61,13 +93,7 @@ struct priv {
snd_output_t *output;
- char *cfg_device;
- char *cfg_mixer_device;
- char *cfg_mixer_name;
- int cfg_mixer_index;
- int cfg_resample;
- int cfg_ni;
- int cfg_ignore_chmap;
+ struct ao_alsa_opts *opts;
};
#define BUFFER_TIME 250000 // 250ms
@@ -125,13 +151,13 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
snd_mixer_selem_id_alloca(&sid);
- snd_mixer_selem_id_set_index(sid, p->cfg_mixer_index);
- snd_mixer_selem_id_set_name(sid, p->cfg_mixer_name);
+ snd_mixer_selem_id_set_index(sid, p->opts->mixer_index);
+ snd_mixer_selem_id_set_name(sid, p->opts->mixer_name);
err = snd_mixer_open(&handle, 0);
CHECK_ALSA_ERROR("Mixer open error");
- err = snd_mixer_attach(handle, p->cfg_mixer_device);
+ err = snd_mixer_attach(handle, p->opts->mixer_device);
CHECK_ALSA_ERROR("Mixer attach error");
err = snd_mixer_selem_register(handle, NULL, NULL);
@@ -447,7 +473,7 @@ static int set_chmap(struct ao *ao, struct mp_chmap *dev_chmap, int num_channels
MP_VERBOSE(ao, "which we understand as: %s\n", mp_chmap_to_str(&chmap));
- if (p->cfg_ignore_chmap) {
+ if (p->opts->ignore_chmap) {
MP_VERBOSE(ao, "user set ignore-chmap; ignoring the channel map.\n");
} else if (af_fmt_is_spdif(ao->format)) {
MP_VERBOSE(ao, "using spdif passthrough; ignoring the channel map.\n");
@@ -564,11 +590,16 @@ static int try_open_device(struct ao *ao, const char *device, int mode)
bstr dev;
bstr_split_tok(bstr0(device), ":", &dev, &(bstr){0});
if (bstr_equals0(dev, "default")) {
- ac3_device = append_params(tmp, "iec958", params);
- MP_VERBOSE(ao, "got error %d; opening iec fallback device '%s'\n",
- err, ac3_device);
- err = snd_pcm_open
- (&p->alsa, ac3_device, SND_PCM_STREAM_PLAYBACK, mode);
+ const char *const fallbacks[] = {"hdmi", "iec958", NULL};
+ for (int n = 0; fallbacks[n]; n++) {
+ char *ndev = append_params(tmp, fallbacks[n], params);
+ MP_VERBOSE(ao, "got error %d; opening iec fallback "
+ "device '%s'\n", err, ndev);
+ err = snd_pcm_open
+ (&p->alsa, ndev, SND_PCM_STREAM_PLAYBACK, mode);
+ if (err >= 0)
+ break;
+ }
}
}
talloc_free(tmp);
@@ -615,8 +646,8 @@ static int init_device(struct ao *ao, int mode)
const char *device = "default";
if (ao->device)
device = ao->device;
- if (p->cfg_device && p->cfg_device[0])
- device = p->cfg_device;
+ if (p->opts->device && p->opts->device[0])
+ device = p->opts->device;
err = try_open_device(ao, device, mode);
CHECK_ALSA_ERROR("Playback open error");
@@ -641,7 +672,7 @@ static int init_device(struct ao *ao, int mode)
// Some ALSA drivers have broken delay reporting, so disable the ALSA
// resampling plugin by default.
- if (!p->cfg_resample) {
+ if (!p->opts->resample) {
err = snd_pcm_hw_params_set_rate_resample(p->alsa, alsa_hwparams, 0);
CHECK_ALSA_ERROR