diff --git a/Makefile.am b/Makefile.am index 764dee3eb..df9be5aaf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,6 +11,7 @@ noinst_LIBRARIES = src_mpd_CFLAGS = $(AM_CFLAGS) $(MPD_CFLAGS) src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \ + $(AVAHI_CFLAGS) \ $(LIBWRAP_CFLAGS) \ $(SQLITE_CFLAGS) \ $(ARCHIVE_CFLAGS) \ @@ -21,6 +22,7 @@ src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \ $(FILTER_CFLAGS) \ $(OUTPUT_CFLAGS) src_mpd_LDADD = $(MPD_LIBS) \ + $(AVAHI_LIBS) \ $(LIBWRAP_LDFLAGS) \ $(SQLITE_LIBS) \ $(ARCHIVE_LIBS) \ diff --git a/NEWS b/NEWS index 34cf90e45..c749c3156 100644 --- a/NEWS +++ b/NEWS @@ -21,11 +21,15 @@ ver 0.17 (2011/??/??) ver 0.16.4 (2011/??/??) +* don't abort configure when avahi is not found +* auto-detect libmad without pkg-config * fix memory leaks * don't resume playback when seeking to another song while paused * apply follow_inside_symlinks to absolute symlinks +* fix playback discontinuation after seeking * input: - curl: limit the receive buffer size + - curl: implement a hard-coded timeout of 10 seconds * decoder: - ffmpeg: workaround for semantic API change in recent ffmpeg versions - flac: validate the sample rate when scanning the tag @@ -35,6 +39,7 @@ ver 0.16.4 (2011/??/??) * output: - alsa: fix SIGFPE when alsa announces a period size of 0 - httpd: don't warn on client disconnect + - osx: don't drain the buffer when closing - pulse: fix deadlock when resuming the stream - pulse: fix deadlock when the stream was suspended diff --git a/configure.ac b/configure.ac index 46d592bcf..0a749842b 100644 --- a/configure.ac +++ b/configure.ac @@ -186,9 +186,9 @@ AC_ARG_ENABLE(fifo, enable_fifo=yes) AC_ARG_ENABLE(flac, - AS_HELP_STRING([--disable-flac], - [disable flac support (default: enable)]),, - enable_flac=yes) + AS_HELP_STRING([--enable-flac], + [enable FLAC decoder]),, + enable_flac=auto) AC_ARG_ENABLE(fluidsynth, AS_HELP_STRING([--enable-fluidsynth], @@ -216,9 +216,9 @@ AC_ARG_ENABLE(raop-output, [enable_raop_output=auto]) AC_ARG_ENABLE(id3, - AS_HELP_STRING([--disable-id3], - [disable id3 support (default: enable)]),, - enable_id3=yes) + AS_HELP_STRING([--enable-id3], + [disable id3 support]),, + enable_id3=auto) AC_ARG_ENABLE(inotify, AS_HELP_STRING([--disable-inotify], @@ -378,9 +378,9 @@ AC_ARG_ENABLE(un, [enable_un=yes]) AC_ARG_ENABLE(vorbis, - AS_HELP_STRING([--disable-vorbis], - [disable Ogg Vorbis support (default: enable)]),, - enable_vorbis=yes) + AS_HELP_STRING([--enable-vorbis], + [enable Ogg Vorbis decoder]),, + enable_vorbis=auto) AC_ARG_ENABLE(vorbis-encoder, AS_HELP_STRING([--enable-vorbis-encoder], @@ -535,13 +535,8 @@ fi AM_CONDITIONAL(HAVE_CUE, test x$enable_cue = xyes) dnl -------------------------------- libid3tag -------------------------------- -if test x$enable_id3 = xyes; then - PKG_CHECK_MODULES([ID3TAG], [id3tag],, - AC_CHECK_LIB(id3tag, id3_file_open, - [ID3TAG_LIBS="-lid3tag -lz" ID3TAG_CFLAGS=""], - enable_id3=no)) -fi - +MPD_AUTO_PKG_LIB(id3, ID3TAG, id3tag, id3tag, id3_file_open, [-lid3tag -lz], [], + [id3tag], [libid3tag not found]) if test x$enable_id3 = xyes; then AC_DEFINE(HAVE_ID3TAG, 1, [Define to use id3tag]) fi @@ -555,28 +550,31 @@ dnl --------------------------------------------------------------------------- dnl --------------------------------- zeroconf -------------------------------- case $with_zeroconf in -no|avahi|bonjour) +no|bonjour) + enable_avahi=no ;; + +avahi) + enable_avahi=yes + ;; + *) with_zeroconf=auto + enable_avahi=auto ;; esac -enable_avahi=no +MPD_AUTO_PKG(avahi, AVAHI, [avahi-client avahi-glib], + [avahi client library], [avahi client+glib not found]) +if test x$enable_avahi = xyes; then + AC_DEFINE([HAVE_AVAHI], 1, [Define to enable Avahi Zeroconf support]) + with_zeroconf=avahi +fi + +AM_CONDITIONAL(HAVE_AVAHI, test x$enable_avahi = xyes) + enable_bounjour=no if test x$with_zeroconf != xno; then - if test x$with_zeroconf = xavahi || test x$with_zeroconf = xauto; then - PKG_CHECK_MODULES([AVAHI], [avahi-client avahi-glib], - [enable_avahi=yes;AC_DEFINE([HAVE_AVAHI], 1, [Define to enable Avahi Zeroconf support])] - MPD_LIBS="$MPD_LIBS $AVAHI_LIBS" MPD_CFLAGS="$MPD_CFLAGS $AVAHI_CFLAGS") - fi - - if test x$enable_avahi = xyes; then - with_zeroconf=avahi - elif test x$with_zeroconf = xavahi; then - AC_MSG_ERROR([Avahi support requested but not found]) - fi - if test x$with_zeroconf = xbonjour || test x$with_zeroconf = xauto; then AC_CHECK_HEADER(dns_sd.h, [enable_bonjour=yes;AC_DEFINE([HAVE_BONJOUR], 1, [Define to enable Bonjour Zeroconf support])]) @@ -599,7 +597,6 @@ if test x$with_zeroconf != xno; then fi AM_CONDITIONAL(HAVE_ZEROCONF, test x$with_zeroconf != xno) -AM_CONDITIONAL(HAVE_AVAHI, test x$with_zeroconf = xavahi) AM_CONDITIONAL(HAVE_BONJOUR, test x$with_zeroconf = xbonjour) dnl --------------------------------------------------------------------------- @@ -686,11 +683,6 @@ fi AM_CONDITIONAL(ENABLE_CDIO_PARANOIA, test x$enable_cdio_paranoia = xyes) -dnl ---------------------------------- libogg --------------------------------- -if test x$with_tremor = xno || test -z $with_tremor; then - PKG_CHECK_MODULES(OGG, [ogg], enable_ogg=yes, enable_ogg=no) -fi - dnl ---------------------------------- libmms --------------------------------- MPD_AUTO_PKG(mms, MMS, [libmms >= 0.4], [libmms mms:// protocol support], [libmms not found]) @@ -794,10 +786,12 @@ fi AM_CONDITIONAL(HAVE_FFMPEG, test x$enable_ffmpeg = xyes) dnl ----------------------------------- FLAC ---------------------------------- + +MPD_AUTO_PKG(flac, FLAC, [flac >= 1.1], + [FLAC decoder], [libFLAC not found]) + if test x$enable_flac = xyes; then - PKG_CHECK_MODULES(FLAC, [flac >= 1.1], - AC_DEFINE(HAVE_FLAC, 1, [Define for FLAC support]), - enable_flac=no) + AC_DEFINE(HAVE_FLAC, 1, [Define for FLAC support]) oldcflags="$CFLAGS" oldlibs="$LIBS" @@ -812,12 +806,10 @@ if test x$enable_flac = xyes; then LIBS="$oldlibs" if test x$enable_oggflac = xflac; then - if test x$enable_ogg = xyes; then - FLAC_LIBS="${FLAC_LIBS} -logg" - else - enable_oggflac=yes - AC_MSG_WARN("FLAC has the ogg API built in, but couldn't find ogg. Disabling oggflac.") - fi + PKG_CHECK_MODULES(OGG, [ogg], + [FLAC_LIBS="${FLAC_LIBS} ${OGG_LIBS}" FLAC_CFLAGS="${FLAC_CFLAGS} ${OGG_CFLAGS}"], + [enable_oggflac=yes; + AC_MSG_WARN("FLAC has the ogg API built in, but couldn't find ogg. Disabling oggflac.")]) fi fi @@ -843,7 +835,8 @@ if test x$enable_gme = xyes; then fi dnl ---------------------------------- libmad --------------------------------- -MPD_AUTO_PKG(mad, MAD, [mad], +MPD_AUTO_PKG_LIB(mad, MAD, [mad], + mad, mad_stream_init, [-lmad], [], [libmad MP3 decoder plugin], [libmad not found]) if test x$enable_mad = xyes; then AC_DEFINE(HAVE_MAD, 1, [Define to use libmad]) @@ -984,7 +977,7 @@ fi if test x$enable_tremor = xyes; then AC_DEFINE(HAVE_TREMOR,1, [Define to use tremor (libvorbisidec) for ogg support]) - AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support]), + AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support]) else TREMOR_CFLAGS= TREMOR_LIBS= @@ -1013,18 +1006,18 @@ fi AM_CONDITIONAL(HAVE_OGGFLAC, test x$enable_oggflac = xyes) dnl -------------------------------- Ogg Vorbis ------------------------------- -if test x$enable_vorbis = xyes; then - if test x$enable_tremor = xyes; then + +if test x$enable_tremor = xyes; then + if test x$enable_vorbis = xyes; then AC_MSG_WARN(["OggTremor detected, could not enable Vorbis."]) - enable_vorbis=no - elif test x$enable_ogg = xyes; then - PKG_CHECK_MODULES(VORBIS, [vorbis vorbisfile], - AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support]), - enable_vorbis=no) - else - AC_MSG_WARN(["Ogg not detected, could not enable Vorbis."]) - enable_vorbis=no fi + enable_vorbis=no +fi + +MPD_AUTO_PKG(vorbis, VORBIS, [vorbis vorbisfile ogg], + [Ogg Vorbis decoder], [libvorbis not found]) +if test x$enable_vorbis = xyes; then + AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support]) fi AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enable_tremor = xyes) diff --git a/doc/developer.xml b/doc/developer.xml index c63e2c265..010b85064 100644 --- a/doc/developer.xml +++ b/doc/developer.xml @@ -57,7 +57,7 @@ Some example code: - static inline bool + static inline int foo(const char *abc, int xyz) { if (abc == NULL) { diff --git a/m4/mpd_auto.m4 b/m4/mpd_auto.m4 index 3f233938f..23713d5b7 100644 --- a/m4/mpd_auto.m4 +++ b/m4/mpd_auto.m4 @@ -63,3 +63,18 @@ AC_DEFUN([MPD_AUTO_PKG], [ MPD_AUTO_RESULT([$1], [$4], [$5]) ]) + +dnl Check with pkg-config first, fall back to AC_CHECK_LIB. +dnl +dnl Parameters: varname1, varname2, pkgname, libname, symname, libs, cflags, description, errmsg +AC_DEFUN([MPD_AUTO_PKG_LIB], [ + if eval "test x`echo '$'enable_$1` != xno"; then + PKG_CHECK_MODULES([$2], [$3], + [eval "found_$1=yes"], + AC_CHECK_LIB($4, $5, + [eval "found_$1=yes $2_LIBS='$6' $2_CFLAGS='$7'"], + [eval "found_$1=no"])) + fi + + MPD_AUTO_RESULT([$1], [$8], [$9]) +]) diff --git a/scripts/makedist.sh b/scripts/makedist.sh index d342cea3a..7f8624d8f 100755 --- a/scripts/makedist.sh +++ b/scripts/makedist.sh @@ -3,7 +3,7 @@ PWD=`pwd` ## If we're not in the scripts directory ## assume the base directory. -if test "`basename $PWD`" == "scripts"; then +if test "`basename $PWD`" = "scripts"; then cd ../ else MYOLDPWD=`pwd` @@ -18,7 +18,7 @@ fi make make dist -if test "`basename $PWD`" == "scripts"; then +if test "`basename $PWD`" = "scripts"; then cd contrib/ else cd $MYOLDPWD diff --git a/src/input/curl_input_plugin.c b/src/input/curl_input_plugin.c index 54c6d2f18..a591c9272 100644 --- a/src/input/curl_input_plugin.c +++ b/src/input/curl_input_plugin.c @@ -1124,6 +1124,9 @@ input_curl_easy_init(struct input_curl *c, GError **error_r) curl_easy_setopt(c->easy, CURLOPT_MAXREDIRS, 5); curl_easy_setopt(c->easy, CURLOPT_FAILONERROR, true); curl_easy_setopt(c->easy, CURLOPT_ERRORBUFFER, c->error); + curl_easy_setopt(c->easy, CURLOPT_NOPROGRESS, 1l); + curl_easy_setopt(c->easy, CURLOPT_NOSIGNAL, 1l); + curl_easy_setopt(c->easy, CURLOPT_CONNECTTIMEOUT, 10l); if (proxy != NULL) curl_easy_setopt(c->easy, CURLOPT_PROXY, proxy); diff --git a/src/output/osx_plugin.c b/src/output/osx_plugin.c index 618cd351f..8091660ab 100644 --- a/src/output/osx_plugin.c +++ b/src/output/osx_plugin.c @@ -121,12 +121,6 @@ static void osx_output_close(void *data) { struct osx_output *od = data; - g_mutex_lock(od->mutex); - while (od->len) { - g_cond_wait(od->condition, od->mutex); - } - g_mutex_unlock(od->mutex); - AudioOutputUnitStop(od->au); AudioUnitUninitialize(od->au); CloseComponent(od->au); @@ -169,8 +163,8 @@ osx_render(void *vdata, if (od->pos >= od->buffer_size) od->pos = 0; - g_mutex_unlock(od->mutex); g_cond_signal(od->condition); + g_mutex_unlock(od->mutex); buffer->mDataByteSize = buffer_size; diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c index 9fb0b3ade..9825785ee 100644 --- a/src/output/pulse_output_plugin.c +++ b/src/output/pulse_output_plugin.c @@ -224,6 +224,44 @@ pulse_output_connect(struct pulse_output *po, GError **error_r) return true; } +/** + * Frees and clears the stream. + */ +static void +pulse_output_delete_stream(struct pulse_output *po) +{ + assert(po != NULL); + assert(po->stream != NULL); + +#if PA_CHECK_VERSION(0,9,8) + pa_stream_set_suspended_callback(po->stream, NULL, NULL); +#endif + + pa_stream_set_state_callback(po->stream, NULL, NULL); + pa_stream_set_write_callback(po->stream, NULL, NULL); + + pa_stream_disconnect(po->stream); + pa_stream_unref(po->stream); + po->stream = NULL; +} + +/** + * Frees and clears the context. + */ +static void +pulse_output_delete_context(struct pulse_output *po) +{ + assert(po != NULL); + assert(po->context != NULL); + + pa_context_set_state_callback(po->context, NULL, NULL); + pa_context_set_subscribe_callback(po->context, NULL, NULL); + + pa_context_disconnect(po->context); + pa_context_unref(po->context); + po->context = NULL; +} + /** * Create, set up and connect a context. * @@ -249,28 +287,13 @@ pulse_output_setup_context(struct pulse_output *po, GError **error_r) pulse_output_subscribe_cb, po); if (!pulse_output_connect(po, error_r)) { - pa_context_unref(po->context); - po->context = NULL; + pulse_output_delete_context(po); return false; } return true; } -/** - * Frees and clears the context. - */ -static void -pulse_output_delete_context(struct pulse_output *po) -{ - assert(po != NULL); - assert(po->context != NULL); - - pa_context_disconnect(po->context); - pa_context_unref(po->context); - po->context = NULL; -} - static void * pulse_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, const struct config_param *param, @@ -540,8 +563,7 @@ pulse_output_open(void *data, struct audio_format *audio_format, error = pa_stream_connect_playback(po->stream, po->sink, NULL, 0, NULL, NULL); if (error < 0) { - pa_stream_unref(po->stream); - po->stream = NULL; + pulse_output_delete_stream(po); g_set_error(error_r, pulse_output_quark(), 0, "pa_stream_connect_playback() has failed: %s", @@ -579,9 +601,7 @@ pulse_output_close(void *data) pulse_wait_for_operation(po->mainloop, o); } - pa_stream_disconnect(po->stream); - pa_stream_unref(po->stream); - po->stream = NULL; + pulse_output_delete_stream(po); if (po->context != NULL && pa_context_get_state(po->context) != PA_CONTEXT_READY) diff --git a/src/output_all.c b/src/output_all.c index bd7c48c92..7f4694a8b 100644 --- a/src/output_all.c +++ b/src/output_all.c @@ -206,27 +206,14 @@ static void audio_output_wait_all(void) notify_wait(&audio_output_client_notify); } -/** - * Signals the audio output if it is open. This function locks the - * mutex. - */ -static void -audio_output_lock_signal(struct audio_output *ao) -{ - g_mutex_lock(ao->mutex); - if (audio_output_is_open(ao)) - g_cond_signal(ao->cond); - g_mutex_unlock(ao->mutex); -} - /** * Signals all audio outputs which are open. */ static void -audio_output_signal_all(void) +audio_output_allow_play_all(void) { for (unsigned i = 0; i < num_audio_outputs; ++i) - audio_output_lock_signal(&audio_outputs[i]); + audio_output_allow_play(&audio_outputs[i]); } static void @@ -531,7 +518,7 @@ audio_output_all_cancel(void) /* the audio outputs are now waiting for a signal, to synchronize the cleared music pipe */ - audio_output_signal_all(); + audio_output_allow_play_all(); /* invalidate elapsed_time */ diff --git a/src/output_control.c b/src/output_control.c index 22c63b822..7ddcb8b19 100644 --- a/src/output_control.c +++ b/src/output_control.c @@ -139,6 +139,8 @@ audio_output_open(struct audio_output *ao, { bool open; + assert(ao != NULL); + assert(ao->allow_play); assert(audio_format_valid(audio_format)); assert(mp != NULL); @@ -164,10 +166,6 @@ audio_output_open(struct audio_output *ao, /* we're not using audio_output_cancel() here, because that function is asynchronous */ ao_command(ao, AO_COMMAND_CANCEL); - - /* the audio output is now waiting for a - signal; wake it up immediately */ - g_cond_signal(ao->cond); } return true; @@ -205,6 +203,7 @@ static void audio_output_close_locked(struct audio_output *ao) { assert(ao != NULL); + assert(ao->allow_play); if (ao->mixer != NULL) mixer_auto_close(ao->mixer); @@ -247,6 +246,8 @@ audio_output_play(struct audio_output *ao) { g_mutex_lock(ao->mutex); + assert(ao->allow_play); + if (audio_output_is_open(ao)) g_cond_signal(ao->cond); @@ -262,6 +263,7 @@ void audio_output_pause(struct audio_output *ao) mixer_auto_close(ao->mixer); g_mutex_lock(ao->mutex); + assert(ao->allow_play); if (audio_output_is_open(ao)) ao_command_async(ao, AO_COMMAND_PAUSE); g_mutex_unlock(ao->mutex); @@ -271,6 +273,7 @@ void audio_output_drain_async(struct audio_output *ao) { g_mutex_lock(ao->mutex); + assert(ao->allow_play); if (audio_output_is_open(ao)) ao_command_async(ao, AO_COMMAND_DRAIN); g_mutex_unlock(ao->mutex); @@ -279,8 +282,24 @@ audio_output_drain_async(struct audio_output *ao) void audio_output_cancel(struct audio_output *ao) { g_mutex_lock(ao->mutex); - if (audio_output_is_open(ao)) + + if (audio_output_is_open(ao)) { + ao->allow_play = false; ao_command_async(ao, AO_COMMAND_CANCEL); + } + + g_mutex_unlock(ao->mutex); +} + +void +audio_output_allow_play(struct audio_output *ao) +{ + g_mutex_lock(ao->mutex); + + ao->allow_play = true; + if (audio_output_is_open(ao)) + g_cond_signal(ao->cond); + g_mutex_unlock(ao->mutex); } @@ -310,6 +329,7 @@ void audio_output_finish(struct audio_output *ao) assert(ao->fail_timer == NULL); if (ao->thread != NULL) { + assert(ao->allow_play); ao_lock_command(ao, AO_COMMAND_KILL); g_thread_join(ao->thread); ao->thread = NULL; diff --git a/src/output_control.h b/src/output_control.h index 8711a6566..f58a113e6 100644 --- a/src/output_control.h +++ b/src/output_control.h @@ -72,8 +72,19 @@ void audio_output_pause(struct audio_output *ao); void audio_output_drain_async(struct audio_output *ao); +/** + * Clear the "allow_play" flag and send the "CANCEL" command + * asynchronously. To finish the operation, the caller has to call + * audio_output_allow_play(). + */ void audio_output_cancel(struct audio_output *ao); +/** + * Set the "allow_play" and signal the thread. + */ +void +audio_output_allow_play(struct audio_output *ao); + void audio_output_close(struct audio_output *ao); /** diff --git a/src/output_init.c b/src/output_init.c index 107abed41..c52dcc8cd 100644 --- a/src/output_init.c +++ b/src/output_init.c @@ -193,6 +193,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param, ao->really_enabled = false; ao->open = false; ao->pause = false; + ao->allow_play = true; ao->fail_timer = NULL; pcm_buffer_init(&ao->cross_fade_buffer); diff --git a/src/output_internal.h b/src/output_internal.h index 873ec8532..eba3aed91 100644 --- a/src/output_internal.h +++ b/src/output_internal.h @@ -109,6 +109,15 @@ struct audio_output { */ bool pause; + /** + * When this flag is set, the output thread will not do any + * playback. It will wait until the flag is cleared. + * + * This is used to synchronize the "clear" operation on the + * shared music pipe during the CANCEL command. + */ + bool allow_play; + /** * If not NULL, the device has failed, and this timer is used * to estimate how long it should stay disabled (unless diff --git a/src/output_thread.c b/src/output_thread.c index ec5fc5b31..c36ba5f4f 100644 --- a/src/output_thread.c +++ b/src/output_thread.c @@ -649,12 +649,6 @@ static gpointer audio_output_task(gpointer arg) } ao_command_finished(ao); - - /* the player thread will now clear our music - pipe - wait for a notify, to give it some - time */ - if (ao->command == AO_COMMAND_NONE) - g_cond_wait(ao->cond, ao->mutex); continue; case AO_COMMAND_KILL: @@ -664,7 +658,7 @@ static gpointer audio_output_task(gpointer arg) return NULL; } - if (ao->open && ao_play(ao)) + if (ao->open && ao->allow_play && ao_play(ao)) /* don't wait for an event if there are more chunks in the pipe */ continue;