From 31b380b2664e74d47f6862ecf44d62b0a82eddf2 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 14 Aug 2012 20:22:32 +0200 Subject: output/httpd: move code to _has_clients() --- src/output/httpd_output_plugin.c | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) (limited to 'src/output') diff --git a/src/output/httpd_output_plugin.c b/src/output/httpd_output_plugin.c index e7344320..f7c6127c 100644 --- a/src/output/httpd_output_plugin.c +++ b/src/output/httpd_output_plugin.c @@ -53,6 +53,31 @@ httpd_output_quark(void) return g_quark_from_static_string("httpd_output"); } +/** + * Check whether there is at least one client. + * + * Caller must lock the mutex. + */ +G_GNUC_PURE +static bool +httpd_output_has_clients(const struct httpd_output *httpd) +{ + return httpd->clients != NULL; +} + +/** + * Check whether there is at least one client. + */ +G_GNUC_PURE +static bool +httpd_output_lock_has_clients(const struct httpd_output *httpd) +{ + g_mutex_lock(httpd->mutex); + bool result = httpd_output_has_clients(httpd); + g_mutex_unlock(httpd->mutex); + return result; +} + static void httpd_listen_in_event(int fd, const struct sockaddr *address, size_t address_length, int uid, void *ctx); @@ -475,13 +500,8 @@ httpd_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error) { struct httpd_output *httpd = (struct httpd_output *)ao; - bool has_clients; - - g_mutex_lock(httpd->mutex); - has_clients = httpd->clients != NULL; - g_mutex_unlock(httpd->mutex); - if (has_clients) { + if (httpd_output_lock_has_clients(httpd)) { bool success; success = httpd_output_encode_and_play(httpd, chunk, size, @@ -502,11 +522,7 @@ httpd_output_pause(struct audio_output *ao) { struct httpd_output *httpd = (struct httpd_output *)ao; - g_mutex_lock(httpd->mutex); - bool has_clients = httpd->clients != NULL; - g_mutex_unlock(httpd->mutex); - - if (has_clients) { + if (httpd_output_lock_has_clients(httpd)) { static const char silence[1020]; return httpd_output_play(ao, silence, sizeof(silence), NULL) > 0; -- cgit v1.2.3 From 302972e9fc179fe17b8d658b8c5b4d47c1b8eeab Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 14 Aug 2012 21:39:33 +0200 Subject: output/httpd: fix throttling bug after resuming playback Reset the timer when paused and no client is connected. This fixes Mantis ticket 0003527. --- NEWS | 1 + src/output/httpd_output_plugin.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'src/output') diff --git a/NEWS b/NEWS index 9b72f6ba..27817a27 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ ver 0.17.2 (2012/??/??) - fix crash in local file check * output: - httpd: use monotonic clock, avoid hiccups after system clock adjustment + - httpd: fix throttling bug after resuming playback * mapper: fix non-UTF8 music directory name diff --git a/src/output/httpd_output_plugin.c b/src/output/httpd_output_plugin.c index f7c6127c..a063479d 100644 --- a/src/output/httpd_output_plugin.c +++ b/src/output/httpd_output_plugin.c @@ -422,6 +422,14 @@ httpd_output_delay(struct audio_output *ao) { struct httpd_output *httpd = (struct httpd_output *)ao; + if (!httpd_output_lock_has_clients(httpd) && httpd->base.pause) { + /* if there's no client and this output is paused, + then httpd_output_pause() will not do anything, it + will not fill the buffer and it will not update the + timer; therefore, we reset the timer here */ + timer_reset(httpd->timer); + } + return httpd->timer->started ? timer_delay(httpd->timer) : 0; -- cgit v1.2.3 From 249dcd967ede5ad20cda92a7c6c85ba303eed87f Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 14 Aug 2012 21:46:43 +0200 Subject: output/httpd: move delay from _pause() to _delay() --- src/output/httpd_output_plugin.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/output') diff --git a/src/output/httpd_output_plugin.c b/src/output/httpd_output_plugin.c index a063479d..abef826b 100644 --- a/src/output/httpd_output_plugin.c +++ b/src/output/httpd_output_plugin.c @@ -428,6 +428,11 @@ httpd_output_delay(struct audio_output *ao) will not fill the buffer and it will not update the timer; therefore, we reset the timer here */ timer_reset(httpd->timer); + + /* some arbitrary delay that is long enough to avoid + consuming too much CPU, and short enough to notice + new clients quickly enough */ + return 1000; } return httpd->timer->started @@ -535,7 +540,6 @@ httpd_output_pause(struct audio_output *ao) return httpd_output_play(ao, silence, sizeof(silence), NULL) > 0; } else { - g_usleep(100000); return true; } } -- cgit v1.2.3 From 51d793bec1f61e7048188ac4f86964486ab79142 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 14 Aug 2012 22:22:55 +0200 Subject: output/pulse: simplify _wait_stream() One large loop and only one pa_stream_get_state() call. --- src/output/pulse_output_plugin.c | 71 +++++++++------------------------------- 1 file changed, 16 insertions(+), 55 deletions(-) (limited to 'src/output') diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c index 0dc9be0e..42908fbc 100644 --- a/src/output/pulse_output_plugin.c +++ b/src/output/pulse_output_plugin.c @@ -681,35 +681,6 @@ pulse_output_close(struct audio_output *ao) pa_threaded_mainloop_unlock(po->mainloop); } -/** - * Check if the stream is (already) connected, and waits for a signal - * if not. The mainloop must be locked before calling this function. - * - * @return the current stream state - */ -static pa_stream_state_t -pulse_output_check_stream(struct pulse_output *po) -{ - pa_stream_state_t state = pa_stream_get_state(po->stream); - - assert(po->mainloop != NULL); - - switch (state) { - case PA_STREAM_READY: - case PA_STREAM_FAILED: - case PA_STREAM_TERMINATED: - case PA_STREAM_UNCONNECTED: - break; - - case PA_STREAM_CREATING: - pa_threaded_mainloop_wait(po->mainloop); - state = pa_stream_get_state(po->stream); - break; - } - - return state; -} - /** * Check if the stream is (already) connected, and waits if not. The * mainloop must be locked before calling this function. @@ -719,35 +690,25 @@ pulse_output_check_stream(struct pulse_output *po) static bool pulse_output_wait_stream(struct pulse_output *po, GError **error_r) { - pa_stream_state_t state = pa_stream_get_state(po->stream); - - switch (state) { - case PA_STREAM_READY: - return true; - - case PA_STREAM_FAILED: - case PA_STREAM_TERMINATED: - case PA_STREAM_UNCONNECTED: - g_set_error(error_r, pulse_output_quark(), 0, - "disconnected"); - return false; - - case PA_STREAM_CREATING: - break; - } + while (true) { + switch (pa_stream_get_state(po->stream)) { + case PA_STREAM_READY: + return true; - do { - state = pulse_output_check_stream(po); - } while (state == PA_STREAM_CREATING); + case PA_STREAM_FAILED: + case PA_STREAM_TERMINATED: + case PA_STREAM_UNCONNECTED: + g_set_error(error_r, pulse_output_quark(), + pa_context_errno(po->context), + "failed to connect the stream: %s", + pa_strerror(pa_context_errno(po->context))); + return false; - if (state != PA_STREAM_READY) { - g_set_error(error_r, pulse_output_quark(), 0, - "failed to connect the stream: %s", - pa_strerror(pa_context_errno(po->context))); - return false; + case PA_STREAM_CREATING: + pa_threaded_mainloop_wait(po->mainloop); + break; + } } - - return true; } /** -- cgit v1.2.3 From 335d5d5d72acb310e7853c64c1d2ca1404f9f62f Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 14 Aug 2012 22:30:46 +0200 Subject: output/pulse: implement method delay() Reduce command latency while paused. --- src/output/pulse_output_plugin.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'src/output') diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c index 42908fbc..e267427d 100644 --- a/src/output/pulse_output_plugin.c +++ b/src/output/pulse_output_plugin.c @@ -762,6 +762,24 @@ pulse_output_stream_pause(struct pulse_output *po, bool pause, return true; } +static unsigned +pulse_output_delay(struct audio_output *ao) +{ + struct pulse_output *po = (struct pulse_output *)ao; + unsigned result = 0; + + pa_threaded_mainloop_lock(po->mainloop); + + if (po->base.pause && pulse_output_stream_is_paused(po) && + pa_stream_get_state(po->stream) == PA_STREAM_READY) + /* idle while paused */ + result = 1000; + + pa_threaded_mainloop_unlock(po->mainloop); + + return result; +} + static size_t pulse_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error_r) @@ -889,13 +907,8 @@ pulse_output_pause(struct audio_output *ao) /* cork the stream */ - if (pulse_output_stream_is_paused(po)) { - /* already paused; due to a MPD API limitation, we - have to sleep a little bit here, to avoid hogging - the CPU */ - - g_usleep(50000); - } else if (!pulse_output_stream_pause(po, true, &error)) { + if (!pulse_output_stream_is_paused(po) && + !pulse_output_stream_pause(po, true, &error)) { pa_threaded_mainloop_unlock(po->mainloop); g_warning("%s", error->message); g_error_free(error); @@ -932,6 +945,7 @@ const struct audio_output_plugin pulse_output_plugin = { .enable = pulse_output_enable, .disable = pulse_output_disable, .open = pulse_output_open, + .delay = pulse_output_delay, .play = pulse_output_play, .cancel = pulse_output_cancel, .pause = pulse_output_pause, -- cgit v1.2.3 From c9aaabb5d4467047514d291ff652d516a7025486 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 14 Aug 2012 22:47:25 +0200 Subject: output/jack: implement method delay() Eliminate the g_usleep() call. --- src/output/jack_output_plugin.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/output') diff --git a/src/output/jack_output_plugin.c b/src/output/jack_output_plugin.c index a24cb855..d5c8ca41 100644 --- a/src/output/jack_output_plugin.c +++ b/src/output/jack_output_plugin.c @@ -608,6 +608,16 @@ mpd_jack_close(G_GNUC_UNUSED struct audio_output *ao) mpd_jack_stop(jd); } +static unsigned +mpd_jack_delay(struct audio_output *ao) +{ + struct jack_data *jd = (struct jack_data *)ao; + + return jd->base.pause && jd->pause && !jd->shutdown + ? 1000 + : 0; +} + static inline jack_default_audio_sample_t sample_16_to_jack(int16_t sample) { @@ -727,10 +737,6 @@ mpd_jack_pause(struct audio_output *ao) jd->pause = true; - /* due to a MPD API limitation, we have to sleep a little bit - here, to avoid hogging the CPU */ - g_usleep(50000); - return true; } @@ -742,6 +748,7 @@ const struct audio_output_plugin jack_output_plugin = { .enable = mpd_jack_enable, .disable = mpd_jack_disable, .open = mpd_jack_open, + .delay = mpd_jack_delay, .play = mpd_jack_play, .pause = mpd_jack_pause, .close = mpd_jack_close, -- cgit v1.2.3