From 5929dc458f46f75648af1ee7a293e899e67d8e66 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 29 May 2014 23:56:48 +0200 Subject: audio/out/push: add mechanism for event-based waiting Until now, we've always calculated a timeout based on a heuristic when to refill the audio buffers. Allow AOs to do it completely event-based by providing wait and wakeup callbacks. This also shuffles around the heuristic used for other AOs, and there is a minor possibility that behavior slightly changes in real-world cases. But in general it should be much more robust now. ao_pulse.c now makes use of event-based waiting. It already did before, but the code for time-based waiting was also involved. This commit also removes one awkward artifact of the PulseAudio API out of the generic code: the callback asking for more data can be reentrant, and thus requires a separate lock for waiting (or a recursive mutex). --- audio/out/ao_pulse.c | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) (limited to 'audio/out/ao_pulse.c') diff --git a/audio/out/ao_pulse.c b/audio/out/ao_pulse.c index e327643154..dad21fe1f1 100644 --- a/audio/out/ao_pulse.c +++ b/audio/out/ao_pulse.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -55,6 +56,11 @@ struct priv { bool broken_pause; int retval; + // for wakeup handling + pthread_mutex_t wakeup_lock; + pthread_cond_t wakeup; + int wakeup_status; + char *cfg_host; char *cfg_sink; int cfg_buffer; @@ -90,14 +96,38 @@ static void stream_state_cb(pa_stream *s, void *userdata) } } +static void wakeup(struct ao *ao) +{ + struct priv *priv = ao->priv; + pthread_mutex_lock(&priv->wakeup_lock); + priv->wakeup_status = 1; + pthread_cond_signal(&priv->wakeup); + pthread_mutex_unlock(&priv->wakeup_lock); +} + static void stream_request_cb(pa_stream *s, size_t length, void *userdata) { struct ao *ao = userdata; struct priv *priv = ao->priv; - ao_need_data(ao); + wakeup(ao); pa_threaded_mainloop_signal(priv->mainloop, 0); } +static int wait(struct ao *ao, pthread_mutex_t *lock) +{ + struct priv *priv = ao->priv; + // We don't use this mutex, because pulse like to call stream_request_cb + // while we have the central mutex held. + pthread_mutex_unlock(lock); + pthread_mutex_lock(&priv->wakeup_lock); + while (!priv->wakeup_status) + pthread_cond_wait(&priv->wakeup, &priv->wakeup_lock); + priv->wakeup_status = 0; + pthread_mutex_unlock(&priv->wakeup_lock); + pthread_mutex_lock(lock); + return 0; +} + static void stream_latency_update_cb(pa_stream *s, void *userdata) { struct ao *ao = userdata; @@ -237,6 +267,9 @@ static void uninit(struct ao *ao) pa_threaded_mainloop_free(priv->mainloop); priv->mainloop = NULL; } + + pthread_cond_destroy(&priv->wakeup); + pthread_mutex_destroy(&priv->wakeup_lock); } static int init(struct ao *ao) @@ -249,6 +282,9 @@ static int init(struct ao *ao) char *sink = priv->cfg_sink && priv->cfg_sink[0] ? priv->cfg_sink : NULL; const char *version = pa_get_library_version(); + pthread_mutex_init(&priv->wakeup_lock, NULL); + pthread_cond_init(&priv->wakeup, NULL); + ao->per_application_mixer = true; priv->broken_pause = false; @@ -626,6 +662,8 @@ const struct ao_driver audio_out_pulse = { .pause = pause, .resume = resume, .drain = drain, + .wait = wait, + .wakeup = wakeup, .priv_size = sizeof(struct priv), .priv_defaults = &(const struct priv) { .cfg_buffer = 250, -- cgit v1.2.3