in the meantime, another bugfix release, mpd 0.19.3 was released; changelog below, debdiff attached. This upload would additionally fix #768094 (important: "crashes with std::logic_error on database update") and #769436 (normal: "Fails to play some audio streams")
ver 0.19.3 (2014/11/11) * protocol - fix "(null)" result string to "list" when AlbumArtist is disabled * database - upnp: fix breakage due to malformed URIs * input - curl: another fix for redirected streams * decoder - audiofile: fix crash while playing streams - audiofile: fix bit rate calculation - ffmpeg: support opus - opus: fix bogus duration on streams - opus: support chained streams - opus: improved error logging * fix distorted audio with soxr resampler * fix build failure on Mac OS X with non-Apple compilers
(auto-generated files removed) diff -Nru mpd-0.19.1/aclocal.m4 mpd-0.19.3/aclocal.m4 --- mpd-0.19.1/aclocal.m4 2014-10-11 20:26:11.000000000 +0200 +++ mpd-0.19.3/aclocal.m4 2014-11-05 18:24:19.000000000 +0100 @@ -1160,6 +1160,7 @@ m4_include([m4/faad.m4]) m4_include([m4/libwrap.m4]) m4_include([m4/mpd_auto.m4]) +m4_include([m4/mpd_depends.m4]) m4_include([m4/mpd_func.m4]) m4_include([m4/pkg.m4]) m4_include([m4/pretty_print.m4]) diff -Nru mpd-0.19.1/config.h.in mpd-0.19.3/config.h.in --- mpd-0.19.1/config.h.in 2014-10-11 20:26:20.000000000 +0200 +++ mpd-0.19.3/config.h.in 2014-11-05 18:24:30.000000000 +0100 @@ -138,9 +138,6 @@ /* Define to use FAAD2 for AAC decoding */ #undef HAVE_FAAD -/* Define if faad.h uses the broken "unsigned long" pointers */ -#undef HAVE_FAAD_LONG - /* Define for FFMPEG support */ #undef HAVE_FFMPEG diff -Nru mpd-0.19.1/configure.ac mpd-0.19.3/configure.ac --- mpd-0.19.1/configure.ac 2014-10-11 20:25:57.000000000 +0200 +++ mpd-0.19.3/configure.ac 2014-11-05 18:24:15.000000000 +0100 @@ -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.3, musicpd-dev-t...@lists.sourceforge.net) VERSION_MAJOR=0 VERSION_MINOR=19 -VERSION_REVISION=0 +VERSION_REVISION=3 VERSION_EXTRA=0 AC_CONFIG_SRCDIR([src/Main.cxx]) @@ -293,7 +293,9 @@ 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(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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 --------------------------------------------------------------------------- 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 @@ 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(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 -Nru mpd-0.19.1/debian/changelog mpd-0.19.3/debian/changelog --- mpd-0.19.1/debian/changelog 2014-10-20 00:38:46.000000000 +0200 +++ mpd-0.19.3/debian/changelog 2014-11-13 21:49:32.000000000 +0100 @@ -1,3 +1,11 @@ +mpd (0.19.3-1) unstable; urgency=medium + + * Import Upstream version 0.19.3 (closes: #767684, #769436) + * Disable libmp4v2 due to incompatible license (closes: #767504) + * Add charset_conversion_crash.patch (closes: #768094) + + -- Florian Schlichting <f...@debian.org> Thu, 13 Nov 2014 21:34:28 +0100 + mpd (0.19.1-1) unstable; urgency=medium * Import Upstream version 0.19.1 diff -Nru mpd-0.19.1/debian/patches/charset_conversion_crash.patch mpd-0.19.3/debian/patches/charset_conversion_crash.patch --- mpd-0.19.1/debian/patches/charset_conversion_crash.patch 1970-01-01 01:00:00.000000000 +0100 +++ mpd-0.19.3/debian/patches/charset_conversion_crash.patch 2014-11-13 21:49:32.000000000 +0100 @@ -0,0 +1,19 @@ +Description: fix crash on failed filename charset conversion + Construct a Null AllocatedPath if the filename conversion into UTF8 failed +Origin: http://git.musicpd.org/cgit/master/mpd.git/commit/?id=7e7b403043b55c2e1bb9227fce725ad87626ae97 + +--- a/src/fs/AllocatedPath.cxx ++++ b/src/fs/AllocatedPath.cxx +@@ -46,7 +46,11 @@ + AllocatedPath::FromUTF8(const char *path_utf8) + { + #ifdef HAVE_GLIB +- return AllocatedPath(Donate(), ::PathFromUTF8(path_utf8)); ++ char *path = ::PathFromUTF8(path_utf8); ++ if (path == nullptr) ++ return AllocatedPath::Null(); ++ ++ return AllocatedPath(Donate(), path); + #else + return FromFS(path_utf8); + #endif diff -Nru mpd-0.19.1/debian/patches/series mpd-0.19.3/debian/patches/series --- mpd-0.19.1/debian/patches/series 2014-10-19 20:59:54.000000000 +0200 +++ mpd-0.19.3/debian/patches/series 2014-11-13 21:49:32.000000000 +0100 @@ -1,2 +1,3 @@ systemd_honor_MPDCONF.patch +charset_conversion_crash.patch typo.patch diff -Nru mpd-0.19.1/doc/doxygen.conf mpd-0.19.3/doc/doxygen.conf --- mpd-0.19.1/doc/doxygen.conf 2014-10-11 20:26:19.000000000 +0200 +++ mpd-0.19.3/doc/doxygen.conf 2014-11-05 18:24:29.000000000 +0100 @@ -31,7 +31,7 @@ # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.19.1 +PROJECT_NUMBER = 0.19.3 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. @@ -534,7 +534,7 @@ # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = /home/max/git/mpd/src/ +INPUT = /home/max/git/stable-mpd/src/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is diff -Nru mpd-0.19.1/m4/faad.m4 mpd-0.19.3/m4/faad.m4 --- mpd-0.19.1/m4/faad.m4 2013-07-26 12:08:46.000000000 +0200 +++ mpd-0.19.3/m4/faad.m4 2014-10-27 09:26:34.000000000 +0100 @@ -62,36 +62,7 @@ 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 -Nru mpd-0.19.1/m4/mpd_depends.m4 mpd-0.19.3/m4/mpd_depends.m4 --- mpd-0.19.1/m4/mpd_depends.m4 1970-01-01 01:00:00.000000000 +0100 +++ mpd-0.19.3/m4/mpd_depends.m4 2014-10-27 09:26:34.000000000 +0100 @@ -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 -Nru mpd-0.19.1/Makefile.am mpd-0.19.3/Makefile.am --- mpd-0.19.1/Makefile.am 2014-10-12 08:41:09.000000000 +0200 +++ mpd-0.19.3/Makefile.am 2014-11-07 18:42:57.000000000 +0100 @@ -130,7 +130,6 @@ 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 \ @@ -1113,6 +1112,7 @@ # libinput_a_SOURCES = \ + src/input/Domain.cxx src/input/Domain.hxx \ src/input/Init.cxx src/input/Init.hxx \ src/input/Registry.cxx src/input/Registry.hxx \ src/input/Open.cxx \ @@ -2141,19 +2141,11 @@ 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 -Nru mpd-0.19.1/NEWS mpd-0.19.3/NEWS --- mpd-0.19.1/NEWS 2014-10-19 01:03:36.000000000 +0200 +++ mpd-0.19.3/NEWS 2014-11-11 11:21:38.000000000 +0100 @@ -1,3 +1,38 @@ +ver 0.19.3 (2014/11/11) +* protocol + - fix "(null)" result string to "list" when AlbumArtist is disabled +* database + - upnp: fix breakage due to malformed URIs +* input + - curl: another fix for redirected streams +* decoder + - audiofile: fix crash while playing streams + - audiofile: fix bit rate calculation + - ffmpeg: support opus + - opus: fix bogus duration on streams + - opus: support chained streams + - opus: improved error logging +* fix distorted audio with soxr resampler +* fix build failure on Mac OS X with non-Apple compilers + +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 +120,15 @@ * 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 -Nru mpd-0.19.1/src/db/Count.cxx mpd-0.19.3/src/db/Count.cxx --- mpd-0.19.1/src/db/Count.cxx 2014-08-29 23:30:42.000000000 +0200 +++ mpd-0.19.3/src/db/Count.cxx 2014-11-10 08:37:05.000000000 +0100 @@ -23,7 +23,7 @@ #include "Interface.hxx" #include "client/Client.hxx" #include "LightSong.hxx" -#include "tag/Set.hxx" +#include "tag/Tag.hxx" #include <functional> #include <map> diff -Nru mpd-0.19.1/src/db/plugins/upnp/Directory.cxx mpd-0.19.3/src/db/plugins/upnp/Directory.cxx --- mpd-0.19.1/src/db/plugins/upnp/Directory.cxx 2014-08-29 19:30:43.000000000 +0200 +++ mpd-0.19.3/src/db/plugins/upnp/Directory.cxx 2014-11-10 08:37:05.000000000 +0100 @@ -129,6 +129,7 @@ state(NONE), tag_type(TAG_NUM_OF_ITEM_TYPES) { + m_tobj.clear(); } protected: diff -Nru mpd-0.19.1/src/decoder/DecoderThread.cxx mpd-0.19.3/src/decoder/DecoderThread.cxx --- mpd-0.19.1/src/decoder/DecoderThread.cxx 2014-10-02 21:55:44.000000000 +0200 +++ mpd-0.19.3/src/decoder/DecoderThread.cxx 2014-11-01 13:15:50.000000000 +0100 @@ -237,7 +237,8 @@ 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 -Nru mpd-0.19.1/src/decoder/plugins/AudiofileDecoderPlugin.cxx mpd-0.19.3/src/decoder/plugins/AudiofileDecoderPlugin.cxx --- mpd-0.19.1/src/decoder/plugins/AudiofileDecoderPlugin.cxx 2014-09-19 23:57:02.000000000 +0200 +++ mpd-0.19.3/src/decoder/plugins/AudiofileDecoderPlugin.cxx 2014-11-10 08:59:42.000000000 +0100 @@ -209,7 +209,7 @@ const auto total_time = audiofile_get_duration(fh); const uint16_t kbit_rate = (uint16_t) - (is.GetSize() * uint64_t(8000) / total_time.ToMS()); + (is.GetSize() * uint64_t(8) / total_time.ToMS()); const unsigned frame_size = (unsigned) afGetVirtualFrameSize(fh, AF_DEFAULT_TRACK, true); diff -Nru mpd-0.19.1/src/decoder/plugins/FaadDecoderPlugin.cxx mpd-0.19.3/src/decoder/plugins/FaadDecoderPlugin.cxx --- mpd-0.19.1/src/decoder/plugins/FaadDecoderPlugin.cxx 2014-09-24 18:56:53.000000000 +0200 +++ mpd-0.19.3/src/decoder/plugins/FaadDecoderPlugin.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -255,20 +255,12 @@ } 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 -Nru mpd-0.19.1/src/decoder/plugins/FfmpegDecoderPlugin.cxx mpd-0.19.3/src/decoder/plugins/FfmpegDecoderPlugin.cxx --- mpd-0.19.1/src/decoder/plugins/FfmpegDecoderPlugin.cxx 2014-09-11 19:24:55.000000000 +0200 +++ mpd-0.19.3/src/decoder/plugins/FfmpegDecoderPlugin.cxx 2014-11-10 08:37:05.000000000 +0100 @@ -657,7 +657,7 @@ "mj2", "mjpeg", "mjpg", "mka", "mkv", "mlp", "mm", "mmf", "mov", "mp+", "mp1", "mp2", "mp3", "mp4", "mpc", "mpeg", "mpg", "mpga", "mpp", "mpu", "mve", "mvi", "mxf", "nc", "nsv", "nut", "nuv", "oga", "ogm", "ogv", - "ogx", "oma", "ogg", "omg", "psp", "pva", "qcp", "qt", "r3d", "ra", + "ogx", "oma", "ogg", "omg", "opus", "psp", "pva", "qcp", "qt", "r3d", "ra", "ram", "rl2", "rm", "rmvb", "roq", "rpl", "rvc", "shn", "smk", "snd", "sol", "son", "spx", "str", "swf", "tgi", "tgq", "tgv", "thp", "ts", "tsp", "tta", "xa", "xvid", "uv", "uv2", "vb", "vid", "vob", "voc", @@ -680,6 +680,7 @@ "audio/8svx", "audio/16sv", "audio/aac", + "audio/aacp", "audio/ac3", "audio/aiff" "audio/amr", @@ -690,6 +691,7 @@ "audio/mpeg", "audio/musepack", "audio/ogg", + "audio/opus", "audio/qcelp", "audio/vorbis", "audio/vorbis+ogg", diff -Nru mpd-0.19.1/src/decoder/plugins/MadDecoderPlugin.cxx mpd-0.19.3/src/decoder/plugins/MadDecoderPlugin.cxx --- mpd-0.19.1/src/decoder/plugins/MadDecoderPlugin.cxx 2014-09-24 22:42:53.000000000 +0200 +++ mpd-0.19.3/src/decoder/plugins/MadDecoderPlugin.cxx 2014-10-31 08:47:38.000000000 +0100 @@ -657,7 +657,7 @@ 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 -Nru mpd-0.19.1/src/decoder/plugins/Mp4v2DecoderPlugin.cxx mpd-0.19.3/src/decoder/plugins/Mp4v2DecoderPlugin.cxx --- mpd-0.19.1/src/decoder/plugins/Mp4v2DecoderPlugin.cxx 2014-09-24 23:28:54.000000000 +0200 +++ mpd-0.19.3/src/decoder/plugins/Mp4v2DecoderPlugin.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -39,15 +39,7 @@ 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 @@ uint8_t channels; int32_t nbytes = NeAACDecInit(decoder, buff, buff_size, - sample_rate_r, &channels); + &sample_rate, &channels); free(buff); diff -Nru mpd-0.19.1/src/decoder/plugins/OpusDecoderPlugin.cxx mpd-0.19.3/src/decoder/plugins/OpusDecoderPlugin.cxx --- mpd-0.19.1/src/decoder/plugins/OpusDecoderPlugin.cxx 2014-08-29 23:30:42.000000000 +0200 +++ mpd-0.19.3/src/decoder/plugins/OpusDecoderPlugin.cxx 2014-11-11 11:20:18.000000000 +0100 @@ -40,6 +40,12 @@ static constexpr opus_int32 opus_sample_rate = 48000; +/** + * Allocate an output buffer for 16 bit PCM samples big enough to hold + * a quarter second, larger than 120ms required by libopus. + */ +static constexpr unsigned opus_output_buffer_frames = opus_sample_rate / 4; + gcc_pure static bool IsOpusHead(const ogg_packet &packet) @@ -70,10 +76,16 @@ OpusDecoder *opus_decoder; opus_int16 *output_buffer; - unsigned output_size; + + /** + * If non-zero, then a previous Opus stream has been found + * already with this number of channels. If opus_decoder is + * nullptr, then its end-of-stream packet has been found + * already. + */ + unsigned previous_channels; bool os_initialized; - bool found_opus; int opus_serialno; @@ -86,8 +98,9 @@ InputStream &_input_stream) :decoder(_decoder), input_stream(_input_stream), opus_decoder(nullptr), - output_buffer(nullptr), output_size(0), - os_initialized(false), found_opus(false) {} + output_buffer(nullptr), + previous_channels(0), + os_initialized(false) {} ~MPDOpusDecoder(); bool ReadFirstPage(OggSyncState &oy); @@ -96,6 +109,7 @@ DecoderCommand HandlePackets(); DecoderCommand HandlePacket(const ogg_packet &packet); DecoderCommand HandleBOS(const ogg_packet &packet); + DecoderCommand HandleEOS(); DecoderCommand HandleTags(const ogg_packet &packet); DecoderCommand HandleAudio(const ogg_packet &packet); @@ -159,12 +173,14 @@ MPDOpusDecoder::HandlePacket(const ogg_packet &packet) { if (packet.e_o_s) - return DecoderCommand::STOP; + return HandleEOS(); if (packet.b_o_s) return HandleBOS(packet); - else if (!found_opus) + else if (opus_decoder == nullptr) { + LogDebug(opus_domain, "BOS packet expected"); return DecoderCommand::STOP; + } if (IsOpusTags(packet)) return HandleTags(packet); @@ -184,7 +200,7 @@ /* we do this for local files only, because seeking around remote files is expensive and not worth the troubl */ - return -1; + return false; const auto old_offset = is.GetOffset(); @@ -225,19 +241,29 @@ { assert(packet.b_o_s); - if (found_opus || !IsOpusHead(packet)) + if (opus_decoder != nullptr || !IsOpusHead(packet)) { + LogDebug(opus_domain, "BOS packet must be OpusHead"); return DecoderCommand::STOP; + } unsigned channels; if (!ScanOpusHeader(packet.packet, packet.bytes, channels) || - !audio_valid_channel_count(channels)) + !audio_valid_channel_count(channels)) { + LogDebug(opus_domain, "Malformed BOS packet"); return DecoderCommand::STOP; + } assert(opus_decoder == nullptr); - assert(output_buffer == nullptr); + assert((previous_channels == 0) == (output_buffer == nullptr)); + + if (previous_channels != 0 && channels != previous_channels) { + FormatWarning(opus_domain, + "Next stream has different channels (%u -> %u)", + previous_channels, channels); + return DecoderCommand::STOP; + } opus_serialno = os.serialno; - found_opus = true; /* TODO: parse attributes from the OpusHead (sample rate, channels, ...) */ @@ -251,6 +277,13 @@ return DecoderCommand::STOP; } + if (previous_channels != 0) { + /* decoder was already initialized by the previous + stream; skip the rest of this method */ + LogDebug(opus_domain, "Found another stream"); + return decoder_get_command(decoder); + } + eos_granulepos = LoadEOSGranulePos(input_stream, &decoder, opus_serialno); const auto duration = eos_granulepos >= 0 @@ -258,22 +291,37 @@ opus_sample_rate) : SignedSongTime::Negative(); + previous_channels = channels; const AudioFormat audio_format(opus_sample_rate, SampleFormat::S16, channels); decoder_initialized(decoder, audio_format, eos_granulepos > 0, duration); frame_size = audio_format.GetFrameSize(); - /* allocate an output buffer for 16 bit PCM samples big enough - to hold a quarter second, larger than 120ms required by - libopus */ - output_size = audio_format.sample_rate / 4; - output_buffer = new opus_int16[output_size * audio_format.channels]; + output_buffer = new opus_int16[opus_output_buffer_frames + * audio_format.channels]; return decoder_get_command(decoder); } inline DecoderCommand +MPDOpusDecoder::HandleEOS() +{ + if (eos_granulepos < 0 && previous_channels != 0) { + /* allow chaining of (unseekable) streams */ + assert(opus_decoder != nullptr); + assert(output_buffer != nullptr); + + opus_decoder_destroy(opus_decoder); + opus_decoder = nullptr; + + return decoder_get_command(decoder); + } + + return DecoderCommand::STOP; +} + +inline DecoderCommand MPDOpusDecoder::HandleTags(const ogg_packet &packet) { ReplayGainInfo rgi; @@ -304,10 +352,11 @@ int nframes = opus_decode(opus_decoder, (const unsigned char*)packet.packet, packet.bytes, - output_buffer, output_size, + output_buffer, opus_output_buffer_frames, 0); if (nframes < 0) { - LogError(opus_domain, opus_strerror(nframes)); + FormatError(opus_domain, "libopus error: %s", + opus_strerror(nframes)); return DecoderCommand::STOP; } diff -Nru mpd-0.19.1/src/fs/Charset.cxx mpd-0.19.3/src/fs/Charset.cxx --- mpd-0.19.1/src/fs/Charset.cxx 2014-02-23 22:03:40.000000000 +0100 +++ mpd-0.19.3/src/fs/Charset.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -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 -Nru mpd-0.19.1/src/input/AsyncInputStream.cxx mpd-0.19.3/src/input/AsyncInputStream.cxx --- mpd-0.19.1/src/input/AsyncInputStream.cxx 2014-08-19 21:19:11.000000000 +0200 +++ mpd-0.19.3/src/input/AsyncInputStream.cxx 2014-11-10 08:37:05.000000000 +0100 @@ -19,6 +19,7 @@ #include "config.h" #include "AsyncInputStream.hxx" +#include "Domain.hxx" #include "tag/Tag.hxx" #include "event/Call.hxx" #include "thread/Cond.hxx" @@ -113,8 +114,10 @@ /* no-op */ return true; - if (!IsSeekable()) + if (!IsSeekable()) { + error.Set(input_domain, "Not seekable"); return false; + } /* check if we can fast-forward the buffer */ diff -Nru mpd-0.19.1/src/input/AsyncInputStream.hxx mpd-0.19.3/src/input/AsyncInputStream.hxx --- mpd-0.19.1/src/input/AsyncInputStream.hxx 2014-09-02 21:36:20.000000000 +0200 +++ mpd-0.19.3/src/input/AsyncInputStream.hxx 2014-11-02 21:01:47.000000000 +0100 @@ -83,6 +83,10 @@ */ void SetTag(Tag *_tag); + void ClearTag() { + SetTag(nullptr); + } + void Pause(); bool IsPaused() const { diff -Nru mpd-0.19.1/src/input/Domain.cxx mpd-0.19.3/src/input/Domain.cxx --- mpd-0.19.1/src/input/Domain.cxx 1970-01-01 01:00:00.000000000 +0100 +++ mpd-0.19.3/src/input/Domain.cxx 2014-11-07 18:42:57.000000000 +0100 @@ -0,0 +1,24 @@ +/* + * 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 "Domain.hxx" +#include "util/Domain.hxx" + +const Domain input_domain("input"); diff -Nru mpd-0.19.1/src/input/Domain.hxx mpd-0.19.3/src/input/Domain.hxx --- mpd-0.19.1/src/input/Domain.hxx 1970-01-01 01:00:00.000000000 +0100 +++ mpd-0.19.3/src/input/Domain.hxx 2014-11-07 18:42:57.000000000 +0100 @@ -0,0 +1,27 @@ +/* + * 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. + */ + +#ifndef MPD_INPUT_DOMAIN_HXX +#define MPD_INPUT_DOMAIN_HXX + +class Domain; + +extern const Domain input_domain; + +#endif diff -Nru mpd-0.19.1/src/input/InputStream.hxx mpd-0.19.3/src/input/InputStream.hxx --- mpd-0.19.1/src/input/InputStream.hxx 2014-10-11 21:59:12.000000000 +0200 +++ mpd-0.19.3/src/input/InputStream.hxx 2014-11-02 21:01:47.000000000 +0100 @@ -200,6 +200,10 @@ return mime.empty() ? nullptr : mime.c_str(); } + void ClearMimeType() { + mime.clear(); + } + gcc_nonnull_all void SetMimeType(const char *_mime) { assert(!ready); diff -Nru mpd-0.19.1/src/input/Open.cxx mpd-0.19.3/src/input/Open.cxx --- mpd-0.19.1/src/input/Open.cxx 2014-10-11 21:59:12.000000000 +0200 +++ mpd-0.19.3/src/input/Open.cxx 2014-11-07 18:42:57.000000000 +0100 @@ -22,14 +22,13 @@ #include "Registry.hxx" #include "InputPlugin.hxx" #include "LocalOpen.hxx" +#include "Domain.hxx" #include "plugins/RewindInputPlugin.hxx" #include "fs/Traits.hxx" #include "fs/Path.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" -static constexpr Domain input_domain("input"); - InputStream * InputStream::Open(const char *url, Mutex &mutex, Cond &cond, diff -Nru mpd-0.19.1/src/input/plugins/CurlInputPlugin.cxx mpd-0.19.3/src/input/plugins/CurlInputPlugin.cxx --- mpd-0.19.1/src/input/plugins/CurlInputPlugin.cxx 2014-09-16 18:40:37.000000000 +0200 +++ mpd-0.19.3/src/input/plugins/CurlInputPlugin.cxx 2014-11-10 08:44:08.000000000 +0100 @@ -109,6 +109,13 @@ */ 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,24 @@ } inline void +CurlInputStream::ResponseBoundary() +{ + /* undo all effects of HeaderReceived() because the previous + response was not applicable for this stream */ + + if (IsSeekPending()) + /* don't update metadata while seeking */ + return; + + 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 +670,11 @@ 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 +750,10 @@ 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 -Nru mpd-0.19.1/src/input/TextInputStream.cxx mpd-0.19.3/src/input/TextInputStream.cxx --- mpd-0.19.1/src/input/TextInputStream.cxx 2014-10-19 00:49:40.000000000 +0200 +++ mpd-0.19.3/src/input/TextInputStream.cxx 2014-10-31 08:47:38.000000000 +0100 @@ -38,8 +38,8 @@ 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 @@ 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 -Nru mpd-0.19.1/src/lib/upnp/ContentDirectoryService.cxx mpd-0.19.3/src/lib/upnp/ContentDirectoryService.cxx --- mpd-0.19.1/src/lib/upnp/ContentDirectoryService.cxx 2014-10-10 22:46:59.000000000 +0200 +++ mpd-0.19.3/src/lib/upnp/ContentDirectoryService.cxx 2014-11-10 08:37:05.000000000 +0100 @@ -29,7 +29,7 @@ ContentDirectoryService::ContentDirectoryService(const UPnPDevice &device, const UPnPService &service) - :m_actionURL(uri_apply_base(device.URLBase, service.controlURL)), + :m_actionURL(uri_apply_base(service.controlURL, device.URLBase)), m_serviceType(service.serviceType), m_deviceId(device.UDN), m_friendlyName(device.friendlyName), diff -Nru mpd-0.19.1/src/lib/upnp/Discovery.cxx mpd-0.19.3/src/lib/upnp/Discovery.cxx --- mpd-0.19.1/src/lib/upnp/Discovery.cxx 2014-01-26 15:58:26.000000000 +0100 +++ mpd-0.19.3/src/lib/upnp/Discovery.cxx 2014-11-02 11:04:03.000000000 +0100 @@ -26,6 +26,7 @@ #include <upnp/upnptools.h> +#include <stdlib.h> #include <string.h> // The service type string we are looking for. diff -Nru mpd-0.19.1/src/Main.cxx mpd-0.19.3/src/Main.cxx --- mpd-0.19.1/src/Main.cxx 2014-10-10 19:45:56.000000000 +0200 +++ mpd-0.19.3/src/Main.cxx 2014-11-11 06:22:24.000000000 +0100 @@ -114,6 +114,10 @@ #include <ws2tcpip.h> #endif +#ifdef __BLOCKS__ +#include <dispatch/dispatch.h> +#endif + #include <limits.h> static constexpr unsigned DEFAULT_BUFFER_SIZE = 4096; @@ -401,8 +405,6 @@ { #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 @@ #endif +static int mpd_main_after_fork(struct options); + #ifdef ANDROID static inline #endif @@ -513,6 +517,27 @@ daemonize_begin(options.daemon); #endif +#ifdef __BLOCKS__ + /* 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 -Nru mpd-0.19.1/src/Main.hxx mpd-0.19.3/src/Main.hxx --- mpd-0.19.1/src/Main.hxx 2014-10-10 19:45:56.000000000 +0200 +++ mpd-0.19.3/src/Main.hxx 2014-10-31 15:03:46.000000000 +0100 @@ -75,15 +75,4 @@ #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 -Nru mpd-0.19.1/src/osx/OSXMain.cxx mpd-0.19.3/src/osx/OSXMain.cxx --- mpd-0.19.1/src/osx/OSXMain.cxx 2014-10-10 19:45:56.000000000 +0200 +++ mpd-0.19.3/src/osx/OSXMain.cxx 1970-01-01 01:00:00.000000000 +0100 @@ -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 -Nru mpd-0.19.1/src/output/Internal.hxx mpd-0.19.3/src/output/Internal.hxx --- mpd-0.19.1/src/output/Internal.hxx 2014-08-12 16:00:27.000000000 +0200 +++ mpd-0.19.3/src/output/Internal.hxx 2014-10-27 09:26:34.000000000 +0100 @@ -383,7 +383,12 @@ void Reopen(); AudioFormat OpenFilter(AudioFormat &format, Error &error_r); + + /** + * Mutex must not be locked. + */ void CloseFilter(); + void ReopenFilter(); /** diff -Nru mpd-0.19.1/src/output/OutputThread.cxx mpd-0.19.3/src/output/OutputThread.cxx --- mpd-0.19.1/src/output/OutputThread.cxx 2014-09-24 21:47:32.000000000 +0200 +++ mpd-0.19.3/src/output/OutputThread.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -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 @@ 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 @@ FormatError(error, "Failed to open \"%s\" [%s]", name, plugin.name); + mutex.unlock(); CloseFilter(); + mutex.lock(); + fail_timer.Update(); return; } @@ -184,7 +192,36 @@ 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 @@ { Error error; + mutex.unlock(); CloseFilter(); + mutex.lock(); + const AudioFormat filter_audio_format = OpenFilter(in_audio_format, error); if (!filter_audio_format.IsDefined() || diff -Nru mpd-0.19.1/src/output/plugins/RoarOutputPlugin.cxx mpd-0.19.3/src/output/plugins/RoarOutputPlugin.cxx --- mpd-0.19.1/src/output/plugins/RoarOutputPlugin.cxx 2014-09-28 13:24:40.000000000 +0200 +++ mpd-0.19.3/src/output/plugins/RoarOutputPlugin.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -46,7 +46,7 @@ struct roar_connection con; struct roar_audio_info info; mutable Mutex mutex; - volatile bool alive; + bool alive; public: RoarOutput() diff -Nru mpd-0.19.1/src/pcm/ChannelsConverter.cxx mpd-0.19.3/src/pcm/ChannelsConverter.cxx --- mpd-0.19.1/src/pcm/ChannelsConverter.cxx 2014-01-24 00:22:50.000000000 +0100 +++ mpd-0.19.3/src/pcm/ChannelsConverter.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -43,7 +43,7 @@ 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 -Nru mpd-0.19.1/src/pcm/FormatConverter.cxx mpd-0.19.3/src/pcm/FormatConverter.cxx --- mpd-0.19.1/src/pcm/FormatConverter.cxx 2014-01-24 00:22:50.000000000 +0100 +++ mpd-0.19.3/src/pcm/FormatConverter.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -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 @@ } 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 -Nru mpd-0.19.1/src/pcm/PcmConvert.cxx mpd-0.19.3/src/pcm/PcmConvert.cxx --- mpd-0.19.1/src/pcm/PcmConvert.cxx 2014-09-26 12:17:58.000000000 +0200 +++ mpd-0.19.3/src/pcm/PcmConvert.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -51,7 +51,7 @@ } 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 @@ 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 @@ return false; } + src_format = _src_format; + dest_format = _dest_format; + return true; } diff -Nru mpd-0.19.1/src/pcm/SoxrResampler.cxx mpd-0.19.3/src/pcm/SoxrResampler.cxx --- mpd-0.19.1/src/pcm/SoxrResampler.cxx 2014-08-16 08:24:55.000000000 +0200 +++ mpd-0.19.3/src/pcm/SoxrResampler.cxx 2014-11-10 23:00:14.000000000 +0100 @@ -147,7 +147,8 @@ const size_t n_frames = src.size / frame_size; - const size_t o_frames = size_t(n_frames * ratio + 0.5); + /* always round up: worst case output buffer size */ + const size_t o_frames = size_t(n_frames * ratio) + 1; float *output_buffer = (float *)buffer.Get(o_frames * frame_size); diff -Nru mpd-0.19.1/src/playlist/PlaylistRegistry.cxx mpd-0.19.3/src/playlist/PlaylistRegistry.cxx --- mpd-0.19.1/src/playlist/PlaylistRegistry.cxx 2014-05-12 18:59:46.000000000 +0200 +++ mpd-0.19.3/src/playlist/PlaylistRegistry.cxx 2014-11-01 13:18:48.000000000 +0100 @@ -139,12 +139,12 @@ 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 @@ 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 -Nru mpd-0.19.1/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx mpd-0.19.3/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx --- mpd-0.19.1/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx 2014-10-19 00:37:35.000000000 +0200 +++ mpd-0.19.3/src/playlist/plugins/ExtM3uPlaylistPlugin.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -130,6 +130,7 @@ static const char *const extm3u_suffixes[] = { "m3u", + "m3u8", nullptr }; diff -Nru mpd-0.19.1/src/playlist/plugins/M3uPlaylistPlugin.cxx mpd-0.19.3/src/playlist/plugins/M3uPlaylistPlugin.cxx --- mpd-0.19.1/src/playlist/plugins/M3uPlaylistPlugin.cxx 2014-08-07 16:53:20.000000000 +0200 +++ mpd-0.19.3/src/playlist/plugins/M3uPlaylistPlugin.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -60,6 +60,7 @@ static const char *const m3u_suffixes[] = { "m3u", + "m3u8", nullptr }; diff -Nru mpd-0.19.1/src/PlaylistFile.cxx mpd-0.19.3/src/PlaylistFile.cxx --- mpd-0.19.1/src/PlaylistFile.cxx 2014-08-07 19:35:50.000000000 +0200 +++ mpd-0.19.3/src/PlaylistFile.cxx 2014-10-31 14:58:22.000000000 +0100 @@ -64,6 +64,10 @@ 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 -Nru mpd-0.19.1/src/tag/Set.cxx mpd-0.19.3/src/tag/Set.cxx --- mpd-0.19.1/src/tag/Set.cxx 2014-07-12 18:45:35.000000000 +0200 +++ mpd-0.19.3/src/tag/Set.cxx 2014-11-10 08:37:05.000000000 +0100 @@ -19,6 +19,7 @@ #include "Set.hxx" #include "TagBuilder.hxx" +#include "TagSettings.h" #include <assert.h> @@ -109,6 +110,7 @@ if (!CheckUnique(type, tag, type, group_mask) && (type != TAG_ALBUM_ARTIST || + ignore_tag_items[TAG_ALBUM_ARTIST] || /* fall back to "Artist" if no "AlbumArtist" was found */ !CheckUnique(type, tag, TAG_ARTIST, group_mask))) InsertUnique(tag, type, nullptr, group_mask); diff -Nru mpd-0.19.1/src/TagStream.cxx mpd-0.19.3/src/TagStream.cxx --- mpd-0.19.1/src/TagStream.cxx 2014-05-11 17:03:06.000000000 +0200 +++ mpd-0.19.3/src/TagStream.cxx 2014-11-01 13:15:55.000000000 +0100 @@ -46,7 +46,8 @@ { 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 -Nru mpd-0.19.1/src/util/UriUtil.cxx mpd-0.19.3/src/util/UriUtil.cxx --- mpd-0.19.1/src/util/UriUtil.cxx 2014-10-10 22:43:40.000000000 +0200 +++ mpd-0.19.3/src/util/UriUtil.cxx 2014-11-01 12:51:30.000000000 +0100 @@ -54,6 +54,23 @@ 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 -Nru mpd-0.19.1/src/util/UriUtil.hxx mpd-0.19.3/src/util/UriUtil.hxx --- mpd-0.19.1/src/util/UriUtil.hxx 2014-10-10 22:43:40.000000000 +0200 +++ mpd-0.19.3/src/util/UriUtil.hxx 2014-11-01 13:41:16.000000000 +0100 @@ -42,6 +42,17 @@ 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 -Nru mpd-0.19.1/test/DumpDatabase.cxx mpd-0.19.3/test/DumpDatabase.cxx --- mpd-0.19.1/test/DumpDatabase.cxx 2014-02-23 19:55:27.000000000 +0100 +++ mpd-0.19.3/test/DumpDatabase.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -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 @@ /* initialize GLib */ +#ifdef HAVE_GLIB #if !GLIB_CHECK_VERSION(2,32,0) g_thread_init(nullptr); #endif +#endif /* initialize MPD */ diff -Nru mpd-0.19.1/test/FakeDecoderAPI.cxx mpd-0.19.3/test/FakeDecoderAPI.cxx --- mpd-0.19.1/test/FakeDecoderAPI.cxx 2014-08-29 23:30:42.000000000 +0200 +++ mpd-0.19.3/test/FakeDecoderAPI.cxx 2014-11-10 08:52:27.000000000 +0100 @@ -132,6 +132,12 @@ const void *data, size_t datalen, gcc_unused uint16_t kbit_rate) { + static uint16_t prev_kbit_rate; + if (kbit_rate != prev_kbit_rate) { + prev_kbit_rate = kbit_rate; + fprintf(stderr, "%u kbit/s\n", kbit_rate); + } + gcc_unused ssize_t nbytes = write(1, data, datalen); return DecoderCommand::NONE; } diff -Nru mpd-0.19.1/test/read_mixer.cxx mpd-0.19.3/test/read_mixer.cxx --- mpd-0.19.1/test/read_mixer.cxx 2014-02-19 21:59:25.000000000 +0100 +++ mpd-0.19.3/test/read_mixer.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -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 @@ return EXIT_FAILURE; } +#ifdef HAVE_GLIB #if !GLIB_CHECK_VERSION(2,32,0) g_thread_init(NULL); #endif +#endif EventLoop event_loop; diff -Nru mpd-0.19.1/test/test_archive.cxx mpd-0.19.3/test/test_archive.cxx --- mpd-0.19.1/test/test_archive.cxx 2014-01-24 09:49:05.000000000 +0100 +++ mpd-0.19.3/test/test_archive.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -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 @@ 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 @@ 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 @@ 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 -Nru mpd-0.19.1/test/test_util.cxx mpd-0.19.3/test/test_util.cxx --- mpd-0.19.1/test/test_util.cxx 2014-10-10 22:40:13.000000000 +0200 +++ mpd-0.19.3/test/test_util.cxx 2014-11-01 13:14:50.000000000 +0100 @@ -34,6 +34,25 @@ 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 -Nru mpd-0.19.1/test/visit_archive.cxx mpd-0.19.3/test/visit_archive.cxx --- mpd-0.19.1/test/visit_archive.cxx 2014-10-07 20:09:31.000000000 +0200 +++ mpd-0.19.3/test/visit_archive.cxx 2014-10-27 09:26:34.000000000 +0100 @@ -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 @@ /* initialize GLib */ +#ifdef HAVE_GLIB #if !GLIB_CHECK_VERSION(2,32,0) g_thread_init(NULL); #endif +#endif /* initialize MPD */