diff options
-rw-r--r-- | audio/out/ao.c | 2 | ||||
-rw-r--r-- | audio/out/internal.h | 2 | ||||
-rw-r--r-- | audio/out/pull.c | 9 | ||||
-rw-r--r-- | audio/out/push.c | 45 |
4 files changed, 52 insertions, 6 deletions
diff --git a/audio/out/ao.c b/audio/out/ao.c index f63f3fd09b..56d9232189 100644 --- a/audio/out/ao.c +++ b/audio/out/ao.c @@ -322,7 +322,7 @@ void ao_drain(struct ao *ao) bool ao_eof_reached(struct ao *ao) { - return ao_get_delay(ao) < AO_EOF_DELAY; + return ao->api->get_eof ? ao->api->get_eof(ao) : true; } bool ao_chmap_sel_adjust(struct ao *ao, const struct mp_chmap_sel *s, diff --git a/audio/out/internal.h b/audio/out/internal.h index 4ee7f00f8a..e43b81a5ca 100644 --- a/audio/out/internal.h +++ b/audio/out/internal.h @@ -132,6 +132,8 @@ struct ao_driver { float (*get_delay)(struct ao *ao); // push based: block until all queued audio is played (optional) void (*drain)(struct ao *ao); + // Optional. Return true if audio has stopped in any way. + bool (*get_eof)(struct ao *ao); // Wait until the audio buffer needs to be refilled. The lock is the // internal mutex usually protecting the internal AO state (and used to // protect driver calls), and must be temporarily unlocked while waiting. diff --git a/audio/out/pull.c b/audio/out/pull.c index 0613006f07..002b74fed6 100644 --- a/audio/out/pull.c +++ b/audio/out/pull.c @@ -201,6 +201,14 @@ static void drain(struct ao *ao) reset(ao); } +static bool get_eof(struct ao *ao) +{ + struct ao_pull_state *p = ao->api_priv; + // For simplicity, ignore the latency. Otherwise, we would have to run an + // extra thread to time it. + return mp_ring_buffered(p->buffers[0]) == 0; +} + static void uninit(struct ao *ao) { ao->driver->uninit(ao); @@ -225,6 +233,7 @@ const struct ao_driver ao_api_pull = { .get_space = get_space, .play = play, .get_delay = get_delay, + .get_eof = get_eof, .pause = pause, .resume = resume, .priv_size = sizeof(struct ao_pull_state), diff --git a/audio/out/push.c b/audio/out/push.c index 4a1f2a06c4..5e49595978 100644 --- a/audio/out/push.c +++ b/audio/out/push.c @@ -54,6 +54,7 @@ struct ao_push_state { bool drain; bool buffers_full; bool avoid_ao_wait; + bool still_playing; bool need_wakeup; bool requested_data; bool paused; @@ -87,15 +88,13 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg) return r; } -static float get_delay(struct ao *ao) +static double unlocked_get_delay(struct ao *ao) { struct ao_push_state *p = ao->api_priv; - pthread_mutex_lock(&p->lock); double driver_delay = 0; if (ao->driver->get_delay) driver_delay = ao->driver->get_delay(ao); double delay = driver_delay + mp_audio_buffer_seconds(p->buffer); - pthread_mutex_unlock(&p->lock); if (delay >= AO_EOF_DELAY && p->expected_end_time) { if (mp_time_sec() > p->expected_end_time) { MP_ERR(ao, "Audio device EOF reporting is broken!\n"); @@ -106,6 +105,15 @@ static float get_delay(struct ao *ao) return delay; } +static float get_delay(struct ao *ao) +{ + struct ao_push_state *p = ao->api_priv; + pthread_mutex_lock(&p->lock); + float delay = unlocked_get_delay(ao); + pthread_mutex_unlock(&p->lock); + return delay; +} + static void reset(struct ao *ao) { struct ao_push_state *p = ao->api_priv; @@ -114,6 +122,7 @@ static void reset(struct ao *ao) ao->driver->reset(ao); mp_audio_buffer_clear(p->buffer); p->paused = false; + p->still_playing = false; wakeup_playthread(ao); pthread_mutex_unlock(&p->lock); } @@ -190,6 +199,15 @@ static int get_space(struct ao *ao) return space; } +static bool get_eof(struct ao *ao) +{ + struct ao_push_state *p = ao->api_priv; + pthread_mutex_lock(&p->lock); + bool eof = !p->still_playing; + pthread_mutex_unlock(&p->lock); + return eof; +} + static int play(struct ao *ao, void **data, int samples, int flags) { struct ao_push_state *p = ao->api_priv; @@ -215,6 +233,7 @@ static int play(struct ao *ao, void **data, int samples, int flags) p->expected_end_time = 0; p->final_chunk = is_final; p->paused = false; + p->still_playing |= write_samples > 0; // If we don't have new data, the decoder thread basically promises it // will send new data as soon as it's available. @@ -266,6 +285,7 @@ static void ao_play_data(struct ao *ao) // any new data (due to rounding to period boundaries). p->buffers_full = max >= space && r <= 0; p->avoid_ao_wait = (max == 0 && space > 0) || p->paused || stuck; + p->still_playing |= r > 0; MP_TRACE(ao, "in=%d, space=%d r=%d flags=%d aw=%d full=%d f=%d\n", max, space, r, flags, p->avoid_ao_wait, p->buffers_full, p->final_chunk); } @@ -319,9 +339,23 @@ static void *playthread(void *arg) // The most important part is that the decoder is woken up, so // that the decoder will wake up us in turn. MP_TRACE(ao, "buffer inactive.\n"); - if (!p->requested_data) + + bool was_playing = p->still_playing; + double timeout = -1; + if (p->still_playing && !p->paused) { + timeout = unlocked_get_delay(ao); + if (timeout < AO_EOF_DELAY) + p->still_playing = false; + } + + if (!p->requested_data || (was_playing && !p->still_playing)) mp_input_wakeup(ao->input_ctx); - pthread_cond_wait(&p->wakeup, &p->lock); + + if (p->still_playing && timeout > 0) { + mpthread_cond_timedwait_rel(&p->wakeup, &p->lock, timeout); + } else { + pthread_cond_wait(&p->wakeup, &p->lock); + } } else { if (!ao->driver->wait || ao->driver->wait(ao, &p->lock) < 0) { // Fallback to guessing. @@ -390,6 +424,7 @@ const struct ao_driver ao_api_push = { .pause = audio_pause, .resume = resume, .drain = drain, + .get_eof = get_eof, .priv_size = sizeof(struct ao_push_state), }; |