0
0
mirror of https://github.com/mpv-player/mpv.git synced 2024-09-20 03:52:22 +02:00

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 81e51a15f7 (and also e38b0b245e), 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.
This commit is contained in:
wm4 2019-10-11 16:49:08 +02:00
parent ea4685b233
commit c6c93499cb

View File

@ -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: