Compare commits

...

51 Commits

Author SHA1 Message Date
Max Kellermann
a40510c241 release v0.20.6 2017-03-10 16:57:59 +01:00
Max Kellermann
ac8dce6599 lib/curl/Request: "ICY 200 OK" is a response boundary header 2017-03-10 16:28:02 +01:00
Max Kellermann
190d525099 lib/curl/Request: move code to IsResponseBoundaryHeader() 2017-03-10 16:24:30 +01:00
Max Kellermann
1b6666fa39 Partition: handle SYNC_WITH_PLAYER before TAG_MODIFIED
The TAG_MODIFIED handler (i.e. playlist::TagModified()) works only if
the modified song is the current song - something that is not updated
until SYNC_WITH_PLAYER is finished.  This fixes tag updates right
after a new song is started.
2017-03-10 16:11:34 +01:00
Max Kellermann
1dd01c99e8 decoder/sidplay: make compatible with libsidplayfp < 1.8
https://bugs.musicpd.org/view.php?id=4665
2017-03-10 13:48:52 +01:00
Thomas Zander
d50b30a498 Add missing include for cstdlib, otherwise free() is undefined 2017-03-07 20:02:36 +01:00
Max Kellermann
42a3a87f13 util/HugeAllocator: paranoid check for sysconf()<0
Just in case.
2017-03-01 21:50:26 +01:00
Ben Boeckel
9dfedbe619 ReusableArray: fix build error on GCC7
GCC7 outputs the following error without this change:

    src/util/ReusableArray.hxx:61:35: error: no matching function for call to ‘swap(size_t&, const size_t&)’
       std::swap(capacity, src.capacity);

which can be resolved by just using an rvalue-reference rather than a
const rvalue-reference.

Signed-off-by: Ben Boeckel <mathstuf@gmail.com>
2017-03-01 19:38:41 +01:00
Max Kellermann
88957b4c9d android/build.py: build with libc++ instead of GNU libstdc++
Android is migrating away from GCC, and libstdc++ will disappear
eventually.
2017-03-01 17:31:26 +01:00
Max Kellermann
b2f2c9322b db/simple/Mount: workaround for libc++ 2017-03-01 17:31:26 +01:00
Max Kellermann
3be2051808 decoder/Thread: check ENABLE_FFMPEG, not HAVE_FFMPEG
This repairs the damage to commit 74dbaade6f done by commit
b3f5b4932c
2017-03-01 17:06:23 +01:00
Max Kellermann
ff32b0dc9b input/curl: use %lu instead of %llu
Fixes a GCC warning because %llu appears to be unsupported by the
Windows standard library.
2017-03-01 16:44:11 +01:00
Max Kellermann
c1869a11af input/curl: format Range offset as unsigned 2017-03-01 16:44:08 +01:00
Max Kellermann
e22a4fdba4 command/Error: improve libstdc++ 4.9.x detection for std::rethrow_if_nested() workaround 2017-03-01 16:38:22 +01:00
Max Kellermann
29a7b2c5b5 decoder/mpcdec: ignore empty frames
https://bugs.musicpd.org/view.php?id=4656 describes a crash due to
division by zero because frame.samples==0.  This should never happen,
but apparently can happen after seeking.  The best we can do is to
just ignore this frame.
2017-03-01 16:13:21 +01:00
Max Kellermann
3b6c285c2a configure.ac: prepare for 0.20.6 2017-03-01 16:13:21 +01:00
Max Kellermann
575d1786af release v0.20.5 2017-02-20 21:51:31 +01:00
Max Kellermann
bc1c927952 util/TimeFormat: suppress -Wunused on Windows 2017-02-20 21:44:01 +01:00
Max Kellermann
f95bc85f91 python/build/libs: upgrade FFmpeg to 3.2.4 2017-02-20 21:13:43 +01:00
Max Kellermann
4015195314 doc/user: instructions to compile the Windows binary 2017-02-20 21:06:43 +01:00
Max Kellermann
c3d883c6cb win32/build.py: default to x64 build 2017-02-20 21:06:43 +01:00
Max Kellermann
097e30321b win32/build.py: add option --32 2017-02-20 21:06:31 +01:00
Max Kellermann
b6ddeaacf2 win32/build.py: convert argument parser to loop
Allow multiple arguments.
2017-02-20 21:06:21 +01:00
Max Kellermann
b0c60ec124 win32/build.py: add variable "x64", assign host_arch later 2017-02-20 21:04:34 +01:00
Max Kellermann
f3b788703e tag/Handler: improve snprintf() return value check 2017-02-19 19:34:13 +01:00
Max Kellermann
4bb83781e8 output/httpd/IcyMetaDataServer: cast length to unsigned
Fixes another buffer overflow: if the stream has a very long title or
URL, resulting in a metadata string of more than 2 kB, icy_string[0]
is a negative value, which gets casted to size_t - ouch!

 https://bugs.musicpd.org/view.php?id=4652
2017-02-19 19:28:52 +01:00
Max Kellermann
a73195b7cc output/httpd/IcyMetaDataServer: pad the string with 15 spaces
Fixes a buffer overflow due to the bad formula rounding the buffer
size up.  At the same time, remove the "+1" from the meta_length
calculation, which takes the padding into account and at the same time
implements proper rounding.
2017-02-19 19:27:37 +01:00
Max Kellermann
1bd00b8a9a output/httpd/IcyMetaDataServer: remove the int cast
Why did this cast exist??
2017-02-19 19:27:37 +01:00
Max Kellermann
d84eaeafc5 doc/include/tags.xml: clarify that track/disc are decimal 2017-02-18 19:01:04 +01:00
Max Kellermann
20ae84bff9 {input,mixer}/alsa: cancel the DeferredMonitor in the destructor
Yet another potential crash bug fix.
2017-02-10 15:05:49 +01:00
Max Kellermann
7372c931b3 event/Loop: make IsInsideOrNull() available in the NDEBUG build
Fixes build breakage by commit 4e5271fcdf7; and this method does make
sense in non-debug builds.
2017-02-09 21:21:49 +01:00
Max Kellermann
29e1b6e465 mixer/alsa: reset the MultiSocketMonitor in the destructor
Fixes potential crash bug.
2017-02-09 21:13:19 +01:00
Max Kellermann
eda06993f8 event/MultiSocketMonitor: add method Reset() 2017-02-09 21:12:23 +01:00
Max Kellermann
4b30ef1cf2 event/MultiSocketMonitor: use C++11 initializer 2017-02-09 21:12:23 +01:00
Max Kellermann
e92e5e8eb8 event/MultiSocketMonitor: more API documentation
Now ClearSocketList() may only be called from PrepareSockets().
Calling it before destroying the object doesn't work properly, because
it doesn't unregister the TimeoutMonitor and the IdleMonitor.  Some of
its callers need to be fixed.
2017-02-09 21:12:23 +01:00
Max Kellermann
4e5271fcdf event/Call: allow usage during shutdown
Change EventLoop::IsInside() call to EventLoop::IsInsideOrNull().
This means that BlockingCall() may be used during shutdown, after the
main EventLoop::Run() has finished.  This is important because mixers
are currently registered in the main EventLoop.
2017-02-09 21:12:23 +01:00
Max Kellermann
3c55487a16 configure.ac: don't require libsidutils when building with libsidplayfp
The libsidplayfp fork has merged libsidutils into the main library.
The libsidutils we used to link with was part of the original
libsidplay project.
2017-02-09 13:09:03 +01:00
Max Kellermann
76a1cae5d8 {input,mixer}/alsa: fix off-by-one bug in count check
Doesn't make a practical difference - but it's more correct this way.
2017-02-09 12:46:49 +01:00
Max Kellermann
81a97315e3 NEWS: mention ID3 memory leak fix 2017-02-08 08:44:47 +01:00
Max Kellermann
53c14d97a6 lib/nfs/FileReader: remove debug line 2017-02-08 08:43:56 +01:00
Max Kellermann
69a82eec17 tag/TagId3: use AtScopeExit() for exception-safety 2017-02-06 23:32:07 +01:00
Max Kellermann
45cadef22f configure.ac: prepare for 0.20.5 2017-02-06 23:28:36 +01:00
Max Kellermann
0a033fb10a release v0.20.4 2017-02-01 21:59:36 +01:00
Max Kellermann
591afa0647 lib/nfs/Connection: detect socket hangup and unregister from epoll
Fixes race condition when epoll_ctl() gets called after the socket has
been closed, which may affect a different socket created by another
thread meanwhile.
2017-02-01 21:44:20 +01:00
Max Kellermann
05eac20ffe lib/nfs/Connection: detect libnfs reconnect
When rpc_reconnect_requeue() gets called from inside nfs_service(),
the NfsInputStream can stall completely because the old socket has
been unregistered from epoll automatically, but the new one has never
been registered.  Therefore, nfs_service() will never be called again.

This kludge attempts to detect this condition by checking
nfs_which_events()==POLLOUT.

https://bugs.musicpd.org/view.php?id=4081
2017-02-01 21:36:58 +01:00
Max Kellermann
38d263ac19 output/sndio: work around a libroar C++ incompatibility
Same as in commit e02d8ad8d2, but this time for the sndio plugin
which can be emulated by libroar.
2017-02-01 19:53:23 +01:00
Thomas Zander
f71c204eef Correct method types to match Interface.hxx 2017-01-31 21:22:02 +01:00
Thomas Zander
51147203be free() require cstdlib to be included 2017-01-31 21:21:37 +01:00
Max Kellermann
a931686317 pcm/SampleFormat: workaround for GCC 4.9 "constexpr" bug
GCC 4.9 has incomplete C++14 support.  Specifically, it doesn't allow
switch/case in "constexpr" functions.
2017-01-27 11:02:58 +01:00
Max Kellermann
5bd322bdcf python/libs: upgrade Opus to 1.1.4 2017-01-27 08:47:58 +01:00
Max Kellermann
bb097109f0 configure.ac: prepare for 0.20.4 2017-01-27 08:47:36 +01:00
38 changed files with 369 additions and 95 deletions

28
NEWS

@@ -1,3 +1,31 @@
ver 0.20.6 (2017/03/10)
* input
- curl: fix headers after HTTP redirect to Shoutcast server
* decoder
- ffmpeg: re-enable as fallback
- mpcdec: fix crash (division by zero) after seeking
- sidplay: make compatible with libsidplayfp < 1.8
* fix stream tags after automatic song change
* workaround for GCC 4.9.4 / libstdc++ bug (build failure)
ver 0.20.5 (2017/02/20)
* tags
- id3: fix memory leak on corrupt ID3 tags
* decoder
- sidplay: don't require libsidutils when building with libsidplayfp
* output
- httpd: fix two buffer overflows in IcyMetaData length calculation
* mixer
- alsa: fix crash bug
ver 0.20.4 (2017/02/01)
* input
- nfs: fix freeze after reconnect
* output
- sndio: work around a libroar C++ incompatibility
* workaround for GCC 4.9 "constexpr" bug
* fix FreeBSD build failure
ver 0.20.3 (2017/01/25)
* protocol
- "playlistadd" creates new playlist if it does not exist, as documented

@@ -87,9 +87,14 @@ class AndroidNdkToolchain:
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
self.is_windows = False
libstdcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/gnu-libstdc++', gcc_version)
libstdcxx_cppflags = '-isystem ' + os.path.join(libstdcxx_path, 'include') + ' -isystem ' + os.path.join(libstdcxx_path, 'libs', android_abi, 'include')
libstdcxx_ldadd = os.path.join(libstdcxx_path, 'libs', android_abi, 'libgnustl_static.a')
libcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/llvm-libc++')
libcxx_libs_path = os.path.join(libcxx_path, 'libs', android_abi)
libstdcxx_cppflags = '-nostdinc++ -isystem ' + os.path.join(libcxx_path, 'include') + ' -isystem ' + os.path.join(ndk_path, 'sources/android/support/include')
libstdcxx_ldadd = os.path.join(libcxx_libs_path, 'libc++_static.a') + ' ' + os.path.join(libcxx_libs_path, 'libc++abi.a')
if self.is_armv7:
libstdcxx_ldadd += ' ' + os.path.join(libcxx_libs_path, 'libunwind.a')
if use_cxx:
self.libs += ' ' + libstdcxx_ldadd

@@ -1,10 +1,10 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.20.3, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.20.6, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
VERSION_MINOR=20
VERSION_REVISION=3
VERSION_REVISION=6
VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx])
@@ -992,7 +992,7 @@ AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enab
dnl --------------------------------- sidplay ---------------------------------
if test x$enable_sidplay != xno; then
dnl Check for libsidplayfp first
PKG_CHECK_MODULES([SIDPLAY], [libsidplayfp libsidutils],
PKG_CHECK_MODULES([SIDPLAY], [libsidplayfp],
[found_sidplayfp=yes],
[found_sidplayfp=no])
found_sidplay=$found_sidplayfp

@@ -55,7 +55,8 @@
<listitem>
<para>
<varname>track</varname>: the track number within the album.
<varname>track</varname>: the decimal track number within the
album.
</para>
</listitem>
@@ -103,7 +104,8 @@
<listitem>
<para>
<varname>disc</varname>: the disc number in a multi-disc album.
<varname>disc</varname>: the decimal disc number in a multi-disc
album.
</para>
</listitem>

@@ -135,6 +135,91 @@ apt-get install g++ \
</para>
<programlisting>make install</programlisting>
<section id="windows_build">
<title>Compiling for Windows</title>
<para>
Even though it does not "feel" like a Windows application,
<application>MPD</application> works well under Windows.
Its build process follows the "Linux style", and may seem
awkward for Windows people (who are not used to compiling
their software, anyway).
</para>
<para>
Basically, there are three ways to compile
<application>MPD</application> for Windows:
</para>
<orderedlist>
<listitem>
<para>
Build on Windows for Windows. All you need to do is
described above already: configure and make.
</para>
<para>
For Windows users, this is kind of unusual, because few
Windows users have a GNU toolchain and a UNIX shell
installed.
</para>
</listitem>
<listitem>
<para>
Build on Linux for Windows. This is described above
already: configure and make. You need the <ulink
url="https://mingw-w64.org/"><application>mingw-w64</application>
cross compiler</ulink>. Pass
<parameter>--host=i686-w64-mingw32</parameter> (32 bit)
or <parameter>--host=x86_64-w64-mingw32</parameter> (64
bit) to configure.
</para>
<para>
This is somewhat natural for Linux users. Many
distributions have <application>mingw-w64</application>
packages. The remaining difficulty here is installing
all the external libraries. And
<application>MPD</application> usually needs many,
making this method cumbersome for the casual user.
</para>
</listitem>
<listitem>
<para>
Build on Linux for Windows using the
<application>MPD</application>'s library build script.
</para>
</listitem>
</orderedlist>
<para>
This section is about the latter.
</para>
<para>
Just like with the native build, unpack the
<application>MPD</application> source tarball and change
into the directory. Then, instead of
<command>./configure</command>, type:
</para>
<programlisting>./win32/build.py --64</programlisting>
<para>
This downloads various library sources, and then configures
and builds <application>MPD</application> (for x64; to build
a 32 bit binary, pass <parameter>--32</parameter>). The
resulting EXE files is linked statically, i.e. it contains
all the libraries already, and you do not need carry DLLs
around. It is large, but easy to use. If you wish to have
a small <filename>mpd.exe</filename> with DLLs, you need to
compile manually, without the <filename>build.py</filename>
script.
</para>
</section>
</section>
<section id="systemd_socket">

@@ -19,8 +19,8 @@ libvorbis = AutotoolsProject(
)
opus = AutotoolsProject(
'http://downloads.xiph.org/releases/opus/opus-1.1.3.tar.gz',
'32bbb6b557fe1b6066adc0ae1f08b629',
'http://downloads.xiph.org/releases/opus/opus-1.1.4.tar.gz',
'9122b6b380081dd2665189f97bfd777f04f92dc3ab6698eea1dbb27ad59d8692',
'lib/libopus.a',
['--disable-shared', '--enable-static'],
)
@@ -58,8 +58,8 @@ libmad = AutotoolsProject(
)
ffmpeg = FfmpegProject(
'http://ffmpeg.org/releases/ffmpeg-3.2.2.tar.xz',
'3f01bd1fe1a17a277f8c84869e5d9192b4b978cb660872aa2b54c3cc8a2fedfc',
'http://ffmpeg.org/releases/ffmpeg-3.2.4.tar.xz',
'6e38ff14f080c98b58cf5967573501b8cb586e3a173b591f3807d8f0660daf7a',
'lib/libavcodec.a',
[
'--disable-shared', '--enable-static',

@@ -141,9 +141,9 @@ Partition::OnMixerVolumeChanged(gcc_unused Mixer &mixer, gcc_unused int volume)
void
Partition::OnGlobalEvent(unsigned mask)
{
if ((mask & TAG_MODIFIED) != 0)
TagModified();
if ((mask & SYNC_WITH_PLAYER) != 0)
SyncWithPlayer();
if ((mask & TAG_MODIFIED) != 0)
TagModified();
}

@@ -29,6 +29,29 @@
#include <assert.h>
#define GLIBCXX_490 20140422
#define GLIBCXX_491 20140716
#define GLIBCXX_492 20141030
#define GLIBCXX_492_Debian_9 20141220
#define GLIBCXX_493 20150626
#define GLIBCXX_494 20160803
#define GLIBCXX_49X_NDK_r13b 20150123
/* the big mess attempts to detect whether we're compiling with
libstdc++ 4.9.x; __GLIBCXX__ is a date tag and cannot be used to
check the major version; and just checking the compiler version
isn't enough, because somebody could use an old libstdc++ with
clang - SIGH! */
#if GCC_OLDER_THAN(5,0) || (defined(__GLIBCXX__) && \
(__GLIBCXX__ == GLIBCXX_490 || __GLIBCXX__ == GLIBCXX_491 || \
__GLIBCXX__ == GLIBCXX_492 || \
__GLIBCXX__ == GLIBCXX_492_Debian_9 || \
__GLIBCXX__ == GLIBCXX_493 || \
__GLIBCXX__ == GLIBCXX_494 || \
__GLIBCXX__ == GLIBCXX_49X_NDK_r13b))
#define GLIBCXX_49X
#endif
gcc_const
static enum ack
ToAck(PlaylistResult result)
@@ -100,13 +123,13 @@ ToAck(std::exception_ptr ep)
return ACK_ERROR_SYSTEM;
} catch (const std::invalid_argument &e) {
return ACK_ERROR_ARG;
#if defined(__GLIBCXX__) && __GLIBCXX__ < 20151204
#ifdef GLIBCXX_49X
} catch (const std::exception &e) {
#else
} catch (...) {
#endif
try {
#if defined(__GLIBCXX__) && __GLIBCXX__ < 20151204
#ifdef GLIBCXX_49X
/* workaround for g++ 4.x: no overload for
rethrow_exception(exception_ptr) */
std::rethrow_if_nested(e);

@@ -62,7 +62,7 @@ Print(Response &r, TagType group, const TagCountMap &m)
}
}
static bool
static void
stats_visitor_song(SearchStats &stats, const LightSong &song)
{
stats.n_songs++;
@@ -70,8 +70,6 @@ stats_visitor_song(SearchStats &stats, const LightSong &song)
const auto duration = song.GetDuration();
if (!duration.IsNegative())
stats.total_duration += duration;
return true;
}
static bool
@@ -94,7 +92,7 @@ CollectGroupCounts(TagCountMap &map, TagType group, const Tag &tag)
return found;
}
static bool
static void
GroupCountVisitor(TagCountMap &map, TagType group, const LightSong &song)
{
assert(song.tag != nullptr);
@@ -103,8 +101,6 @@ GroupCountVisitor(TagCountMap &map, TagType group, const LightSong &song)
if (!CollectGroupCounts(map, group, tag) && group == TAG_ALBUM_ARTIST)
/* fall back to "Artist" if no "AlbumArtist" was found */
CollectGroupCounts(map, TAG_ARTIST, tag);
return true;
}
void

@@ -27,13 +27,12 @@
#include <functional>
static bool
static void
AddSong(const Storage &storage, const char *playlist_path_utf8,
const LightSong &song)
{
spl_append_song(playlist_path_utf8,
DatabaseDetachSong(storage, song));
return true;
}
void

@@ -49,16 +49,14 @@ PrintDirectoryURI(Response &r, bool base, const LightDirectory &directory)
ApplyBaseFlag(directory.GetPath(), base));
}
static bool
static void
PrintDirectoryBrief(Response &r, bool base, const LightDirectory &directory)
{
if (!directory.IsRoot())
PrintDirectoryURI(r, base, directory);
return true;
}
static bool
static void
PrintDirectoryFull(Response &r, bool base, const LightDirectory &directory)
{
if (!directory.IsRoot()) {
@@ -67,8 +65,6 @@ PrintDirectoryFull(Response &r, bool base, const LightDirectory &directory)
if (directory.mtime > 0)
time_print(r, "Last-Modified", directory.mtime);
}
return true;
}
static void
@@ -96,7 +92,7 @@ print_playlist_in_directory(Response &r, bool base,
directory->GetPath(), name_utf8);
}
static bool
static void
PrintSongBrief(Response &r, Partition &partition,
bool base, const LightSong &song)
{
@@ -106,11 +102,9 @@ PrintSongBrief(Response &r, Partition &partition,
/* this song file has an embedded CUE sheet */
print_playlist_in_directory(r, base,
song.directory, song.uri);
return true;
}
static bool
static void
PrintSongFull(Response &r, Partition &partition,
bool base, const LightSong &song)
{
@@ -120,21 +114,18 @@ PrintSongFull(Response &r, Partition &partition,
/* this song file has an embedded CUE sheet */
print_playlist_in_directory(r, base,
song.directory, song.uri);
return true;
}
static bool
static void
PrintPlaylistBrief(Response &r, bool base,
const PlaylistInfo &playlist,
const LightDirectory &directory)
{
print_playlist_in_directory(r, base,
&directory, playlist.name.c_str());
return true;
}
static bool
static void
PrintPlaylistFull(Response &r, bool base,
const PlaylistInfo &playlist,
const LightDirectory &directory)
@@ -144,8 +135,6 @@ PrintPlaylistFull(Response &r, bool base,
if (playlist.mtime > 0)
time_print(r, "Last-Modified", playlist.mtime);
return true;
}
void
@@ -191,15 +180,13 @@ db_selection_print(Response &r, Partition &partition,
0, std::numeric_limits<int>::max());
}
static bool
static void
PrintSongURIVisitor(Response &r, Partition &partition, const LightSong &song)
{
song_print_uri(r, partition, song);
return true;
}
static bool
static void
PrintUniqueTag(Response &r, TagType tag_type,
const Tag &tag)
{
@@ -211,8 +198,6 @@ PrintUniqueTag(Response &r, TagType tag_type,
if (item.type != tag_type)
r.Format("%s: %s\n",
tag_item_names[item.type], item.value);
return true;
}
void

@@ -27,14 +27,13 @@
#include <functional>
static bool
static void
AddToQueue(Partition &partition, const LightSong &song)
{
const Storage &storage = *partition.instance.storage;
partition.playlist.AppendSong(partition.pc,
DatabaseDetachSong(storage,
song));
return true;
}
void

@@ -67,15 +67,13 @@ StatsVisitTag(DatabaseStats &stats, StringSet &artists, StringSet &albums,
}
}
static bool
static void
StatsVisitSong(DatabaseStats &stats, StringSet &artists, StringSet &albums,
const LightSong &song)
{
++stats.song_count;
StatsVisitTag(stats, artists, albums, *song.tag);
return true;
}
DatabaseStats

@@ -25,6 +25,13 @@
#include "db/Interface.hxx"
#include "fs/Traits.hxx"
#ifdef _LIBCPP_VERSION
/* workaround for "error: incomplete type 'PlaylistInfo' used in type
trait expression" with libc++ version 3900 (from Android NDK
r13b) */
#include "db/PlaylistInfo.hxx"
#endif
#include <string>
struct PrefixedLightDirectory : LightDirectory {

@@ -235,7 +235,7 @@ decoder_run_stream_fallback(DecoderBridge &bridge, InputStream &is)
{
const struct DecoderPlugin *plugin;
#ifdef HAVE_FFMPEG
#ifdef ENABLE_FFMPEG
plugin = decoder_plugin_from_name("ffmpeg");
#else
plugin = decoder_plugin_from_name("mad");

@@ -207,6 +207,15 @@ mpcdec_decode(DecoderClient &client, InputStream &is)
if (frame.bits == -1)
break;
if (frame.samples <= 0) {
/* empty frame - this has been observed to
happen spuriously after seeking; skip this
obscure frame, and hope libmpcdec
recovers */
cmd = client.GetCommand();
continue;
}
mpc_uint32_t ret = frame.samples;
ret *= info.channels;

@@ -50,6 +50,10 @@
#include <string.h>
#include <stdio.h>
#ifdef HAVE_SIDPLAYFP
#define LIBSIDPLAYFP_VERSION GCC_MAKE_VERSION(LIBSIDPLAYFP_VERSION_MAJ, LIBSIDPLAYFP_VERSION_MIN, LIBSIDPLAYFP_VERSION_LEV)
#endif
#define SUBTUNE_PREFIX "tune_"
static constexpr Domain sidplay_domain("sidplay");
@@ -285,7 +289,11 @@ sidplay_file_decode(DecoderClient &client, Path path_fs)
#endif
#ifdef HAVE_SIDPLAYFP
#if LIBSIDPLAYFP_VERSION >= GCC_MAKE_VERSION(1,8,0)
const bool stereo = tune.getInfo()->sidChips() >= 2;
#else
const bool stereo = tune.getInfo()->isStereo();
#endif
#else
const bool stereo = tune.isStereo();
#endif

@@ -34,6 +34,8 @@
#include <stdexcept>
#include <memory>
#include <cstdlib>
#include <assert.h>
#define ERRORLEN 80

@@ -79,7 +79,7 @@ private:
void
BlockingCall(EventLoop &loop, std::function<void()> &&f)
{
if (loop.IsInside()) {
if (loop.IsInsideOrNull()) {
/* we're already inside the loop - we can simply call
the function */
f();

@@ -222,8 +222,9 @@ EventLoop::Run()
#ifndef NDEBUG
assert(busy);
assert(thread.IsInside());
thread = ThreadId::Null();
#endif
thread = ThreadId::Null();
}
void

@@ -212,12 +212,16 @@ public:
}
#endif
#ifndef NDEBUG
/**
* Like IsInside(), but also returns true if the thread has
* already ended (or was not started yet). This is useful for
* code which may run during startup or shutdown, when events
* are not yet/anymore handled.
*/
gcc_pure
bool IsInsideOrNull() const {
return thread.IsNull() || thread.IsInside();
}
#endif
};
#endif /* MAIN_NOTIFY_H */

@@ -28,12 +28,18 @@
#endif
MultiSocketMonitor::MultiSocketMonitor(EventLoop &_loop)
:IdleMonitor(_loop), TimeoutMonitor(_loop), ready(false) {
:IdleMonitor(_loop), TimeoutMonitor(_loop) {
}
MultiSocketMonitor::~MultiSocketMonitor()
void
MultiSocketMonitor::Reset()
{
// TODO
assert(GetEventLoop().IsInsideOrNull());
fds.clear();
IdleMonitor::Cancel();
TimeoutMonitor::Cancel();
ready = refresh = false;
}
void

@@ -96,7 +96,19 @@ class MultiSocketMonitor : IdleMonitor, TimeoutMonitor
friend class SingleFD;
bool ready, refresh;
/**
* DispatchSockets() should be called.
*/
bool ready = false;
/**
* PrepareSockets() should be called.
*
* Note that this doesn't need to be initialized by the
* constructor; this class is activated with the first
* InvalidateSockets() call, which initializes this flag.
*/
bool refresh;
std::forward_list<SingleFD> fds;
@@ -107,11 +119,26 @@ public:
static constexpr unsigned HANGUP = SocketMonitor::HANGUP;
MultiSocketMonitor(EventLoop &_loop);
~MultiSocketMonitor();
using IdleMonitor::GetEventLoop;
public:
/**
* Clear the socket list and disable all #EventLoop
* registrations. Run this in the #EventLoop thread before
* destroying this object.
*
* Later, this object can be reused and reactivated by calling
* InvalidateSockets().
*
* Note that this class doesn't have a destructor which calls
* this method, because this would be racy and thus pointless:
* at the time ~MultiSocketMonitor() is called, our virtual
* methods have been morphed to be pure again, and in the
* meantime the #EventLoop thread could invoke those pure
* methods.
*/
void Reset();
/**
* Invalidate the socket list. A call to PrepareSockets() is
* scheduled which will then update the list.
@@ -121,12 +148,19 @@ public:
IdleMonitor::Schedule();
}
/**
* Add one socket to the list of monitored sockets.
*
* May only be called from PrepareSockets().
*/
void AddSocket(int fd, unsigned events) {
fds.emplace_front(*this, fd, events);
}
/**
* Remove all sockets.
*
* May only be called from PrepareSockets().
*/
void ClearSocketList();
@@ -135,6 +169,8 @@ public:
* each one; its return value is the events bit mask. A
* return value of 0 means the socket will be removed from the
* list.
*
* May only be called from PrepareSockets().
*/
template<typename E>
void UpdateSocketList(E &&e) {
@@ -157,15 +193,26 @@ public:
/**
* Replace the socket list with the given file descriptors.
* The given pollfd array will be modified by this method.
*
* May only be called from PrepareSockets().
*/
void ReplaceSocketList(pollfd *pfds, unsigned n);
#endif
protected:
/**
* Override this method and update the socket registrations.
* To do that, call AddSocket(), ClearSocketList(),
* UpdateSocketList() and ReplaceSocketList().
*
* @return timeout or a negative value for no timeout
*/
virtual std::chrono::steady_clock::duration PrepareSockets() = 0;
/**
* At least one socket is ready or the timeout has expired.
* This method should be used to perform I/O.
*/
virtual void DispatchSockets() = 0;
private:

@@ -98,12 +98,9 @@ public:
}
~AlsaInputStream() {
/* ClearSocketList must be called from within the
IOThread; if we don't do it manually here, the
~MultiSocketMonitor() will do it in the current
thread */
BlockingCall(MultiSocketMonitor::GetEventLoop(), [this](){
ClearSocketList();
MultiSocketMonitor::Reset();
DeferredMonitor::Cancel();
});
snd_pcm_close(capture_handle);
@@ -181,7 +178,7 @@ AlsaInputStream::PrepareSockets()
}
int count = snd_pcm_poll_descriptors_count(capture_handle);
if (count < 0) {
if (count <= 0) {
ClearSocketList();
return std::chrono::steady_clock::duration(-1);
}

@@ -398,7 +398,12 @@ CurlInputStream::SeekInternal(offset_type new_offset)
/* send the "Range" header */
if (offset > 0) {
sprintf(range, "%lld-", (long long)offset);
#ifdef WIN32
// TODO: what can we use on Windows to format 64 bit?
sprintf(range, "%lu-", (long)offset);
#else
sprintf(range, "%llu-", (unsigned long long)offset);
#endif
request->SetOption(CURLOPT_RANGE, range);
}
}

@@ -168,13 +168,23 @@ CurlRequest::Done(CURLcode result)
}
}
gcc_pure
static bool
IsResponseBoundaryHeader(StringView s)
{
return s.size > 5 && (memcmp(s.data, "HTTP/", 5) == 0 ||
/* the proprietary "ICY 200 OK" is
emitted by Shoutcast */
memcmp(s.data, "ICY 2", 5) == 0);
}
inline void
CurlRequest::HeaderFunction(StringView s)
{
if (state > State::HEADERS)
return;
if (s.size > 5 && memcmp(s.data, "HTTP/", 5) == 0) {
if (IsResponseBoundaryHeader(s)) {
/* this is the boundary to a new response, for example
after a redirect */
headers.clear();

@@ -396,6 +396,17 @@ NfsConnection::ScheduleSocket()
assert(GetEventLoop().IsInside());
assert(context != nullptr);
const int which_events = nfs_which_events(context);
if (which_events == POLLOUT && SocketMonitor::IsDefined())
/* kludge: if libnfs asks only for POLLOUT, it means
that it is currently waiting for the connect() to
finish - rpc_reconnect_requeue() may have been
called from inside nfs_service(); we must now
unregister the old socket and register the new one
instead */
SocketMonitor::Steal();
if (!SocketMonitor::IsDefined()) {
int _fd = nfs_get_fd(context);
if (_fd < 0)
@@ -405,7 +416,8 @@ NfsConnection::ScheduleSocket()
SocketMonitor::Open(_fd);
}
SocketMonitor::Schedule(libnfs_to_events(nfs_which_events(context)));
SocketMonitor::Schedule(libnfs_to_events(which_events)
| SocketMonitor::HANGUP);
}
inline int
@@ -442,10 +454,14 @@ NfsConnection::OnSocketReady(unsigned flags)
bool closed = false;
const bool was_mounted = mount_finished;
if (!mount_finished)
if (!mount_finished || (flags & SocketMonitor::HANGUP) != 0)
/* until the mount is finished, the NFS client may use
various sockets, therefore we unregister and
re-register it each time */
/* also re-register the socket if we got a HANGUP,
which is a sure sign that libnfs will close the
socket, which can lead to a race condition if
epoll_ctl() is called later */
SocketMonitor::Steal();
const int result = Service(flags);

@@ -23,6 +23,7 @@
#include "output/OutputAPI.hxx"
#include "event/MultiSocketMonitor.hxx"
#include "event/DeferredMonitor.hxx"
#include "event/Call.hxx"
#include "util/ASCII.hxx"
#include "util/ReusableArray.hxx"
#include "util/Domain.hxx"
@@ -53,6 +54,13 @@ public:
DeferredMonitor::Schedule();
}
~AlsaMixerMonitor() {
BlockingCall(MultiSocketMonitor::GetEventLoop(), [this](){
MultiSocketMonitor::Reset();
DeferredMonitor::Cancel();
});
}
private:
virtual void RunDeferred() override {
InvalidateSockets();
@@ -102,7 +110,7 @@ AlsaMixerMonitor::PrepareSockets()
}
int count = snd_mixer_poll_descriptors_count(mixer);
if (count < 0)
if (count <= 0)
count = 0;
struct pollfd *pfds = pfd_buffer.Get(count);

@@ -24,8 +24,16 @@
#include "util/Domain.hxx"
#include "Log.hxx"
/* work around a C++ incompatibility if the sndio API is emulated by
libroar: libroar's "struct roar_service_stream" has a member named
"new", which is an illegal identifier in C++ */
#define new new_
#include <sndio.h>
/* undo the libroar workaround */
#undef new
#include <stdexcept>
#ifndef SIO_DEVANY

@@ -60,7 +60,11 @@ icy_server_metadata_string(const char *stream_title, const char* stream_url)
{
// The leading n is a placeholder for the length information
auto icy_metadata = FormatString("nStreamTitle='%s';"
"StreamUrl='%s';",
"StreamUrl='%s';"
/* pad 15 spaces just in case
the length needs to be
rounded up */
" ",
stream_title,
stream_url);
@@ -68,7 +72,7 @@ icy_server_metadata_string(const char *stream_title, const char* stream_url)
meta_length--; // subtract placeholder
meta_length = ((int)meta_length / 16) + 1;
meta_length = meta_length / 16;
icy_metadata[0] = meta_length;
@@ -109,5 +113,5 @@ icy_server_metadata_page(const Tag &tag, const TagType *types)
if (icy_string.IsNull())
return nullptr;
return Page::Copy(icy_string.c_str(), (icy_string[0] * 16) + 1);
return Page::Copy(icy_string.c_str(), uint8_t(icy_string[0]) * 16 + 1);
}

@@ -64,7 +64,10 @@ enum class SampleFormat : uint8_t {
/**
* Checks whether the sample format is valid.
*/
static constexpr inline bool
#if !GCC_OLDER_THAN(5,0)
constexpr
#endif
static inline bool
audio_valid_sample_format(SampleFormat format)
{
switch (format) {
@@ -83,7 +86,10 @@ audio_valid_sample_format(SampleFormat format)
return false;
}
static constexpr inline unsigned
#if !GCC_OLDER_THAN(5,0)
constexpr
#endif
static inline unsigned
sample_format_size(SampleFormat format)
{
switch (format) {

@@ -44,7 +44,7 @@ add_tag_tag(TagType type, const char *value, void *ctx)
unsigned n = strtoul(value, &end, 10);
if (value != end) {
char s[21];
if (snprintf(s, 21, "%u", n) >= 0)
if (snprintf(s, 21, "%u", n) > 0)
tag.AddItem(type, s);
}
} else

@@ -24,6 +24,7 @@
#include "TagTable.hxx"
#include "TagBuilder.hxx"
#include "util/Alloc.hxx"
#include "util/ScopeExit.hxx"
#include "util/StringUtil.hxx"
#include "Log.hxx"
@@ -78,11 +79,9 @@ import_id3_string(const id3_ucs4_t *ucs4)
if (gcc_unlikely(utf8 == nullptr))
return nullptr;
id3_utf8_t *utf8_stripped = (id3_utf8_t *)
xstrdup(Strip((char *)utf8));
free(utf8);
AtScopeExit(utf8) { free(utf8); };
return utf8_stripped;
return (id3_utf8_t *)xstrdup(Strip((char *)utf8));
}
/**
@@ -126,9 +125,10 @@ tag_id3_import_text_frame(const struct id3_frame *frame,
if (utf8 == nullptr)
continue;
AtScopeExit(utf8) { free(utf8); };
tag_handler_invoke_tag(handler, handler_ctx,
type, (const char *)utf8);
free(utf8);
}
}
@@ -177,8 +177,9 @@ tag_id3_import_comment_frame(const struct id3_frame *frame, TagType type,
if (utf8 == nullptr)
return;
AtScopeExit(utf8) { free(utf8); };
tag_handler_invoke_tag(handler, handler_ctx, type, (const char *)utf8);
free(utf8);
}
/**
@@ -236,22 +237,23 @@ tag_id3_import_musicbrainz(struct id3_tag *id3_tag,
if (name == nullptr)
continue;
AtScopeExit(name) { free(name); };
id3_utf8_t *value = tag_id3_getstring(frame, 2);
if (value == nullptr)
continue;
AtScopeExit(value) { free(value); };
tag_handler_invoke_pair(handler, handler_ctx,
(const char *)name,
(const char *)value);
TagType type = tag_id3_parse_txxx_name((const char*)name);
free(name);
if (type != TAG_NUM_OF_ITEM_TYPES)
tag_handler_invoke_tag(handler, handler_ctx,
type, (const char*)value);
free(value);
}
}

@@ -48,7 +48,7 @@ static size_t
AlignToPageSize(size_t size)
{
static const long page_size = sysconf(_SC_PAGESIZE);
if (page_size == 0)
if (page_size <= 0)
return size;
size_t ps(page_size);

@@ -56,7 +56,7 @@ public:
:buffer(std::exchange(src.buffer, nullptr)),
capacity(std::exchange(src.capacity, 0)) {}
ReusableArray &operator=(const ReusableArray &&src) {
ReusableArray &operator=(ReusableArray &&src) {
std::swap(buffer, src.buffer);
std::swap(capacity, src.capacity);
return *this;

@@ -60,6 +60,8 @@ ParseTimePoint(const char *s, const char *format)
#ifdef WIN32
/* TODO: emulate strptime()? */
(void)s;
(void)format;
throw std::runtime_error("Time parsing not implemented on Windows");
#else
struct tm tm;

@@ -36,6 +36,7 @@
#include <type_traits>
#include <utility>
#include <new>
#include <cstdlib>
/**
* Allocate and construct a variable-size object. That is useful for

@@ -5,11 +5,22 @@ import sys, subprocess
configure_args = sys.argv[1:]
host_arch = 'i686-w64-mingw32'
x64 = True
if len(configure_args) > 0 and configure_args[0] == '--64':
configure_args = configure_args[1:]
while len(configure_args) > 0:
arg = configure_args[0]
if arg == '--64':
x64 = True
elif arg == '--32':
x64 = False
else:
break
configure_args.pop(0)
if x64:
host_arch = 'x86_64-w64-mingw32'
else:
host_arch = 'i686-w64-mingw32'
# the path to the MPD sources
mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..'))