Package: release.debian.org Severity: normal User: release.debian....@packages.debian.org Usertags: unblock
Please unblock / pre-approve package mpd mpd 0.19.2 is a bugfix release in the stable mpd series. It fixes a number of issues that would be good to have fixed in a stable Debian release, only one of which (#767684, "Stack smashing detected in faad_decoder_init", important) has appeared in the Debian BTS yet. mpd will need an unblock to fix a license incompatibility issue (#767504, serious), and I would like to fix as many important or yet-to-be filed important bugs in the same upload. I realize the 0.19.1 to 0.19.2 diff (attached) is not very short and not strictly limited to important bugs, so I'm asking for pre-approval before uploading to unstable and hope for a favourable decision given that the freeze hasn't technically started yet. The full upstream changelog is as follows: ver 0.19.2 (2014/11/02) * input - curl: fix redirected streams * playlist - don't allow empty playlist name - m3u: don't ignore unterminated last line - m3u: recognize the file suffix ".m3u8" * decoder - ignore URI query string for plugin detection - faad: remove workaround for ancient libfaad2 ABI bug - ffmpeg: recognize MIME type audio/aacp - mad: fix negative replay gain values * output - fix memory leak after filter initialization error - fall back to PCM if given DSD sample rate is not supported * fix assertion failure on unsupported PCM conversion * auto-disable plugins that require GLib when --disable-glib is used unblock mpd/0.19.2-1
diff --git a/Makefile.am b/Makefile.am index 2f3ab90..dac3e17 100644 --- a/Makefile.am +++ b/Makefile.am @@ -130,7 +130,6 @@ libmpd_a_SOURCES = \ src/IOThread.cxx src/IOThread.hxx \ src/Instance.cxx src/Instance.hxx \ src/win32/Win32Main.cxx \ - src/osx/OSXMain.cxx \ src/GlobalEvents.cxx src/GlobalEvents.hxx \ src/MixRampInfo.hxx \ src/MusicBuffer.cxx src/MusicBuffer.hxx \ @@ -2141,19 +2140,11 @@ user_DATA = $(wildcard doc/user/*.html) developerdir = $(docdir)/developer developer_DATA = $(wildcard doc/developer/*.html) -if HAVE_XMLTO - DOCBOOK_HTML = $(patsubst %.xml,%/index.html,$(DOCBOOK_FILES)) $(DOCBOOK_HTML): %/index.html: %.xml $(XMLTO) -o $(@D) --stringparam chunker.output.encoding=utf-8 html --stringparam use.id.as.filename=1 $< -else - -DOCBOOK_HTML = - -endif - doc/api/html/index.html: doc/doxygen.conf @$(MKDIR_P) $(@D) $(DOXYGEN) $< diff --git a/NEWS b/NEWS index 5a24844..e8e84ff 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,21 @@ +ver 0.19.2 (2014/11/02) +* input + - curl: fix redirected streams +* playlist + - don't allow empty playlist name + - m3u: don't ignore unterminated last line + - m3u: recognize the file suffix ".m3u8" +* decoder + - ignore URI query string for plugin detection + - faad: remove workaround for ancient libfaad2 ABI bug + - ffmpeg: recognize MIME type audio/aacp + - mad: fix negative replay gain values +* output + - fix memory leak after filter initialization error + - fall back to PCM if given DSD sample rate is not supported +* fix assertion failure on unsupported PCM conversion +* auto-disable plugins that require GLib when --disable-glib is used + ver 0.19.1 (2014/10/19) * input - mms: fix deadlock bug @@ -85,6 +103,15 @@ ver 0.19 (2014/10/10) * install systemd unit for socket activation * Android port +ver 0.18.17 (2014/11/02) +* playlist + - don't allow empty playlist name + - m3u: recognize the file suffix ".m3u8" +* decoder + - ignore URI query string for plugin detection + - faad: remove workaround for ancient libfaad2 ABI bug + - ffmpeg: recognize MIME type audio/aacp + ver 0.18.16 (2014/09/26) * fix DSD breakage due to typo in configure.ac diff --git a/configure.ac b/configure.ac index 3f90bb6..f689a9f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,10 @@ AC_PREREQ(2.60) -AC_INIT(mpd, 0.19.1, musicpd-dev-t...@lists.sourceforge.net) +AC_INIT(mpd, 0.19.2, musicpd-dev-t...@lists.sourceforge.net) VERSION_MAJOR=0 VERSION_MINOR=19 -VERSION_REVISION=0 +VERSION_REVISION=2 VERSION_EXTRA=0 AC_CONFIG_SRCDIR([src/Main.cxx]) @@ -293,7 +293,9 @@ fi AC_ARG_ENABLE(libmpdclient, AS_HELP_STRING([--enable-libmpdclient], [enable support for the MPD client]),, - enable_libmpdclient=$database_auto) + enable_libmpdclient=auto) +MPD_DEPENDS([enable_libmpdclient], [enable_database], + [Cannot use --enable-libmpdclient with --disable-database]) AC_ARG_ENABLE(expat, AS_HELP_STRING([--enable-expat], @@ -303,7 +305,9 @@ AC_ARG_ENABLE(expat, AC_ARG_ENABLE(upnp, AS_HELP_STRING([--enable-upnp], [enable UPnP client support (default: auto)]),, - enable_upnp=$database_auto) + enable_upnp=auto) +MPD_DEPENDS([enable_upnp], [enable_database], + [Cannot use --enable-upnp with --disable-database]) AC_ARG_ENABLE(adplug, AS_HELP_STRING([--enable-adplug], @@ -323,6 +327,8 @@ AC_ARG_ENABLE(ao, AS_HELP_STRING([--enable-ao], [enable support for libao]),, enable_ao=auto) +MPD_DEPENDS([enable_ao], [enable_glib], + [Cannot use --enable-ao with --disable-glib]) AC_ARG_ENABLE(audiofile, AS_HELP_STRING([--enable-audiofile], @@ -343,6 +349,8 @@ AC_ARG_ENABLE(cdio-paranoia, AS_HELP_STRING([--enable-cdio-paranoia], [enable support for audio CD support]),, enable_cdio_paranoia=auto) +MPD_DEPENDS([enable_cdio_paranoia], [enable_glib], + [Cannot use --enable-cdio-paranoia with --disable-glib]) AC_ARG_ENABLE(curl, AS_HELP_STRING([--enable-curl], @@ -398,11 +406,15 @@ AC_ARG_ENABLE(gme, AS_HELP_STRING([--enable-gme], [enable Blargg's game music emulator plugin]),, enable_gme=auto) +MPD_DEPENDS([enable_gme], [enable_glib], + [Cannot use --enable-gme with --disable-glib]) AC_ARG_ENABLE(httpd-output, AS_HELP_STRING([--enable-httpd-output], [enables the HTTP server output]),, [enable_httpd_output=auto]) +MPD_DEPENDS([enable_httpd_output], [enable_glib], + [Cannot use --enable-httpd-output with --disable-glib]) AC_ARG_ENABLE(id3, AS_HELP_STRING([--enable-id3], @@ -428,6 +440,8 @@ AC_ARG_ENABLE(jack, AS_HELP_STRING([--enable-jack], [enable jack support]),, enable_jack=auto) +MPD_DEPENDS([enable_jack], [enable_glib], + [Cannot use --enable-jack with --disable-glib]) AC_SYS_LARGEFILE @@ -440,6 +454,8 @@ AC_ARG_ENABLE(soundcloud, AS_HELP_STRING([--enable-soundcloud], [enable support for soundcloud.com]),, [enable_soundcloud=auto]) +MPD_DEPENDS([enable_soundcloud], [enable_glib], + [Cannot use --enable-soundcloud with --disable-glib]) AC_ARG_ENABLE(lame-encoder, AS_HELP_STRING([--enable-lame-encoder], @@ -534,6 +550,8 @@ AC_ARG_ENABLE(sidplay, AS_HELP_STRING([--enable-sidplay], [enable C64 SID support via libsidplay2]),, enable_sidplay=auto) +MPD_DEPENDS([enable_sidplay], [enable_glib], + [Cannot use --enable-sidplay with --disable-glib]) AC_ARG_ENABLE(shine-encoder, AS_HELP_STRING([--enable-shine-encoder], @@ -559,6 +577,8 @@ AC_ARG_ENABLE(sqlite, AS_HELP_STRING([--enable-sqlite], [enable support for the SQLite database]),, [enable_sqlite=$database_auto]) +MPD_DEPENDS([enable_sqlite], [enable_glib], + [Cannot use --enable-sqlite with --disable-glib]) AC_ARG_ENABLE(systemd-daemon, AS_HELP_STRING([--enable-systemd-daemon], @@ -599,6 +619,8 @@ AC_ARG_ENABLE(vorbis-encoder, AS_HELP_STRING([--enable-vorbis-encoder], [enable the Ogg Vorbis encoder]),, [enable_vorbis_encoder=auto]) +MPD_DEPENDS([enable_vorbis_encoder], [enable_glib], + [Cannot use --enable-vorbis-encoder with --disable-glib]) AC_ARG_ENABLE(wave-encoder, AS_HELP_STRING([--enable-wave-encoder], @@ -609,6 +631,8 @@ AC_ARG_ENABLE(wavpack, AS_HELP_STRING([--enable-wavpack], [enable WavPack support]),, enable_wavpack=auto) +MPD_DEPENDS([enable_wavpack], [enable_glib], + [Cannot use --enable-wavpack with --disable-glib]) AC_ARG_ENABLE(werror, AS_HELP_STRING([--enable-werror], @@ -1709,8 +1733,11 @@ dnl Documentation dnl --------------------------------------------------------------------------- if test x$enable_documentation = xyes; then AC_PATH_PROG(XMLTO, xmlto) + if test x$XMLTO = x; then + AC_MSG_ERROR([xmlto not found]) + fi + AC_SUBST(XMLTO) - AM_CONDITIONAL(HAVE_XMLTO, test x$XMLTO != x) AC_PATH_PROG(DOXYGEN, doxygen) if test x$DOXYGEN = x; then @@ -1718,8 +1745,6 @@ if test x$enable_documentation = xyes; then fi AC_SUBST(DOXYGEN) -else - AM_CONDITIONAL(HAVE_XMLTO, false) fi AM_CONDITIONAL(ENABLE_DOCUMENTATION, test x$enable_documentation = xyes) @@ -1826,9 +1851,14 @@ results(ipv6, "IPv6") results(tcp, "TCP") results(un,[UNIX Domain Sockets]) +printf '\nStorage support:\n\t' +results(nfs, [NFS]) +results(smbclient, [SMB]) + printf '\nFile format support:\n\t' results(aac, [AAC]) results(adplug, [AdPlug]) +results(dsd, [DSD]) results(sidplay, [C64 SID]) results(ffmpeg, [FFMPEG]) results(flac, [FLAC]) diff --git a/m4/faad.m4 b/m4/faad.m4 index 5ca520e..9dcb1cc 100644 --- a/m4/faad.m4 +++ b/m4/faad.m4 @@ -62,36 +62,7 @@ int main() { CPPFLAGS=$oldcppflags fi -if test x$enable_aac = xyes; then - oldcflags=$CFLAGS - oldlibs=$LIBS - oldcppflags=$CPPFLAGS - CFLAGS="$CFLAGS $FAAD_CFLAGS -Werror" - LIBS="$LIBS $FAAD_LIBS" - CPPFLAGS=$CFLAGS - - AC_MSG_CHECKING(for broken libfaad headers) - AC_COMPILE_IFELSE([AC_LANG_SOURCE([ -#include <faad.h> -#include <stddef.h> -#include <stdint.h> - -int main() { - unsigned char channels; - uint32_t sample_rate; - - NeAACDecInit2(NULL, NULL, 0, &sample_rate, &channels); - return 0; -} - ])], - [AC_MSG_RESULT(correct)], - [AC_MSG_RESULT(broken); - AC_DEFINE(HAVE_FAAD_LONG, 1, [Define if faad.h uses the broken "unsigned long" pointers])]) - - CFLAGS=$oldcflags - LIBS=$oldlibs - CPPFLAGS=$oldcppflags -else +if test x$enable_aac = xno; then FAAD_LIBS="" FAAD_CFLAGS="" fi diff --git a/m4/mpd_depends.m4 b/m4/mpd_depends.m4 new file mode 100644 index 0000000..4898f90 --- /dev/null +++ b/m4/mpd_depends.m4 @@ -0,0 +1,9 @@ +AC_DEFUN([MPD_DEPENDS], [ + if test x$$2 = xno; then + if test x$$1 = xauto; then + $1=no + elif test x$$1 = xyes; then + AC_MSG_ERROR([$3]) + fi + fi +]) diff --git a/src/Main.cxx b/src/Main.cxx index d17590e..2719c05 100644 --- a/src/Main.cxx +++ b/src/Main.cxx @@ -114,6 +114,10 @@ #include <ws2tcpip.h> #endif +#ifdef __APPLE__ +#include <dispatch/dispatch.h> +#endif + #include <limits.h> static constexpr unsigned DEFAULT_BUFFER_SIZE = 4096; @@ -401,8 +405,6 @@ int main(int argc, char *argv[]) { #ifdef WIN32 return win32_main(argc, argv); -#elif __APPLE__ - return osx_main(argc, argv); #else return mpd_main(argc, argv); #endif @@ -410,6 +412,8 @@ int main(int argc, char *argv[]) #endif +static int mpd_main_after_fork(struct options); + #ifdef ANDROID static inline #endif @@ -513,6 +517,27 @@ int mpd_main(int argc, char *argv[]) daemonize_begin(options.daemon); #endif +#ifdef __APPLE__ + /* Runs the OS X native event loop in the main thread, and runs + the rest of mpd_main on a new thread. This lets CoreAudio receive + route change notifications (e.g. plugging or unplugging headphones). + All hardware output on OS X ultimately uses CoreAudio internally. + This must be run after forking; if dispatch is called before forking, + the child process will have a broken internal dispatch state. */ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + exit(mpd_main_after_fork(options)); + }); + dispatch_main(); + return EXIT_FAILURE; // unreachable, because dispatch_main never returns +#else + return mpd_main_after_fork(options); +#endif +} + +static int mpd_main_after_fork(struct options options) +{ + Error error; + GlobalEvents::Initialize(*instance->event_loop); GlobalEvents::Register(GlobalEvents::IDLE, idle_event_emitted); #ifdef WIN32 diff --git a/src/Main.hxx b/src/Main.hxx index dae7a50..7e3fecd 100644 --- a/src/Main.hxx +++ b/src/Main.hxx @@ -75,15 +75,4 @@ win32_app_stopping(void); #endif -#ifdef __APPLE__ - -/* Runs the OS X native event loop in the main thread, and runs - * mpd_main on a new thread. This lets CoreAudio receive route - * change notifications (e.g. plugging or unplugging headphones). - * All hardware output on OS X ultimately uses CoreAudio internally. - */ -int osx_main(int argc, char *argv[]); - -#endif - #endif diff --git a/src/PlaylistFile.cxx b/src/PlaylistFile.cxx index f0aa2d2..ab26937 100644 --- a/src/PlaylistFile.cxx +++ b/src/PlaylistFile.cxx @@ -64,6 +64,10 @@ spl_global_init(void) bool spl_valid_name(const char *name_utf8) { + if (*name_utf8 == 0) + /* empty name not allowed */ + return false; + /* * Not supporting '/' was done out of laziness, and we should * really strive to support it in the future. diff --git a/src/TagStream.cxx b/src/TagStream.cxx index 6397633..6201028 100644 --- a/src/TagStream.cxx +++ b/src/TagStream.cxx @@ -46,7 +46,8 @@ tag_stream_scan(InputStream &is, const tag_handler &handler, void *ctx) { assert(is.IsReady()); - const char *const suffix = uri_get_suffix(is.GetURI()); + UriSuffixBuffer suffix_buffer; + const char *const suffix = uri_get_suffix(is.GetURI(), suffix_buffer); const char *const mime = is.GetMimeType(); if (suffix == nullptr && mime == nullptr) diff --git a/src/decoder/DecoderThread.cxx b/src/decoder/DecoderThread.cxx index a39cfa6..dd5518b 100644 --- a/src/decoder/DecoderThread.cxx +++ b/src/decoder/DecoderThread.cxx @@ -237,7 +237,8 @@ static bool decoder_run_stream_locked(Decoder &decoder, InputStream &is, const char *uri, bool &tried_r) { - const char *const suffix = uri_get_suffix(uri); + UriSuffixBuffer suffix_buffer; + const char *const suffix = uri_get_suffix(uri, suffix_buffer); using namespace std::placeholders; const auto f = std::bind(decoder_run_stream_plugin, diff --git a/src/decoder/plugins/FaadDecoderPlugin.cxx b/src/decoder/plugins/FaadDecoderPlugin.cxx index 793ab10..add23aa 100644 --- a/src/decoder/plugins/FaadDecoderPlugin.cxx +++ b/src/decoder/plugins/FaadDecoderPlugin.cxx @@ -255,20 +255,12 @@ faad_decoder_init(NeAACDecHandle decoder, DecoderBuffer &buffer, } uint8_t channels; - uint32_t sample_rate; -#ifdef HAVE_FAAD_LONG - /* neaacdec.h declares all arguments as "unsigned long", but - internally expects uint32_t pointers. To avoid gcc - warnings, use this workaround. */ - unsigned long *sample_rate_p = (unsigned long *)(void *)&sample_rate; -#else - uint32_t *sample_rate_p = &sample_rate; -#endif + unsigned long sample_rate; long nbytes = NeAACDecInit(decoder, /* deconst hack, libfaad requires this */ const_cast<unsigned char *>(data.data), data.size, - sample_rate_p, &channels); + &sample_rate, &channels); if (nbytes < 0) { error.Set(faad_decoder_domain, "Not an AAC stream"); return false; diff --git a/src/decoder/plugins/FfmpegDecoderPlugin.cxx b/src/decoder/plugins/FfmpegDecoderPlugin.cxx index 904e21a..2e72269 100644 --- a/src/decoder/plugins/FfmpegDecoderPlugin.cxx +++ b/src/decoder/plugins/FfmpegDecoderPlugin.cxx @@ -680,6 +680,7 @@ static const char *const ffmpeg_mime_types[] = { "audio/8svx", "audio/16sv", "audio/aac", + "audio/aacp", "audio/ac3", "audio/aiff" "audio/amr", diff --git a/src/decoder/plugins/MadDecoderPlugin.cxx b/src/decoder/plugins/MadDecoderPlugin.cxx index 41efb59..de6c9b1 100644 --- a/src/decoder/plugins/MadDecoderPlugin.cxx +++ b/src/decoder/plugins/MadDecoderPlugin.cxx @@ -657,7 +657,7 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen) unsigned name = mad_bit_read(ptr, 3); /* gain name */ unsigned orig = mad_bit_read(ptr, 3); /* gain originator */ unsigned sign = mad_bit_read(ptr, 1); /* sign bit */ - unsigned gain = mad_bit_read(ptr, 9); /* gain*10 */ + int gain = mad_bit_read(ptr, 9); /* gain*10 */ if (gain && name == 1 && orig != 0) { lame->track_gain = ((sign ? -gain : gain) / 10.0) + adj; FormatDebug(mad_domain, "LAME track gain found: %f", diff --git a/src/decoder/plugins/Mp4v2DecoderPlugin.cxx b/src/decoder/plugins/Mp4v2DecoderPlugin.cxx index bf97763..34bccd2 100644 --- a/src/decoder/plugins/Mp4v2DecoderPlugin.cxx +++ b/src/decoder/plugins/Mp4v2DecoderPlugin.cxx @@ -39,15 +39,7 @@ static MP4TrackId mp4_get_aac_track(MP4FileHandle handle, NeAACDecHandle decoder, AudioFormat &audio_format, Error &error) { - uint32_t sample_rate; -#ifdef HAVE_FAAD_LONG - /* neaacdec.h declares all arguments as "unsigned long", but - internally expects uint32_t pointers. To avoid gcc - warnings, use this workaround. */ - unsigned long *sample_rate_r = (unsigned long*)&sample_rate; -#else - uint32_t *sample_rate_r = sample_rate; -#endif + unsigned long sample_rate; const MP4TrackId tracks = MP4GetNumberOfTracks(handle); @@ -80,7 +72,7 @@ mp4_get_aac_track(MP4FileHandle handle, NeAACDecHandle decoder, uint8_t channels; int32_t nbytes = NeAACDecInit(decoder, buff, buff_size, - sample_rate_r, &channels); + &sample_rate, &channels); free(buff); diff --git a/src/fs/Charset.cxx b/src/fs/Charset.cxx index 2d289c3..abf4d82 100644 --- a/src/fs/Charset.cxx +++ b/src/fs/Charset.cxx @@ -34,6 +34,8 @@ #include <assert.h> #include <string.h> +#ifdef HAVE_GLIB + /** * Maximal number of bytes required to represent path name in UTF-8 * (including nul-terminator). @@ -44,7 +46,6 @@ */ static constexpr size_t MPD_PATH_MAX_UTF8 = (MPD_PATH_MAX - 1) * 4 + 1; -#ifdef HAVE_GLIB static std::string fs_charset; gcc_pure diff --git a/src/input/AsyncInputStream.hxx b/src/input/AsyncInputStream.hxx index 7935f1a..d1f0c3b 100644 --- a/src/input/AsyncInputStream.hxx +++ b/src/input/AsyncInputStream.hxx @@ -83,6 +83,10 @@ protected: */ void SetTag(Tag *_tag); + void ClearTag() { + SetTag(nullptr); + } + void Pause(); bool IsPaused() const { diff --git a/src/input/InputStream.hxx b/src/input/InputStream.hxx index 15c3501..81b903b 100644 --- a/src/input/InputStream.hxx +++ b/src/input/InputStream.hxx @@ -200,6 +200,10 @@ public: return mime.empty() ? nullptr : mime.c_str(); } + void ClearMimeType() { + mime.clear(); + } + gcc_nonnull_all void SetMimeType(const char *_mime) { assert(!ready); diff --git a/src/input/TextInputStream.cxx b/src/input/TextInputStream.cxx index b79f64b..5a8dcc0 100644 --- a/src/input/TextInputStream.cxx +++ b/src/input/TextInputStream.cxx @@ -38,8 +38,8 @@ TextInputStream::ReadLine() while (true) { auto dest = buffer.Write(); if (dest.size < 2) { - /* end of file (or line too long): terminate - the current line */ + /* line too long: terminate the current + line */ assert(!dest.IsEmpty()); dest[0] = 0; @@ -66,7 +66,19 @@ TextInputStream::ReadLine() if (line != nullptr) return line; - if (nbytes == 0) - return nullptr; + if (nbytes == 0) { + /* end of file: see if there's an unterminated + line */ + + dest = buffer.Write(); + assert(!dest.IsEmpty()); + dest[0] = 0; + + auto r = buffer.Read(); + buffer.Clear(); + return r.IsEmpty() + ? nullptr + : r.data; + } } } diff --git a/src/input/plugins/CurlInputPlugin.cxx b/src/input/plugins/CurlInputPlugin.cxx index 617805e..1e1a461 100644 --- a/src/input/plugins/CurlInputPlugin.cxx +++ b/src/input/plugins/CurlInputPlugin.cxx @@ -109,6 +109,13 @@ struct CurlInputStream final : public AsyncInputStream { */ void FreeEasyIndirect(); + /** + * Called when a new response begins. This is used to discard + * headers from previous responses (for example authentication + * and redirects). + */ + void ResponseBoundary(); + void HeaderReceived(const char *name, std::string &&value); size_t DataReceived(const void *ptr, size_t size); @@ -598,6 +605,20 @@ CurlInputStream::~CurlInputStream() } inline void +CurlInputStream::ResponseBoundary() +{ + /* undo all effects of HeaderReceived() because the previous + response was not applicable for this stream */ + + seekable = false; + size = UNKNOWN_SIZE; + ClearMimeType(); + ClearTag(); + + // TODO: reset the IcyInputStream? +} + +inline void CurlInputStream::HeaderReceived(const char *name, std::string &&value) { if (IsSeekPending()) @@ -645,6 +666,11 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream) size *= nmemb; const char *header = (const char *)ptr; + if (size > 5 && memcmp(header, "HTTP/", 5) == 0) { + c.ResponseBoundary(); + return size; + } + const char *end = header + size; char name[64]; @@ -720,10 +746,10 @@ CurlInputStream::InitEasy(Error &error) input_curl_writefunction); curl_easy_setopt(easy, CURLOPT_WRITEDATA, this); curl_easy_setopt(easy, CURLOPT_HTTP200ALIASES, http_200_aliases); - curl_easy_setopt(easy, CURLOPT_FOLLOWLOCATION, 1); - curl_easy_setopt(easy, CURLOPT_NETRC, 1); - curl_easy_setopt(easy, CURLOPT_MAXREDIRS, 5); - curl_easy_setopt(easy, CURLOPT_FAILONERROR, true); + curl_easy_setopt(easy, CURLOPT_FOLLOWLOCATION, 1l); + curl_easy_setopt(easy, CURLOPT_NETRC, 1l); + curl_easy_setopt(easy, CURLOPT_MAXREDIRS, 5l); + curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1l); curl_easy_setopt(easy, CURLOPT_ERRORBUFFER, error_buffer); curl_easy_setopt(easy, CURLOPT_NOPROGRESS, 1l); curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1l); diff --git a/src/lib/upnp/Discovery.cxx b/src/lib/upnp/Discovery.cxx index 9ea78c6..1539e15 100644 --- a/src/lib/upnp/Discovery.cxx +++ b/src/lib/upnp/Discovery.cxx @@ -26,6 +26,7 @@ #include <upnp/upnptools.h> +#include <stdlib.h> #include <string.h> // The service type string we are looking for. diff --git a/src/osx/OSXMain.cxx b/src/osx/OSXMain.cxx deleted file mode 100644 index 1ac9aec..0000000 --- a/src/osx/OSXMain.cxx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2003-2014 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 "config.h" -#include "Main.hxx" - -#ifdef __APPLE__ - -#include <stdlib.h> -#include <dispatch/dispatch.h> - -int osx_main(int argc, char *argv[]) -{ - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - exit(mpd_main(argc, argv)); - }); - dispatch_main(); - return EXIT_FAILURE; // unreachable, because dispatch_main never returns -} - -#endif diff --git a/src/output/Internal.hxx b/src/output/Internal.hxx index 658ebd4..6e6ffb4 100644 --- a/src/output/Internal.hxx +++ b/src/output/Internal.hxx @@ -383,7 +383,12 @@ private: void Reopen(); AudioFormat OpenFilter(AudioFormat &format, Error &error_r); + + /** + * Mutex must not be locked. + */ void CloseFilter(); + void ReopenFilter(); /** diff --git a/src/output/OutputThread.cxx b/src/output/OutputThread.cxx index e464250..2ec0670 100644 --- a/src/output/OutputThread.cxx +++ b/src/output/OutputThread.cxx @@ -22,6 +22,7 @@ #include "OutputAPI.hxx" #include "Domain.hxx" #include "pcm/PcmMix.hxx" +#include "pcm/Domain.hxx" #include "notify.hxx" #include "filter/FilterInternal.hxx" #include "filter/plugins/ConvertFilterPlugin.hxx" @@ -165,6 +166,10 @@ AudioOutput::Open() out_audio_format.ApplyMask(config_audio_format); mutex.unlock(); + + const AudioFormat retry_audio_format = out_audio_format; + + retry_without_dsd: success = ao_plugin_open(this, out_audio_format, error); mutex.lock(); @@ -174,7 +179,10 @@ AudioOutput::Open() FormatError(error, "Failed to open \"%s\" [%s]", name, plugin.name); + mutex.unlock(); CloseFilter(); + mutex.lock(); + fail_timer.Update(); return; } @@ -184,7 +192,36 @@ AudioOutput::Open() FormatError(error, "Failed to convert for \"%s\" [%s]", name, plugin.name); + mutex.unlock(); + ao_plugin_close(this); + + if (error.IsDomain(pcm_domain) && + out_audio_format.format == SampleFormat::DSD) { + /* if the audio output supports DSD, but not + the given sample rate, it asks MPD to + resample; resampling DSD however is not + implemented; our last resort is to give up + DSD and fall back to PCM */ + + // TODO: clean up this workaround + + FormatError(output_domain, "Retrying without DSD"); + + out_audio_format = retry_audio_format; + out_audio_format.format = SampleFormat::FLOAT; + + /* clear the Error to allow reusing it */ + error.Clear(); + + /* sorry for the "goto" - this is a workaround + for the stable branch that should be as + unintrusive as possible */ + goto retry_without_dsd; + } + CloseFilter(); + mutex.lock(); + fail_timer.Update(); return; } @@ -233,7 +270,10 @@ AudioOutput::ReopenFilter() { Error error; + mutex.unlock(); CloseFilter(); + mutex.lock(); + const AudioFormat filter_audio_format = OpenFilter(in_audio_format, error); if (!filter_audio_format.IsDefined() || diff --git a/src/output/plugins/RoarOutputPlugin.cxx b/src/output/plugins/RoarOutputPlugin.cxx index 508e147..aa37c91 100644 --- a/src/output/plugins/RoarOutputPlugin.cxx +++ b/src/output/plugins/RoarOutputPlugin.cxx @@ -46,7 +46,7 @@ class RoarOutput { struct roar_connection con; struct roar_audio_info info; mutable Mutex mutex; - volatile bool alive; + bool alive; public: RoarOutput() diff --git a/src/pcm/ChannelsConverter.cxx b/src/pcm/ChannelsConverter.cxx index f93f4f6..7146137 100644 --- a/src/pcm/ChannelsConverter.cxx +++ b/src/pcm/ChannelsConverter.cxx @@ -43,7 +43,7 @@ PcmChannelsConverter::Open(SampleFormat _format, default: error.Format(pcm_domain, "PCM channel conversion for %s is not implemented", - sample_format_to_string(format)); + sample_format_to_string(_format)); return false; } diff --git a/src/pcm/FormatConverter.cxx b/src/pcm/FormatConverter.cxx index 64e2d85..b058b32 100644 --- a/src/pcm/FormatConverter.cxx +++ b/src/pcm/FormatConverter.cxx @@ -28,11 +28,31 @@ bool PcmFormatConverter::Open(SampleFormat _src_format, SampleFormat _dest_format, - gcc_unused Error &error) + Error &error) { assert(_src_format != SampleFormat::UNDEFINED); assert(_dest_format != SampleFormat::UNDEFINED); + switch (_dest_format) { + case SampleFormat::UNDEFINED: + assert(false); + gcc_unreachable(); + + case SampleFormat::S8: + case SampleFormat::DSD: + error.Format(pcm_domain, + "PCM conversion from %s to %s is not implemented", + sample_format_to_string(_src_format), + sample_format_to_string(_dest_format)); + return nullptr; + + case SampleFormat::S16: + case SampleFormat::S24_P32: + case SampleFormat::S32: + case SampleFormat::FLOAT: + break; + } + src_format = _src_format; dest_format = _dest_format; return true; @@ -48,20 +68,14 @@ PcmFormatConverter::Close() } ConstBuffer<void> -PcmFormatConverter::Convert(ConstBuffer<void> src, Error &error) +PcmFormatConverter::Convert(ConstBuffer<void> src, gcc_unused Error &error) { switch (dest_format) { case SampleFormat::UNDEFINED: - assert(false); - gcc_unreachable(); - case SampleFormat::S8: case SampleFormat::DSD: - error.Format(pcm_domain, - "PCM conversion from %s to %s is not implemented", - sample_format_to_string(src_format), - sample_format_to_string(dest_format)); - return nullptr; + assert(false); + gcc_unreachable(); case SampleFormat::S16: return pcm_convert_to_16(buffer, dither, diff --git a/src/pcm/PcmConvert.cxx b/src/pcm/PcmConvert.cxx index 2b7cfe7..4385667 100644 --- a/src/pcm/PcmConvert.cxx +++ b/src/pcm/PcmConvert.cxx @@ -51,7 +51,7 @@ PcmConvert::~PcmConvert() } bool -PcmConvert::Open(AudioFormat _src_format, AudioFormat _dest_format, +PcmConvert::Open(const AudioFormat _src_format, const AudioFormat _dest_format, Error &error) { assert(!src_format.IsValid()); @@ -59,36 +59,34 @@ PcmConvert::Open(AudioFormat _src_format, AudioFormat _dest_format, assert(_src_format.IsValid()); assert(_dest_format.IsValid()); - src_format = _src_format; - dest_format = _dest_format; - - AudioFormat format = src_format; + AudioFormat format = _src_format; if (format.format == SampleFormat::DSD) format.format = SampleFormat::FLOAT; - enable_resampler = format.sample_rate != dest_format.sample_rate; + enable_resampler = format.sample_rate != _dest_format.sample_rate; if (enable_resampler) { - if (!resampler.Open(format, dest_format.sample_rate, error)) + if (!resampler.Open(format, _dest_format.sample_rate, error)) return false; format.format = resampler.GetOutputSampleFormat(); - format.sample_rate = dest_format.sample_rate; + format.sample_rate = _dest_format.sample_rate; } - enable_format = format.format != dest_format.format; + enable_format = format.format != _dest_format.format; if (enable_format && - !format_converter.Open(format.format, dest_format.format, error)) { + !format_converter.Open(format.format, _dest_format.format, + error)) { if (enable_resampler) resampler.Close(); return false; } - format.format = dest_format.format; + format.format = _dest_format.format; - enable_channels = format.channels != dest_format.channels; + enable_channels = format.channels != _dest_format.channels; if (enable_channels && !channels_converter.Open(format.format, format.channels, - dest_format.channels, error)) { + _dest_format.channels, error)) { if (enable_format) format_converter.Close(); if (enable_resampler) @@ -96,6 +94,9 @@ PcmConvert::Open(AudioFormat _src_format, AudioFormat _dest_format, return false; } + src_format = _src_format; + dest_format = _dest_format; + return true; } diff --git a/src/playlist/PlaylistRegistry.cxx b/src/playlist/PlaylistRegistry.cxx index bc5932d..4e9ef89 100644 --- a/src/playlist/PlaylistRegistry.cxx +++ b/src/playlist/PlaylistRegistry.cxx @@ -139,12 +139,12 @@ static SongEnumerator * playlist_list_open_uri_suffix(const char *uri, Mutex &mutex, Cond &cond, const bool *tried) { - const char *suffix; SongEnumerator *playlist = nullptr; assert(uri != nullptr); - suffix = uri_get_suffix(uri); + UriSuffixBuffer suffix_buffer; + const char *const suffix = uri_get_suffix(uri, suffix_buffer); if (suffix == nullptr) return nullptr; @@ -257,7 +257,10 @@ playlist_list_open_stream(InputStream &is, const char *uri) return playlist; } - const char *suffix = uri != nullptr ? uri_get_suffix(uri) : nullptr; + UriSuffixBuffer suffix_buffer; + const char *suffix = uri != nullptr + ? uri_get_suffix(uri, suffix_buffer) + : nullptr; if (suffix != nullptr) { auto playlist = playlist_list_open_stream_suffix(is, suffix); if (playlist != nullptr) diff --git a/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx b/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx index fdd4357..93316ca 100644 --- a/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx +++ b/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx @@ -130,6 +130,7 @@ ExtM3uPlaylist::NextSong() static const char *const extm3u_suffixes[] = { "m3u", + "m3u8", nullptr }; diff --git a/src/playlist/plugins/M3uPlaylistPlugin.cxx b/src/playlist/plugins/M3uPlaylistPlugin.cxx index a4125bc..0428d29 100644 --- a/src/playlist/plugins/M3uPlaylistPlugin.cxx +++ b/src/playlist/plugins/M3uPlaylistPlugin.cxx @@ -60,6 +60,7 @@ M3uPlaylist::NextSong() static const char *const m3u_suffixes[] = { "m3u", + "m3u8", nullptr }; diff --git a/src/util/UriUtil.cxx b/src/util/UriUtil.cxx index fdca47c..62977e9 100644 --- a/src/util/UriUtil.cxx +++ b/src/util/UriUtil.cxx @@ -54,6 +54,23 @@ uri_get_suffix(const char *uri) return suffix; } +const char * +uri_get_suffix(const char *uri, UriSuffixBuffer &buffer) +{ + const char *suffix = uri_get_suffix(uri); + if (suffix == nullptr) + return nullptr; + + const char *q = strchr(suffix, '?'); + if (q != nullptr && size_t(q - suffix) < sizeof(buffer.data)) { + memcpy(buffer.data, suffix, q - suffix); + buffer.data[q - suffix] = 0; + suffix = buffer.data; + } + + return suffix; +} + static const char * verify_uri_segment(const char *p) { diff --git a/src/util/UriUtil.hxx b/src/util/UriUtil.hxx index c2cc97a..d478d5b 100644 --- a/src/util/UriUtil.hxx +++ b/src/util/UriUtil.hxx @@ -42,6 +42,17 @@ gcc_pure const char * uri_get_suffix(const char *uri); +struct UriSuffixBuffer { + char data[8]; +}; + +/** + * Returns the file name suffix, ignoring the query string. + */ +gcc_pure +const char * +uri_get_suffix(const char *uri, UriSuffixBuffer &buffer); + /** * Returns true if this is a safe "local" URI: * diff --git a/test/DumpDatabase.cxx b/test/DumpDatabase.cxx index d5b6f22..07f3423 100644 --- a/test/DumpDatabase.cxx +++ b/test/DumpDatabase.cxx @@ -33,7 +33,9 @@ #include "event/Loop.hxx" #include "util/Error.hxx" +#ifdef HAVE_GLIB #include <glib.h> +#endif #include <iostream> using std::cout; @@ -107,9 +109,11 @@ main(int argc, char **argv) /* initialize GLib */ +#ifdef HAVE_GLIB #if !GLIB_CHECK_VERSION(2,32,0) g_thread_init(nullptr); #endif +#endif /* initialize MPD */ diff --git a/test/read_mixer.cxx b/test/read_mixer.cxx index 9752905..de77a00 100644 --- a/test/read_mixer.cxx +++ b/test/read_mixer.cxx @@ -28,7 +28,9 @@ #include "util/Error.hxx" #include "Log.hxx" +#ifdef HAVE_GLIB #include <glib.h> +#endif #include <assert.h> #include <string.h> @@ -50,9 +52,11 @@ int main(int argc, gcc_unused char **argv) return EXIT_FAILURE; } +#ifdef HAVE_GLIB #if !GLIB_CHECK_VERSION(2,32,0) g_thread_init(NULL); #endif +#endif EventLoop event_loop; diff --git a/test/test_archive.cxx b/test/test_archive.cxx index 3536995..1b15e30 100644 --- a/test/test_archive.cxx +++ b/test/test_archive.cxx @@ -7,8 +7,6 @@ #include <cppunit/ui/text/TestRunner.h> #include <cppunit/extensions/HelperMacros.h> -#include <glib.h> - #include <string.h> #include <stdlib.h> @@ -29,22 +27,22 @@ ArchiveLookupTest::TestArchiveLookup() char *path = strdup(""); CPPUNIT_ASSERT_EQUAL(false, archive_lookup(path, &archive, &inpath, &suffix)); - g_free(path); + free(path); path = strdup("."); CPPUNIT_ASSERT_EQUAL(false, archive_lookup(path, &archive, &inpath, &suffix)); - g_free(path); + free(path); path = strdup("config.h"); CPPUNIT_ASSERT_EQUAL(false, archive_lookup(path, &archive, &inpath, &suffix)); - g_free(path); + free(path); path = strdup("src/foo/bar"); CPPUNIT_ASSERT_EQUAL(false, archive_lookup(path, &archive, &inpath, &suffix)); - g_free(path); + free(path); path = strdup("Makefile/foo/bar"); CPPUNIT_ASSERT_EQUAL(true, @@ -53,7 +51,7 @@ ArchiveLookupTest::TestArchiveLookup() CPPUNIT_ASSERT_EQUAL(0, strcmp(archive, "Makefile")); CPPUNIT_ASSERT_EQUAL(0, strcmp(inpath, "foo/bar")); CPPUNIT_ASSERT_EQUAL((const char *)nullptr, suffix); - g_free(path); + free(path); path = strdup("config.h/foo/bar"); CPPUNIT_ASSERT_EQUAL(true, @@ -62,7 +60,7 @@ ArchiveLookupTest::TestArchiveLookup() CPPUNIT_ASSERT_EQUAL(0, strcmp(archive, "config.h")); CPPUNIT_ASSERT_EQUAL(0, strcmp(inpath, "foo/bar")); CPPUNIT_ASSERT_EQUAL(0, strcmp(suffix, "h")); - g_free(path); + free(path); } CPPUNIT_TEST_SUITE_REGISTRATION(ArchiveLookupTest); diff --git a/test/test_util.cxx b/test/test_util.cxx index aaadec6..3e79aec 100644 --- a/test/test_util.cxx +++ b/test/test_util.cxx @@ -34,6 +34,25 @@ public: uri_get_suffix(".jpg")); CPPUNIT_ASSERT_EQUAL((const char *)nullptr, uri_get_suffix("/foo/.jpg")); + + /* the first overload does not eliminate the query + string */ + CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg?query_string"), + "jpg?query_string")); + + /* ... but the second one does */ + UriSuffixBuffer buffer; + CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg?query_string", + buffer), + "jpg")); + + /* repeat some of the above tests with the second overload */ + CPPUNIT_ASSERT_EQUAL((const char *)nullptr, + uri_get_suffix("/foo/bar", buffer)); + CPPUNIT_ASSERT_EQUAL((const char *)nullptr, + uri_get_suffix("/foo.jpg/bar", buffer)); + CPPUNIT_ASSERT_EQUAL(0, strcmp(uri_get_suffix("/foo/bar.jpg", buffer), + "jpg")); } void TestRemoveAuth() { diff --git a/test/visit_archive.cxx b/test/visit_archive.cxx index f5dba5c..1ff3ba4 100644 --- a/test/visit_archive.cxx +++ b/test/visit_archive.cxx @@ -30,7 +30,9 @@ #include "fs/Path.hxx" #include "util/Error.hxx" +#ifdef HAVE_GLIB #include <glib.h> +#endif #include <unistd.h> #include <stdlib.h> @@ -57,9 +59,11 @@ main(int argc, char **argv) /* initialize GLib */ +#ifdef HAVE_GLIB #if !GLIB_CHECK_VERSION(2,32,0) g_thread_init(NULL); #endif +#endif /* initialize MPD */