summaryrefslogtreecommitdiffstats
path: root/audio/out/ao.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/out/ao.c')
-rw-r--r--audio/out/ao.c180
1 files changed, 53 insertions, 127 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c
index 71c17e03b0..3c61ee786c 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -29,7 +29,7 @@
#include "audio/format.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"
@@ -41,6 +41,7 @@ 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_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;
@@ -64,6 +65,9 @@ static const struct ao_driver * const audio_out_drivers[] = {
#if HAVE_COREAUDIO
&audio_out_coreaudio,
#endif
+#if HAVE_PIPEWIRE
+ &audio_out_pipewire,
+#endif
#if HAVE_PULSE
&audio_out_pulse,
#endif
@@ -98,15 +102,11 @@ static const struct ao_driver * const audio_out_drivers[] = {
#endif
&audio_out_pcm,
&audio_out_lavc,
-#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) {
@@ -127,7 +127,6 @@ static bool get_desc(struct m_obj_desc *dst, int index)
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,
@@ -136,11 +135,12 @@ static const struct m_obj_list ao_obj_list = {
#define OPT_BASE_STRUCT struct ao_opts
const struct m_sub_options ao_conf = {
.opts = (const struct m_option[]) {
- OPT_SETTINGSLIST("ao", audio_driver_list, UPDATE_AUDIO, &ao_obj_list, ),
- OPT_STRING("audio-device", audio_device, UPDATE_AUDIO),
- OPT_STRING("audio-client-name", audio_client_name, UPDATE_AUDIO),
- OPT_DOUBLE("audio-buffer", audio_buffer,
- UPDATE_AUDIO | M_OPT_MIN | M_OPT_MAX, .min = 0, .max = 10),
+ {"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),
@@ -210,14 +210,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) {
@@ -226,18 +221,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;
@@ -248,8 +239,10 @@ 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);
@@ -259,12 +252,12 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
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;
}
@@ -319,7 +312,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;
@@ -358,86 +351,6 @@ 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);
- atomic_fetch_and(&ao->events_, ~(unsigned int)AO_EVENT_UNDERRUN);
-}
-
-// 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)
{
@@ -466,12 +379,6 @@ void ao_hotplug_event(struct ao *ao)
ao_add_events(ao, AO_EVENT_HOTPLUG);
}
-// Returns whether this call actually set a new underrun flag.
-bool ao_underrun_event(struct ao *ao)
-{
- return ao_add_events(ao, AO_EVENT_UNDERRUN);
-}
-
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map)
{
@@ -545,8 +452,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;
@@ -586,7 +494,8 @@ bool ao_hotplug_check_update(struct ao_hotplug *hp)
}
// 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;
@@ -598,7 +507,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
@@ -609,10 +531,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);
}
@@ -661,10 +586,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];
@@ -686,7 +612,7 @@ void ao_set_gain(struct ao *ao, float gain)
#define MUL_GAIN_f(d, num_samples, gain) \
for (int n = 0; n < (num_samples); n++) \
- (d)[n] = MPCLAMP(((d)[n]) * (gain), -1.0, 1.0)
+ (d)[n] = (d)[n] * (gain)
static void process_plane(struct ao *ao, void *data, int num_samples)
{
@@ -775,7 +701,7 @@ static void convert_plane(int type, void *data, int num_samples)
break;
}
default:
- abort();
+ MP_ASSERT_UNREACHABLE();
}
}