aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2012-08-14 23:58:54 +0200
committerMax Kellermann <max@duempel.org>2012-08-14 23:58:54 +0200
commit7d27d2ea5e8b622a288c80518bc0daec53dbbc93 (patch)
treeb1b9e98369919d66869b3ff1626372bf16a38c23 /src
parent5cc3338267214eb050e39bc509d8b4258cec6afd (diff)
parentdc22846d58264bfae3b4516e2de1614b3b97a5ca (diff)
Merge branch 'v0.17.x'
Diffstat (limited to 'src')
-rw-r--r--src/clock.c95
-rw-r--r--src/clock.h41
-rw-r--r--src/input/ffmpeg_input_plugin.c5
-rw-r--r--src/log.c56
-rw-r--r--src/log.h5
-rw-r--r--src/main.c2
-rw-r--r--src/output/httpd_output_plugin.c52
-rw-r--r--src/output/jack_output_plugin.c15
-rw-r--r--src/output/pulse_output_plugin.c99
-rw-r--r--src/timer.c17
10 files changed, 261 insertions, 126 deletions
diff --git a/src/clock.c b/src/clock.c
new file mode 100644
index 00000000..4100fa2d
--- /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 00000000..f1338938
--- /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 d71b3d4c..6d339a06 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;
diff --git a/src/log.c b/src/log.c
index 86dd86ea..2d3c7caf 100644
--- a/src/log.c
+++ b/src/log.c
@@ -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
-}
-
diff --git a/src/log.h b/src/log.h
index 75e386b2..683ff3e9 100644
--- a/src/log.h
+++ b/src/log.h
@@ -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 a54ca9f3..fea31782 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 e7344320..abef826b 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 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,
diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c
index 0dc9be0e..e267427d 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 691ab76b..2d955070 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);
}