Compare commits

...

92 Commits

Author SHA1 Message Date
Max Kellermann
82e261ad33 release v0.19.12 2015-12-15 21:54:42 +01:00
Benno Fünfstück
cae2811762 fix mpd crash on invalid utf8 stream title 2015-12-15 21:49:53 +01:00
Ben Boeckel
09112c6869 docs: add vlc and mpv to the list of example applications
These are other popular clients. In particular, VLC is available on
mobile devices.

Signed-off-by: Ben Boeckel <mathstuf@gmail.com>
2015-12-15 21:30:26 +01:00
Christian Hesse
77aaf1baee fix LimitRTTIME in systemd unit file
systemd does not understand LimitRTTIME=-1. For no limit we have to use
the string 'infinity' (see systemd.exec(5)).

Signed-off-by: Christian Hesse <mail@eworm.de>
2015-12-15 21:17:04 +01:00
Jörg Krause
6626c2d00d Makefile.am: fix static build with alsa
Add ALSA_LIBS to MIXER_LIBS, otherwise building mpd in a static context fails
with lot of undefined references to alsa-lib (libasound) required by
src/mixer/plugins/AlsaMixerPlugin.cxx.

Signed-off-by: Jörg Krause <joerg.krause@embedded.rocks>
2015-12-15 21:16:45 +01:00
Michael Paquier
315f9d98f6 Main: fix build failure on non-Linux systems 2015-11-10 08:38:53 +01:00
Max Kellermann
f087518e7a configure.ac: prepare for 0.19.12 2015-11-10 08:33:50 +01:00
Max Kellermann
db9997a106 release v0.19.11 2015-10-27 10:42:20 +01:00
Max Kellermann
0cbfb610f2 systemd: remove obsolete ControlGroup settings
This systemd feature has been removed a while ago without replacement,
and it turns out that systemd developers suggest not using control
groups at all to assign real-time privileges.  Therfore, a replacement
feature will not be implement in future systemd releases, and we can
really remove those lines completely.

See http://bugs.musicpd.org/view.php?id=4413
2015-10-27 10:36:23 +01:00
Max Kellermann
f901cd042b doc/user: section about real-time scheduling 2015-10-27 10:31:50 +01:00
Max Kellermann
5719207dfa gme: don't loop forever, fall back to GME's default play length
Fixes http://bugs.musicpd.org/view.php?id=4432
2015-10-26 17:16:20 +01:00
Max Kellermann
a84fbbe327 decoder/gme: free the gme_info_t as early as possible 2015-10-26 17:15:24 +01:00
Max Kellermann
93c97972b9 decoder/gme: call decoder_seek_error() on seek error 2015-10-26 16:32:39 +01:00
Max Kellermann
ac61d43720 output/Command: flush the mixer cache when enabling/disabling output
Fixes mixer lag (http://bugs.musicpd.org/view.php?id=4425).
2015-10-26 16:29:07 +01:00
Max Kellermann
1958f78cc1 decoder/ffmpeg: fix crash due to wrong avio_alloc_context() call
Allocate the buffer dynamically using av_malloc(), and free
AVIOContext.buffer in the destructor, as mandated by the libavformat
documentation.

Fixes http://bugs.musicpd.org/view.php?id=4446
2015-10-26 13:06:29 +01:00
Max Kellermann
a7ee64a25b decoder/mpcdec: use SampleTraits<SampleFormat::S24_P32>
Eliminates some duplicate code, and as a side effect, this works
around clang 3.8 compiler warning because a negative value was
shifted.
2015-10-16 18:12:32 +02:00
Max Kellermann
2a58f22649 decoder/mpcdec: use Clamp() 2015-10-16 18:11:42 +02:00
Max Kellermann
f066bb7716 unix/Daemon, playlist/...: remove unused Domain variables 2015-10-16 18:08:59 +02:00
Max Kellermann
4e3d182189 encoder/flac: fix crash with 32 bit playback
Copy to encoder->audio_format *after* adjusting the sample format to
S24_P32.

Fixes http://bugs.musicpd.org/view.php?id=4433
2015-10-16 18:05:34 +02:00
Max Kellermann
205fba74cf tag/ApeLoader: fix buffer overflow after unterminated key 2015-10-16 14:55:40 +02:00
Max Kellermann
a9bcf8d50d configure.ac: prepare for 0.19.11 2015-10-16 14:55:40 +02:00
Max Kellermann
b0ff3bc7a3 release v0.19.10 2015-06-21 15:51:50 +02:00
Max Kellermann
06301e279c PlayerThread: start the decoder on PlayerCommand::QUEUE
Fixes missing SongBorder() call, which causes "single" mode breakage.
2015-06-21 15:38:48 +02:00
Max Kellermann
6d6f274648 DecoderAPI: discard unused song tag early
If there's a stream tag, don't let the song tag override it in the
next update_stream_tag() call.
2015-06-21 15:10:55 +02:00
Max Kellermann
9acefcb256 DecoderThread: set Decoder::song_tag only for local files
If the song tag comes from a stream, and MPD playback restarts, MPD
would believe the tag should override the newly received tag.  This
makes the previous tag appear stuck.  This change passes the song tag
only if it's authoritative - i.e. if it's a song file.
2015-06-21 15:02:14 +02:00
Max Kellermann
e4d0293a31 DecoderAPI: "move" the Tag object
Reduce runtime overhead.
2015-06-20 16:06:09 +02:00
Romain Rollet
ae77542a11 OutputControl: fix fail_timer check right after booting
Right after booting, the monotonic clock starts with a very small
value, and AudioOutput::LockUpdate() may believe that the fail_timer
has not recovered yet.
2015-06-20 15:37:19 +02:00
Max Kellermann
980187f856 system/PeriodClock: make IsDefined() "constexpr" 2015-06-20 15:33:17 +02:00
Max Kellermann
327a8e6c59 decoder/ffmpeg: skip unwanted samples after seeking
When seeking to the beginning of a packet, skip the samples that come
before the desired time stamp.
2015-06-20 15:01:05 +02:00
Max Kellermann
d11e2724c4 decoder/ffmpeg: use AVSEEK_FLAG_BACKWARD for seeking
Ask FFmpeg to seek to the next packet boundary *before* the seek
position, so we don't miss audio data.  Now we get too much, but we'll
solve that in the next commit.
2015-06-20 14:29:42 +02:00
Max Kellermann
f768ca3a2d decoder/ffmpeg: move code to StreamRelativePts() 2015-06-19 18:56:29 +02:00
Max Kellermann
947e902288 input/curl: trigger the condition variable in RequestDone()
Fixes deadlock on small responses.
2015-06-19 16:45:38 +02:00
Thomas Guillem
3436a646b5 storage/nfs: fix deadlock when connecting
The Connect method can be called between Schedule and lock. In that case, when
locked, the state is already set to CONNECTING of READY and the condition won't
be signaled anymore.
2015-05-29 22:39:14 +02:00
jai
aed0af1e00 input/smbclient: fix DFF playback 2015-05-29 22:37:49 +02:00
Michal Nazarewicz
0d7ee2b014 OpusEncoderPlugin: initialise granulepos so we end up with sane values
Not initialising granulepos leads to it having arbitrary values in the
encoded stream including possibly negative values which are not valid
and confuse opusdec.  Explicitly initialise opus_encoder::granulepos
to avoid that problem.
2015-03-25 18:02:54 +01:00
Max Kellermann
2f5fd91bd8 configure.ac: prepare for 0.19.10 2015-03-25 18:02:54 +01:00
Max Kellermann
5761800197 release v0.19.9 2015-02-06 17:08:25 +01:00
Max Kellermann
0eebacc521 Merge tag 'v0.18.23' into v0.19.x 2015-02-06 17:07:47 +01:00
Max Kellermann
4a5528697d release v0.18.23 2015-02-06 17:04:14 +01:00
Max Kellermann
d38034bb5c fs/io/FileOutputStream: don't auto-delete file on WIN32
The file handle is never reset to INVALID_HANDLE_VALUE, and thus the
destructor will assume the operation shall be cancelled and will
delete the temporary file.

This was a major breakage for saving the database file and the state
file.
2015-02-06 14:36:55 +01:00
Max Kellermann
b3fe3e8b3d TagBuilder: allow adding duplicate tag types in Complement()
Build a table of pre-existing tag types before adding new items.  The
old way would check HasType() each time, which would return true after
the first instance of that tag type had been added, preventing
duplicate tag types to be merged.

This broke duplicate tag types loaded from the state file, because
this code path uses TagBuilder::Complement().
2015-02-06 12:25:34 +01:00
Max Kellermann
5489dec28d NEWS: fix v0.18.22 release date 2015-02-01 12:22:24 +01:00
Max Kellermann
8a6b4db19f Makefile.am: move org_musicpd_Bridge.h to BUILT_SOURCES 2015-01-31 00:05:30 +01:00
Max Kellermann
df43b6a05c Makefile.am: generate icon before compiling JNI classes
Apparently, Android's build.xml requires the icon to be available,
even when only running the Java compiler.
2015-01-31 00:05:30 +01:00
Max Kellermann
3adca3c2fa db/update/Walk: use std::unique_ptr instead of std::auto_ptr
std::auto_ptr is deprecated, and std::unique_ptr is much better
anyway.
2015-01-29 08:37:23 +01:00
PHO
39abd3ecb4 Avoid integer overflow in MonotonicClock{S,MS,US}
This is Darwin specific: the previous implementation was causing an integer
overflow when base.numer is very large. On PPC Darwin, the timebase info is 1000000000/33330116 and this is too large for integer arithmetic.
2015-01-29 08:34:37 +01:00
PHO
a4f4fc50b9 Avoid integer overflow in MonotonicClock{S,MS,US}
This is Darwin specific: the previous implementation was causing an integer
overflow when base.numer is very large. On PPC Darwin, the timebase info is 1000000000/33330116 and this is too large for integer arithmetic.
2015-01-29 08:33:48 +01:00
Max Kellermann
7bf638b0de decoder/DsdLib: use new[] to allocate the ID3 buffer
Don't abort the process if there's not enough memory.  This buffer is
not important and can be large.
2015-01-29 08:24:34 +01:00
Max Kellermann
56662a703c decoder/DsdLib: free ID3 buffer right after id3_tag_parse()
Merge two free() calls.
2015-01-29 08:24:34 +01:00
Max Kellermann
8b5f47d3a3 decoder/DsdLib: raise ID3 tag limit to 1 MB
A bug report was submitted with a 600 kB ID3 tag that could not be
read by MPD.
2015-01-29 08:20:14 +01:00
Max Kellermann
a289dcb9ee Merge branch 'v0.18.x' into v0.19.x 2015-01-26 20:48:19 +01:00
PHO
023b9c1e7e Test the existence of strndup(3) before using it.
This can eliminate the ad-hoc "#ifdef WIN32" and can also support other platforms lacking it as well (including Darwin 9).
2015-01-26 20:39:49 +01:00
Max Kellermann
4c61662644 test/read_mixer: add missing stdlib.h include 2015-01-26 20:39:49 +01:00
Max Kellermann
ad1b6ef0ac {playlist,input}/despotify: remove defunct plugin 2015-01-26 09:55:31 +01:00
Max Kellermann
ed5c6be2f1 util/list: disable gcc5 warning
This file has been removed in newer MPD versions, so don't care about
it now.
2015-01-23 16:50:31 +01:00
Max Kellermann
30cb082932 ClientProcess: cast enum to int before passing to printf()
Fixes gcc5 warning.
2015-01-23 16:50:31 +01:00
Max Kellermann
645554d12f configure.ac: prepare for 0.18.23 2015-01-23 16:47:13 +01:00
Max Kellermann
212b0faf0c android/build.py: use os.path.abspath() to build mpd_path
Fixes a bug that occurs when runing "build.py" from inside the
"android" directory.
2015-01-22 18:51:53 +01:00
Max Kellermann
276a0d9500 thread/Name: include stdio.h if HAVE_PRCTL
Caused a build failure with uClibc because snprintf() was not
available.
2015-01-21 20:40:56 +01:00
Max Kellermann
384b6c8288 doc/protocol: "playlistsearch" is case insensitive 2015-01-16 17:27:19 +01:00
Max Kellermann
a2af158fd3 configure.ac: prepare for 0.19.9 2015-01-16 17:26:50 +01:00
Max Kellermann
f33d2fb2e7 release v0.19.8 2015-01-14 23:12:24 +01:00
Max Kellermann
a9eec35aff Merge tag 'v0.18.22' into v0.19.x 2015-01-14 23:12:08 +01:00
Max Kellermann
8534f2d1e2 release v0.18.22 2015-01-14 23:04:49 +01:00
Max Kellermann
00740fb23b android/build.py: prepend "./" to "configure" if path is empty
Fixes in-tree build when the script is called as "android/build.py"
and not "./android/build.py".
2015-01-09 16:51:52 +01:00
Max Kellermann
37e9010887 input/async: reset the "open" flag after seeking successfully
Fixes a problem with the "curl" input plugin: IsEOF() always returns
true because the "open" flag was cleared by
CurlInputStream::RequestDone() when end-of-stream was reached.  This
flag stays false even when seeking to another position has succeeded.

This patch resets the "open" flag to true after seeking successfully.
2015-01-06 12:46:28 +01:00
Max Kellermann
4bd2c75056 thread/Name: disable pthread_setname_np() on NetBSD
NetBSD's pthread_setname_np() prototype is incompatible with the rest
of the world, and it requires to pass the string argument as a
non-const pointer.  Instead of working around this misdesign, I hereby
disable the feature on NetBSD.
2015-01-06 12:08:36 +01:00
Max Kellermann
b9ed850b98 thread/Name: enable FormatThreadName() with prctl()
Add macro HAVE_THREAD_NAME which is set when any method to set the
thread name is available.  Use that macro in FormatThreadName()
instead of just checking for HAVE_PTHREAD_SETNAME_NP.
2015-01-06 12:04:30 +01:00
Max Kellermann
11cea17496 thread/Name: indent preprocessor commands 2015-01-06 12:04:15 +01:00
Max Kellermann
163597ef69 db/simple: fix implicit nullptr/bool conversion
Return false on error, not nullptr.
2014-12-26 14:34:03 +01:00
Max Kellermann
95f84afd33 fs/Traits, ...: work around -Wtautological-pointer-compare
New in clang 3.6.
2014-12-26 14:34:03 +01:00
Max Kellermann
9f7fd1fbfb db/lazy, input/mms: add "override" keywords
Fixes -Winconsistent-missing-override (clang 3.6).
2014-12-26 14:29:29 +01:00
Max Kellermann
940cab8620 Merge branch 'v0.18.x' into v0.19.x 2014-12-26 14:28:52 +01:00
Max Kellermann
5b84c99d79 doc/user: remove autoconf/automake from Debian build dependencies 2014-12-26 14:00:50 +01:00
Max Kellermann
b295024574 doc/user: add more Debian build dependencies 2014-12-26 13:56:26 +01:00
Max Kellermann
34180f1745 doc/user: add libicu-dev to Debian build dependencies 2014-12-26 13:54:33 +01:00
Max Kellermann
665031467a db/proxy, output/shout: fix implicit nullptr/bool conversion
Return false on error, not nullptr.
2014-12-26 13:50:54 +01:00
Max Kellermann
df33171107 db/{simple,proxy}, ...: add "override" keywords
Fixes -Winconsistent-missing-override (clang 3.6).
2014-12-26 13:47:04 +01:00
Max Kellermann
53f4044890 util/{ASCII,UriUtil}, ...: work around -Wtautological-pointer-compare
New in clang 3.6.
2014-12-26 13:43:32 +01:00
Max Kellermann
a5049136ff DatabaseGlue: convert nullptr check to assertion 2014-12-26 13:43:32 +01:00
Max Kellermann
705b3c6b63 util/ASCII: fix indent 2014-12-26 13:37:38 +01:00
Max Kellermann
6b4ac66962 Compiler.h: add macro CLANG_CHECK_VERSION() 2014-12-26 13:31:03 +01:00
Max Kellermann
0964b06240 Compiler.h: add macro GCC_OLDER_THAN() 2014-12-26 13:30:44 +01:00
Max Kellermann
92eeca3ba7 util/Manual: reimplement GCC_CHECK_VERSION() using GCC_MAKE_VERSION() 2014-12-26 13:30:22 +01:00
Max Kellermann
2a86554ac4 Compiler.h: add macro GCC_MAKE_VERSION() 2014-12-26 13:30:11 +01:00
Max Kellermann
805caa30ce configure.ac: prepare for 0.18.22 2014-12-26 13:23:04 +01:00
Max Kellermann
a56949e9fa decoder/ffmpeg: support interleaved floating point 2014-12-23 20:51:08 +01:00
Max Kellermann
43da4c0eca input/mms: limit the mmsx_read() size 2014-12-23 20:34:45 +01:00
Max Kellermann
b9c7771830 decoder/DsdLib: add missing stdlib.h include 2014-12-23 10:08:46 +01:00
Jan Brittenson
35db88affe DSF ID3 tags hitting 4k size limit
Here's a change to dynamically allocate the DSD ID3 tag buffer.
Pretty much anything with cover art is going to exceed the existing,
static 4k limit...  Here's a change to dynamically allocate the buffer
and sanity check it at some upper limit.  I rather arbitrarily pulled
256k out of thin air just to keep a corrupt file from causing it to
trying to allocate a buffer larger than available memory.
2014-12-23 09:49:33 +01:00
Max Kellermann
e38faca455 configure.ac: prepare for 0.19.8 2014-12-23 09:48:31 +01:00
Max Kellermann
0255e8710c android: release v0.19.7 2014-12-23 09:42:52 +01:00
77 changed files with 513 additions and 1030 deletions

@@ -116,9 +116,6 @@ For WavPack playback.
libadplug - http://adplug.sourceforge.net/
For AdLib playback.
despotify - https://github.com/SimonKagstrom/despotify
For Spotify playback.
Optional Miscellaneous Dependencies
-----------------------------------

@@ -28,8 +28,6 @@ noinst_LIBRARIES = \
libmixer_plugins.a \
liboutput_plugins.a
libmpd_a_DEPENDENCIES =
libmpd_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(LIBMPDCLIENT_CFLAGS) \
$(AVAHI_CFLAGS) \
@@ -283,13 +281,13 @@ android/build/build.xml: android/AndroidManifest.xml
ln -s $(abs_srcdir)/android/res/values android/build/res
$(ANDROID_SDK)/tools/android update project --path android/build --target android-17
android/build/bin/classes/org/musicpd/Bridge.class: android/src/Bridge.java android/build/build.xml
android/build/bin/classes/org/musicpd/Bridge.class: android/src/Bridge.java android/build/build.xml android/build/res/drawable/icon.png
cd android/build && ant compile-jni-classes
android/build/include/org_musicpd_Bridge.h: android/build/bin/classes/org/musicpd/Bridge.class
javah -classpath $(ANDROID_SDK)/platforms/android-17/android.jar:android/build/bin/classes -d $(@D) org.musicpd.Bridge
libmpd_a_DEPENDENCIES += android/build/include/org_musicpd_Bridge.h
BUILT_SOURCES = android/build/include/org_musicpd_Bridge.h
android/build/libs/armeabi-v7a/libmpd.so: libmpd.so android/build/build.xml
mkdir -p $(@D)
@@ -1125,7 +1123,6 @@ libinput_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(NFS_CFLAGS) \
$(CDIO_PARANOIA_CFLAGS) \
$(FFMPEG_CFLAGS) \
$(DESPOTIFY_CFLAGS) \
$(MMS_CFLAGS)
INPUT_LIBS = \
@@ -1135,7 +1132,6 @@ INPUT_LIBS = \
$(NFS_LIBS) \
$(CDIO_PARANOIA_LIBS) \
$(FFMPEG_LIBS2) \
$(DESPOTIFY_LIBS) \
$(MMS_LIBS)
if HAVE_ALSA
@@ -1181,15 +1177,6 @@ libinput_a_SOURCES += \
src/input/plugins/MmsInputPlugin.cxx src/input/plugins/MmsInputPlugin.hxx
endif
if ENABLE_DESPOTIFY
libinput_a_SOURCES += \
src/lib/despotify/DespotifyUtils.cxx \
src/lib/despotify/DespotifyUtils.hxx \
src/input/plugins/DespotifyInputPlugin.cxx \
src/input/plugins/DespotifyInputPlugin.hxx
endif
liboutput_plugins_a_CPPFLAGS = $(AM_CPPFLAGS) \
$(AO_CFLAGS) \
$(ALSA_CFLAGS) \
@@ -1232,6 +1219,7 @@ liboutput_plugins_a_SOURCES = \
MIXER_LIBS = \
libmixer_plugins.a \
$(ALSA_LIBS) \
$(PULSE_LIBS)
MIXER_API_SRC = \
@@ -1394,14 +1382,6 @@ PLAYLIST_LIBS = \
$(EXPAT_LIBS) \
$(FLAC_LIBS)
if ENABLE_DESPOTIFY
libplaylist_plugins_a_SOURCES += \
src/lib/despotify/DespotifyUtils.cxx \
src/lib/despotify/DespotifyUtils.hxx \
src/playlist/plugins/DespotifyPlaylistPlugin.cxx \
src/playlist/plugins/DespotifyPlaylistPlugin.hxx
endif
if ENABLE_SOUNDCLOUD
libplaylist_plugins_a_SOURCES += \
src/playlist/plugins/SoundCloudPlaylistPlugin.cxx \
@@ -1638,12 +1618,6 @@ if HAVE_LIBUPNP
test_run_neighbor_explorer_SOURCES += src/lib/expat/ExpatParser.cxx
endif
if ENABLE_DESPOTIFY
test_run_neighbor_explorer_SOURCES += \
src/lib/despotify/DespotifyUtils.cxx \
src/lib/despotify/DespotifyUtils.hxx
endif
endif
if ENABLE_ARCHIVE

61
NEWS

@@ -1,3 +1,56 @@
ver 0.19.12 (2015/12/15)
* fix assertion failure on malformed UTF-8 tag
* fix build failure on non-Linux systems
* fix LimitRTTIME in systemd unit file
ver 0.19.11 (2015/10/27)
* tags
- ape: fix buffer overflow
* decoder
- ffmpeg: fix crash due to wrong avio_alloc_context() call
- gme: don't loop forever, fall back to GME's default play length
* encoder
- flac: fix crash with 32 bit playback
* mixer
- fix mixer lag after enabling/disabling output
ver 0.19.10 (2015/06/21)
* input
- curl: fix deadlock on small responses
- smbclient: fix DFF playback
* decoder
- ffmpeg: improve seeking accuracy
- fix stuck stream tags
* encoder
- opus: fix bogus granulepos
* output
- fix failure to open device right after booting
* neighbor
- nfs: fix deadlock when connecting
* fix "single" mode breakage due to queue edits
ver 0.19.9 (2015/02/06)
* decoder
- dsdiff, dsf: raise ID3 tag limit to 1 MB
* playlist: fix loading duplicate tag types from state file
* despotify: remove defunct plugin
* fix clock integer overflow on OS X
* fix gcc 5.0 warnings
* fix build failure with uClibc
* fix build failure on non-POSIX operating systems
* fix dependency issue on parallel Android build
* fix database/state file saving on Windows
ver 0.19.8 (2015/01/14)
* input
- curl: fix bug after rewinding from end-of-file
- mms: reduce delay at the beginning of playback
* decoder
- dsdiff, dsf: allow ID3 tags larger than 4 kB
- ffmpeg: support interleaved floating point
* fix clang 3.6 warnings
* fix build failure on NetBSD
ver 0.19.7 (2014/12/17)
* input
- nfs: fix crash while canceling a failing file open operation
@@ -161,6 +214,14 @@ ver 0.19 (2014/10/10)
* install systemd unit for socket activation
* Android port
ver 0.18.23 (2015/02/06)
* despotify: remove defunct plugin
* fix clock integer overflow on OS X
* fix gcc 5.0 warnings
ver 0.18.22 (2015/01/14)
* fix clang 3.6 warnings
ver 0.18.21 (2014/12/17)
* playlist
- embcue: fix filename suffix detection

@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd"
android:installLocation="auto"
android:versionCode="10"
android:versionName="0.19.6">
android:versionCode="13"
android:versionName="0.19.9">
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17"/>

@@ -23,7 +23,7 @@ if not os.path.isdir(ndk_path):
sys.exit(1)
# the path to the MPD sources
mpd_path = os.path.dirname(os.path.dirname(sys.argv[0]))
mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..'))
# output directories
lib_path = os.path.abspath('lib')

@@ -1,10 +1,10 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.19.7, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.19.12, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0
VERSION_MINOR=19
VERSION_REVISION=7
VERSION_REVISION=12
VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx])
@@ -206,6 +206,7 @@ if test x$host_is_linux = xyes; then
fi
AC_CHECK_FUNCS(getpwnam_r getpwuid_r)
AC_CHECK_FUNCS(strndup)
if test x$host_is_linux = xyes; then
MPD_OPTIONAL_FUNC(eventfd, eventfd, USE_EVENTFD)
@@ -445,11 +446,6 @@ MPD_DEPENDS([enable_jack], [enable_glib],
AC_SYS_LARGEFILE
AC_ARG_ENABLE(despotify,
AS_HELP_STRING([--enable-despotify],
[enable support for despotify (default: disable)]),,
[enable_despotify=no])
AC_ARG_ENABLE(soundcloud,
AS_HELP_STRING([--enable-soundcloud],
[enable support for soundcloud.com]),,
@@ -979,14 +975,6 @@ if test x$enable_nfs = xyes; then
fi
AM_CONDITIONAL(ENABLE_NFS, test x$enable_nfs = xyes)
dnl --------------------------------- Despotify ---------------------------------
MPD_AUTO_PKG(despotify, DESPOTIFY, [despotify],
[Despotify support], [despotify not found])
if test x$enable_despotify = xyes; then
AC_DEFINE(ENABLE_DESPOTIFY, 1, [Define when despotify is enabled])
fi
AM_CONDITIONAL(ENABLE_DESPOTIFY, test x$enable_despotify = xyes)
dnl --------------------------------- Soundcloud ------------------------------
if test x$enable_soundcloud != xno; then
PKG_CHECK_MODULES([YAJL], [yajl >= 2.0],
@@ -1903,7 +1891,6 @@ printf '\nStreaming support:\n\t'
results(cdio_paranoia, [CDIO_PARANOIA])
results(curl,[CURL])
results(smbclient,[SMBCLIENT])
results(despotify,[Despotify])
results(soundcloud,[Soundcloud])
printf '\n\t'
results(mms,[MMS])

@@ -195,16 +195,6 @@ of database.
Limit the depth of the directories being watched, 0 means only watch
the music directory itself. There is no limit by default.
.TP
.B despotify_user <name>
This specifies the user to use when logging in to Spotify using the despotify plugins.
.TP
.B despotify_password <name>
This specifies the password to use when logging in to Spotify using the despotify plugins.
.TP
.B despotify_high_bitrate <yes or no>
This specifies if the requested bitrate for Spotify should be high or not. Higher sounds
better but requires more processing and higher bandwidth. Default is yes.
.TP
.SH REQUIRED AUDIO OUTPUT PARAMETERS
.TP
.B type <type>

@@ -1141,7 +1141,7 @@ OK
</term>
<listitem>
<para>
Searches case-sensitively for partial matches in the
Searches case-insensitively for partial matches in the
current playlist.
</para>
</listitem>

@@ -89,7 +89,7 @@ cd mpd-version</programlisting>
</para>
<programlisting>
apt-get install g++ automake autoconf \
apt-get install g++ \
libmad0-dev libmpg123-dev libid3tag0-dev \
libflac-dev libvorbis-dev libopus-dev \
libadplug-dev libaudiofile-dev libsndfile1-dev libfaad-dev \
@@ -98,19 +98,21 @@ apt-get install g++ automake autoconf \
libsidplay2-dev libsidutils-dev libresid-builder-dev \
libavcodec-dev libavformat-dev \
libmp3lame-dev \
libsamplerate0-dev \
libsamplerate0-dev libsoxr-dev \
libbz2-dev libcdio-paranoia-dev libiso9660-dev libmms-dev \
libzzip-dev \
libcurl4-gnutls-dev libyajl-dev \
libcurl4-gnutls-dev libyajl-dev libexpat-dev \
libasound2-dev libao-dev libjack-jackd2-dev libopenal-dev \
libpulse-dev libroar-dev libshout3-dev \
libmpdclient-dev \
libnfs-dev libsmbclient-dev \
libupnp-dev \
libavahi-client-dev \
libsqlite3-dev \
libsystemd-daemon-dev libwrap0-dev \
libcppunit-dev xmlto \
libboost-dev \
libglib2.0-dev
libglib2.0-dev libicu-dev
</programlisting>
<para>
@@ -313,9 +315,8 @@ systemctl start mpd.socket</programlisting>
</para>
<programlisting>input {
plugin "despotify"
user "foo"
password "bar"
plugin "curl"
proxy "proxy.local"
}
</programlisting>
@@ -1189,6 +1190,58 @@ database {
plugin).
</para>
</section>
<section id="realtime">
<title>Real-Time Scheduling</title>
<para>
On Linux, <application>MPD</application> attempts to configure
<ulink
url="https://en.wikipedia.org/wiki/Real-time_computing">real-time
scheduling</ulink> for some threads that benefit from it.
</para>
<para>
This is only possible you allow <application>MPD</application>
to do it. This privilege is controlled by
<varname>RLIMIT_RTPRIO</varname>
<varname>RLIMIT_RTTIME</varname>. You can configure this
privilege with <command>ulimit</command> before launching
<application>MPD</application>:
</para>
<programlisting>ulimit -HS -r 50; mpd</programlisting>
<para>
Or you can use the <command>prlimit</command> program from the
<application>util-linux</application> package:
</para>
<programlisting>prlimit --rtprio=50 --rttime=unlimited mpd</programlisting>
<para>
The <application>systemd</application> service file shipped
with <application>MPD</application> comes with this setting.
</para>
<para>
This works only if the Linux kernel was compiled with
<varname>CONFIG_RT_GROUP_SCHED</varname> disabled. Use the
following command to check this option for your current
kernel:
</para>
<programlisting>zgrep ^CONFIG_RT_GROUP_SCHED /proc/config.gz</programlisting>
<note>
<para>
There is a rumor that real-time scheduling improves audio
quality. That is not true. All it does is reduce the
probability of skipping (audio buffer xruns) when the
computer is under heavy load.
</para>
</note>
</section>
</chapter>
<chapter id="use">
@@ -1738,66 +1791,6 @@ buffer_size: 16384</programlisting>
</informaltable>
</section>
<section>
<title><varname>despotify</varname></title>
<para>
Plays <ulink url="http://www.spotify.com">Spotify</ulink> tracks using the despotify
library. The despotify plugin uses a <filename>spt://</filename> URI and a Spotify
URL. So for example, you can add a song with:
</para>
<para>
<filename>mpc add spt://spotify:track:5qENVY0YEdZ7fiuOax70x1</filename>
</para>
<para>
You need a Spotify premium account to use this plugin, and you need
to setup username and password in the configuration file. The
configuration settings are global since the despotify playlist plugin
use the same settings.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>despotify_user</varname>
</entry>
<entry>
Sets up the Spotify username (required)
</entry>
</row>
<row>
<entry>
<varname>despotify_password</varname>
</entry>
<entry>
Sets up the Spotify password (required)
</entry>
</row>
<row>
<entry>
<varname>despotify_high_bitrate</varname>
</entry>
<entry>
Set up if high bitrate should be used for Spotify tunes.
High bitrate sounds better but slow systems can have problems
with playback (default yes).
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>
<title><varname>file</varname></title>
@@ -2654,7 +2647,8 @@ buffer_size: 16384</programlisting>
/ <ulink
url="http://icecast.org/"><application>IceCast</application></ulink>.
HTTP streaming clients like
<application>mplayer</application> can connect to it.
<application>mplayer</application>, <application>VLC</application>,
and <application>mpv</application> can connect to it.
</para>
<para>
@@ -3284,70 +3278,6 @@ buffer_size: 16384</programlisting>
playlist files.
</para>
</section>
<section>
<title><varname>despotify</varname></title>
<para>
Adds <ulink url="http://www.spotify.com/">Spotify</ulink>
playlists. Spotify playlists use the <filename>spt://</filename> URI,
and a Spotify playlist URL. So for example, you can load a playlist
with
</para>
<para>
<filename>mpc load spt://spotify:user:simon.kagstrom:playlist:3SUwkOe5VbVHysZcidEZtH</filename>
</para>
<para>
See the despotify input plugin for configuration options (username
and password needs to be setup)
</para>
</section>
<section>
<title><varname>soundcloud</varname></title>
<para>
Adds <ulink url="https://www.soundcloud.com/">Soundcloud</ulink>
playlists. SoundCloud playlists use the <filename>soundcloud://</filename> URI,
and with a number of arguments, you may load different playlists with
</para>
<programlisting>
mpc load soundcloud://track/TRACK_ID
mpc load soundcloud://playlist/PLAYLIST_ID
mpc load soundcloud://user/USERNAME
mpc load soundcloud://search/SEARCH_QUERY
mpc load soundcloud://url/https://soundcloud.com/ARTIST/TRACK-NAME
</programlisting>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>apikey</varname>
<parameter>client_id</parameter>
</entry>
<entry>
User apikey/client_id can override the
<application>MPD</application> token provided by
SoundCloud.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
</section>
</chapter>
</book>

@@ -20,33 +20,45 @@
#ifndef COMPILER_H
#define COMPILER_H
#define GCC_CHECK_VERSION(major, minor) \
(defined(__GNUC__) && \
(__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))))
#define GCC_MAKE_VERSION(major, minor, patchlevel) ((major) * 10000 + (minor) * 100 + patchlevel)
#ifdef __GNUC__
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#define GCC_VERSION GCC_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
#else
#define GCC_VERSION 0
#endif
#define GCC_CHECK_VERSION(major, minor) \
(defined(__GNUC__) && GCC_VERSION >= GCC_MAKE_VERSION(major, minor, 0))
/**
* Are we building with gcc (not clang or any other compiler) and a
* version older than the specified one?
*/
#define GCC_OLDER_THAN(major, minor) \
(defined(__GNUC__) && !defined(__clang__) && \
GCC_VERSION < GCC_MAKE_VERSION(major, minor, 0))
#ifdef __clang__
# define CLANG_VERSION (__clang_major__ * 10000 \
+ __clang_minor__ * 100 \
+ __clang_patchlevel__)
# define CLANG_VERSION GCC_MAKE_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
# if __clang_major__ < 3
# error Sorry, your clang version is too old. You need at least version 3.1.
# endif
#elif defined(__GNUC__)
# if !GCC_CHECK_VERSION(4,6)
# if GCC_OLDER_THAN(4,6)
# error Sorry, your gcc version is too old. You need at least version 4.6.
# endif
#else
# warning Untested compiler. Use at your own risk!
#endif
/**
* Are we building with the specified version of clang or newer?
*/
#define CLANG_CHECK_VERSION(major, minor) \
(defined(__clang__) && \
CLANG_VERSION >= GCC_MAKE_VERSION(major, minor, 0))
#if GCC_CHECK_VERSION(4,0)
/* GCC 4.x */
@@ -141,7 +153,7 @@
#if defined(__cplusplus)
/* support for C++11 "override" was added in gcc 4.7 */
#if !defined(__clang__) && !GCC_CHECK_VERSION(4,7)
#if GCC_OLDER_THAN(4,7)
#define override
#define final
#endif

@@ -76,7 +76,10 @@ idle_get_names(void)
unsigned
idle_parse_name(const char *name)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(name != nullptr);
#endif
for (unsigned i = 0; idle_names[i] != nullptr; ++i)
if (StringEqualsCaseASCII(name, idle_names[i]))

@@ -54,7 +54,6 @@
#include "system/FatalError.hxx"
#include "util/UriUtil.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "thread/Id.hxx"
#include "thread/Slack.hxx"
#include "lib/icu/Init.hxx"
@@ -123,8 +122,6 @@
static constexpr unsigned DEFAULT_BUFFER_SIZE = 4096;
static constexpr unsigned DEFAULT_BUFFER_BEFORE_PLAY = 10;
static constexpr Domain main_domain("main");
#ifdef ANDROID
Context *context;
#endif
@@ -633,7 +630,7 @@ static int mpd_main_after_fork(struct options options)
config_get_unsigned(CONF_AUTO_UPDATE_DEPTH,
INT_MAX));
#else
FormatWarning(main_domain,
FormatWarning(config_domain,
"inotify: auto_update was disabled. enable during compilation phase");
#endif
}

@@ -612,6 +612,12 @@ Player::ProcessCommand()
queued = true;
pc.CommandFinished();
pc.Unlock();
if (dc.LockIsIdle())
StartDecoder(*new MusicPipe());
pc.Lock();
break;
case PlayerCommand::PAUSE:

@@ -77,7 +77,10 @@ SongFilter::Item::Item(unsigned _tag, time_t _time)
bool
SongFilter::Item::StringMatch(const char *s) const
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(s != nullptr);
#endif
if (fold_case) {
const std::string folded = IcuCaseFold(s);

@@ -77,7 +77,10 @@ SongLoader::LoadFile(const char *path_utf8, Error &error) const
DetachedSong *
SongLoader::LoadSong(const char *uri_utf8, Error &error) const
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(uri_utf8 != nullptr);
#endif
if (memcmp(uri_utf8, "file:///", 8) == 0)
/* absolute path */

@@ -41,7 +41,7 @@ client_process_command_list(Client &client, bool list_ok,
FormatDebug(client_domain, "process command \"%s\"", cmd);
ret = command_process(client, num++, cmd);
FormatDebug(client_domain, "command returned %i", ret);
FormatDebug(client_domain, "command returned %i", int(ret));
if (ret != CommandResult::OK || client.IsExpired())
break;
else if (list_ok)
@@ -90,7 +90,7 @@ client_process_line(Client &client, char *line)
std::move(cmd_list));
FormatDebug(client_domain,
"[%u] process command "
"list returned %i", client.num, ret);
"list returned %i", client.num, int(ret));
if (ret == CommandResult::CLOSE ||
client.IsExpired())
@@ -126,7 +126,7 @@ client_process_line(Client &client, char *line)
ret = command_process(client, 0, line);
FormatDebug(client_domain,
"[%u] command returned %i",
client.num, ret);
client.num, int(ret));
if (ret == CommandResult::CLOSE ||
client.IsExpired())

@@ -43,7 +43,7 @@ public:
virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override;
virtual void ReturnSong(const LightSong *song) const;
void ReturnSong(const LightSong *song) const override;
virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory,

@@ -103,7 +103,7 @@ public:
virtual void Close() override;
virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override;
virtual void ReturnSong(const LightSong *song) const;
void ReturnSong(const LightSong *song) const override;
virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory,
@@ -731,7 +731,7 @@ ProxyDatabase::Visit(const DatabaseSelection &selection,
{
// TODO: eliminate the const_cast
if (!const_cast<ProxyDatabase *>(this)->EnsureConnected(error))
return nullptr;
return false;
if (!visit_directory && !visit_playlist && selection.recursive &&
(ServerSupportsSearchBase(connection)
@@ -757,7 +757,7 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
{
// TODO: eliminate the const_cast
if (!const_cast<ProxyDatabase *>(this)->EnsureConnected(error))
return nullptr;
return false;
enum mpd_tag_type tag_type2 = Convert(tag_type);
if (tag_type2 == MPD_TAG_COUNT) {
@@ -810,7 +810,7 @@ ProxyDatabase::GetStats(const DatabaseSelection &selection,
// TODO: eliminate the const_cast
if (!const_cast<ProxyDatabase *>(this)->EnsureConnected(error))
return nullptr;
return false;
struct mpd_stats *stats2 =
mpd_run_stats(connection);

@@ -435,9 +435,12 @@ SimpleDatabase::Save(Error &error)
bool
SimpleDatabase::Mount(const char *uri, Database *db, Error &error)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(uri != nullptr);
assert(*uri != 0);
assert(db != nullptr);
#endif
assert(*uri != 0);
ScopeDatabaseLock protect;
@@ -445,13 +448,13 @@ SimpleDatabase::Mount(const char *uri, Database *db, Error &error)
if (r.uri == nullptr) {
error.Format(db_domain, DB_CONFLICT,
"Already exists: %s", uri);
return nullptr;
return false;
}
if (strchr(r.uri, '/') != nullptr) {
error.Format(db_domain, DB_NOT_FOUND,
"Parent not found: %s", uri);
return nullptr;
return false;
}
Directory *mnt = r.directory->CreateChild(r.uri);
@@ -478,7 +481,7 @@ SimpleDatabase::Mount(const char *local_uri, const char *storage_uri,
if (cache_path.IsNull()) {
error.Format(db_domain, DB_NOT_FOUND,
"No 'cache_directory' configured");
return nullptr;
return false;
}
std::string name(storage_uri);

@@ -110,9 +110,9 @@ public:
virtual bool Open(Error &error) override;
virtual void Close() override;
virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override;
virtual void ReturnSong(const LightSong *song) const;
const LightSong *GetSong(const char *uri_utf8,
Error &error) const override;
void ReturnSong(const LightSong *song) const override;
virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory,

@@ -85,7 +85,7 @@ public:
virtual void Close() override;
virtual const LightSong *GetSong(const char *uri_utf8,
Error &error) const override;
virtual void ReturnSong(const LightSong *song) const;
void ReturnSong(const LightSong *song) const override;
virtual bool Visit(const DatabaseSelection &selection,
VisitDirectory visit_directory,
@@ -101,7 +101,9 @@ public:
virtual bool GetStats(const DatabaseSelection &selection,
DatabaseStats &stats,
Error &error) const override;
virtual time_t GetUpdateStamp() const {return 0;}
time_t GetUpdateStamp() const override {
return 0;
}
protected:
bool Configure(const config_param &param, Error &error);

@@ -334,7 +334,7 @@ UpdateWalk::UpdateDirectory(Directory &directory, const FileInfo &info)
directory_set_stat(directory, info);
Error error;
const std::auto_ptr<StorageDirectoryReader> reader(storage.OpenDirectory(directory.GetPath(), error));
const std::unique_ptr<StorageDirectoryReader> reader(storage.OpenDirectory(directory.GetPath(), error));
if (reader.get() == nullptr) {
LogError(error);
return false;

@@ -433,8 +433,11 @@ update_stream_tag(Decoder &decoder, InputStream *is)
/* no stream tag present - submit the song tag
instead */
decoder.song_tag = nullptr;
}
} else
/* discard the song tag; we don't need it */
delete decoder.song_tag;
decoder.song_tag = nullptr;
delete decoder.stream_tag;
decoder.stream_tag = tag;
@@ -566,7 +569,7 @@ decoder_tag(Decoder &decoder, InputStream *is,
/* save the tag */
delete decoder.decoder_tag;
decoder.decoder_tag = new Tag(tag);
decoder.decoder_tag = new Tag(std::move(tag));
/* check for a new stream tag */

@@ -26,7 +26,10 @@
bool
DecoderPlugin::SupportsSuffix(const char *suffix) const
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(suffix != nullptr);
#endif
return suffixes != nullptr && string_array_contains(suffixes, suffix);
@@ -35,7 +38,10 @@ DecoderPlugin::SupportsSuffix(const char *suffix) const
bool
DecoderPlugin::SupportsMimeType(const char *mime_type) const
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(mime_type != nullptr);
#endif
return mime_types != nullptr &&
string_array_contains(mime_types, mime_type);

@@ -380,7 +380,11 @@ decoder_run_song(DecoderControl &dc,
const DetachedSong &song, const char *uri, Path path_fs)
{
Decoder decoder(dc, dc.start_time.IsPositive(),
new Tag(song.GetTag()));
/* pass the song tag only if it's
authoritative, i.e. if it's a local file -
tags on "stream" songs are just remembered
from the last time we played it*/
song.IsFile() ? new Tag(song.GetTag()) : nullptr);
int ret;
dc.state = DecoderState::START;

@@ -29,8 +29,10 @@
#include "input/InputStream.hxx"
#include "tag/TagId3.hxx"
#include "util/Error.hxx"
#include "util/Alloc.hxx"
#include <string.h>
#include <stdlib.h>
#ifdef HAVE_ID3TAG
#include <id3tag.h>
@@ -123,22 +125,26 @@ dsdlib_tag_id3(InputStream &is,
const id3_length_t count = size - offset;
/* Check and limit id3 tag size to prevent a stack overflow */
id3_byte_t dsdid3[4096];
if (count == 0 || count > sizeof(dsdid3))
if (count < 10 || count > 1024 * 1024)
return;
if (!decoder_read_full(nullptr, is, dsdid3, count))
id3_byte_t *const id3_buf = new id3_byte_t[count];
if (id3_buf == nullptr)
return;
struct id3_tag *id3_tag = id3_tag_parse(dsdid3, count);
if (!decoder_read_full(nullptr, is, id3_buf, count)) {
delete[] id3_buf;
return;
}
struct id3_tag *id3_tag = id3_tag_parse(id3_buf, count);
delete[] id3_buf;
if (id3_tag == nullptr)
return;
scan_id3_tag(id3_tag, handler, handler_ctx);
id3_tag_delete(id3_tag);
return;
}
#endif

@@ -92,14 +92,14 @@ struct AvioStream {
AVIOContext *io;
unsigned char buffer[8192];
AvioStream(Decoder *_decoder, InputStream &_input)
:decoder(_decoder), input(_input), io(nullptr) {}
~AvioStream() {
if (io != nullptr)
if (io != nullptr) {
av_free(io->buffer);
av_free(io);
}
}
bool Open();
@@ -153,11 +153,20 @@ mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
bool
AvioStream::Open()
{
io = avio_alloc_context(buffer, sizeof(buffer),
constexpr size_t BUFFER_SIZE = 8192;
auto buffer = (unsigned char *)av_malloc(BUFFER_SIZE);
if (buffer == nullptr)
return false;
io = avio_alloc_context(buffer, BUFFER_SIZE,
false, this,
mpd_ffmpeg_stream_read, nullptr,
input.IsSeekable()
? mpd_ffmpeg_stream_seek : nullptr);
/* If avio_alloc_context() fails, who frees the buffer? The
libavformat API documentation does not specify this, it
only says that AVIOContext.buffer must be freed in the end,
however no AVIOContext exists in that failure code path. */
return io != nullptr;
}
@@ -305,20 +314,60 @@ copy_interleave_frame(const AVCodecContext *codec_context,
return data_size;
}
/**
* Convert AVPacket::pts to a stream-relative time stamp (still in
* AVStream::time_base units). Returns a negative value on error.
*/
gcc_pure
static int64_t
StreamRelativePts(const AVPacket &packet, const AVStream &stream)
{
auto pts = packet.pts;
if (pts < 0 || pts == int64_t(AV_NOPTS_VALUE))
return -1;
auto start = start_time_fallback(stream);
return pts - start;
}
/**
* Convert a non-negative stream-relative time stamp in
* AVStream::time_base units to a PCM frame number.
*/
gcc_pure
static uint64_t
PtsToPcmFrame(uint64_t pts, const AVStream &stream,
const AVCodecContext &codec_context)
{
return av_rescale_q(pts, stream.time_base, codec_context.time_base);
}
/**
* @param min_frame skip all data before this PCM frame number; this
* is used after seeking to skip data in an AVPacket until the exact
* desired time stamp has been reached
*/
static DecoderCommand
ffmpeg_send_packet(Decoder &decoder, InputStream &is,
const AVPacket *packet,
AVCodecContext *codec_context,
const AVStream *stream,
AVFrame *frame,
uint64_t min_frame, size_t pcm_frame_size,
uint8_t **buffer, int *buffer_size)
{
if (packet->pts >= 0 && packet->pts != (int64_t)AV_NOPTS_VALUE) {
auto start = start_time_fallback(*stream);
if (packet->pts >= start)
size_t skip_bytes = 0;
const auto pts = StreamRelativePts(*packet, *stream);
if (pts >= 0) {
if (min_frame > 0) {
auto cur_frame = PtsToPcmFrame(pts, *stream,
*codec_context);
if (cur_frame < min_frame)
skip_bytes = pcm_frame_size * (min_frame - cur_frame);
} else
decoder_timestamp(decoder,
time_from_ffmpeg(packet->pts - start,
stream->time_base));
time_from_ffmpeg(pts, stream->time_base));
}
AVPacket packet2 = *packet;
@@ -354,8 +403,20 @@ ffmpeg_send_packet(Decoder &decoder, InputStream &is,
if (audio_size <= 0)
continue;
const uint8_t *data = output_buffer;
if (skip_bytes > 0) {
if (skip_bytes >= size_t(audio_size)) {
skip_bytes -= audio_size;
continue;
}
data += skip_bytes;
audio_size -= skip_bytes;
skip_bytes = 0;
}
cmd = decoder_data(decoder, is,
output_buffer, audio_size,
data, audio_size,
codec_context->bit_rate / 1000);
}
return cmd;
@@ -374,6 +435,7 @@ ffmpeg_sample_format(enum AVSampleFormat sample_fmt)
case AV_SAMPLE_FMT_S32P:
return SampleFormat::S32;
case AV_SAMPLE_FMT_FLT:
case AV_SAMPLE_FMT_FLTP:
return SampleFormat::FLOAT;
@@ -558,6 +620,8 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
uint8_t *interleaved_buffer = nullptr;
int interleaved_buffer_size = 0;
uint64_t min_frame = 0;
DecoderCommand cmd;
do {
AVPacket packet;
@@ -565,13 +629,15 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
/* end of file */
break;
if (packet.stream_index == audio_stream)
if (packet.stream_index == audio_stream) {
cmd = ffmpeg_send_packet(decoder, input,
&packet, codec_context,
av_stream,
frame,
min_frame, audio_format.GetFrameSize(),
&interleaved_buffer, &interleaved_buffer_size);
else
min_frame = 0;
} else
cmd = decoder_get_command(decoder);
av_free_packet(&packet);
@@ -582,11 +648,16 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
av_stream->time_base) +
start_time_fallback(*av_stream);
/* AVSEEK_FLAG_BACKWARD asks FFmpeg to seek to
the packet boundary before the seek time
stamp, not after */
if (av_seek_frame(format_context, audio_stream, where,
AVSEEK_FLAG_ANY) < 0)
AVSEEK_FLAG_ANY|AVSEEK_FLAG_BACKWARD) < 0)
decoder_seek_error(decoder);
else {
avcodec_flush_buffers(codec_context);
min_frame = decoder_seek_where_frame(decoder);
decoder_command_finished(decoder);
}
}

@@ -156,8 +156,11 @@ gme_file_decode(Decoder &decoder, Path path_fs)
return;
}
const SignedSongTime song_len = ti->length > 0
? SignedSongTime::FromMS(ti->length)
const int length = ti->play_length;
gme_free_info(ti);
const SignedSongTime song_len = length > 0
? SignedSongTime::FromMS(length)
: SignedSongTime::Negative();
/* initialize the MPD decoder */
@@ -168,7 +171,6 @@ gme_file_decode(Decoder &decoder, Path path_fs)
SampleFormat::S16, GME_CHANNELS,
error)) {
LogError(error);
gme_free_info(ti);
gme_delete(emu);
return;
}
@@ -179,8 +181,8 @@ gme_file_decode(Decoder &decoder, Path path_fs)
if (gme_err != nullptr)
LogWarning(gme_domain, gme_err);
if (ti->length > 0)
gme_set_fade(emu, ti->length);
if (length > 0)
gme_set_fade(emu, length);
/* play */
DecoderCommand cmd;
@@ -196,16 +198,17 @@ gme_file_decode(Decoder &decoder, Path path_fs)
if (cmd == DecoderCommand::SEEK) {
unsigned where = decoder_seek_time(decoder).ToMS();
gme_err = gme_seek(emu, where);
if (gme_err != nullptr)
if (gme_err != nullptr) {
LogWarning(gme_domain, gme_err);
decoder_command_finished(decoder);
decoder_seek_error(decoder);
} else
decoder_command_finished(decoder);
}
if (gme_track_ended(emu))
break;
} while (cmd != DecoderCommand::STOP);
gme_free_info(ti);
gme_delete(emu);
}
@@ -236,9 +239,9 @@ gme_scan_file(Path path_fs,
assert(ti != nullptr);
if (ti->length > 0)
if (ti->play_length > 0)
tag_handler_invoke_duration(handler, handler_ctx,
SongTime::FromMS(ti->length));
SongTime::FromMS(ti->play_length));
if (ti->song != nullptr) {
if (gme_track_count(emu) > 1) {

@@ -22,10 +22,12 @@
#include "../DecoderAPI.hxx"
#include "input/InputStream.hxx"
#include "CheckAudioFormat.hxx"
#include "pcm/Traits.hxx"
#include "tag/TagHandler.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "util/Macros.hxx"
#include "util/Clamp.hxx"
#include "Log.hxx"
#include <mpc/mpcdec.h>
@@ -42,6 +44,9 @@ struct mpc_decoder_data {
static constexpr Domain mpcdec_domain("mpcdec");
static constexpr SampleFormat mpcdec_sample_format = SampleFormat::S24_P32;
typedef SampleTraits<mpcdec_sample_format> MpcdecSampleTraits;
static mpc_int32_t
mpc_read_cb(mpc_reader *reader, void *ptr, mpc_int32_t size)
{
@@ -91,18 +96,15 @@ mpc_getsize_cb(mpc_reader *reader)
}
/* this _looks_ performance-critical, don't de-inline -- eric */
static inline int32_t
static inline MpcdecSampleTraits::value_type
mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
{
/* only doing 16-bit audio for now */
int32_t val;
MpcdecSampleTraits::value_type val;
enum {
bits = 24,
};
const int clip_min = -1 << (bits - 1);
const int clip_max = (1 << (bits - 1)) - 1;
constexpr int bits = MpcdecSampleTraits::BITS;
constexpr auto clip_min = MpcdecSampleTraits::MIN;
constexpr auto clip_max = MpcdecSampleTraits::MAX;
#ifdef MPC_FIXED_POINT
const int shift = bits - MPC_FIXED_POINT_SCALE_SHIFT;
@@ -117,16 +119,12 @@ mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
val = sample * float_scale;
#endif
if (val < clip_min)
val = clip_min;
else if (val > clip_max)
val = clip_max;
return val;
return Clamp(val, clip_min, clip_max);
}
static void
mpc_to_mpd_buffer(int32_t *dest, const MPC_SAMPLE_FORMAT *src,
mpc_to_mpd_buffer(MpcdecSampleTraits::pointer_type dest,
const MPC_SAMPLE_FORMAT *src,
unsigned num_samples)
{
while (num_samples-- > 0)
@@ -162,7 +160,7 @@ mpcdec_decode(Decoder &mpd_decoder, InputStream &is)
Error error;
AudioFormat audio_format;
if (!audio_format_init_checked(audio_format, info.sample_freq,
SampleFormat::S24_P32,
mpcdec_sample_format,
info.channels, error)) {
LogError(error);
mpc_demux_exit(demux);
@@ -214,7 +212,7 @@ mpcdec_decode(Decoder &mpd_decoder, InputStream &is)
mpc_uint32_t ret = frame.samples;
ret *= info.channels;
int32_t chunk[ARRAY_SIZE(sample_buffer)];
MpcdecSampleTraits::value_type chunk[ARRAY_SIZE(sample_buffer)];
mpc_to_mpd_buffer(chunk, sample_buffer, ret);
long bit_rate = vbr_update_bits * audio_format.sample_rate

@@ -157,8 +157,6 @@ flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
unsigned bits_per_sample;
encoder->audio_format = audio_format;
/* FIXME: flac should support 32bit as well */
switch (audio_format.format) {
case SampleFormat::S8:
@@ -178,6 +176,8 @@ flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
audio_format.format = SampleFormat::S24_P32;
}
encoder->audio_format = audio_format;
/* allocate the encoder */
encoder->fse = FLAC__stream_encoder_new();
if (encoder->fse == nullptr) {

@@ -66,7 +66,7 @@ struct opus_encoder {
ogg_int64_t granulepos;
opus_encoder():encoder(opus_encoder_plugin) {}
opus_encoder():encoder(opus_encoder_plugin), granulepos(0) {}
};
static constexpr Domain opus_encoder_domain("opus_encoder");

@@ -26,7 +26,6 @@
#include "util/NumberParser.hxx"
#include "util/DynamicFifoBuffer.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
extern "C"
{
@@ -60,8 +59,6 @@ struct ShineEncoder {
bool WriteChunk(bool flush);
};
static constexpr Domain shine_encoder_domain("shine_encoder");
inline bool
ShineEncoder::Configure(const config_param &param,
gcc_unused Error &error)

@@ -53,10 +53,11 @@ public:
children.emplace_back(name, filter);
}
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close();
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error);
/* virtual methods from class Filter */
AudioFormat Open(AudioFormat &af, Error &error) override;
void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
private:
/**

@@ -34,10 +34,11 @@ class NormalizeFilter final : public Filter {
PcmBuffer buffer;
public:
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close();
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
/* virtual methods from class Filter */
AudioFormat Open(AudioFormat &af, Error &error) override;
void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
};
static Filter *

@@ -112,10 +112,11 @@ public:
*/
void Update();
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close();
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
/* virtual methods from class Filter */
AudioFormat Open(AudioFormat &af, Error &error) override;
void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
};
void

@@ -120,10 +120,11 @@ public:
*/
bool Configure(const config_param &param, Error &error);
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close();
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
/* virtual methods from class Filter */
AudioFormat Open(AudioFormat &af, Error &error) override;
void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
};
bool

@@ -26,7 +26,6 @@
#include "AudioFormat.hxx"
#include "util/ConstBuffer.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include <assert.h>
#include <string.h>
@@ -43,14 +42,13 @@ public:
pv.SetVolume(_volume);
}
virtual AudioFormat Open(AudioFormat &af, Error &error) override;
virtual void Close();
virtual ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
/* virtual methods from class Filter */
AudioFormat Open(AudioFormat &af, Error &error) override;
void Close() override;
ConstBuffer<void> FilterPCM(ConstBuffer<void> src,
Error &error) override;
};
static constexpr Domain volume_domain("pcm_volume");
static Filter *
volume_filter_init(gcc_unused const config_param &param,
gcc_unused Error &error)

@@ -103,7 +103,10 @@ static inline void FixSeparators(std::string &s)
std::string
PathToUTF8(const char *path_fs)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(path_fs != nullptr);
#endif
#ifdef HAVE_GLIB
if (fs_charset.empty()) {
@@ -144,7 +147,10 @@ PathToUTF8(const char *path_fs)
char *
PathFromUTF8(const char *path_utf8)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(path_utf8 != nullptr);
#endif
if (fs_charset.empty())
return g_strdup(path_utf8);

@@ -52,7 +52,10 @@ template<typename Traits>
typename Traits::const_pointer
GetBasePathImpl(typename Traits::const_pointer p)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr);
#endif
typename Traits::const_pointer sep = Traits::FindLastSeparator(p);
return sep != nullptr
@@ -64,7 +67,10 @@ template<typename Traits>
typename Traits::string
GetParentPathImpl(typename Traits::const_pointer p)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr);
#endif
typename Traits::const_pointer sep = Traits::FindLastSeparator(p);
if (sep == nullptr)

@@ -57,7 +57,11 @@ struct PathTraitsFS {
gcc_pure gcc_nonnull_all
static const_pointer FindLastSeparator(const_pointer p) {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr);
#endif
#ifdef WIN32
const_pointer pos = p + GetLength(p);
while (p != pos && !IsSeparator(*pos))
@@ -77,7 +81,11 @@ struct PathTraitsFS {
gcc_pure gcc_nonnull_all
static bool IsAbsolute(const_pointer p) {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr);
#endif
#ifdef WIN32
if (IsDrive(p) && IsSeparator(p[2]))
return true;
@@ -147,7 +155,11 @@ struct PathTraitsUTF8 {
gcc_pure gcc_nonnull_all
static const_pointer FindLastSeparator(const_pointer p) {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr);
#endif
return strrchr(p, SEPARATOR);
}
@@ -160,7 +172,11 @@ struct PathTraitsUTF8 {
gcc_pure gcc_nonnull_all
static bool IsAbsolute(const_pointer p) {
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(p != nullptr);
#endif
#ifdef WIN32
if (IsDrive(p) && IsSeparator(p[2]))
return true;

@@ -62,6 +62,7 @@ FileOutputStream::Commit(gcc_unused Error &error)
assert(IsDefined());
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
return true;
}
@@ -71,6 +72,7 @@ FileOutputStream::Cancel()
assert(IsDefined());
CloseHandle(handle);
handle = INVALID_HANDLE_VALUE;
RemoveFile(path);
}

@@ -160,6 +160,11 @@ AsyncInputStream::SeekDone()
assert(io_thread_inside());
assert(IsSeekPending());
/* we may have reached end-of-file previously, and the
connection may have been closed already; however after
seeking successfully, the connection must be alive again */
open = true;
seek_state = SeekState::NONE;
cond.broadcast();
}

@@ -122,7 +122,10 @@ InputStream::IsAvailable()
size_t
InputStream::LockRead(void *ptr, size_t _size, Error &error)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(ptr != nullptr);
#endif
assert(_size > 0);
const ScopeLock protect(mutex);

@@ -54,10 +54,6 @@
#include "plugins/CdioParanoiaInputPlugin.hxx"
#endif
#ifdef ENABLE_DESPOTIFY
#include "plugins/DespotifyInputPlugin.hxx"
#endif
const InputPlugin *const input_plugins[] = {
&input_plugin_file,
#ifdef HAVE_ALSA
@@ -83,9 +79,6 @@ const InputPlugin *const input_plugins[] = {
#endif
#ifdef ENABLE_CDIO_PARANOIA
&input_plugin_cdio_paranoia,
#endif
#ifdef ENABLE_DESPOTIFY
&input_plugin_despotify,
#endif
nullptr
};

@@ -453,6 +453,8 @@ CurlInputStream::RequestDone(CURLcode result, long status)
SeekDone();
else if (!IsReady())
SetReady();
else
cond.broadcast();
}
static void

@@ -1,227 +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 "DespotifyInputPlugin.hxx"
#include "lib/despotify/DespotifyUtils.hxx"
#include "../InputStream.hxx"
#include "../InputPlugin.hxx"
#include "tag/Tag.hxx"
#include "util/StringUtil.hxx"
#include "Log.hxx"
extern "C" {
#include <despotify.h>
}
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
class DespotifyInputStream final : public InputStream {
struct despotify_session *session;
struct ds_track *track;
Tag tag;
struct ds_pcm_data pcm;
size_t len_available;
bool eof;
DespotifyInputStream(const char *_uri,
Mutex &_mutex, Cond &_cond,
despotify_session *_session,
ds_track *_track)
:InputStream(_uri, _mutex, _cond),
session(_session), track(_track),
tag(mpd_despotify_tag_from_track(*track)),
len_available(0), eof(false) {
memset(&pcm, 0, sizeof(pcm));
/* Despotify outputs pcm data */
SetMimeType("audio/x-mpd-cdda-pcm");
SetReady();
}
public:
~DespotifyInputStream();
static InputStream *Open(const char *url, Mutex &mutex, Cond &cond,
Error &error);
void Callback(int sig);
/* virtual methods from InputStream */
bool IsEOF() override {
return eof;
}
Tag *ReadTag() override {
if (tag.IsEmpty())
return nullptr;
Tag *result = new Tag(std::move(tag));
tag.Clear();
return result;
}
size_t Read(void *ptr, size_t size, Error &error) override;
private:
void FillBuffer();
};
inline void
DespotifyInputStream::FillBuffer()
{
/* Wait until there is data */
while (1) {
int rc = despotify_get_pcm(session, &pcm);
if (rc == 0 && pcm.len) {
len_available = pcm.len;
break;
}
if (eof == true)
break;
if (rc < 0) {
LogDebug(despotify_domain, "despotify_get_pcm error");
eof = true;
break;
}
/* Wait a while until next iteration */
usleep(50 * 1000);
}
}
inline void
DespotifyInputStream::Callback(int sig)
{
switch (sig) {
case DESPOTIFY_NEW_TRACK:
break;
case DESPOTIFY_TIME_TELL:
break;
case DESPOTIFY_TRACK_PLAY_ERROR:
LogWarning(despotify_domain, "Track play error");
eof = true;
len_available = 0;
break;
case DESPOTIFY_END_OF_PLAYLIST:
eof = true;
LogDebug(despotify_domain, "End of playlist");
break;
}
}
static void callback(gcc_unused struct despotify_session* ds,
int sig, gcc_unused void* data, void* callback_data)
{
DespotifyInputStream *ctx = (DespotifyInputStream *)callback_data;
ctx->Callback(sig);
}
DespotifyInputStream::~DespotifyInputStream()
{
mpd_despotify_unregister_callback(callback);
despotify_free_track(track);
}
inline InputStream *
DespotifyInputStream::Open(const char *url,
Mutex &mutex, Cond &cond,
gcc_unused Error &error)
{
if (!StringStartsWith(url, "spt://"))
return nullptr;
despotify_session *session = mpd_despotify_get_session();
if (session == nullptr)
return nullptr;
ds_link *ds_link = despotify_link_from_uri(url + 6);
if (!ds_link) {
FormatDebug(despotify_domain, "Can't find %s", url);
return nullptr;
}
if (ds_link->type != LINK_TYPE_TRACK) {
despotify_free_link(ds_link);
return nullptr;
}
ds_track *track = despotify_link_get_track(session, ds_link);
despotify_free_link(ds_link);
if (!track)
return nullptr;
DespotifyInputStream *ctx =
new DespotifyInputStream(url, mutex, cond,
session, track);
if (!mpd_despotify_register_callback(callback, ctx)) {
delete ctx;
return nullptr;
}
if (despotify_play(ctx->session, ctx->track, false) == false) {
mpd_despotify_unregister_callback(callback);
delete ctx;
return nullptr;
}
return ctx;
}
static InputStream *
input_despotify_open(const char *url, Mutex &mutex, Cond &cond, Error &error)
{
return DespotifyInputStream::Open(url, mutex, cond, error);
}
size_t
DespotifyInputStream::Read(void *ptr, size_t read_size,
gcc_unused Error &error)
{
if (len_available == 0)
FillBuffer();
size_t to_cpy = std::min(read_size, len_available);
memcpy(ptr, pcm.buf, to_cpy);
len_available -= to_cpy;
offset += to_cpy;
return to_cpy;
}
const InputPlugin input_plugin_despotify = {
"despotify",
nullptr,
nullptr,
input_despotify_open,
};

@@ -1,25 +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.
*/
#ifndef INPUT_DESPOTIFY_HXX
#define INPUT_DESPOTIFY_HXX
extern const struct InputPlugin input_plugin_despotify;
#endif

@@ -43,7 +43,7 @@ protected:
virtual size_t ThreadRead(void *ptr, size_t size,
Error &error) override;
virtual void Close() {
void Close() override {
mmsx_close(mms);
}
};
@@ -92,6 +92,13 @@ input_mms_open(const char *url,
size_t
MmsInputStream::ThreadRead(void *ptr, size_t read_size, Error &error)
{
/* unfortunately, mmsx_read() blocks until the whole buffer
has been filled; to avoid big latencies, limit the size of
each chunk we read to a reasonable size */
constexpr size_t MAX_CHUNK = 16384;
if (read_size > MAX_CHUNK)
read_size = MAX_CHUNK;
int nbytes = mmsx_read(nullptr, mms, (char *)ptr, read_size);
if (nbytes <= 0) {
if (nbytes < 0)

@@ -132,6 +132,7 @@ SmbclientInputStream::Read(void *ptr, size_t read_size, Error &error)
nbytes = 0;
}
offset += nbytes;
return nbytes;
}

@@ -1,153 +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 "DespotifyUtils.hxx"
#include "tag/Tag.hxx"
#include "tag/TagBuilder.hxx"
#include "config/ConfigGlobal.hxx"
#include "config/ConfigOption.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
extern "C" {
#include <despotify.h>
}
#include <stdio.h>
const Domain despotify_domain("despotify");
static struct despotify_session *g_session;
static void (*registered_callbacks[8])(struct despotify_session *,
int, void *, void *);
static void *registered_callback_data[8];
static void
callback(struct despotify_session* ds, int sig,
void *data, gcc_unused void *callback_data)
{
size_t i;
for (i = 0; i < sizeof(registered_callbacks) / sizeof(registered_callbacks[0]); i++) {
void (*cb)(struct despotify_session *, int, void *, void *) = registered_callbacks[i];
void *cb_data = registered_callback_data[i];
if (cb)
cb(ds, sig, data, cb_data);
}
}
bool mpd_despotify_register_callback(void (*cb)(struct despotify_session *, int, void *, void *),
void *cb_data)
{
size_t i;
for (i = 0; i < sizeof(registered_callbacks) / sizeof(registered_callbacks[0]); i++) {
if (!registered_callbacks[i]) {
registered_callbacks[i] = cb;
registered_callback_data[i] = cb_data;
return true;
}
}
return false;
}
void mpd_despotify_unregister_callback(void (*cb)(struct despotify_session *, int, void *, void *))
{
size_t i;
for (i = 0; i < sizeof(registered_callbacks) / sizeof(registered_callbacks[0]); i++) {
if (registered_callbacks[i] == cb) {
registered_callbacks[i] = nullptr;
}
}
}
Tag
mpd_despotify_tag_from_track(const ds_track &track)
{
char tracknum[20];
char comment[80];
char date[20];
if (!track.has_meta_data)
return Tag();
TagBuilder tag;
snprintf(tracknum, sizeof(tracknum), "%d", track.tracknumber);
snprintf(date, sizeof(date), "%d", track.year);
snprintf(comment, sizeof(comment), "Bitrate %d Kbps, %sgeo restricted",
track.file_bitrate / 1000,
track.geo_restricted ? "" : "not ");
tag.AddItem(TAG_TITLE, track.title);
tag.AddItem(TAG_ARTIST, track.artist->name);
tag.AddItem(TAG_TRACK, tracknum);
tag.AddItem(TAG_ALBUM, track.album);
tag.AddItem(TAG_DATE, date);
tag.AddItem(TAG_COMMENT, comment);
tag.SetDuration(SignedSongTime::FromMS(track.length));
return tag.Commit();
}
struct despotify_session *mpd_despotify_get_session(void)
{
const char *user;
const char *passwd;
bool high_bitrate;
if (g_session)
return g_session;
user = config_get_string(CONF_DESPOTIFY_USER, nullptr);
passwd = config_get_string(CONF_DESPOTIFY_PASSWORD, nullptr);
high_bitrate = config_get_bool(CONF_DESPOTIFY_HIGH_BITRATE, true);
if (user == nullptr || passwd == nullptr) {
LogDebug(despotify_domain,
"disabling despotify because account is not configured");
return nullptr;
}
if (!despotify_init()) {
LogWarning(despotify_domain, "Can't initialize despotify");
return nullptr;
}
g_session = despotify_init_client(callback, nullptr,
high_bitrate, true);
if (!g_session) {
LogWarning(despotify_domain,
"Can't initialize despotify client");
return nullptr;
}
if (!despotify_authenticate(g_session, user, passwd)) {
LogWarning(despotify_domain,
"Can't authenticate despotify session");
despotify_exit(g_session);
return nullptr;
}
return g_session;
}

@@ -1,71 +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.
*/
#ifndef MPD_DESPOTIFY_H
#define MPD_DESPOTIFY_H
struct Tag;
struct despotify_session;
struct ds_track;
extern const class Domain despotify_domain;
/**
* Return the current despotify session.
*
* If the session isn't initialized, this function will initialize
* it and connect to Spotify.
*
* @return a pointer to the despotify session, or nullptr if it can't
* be initialized (e.g., if the configuration isn't supplied)
*/
struct despotify_session *mpd_despotify_get_session(void);
/**
* Create a MPD tags structure from a spotify track
*
* @param track the track to convert
*
* @return filled in #Tag structure
*/
Tag
mpd_despotify_tag_from_track(const ds_track &track);
/**
* Register a despotify callback.
*
* Despotify calls this e.g., when a track ends.
*
* @param cb the callback
* @param cb_data the data to pass to the callback
*
* @return true if the callback could be registered
*/
bool mpd_despotify_register_callback(void (*cb)(struct despotify_session *, int, void *, void *),
void *cb_data);
/**
* Unregister a despotify callback.
*
* @param cb the callback to unregister.
*/
void mpd_despotify_unregister_callback(void (*cb)(struct despotify_session *, int, void *, void *));
#endif

@@ -121,8 +121,11 @@ gcc_pure
int
IcuCollate(const char *a, const char *b)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(a != nullptr);
assert(b != nullptr);
#endif
#ifdef HAVE_ICU
assert(collator != nullptr);
@@ -159,7 +162,10 @@ IcuCaseFold(const char *src)
{
#ifdef HAVE_ICU
assert(collator != nullptr);
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(src != nullptr);
#endif
const auto u = UCharFromUTF8(src);
if (u.IsNull())

@@ -58,9 +58,6 @@ static const char *remoteUrlPrefixes[] = {
#ifdef ENABLE_CDIO_PARANOIA
"cdda://",
#endif
#ifdef ENABLE_DESPOTIFY
"spt://",
#endif
#ifdef HAVE_ALSA
"alsa://",
#endif

@@ -25,13 +25,10 @@
#include "output/Internal.hxx"
#include "pcm/Volume.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
#include <assert.h>
static constexpr Domain mixer_domain("mixer");
static int
output_mixer_get_volume(const AudioOutput &ao)
{

@@ -30,6 +30,7 @@
#include "Internal.hxx"
#include "PlayerControl.hxx"
#include "mixer/MixerControl.hxx"
#include "mixer/Volume.hxx"
#include "Idle.hxx"
extern unsigned audio_output_state_version;
@@ -47,6 +48,11 @@ audio_output_enable_index(MultipleOutputs &outputs, unsigned idx)
ao.enabled = true;
idle_add(IDLE_OUTPUT);
if (ao.mixer != nullptr) {
InvalidateHardwareVolume();
idle_add(IDLE_MIXER);
}
ao.player_control->UpdateAudio();
++audio_output_state_version;
@@ -70,6 +76,7 @@ audio_output_disable_index(MultipleOutputs &outputs, unsigned idx)
Mixer *mixer = ao.mixer;
if (mixer != nullptr) {
mixer_close(mixer);
InvalidateHardwareVolume();
idle_add(IDLE_MIXER);
}
@@ -94,6 +101,7 @@ audio_output_toggle_index(MultipleOutputs &outputs, unsigned idx)
Mixer *mixer = ao.mixer;
if (mixer != nullptr) {
mixer_close(mixer);
InvalidateHardwareVolume();
idle_add(IDLE_MIXER);
}
}

@@ -184,7 +184,8 @@ AudioOutput::LockUpdate(const AudioFormat audio_format,
const ScopeLock protect(mutex);
if (enabled && really_enabled) {
if (fail_timer.Check(REOPEN_AFTER * 1000)) {
if (!fail_timer.IsDefined() ||
fail_timer.Check(REOPEN_AFTER * 1000)) {
return Open(audio_format, mp);
}
} else if (IsOpen())

@@ -22,7 +22,6 @@
#include "../OutputAPI.hxx"
#include "config/ConfigError.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include <string>
@@ -44,8 +43,6 @@ struct PipeOutput {
bool Configure(const config_param &param, Error &error);
};
static constexpr Domain pipe_output_domain("pipe_output");
inline bool
PipeOutput::Configure(const config_param &param, Error &error)
{

@@ -109,7 +109,7 @@ ShoutOutput::Configure(const config_param &param, Error &error)
if (!audio_format.IsFullyDefined()) {
error.Set(config_domain,
"Need full audio format specification");
return nullptr;
return false;
}
const char *host = require_block_string(param, "host");

@@ -44,7 +44,7 @@ PcmFormatConverter::Open(SampleFormat _src_format, SampleFormat _dest_format,
"PCM conversion from %s to %s is not implemented",
sample_format_to_string(_src_format),
sample_format_to_string(_dest_format));
return nullptr;
return false;
case SampleFormat::S16:
case SampleFormat::S24_P32:

@@ -23,7 +23,6 @@
#include "plugins/ExtM3uPlaylistPlugin.hxx"
#include "plugins/M3uPlaylistPlugin.hxx"
#include "plugins/XspfPlaylistPlugin.hxx"
#include "plugins/DespotifyPlaylistPlugin.hxx"
#include "plugins/SoundCloudPlaylistPlugin.hxx"
#include "plugins/PlsPlaylistPlugin.hxx"
#include "plugins/AsxPlaylistPlugin.hxx"
@@ -54,9 +53,6 @@ const struct playlist_plugin *const playlist_plugins[] = {
&asx_playlist_plugin,
&rss_playlist_plugin,
#endif
#ifdef ENABLE_DESPOTIFY
&despotify_playlist_plugin,
#endif
#ifdef ENABLE_SOUNDCLOUD
&soundcloud_playlist_plugin,
#endif

@@ -1,142 +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 "DespotifyPlaylistPlugin.hxx"
#include "lib/despotify/DespotifyUtils.hxx"
#include "../PlaylistPlugin.hxx"
#include "../MemorySongEnumerator.hxx"
#include "tag/Tag.hxx"
#include "DetachedSong.hxx"
#include "Log.hxx"
extern "C" {
#include <despotify.h>
}
#include <string.h>
#include <stdlib.h>
#include <string.h>
static void
add_song(std::forward_list<DetachedSong> &songs, ds_track &track)
{
const char *dsp_scheme = despotify_playlist_plugin.schemes[0];
char uri[128];
char *ds_uri;
/* Create a spt://... URI for MPD */
snprintf(uri, sizeof(uri), "%s://", dsp_scheme);
ds_uri = uri + strlen(dsp_scheme) + 3;
if (despotify_track_to_uri(&track, ds_uri) != ds_uri) {
/* Should never really fail, but let's be sure */
FormatDebug(despotify_domain,
"Can't add track %s", track.title);
return;
}
songs.emplace_front(uri, mpd_despotify_tag_from_track(track));
}
static bool
parse_track(struct despotify_session *session,
std::forward_list<DetachedSong> &songs,
struct ds_link *link)
{
struct ds_track *track = despotify_link_get_track(session, link);
if (track == nullptr)
return false;
add_song(songs, *track);
return true;
}
static bool
parse_playlist(struct despotify_session *session,
std::forward_list<DetachedSong> &songs,
struct ds_link *link)
{
ds_playlist *playlist = despotify_link_get_playlist(session, link);
if (playlist == nullptr)
return false;
for (ds_track *track = playlist->tracks; track != nullptr;
track = track->next)
add_song(songs, *track);
return true;
}
static SongEnumerator *
despotify_playlist_open_uri(const char *url,
gcc_unused Mutex &mutex, gcc_unused Cond &cond)
{
despotify_session *session = mpd_despotify_get_session();
if (session == nullptr)
return nullptr;
/* Get link without spt:// */
ds_link *link =
despotify_link_from_uri(url + strlen(despotify_playlist_plugin.schemes[0]) + 3);
if (link == nullptr) {
FormatDebug(despotify_domain, "Can't find %s\n", url);
return nullptr;
}
std::forward_list<DetachedSong> songs;
bool parse_result;
switch (link->type) {
case LINK_TYPE_TRACK:
parse_result = parse_track(session, songs, link);
break;
case LINK_TYPE_PLAYLIST:
parse_result = parse_playlist(session, songs, link);
break;
default:
parse_result = false;
break;
}
despotify_free_link(link);
if (!parse_result)
return nullptr;
songs.reverse();
return new MemorySongEnumerator(std::move(songs));
}
static const char *const despotify_schemes[] = {
"spt",
nullptr
};
const struct playlist_plugin despotify_playlist_plugin = {
"despotify",
nullptr,
nullptr,
despotify_playlist_open_uri,
nullptr,
despotify_schemes,
nullptr,
nullptr,
};

@@ -1,25 +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.
*/
#ifndef MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_HXX
#define MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_HXX
extern const struct playlist_plugin despotify_playlist_plugin;
#endif

@@ -25,14 +25,11 @@
#include "input/InputStream.hxx"
#include "tag/TagBuilder.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "lib/expat/ExpatParser.hxx"
#include "Log.hxx"
#include <string.h>
static constexpr Domain xspf_domain("xspf");
/**
* This is the state object for the GLib XML parser.
*/

@@ -177,6 +177,8 @@ private:
mutex.unlock();
DeferredMonitor::Schedule();
mutex.lock();
if (state == State::INITIAL)
cond.wait(mutex);
break;
case State::CONNECTING:
@@ -188,8 +190,6 @@ private:
error.Set(last_error);
return false;
}
cond.wait(mutex);
}
}
@@ -288,7 +288,7 @@ NfsStorage::GetInfo(const char *uri_utf8, gcc_unused bool follow,
return false;
if (!WaitConnected(error))
return nullptr;
return false;
NfsGetInfoOperation operation(*connection, path.c_str(), info);
return operation.Run(error);

@@ -40,8 +40,8 @@ MonotonicClockS(void)
if (base.denom == 0)
(void)mach_timebase_info(&base);
return (unsigned)((mach_absolute_time() * base.numer / 1000)
/ (1000000 * base.denom));
return (unsigned)(((double)mach_absolute_time() * base.numer / 1000)
/ base.denom / 1000000);
#elif defined(CLOCK_MONOTONIC)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
@@ -62,8 +62,8 @@ MonotonicClockMS(void)
if (base.denom == 0)
(void)mach_timebase_info(&base);
return (unsigned)((mach_absolute_time() * base.numer)
/ (1000000 * base.denom));
return (unsigned)(((double)mach_absolute_time() * base.numer)
/ base.denom / 1000000);
#elif defined(CLOCK_MONOTONIC)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
@@ -104,8 +104,8 @@ MonotonicClockUS(void)
if (base.denom == 0)
(void)mach_timebase_info(&base);
return ((uint64_t)mach_absolute_time() * (uint64_t)base.numer)
/ (1000 * (uint64_t)base.denom);
return (uint64_t)(((double)mach_absolute_time() * base.numer)
/ base.denom / 1000);
#elif defined(CLOCK_MONOTONIC)
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);

@@ -63,7 +63,7 @@ protected:
}
public:
bool IsDefined() const {
constexpr bool IsDefined() const {
return last != 0;
}

@@ -78,12 +78,12 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback)
/* get the key */
const char *key = p;
while (remaining > size && *p != '\0') {
p++;
remaining--;
}
p++;
remaining--;
const char *key_end = (const char *)memchr(p, '\0', remaining);
if (key_end == nullptr)
break;
p = key_end + 1;
remaining -= p - key;
/* get the value */
if (remaining < size)

@@ -25,6 +25,8 @@
#include "Tag.hxx"
#include "util/WritableBuffer.hxx"
#include <array>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
@@ -168,12 +170,19 @@ TagBuilder::Complement(const Tag &other)
has_playlist |= other.has_playlist;
/* build a table of tag types that were already present in
this object, which will not be copied from #other */
std::array<bool, TAG_NUM_OF_ITEM_TYPES> present;
present.fill(false);
for (const TagItem *i : items)
present[i->type] = true;
items.reserve(items.size() + other.num_items);
tag_pool_lock.lock();
for (unsigned i = 0, n = other.num_items; i != n; ++i) {
TagItem *item = other.items[i];
if (!HasType(item->type))
if (!present[item->type])
items.push_back(tag_pool_dup_item(item));
}
tag_pool_lock.unlock();
@@ -182,7 +191,10 @@ TagBuilder::Complement(const Tag &other)
inline void
TagBuilder::AddItemInternal(TagType type, const char *value, size_t length)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(value != nullptr);
#endif
assert(length > 0);
auto f = FixTagString(value, length);
@@ -203,7 +215,10 @@ TagBuilder::AddItemInternal(TagType type, const char *value, size_t length)
void
TagBuilder::AddItem(TagType type, const char *value, size_t length)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(value != nullptr);
#endif
if (length == 0 || ignore_tag_items[type])
return;
@@ -214,7 +229,10 @@ TagBuilder::AddItem(TagType type, const char *value, size_t length)
void
TagBuilder::AddItem(TagType type, const char *value)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(value != nullptr);
#endif
AddItem(type, value, strlen(value));
}

@@ -40,9 +40,9 @@ FindInvalidUTF8(const char *p, const char *const end)
/* now call the other SequenceLengthUTF8() overload
which also validates the continuations */
const size_t t = SequenceLengthUTF8(p);
assert(s == t);
if (t == 0)
return p;
assert(s == t);
p += s;
}

@@ -20,17 +20,28 @@
#ifndef MPD_THREAD_NAME_HXX
#define MPD_THREAD_NAME_HXX
#ifdef HAVE_PTHREAD_SETNAME_NP
#include <pthread.h>
#include <stdio.h>
#if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__NetBSD__)
# define HAVE_THREAD_NAME
# include <pthread.h>
#elif defined(HAVE_PRCTL)
#include <sys/prctl.h>
# include <sys/prctl.h>
# ifdef PR_SET_NAME
# define HAVE_THREAD_NAME
# endif
#endif
#ifdef HAVE_THREAD_NAME
# include <stdio.h>
#endif
static inline void
SetThreadName(const char *name)
{
#ifdef HAVE_PTHREAD_SETNAME_NP
#if defined(HAVE_PTHREAD_SETNAME_NP) && !defined(__NetBSD__)
/* not using pthread_setname_np() on NetBSD because it
requires a non-const pointer argument, which we don't have
here */
#ifdef __APPLE__
pthread_setname_np(name);
#else
@@ -47,7 +58,7 @@ template<typename... Args>
static inline void
FormatThreadName(const char *fmt, gcc_unused Args&&... args)
{
#ifdef HAVE_PTHREAD_SETNAME_NP
#ifdef HAVE_THREAD_NAME
char buffer[16];
snprintf(buffer, sizeof(buffer), fmt, args...);
SetThreadName(buffer);

@@ -22,7 +22,6 @@
#include "system/FatalError.hxx"
#include "fs/AllocatedPath.hxx"
#include "fs/FileSystem.hxx"
#include "util/Domain.hxx"
#include "PidFile.hxx"
#include "Log.hxx"
@@ -37,8 +36,6 @@
#include <grp.h>
#endif
static constexpr Domain daemon_domain("daemon");
#ifndef WIN32
/** the Unix user name which MPD runs as */

@@ -43,24 +43,30 @@ gcc_pure gcc_nonnull_all
static inline bool
StringEqualsCaseASCII(const char *a, const char *b)
{
assert(a != nullptr);
assert(b != nullptr);
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(a != nullptr);
assert(b != nullptr);
#endif
/* note: strcasecmp() depends on the locale, but for ASCII-only
strings, it's safe to use */
return strcasecmp(a, b) == 0;
/* note: strcasecmp() depends on the locale, but for ASCII-only
strings, it's safe to use */
return strcasecmp(a, b) == 0;
}
gcc_pure gcc_nonnull_all
static inline bool
StringEqualsCaseASCII(const char *a, const char *b, size_t n)
{
assert(a != nullptr);
assert(b != nullptr);
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(a != nullptr);
assert(b != nullptr);
#endif
/* note: strcasecmp() depends on the locale, but for ASCII-only
strings, it's safe to use */
return strncasecmp(a, b, n) == 0;
/* note: strcasecmp() depends on the locale, but for ASCII-only
strings, it's safe to use */
return strncasecmp(a, b, n) == 0;
}
#endif

@@ -17,6 +17,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include "Alloc.hxx"
#include <stdlib.h>
@@ -62,14 +63,14 @@ xstrdup(const char *s)
char *
xstrndup(const char *s, size_t n)
{
#ifdef WIN32
char *p = (char *)xalloc(n + 1);
memcpy(p, s, n);
p[n] = 0;
#else
#ifdef HAVE_STRNDUP
char *p = strndup(s, n);
if (gcc_unlikely(p == nullptr))
oom();
#else
char *p = (char *)xalloc(n + 1);
memcpy(p, s, n);
p[n] = 0;
#endif
return p;

@@ -35,7 +35,7 @@
#include <new>
#include <utility>
#if !defined(__clang__) && __GNUC__ && !GCC_CHECK_VERSION(4,8)
#if GCC_OLDER_THAN(4,8)
#include <type_traits>
#endif
@@ -54,7 +54,7 @@
*/
template<class T>
class Manual {
#if !defined(__clang__) && __GNUC__ && !GCC_CHECK_VERSION(4,8)
#if GCC_OLDER_THAN(4,8)
/* no alignas() on gcc < 4.8: apply worst-case fallback */
__attribute__((aligned(8)))
#else

@@ -140,8 +140,11 @@ uri_remove_auth(const char *uri)
bool
uri_is_child(const char *parent, const char *child)
{
#if !CLANG_CHECK_VERSION(3,6)
/* disabled on clang due to -Wtautological-pointer-compare */
assert(parent != nullptr);
assert(child != nullptr);
#endif
const size_t parent_length = strlen(parent);
return memcmp(parent, child, parent_length) == 0 &&

@@ -7,16 +7,7 @@ ExecStart=@prefix@/bin/mpd --no-daemon
# allow MPD to use real-time priority 50
LimitRTPRIO=50
LimitRTTIME=-1
# move MPD to a top-level cgroup, as real-time budget assignment fails
# in cgroup /system/mpd.service, because /system has a zero real-time
# budget; see
# http://www.freedesktop.org/wiki/Software/systemd/MyServiceCantGetRealtime/
ControlGroup=cpu:/mpd
# assign a real-time budget
ControlGroupAttribute=cpu.rt_runtime_us 500000
LimitRTTIME=infinity
[Install]
WantedBy=multi-user.target

@@ -35,6 +35,7 @@
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
const struct filter_plugin *
filter_plugin_by_name(gcc_unused const char *name)