diff options
author | Max Kellermann <max@duempel.org> | 2012-08-14 23:58:54 +0200 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2012-08-14 23:58:54 +0200 |
commit | 7d27d2ea5e8b622a288c80518bc0daec53dbbc93 (patch) | |
tree | b1b9e98369919d66869b3ff1626372bf16a38c23 /src | |
parent | 5cc3338267214eb050e39bc509d8b4258cec6afd (diff) | |
parent | dc22846d58264bfae3b4516e2de1614b3b97a5ca (diff) | |
download | mpd-7d27d2ea5e8b622a288c80518bc0daec53dbbc93.tar.gz mpd-7d27d2ea5e8b622a288c80518bc0daec53dbbc93.tar.xz mpd-7d27d2ea5e8b622a288c80518bc0daec53dbbc93.zip |
Merge branch 'v0.17.x'
Diffstat (limited to '')
-rw-r--r-- | src/clock.c | 95 | ||||
-rw-r--r-- | src/clock.h | 41 | ||||
-rw-r--r-- | src/input/ffmpeg_input_plugin.c | 5 | ||||
-rw-r--r-- | src/log.c | 56 | ||||
-rw-r--r-- | src/log.h | 5 | ||||
-rw-r--r-- | src/main.c | 2 | ||||
-rw-r--r-- | src/output/httpd_output_plugin.c | 52 | ||||
-rw-r--r-- | src/output/jack_output_plugin.c | 15 | ||||
-rw-r--r-- | src/output/pulse_output_plugin.c | 99 | ||||
-rw-r--r-- | src/timer.c | 17 |
10 files changed, 261 insertions, 126 deletions
diff --git a/src/clock.c b/src/clock.c new file mode 100644 index 000000000..4100fa2d8 --- /dev/null +++ b/src/clock.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2003-2012 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "clock.h" + +#ifdef WIN32 +#include <windows.h> +#elif defined(__APPLE__) +#include <mach/mach_time.h> +#else +#include <time.h> +#endif + +unsigned +monotonic_clock_ms(void) +{ +#ifdef WIN32 + return GetTickCount(); +#elif defined(__APPLE__) /* OS X does not define CLOCK_MONOTONIC */ + static mach_timebase_info_data_t base; + if (base.denom == 0) + (void)mach_timebase_info(&base); + + return (unsigned)((mach_absolute_time() * base.numer) + / (1000000 * base.denom)); +#elif defined(CLOCK_MONOTONIC) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +#else + /* we have no monotonic clock, fall back to gettimeofday() */ + struct timeval tv; + gettimeofday(&tv, 0); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +#endif +} + +uint64_t +monotonic_clock_us(void) +{ +#ifdef WIN32 + LARGE_INTEGER l_value, l_frequency; + + if (!QueryPerformanceCounter(&l_value) || + !QueryPerformanceFrequency(&l_frequency)) + return 0; + + uint64_t value = l_value.QuadPart; + uint64_t frequency = l_frequency.QuadPart; + + if (frequency > 1000000) { + value *= 10000; + value /= frequency / 100; + } else if (frequency < 1000000) { + value *= 10000; + value /= frequency; + value *= 100; + } + + return value; +#elif defined(__APPLE__) /* OS X does not define CLOCK_MONOTONIC */ + static mach_timebase_info_data_t base; + if (base.denom == 0) + (void)mach_timebase_info(&base); + + return ((uint64_t)mach_absolute_time() * (uint64_t)base.numer) + / (1000 * (uint64_t)base.denom); +#elif defined(CLOCK_MONOTONIC) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000000 + (uint64_t)(ts.tv_nsec / 1000); +#else + /* we have no monotonic clock, fall back to gettimeofday() */ + struct timeval tv; + gettimeofday(&tv, 0); + return (uint64_t)tv.tv_sec * 1000 + (uint64_t)(tv.tv_usec) / 1000(; +#endif +} + diff --git a/src/clock.h b/src/clock.h new file mode 100644 index 000000000..f1338938f --- /dev/null +++ b/src/clock.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2003-2012 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_CLOCK_H +#define MPD_CLOCK_H + +#include "gcc.h" + +#include <stdint.h> + +/** + * Returns the value of a monotonic clock in milliseconds. + */ +gcc_pure +unsigned +monotonic_clock_ms(void); + +/** + * Returns the value of a monotonic clock in microseconds. + */ +gcc_pure +uint64_t +monotonic_clock_us(void); + +#endif diff --git a/src/input/ffmpeg_input_plugin.c b/src/input/ffmpeg_input_plugin.c index d71b3d4c0..6d339a067 100644 --- a/src/input/ffmpeg_input_plugin.c +++ b/src/input/ffmpeg_input_plugin.c @@ -22,16 +22,13 @@ #include "input_internal.h" #include "input_plugin.h" +#include <libavutil/avutil.h> #include <libavformat/avio.h> #include <libavformat/avformat.h> #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "input_ffmpeg" -#ifndef AV_VERSION_INT -#define AV_VERSION_INT(a, b, c) (a<<16 | b<<8 | c) -#endif - struct input_ffmpeg { struct input_stream base; @@ -55,7 +55,7 @@ static const char *log_charset; static bool stdout_mode = true; static int out_fd; -static const char *out_filename; +static char *out_filename; static void redirect_logs(int fd) { @@ -134,14 +134,15 @@ open_log_file(void) } static bool -log_init_file(const char *path, unsigned line, GError **error_r) +log_init_file(unsigned line, GError **error_r) { - out_filename = path; + assert(out_filename != NULL); + out_fd = open_log_file(); if (out_fd < 0) { g_set_error(error_r, log_quark(), errno, "failed to open log file \"%s\" (config line %u): %s", - path, line, g_strerror(errno)); + out_filename, line, g_strerror(errno)); return false; } @@ -271,22 +272,33 @@ log_init(bool verbose, bool use_stdout, GError **error_r) return true; #endif } else { - GError *error = NULL; - char *path = config_dup_path(CONF_LOG_FILE, &error); - if (path == NULL) { - assert(error != NULL); - g_propagate_error(error_r, error); - return false; - } - - bool success = log_init_file(path, param->line, - error_r); - g_free(path); - return success; + out_filename = config_dup_path(CONF_LOG_FILE, error_r); + return out_filename != NULL && + log_init_file(param->line, error_r); } } } +static void +close_log_files(void) +{ + if (stdout_mode) + return; + +#ifdef HAVE_SYSLOG + if (out_filename == NULL) + closelog(); +#endif +} + +void +log_deinit(void) +{ + close_log_files(); + g_free(out_filename); +} + + void setup_log_output(bool use_stdout) { fflush(NULL); @@ -327,15 +339,3 @@ int cycle_log_files(void) g_debug("Done cycling log files\n"); return 0; } - -void close_log_files(void) -{ - if (stdout_mode) - return; - -#ifdef HAVE_SYSLOG - if (out_filename == NULL) - closelog(); -#endif -} - @@ -44,10 +44,11 @@ log_early_init(bool verbose); bool log_init(bool verbose, bool use_stdout, GError **error_r); +void +log_deinit(void); + void setup_log_output(bool use_stdout); int cycle_log_files(void); -void close_log_files(void); - #endif /* LOG_H */ diff --git a/src/main.c b/src/main.c index a54ca9f3d..fea31782f 100644 --- a/src/main.c +++ b/src/main.c @@ -548,6 +548,6 @@ int mpd_main(int argc, char *argv[]) WSACleanup(); #endif - close_log_files(); + log_deinit(); return EXIT_SUCCESS; } diff --git a/src/output/httpd_output_plugin.c b/src/output/httpd_output_plugin.c index e7344320c..abef826bc 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); @@ -397,6 +422,19 @@ 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); + + /* 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 ? timer_delay(httpd->timer) : 0; @@ -475,13 +513,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,16 +535,11 @@ 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; } else { - g_usleep(100000); return true; } } diff --git a/src/output/jack_output_plugin.c b/src/output/jack_output_plugin.c index a24cb8557..d5c8ca412 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, diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c index 0dc9be0e4..e267427df 100644 --- a/src/output/pulse_output_plugin.c +++ b/src/output/pulse_output_plugin.c @@ -682,35 +682,6 @@ pulse_output_close(struct audio_output *ao) } /** - * 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; } /** @@ -801,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) @@ -928,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); @@ -971,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, diff --git a/src/timer.c b/src/timer.c index 691ab76be..2d9550706 100644 --- a/src/timer.c +++ b/src/timer.c @@ -20,23 +20,14 @@ #include "config.h" #include "timer.h" #include "audio_format.h" +#include "clock.h" #include <glib.h> #include <assert.h> #include <limits.h> -#include <sys/time.h> #include <stddef.h> -static uint64_t now(void) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - return ((uint64_t)tv.tv_sec * 1000000) + tv.tv_usec; -} - struct timer *timer_new(const struct audio_format *af) { struct timer *timer = g_new(struct timer, 1); @@ -54,7 +45,7 @@ void timer_free(struct timer *timer) void timer_start(struct timer *timer) { - timer->time = now(); + timer->time = monotonic_clock_us(); timer->started = 1; } @@ -74,7 +65,7 @@ void timer_add(struct timer *timer, int size) unsigned timer_delay(const struct timer *timer) { - int64_t delay = (int64_t)(timer->time - now()) / 1000; + int64_t delay = (int64_t)(timer->time - monotonic_clock_us()) / 1000; if (delay < 0) return 0; @@ -90,7 +81,7 @@ void timer_sync(struct timer *timer) assert(timer->started); - sleep_duration = timer->time - now(); + sleep_duration = timer->time - monotonic_clock_us(); if (sleep_duration > 0) g_usleep(sleep_duration); } |