Merge branch 'v0.17.x'
This commit is contained in:
commit
7d27d2ea5e
|
@ -232,6 +232,7 @@ src_mpd_SOURCES = \
|
|||
$(OUTPUT_API_SRC) \
|
||||
$(MIXER_API_SRC) \
|
||||
src/glib_socket.h \
|
||||
src/clock.c src/clock.h \
|
||||
src/notify.c \
|
||||
src/audio_config.c src/audio_config.h \
|
||||
src/audio_check.c \
|
||||
|
@ -1110,7 +1111,7 @@ test_dump_playlist_SOURCES = test/dump_playlist.c \
|
|||
src/audio_check.c src/pcm_buffer.c \
|
||||
src/text_input_stream.c src/fifo_buffer.c \
|
||||
src/cue/cue_parser.c src/cue/cue_parser.h \
|
||||
src/timer.c \
|
||||
src/timer.c src/clock.c \
|
||||
src/fd_util.c
|
||||
|
||||
if HAVE_FLAC
|
||||
|
@ -1137,7 +1138,7 @@ test_run_decoder_SOURCES = test/run_decoder.c \
|
|||
src/fd_util.c \
|
||||
src/audio_check.c \
|
||||
src/audio_format.c \
|
||||
src/timer.c \
|
||||
src/timer.c src/clock.c \
|
||||
$(ARCHIVE_SRC) \
|
||||
$(INPUT_SRC) \
|
||||
$(TAG_SRC) \
|
||||
|
@ -1159,7 +1160,7 @@ test_read_tags_SOURCES = test/read_tags.c \
|
|||
src/uri.c \
|
||||
src/fd_util.c \
|
||||
src/audio_check.c \
|
||||
src/timer.c \
|
||||
src/timer.c src/clock.c \
|
||||
$(DECODER_SRC)
|
||||
|
||||
if HAVE_ID3TAG
|
||||
|
@ -1281,7 +1282,7 @@ test_run_output_SOURCES = test/run_output.c \
|
|||
src/audio_check.c \
|
||||
src/audio_format.c \
|
||||
src/audio_parser.c \
|
||||
src/timer.c \
|
||||
src/timer.c src/clock.c \
|
||||
src/tag.c src/tag_pool.c \
|
||||
src/fifo_buffer.c src/growing_fifo.c \
|
||||
src/page.c \
|
||||
|
|
3
NEWS
3
NEWS
|
@ -4,6 +4,9 @@ ver 0.18 (2012/??/??)
|
|||
ver 0.17.2 (2012/??/??)
|
||||
* protocol:
|
||||
- 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
|
||||
|
||||
|
||||
|
|
|
@ -252,11 +252,12 @@ when saving playlists. The default is "no".
|
|||
This specifies the tag types that will be scanned for and made available to
|
||||
clients. Note that you must recreate (not update) your database for changes to
|
||||
this parameter to take effect. Possible values are artist, album, title,
|
||||
track, name, genre, date, composer, performer, comment, and disc. Multiple
|
||||
tags may be specified as a comma separated list. An example value is
|
||||
"artist,album,title,track". The special value "none" may be used alone to
|
||||
disable all metadata. The default is to use all known tag types except for
|
||||
comments.
|
||||
track, name, genre, date, composer, performer, comment, disc,
|
||||
musicbrainz_artistid, musicbrainz_albumid, musicbrainz_albumartistid,
|
||||
musicbrainz_trackid. Multiple tags may be specified as a comma separated list.
|
||||
An example value is "artist,album,title,track". The special value "none" may
|
||||
be used alone to disable all metadata. The default is to use all known tag
|
||||
types except for comments and those starting with "musicbrainz".
|
||||
.TP
|
||||
.B auto_update <yes or no>
|
||||
This specifies the wheter to support automatic update of music database when
|
||||
|
|
|
@ -113,10 +113,9 @@
|
|||
#
|
||||
#save_absolute_paths_in_playlists "no"
|
||||
#
|
||||
# This setting defines a list of tag types that will be extracted during the
|
||||
# audio file discovery process. Optionally, 'comment' can be added to this
|
||||
# list.
|
||||
#
|
||||
# This setting defines a list of tag types that will be extracted during the
|
||||
# audio file discovery process. The complete list of possible values can be
|
||||
# found in the mpd.conf man page.
|
||||
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
|
||||
#
|
||||
# This setting enables automatic update of MPD's database when files in
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
|
56
src/log.c
56
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -548,6 +548,6 @@ int mpd_main(int argc, char *argv[])
|
|||
WSACleanup();
|
||||
#endif
|
||||
|
||||
close_log_files();
|
||||
log_deinit();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
while (true) {
|
||||
switch (pa_stream_get_state(po->stream)) {
|
||||
case PA_STREAM_READY:
|
||||
return true;
|
||||
|
||||
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(),
|
||||
pa_context_errno(po->context),
|
||||
"failed to connect the stream: %s",
|
||||
pa_strerror(pa_context_errno(po->context)));
|
||||
return false;
|
||||
|
||||
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;
|
||||
case PA_STREAM_CREATING:
|
||||
pa_threaded_mainloop_wait(po->mainloop);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
state = pulse_output_check_stream(po);
|
||||
} while (state == PA_STREAM_CREATING);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
17
src/timer.c
17
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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue