Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d4b7e3de0 | ||
|
|
1674a4ec82 | ||
|
|
ce370bee60 | ||
|
|
2a1f4539f6 | ||
|
|
03018611f8 | ||
|
|
e6c3acaa6f | ||
|
|
0022fb100b | ||
|
|
4f2d67dfb0 | ||
|
|
b75d53413d | ||
|
|
c44a744c0b | ||
|
|
60b4f6b3eb | ||
|
|
546232b1c0 | ||
|
|
42c5788de3 | ||
|
|
23cd8a74be | ||
|
|
cc1debc948 | ||
|
|
ad52eb236d | ||
|
|
462bba8e2f | ||
|
|
dec7090198 | ||
|
|
83ec0e5552 | ||
|
|
cc261872c2 | ||
|
|
5223261f19 | ||
|
|
c594afeee7 | ||
|
|
32d10eedbd | ||
|
|
dfd98eede7 | ||
|
|
a728d7a026 | ||
|
|
e8d8bd4c0d | ||
|
|
8d5fa754e8 | ||
|
|
2ee047a1dd | ||
|
|
9562f66741 | ||
|
|
21223154aa | ||
|
|
a549d871f3 |
25
NEWS
25
NEWS
@@ -1,3 +1,28 @@
|
|||||||
|
ver 0.15.16 (2011/03/13)
|
||||||
|
* output:
|
||||||
|
- ao: initialize the ao_sample_format struct
|
||||||
|
- jack: fix crash with mono playback
|
||||||
|
* encoders:
|
||||||
|
- lame: explicitly configure the output sample rate
|
||||||
|
* update: log all file permission problems
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.15.15 (2010/11/08)
|
||||||
|
* input:
|
||||||
|
- rewind: fix assertion failure
|
||||||
|
* output:
|
||||||
|
- shout: artist comes first in stream title
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.15.14 (2010/11/06)
|
||||||
|
* player_thread: fix assertion failure due to wrong music pipe on seek
|
||||||
|
* output_thread: fix assertion failure due to race condition in OPEN
|
||||||
|
* input:
|
||||||
|
- rewind: fix double free bug
|
||||||
|
* decoders:
|
||||||
|
- mp4ff, ffmpeg: add extension ".m4b" (audio book)
|
||||||
|
|
||||||
|
|
||||||
ver 0.15.13 (2010/10/10)
|
ver 0.15.13 (2010/10/10)
|
||||||
* output_thread: fix race condition after CANCEL command
|
* output_thread: fix race condition after CANCEL command
|
||||||
* output:
|
* output:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
AC_PREREQ(2.60)
|
AC_PREREQ(2.60)
|
||||||
AC_INIT(mpd, 0.15.13, musicpd-dev-team@lists.sourceforge.net)
|
AC_INIT(mpd, 0.15.16, musicpd-dev-team@lists.sourceforge.net)
|
||||||
AC_CONFIG_SRCDIR([src/main.c])
|
AC_CONFIG_SRCDIR([src/main.c])
|
||||||
AM_INIT_AUTOMAKE([foreign 1.9 dist-bzip2])
|
AM_INIT_AUTOMAKE([foreign 1.9 dist-bzip2])
|
||||||
AM_CONFIG_HEADER(config.h)
|
AM_CONFIG_HEADER(config.h)
|
||||||
|
|||||||
12
m4/faad.m4
12
m4/faad.m4
@@ -58,7 +58,7 @@ if test x$enable_aac = xyes; then
|
|||||||
fi
|
fi
|
||||||
if test x$enable_aac = xyes; then
|
if test x$enable_aac = xyes; then
|
||||||
AC_MSG_CHECKING(that FAAD2 uses buffer and bufferlen)
|
AC_MSG_CHECKING(that FAAD2 uses buffer and bufferlen)
|
||||||
AC_COMPILE_IFELSE([
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
#include <faad.h>
|
#include <faad.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
@@ -82,9 +82,9 @@ int main() {
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
],[AC_MSG_RESULT(yes);AC_DEFINE(HAVE_FAAD_BUFLEN_FUNCS,1,[Define if FAAD2 uses buflen in function calls])],[AC_MSG_RESULT(no);
|
])],[AC_MSG_RESULT(yes);AC_DEFINE(HAVE_FAAD_BUFLEN_FUNCS,1,[Define if FAAD2 uses buflen in function calls])],[AC_MSG_RESULT(no);
|
||||||
AC_MSG_CHECKING(that FAAD2 can even be used)
|
AC_MSG_CHECKING(that FAAD2 can even be used)
|
||||||
AC_COMPILE_IFELSE([
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
#include <faad.h>
|
#include <faad.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
@@ -113,7 +113,7 @@ int main() {
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
],AC_MSG_RESULT(yes),[AC_MSG_RESULT(no);enable_aac=no])
|
])],AC_MSG_RESULT(yes),[AC_MSG_RESULT(no);enable_aac=no])
|
||||||
])
|
])
|
||||||
fi
|
fi
|
||||||
if test x$enable_aac = xyes; then
|
if test x$enable_aac = xyes; then
|
||||||
@@ -136,7 +136,7 @@ if test x$enable_aac = xyes; then
|
|||||||
CPPFLAGS=$CFLAGS
|
CPPFLAGS=$CFLAGS
|
||||||
|
|
||||||
AC_MSG_CHECKING(for broken libfaad headers)
|
AC_MSG_CHECKING(for broken libfaad headers)
|
||||||
AC_COMPILE_IFELSE([
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
#include <faad.h>
|
#include <faad.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -148,7 +148,7 @@ int main() {
|
|||||||
faacDecInit2(NULL, NULL, 0, &sample_rate, &channels);
|
faacDecInit2(NULL, NULL, 0, &sample_rate, &channels);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
],
|
])],
|
||||||
[AC_MSG_RESULT(correct)],
|
[AC_MSG_RESULT(correct)],
|
||||||
[AC_MSG_RESULT(broken);
|
[AC_MSG_RESULT(broken);
|
||||||
AC_DEFINE(HAVE_FAAD_LONG, 1, [Define if faad.h uses the broken "unsigned long" pointers])])
|
AC_DEFINE(HAVE_FAAD_LONG, 1, [Define if faad.h uses the broken "unsigned long" pointers])])
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ AC_DEFUN([MPD_CHECK_FLAG],[
|
|||||||
[mpd_check_cflag_$var],[
|
[mpd_check_cflag_$var],[
|
||||||
save_CFLAGS="$CFLAGS"
|
save_CFLAGS="$CFLAGS"
|
||||||
CFLAGS="$CFLAGS $1"
|
CFLAGS="$CFLAGS $1"
|
||||||
AC_COMPILE_IFELSE([
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
int main(void) { return 0; }
|
int main(void) { return 0; }
|
||||||
], [ eval "mpd_check_cflag_$var=yes"
|
])], [ eval "mpd_check_cflag_$var=yes"
|
||||||
], [ eval "mpd_check_cflag_$var=no" ])
|
], [ eval "mpd_check_cflag_$var=no" ])
|
||||||
CFLAGS="$save_CFLAGS"
|
CFLAGS="$save_CFLAGS"
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -501,7 +501,9 @@ static const char *const ffmpeg_suffixes[] = {
|
|||||||
"atrac", "au", "aud", "avi", "avm2", "avs", "bap", "bfi", "c93", "cak",
|
"atrac", "au", "aud", "avi", "avm2", "avs", "bap", "bfi", "c93", "cak",
|
||||||
"cin", "cmv", "cpk", "daud", "dct", "divx", "dts", "dv", "dvd", "dxa",
|
"cin", "cmv", "cpk", "daud", "dct", "divx", "dts", "dv", "dvd", "dxa",
|
||||||
"eac3", "film", "flac", "flc", "fli", "fll", "flx", "flv", "g726",
|
"eac3", "film", "flac", "flc", "fli", "fll", "flx", "flv", "g726",
|
||||||
"gsm", "gxf", "iss", "m1v", "m2v", "m2t", "m2ts", "m4a", "m4v", "mad",
|
"gsm", "gxf", "iss", "m1v", "m2v", "m2t", "m2ts",
|
||||||
|
"m4a", "m4b", "m4v",
|
||||||
|
"mad",
|
||||||
"mj2", "mjpeg", "mjpg", "mka", "mkv", "mlp", "mm", "mmf", "mov", "mp+",
|
"mj2", "mjpeg", "mjpg", "mka", "mkv", "mlp", "mm", "mmf", "mov", "mp+",
|
||||||
"mp1", "mp2", "mp3", "mp4", "mpc", "mpeg", "mpg", "mpga", "mpp", "mpu",
|
"mp1", "mp2", "mp3", "mp4", "mpc", "mpeg", "mpg", "mpga", "mpp", "mpu",
|
||||||
"mve", "mvi", "mxf", "nc", "nsv", "nut", "nuv", "oga", "ogm", "ogv",
|
"mve", "mvi", "mxf", "nc", "nsv", "nut", "nuv", "oga", "ogm", "ogv",
|
||||||
|
|||||||
@@ -425,7 +425,13 @@ mp4_tag_dup(const char *file)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *const mp4_suffixes[] = { "m4a", "mp4", NULL };
|
static const char *const mp4_suffixes[] = {
|
||||||
|
"m4a",
|
||||||
|
"m4b",
|
||||||
|
"mp4",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static const char *const mp4_mime_types[] = { "audio/mp4", "audio/m4a", NULL };
|
static const char *const mp4_mime_types[] = { "audio/mp4", "audio/m4a", NULL };
|
||||||
|
|
||||||
const struct decoder_plugin mp4ff_decoder_plugin = {
|
const struct decoder_plugin mp4ff_decoder_plugin = {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "decoder_control.h"
|
#include "decoder_control.h"
|
||||||
|
#include "pipe.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
@@ -58,22 +59,28 @@ static void dc_command_async(enum decoder_command cmd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
dc_start(struct notify *notify, struct song *song)
|
dc_start(struct notify *notify, struct song *song, struct music_pipe *pipe)
|
||||||
{
|
{
|
||||||
assert(dc.pipe != NULL);
|
assert(dc.pipe == NULL);
|
||||||
assert(song != NULL);
|
assert(song != NULL);
|
||||||
|
assert(pipe != NULL);
|
||||||
|
assert(music_pipe_empty(pipe));
|
||||||
|
|
||||||
dc.next_song = song;
|
dc.next_song = song;
|
||||||
|
dc.pipe = pipe;
|
||||||
dc_command(notify, DECODE_COMMAND_START);
|
dc_command(notify, DECODE_COMMAND_START);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
dc_start_async(struct song *song)
|
dc_start_async(struct song *song, struct music_pipe *pipe)
|
||||||
{
|
{
|
||||||
assert(dc.pipe != NULL);
|
assert(dc.pipe == NULL);
|
||||||
assert(song != NULL);
|
assert(song != NULL);
|
||||||
|
assert(pipe != NULL);
|
||||||
|
assert(music_pipe_empty(pipe));
|
||||||
|
|
||||||
dc.next_song = song;
|
dc.next_song = song;
|
||||||
|
dc.pipe = pipe;
|
||||||
dc_command_async(DECODE_COMMAND_START);
|
dc_command_async(DECODE_COMMAND_START);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,10 +118,10 @@ void
|
|||||||
dc_command_wait(struct notify *notify);
|
dc_command_wait(struct notify *notify);
|
||||||
|
|
||||||
void
|
void
|
||||||
dc_start(struct notify *notify, struct song *song);
|
dc_start(struct notify *notify, struct song *song, struct music_pipe *pipe);
|
||||||
|
|
||||||
void
|
void
|
||||||
dc_start_async(struct song *song);
|
dc_start_async(struct song *song, struct music_pipe *pipe);
|
||||||
|
|
||||||
void
|
void
|
||||||
dc_stop(struct notify *notify);
|
dc_stop(struct notify *notify);
|
||||||
|
|||||||
@@ -28,8 +28,8 @@
|
|||||||
|
|
||||||
#define DIRECTORY_DIR "directory: "
|
#define DIRECTORY_DIR "directory: "
|
||||||
|
|
||||||
#define DEVICE_INARCHIVE (unsigned)(-1)
|
#define DEVICE_INARCHIVE (dev_t)(-1)
|
||||||
#define DEVICE_CONTAINER (unsigned)(-2)
|
#define DEVICE_CONTAINER (dev_t)(-2)
|
||||||
|
|
||||||
struct directory {
|
struct directory {
|
||||||
struct dirvec children;
|
struct dirvec children;
|
||||||
|
|||||||
@@ -169,6 +169,13 @@ lame_encoder_setup(struct lame_encoder *encoder, GError **error)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (0 != lame_set_out_samplerate(encoder->gfp,
|
||||||
|
encoder->audio_format.sample_rate)) {
|
||||||
|
g_set_error(error, lame_encoder_quark(), 0,
|
||||||
|
"error setting lame out sample rate");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (0 > lame_init_params(encoder->gfp)) {
|
if (0 > lame_init_params(encoder->gfp)) {
|
||||||
g_set_error(error, lame_encoder_quark(), 0,
|
g_set_error(error, lame_encoder_quark(), 0,
|
||||||
"error initializing lame params");
|
"error initializing lame params");
|
||||||
|
|||||||
@@ -83,6 +83,9 @@ copy_attributes(struct input_stream *dest)
|
|||||||
const struct input_rewind *r = dest->data;
|
const struct input_rewind *r = dest->data;
|
||||||
const struct input_stream *src = &r->input;
|
const struct input_stream *src = &r->input;
|
||||||
|
|
||||||
|
assert(dest != src);
|
||||||
|
assert(src->mime == NULL || dest->mime != src->mime);
|
||||||
|
|
||||||
dest->ready = src->ready;
|
dest->ready = src->ready;
|
||||||
dest->seekable = src->seekable;
|
dest->seekable = src->seekable;
|
||||||
dest->error = src->error;
|
dest->error = src->error;
|
||||||
@@ -90,8 +93,7 @@ copy_attributes(struct input_stream *dest)
|
|||||||
dest->offset = src->offset;
|
dest->offset = src->offset;
|
||||||
|
|
||||||
if (src->mime != NULL) {
|
if (src->mime != NULL) {
|
||||||
if (dest->mime != NULL)
|
g_free(dest->mime);
|
||||||
g_free(dest->mime);
|
|
||||||
dest->mime = g_strdup(src->mime);
|
dest->mime = g_strdup(src->mime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -244,4 +246,5 @@ input_rewind_open(struct input_stream *is)
|
|||||||
input stream */
|
input stream */
|
||||||
is->plugin = &rewind_input_plugin;
|
is->plugin = &rewind_input_plugin;
|
||||||
is->data = c;
|
is->data = c;
|
||||||
|
is->mime = g_strdup(c->input.mime);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "ao"
|
#define G_LOG_DOMAIN "ao"
|
||||||
|
|
||||||
|
/* An ao_sample_format, with all fields set to zero: */
|
||||||
|
static const ao_sample_format OUR_AO_FORMAT_INITIALIZER;
|
||||||
|
|
||||||
static unsigned ao_output_ref;
|
static unsigned ao_output_ref;
|
||||||
|
|
||||||
struct ao_data {
|
struct ao_data {
|
||||||
@@ -166,7 +169,7 @@ static bool
|
|||||||
ao_output_open(void *data, struct audio_format *audio_format,
|
ao_output_open(void *data, struct audio_format *audio_format,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
ao_sample_format format;
|
ao_sample_format format = OUR_AO_FORMAT_INITIALIZER;
|
||||||
struct ao_data *ad = (struct ao_data *)data;
|
struct ao_data *ad = (struct ao_data *)data;
|
||||||
|
|
||||||
/* support for 24 bit samples in libao is currently dubious,
|
/* support for 24 bit samples in libao is currently dubious,
|
||||||
|
|||||||
@@ -27,6 +27,9 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#undef G_LOG_DOMAIN
|
||||||
|
#define G_LOG_DOMAIN "httpd_output"
|
||||||
|
|
||||||
struct httpd_client {
|
struct httpd_client {
|
||||||
/**
|
/**
|
||||||
* The httpd output object this client is connected to.
|
* The httpd output object this client is connected to.
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "jack"
|
#define G_LOG_DOMAIN "jack"
|
||||||
|
|
||||||
static const size_t sample_size = sizeof(jack_default_audio_sample_t);
|
static const size_t jack_sample_size = sizeof(jack_default_audio_sample_t);
|
||||||
|
|
||||||
static const char *const port_names[2] = {
|
static const char *const port_names[2] = {
|
||||||
"left", "right",
|
"left", "right",
|
||||||
@@ -118,14 +118,15 @@ mpd_jack_process(jack_nframes_t nframes, void *arg)
|
|||||||
|
|
||||||
for (unsigned i = 0; i < G_N_ELEMENTS(jd->ringbuffer); ++i) {
|
for (unsigned i = 0; i < G_N_ELEMENTS(jd->ringbuffer); ++i) {
|
||||||
available = jack_ringbuffer_read_space(jd->ringbuffer[i]);
|
available = jack_ringbuffer_read_space(jd->ringbuffer[i]);
|
||||||
assert(available % sample_size == 0);
|
assert(available % jack_sample_size == 0);
|
||||||
available /= sample_size;
|
available /= jack_sample_size;
|
||||||
if (available > nframes)
|
if (available > nframes)
|
||||||
available = nframes;
|
available = nframes;
|
||||||
|
|
||||||
out = jack_port_get_buffer(jd->ports[i], nframes);
|
out = jack_port_get_buffer(jd->ports[i], nframes);
|
||||||
jack_ringbuffer_read(jd->ringbuffer[i],
|
jack_ringbuffer_read(jd->ringbuffer[i],
|
||||||
(char *)out, available * sample_size);
|
(char *)out,
|
||||||
|
available * jack_sample_size);
|
||||||
|
|
||||||
while (available < nframes)
|
while (available < nframes)
|
||||||
/* ringbuffer underrun, fill with silence */
|
/* ringbuffer underrun, fill with silence */
|
||||||
@@ -422,7 +423,7 @@ mpd_jack_play(void *data, const void *chunk, size_t size, GError **error)
|
|||||||
/* send data symmetrically */
|
/* send data symmetrically */
|
||||||
space = space1;
|
space = space1;
|
||||||
|
|
||||||
if (space >= frame_size)
|
if (space >= jack_sample_size)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* XXX do something more intelligent to
|
/* XXX do something more intelligent to
|
||||||
@@ -430,7 +431,7 @@ mpd_jack_play(void *data, const void *chunk, size_t size, GError **error)
|
|||||||
g_usleep(1000);
|
g_usleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
space /= sample_size;
|
space /= jack_sample_size;
|
||||||
if (space < size)
|
if (space < size)
|
||||||
size = space;
|
size = space;
|
||||||
|
|
||||||
|
|||||||
@@ -483,7 +483,7 @@ shout_tag_to_metadata(const struct tag *tag, char *dest, size_t size)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(dest, size, "%s - %s", title, artist);
|
snprintf(dest, size, "%s - %s", artist, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void my_shout_set_tag(void *data,
|
static void my_shout_set_tag(void *data,
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ audio_output_all_open(const struct audio_format *audio_format,
|
|||||||
else
|
else
|
||||||
/* if the pipe hasn't been cleared, the the audio
|
/* if the pipe hasn't been cleared, the the audio
|
||||||
format must not have changed */
|
format must not have changed */
|
||||||
assert(music_pipe_size(g_mp) == 0 ||
|
assert(music_pipe_empty(g_mp) ||
|
||||||
audio_format_equals(audio_format,
|
audio_format_equals(audio_format,
|
||||||
&input_audio_format));
|
&input_audio_format));
|
||||||
|
|
||||||
@@ -378,7 +378,7 @@ audio_output_all_check(void)
|
|||||||
assert(g_mp != NULL);
|
assert(g_mp != NULL);
|
||||||
|
|
||||||
while ((chunk = music_pipe_peek(g_mp)) != NULL) {
|
while ((chunk = music_pipe_peek(g_mp)) != NULL) {
|
||||||
assert(music_pipe_size(g_mp) > 0);
|
assert(!music_pipe_empty(g_mp));
|
||||||
|
|
||||||
if (!chunk_is_consumed(chunk))
|
if (!chunk_is_consumed(chunk))
|
||||||
/* at least one output is not finished playing
|
/* at least one output is not finished playing
|
||||||
|
|||||||
@@ -50,6 +50,20 @@ static void ao_command(struct audio_output *ao, enum audio_output_command cmd)
|
|||||||
ao_command_wait(ao);
|
ao_command_wait(ao);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Like ao_command(), but assumes the object is locked by the caller.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ao_command_locked(struct audio_output *ao, enum audio_output_command cmd)
|
||||||
|
{
|
||||||
|
assert(ao->command == AO_COMMAND_NONE);
|
||||||
|
ao->command = cmd;
|
||||||
|
|
||||||
|
g_mutex_unlock(ao->mutex);
|
||||||
|
ao_command_wait(ao);
|
||||||
|
g_mutex_lock(ao->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
static void ao_command_async(struct audio_output *ao,
|
static void ao_command_async(struct audio_output *ao,
|
||||||
enum audio_output_command cmd)
|
enum audio_output_command cmd)
|
||||||
{
|
{
|
||||||
@@ -58,6 +72,12 @@ static void ao_command_async(struct audio_output *ao,
|
|||||||
notify_signal(&ao->notify);
|
notify_signal(&ao->notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
audio_output_close_locked(struct audio_output *ao);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object must be locked (and unlocked) by the caller.
|
||||||
|
*/
|
||||||
static bool
|
static bool
|
||||||
audio_output_open(struct audio_output *ao,
|
audio_output_open(struct audio_output *ao,
|
||||||
const struct audio_format *audio_format,
|
const struct audio_format *audio_format,
|
||||||
@@ -84,7 +104,7 @@ audio_output_open(struct audio_output *ao,
|
|||||||
|
|
||||||
/* we're not using audio_output_cancel() here,
|
/* we're not using audio_output_cancel() here,
|
||||||
because that function is asynchronous */
|
because that function is asynchronous */
|
||||||
ao_command(ao, AO_COMMAND_CANCEL);
|
ao_command_locked(ao, AO_COMMAND_CANCEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -95,7 +115,7 @@ audio_output_open(struct audio_output *ao,
|
|||||||
|
|
||||||
if (!ao->config_audio_format) {
|
if (!ao->config_audio_format) {
|
||||||
if (ao->open)
|
if (ao->open)
|
||||||
audio_output_close(ao);
|
audio_output_close_locked(ao);
|
||||||
|
|
||||||
/* no audio format is configured: copy in->out, let
|
/* no audio format is configured: copy in->out, let
|
||||||
the output's open() method determine the effective
|
the output's open() method determine the effective
|
||||||
@@ -110,7 +130,7 @@ audio_output_open(struct audio_output *ao,
|
|||||||
|
|
||||||
open = ao->open;
|
open = ao->open;
|
||||||
if (!open) {
|
if (!open) {
|
||||||
ao_command(ao, AO_COMMAND_OPEN);
|
ao_command_locked(ao, AO_COMMAND_OPEN);
|
||||||
open = ao->open;
|
open = ao->open;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,12 +147,19 @@ audio_output_update(struct audio_output *ao,
|
|||||||
{
|
{
|
||||||
assert(mp != NULL);
|
assert(mp != NULL);
|
||||||
|
|
||||||
|
g_mutex_lock(ao->mutex);
|
||||||
|
|
||||||
if (ao->enabled) {
|
if (ao->enabled) {
|
||||||
if (ao->fail_timer == NULL ||
|
if (ao->fail_timer == NULL ||
|
||||||
g_timer_elapsed(ao->fail_timer, NULL) > REOPEN_AFTER)
|
g_timer_elapsed(ao->fail_timer, NULL) > REOPEN_AFTER) {
|
||||||
return audio_output_open(ao, audio_format, mp);
|
bool ret = audio_output_open(ao, audio_format, mp);
|
||||||
|
g_mutex_unlock(ao->mutex);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
} else if (audio_output_is_open(ao))
|
} else if (audio_output_is_open(ao))
|
||||||
audio_output_close(ao);
|
audio_output_close_locked(ao);
|
||||||
|
|
||||||
|
g_mutex_unlock(ao->mutex);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -162,21 +189,33 @@ void audio_output_cancel(struct audio_output *ao)
|
|||||||
ao_command_async(ao, AO_COMMAND_CANCEL);
|
ao_command_async(ao, AO_COMMAND_CANCEL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_output_close(struct audio_output *ao)
|
static void
|
||||||
|
audio_output_close_locked(struct audio_output *ao)
|
||||||
{
|
{
|
||||||
|
assert(ao != NULL);
|
||||||
assert(!ao->open || ao->fail_timer == NULL);
|
assert(!ao->open || ao->fail_timer == NULL);
|
||||||
|
|
||||||
if (ao->mixer != NULL)
|
if (ao->mixer != NULL)
|
||||||
mixer_auto_close(ao->mixer);
|
mixer_auto_close(ao->mixer);
|
||||||
|
|
||||||
if (ao->open)
|
if (ao->open)
|
||||||
ao_command(ao, AO_COMMAND_CLOSE);
|
ao_command_locked(ao, AO_COMMAND_CLOSE);
|
||||||
else if (ao->fail_timer != NULL) {
|
else if (ao->fail_timer != NULL) {
|
||||||
g_timer_destroy(ao->fail_timer);
|
g_timer_destroy(ao->fail_timer);
|
||||||
ao->fail_timer = NULL;
|
ao->fail_timer = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void audio_output_close(struct audio_output *ao)
|
||||||
|
{
|
||||||
|
assert(ao != NULL);
|
||||||
|
assert(!ao->open || ao->fail_timer == NULL);
|
||||||
|
|
||||||
|
g_mutex_lock(ao->mutex);
|
||||||
|
audio_output_close_locked(ao);
|
||||||
|
g_mutex_unlock(ao->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
void audio_output_finish(struct audio_output *ao)
|
void audio_output_finish(struct audio_output *ao)
|
||||||
{
|
{
|
||||||
audio_output_close(ao);
|
audio_output_close(ao);
|
||||||
|
|||||||
@@ -131,7 +131,8 @@ struct audio_output {
|
|||||||
const struct music_pipe *pipe;
|
const struct music_pipe *pipe;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This mutex protects #open, #chunk and #chunk_finished.
|
* This mutex protects #open, #fail_timer, #chunk and
|
||||||
|
* #chunk_finished.
|
||||||
*/
|
*/
|
||||||
GMutex *mutex;
|
GMutex *mutex;
|
||||||
|
|
||||||
|
|||||||
@@ -105,7 +105,12 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
|
|||||||
|
|
||||||
/* don't automatically reopen this device for
|
/* don't automatically reopen this device for
|
||||||
10 seconds */
|
10 seconds */
|
||||||
|
g_mutex_lock(ao->mutex);
|
||||||
|
|
||||||
|
assert(ao->fail_timer == NULL);
|
||||||
ao->fail_timer = g_timer_new();
|
ao->fail_timer = g_timer_new();
|
||||||
|
|
||||||
|
g_mutex_unlock(ao->mutex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,10 +197,18 @@ static gpointer audio_output_task(gpointer arg)
|
|||||||
|
|
||||||
case AO_COMMAND_OPEN:
|
case AO_COMMAND_OPEN:
|
||||||
assert(!ao->open);
|
assert(!ao->open);
|
||||||
assert(ao->fail_timer == NULL);
|
|
||||||
assert(ao->pipe != NULL);
|
assert(ao->pipe != NULL);
|
||||||
assert(ao->chunk == NULL);
|
assert(ao->chunk == NULL);
|
||||||
|
|
||||||
|
if (ao->fail_timer != NULL) {
|
||||||
|
/* this can only happen when this
|
||||||
|
output thread fails while
|
||||||
|
audio_output_open() is run in the
|
||||||
|
player thread */
|
||||||
|
g_timer_destroy(ao->fail_timer);
|
||||||
|
ao->fail_timer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
error = NULL;
|
error = NULL;
|
||||||
ret = ao_plugin_open(ao->plugin, ao->data,
|
ret = ao_plugin_open(ao->plugin, ao->data,
|
||||||
&ao->out_audio_format,
|
&ao->out_audio_format,
|
||||||
|
|||||||
@@ -20,9 +20,9 @@
|
|||||||
#ifndef MPD_PIPE_H
|
#ifndef MPD_PIPE_H
|
||||||
#define MPD_PIPE_H
|
#define MPD_PIPE_H
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
struct audio_format;
|
struct audio_format;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -99,4 +99,10 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk);
|
|||||||
unsigned
|
unsigned
|
||||||
music_pipe_size(const struct music_pipe *mp);
|
music_pipe_size(const struct music_pipe *mp);
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
music_pipe_empty(const struct music_pipe *mp)
|
||||||
|
{
|
||||||
|
return music_pipe_size(mp) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -118,6 +118,33 @@ static void player_command_finished(void)
|
|||||||
notify_signal(&main_notify);
|
notify_signal(&main_notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the decoder still busy on the same song as the player?
|
||||||
|
*
|
||||||
|
* Note: this function does not check if the decoder is already
|
||||||
|
* finished.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
player_dc_at_current_song(const struct player *player)
|
||||||
|
{
|
||||||
|
assert(player != NULL);
|
||||||
|
assert(player->pipe != NULL);
|
||||||
|
|
||||||
|
return dc.pipe == player->pipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has the decoder already begun decoding the next song?
|
||||||
|
*
|
||||||
|
* Note: this function does not check if the decoder is already
|
||||||
|
* finished.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
player_dc_at_next_song(const struct player *player)
|
||||||
|
{
|
||||||
|
return dc.pipe != NULL && !player_dc_at_current_song(player);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the decoder and clears (and frees) its music pipe.
|
* Stop the decoder and clears (and frees) its music pipe.
|
||||||
*/
|
*/
|
||||||
@@ -297,10 +324,9 @@ static bool player_seek_decoder(struct player *player)
|
|||||||
/* clear music chunks which might still reside in the
|
/* clear music chunks which might still reside in the
|
||||||
pipe */
|
pipe */
|
||||||
music_pipe_clear(player->pipe, player_buffer);
|
music_pipe_clear(player->pipe, player_buffer);
|
||||||
dc.pipe = player->pipe;
|
|
||||||
|
|
||||||
/* re-start the decoder */
|
/* re-start the decoder */
|
||||||
dc_start_async(pc.next_song);
|
dc_start_async(pc.next_song, player->pipe);
|
||||||
ret = player_wait_for_decoder(player);
|
ret = player_wait_for_decoder(player);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
/* decoder failure */
|
/* decoder failure */
|
||||||
@@ -308,6 +334,14 @@ static bool player_seek_decoder(struct player *player)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (!player_dc_at_current_song(player)) {
|
||||||
|
/* the decoder is already decoding the "next" song,
|
||||||
|
but it is the same song file; exchange the pipe */
|
||||||
|
music_pipe_clear(player->pipe, player_buffer);
|
||||||
|
music_pipe_free(player->pipe);
|
||||||
|
player->pipe = dc.pipe;
|
||||||
|
}
|
||||||
|
|
||||||
pc.next_song = NULL;
|
pc.next_song = NULL;
|
||||||
player->queued = false;
|
player->queued = false;
|
||||||
}
|
}
|
||||||
@@ -364,7 +398,7 @@ static void player_process_command(struct player *player)
|
|||||||
case PLAYER_COMMAND_QUEUE:
|
case PLAYER_COMMAND_QUEUE:
|
||||||
assert(pc.next_song != NULL);
|
assert(pc.next_song != NULL);
|
||||||
assert(!player->queued);
|
assert(!player->queued);
|
||||||
assert(dc.pipe == NULL || dc.pipe == player->pipe);
|
assert(!player_dc_at_next_song(player));
|
||||||
|
|
||||||
player->queued = true;
|
player->queued = true;
|
||||||
player_command_finished();
|
player_command_finished();
|
||||||
@@ -409,7 +443,7 @@ static void player_process_command(struct player *player)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dc.pipe != NULL && dc.pipe != player->pipe)
|
if (player_dc_at_next_song(player))
|
||||||
/* the decoder is already decoding the song -
|
/* the decoder is already decoding the song -
|
||||||
stop it and reset the position */
|
stop it and reset the position */
|
||||||
player_dc_stop(player);
|
player_dc_stop(player);
|
||||||
@@ -505,7 +539,7 @@ play_next_chunk(struct player *player)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (player->xfade == XFADE_ENABLED &&
|
if (player->xfade == XFADE_ENABLED &&
|
||||||
dc.pipe != NULL && dc.pipe != player->pipe &&
|
player_dc_at_next_song(player) &&
|
||||||
(cross_fade_position = music_pipe_size(player->pipe))
|
(cross_fade_position = music_pipe_size(player->pipe))
|
||||||
<= player->cross_fade_chunks) {
|
<= player->cross_fade_chunks) {
|
||||||
/* perform cross fade */
|
/* perform cross fade */
|
||||||
@@ -638,8 +672,7 @@ static void do_play(void)
|
|||||||
player.pipe = music_pipe_new();
|
player.pipe = music_pipe_new();
|
||||||
|
|
||||||
dc.buffer = player_buffer;
|
dc.buffer = player_buffer;
|
||||||
dc.pipe = player.pipe;
|
dc_start(&pc.notify, pc.next_song, player.pipe);
|
||||||
dc_start(&pc.notify, pc.next_song);
|
|
||||||
if (!player_wait_for_decoder(&player)) {
|
if (!player_wait_for_decoder(&player)) {
|
||||||
player_dc_stop(&player);
|
player_dc_stop(&player);
|
||||||
player_command_finished();
|
player_command_finished();
|
||||||
@@ -706,14 +739,15 @@ static void do_play(void)
|
|||||||
/* the decoder has finished the current song;
|
/* the decoder has finished the current song;
|
||||||
make it decode the next song */
|
make it decode the next song */
|
||||||
assert(pc.next_song != NULL);
|
assert(pc.next_song != NULL);
|
||||||
assert(dc.pipe == NULL || dc.pipe == player.pipe);
|
assert(!player_dc_at_next_song(&player));
|
||||||
|
|
||||||
|
dc.pipe = NULL;
|
||||||
|
|
||||||
player.queued = false;
|
player.queued = false;
|
||||||
dc.pipe = music_pipe_new();
|
dc_start_async(pc.next_song, music_pipe_new());
|
||||||
dc_start_async(pc.next_song);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dc.pipe != NULL && dc.pipe != player.pipe &&
|
if (player_dc_at_next_song(&player) &&
|
||||||
player.xfade == XFADE_UNKNOWN &&
|
player.xfade == XFADE_UNKNOWN &&
|
||||||
!decoder_is_starting()) {
|
!decoder_is_starting()) {
|
||||||
/* enable cross fading in this song? if yes,
|
/* enable cross fading in this song? if yes,
|
||||||
@@ -736,7 +770,7 @@ static void do_play(void)
|
|||||||
|
|
||||||
if (player.paused)
|
if (player.paused)
|
||||||
notify_wait(&pc.notify);
|
notify_wait(&pc.notify);
|
||||||
else if (music_pipe_size(player.pipe) > 0) {
|
else if (!music_pipe_empty(player.pipe)) {
|
||||||
/* at least one music chunk is ready - send it
|
/* at least one music chunk is ready - send it
|
||||||
to the audio output */
|
to the audio output */
|
||||||
|
|
||||||
@@ -748,7 +782,7 @@ static void do_play(void)
|
|||||||
|
|
||||||
/* XXX synchronize in a better way */
|
/* XXX synchronize in a better way */
|
||||||
g_usleep(10000);
|
g_usleep(10000);
|
||||||
} else if (dc.pipe != NULL && dc.pipe != player.pipe) {
|
} else if (player_dc_at_next_song(&player)) {
|
||||||
/* at the beginning of a new song */
|
/* at the beginning of a new song */
|
||||||
|
|
||||||
if (!player_song_border(&player))
|
if (!player_song_border(&player))
|
||||||
@@ -757,7 +791,7 @@ static void do_play(void)
|
|||||||
/* check the size of the pipe again, because
|
/* check the size of the pipe again, because
|
||||||
the decoder thread may have added something
|
the decoder thread may have added something
|
||||||
since we last checked */
|
since we last checked */
|
||||||
if (music_pipe_size(player.pipe) == 0)
|
if (music_pipe_empty(player.pipe))
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
/* the decoder is too busy and hasn't provided
|
/* the decoder is too busy and hasn't provided
|
||||||
|
|||||||
@@ -254,6 +254,9 @@ stat_directory(const struct directory *directory, struct stat *st)
|
|||||||
if (path_fs == NULL)
|
if (path_fs == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
ret = stat(path_fs, st);
|
ret = stat(path_fs, st);
|
||||||
|
if (ret < 0)
|
||||||
|
g_warning("Failed to stat %s: %s", path_fs, g_strerror(errno));
|
||||||
|
|
||||||
g_free(path_fs);
|
g_free(path_fs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -270,6 +273,9 @@ stat_directory_child(const struct directory *parent, const char *name,
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ret = stat(path_fs, st);
|
ret = stat(path_fs, st);
|
||||||
|
if (ret < 0)
|
||||||
|
g_warning("Failed to stat %s: %s", path_fs, g_strerror(errno));
|
||||||
|
|
||||||
g_free(path_fs);
|
g_free(path_fs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ void init_zeroconf_osx(const char *serviceName)
|
|||||||
DNSServiceErrorType error = DNSServiceRegister(&dnsReference,
|
DNSServiceErrorType error = DNSServiceRegister(&dnsReference,
|
||||||
0, 0, serviceName,
|
0, 0, serviceName,
|
||||||
SERVICE_TYPE, NULL, NULL,
|
SERVICE_TYPE, NULL, NULL,
|
||||||
htons(listen_port), 0,
|
g_htons(listen_port), 0,
|
||||||
NULL,
|
NULL,
|
||||||
dnsRegisterCallback,
|
dnsRegisterCallback,
|
||||||
NULL);
|
NULL);
|
||||||
|
|||||||
Reference in New Issue
Block a user