summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2019-10-11 16:49:08 +0200
committerwm4 <wm4@nowhere>2019-10-11 16:52:45 +0200
commitc6c93499cbed839a624fdd279628488f16826616 (patch)
tree180e114e4e1c710a6e96204cdb48c2862b90cb65
parentea4685b233090bb155cfa4ec6bf87f421a553168 (diff)
downloadmpv-c6c93499cbed839a624fdd279628488f16826616.tar.bz2
mpv-c6c93499cbed839a624fdd279628488f16826616.tar.xz
ao_alsa: mess with underrun handling again
This commit tries to prepare for better underrun reporting. The goal is to report underruns relatively immediately. Until now, this happened only when play() was called. Change this, and abuse that get_delay() is called "relatively often" - this reports the underrun immediately in practice. Background: In commit 81e51a15f7e1 (and also e38b0b245ed4), we were quite confused about ALSA underrun handling. The commit message showed uncertainty how case 3 happened, but it's blindingly obvious and simple. Actually reading the code shows that ALSA does not have a concept of a "final chunk" (or we don't use it). It's obvious we never pass the AOPLAY_FINAL_CHUNK flag along to the ALSA API in any way. The only thing we do is simply writing a partial fragment. Of course this will cause an underrun. Doing a partial write saves us the trouble to pad the last frame with silence, or so. The main reason why the underrun message was avoided was that play() was never called with a non-0 sample count again (except if reset() was called before that). That was OK, at least the goal of avoiding the unwanted message was reached. (And the original "bogus" message at end of playback was perfectly correct, as far as ALSA goes.) If network stalls, play() will called again only once new data is available. Obviously, this could take a long time, thus it's too late.
-rw-r--r--audio/out/ao_alsa.c33
1 files changed, 27 insertions, 6 deletions
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index dfbe9f1ceb..3185cb4c4b 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -94,6 +94,7 @@ struct priv {
snd_pcm_format_t alsa_fmt;
bool can_pause;
bool paused;
+ bool final_chunk_written;
snd_pcm_sframes_t prepause_frames;
double delay_before_pause;
snd_pcm_uframes_t buffersize;
@@ -135,6 +136,18 @@ static bool check_device_present(struct ao *ao, int alsa_err)
return false;
}
+static void handle_underrun(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+
+ if (!p->final_chunk_written) {
+ MP_WARN(ao, "Device underrun detected.\n");
+ int err = snd_pcm_prepare(p->alsa);
+ CHECK_ALSA_ERROR("pcm prepare error");
+ alsa_error: ;
+ }
+}
+
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
struct priv *p = ao->priv;
@@ -975,8 +988,12 @@ static double get_delay(struct ao *ao)
if (p->paused)
return p->delay_before_pause;
- if (snd_pcm_delay(p->alsa, &delay) < 0)
+ int err = snd_pcm_delay(p->alsa, &delay);
+ if (err < 0) {
+ if (err == -EPIPE)
+ handle_underrun(ao);
return 0;
+ }
if (delay < 0) {
/* underrun - move the application pointer forward to catch up */
@@ -1082,6 +1099,7 @@ static void reset(struct ao *ao)
p->paused = false;
p->prepause_frames = 0;
p->delay_before_pause = 0;
+ p->final_chunk_written = false;
if (ao->stream_silence) {
soft_reset(ao);
@@ -1099,11 +1117,13 @@ static int play(struct ao *ao, void **data, int samples, int flags)
{
struct priv *p = ao->priv;
snd_pcm_sframes_t res = 0;
- if (!(flags & AOPLAY_FINAL_CHUNK))
+ bool final_chunk = flags & AOPLAY_FINAL_CHUNK;
+
+ if (!final_chunk)
samples = samples / p->outburst * p->outburst;
if (samples == 0)
- return 0;
+ goto done;
ao_convert_inplace(&p->convert, data, samples);
do {
@@ -1121,12 +1141,11 @@ static int play(struct ao *ao, void **data, int samples, int flags)
if (res == -ESTRPIPE) { /* suspend */
resume_device(ao);
} else if (res == -EPIPE) {
- MP_WARN(ao, "Device underrun detected.\n");
+ handle_underrun(ao);
} else {
MP_ERR(ao, "Write error: %s\n", snd_strerror(res));
}
- res = snd_pcm_prepare(p->alsa);
- int err = res;
+ int err = snd_pcm_prepare(p->alsa);
CHECK_ALSA_ERROR("pcm prepare error");
res = 0;
}
@@ -1134,6 +1153,8 @@ static int play(struct ao *ao, void **data, int samples, int flags)
p->paused = false;
+done:
+ p->final_chunk_written = res == samples && final_chunk;
return res < 0 ? -1 : res;
alsa_error: