diff options
author | Martin Herkt <lachs0r@srsfckn.biz> | 2016-10-19 19:09:00 +0200 |
---|---|---|
committer | Martin Herkt <lachs0r@srsfckn.biz> | 2016-10-19 19:09:00 +0200 |
commit | 5d74fa7dc0fa1617d1d11b69769423b1387ec785 (patch) | |
tree | 882752015b3500c91b8ab3a4d669696f23feb817 /audio | |
parent | c226bc7616ab2ae9de6172660e9cf727f07dc374 (diff) | |
parent | a2fa0d0b68ce200598122145254f56ae1adff91e (diff) | |
download | mpv-5d74fa7dc0fa1617d1d11b69769423b1387ec785.tar.bz2 mpv-5d74fa7dc0fa1617d1d11b69769423b1387ec785.tar.xz |
Merge branch 'master' into release/current
Diffstat (limited to 'audio')
-rw-r--r-- | audio/decode/ad_lavc.c | 8 | ||||
-rw-r--r-- | audio/decode/dec_audio.c | 6 | ||||
-rw-r--r-- | audio/filter/af.c | 8 | ||||
-rw-r--r-- | audio/filter/af_pan.c | 244 | ||||
-rw-r--r-- | audio/filter/af_rubberband.c | 26 | ||||
-rw-r--r-- | audio/out/ao.c | 56 | ||||
-rw-r--r-- | audio/out/ao.h | 7 | ||||
-rw-r--r-- | audio/out/ao_alsa.c | 98 | ||||
-rw-r--r-- | audio/out/ao_coreaudio.c | 6 | ||||
-rw-r--r-- | audio/out/ao_coreaudio_exclusive.c | 2 | ||||
-rw-r--r-- | audio/out/ao_jack.c | 64 | ||||
-rw-r--r-- | audio/out/ao_null.c | 1 | ||||
-rw-r--r-- | audio/out/ao_openal.c | 20 | ||||
-rw-r--r-- | audio/out/ao_opensles.c | 1 | ||||
-rw-r--r-- | audio/out/ao_oss.c | 3 | ||||
-rw-r--r-- | audio/out/ao_pcm.c | 3 | ||||
-rw-r--r-- | audio/out/ao_pulse.c | 3 | ||||
-rw-r--r-- | audio/out/ao_rsound.c | 7 | ||||
-rw-r--r-- | audio/out/ao_sdl.c | 1 | ||||
-rw-r--r-- | audio/out/ao_sndio.c | 4 | ||||
-rw-r--r-- | audio/out/ao_wasapi.c | 8 | ||||
-rw-r--r-- | audio/out/ao_wasapi.h | 2 | ||||
-rw-r--r-- | audio/out/ao_wasapi_changenotify.c | 1 | ||||
-rw-r--r-- | audio/out/ao_wasapi_utils.c | 1 | ||||
-rw-r--r-- | audio/out/internal.h | 11 | ||||
-rw-r--r-- | audio/out/pull.c | 4 | ||||
-rw-r--r-- | audio/out/push.c | 8 |
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 |