summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-06-25 15:57:15 +0200
committerwm4 <wm4@nowhere>2017-06-25 15:57:43 +0200
commit037c37519b305bb5d5ea0379122d62ba356cc103 (patch)
tree5f93237279937290a3cd882e60a2bc040907a490
parent72ef74dab5244753c9bc6f33687aa60a2886c53b (diff)
downloadmpv-037c37519b305bb5d5ea0379122d62ba356cc103.tar.bz2
mpv-037c37519b305bb5d5ea0379122d62ba356cc103.tar.xz
audio/out: require AO drivers to report period size and correct buffer
Before this change, AOs could have internal alignment, and play() would not consume the trailing data if the size passed to it is not aligned. Change this to require AOs to report their alignment (via period_size), and make sure to always send aligned data. The buffer reported by get_space() now always has to be correct and reliable. If play() does not consume all data provided (which is bounded by get_space()), an error is printed. This is preparation for potential further AO changes. I casually checked alsa/lavc/null/pcm, the other AOs might or might not work.
-rw-r--r--audio/out/ao.c7
-rw-r--r--audio/out/ao_alsa.c1
-rw-r--r--audio/out/ao_lavc.c4
-rw-r--r--audio/out/ao_null.c2
-rw-r--r--audio/out/ao_openal.c1
-rw-r--r--audio/out/ao_oss.c3
-rw-r--r--audio/out/ao_sndio.c2
-rw-r--r--audio/out/internal.h8
-rw-r--r--audio/out/push.c15
9 files changed, 39 insertions, 4 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c
index a2a001c50c..b699b64c5a 100644
--- a/audio/out/ao.c
+++ b/audio/out/ao.c
@@ -194,6 +194,8 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
ao->stream_silence = flags & AO_INIT_STREAM_SILENCE;
+ ao->period_size = 1;
+
int r = ao->driver->init(ao);
if (r < 0) {
// Silly exception for coreaudio spdif redirection
@@ -209,6 +211,11 @@ static struct ao *ao_init(bool probing, struct mpv_global *global,
goto fail;
}
+ if (ao->period_size < 1) {
+ MP_ERR(ao, "Invalid period size set.\n");
+ goto fail;
+ }
+
ao->sstride = af_fmt_to_bytes(ao->format);
ao->num_planes = 1;
if (af_fmt_is_planar(ao->format)) {
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index c0efdaff40..b0099011dc 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -840,6 +840,7 @@ static int init_device(struct ao *ao, int mode)
MP_VERBOSE(ao, "period size: %d samples\n", (int)p->outburst);
ao->device_buffer = p->buffersize;
+ ao->period_size = p->outburst;
return 0;
diff --git a/audio/out/ao_lavc.c b/audio/out/ao_lavc.c
index 619f4fe72a..f247cba12e 100644
--- a/audio/out/ao_lavc.c
+++ b/audio/out/ao_lavc.c
@@ -160,6 +160,8 @@ static int init(struct ao *ao)
ao->untimed = true;
+ ao->period_size = ac->aframesize * ac->framecount;
+
if (ao->channels.num > AV_NUM_DATA_POINTERS)
goto fail;
@@ -203,7 +205,7 @@ static void uninit(struct ao *ao)
ac->shutdown = true;
}
-// return: how many bytes can be played without blocking
+// return: how many samples can be played without blocking
static int get_space(struct ao *ao)
{
struct priv *ac = ao->priv;
diff --git a/audio/out/ao_null.c b/audio/out/ao_null.c
index 4b07634d22..29d88d4670 100644
--- a/audio/out/ao_null.c
+++ b/audio/out/ao_null.c
@@ -108,6 +108,8 @@ static int init(struct ao *ao)
priv->last_time = mp_time_sec();
+ ao->period_size = priv->outburst;
+
return 0;
}
diff --git a/audio/out/ao_openal.c b/audio/out/ao_openal.c
index 9d22abb38e..715ffddceb 100644
--- a/audio/out/ao_openal.c
+++ b/audio/out/ao_openal.c
@@ -214,6 +214,7 @@ static int init(struct ao *ao)
}
p->chunk_size = CHUNK_SAMPLES * af_fmt_to_bytes(ao->format);
+ ao->period_size = CHUNK_SAMPLES;
return 0;
err_out:
diff --git a/audio/out/ao_oss.c b/audio/out/ao_oss.c
index 1478d1400e..1edf55a095 100644
--- a/audio/out/ao_oss.c
+++ b/audio/out/ao_oss.c
@@ -394,7 +394,8 @@ static int reopen_device(struct ao *ao, bool allow_format_changes)
}
}
- p->outburst -= p->outburst % (channels.num * af_fmt_to_bytes(format)); // round down
+ ao->period_size = channels.num * af_fmt_to_bytes(format);
+ p->outburst -= p->outburst % ao->period_size; // round down
return 0;
diff --git a/audio/out/ao_sndio.c b/audio/out/ao_sndio.c
index d9ac4ef7e4..20490fe0a3 100644
--- a/audio/out/ao_sndio.c
+++ b/audio/out/ao_sndio.c
@@ -195,6 +195,8 @@ static int init(struct ao *ao)
if (!p->pfd)
goto error;
+ ao->period_size = p->par.round;
+
return 0;
error:
diff --git a/audio/out/internal.h b/audio/out/internal.h
index a4be59982a..0c59ce973e 100644
--- a/audio/out/internal.h
+++ b/audio/out/internal.h
@@ -48,6 +48,14 @@ struct ao {
int init_flags; // AO_INIT_* flags
bool stream_silence; // if audio inactive, just play silence
+ // Set by the driver on init. This is typically the period size, and the
+ // smallest unit the driver will accept in one piece (although if
+ // AOPLAY_FINAL_CHUNK is set, the driver must accept everything).
+ // This value is in complete samples (i.e. 1 for stereo means 1 sample
+ // for both channels each).
+ // Used for push based API only.
+ int period_size;
+
// 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
// default device should be used, this is set to NULL.
diff --git a/audio/out/push.c b/audio/out/push.c
index a722d19ea2..c271fc0cdc 100644
--- a/audio/out/push.c
+++ b/audio/out/push.c
@@ -285,6 +285,8 @@ static void ao_play_data(struct ao *ao)
int space = ao->driver->get_space(ao);
bool play_silence = p->paused || (ao->stream_silence && !p->still_playing);
space = MPMAX(space, 0);
+ if (space % ao->period_size)
+ MP_ERR(ao, "Audio device reports unaligned available buffer size.\n");
struct mp_audio data;
if (play_silence) {
ao_get_silence(ao, &data, space);
@@ -295,16 +297,25 @@ static void ao_play_data(struct ao *ao)
if (data.samples > space)
data.samples = space;
int flags = 0;
- if (p->final_chunk && data.samples == max)
+ if (p->final_chunk && data.samples == max) {
flags |= AOPLAY_FINAL_CHUNK;
+ } else {
+ data.samples = data.samples / ao->period_size * ao->period_size;
+ }
MP_STATS(ao, "start ao fill");
int r = 0;
if (data.samples)
r = ao->driver->play(ao, data.planes, data.samples, flags);
MP_STATS(ao, "end ao fill");
if (r > data.samples) {
- MP_WARN(ao, "Audio device returned non-sense value.\n");
+ MP_ERR(ao, "Audio device returned non-sense value.\n");
r = data.samples;
+ } else if (r < 0) {
+ MP_ERR(ao, "Error writing audio to device.\n");
+ } else if (r != data.samples) {
+ MP_ERR(ao, "Audio device returned broken buffer state (sent %d samples, "
+ "got %d samples, %d period%s)!\n", data.samples, r,
+ ao->period_size, flags & AOPLAY_FINAL_CHUNK ? " final" : "");
}
r = MPMAX(r, 0);
// Probably can't copy the rest of the buffer due to period alignment.