diff options
Diffstat (limited to 'audio/out')
32 files changed, 5663 insertions, 3540 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c index b699b64c5a..75fcbac6fa 100644 --- a/audio/out/ao.c +++ b/audio/out/ao.c @@ -18,6 +18,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <math.h> #include <assert.h> #include "mpv_talloc.h" @@ -26,19 +27,22 @@ #include "ao.h" #include "internal.h" #include "audio/format.h" -#include "audio/audio.h" #include "options/options.h" -#include "options/m_config.h" +#include "options/m_config_frontend.h" +#include "osdep/endian.h" #include "common/msg.h" #include "common/common.h" #include "common/global.h" extern const struct ao_driver audio_out_oss; +extern const struct ao_driver audio_out_audiotrack; extern const struct ao_driver audio_out_audiounit; extern const struct ao_driver audio_out_coreaudio; extern const struct ao_driver audio_out_coreaudio_exclusive; +extern const struct ao_driver audio_out_avfoundation; extern const struct ao_driver audio_out_rsound; +extern const struct ao_driver audio_out_pipewire; extern const struct ao_driver audio_out_sndio; extern const struct ao_driver audio_out_pulse; extern const struct ao_driver audio_out_jack; @@ -53,12 +57,21 @@ extern const struct ao_driver audio_out_sdl; static const struct ao_driver * const audio_out_drivers[] = { // native: +#if HAVE_ANDROID + &audio_out_audiotrack, +#endif #if HAVE_AUDIOUNIT &audio_out_audiounit, #endif #if HAVE_COREAUDIO &audio_out_coreaudio, #endif +#if HAVE_AVFOUNDATION + &audio_out_avfoundation, +#endif +#if HAVE_PIPEWIRE + &audio_out_pipewire, +#endif #if HAVE_PULSE &audio_out_pulse, #endif @@ -81,7 +94,7 @@ static const struct ao_driver * const audio_out_drivers[] = { #if HAVE_OPENSLES &audio_out_opensles, #endif -#if HAVE_SDL1 || HAVE_SDL2 +#if HAVE_SDL2_AUDIO &audio_out_sdl, #endif #if HAVE_SNDIO @@ -92,18 +105,12 @@ static const struct ao_driver * const audio_out_drivers[] = { &audio_out_coreaudio_exclusive, #endif &audio_out_pcm, -#if HAVE_ENCODING &audio_out_lavc, -#endif -#if HAVE_RSOUND - &audio_out_rsound, -#endif - NULL }; static bool get_desc(struct m_obj_desc *dst, int index) { - if (index >= MP_ARRAY_SIZE(audio_out_drivers) - 1) + if (index >= MP_ARRAY_SIZE(audio_out_drivers)) return false; const struct ao_driver *ao = audio_out_drivers[index]; *dst = (struct m_obj_desc) { @@ -121,22 +128,39 @@ static bool get_desc(struct m_obj_desc *dst, int index) } // For the ao option -const struct m_obj_list ao_obj_list = { +static const struct m_obj_list ao_obj_list = { .get_desc = get_desc, .description = "audio outputs", - .allow_unknown_entries = true, .allow_trailer = true, .disallow_positional_parameters = true, .use_global_options = true, }; +#define OPT_BASE_STRUCT struct ao_opts +const struct m_sub_options ao_conf = { + .opts = (const struct m_option[]) { + {"ao", OPT_SETTINGSLIST(audio_driver_list, &ao_obj_list), + .flags = UPDATE_AUDIO}, + {"audio-device", OPT_STRING(audio_device), .flags = UPDATE_AUDIO}, + {"audio-client-name", OPT_STRING(audio_client_name), .flags = UPDATE_AUDIO}, + {"audio-buffer", OPT_DOUBLE(audio_buffer), + .flags = UPDATE_AUDIO, M_RANGE(0, 10)}, + {0} + }, + .size = sizeof(OPT_BASE_STRUCT), + .defaults = &(const OPT_BASE_STRUCT){ + .audio_buffer = 0.2, + .audio_device = "auto", + .audio_client_name = "mpv", + }, +}; + static struct ao *ao_alloc(bool probing, struct mpv_global *global, void (*wakeup_cb)(void *ctx), void *wakeup_ctx, char *name) { assert(wakeup_cb); - 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))) { @@ -144,6 +168,7 @@ static struct ao *ao_alloc(bool probing, struct mpv_global *global, talloc_free(log); return NULL; }; + struct ao_opts *opts = mp_get_config_group(NULL, global, &ao_conf); struct ao *ao = talloc_ptrtype(NULL, ao); talloc_steal(ao, log); *ao = (struct ao) { @@ -156,9 +181,11 @@ static struct ao *ao_alloc(bool probing, struct mpv_global *global, .def_buffer = opts->audio_buffer, .client_name = talloc_strdup(ao, opts->audio_client_name), }; + talloc_free(opts); ao->priv = m_config_group_from_desc(ao, ao->log, global, &desc, name); if (!ao->priv) goto error; + ao_set_gain(ao, 1.0f); return ao; error: talloc_free(ao); @@ -187,14 +214,9 @@ static struct ao *ao_init(bool probing, struct mpv_global *global, af_fmt_to_str(ao->format)); ao->device = talloc_strdup(ao, dev); - - ao->api = ao->driver->play ? &ao_api_push : &ao_api_pull; - ao->api_priv = talloc_zero_size(ao, ao->api->priv_size); - assert(!ao->api->priv_defaults && !ao->api->options); - ao->stream_silence = flags & AO_INIT_STREAM_SILENCE; - ao->period_size = 1; + init_buffer_pre(ao); int r = ao->driver->init(ao); if (r < 0) { @@ -203,18 +225,14 @@ static struct ao *ao_init(bool probing, struct mpv_global *global, char redirect[80], rdevice[80]; snprintf(redirect, sizeof(redirect), "%s", ao->redirect); snprintf(rdevice, sizeof(rdevice), "%s", ao->device ? ao->device : ""); - talloc_free(ao); + ao_uninit(ao); return ao_init(probing, global, wakeup_cb, wakeup_ctx, encode_lavc_ctx, flags, samplerate, format, channels, rdevice, redirect); } goto fail; } - - if (ao->period_size < 1) { - MP_ERR(ao, "Invalid period size set.\n"); - goto fail; - } + ao->driver_initialized = true; ao->sstride = af_fmt_to_bytes(ao->format); ao->num_planes = 1; @@ -225,22 +243,25 @@ static struct ao *ao_init(bool probing, struct mpv_global *global, } ao->bps = ao->samplerate * ao->sstride; - if (!ao->device_buffer && ao->driver->get_space) - ao->device_buffer = ao->driver->get_space(ao); + if (ao->device_buffer <= 0 && ao->driver->write) { + MP_ERR(ao, "Device buffer size not set.\n"); + goto fail; + } if (ao->device_buffer) MP_VERBOSE(ao, "device buffer: %d samples.\n", ao->device_buffer); ao->buffer = MPMAX(ao->device_buffer, ao->def_buffer * ao->samplerate); + ao->buffer = MPMAX(ao->buffer, 1); int align = af_format_sample_alignment(ao->format); ao->buffer = (ao->buffer + align - 1) / align * align; MP_VERBOSE(ao, "using soft-buffer of %d samples.\n", ao->buffer); - if (ao->api->init(ao) < 0) + if (!init_buffer_post(ao)) goto fail; return ao; fail: - talloc_free(ao); + ao_uninit(ao); return NULL; } @@ -266,8 +287,8 @@ 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; void *tmp = talloc_new(NULL); + struct ao_opts *opts = mp_get_config_group(tmp, global, &ao_conf); struct mp_log *log = mp_log_new(tmp, global->log, "ao"); struct ao *ao = NULL; struct m_obj_settings *ao_list = NULL; @@ -295,7 +316,7 @@ struct ao *ao_init_best(struct mpv_global *global, } if (autoprobe) { - for (int n = 0; audio_out_drivers[n]; n++) { + for (int n = 0; n < MP_ARRAY_SIZE(audio_out_drivers); n++) { const struct ao_driver *driver = audio_out_drivers[n]; if (driver == &audio_out_null) break; @@ -334,95 +355,20 @@ struct ao *ao_init_best(struct mpv_global *global, return ao; } -// Uninitialize and destroy the AO. Remaining audio must be dropped. -void ao_uninit(struct ao *ao) -{ - if (ao) - ao->api->uninit(ao); - talloc_free(ao); -} - -// Queue the given audio data. Start playback if it hasn't started yet. Return -// the number of samples that was accepted (the core will try to queue the rest -// again later). Should never block. -// data: start pointer for each plane. If the audio data is packed, only -// data[0] is valid, otherwise there is a plane for each channel. -// samples: size of the audio data (see ao->sstride) -// flags: currently AOPLAY_FINAL_CHUNK can be set -int ao_play(struct ao *ao, void **data, int samples, int flags) -{ - return ao->api->play(ao, data, samples, flags); -} - -int ao_control(struct ao *ao, enum aocontrol cmd, void *arg) -{ - return ao->api->control ? ao->api->control(ao, cmd, arg) : CONTROL_UNKNOWN; -} - -// Return size of the buffered data in seconds. Can include the device latency. -// Basically, this returns how much data there is still to play, and how long -// it takes until the last sample in the buffer reaches the speakers. This is -// used for audio/video synchronization, so it's very important to implement -// this correctly. -double ao_get_delay(struct ao *ao) -{ - return ao->api->get_delay(ao); -} - -// Return free size of the internal audio buffer. This controls how much audio -// the core should decode and try to queue with ao_play(). -int ao_get_space(struct ao *ao) -{ - return ao->api->get_space(ao); -} - -// Stop playback and empty buffers. Essentially go back to the state after -// ao->init(). -void ao_reset(struct ao *ao) -{ - if (ao->api->reset) - ao->api->reset(ao); -} - -// Pause playback. Keep the current buffer. ao_get_delay() must return the -// same value as before pausing. -void ao_pause(struct ao *ao) -{ - if (ao->api->pause) - ao->api->pause(ao); -} - -// Resume playback. Play the remaining buffer. If the driver doesn't support -// pausing, it has to work around this and e.g. use ao_play_silence() to fill -// the lost audio. -void ao_resume(struct ao *ao) -{ - if (ao->api->resume) - ao->api->resume(ao); -} - -// Block until the current audio buffer has played completely. -void ao_drain(struct ao *ao) -{ - if (ao->api->drain) - ao->api->drain(ao); -} - -bool ao_eof_reached(struct ao *ao) -{ - return ao->api->get_eof ? ao->api->get_eof(ao) : true; -} - // Query the AO_EVENT_*s as requested by the events parameter, and return them. int ao_query_and_reset_events(struct ao *ao, int events) { return atomic_fetch_and(&ao->events_, ~(unsigned)events) & events; } -static void ao_add_events(struct ao *ao, int events) +// Returns events that were set by this calls. +int ao_add_events(struct ao *ao, int events) { - atomic_fetch_or(&ao->events_, events); - ao->wakeup_cb(ao->wakeup_ctx); + unsigned prev_events = atomic_fetch_or(&ao->events_, events); + unsigned new = events & ~prev_events; + if (new) + ao->wakeup_cb(ao->wakeup_ctx); + return new; } // Request that the player core destroys and recreates the AO. Fully thread-safe. @@ -479,12 +425,12 @@ bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s, // --- The following functions just return immutable information. -void ao_get_format(struct ao *ao, struct mp_audio *format) +void ao_get_format(struct ao *ao, + int *samplerate, int *format, struct mp_chmap *channels) { - *format = (struct mp_audio){0}; - mp_audio_set_format(format, ao->format); - mp_audio_set_channels(format, &ao->channels); - format->rate = ao->samplerate; + *samplerate = ao->samplerate; + *format = ao->format; + *channels = ao->channels; } const char *ao_get_name(struct ao *ao) @@ -510,8 +456,9 @@ struct ao_hotplug { 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. - // This is _not_ the same AO instance as used for playing audio. + // a single audio API providing all events. + // This is _not_ necessarily the same AO instance as used for playing + // audio. struct ao *ao; // cached struct ao_device_list *list; @@ -550,15 +497,9 @@ bool ao_hotplug_check_update(struct ao_hotplug *hp) return false; } -const char *ao_hotplug_get_detected_device(struct ao_hotplug *hp) -{ - if (!hp || !hp->ao) - return NULL; - return hp->ao->detected_device; -} - // 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 *ao_hotplug_get_device_list(struct ao_hotplug *hp, + struct ao *playback_ao) { if (hp->list && !hp->needs_update) return hp->list; @@ -570,7 +511,20 @@ struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp) 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++) { + // Try to use the same AO for hotplug handling as for playback. + // Different AOs may not agree and the playback one is the only one the + // user knows about and may even have configured explicitly. + if (!hp->ao && playback_ao && playback_ao->driver->hotplug_init) { + struct ao *ao = ao_alloc(true, hp->global, hp->wakeup_cb, hp->wakeup_ctx, + (char *)playback_ao->driver->name); + if (playback_ao->driver->hotplug_init(ao) >= 0) { + hp->ao = ao; + } else { + talloc_free(ao); + } + } + + for (int n = 0; n < MP_ARRAY_SIZE(audio_out_drivers); n++) { const struct ao_driver *d = audio_out_drivers[n]; if (d == &audio_out_null) break; // don't add unsafe/special entries @@ -581,10 +535,13 @@ struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp) continue; 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); + if (ao->driver->hotplug_init(ao) >= 0) { + get_devices(ao, list); + if (hp->ao) + ao->driver->hotplug_uninit(ao); + else + hp->ao = ao; // keep this one + } } else { get_devices(ao, list); } @@ -633,10 +590,11 @@ static void dummy_wakeup(void *ctx) { } -void ao_print_devices(struct mpv_global *global, struct mp_log *log) +void ao_print_devices(struct mpv_global *global, struct mp_log *log, + struct ao *playback_ao) { struct ao_hotplug *hp = ao_hotplug_create(global, dummy_wakeup, NULL); - struct ao_device_list *list = ao_hotplug_get_device_list(hp); + struct ao_device_list *list = ao_hotplug_get_device_list(hp, playback_ao); 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]; @@ -644,3 +602,122 @@ void ao_print_devices(struct mpv_global *global, struct mp_log *log) } ao_hotplug_destroy(hp); } + +void ao_set_gain(struct ao *ao, float gain) +{ + atomic_store(&ao->gain, gain); +} + +#define MUL_GAIN_i(d, num_samples, gain, low, center, high) \ + for (int n = 0; n < (num_samples); n++) \ + (d)[n] = MPCLAMP( \ + ((((int64_t)((d)[n]) - (center)) * (gain) + 128) >> 8) + (center), \ + (low), (high)) + +#define MUL_GAIN_f(d, num_samples, gain) \ + for (int n = 0; n < (num_samples); n++) \ + (d)[n] = (d)[n] * (gain) + +static void process_plane(struct ao *ao, void *data, int num_samples) +{ + float gain = atomic_load_explicit(&ao->gain, memory_order_relaxed); + int gi = lrint(256.0 * gain); + if (gi == 256) + return; + switch (af_fmt_from_planar(ao->format)) { + case AF_FORMAT_U8: + MUL_GAIN_i((uint8_t *)data, num_samples, gi, 0, 128, 255); + break; + case AF_FORMAT_S16: + MUL_GAIN_i((int16_t *)data, num_samples, gi, INT16_MIN, 0, INT16_MAX); + break; + case AF_FORMAT_S32: + MUL_GAIN_i((int32_t *)data, num_samples, gi, INT32_MIN, 0, INT32_MAX); + break; + case AF_FORMAT_FLOAT: + MUL_GAIN_f((float *)data, num_samples, gain); + break; + case AF_FORMAT_DOUBLE: + MUL_GAIN_f((double *)data, num_samples, gain); + break; + default:; + // all other sample formats are simply not supported + } +} + +void ao_post_process_data(struct ao *ao, void **data, int num_samples) +{ + bool planar = af_fmt_is_planar(ao->format); + int planes = planar ? ao->channels.num : 1; + int plane_samples = num_samples * (planar ? 1: ao->channels.num); + for (int n = 0; n < planes; n++) + process_plane(ao, data[n], plane_samples); +} + +static int get_conv_type(struct ao_convert_fmt *fmt) +{ + if (af_fmt_to_bytes(fmt->src_fmt) * 8 == fmt->dst_bits && !fmt->pad_msb) + return 0; // passthrough + if (fmt->src_fmt == AF_FORMAT_S32 && fmt->dst_bits == 24 && !fmt->pad_msb) + return 1; // simple 32->24 bit conversion + if (fmt->src_fmt == AF_FORMAT_S32 && fmt->dst_bits == 32 && fmt->pad_msb == 8) + return 2; // simple 32->24 bit conversion, with MSB padding + return -1; // unsupported +} + +// Check whether ao_convert_inplace() can be called. As an exception, the +// planar-ness of the sample format and the number of channels is ignored. +// All other parameters must be as passed to ao_convert_inplace(). +bool ao_can_convert_inplace(struct ao_convert_fmt *fmt) +{ + return get_conv_type(fmt) >= 0; +} + +bool ao_need_conversion(struct ao_convert_fmt *fmt) +{ + return get_conv_type(fmt) != 0; +} + +// The LSB is always ignored. +#if BYTE_ORDER == BIG_ENDIAN +#define SHIFT24(x) ((3-(x))*8) +#else +#define SHIFT24(x) (((x)+1)*8) +#endif + +static void convert_plane(int type, void *data, int num_samples) +{ + switch (type) { + case 0: + break; + case 1: /* fall through */ + case 2: { + int bytes = type == 1 ? 3 : 4; + for (int s = 0; s < num_samples; s++) { + uint32_t val = *((uint32_t *)data + s); + uint8_t *ptr = (uint8_t *)data + s * bytes; + ptr[0] = val >> SHIFT24(0); + ptr[1] = val >> SHIFT24(1); + ptr[2] = val >> SHIFT24(2); + if (type == 2) + ptr[3] = 0; + } + break; + } + default: + MP_ASSERT_UNREACHABLE(); + } +} + +// data[n] contains the pointer to the first sample of the n-th plane, in the +// format implied by fmt->src_fmt. src_fmt also controls whether the data is +// all in one plane, or if there is a plane per channel. +void ao_convert_inplace(struct ao_convert_fmt *fmt, void **data, int num_samples) +{ + int type = get_conv_type(fmt); + bool planar = af_fmt_is_planar(fmt->src_fmt); + int planes = planar ? fmt->channels : 1; + int plane_samples = num_samples * (planar ? 1: fmt->channels); + for (int n = 0; n < planes; n++) + convert_plane(type, data[n], plane_samples); +} diff --git a/audio/out/ao.h b/audio/out/ao.h index 211f0e5974..18c7cdc02f 100644 --- a/audio/out/ao.h +++ b/audio/out/ao.h @@ -26,8 +26,7 @@ #include "audio/chmap_sel.h" enum aocontrol { - // _VOLUME commands take struct ao_control_vol pointer for input/output. - // If there's only one volume, SET should use average of left/right. + // _VOLUME commands take a pointer to float for input/output. AOCONTROL_GET_VOLUME, AOCONTROL_SET_VOLUME, // _MUTE commands take a pointer to bool @@ -35,19 +34,16 @@ enum aocontrol { AOCONTROL_SET_MUTE, // Has char* as argument, which contains the desired stream title. AOCONTROL_UPDATE_STREAM_TITLE, - // the AO does the equivalent of af_volume (return CONTROL_TRUE if yes) - AOCONTROL_HAS_SOFT_VOLUME, - // like above, but volume persists (per app), mpv won't restore volume - AOCONTROL_HAS_PER_APP_VOLUME, }; // If set, then the queued audio data is the last. Note that after a while, new // data might be written again, instead of closing the AO. -#define AOPLAY_FINAL_CHUNK 1 +#define PLAYER_FINAL_CHUNK 1 enum { AO_EVENT_RELOAD = 1, AO_EVENT_HOTPLUG = 2, + AO_EVENT_INITIAL_UNBLOCK = 4, }; enum { @@ -60,13 +56,10 @@ enum { AO_INIT_STREAM_SILENCE = 1 << 2, // Force exclusive mode, i.e. lock out the system mixer. AO_INIT_EXCLUSIVE = 1 << 3, + // Initialize with music role. + AO_INIT_MEDIA_ROLE_MUSIC = 1 << 4, }; -typedef struct ao_control_vol { - float left; - float right; -} ao_control_vol_t; - struct ao_device_desc { const char *name; // symbolic name; will be set on ao->device const char *desc; // verbose human readable name @@ -81,7 +74,13 @@ struct ao; struct mpv_global; struct input_ctx; struct encode_lavc_context; -struct mp_audio; + +struct ao_opts { + struct m_obj_settings *audio_driver_list; + char *audio_device; + char *audio_client_name; + double audio_buffer; +}; struct ao *ao_init_best(struct mpv_global *global, int init_flags, @@ -89,20 +88,24 @@ struct ao *ao_init_best(struct mpv_global *global, struct encode_lavc_context *encode_lavc_ctx, int samplerate, int format, struct mp_chmap channels); void ao_uninit(struct ao *ao); -void ao_get_format(struct ao *ao, struct mp_audio *format); +void ao_get_format(struct ao *ao, + int *samplerate, int *format, struct mp_chmap *channels); const char *ao_get_name(struct ao *ao); const char *ao_get_description(struct ao *ao); bool ao_untimed(struct ao *ao); -int ao_play(struct ao *ao, void **data, int samples, int flags); int ao_control(struct ao *ao, enum aocontrol cmd, void *arg); +void ao_set_gain(struct ao *ao, float gain); double ao_get_delay(struct ao *ao); -int ao_get_space(struct ao *ao); void ao_reset(struct ao *ao); -void ao_pause(struct ao *ao); -void ao_resume(struct ao *ao); +void ao_start(struct ao *ao); +void ao_set_paused(struct ao *ao, bool paused, bool eof); void ao_drain(struct ao *ao); -bool ao_eof_reached(struct ao *ao); +bool ao_is_playing(struct ao *ao); +struct mp_async_queue; +struct mp_async_queue *ao_get_queue(struct ao *ao); int ao_query_and_reset_events(struct ao *ao, int events); +int ao_add_events(struct ao *ao, int events); +void ao_unblock(struct ao *ao); void ao_request_reload(struct ao *ao); void ao_hotplug_event(struct ao *ao); @@ -112,9 +115,8 @@ struct ao_hotplug *ao_hotplug_create(struct mpv_global *global, 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); -struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp); +struct ao_device_list *ao_hotplug_get_device_list(struct ao_hotplug *hp, struct ao *playback_ao); -void ao_print_devices(struct mpv_global *global, struct mp_log *log); +void ao_print_devices(struct mpv_global *global, struct mp_log *log, struct ao *playback_ao); #endif /* MPLAYER_AUDIO_OUT_H */ diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c index b0099011dc..92ea0db237 100644 --- a/audio/out/ao_alsa.c +++ b/audio/out/ao_alsa.c @@ -12,28 +12,28 @@ * * This file is part of mpv. * - * mpv is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. * * mpv is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with mpv. If not, see <http://www.gnu.org/licenses/>. + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. */ #include <errno.h> #include <sys/time.h> #include <stdlib.h> #include <stdarg.h> +#include <limits.h> #include <math.h> #include <string.h> -#include "config.h" #include "options/options.h" #include "options/m_config.h" #include "options/m_option.h" @@ -42,8 +42,11 @@ #include <alsa/asoundlib.h> -#define HAVE_CHMAP_API \ - (defined(SND_CHMAP_API_VERSION) && SND_CHMAP_API_VERSION >= (1 << 16)) +#if defined(SND_CHMAP_API_VERSION) && SND_CHMAP_API_VERSION >= (1 << 16) +#define HAVE_CHMAP_API 1 +#else +#define HAVE_CHMAP_API 0 +#endif #include "ao.h" #include "internal.h" @@ -53,27 +56,31 @@ struct ao_alsa_opts { char *mixer_device; char *mixer_name; int mixer_index; - int resample; - int ni; - int ignore_chmap; + bool resample; + bool ni; + bool ignore_chmap; + int buffer_time; + int frags; }; #define OPT_BASE_STRUCT struct ao_alsa_opts static const struct m_sub_options ao_alsa_conf = { .opts = (const struct m_option[]) { - 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), + {"alsa-resample", OPT_BOOL(resample)}, + {"alsa-mixer-device", OPT_STRING(mixer_device)}, + {"alsa-mixer-name", OPT_STRING(mixer_name)}, + {"alsa-mixer-index", OPT_INT(mixer_index), M_RANGE(0, 99)}, + {"alsa-non-interleaved", OPT_BOOL(ni)}, + {"alsa-ignore-chmap", OPT_BOOL(ignore_chmap)}, + {"alsa-buffer-time", OPT_INT(buffer_time), M_RANGE(0, INT_MAX)}, + {"alsa-periods", OPT_INT(frags), M_RANGE(0, INT_MAX)}, {0} }, .defaults = &(const struct ao_alsa_opts) { .mixer_device = "default", .mixer_name = "Master", - .mixer_index = 0, - .ni = 0, + .buffer_time = 100000, + .frags = 4, }, .size = sizeof(struct ao_alsa_opts), }; @@ -83,20 +90,16 @@ struct priv { bool device_lost; snd_pcm_format_t alsa_fmt; bool can_pause; - bool paused; - snd_pcm_sframes_t prepause_frames; - double delay_before_pause; snd_pcm_uframes_t buffersize; snd_pcm_uframes_t outburst; snd_output_t *output; + struct ao_convert_fmt convert; + struct ao_alsa_opts *opts; }; -#define BUFFER_TIME 250000 // 250ms -#define FRAGCOUNT 16 - #define CHECK_A |