Merge branch 'v0.17.x'
This commit is contained in:
commit
7d27d2ea5e
@ -232,6 +232,7 @@ src_mpd_SOURCES = \
|
|||||||
$(OUTPUT_API_SRC) \
|
$(OUTPUT_API_SRC) \
|
||||||
$(MIXER_API_SRC) \
|
$(MIXER_API_SRC) \
|
||||||
src/glib_socket.h \
|
src/glib_socket.h \
|
||||||
|
src/clock.c src/clock.h \
|
||||||
src/notify.c \
|
src/notify.c \
|
||||||
src/audio_config.c src/audio_config.h \
|
src/audio_config.c src/audio_config.h \
|
||||||
src/audio_check.c \
|
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/audio_check.c src/pcm_buffer.c \
|
||||||
src/text_input_stream.c src/fifo_buffer.c \
|
src/text_input_stream.c src/fifo_buffer.c \
|
||||||
src/cue/cue_parser.c src/cue/cue_parser.h \
|
src/cue/cue_parser.c src/cue/cue_parser.h \
|
||||||
src/timer.c \
|
src/timer.c src/clock.c \
|
||||||
src/fd_util.c
|
src/fd_util.c
|
||||||
|
|
||||||
if HAVE_FLAC
|
if HAVE_FLAC
|
||||||
@ -1137,7 +1138,7 @@ test_run_decoder_SOURCES = test/run_decoder.c \
|
|||||||
src/fd_util.c \
|
src/fd_util.c \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/audio_format.c \
|
src/audio_format.c \
|
||||||
src/timer.c \
|
src/timer.c src/clock.c \
|
||||||
$(ARCHIVE_SRC) \
|
$(ARCHIVE_SRC) \
|
||||||
$(INPUT_SRC) \
|
$(INPUT_SRC) \
|
||||||
$(TAG_SRC) \
|
$(TAG_SRC) \
|
||||||
@ -1159,7 +1160,7 @@ test_read_tags_SOURCES = test/read_tags.c \
|
|||||||
src/uri.c \
|
src/uri.c \
|
||||||
src/fd_util.c \
|
src/fd_util.c \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/timer.c \
|
src/timer.c src/clock.c \
|
||||||
$(DECODER_SRC)
|
$(DECODER_SRC)
|
||||||
|
|
||||||
if HAVE_ID3TAG
|
if HAVE_ID3TAG
|
||||||
@ -1281,7 +1282,7 @@ test_run_output_SOURCES = test/run_output.c \
|
|||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/audio_format.c \
|
src/audio_format.c \
|
||||||
src/audio_parser.c \
|
src/audio_parser.c \
|
||||||
src/timer.c \
|
src/timer.c src/clock.c \
|
||||||
src/tag.c src/tag_pool.c \
|
src/tag.c src/tag_pool.c \
|
||||||
src/fifo_buffer.c src/growing_fifo.c \
|
src/fifo_buffer.c src/growing_fifo.c \
|
||||||
src/page.c \
|
src/page.c \
|
||||||
|
3
NEWS
3
NEWS
@ -4,6 +4,9 @@ ver 0.18 (2012/??/??)
|
|||||||
ver 0.17.2 (2012/??/??)
|
ver 0.17.2 (2012/??/??)
|
||||||
* protocol:
|
* protocol:
|
||||||
- fix crash in local file check
|
- 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
|
* 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
|
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
|
clients. Note that you must recreate (not update) your database for changes to
|
||||||
this parameter to take effect. Possible values are artist, album, title,
|
this parameter to take effect. Possible values are artist, album, title,
|
||||||
track, name, genre, date, composer, performer, comment, and disc. Multiple
|
track, name, genre, date, composer, performer, comment, disc,
|
||||||
tags may be specified as a comma separated list. An example value is
|
musicbrainz_artistid, musicbrainz_albumid, musicbrainz_albumartistid,
|
||||||
"artist,album,title,track". The special value "none" may be used alone to
|
musicbrainz_trackid. Multiple tags may be specified as a comma separated list.
|
||||||
disable all metadata. The default is to use all known tag types except for
|
An example value is "artist,album,title,track". The special value "none" may
|
||||||
comments.
|
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
|
.TP
|
||||||
.B auto_update <yes or no>
|
.B auto_update <yes or no>
|
||||||
This specifies the wheter to support automatic update of music database when
|
This specifies the wheter to support automatic update of music database when
|
||||||
|
@ -113,10 +113,9 @@
|
|||||||
#
|
#
|
||||||
#save_absolute_paths_in_playlists "no"
|
#save_absolute_paths_in_playlists "no"
|
||||||
#
|
#
|
||||||
# This setting defines a list of tag types that will be extracted during the
|
# 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
|
# audio file discovery process. The complete list of possible values can be
|
||||||
# list.
|
# found in the mpd.conf man page.
|
||||||
#
|
|
||||||
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
|
#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
|
# This setting enables automatic update of MPD's database when files in
|
||||||
|
95
src/clock.c
Normal file
95
src/clock.c
Normal file
@ -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
|
||||||
|
}
|
||||||
|
|
41
src/clock.h
Normal file
41
src/clock.h
Normal file
@ -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_internal.h"
|
||||||
#include "input_plugin.h"
|
#include "input_plugin.h"
|
||||||
|
|
||||||
|
#include <libavutil/avutil.h>
|
||||||
#include <libavformat/avio.h>
|
#include <libavformat/avio.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "input_ffmpeg"
|
#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_ffmpeg {
|
||||||
struct input_stream base;
|
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 bool stdout_mode = true;
|
||||||
static int out_fd;
|
static int out_fd;
|
||||||
static const char *out_filename;
|
static char *out_filename;
|
||||||
|
|
||||||
static void redirect_logs(int fd)
|
static void redirect_logs(int fd)
|
||||||
{
|
{
|
||||||
@ -134,14 +134,15 @@ open_log_file(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
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();
|
out_fd = open_log_file();
|
||||||
if (out_fd < 0) {
|
if (out_fd < 0) {
|
||||||
g_set_error(error_r, log_quark(), errno,
|
g_set_error(error_r, log_quark(), errno,
|
||||||
"failed to open log file \"%s\" (config line %u): %s",
|
"failed to open log file \"%s\" (config line %u): %s",
|
||||||
path, line, g_strerror(errno));
|
out_filename, line, g_strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,22 +272,33 @@ log_init(bool verbose, bool use_stdout, GError **error_r)
|
|||||||
return true;
|
return true;
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
GError *error = NULL;
|
out_filename = config_dup_path(CONF_LOG_FILE, error_r);
|
||||||
char *path = config_dup_path(CONF_LOG_FILE, &error);
|
return out_filename != NULL &&
|
||||||
if (path == NULL) {
|
log_init_file(param->line, error_r);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
void setup_log_output(bool use_stdout)
|
||||||
{
|
{
|
||||||
fflush(NULL);
|
fflush(NULL);
|
||||||
@ -327,15 +339,3 @@ int cycle_log_files(void)
|
|||||||
g_debug("Done cycling log files\n");
|
g_debug("Done cycling log files\n");
|
||||||
return 0;
|
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
|
bool
|
||||||
log_init(bool verbose, bool use_stdout, GError **error_r);
|
log_init(bool verbose, bool use_stdout, GError **error_r);
|
||||||
|
|
||||||
|
void
|
||||||
|
log_deinit(void);
|
||||||
|
|
||||||
void setup_log_output(bool use_stdout);
|
void setup_log_output(bool use_stdout);
|
||||||
|
|
||||||
int cycle_log_files(void);
|
int cycle_log_files(void);
|
||||||
|
|
||||||
void close_log_files(void);
|
|
||||||
|
|
||||||
#endif /* LOG_H */
|
#endif /* LOG_H */
|
||||||
|
@ -548,6 +548,6 @@ int mpd_main(int argc, char *argv[])
|
|||||||
WSACleanup();
|
WSACleanup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
close_log_files();
|
log_deinit();
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,31 @@ httpd_output_quark(void)
|
|||||||
return g_quark_from_static_string("httpd_output");
|
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
|
static void
|
||||||
httpd_listen_in_event(int fd, const struct sockaddr *address,
|
httpd_listen_in_event(int fd, const struct sockaddr *address,
|
||||||
size_t address_length, int uid, void *ctx);
|
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;
|
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
|
return httpd->timer->started
|
||||||
? timer_delay(httpd->timer)
|
? timer_delay(httpd->timer)
|
||||||
: 0;
|
: 0;
|
||||||
@ -475,13 +513,8 @@ httpd_output_play(struct audio_output *ao, const void *chunk, size_t size,
|
|||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
struct httpd_output *httpd = (struct httpd_output *)ao;
|
struct httpd_output *httpd = (struct httpd_output *)ao;
|
||||||
bool has_clients;
|
|
||||||
|
|
||||||
g_mutex_lock(httpd->mutex);
|
if (httpd_output_lock_has_clients(httpd)) {
|
||||||
has_clients = httpd->clients != NULL;
|
|
||||||
g_mutex_unlock(httpd->mutex);
|
|
||||||
|
|
||||||
if (has_clients) {
|
|
||||||
bool success;
|
bool success;
|
||||||
|
|
||||||
success = httpd_output_encode_and_play(httpd, chunk, size,
|
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;
|
struct httpd_output *httpd = (struct httpd_output *)ao;
|
||||||
|
|
||||||
g_mutex_lock(httpd->mutex);
|
if (httpd_output_lock_has_clients(httpd)) {
|
||||||
bool has_clients = httpd->clients != NULL;
|
|
||||||
g_mutex_unlock(httpd->mutex);
|
|
||||||
|
|
||||||
if (has_clients) {
|
|
||||||
static const char silence[1020];
|
static const char silence[1020];
|
||||||
return httpd_output_play(ao, silence, sizeof(silence),
|
return httpd_output_play(ao, silence, sizeof(silence),
|
||||||
NULL) > 0;
|
NULL) > 0;
|
||||||
} else {
|
} else {
|
||||||
g_usleep(100000);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -608,6 +608,16 @@ mpd_jack_close(G_GNUC_UNUSED struct audio_output *ao)
|
|||||||
mpd_jack_stop(jd);
|
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
|
static inline jack_default_audio_sample_t
|
||||||
sample_16_to_jack(int16_t sample)
|
sample_16_to_jack(int16_t sample)
|
||||||
{
|
{
|
||||||
@ -727,10 +737,6 @@ mpd_jack_pause(struct audio_output *ao)
|
|||||||
|
|
||||||
jd->pause = true;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -742,6 +748,7 @@ const struct audio_output_plugin jack_output_plugin = {
|
|||||||
.enable = mpd_jack_enable,
|
.enable = mpd_jack_enable,
|
||||||
.disable = mpd_jack_disable,
|
.disable = mpd_jack_disable,
|
||||||
.open = mpd_jack_open,
|
.open = mpd_jack_open,
|
||||||
|
.delay = mpd_jack_delay,
|
||||||
.play = mpd_jack_play,
|
.play = mpd_jack_play,
|
||||||
.pause = mpd_jack_pause,
|
.pause = mpd_jack_pause,
|
||||||
.close = mpd_jack_close,
|
.close = mpd_jack_close,
|
||||||
|
@ -681,35 +681,6 @@ pulse_output_close(struct audio_output *ao)
|
|||||||
pa_threaded_mainloop_unlock(po->mainloop);
|
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
|
* Check if the stream is (already) connected, and waits if not. The
|
||||||
* mainloop must be locked before calling this function.
|
* mainloop must be locked before calling this function.
|
||||||
@ -719,35 +690,25 @@ pulse_output_check_stream(struct pulse_output *po)
|
|||||||
static bool
|
static bool
|
||||||
pulse_output_wait_stream(struct pulse_output *po, GError **error_r)
|
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_FAILED:
|
||||||
case PA_STREAM_READY:
|
case PA_STREAM_TERMINATED:
|
||||||
return true;
|
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_CREATING:
|
||||||
case PA_STREAM_TERMINATED:
|
pa_threaded_mainloop_wait(po->mainloop);
|
||||||
case PA_STREAM_UNCONNECTED:
|
break;
|
||||||
g_set_error(error_r, pulse_output_quark(), 0,
|
}
|
||||||
"disconnected");
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case PA_STREAM_CREATING:
|
|
||||||
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;
|
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
|
static size_t
|
||||||
pulse_output_play(struct audio_output *ao, const void *chunk, size_t size,
|
pulse_output_play(struct audio_output *ao, const void *chunk, size_t size,
|
||||||
GError **error_r)
|
GError **error_r)
|
||||||
@ -928,13 +907,8 @@ pulse_output_pause(struct audio_output *ao)
|
|||||||
|
|
||||||
/* cork the stream */
|
/* cork the stream */
|
||||||
|
|
||||||
if (pulse_output_stream_is_paused(po)) {
|
if (!pulse_output_stream_is_paused(po) &&
|
||||||
/* already paused; due to a MPD API limitation, we
|
!pulse_output_stream_pause(po, true, &error)) {
|
||||||
have to sleep a little bit here, to avoid hogging
|
|
||||||
the CPU */
|
|
||||||
|
|
||||||
g_usleep(50000);
|
|
||||||
} else if (!pulse_output_stream_pause(po, true, &error)) {
|
|
||||||
pa_threaded_mainloop_unlock(po->mainloop);
|
pa_threaded_mainloop_unlock(po->mainloop);
|
||||||
g_warning("%s", error->message);
|
g_warning("%s", error->message);
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
@ -971,6 +945,7 @@ const struct audio_output_plugin pulse_output_plugin = {
|
|||||||
.enable = pulse_output_enable,
|
.enable = pulse_output_enable,
|
||||||
.disable = pulse_output_disable,
|
.disable = pulse_output_disable,
|
||||||
.open = pulse_output_open,
|
.open = pulse_output_open,
|
||||||
|
.delay = pulse_output_delay,
|
||||||
.play = pulse_output_play,
|
.play = pulse_output_play,
|
||||||
.cancel = pulse_output_cancel,
|
.cancel = pulse_output_cancel,
|
||||||
.pause = pulse_output_pause,
|
.pause = pulse_output_pause,
|
||||||
|
17
src/timer.c
17
src/timer.c
@ -20,23 +20,14 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "audio_format.h"
|
#include "audio_format.h"
|
||||||
|
#include "clock.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <sys/time.h>
|
|
||||||
#include <stddef.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_new(const struct audio_format *af)
|
||||||
{
|
{
|
||||||
struct timer *timer = g_new(struct timer, 1);
|
struct timer *timer = g_new(struct timer, 1);
|
||||||
@ -54,7 +45,7 @@ void timer_free(struct timer *timer)
|
|||||||
|
|
||||||
void timer_start(struct timer *timer)
|
void timer_start(struct timer *timer)
|
||||||
{
|
{
|
||||||
timer->time = now();
|
timer->time = monotonic_clock_us();
|
||||||
timer->started = 1;
|
timer->started = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +65,7 @@ void timer_add(struct timer *timer, int size)
|
|||||||
unsigned
|
unsigned
|
||||||
timer_delay(const struct timer *timer)
|
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)
|
if (delay < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -90,7 +81,7 @@ void timer_sync(struct timer *timer)
|
|||||||
|
|
||||||
assert(timer->started);
|
assert(timer->started);
|
||||||
|
|
||||||
sleep_duration = timer->time - now();
|
sleep_duration = timer->time - monotonic_clock_us();
|
||||||
if (sleep_duration > 0)
|
if (sleep_duration > 0)
|
||||||
g_usleep(sleep_duration);
|
g_usleep(sleep_duration);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user