audio/out/pull: avoid dropping some audio when draining

If the audio API takes a while for starting the audio callback, the
current heuristic can be off. In particular, with very short files, it
can happen that the audio callback is not called before playback is
stopped, so no audio is output at all.

Change draining so that it essentially waits for the ringbuffer to
empty. The assumption is that once the audio API has read the data
via the callback, it will always output it, even if the audio API
is stopped right after the callback has returned.
This commit is contained in:
wm4 2015-06-09 18:21:07 +02:00
parent a2b1c6d3f6
commit 211088943c
1 changed files with 18 additions and 9 deletions

View File

@ -206,15 +206,6 @@ static void resume(struct ao *ao)
ao->driver->resume(ao); ao->driver->resume(ao);
} }
static void drain(struct ao *ao)
{
struct ao_pull_state *p = ao->api_priv;
int state = atomic_load(&p->state);
if (IS_PLAYING(state))
mp_sleep_us(get_delay(ao) * 1000000);
reset(ao);
}
static bool get_eof(struct ao *ao) static bool get_eof(struct ao *ao)
{ {
struct ao_pull_state *p = ao->api_priv; struct ao_pull_state *p = ao->api_priv;
@ -223,6 +214,24 @@ static bool get_eof(struct ao *ao)
return mp_ring_buffered(p->buffers[0]) == 0; return mp_ring_buffered(p->buffers[0]) == 0;
} }
static void drain(struct ao *ao)
{
struct ao_pull_state *p = ao->api_priv;
int state = atomic_load(&p->state);
if (IS_PLAYING(state)) {
// Wait for lower bound.
mp_sleep_us(mp_ring_buffered(p->buffers[0]) / (double)ao->bps * 1e6);
// And then poll for actual end. (Unfortunately, this code considers
// audio APIs which do not want you to use mutexes in the audio
// callback, and an extra semaphore would require slightly more effort.)
// Limit to arbitrary ~250ms max. waiting for robustness.
int64_t max = mp_time_us() + 250000;
while (mp_time_us() < max && !get_eof(ao))
mp_sleep_us(1);
}
reset(ao);
}
static void uninit(struct ao *ao) static void uninit(struct ao *ao)
{ {
ao->driver->uninit(ao); ao->driver->uninit(ao);