summaryrefslogtreecommitdiffstats
path: root/audio/out
diff options
context:
space:
mode:
Diffstat (limited to 'audio/out')
-rw-r--r--audio/out/ao.c347
-rw-r--r--audio/out/ao.h46
-rw-r--r--audio/out/ao_alsa.c565
-rw-r--r--audio/out/ao_audiotrack.c837
-rw-r--r--audio/out/ao_audiounit.m102
-rw-r--r--audio/out/ao_avfoundation.m372
-rw-r--r--audio/out/ao_coreaudio.c171
-rw-r--r--audio/out/ao_coreaudio_chmap.c232
-rw-r--r--audio/out/ao_coreaudio_chmap.h6
-rw-r--r--audio/out/ao_coreaudio_exclusive.c94
-rw-r--r--audio/out/ao_coreaudio_properties.c2
-rw-r--r--audio/out/ao_coreaudio_utils.c76
-rw-r--r--audio/out/ao_coreaudio_utils.h18
-rw-r--r--audio/out/ao_jack.c37
-rw-r--r--audio/out/ao_lavc.c469
-rw-r--r--audio/out/ao_null.c132
-rw-r--r--audio/out/ao_openal.c400
-rw-r--r--audio/out/ao_opensles.c181
-rw-r--r--audio/out/ao_oss.c716
-rw-r--r--audio/out/ao_pcm.c68
-rw-r--r--audio/out/ao_pipewire.c934
-rw-r--r--audio/out/ao_pulse.c287
-rw-r--r--audio/out/ao_rsound.c151
-rw-r--r--audio/out/ao_sdl.c20
-rw-r--r--audio/out/ao_sndio.c335
-rw-r--r--audio/out/ao_wasapi.c164
-rw-r--r--audio/out/ao_wasapi.h30
-rw-r--r--audio/out/ao_wasapi_utils.c633
-rw-r--r--audio/out/buffer.c753
-rw-r--r--audio/out/internal.h179
-rw-r--r--audio/out/pull.c289
-rw-r--r--audio/out/push.c557
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