summaryrefslogtreecommitdiffstats
path: root/audio/out
diff options
context:
space:
mode:
Diffstat (limited to 'audio/out')
-rw-r--r--audio/out/ao.c36
-rw-r--r--audio/out/ao.h12
-rw-r--r--audio/out/ao_alsa.c77
-rw-r--r--audio/out/ao_coreaudio.c2
-rw-r--r--audio/out/ao_coreaudio_exclusive.c2
-rw-r--r--audio/out/ao_coreaudio_properties.c2
-rw-r--r--audio/out/ao_coreaudio_utils.c2
-rw-r--r--audio/out/ao_lavc.c2
-rw-r--r--audio/out/ao_null.c16
-rw-r--r--audio/out/ao_pulse.c5
-rw-r--r--audio/out/ao_wasapi_utils.c2
-rw-r--r--audio/out/internal.h4
-rw-r--r--audio/out/pull.c8
-rw-r--r--audio/out/push.c6
14 files changed, 129 insertions, 47 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c
index c9d8f42b4a..cf66e0c64b 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -158,7 +158,7 @@ error:
static struct ao *ao_init(bool probing, struct mpv_global *global,
struct input_ctx *input_ctx,
- struct encode_lavc_context *encode_lavc_ctx,
+ struct encode_lavc_context *encode_lavc_ctx, int flags,
int samplerate, int format, struct mp_chmap channels,
char *dev, char *name, char **args)
{
@@ -169,6 +169,7 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
ao->channels = channels;
ao->format = format;
ao->encode_lavc_ctx = encode_lavc_ctx;
+ ao->init_flags = flags;
if (ao->driver->encode != !!ao->encode_lavc_ctx)
goto fail;
@@ -182,6 +183,8 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
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;
+
int r = ao->driver->init(ao);
if (r < 0) {
// Silly exception for coreaudio spdif redirection
@@ -190,7 +193,7 @@ 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,
+ return ao_init(probing, global, input_ctx, encode_lavc_ctx, flags,
samplerate, format, channels, rdevice, redirect, NULL);
}
goto fail;
@@ -240,7 +243,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,
- bool ao_null_fallback,
+ int init_flags,
struct input_ctx *input_ctx,
struct encode_lavc_context *encode_lavc_ctx,
int samplerate, int format, struct mp_chmap channels)
@@ -283,7 +286,7 @@ struct ao *ao_init_best(struct mpv_global *global,
}
}
- if (ao_null_fallback) {
+ if (init_flags & AO_INIT_NULL_FALLBACK) {
MP_TARRAY_APPEND(tmp, ao_list, ao_num,
(struct m_obj_settings){.name = "null"});
}
@@ -297,7 +300,7 @@ 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,
+ ao = ao_init(probing, global, input_ctx, encode_lavc_ctx, init_flags,
samplerate, format, channels, dev,
entry->name, entry->attribs);
if (ao)
@@ -429,6 +432,29 @@ bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
return r;
}
+// safe_multichannel=true behaves like ao_chmap_sel_adjust.
+// safe_multichannel=false is a helper for callers which do not support safe
+// handling of arbitrary channel layouts. If the multichannel layouts are not
+// considered "always safe" (e.g. HDMI), then allow only stereo or mono, if
+// they are part of the list in *s.
+bool ao_chmap_sel_adjust2(struct ao *ao, const struct mp_chmap_sel *s,
+ struct mp_chmap *map, bool safe_multichannel)
+{
+ if (!safe_multichannel && (ao->init_flags & AO_INIT_SAFE_MULTICHANNEL_ONLY)) {
+ struct mp_chmap res = *map;
+ if (mp_chmap_sel_adjust(s, &res)) {
+ if (!mp_chmap_equals(&res, &(struct mp_chmap)MP_CHMAP_INIT_MONO) &&
+ !mp_chmap_equals(&res, &(struct mp_chmap)MP_CHMAP_INIT_STEREO))
+ {
+ MP_WARN(ao, "Disabling multichannel output.\n");
+ *map = (struct mp_chmap)MP_CHMAP_INIT_STEREO;
+ }
+ }
+ }
+
+ return ao_chmap_sel_adjust(ao, s, map);
+}
+
bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map, int num)
{
diff --git a/audio/out/ao.h b/audio/out/ao.h
index e8e64e33eb..3b187e7355 100644
--- a/audio/out/ao.h
+++ b/audio/out/ao.h
@@ -50,6 +50,16 @@ enum {
AO_EVENT_HOTPLUG = 2,
};
+enum {
+ // Allow falling back to ao_null if nothing else works.
+ AO_INIT_NULL_FALLBACK = 1 << 0,
+ // Only accept multichannel configurations that are guaranteed to work
+ // (i.e. not sending arbitrary layouts over HDMI).
+ AO_INIT_SAFE_MULTICHANNEL_ONLY = 1 << 1,
+ // Stream silence as long as no audio is playing.
+ AO_INIT_STREAM_SILENCE = 1 << 2,
+};
+
typedef struct ao_control_vol {
float left;
float right;
@@ -72,7 +82,7 @@ struct encode_lavc_context;
struct mp_audio;
struct ao *ao_init_best(struct mpv_global *global,
- bool ao_null_fallback,
+ int init_flags,
struct input_ctx *input_ctx,
struct encode_lavc_context *encode_lavc_ctx,
int samplerate, int format, struct mp_chmap channels);
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index d09f5fc499..bbe15b4b61 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -53,6 +53,7 @@ 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;
@@ -360,7 +361,7 @@ static bool query_chmaps(struct ao *ao, struct mp_chmap *chmap)
snd_pcm_free_chmaps(maps);
- return ao_chmap_sel_adjust(ao, &chmap_sel, chmap);
+ return ao_chmap_sel_adjust2(ao, &chmap_sel, chmap, false);
}
// Map back our selected channel layout to an ALSA one. This is done this way so
@@ -785,6 +786,14 @@ static int init_device(struct ao *ao, int mode)
MP_VERBOSE(ao, "buffersize: %d samples\n", (int)p->buffersize);
MP_VERBOSE(ao, "period size: %d samples\n", (int)p->outburst);
+ ao->device_buffer = p->buffersize;
+
+ // ao_alsa implements this by relying on underrun behavior (no data means
+ // underrun, during which silence is played). Trigger by playing some
+ // initial silence.
+ if (ao->stream_silence)
+ ao_play_silence(ao, p->outburst);
+
return 0;
alsa_error:
@@ -874,7 +883,7 @@ static double get_delay(struct ao *ao)
struct priv *p = ao->priv;
snd_pcm_sframes_t delay;
- if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED)
+ if (p->paused)
return p->delay_before_pause;
if (snd_pcm_delay(p->alsa, &delay) < 0)
@@ -888,27 +897,47 @@ static double get_delay(struct ao *ao)
return delay / (double)ao->samplerate;
}
+// For stream-silence mode: replace remaining buffer with silence.
+// Tries to cause an instant buffer underrun.
+static void soft_reset(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ snd_pcm_sframes_t frames = snd_pcm_rewindable(p->alsa);
+ if (frames > 0 && snd_pcm_state(p->alsa) == SND_PCM_STATE_RUNNING) {
+ frames = snd_pcm_rewind(p->alsa, frames);
+ if (frames < 0) {
+ int err = frames;
+ CHECK_ALSA_WARN("pcm rewind error");
+ }
+ }
+}
+
static void audio_pause(struct ao *ao)
{
struct priv *p = ao->priv;
int err;
- if (p->can_pause) {
+ if (p->paused)
+ return;
+
+ p->delay_before_pause = get_delay(ao);
+ p->prepause_frames = p->delay_before_pause * ao->samplerate;
+
+ if (ao->stream_silence) {
+ soft_reset(ao);
+ } else if (p->can_pause) {
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_RUNNING) {
- p->delay_before_pause = get_delay(ao);
err = snd_pcm_pause(p->alsa, 1);
CHECK_ALSA_ERROR("pcm pause error");
+ p->prepause_frames = 0;
}
} else {
- if (snd_pcm_delay(p->alsa, &p->prepause_frames) < 0
- || p->prepause_frames < 0)
- p->prepause_frames = 0;
- p->delay_before_pause = p->prepause_frames / (double)ao->samplerate;
-
err = snd_pcm_drop(p->alsa);
CHECK_ALSA_ERROR("pcm drop error");
}
+ p->paused = true;
+
alsa_error: ;
}
@@ -930,9 +959,15 @@ static void audio_resume(struct ao *ao)
struct priv *p = ao->priv;
int err;
+ if (!p->paused)
+ return;
+
resume_device(ao);
- if (p->can_pause) {
+ if (ao->stream_silence) {
+ p->paused = false;
+ get_delay(ao); // recovers from underrun (as a side-effect)
+ } else if (p->can_pause) {
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED) {
err = snd_pcm_pause(p->alsa, 0);
CHECK_ALSA_ERROR("pcm resume error");
@@ -941,11 +976,13 @@ static void audio_resume(struct ao *ao)
MP_VERBOSE(ao, "resume not supported by hardware\n");
err = snd_pcm_prepare(p->alsa);
CHECK_ALSA_ERROR("pcm prepare error");
- if (p->prepause_frames)
- ao_play_silence(ao, p->prepause_frames);
}
+ if (p->prepause_frames)
+ ao_play_silence(ao, p->prepause_frames);
+
alsa_error: ;
+ p->paused = false;
}
static void reset(struct ao *ao)
@@ -953,12 +990,18 @@ static void reset(struct ao *ao)
struct priv *p = ao->priv;
int err;
+ p->paused = false;
p->prepause_frames = 0;
p->delay_before_pause = 0;
- err = snd_pcm_drop(p->alsa);
- CHECK_ALSA_ERROR("pcm prepare error");
- err = snd_pcm_prepare(p->alsa);
- CHECK_ALSA_ERROR("pcm prepare error");
+
+ if (ao->stream_silence) {
+ soft_reset(ao);
+ } else {
+ err = snd_pcm_drop(p->alsa);
+ CHECK_ALSA_ERROR("pcm prepare error");
+ err = snd_pcm_prepare(p->alsa);
+ CHECK_ALSA_ERROR("pcm prepare error");
+ }
alsa_error: ;
}
@@ -997,6 +1040,8 @@ static int play(struct ao *ao, void **data, int samples, int flags)
}
} while (res == 0);
+ p->paused = false;
+
return res < 0 ? -1 : res;
alsa_error:
diff --git a/audio/out/ao_coreaudio.c b/audio/out/ao_coreaudio.c
index b0a5dc0da1..fbbacde714 100644
--- a/audio/out/ao_coreaudio.c
+++ b/audio/out/ao_coreaudio.c
@@ -419,7 +419,7 @@ const struct ao_driver audio_out_coreaudio = {
.uninit = uninit,
.init = init,
.control = control,
- .pause = stop,
+ .reset = stop,
.resume = start,
.hotplug_init = hotplug_init,
.hotplug_uninit = hotplug_uninit,
diff --git a/audio/out/ao_coreaudio_exclusive.c b/audio/out/ao_coreaudio_exclusive.c
index 510fa3a529..49f921cd4c 100644
--- a/audio/out/ao_coreaudio_exclusive.c
+++ b/audio/out/ao_coreaudio_exclusive.c
@@ -399,7 +399,7 @@ const struct ao_driver audio_out_coreaudio_exclusive = {
.name = "coreaudio_exclusive",
.uninit = uninit,
.init = init,
- .pause = audio_pause,
+ .reset = audio_pause,
.resume = audio_resume,
.list_devs = ca_get_device_list,
.priv_size = sizeof(struct priv),
diff --git a/audio/out/ao_coreaudio_properties.c b/audio/out/ao_coreaudio_properties.c
index b74cf07289..a66c9dcf1f 100644
--- a/audio/out/ao_coreaudio_properties.c
+++ b/audio/out/ao_coreaudio_properties.c
@@ -70,7 +70,7 @@ OSStatus ca_get_ary(AudioObjectID id, ca_scope scope, ca_sel selector,
return err;
coreaudio_error_free:
- free(*data);
+ talloc_free(*data);
coreaudio_error:
return err;
}
diff --git a/audio/out/ao_coreaudio_utils.c b/audio/out/ao_coreaudio_utils.c
index 0bcc0d62a1..76f5402fd1 100644
--- a/audio/out/ao_coreaudio_utils.c
+++ b/audio/out/ao_coreaudio_utils.c
@@ -147,7 +147,7 @@ bool check_ca_st(struct ao *ao, int level, OSStatus code, const char *message)
{
if (code == noErr) return true;
- mp_msg(ao->log, level, "%s (%s)\n", message, mp_tag_str(code));
+ mp_msg(ao->log, level, "%s (%s/%d)\n", message, mp_tag_str(code), (int)code);
return false;
}
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c
index 6b4279ca87..8ae1317407 100644
--- a/audio/out/ao_lavc.c
+++ b/audio/out/ao_lavc.c
@@ -123,7 +123,7 @@ static int init(struct ao *ao)
struct mp_chmap_sel sel = {0};
mp_chmap_sel_add_any(&sel);
- if (!ao_chmap_sel_adjust(ao, &sel, &ao->channels))
+ if (!ao_chmap_sel_adjust2(ao, &sel, &ao->channels, false))
goto fail;
mp_chmap_reorder_to_lavc(&ao->channels);
ac->codec->channels = ao->channels.num;
diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c
index 7d45795d9e..ac3f8348b5 100644
--- a/audio/out/ao_null.c
+++ b/audio/out/ao_null.c
@@ -57,7 +57,7 @@ struct priv {
// (This is not needed by the AO API, but many AOs behave this way.)
int outburst; // samples
- char **channel_layouts;
+ struct m_channels channel_layouts;
};
static void drain(struct ao *ao)
@@ -91,15 +91,9 @@ static int init(struct ao *ao)
ao->untimed = priv->untimed;
struct mp_chmap_sel sel = {.tmp = ao};
- if (priv->channel_layouts) {
- for (int n = 0; priv->channel_layouts[n]; n++) {
- struct mp_chmap map = {0};
- if (!mp_chmap_from_str(&map, bstr0(priv->channel_layouts[n]))) {
- MP_FATAL(ao, "Invalid channel map in option.\n");
- return -1;
- }
- mp_chmap_sel_add_map(&sel, &map);
- }
+ if (priv->channel_layouts.num_chmaps) {
+ for (int n = 0; n < priv->channel_layouts.num_chmaps; n++)
+ mp_chmap_sel_add_map(&sel, &priv->channel_layouts.chmaps[n]);
} else {
mp_chmap_sel_add_any(&sel);
}
@@ -244,7 +238,7 @@ const struct ao_driver audio_out_null = {
OPT_FLOATRANGE("latency", latency_sec, 0, 0, 100),
OPT_FLAG("broken-eof", broken_eof, 0),
OPT_FLAG("broken-delay", broken_delay, 0),
- OPT_STRINGLIST("channel-layouts", channel_layouts, 0),
+ OPT_CHANNELS("channel-layouts", channel_layouts, 0),
{0}
},
};
diff --git a/audio/out/ao_pulse.c b/audio/out/ao_pulse.c
index d553b6700c..fc0dd0d008 100644
--- a/audio/out/ao_pulse.c
+++ b/audio/out/ao_pulse.c
@@ -23,6 +23,7 @@
#include <stdbool.h>
#include <string.h>
#include <stdint.h>
+#include <math.h>
#include <pthread.h>
#include <pulse/pulseaudio.h>
@@ -34,8 +35,8 @@
#include "ao.h"
#include "internal.h"
-#define VOL_PA2MP(v) ((v) * 100 / PA_VOLUME_NORM)
-#define VOL_MP2PA(v) ((v) * PA_VOLUME_NORM / 100)
+#define VOL_PA2MP(v) ((v) * 100.0 / PA_VOLUME_NORM)
+#define VOL_MP2PA(v) lrint((v) * PA_VOLUME_NORM / 100)
struct priv {
// PulseAudio playback stream object
diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c
index 0e7dec885a..6218153bea 100644
--- a/audio/out/ao_wasapi_utils.c
+++ b/audio/out/ao_wasapi_utils.c
@@ -407,7 +407,7 @@ static bool search_channels(struct ao *ao, WAVEFORMATEXTENSIBLE *wformat)
}
entry = ao->channels;
- if (ao_chmap_sel_adjust(ao, &chmap_sel, &entry)){
+ if (ao_chmap_sel_adjust2(ao, &chmap_sel, &entry, !state->opt_exclusive)){
change_waveformat_channels(wformat, &entry);
return true;
}
diff --git a/audio/out/internal.h b/audio/out/internal.h
index 49131ba293..518661c2bd 100644
--- a/audio/out/internal.h
+++ b/audio/out/internal.h
@@ -43,6 +43,8 @@ struct ao {
struct encode_lavc_context *encode_lavc_ctx;
struct input_ctx *input_ctx;
struct mp_log *log; // Using e.g. "[ao/coreaudio]" as prefix
+ int init_flags; // AO_INIT_* flags
+ bool stream_silence; // if audio inactive, just play silence
// The device as selected by the user, usually using ao_device_desc.name
// from an entry from the list returned by driver->list_devices. If the
@@ -191,6 +193,8 @@ void ao_wakeup_poll(struct ao *ao);
bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map);
+bool ao_chmap_sel_adjust2(struct ao *ao, const struct mp_chmap_sel *s,
+ struct mp_chmap *map, bool safe_multichannel);
bool ao_chmap_sel_get_def(struct ao *ao, const struct mp_chmap_sel *s,
struct mp_chmap *map, int num);
diff --git a/audio/out/pull.c b/audio/out/pull.c
index 89805809b7..2175a58db0 100644
--- a/audio/out/pull.c
+++ b/audio/out/pull.c
@@ -185,7 +185,7 @@ static double get_delay(struct ao *ao)
static void reset(struct ao *ao)
{
struct ao_pull_state *p = ao->api_priv;
- if (ao->driver->reset)
+ if (!ao->stream_silence && ao->driver->reset)
ao->driver->reset(ao); // assumes the audio callback thread is stopped
set_state(ao, AO_STATE_NONE);
for (int n = 0; n < ao->num_planes; n++)
@@ -195,7 +195,7 @@ static void reset(struct ao *ao)
static void pause(struct ao *ao)
{
- if (ao->driver->reset)
+ if (!ao->stream_silence && ao->driver->reset)
ao->driver->reset(ao);
set_state(ao, AO_STATE_NONE);
}
@@ -244,6 +244,10 @@ static int init(struct ao *ao)
p->buffers[n] = mp_ring_new(ao, ao->buffer * ao->sstride);
atomic_store(&p->state, AO_STATE_NONE);
assert(ao->driver->resume);
+
+ if (ao->stream_silence)
+ ao->driver->resume(ao);
+
return 0;
}
diff --git a/audio/out/push.c b/audio/out/push.c
index ac87c62a96..bf5dde46b5 100644
--- a/audio/out/push.c
+++ b/audio/out/push.c
@@ -497,10 +497,8 @@ int ao_wait_poll(struct ao *ao, struct pollfd *fds, int num_fds,
bool wakeup = false;
if (p_fds[num_fds].revents & POLLIN) {
wakeup = true;
- // flush the wakeup pipe contents - might "drown" some wakeups, but
- // that's ok for our use-case
- char buf[100];
- (void)read(p->wakeup_pipe[0], buf, sizeof(buf));
+ // might "drown" some wakeups, but that's ok for our use-case
+ mp_flush_wakeup_pipe(p->wakeup_pipe[0]);
}
return (r >= 0 || r == -EINTR) ? wakeup : -1;
}