mirror of https://github.com/mpv-player/mpv
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 commit81e51a15f7
(and alsoe38b0b245e
), 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:
parent
ea4685b233
commit
c6c93499cb
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue