Compare commits

...

65 Commits

Author SHA1 Message Date
Max Kellermann
975a4ae871 release v0.20.16 2018-02-03 19:55:07 +01:00
Max Kellermann
56aaf3c73e python/build/libs: upgrade CURL to 7.58.0 2018-02-03 19:46:31 +01:00
Max Kellermann
12fd1cad0c archive/iso9660: libcdio 2.0 compatibility
Closes #173
2018-02-03 19:32:31 +01:00
Max Kellermann
e573cbf032 db/update/Queue: work around GCC7 -Wuninitialized 2018-02-01 19:53:42 +01:00
Max Kellermann
dead461542 lib/upnp/Init: enable IPv6 2018-01-31 18:15:46 +01:00
Max Kellermann
3d5da1ac73 lib/upnp/Init: use nullptr instead of 0 2018-01-31 18:14:26 +01:00
Max Kellermann
ec408ca6a6 output/pulse: fix crash during auto-detection
The PulseOutput needs to be "enabled" before WaitConnection() may be
called.

Closes #207
2018-01-30 10:06:36 +01:00
Max Kellermann
ea66cdd6a5 test/read_mixer: another kludge to work around -Wnull-dereference 2018-01-23 16:42:25 +01:00
Max Kellermann
f762e8034f test/NullMixerListener: new class to fix -Wnull-dereference 2018-01-23 16:28:56 +01:00
Max Kellermann
bb1e369f30 playlist/SoundCloud: fix -Wunused-lambda-capture 2018-01-23 09:57:52 +01:00
Max Kellermann
8376578921 db/simple/Mount: drop mount point prefix from LOCATE_TAG_BASE_TYPE
Fixes search within mount points, resulting in error "No such
directory".

Closes #190
2018-01-19 23:52:57 +01:00
Max Kellermann
ed2354cd9d SongFilter: allow copying items 2018-01-19 23:52:03 +01:00
Max Kellermann
386688b87a SongFilter: use std::string instead of AllocatedString 2018-01-19 23:51:42 +01:00
Max Kellermann
38d56dddf1 lib/icu/Compare: allow copying 2018-01-19 23:49:50 +01:00
Max Kellermann
e8975942ec Makefile.am: link libicu.a before libutil.a
libicu.a depends on libutil.a.
2018-01-19 23:38:24 +01:00
Max Kellermann
3ca80a7336 util/RefCount, db/simple/Mount: remove obsolete libc++ workarounds
No longer a problem with NDK r16.
2018-01-19 23:19:46 +01:00
Max Kellermann
d029dae7ad Makefile.am: use Android SDK build-tools 27.0.0 2018-01-19 23:04:54 +01:00
Max Kellermann
9e058732ee android/build.py: add -fpic
Android native code should be position-independent.

The NDK build scripts use "-fpic" instead of "-fPIC" for ARM, but that
doesn't work with FFmpeg's assembly code, because it requires
R_ARM_MOVW_ABS_NC which is unavailable with "-fpic".
2018-01-19 22:40:59 +01:00
Max Kellermann
cad5d11261 android/build.py: simplify libc++ flags
By telling clang which implementation to use, we avoid the dependency
on libstdc++.so.
2018-01-19 22:36:19 +01:00
Max Kellermann
fcaedec2ab {android,win32}/build.py: move "-O* -g" to common_flags 2018-01-19 12:33:28 +01:00
Max Kellermann
ead9d59e88 python/build/libs.py: build only libFLAC, no programs 2018-01-19 12:33:03 +01:00
Max Kellermann
34b8a17ccd python/build/autotools.py: add "subdir" parameter 2018-01-19 11:39:36 +01:00
Max Kellermann
a53d081c39 python/build/libs.py: disable libFLAC API documentation 2018-01-19 11:38:24 +01:00
Max Kellermann
823134e4ba python/build/libs.py: disable Opus documentation and extra programs 2018-01-19 11:32:40 +01:00
Max Kellermann
272167b4fc python/build/libs.py: update LAME to 3.100 2018-01-18 22:07:28 +01:00
Max Kellermann
92f09bba94 Makefile.am: rename JAVA_SOURCES to JAVA_SOURCE_PATHS
Work around automake warning:

    Makefile.am:310: warning: variable 'JAVA_SOURCES' is defined but no program or
    Makefile.am:310: library has 'JAVA' as canonical name (possible typo)

Closes #195
2018-01-18 22:05:04 +01:00
Max Kellermann
1f50bdb230 event/Loop: use std::atomic_bool for the "quit" variable
Fixes thread sanitizer warnings.
2018-01-08 10:06:23 +01:00
Max Kellermann
2eef4e6716 thread/Thread: add debug attribute "inside_handle"
This attribute shall be used only for IsInside() to make this safe
against a race condition described in #188:

> There is no requirement on the implementation that the ID of the
> created thread be available before the newly created thread starts
> executing.

http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_create.html):

This means that on some pthread implementations (e.g. Haiku), the
assert(thread.IsInside()) could fail.

Closes #188
2018-01-08 09:58:18 +01:00
Max Kellermann
d989dbfec4 thread/Thread: make IsInside() debug-only
This method is only used inside assert().
2018-01-08 09:56:39 +01:00
Max Kellermann
ca9fcec364 thread/Thread: fix indent 2018-01-08 09:49:08 +01:00
Max Kellermann
354104f9a9 thread/{Thread,Id}: use defaul-initialized pthread_t as "undefined" value
Use the "==" operator instead of pthread_equal().

This allows us to eliminate two boolean flags which are there to avoid
race conditions, and made the thing so fragile that I got tons of
(correct) thread sanitizer warnings.
2018-01-07 17:20:26 +01:00
Max Kellermann
8649ea3d6f thread/Thread: use BoundMethod 2018-01-07 17:20:26 +01:00
Max Kellermann
752ff12c37 thread/Thread: move code to Run() 2018-01-07 17:20:26 +01:00
Max Kellermann
4bb89b1755 MusicPipe: lock the mutex in Peek() and GetSize() 2018-01-07 17:20:22 +01:00
Max Kellermann
0ef553d30e increment version number to 0.20.16 2018-01-06 13:15:47 +01:00
Max Kellermann
43a62aef07 android: release 0.20.15 2018-01-05 18:09:56 +01:00
Max Kellermann
ed4d0aa909 release v0.20.15 2018-01-05 17:55:25 +01:00
Max Kellermann
023ce4e720 python/build/libs.py: disable even more FFmpeg modules 2018-01-05 17:41:58 +01:00
Max Kellermann
368d9359dd python/build/libs.py: update libogg to 1.3.3 2018-01-05 17:41:58 +01:00
Max Kellermann
d98c19d561 python/build/libs.py: disable more FFmpeg modules 2018-01-05 16:49:28 +01:00
Max Kellermann
cab77e35e0 queue/PlaylistControl: fix crash after seek failure
This completes the bug fix commit
2065e3290452377b2931f3129b230c8cc536cbc8; if we clear "queued" then we
must clear "queued_song" as well, or another variant of the assertion
fails.
2018-01-05 13:00:24 +01:00
Max Kellermann
e3e90b4b93 python/build/libs.py: disable libcurl SMB support
MPD doesn't use this feature anyway.
2018-01-05 12:00:29 +01:00
Max Kellermann
f8c69893e1 python/build/libs.py: disable lots of useless FFmpeg features 2018-01-05 11:11:20 +01:00
Max Kellermann
49678a0893 python/build/libs.py: suppress "visibility default" in libopus build 2018-01-05 10:33:53 +01:00
Max Kellermann
d667b5b48c python/build/libs.py: work around libid3tag CFLAGS bug 2018-01-05 10:25:16 +01:00
Max Kellermann
9cba55b39c python/build/project.py: add "edits" parameter to edit source files 2018-01-05 10:06:22 +01:00
Max Kellermann
c2cbb7b8ce output/haiku: remove unimplemented Cancel() method 2018-01-05 10:05:14 +01:00
Felix Hädicke
8217d75ca1 build/python: refactoring: introduce new class MakeProject
This introduces a the new class MakeProject, which is used as a base
class for all Makefile based thirdparty libraries.
2018-01-05 08:17:17 +01:00
Max Kellermann
1ca70d9759 build/python/autotools: add properties "ldflags", "libs", "install_target" 2018-01-05 08:17:15 +01:00
Felix Hädicke
4303aaa9b8 build/python: use "glibtoolize", not "libtoolize" when compiling on OS X
On OS X, the "libtoolize" command is some Apple tool. The libtoolize
we want is named "glibtoolize" in Homebrew.
2018-01-05 07:55:59 +01:00
Max Kellermann
7b56bae289 python/build/libs.py: pass --disable-debugging to libid3tag and libmad 2018-01-05 07:30:09 +01:00
Max Kellermann
4183416b3e python/build/libs.py: reindent arrays 2018-01-05 07:27:09 +01:00
Max Kellermann
a60dee57ce python/libs: upgrade Boost to 1.66.0 2018-01-05 07:16:38 +01:00
Max Kellermann
5724656acb android/build.py: enable function/data sections in static libraries
.. and make all library symbols hidden by default.

Saves big amounts of .text section size with --gc-sections, because
only this allows discarding unused functions from those (static)
third-party libraries.
2018-01-04 23:33:07 +01:00
Max Kellermann
329f9cd9fe thread/Util: no ioprio_set() on Android due to seccomp/SIGSYS 2018-01-04 19:33:14 +01:00
Felix Hädicke
fbdb8b406e Makefile.am: build Android APK package without Ant
In current Android SDK releases, Ant support was removed. Move the
necessary build steps from the former Ant build system to our Makefile,
and call the required build tools from the Android SDK (aapt and dx),
Java SDK (javac) and Info-ZIP (zip) directly.

[mk: copied from Felix's commit
e52b906dba971a1173f9e8f83d32b52ee9f89af3 in the XCSoar project)
2018-01-04 18:55:54 +01:00
Max Kellermann
85d0bbd957 Makefile.am: add variable ZIPALIGN 2018-01-04 18:55:45 +01:00
Max Kellermann
414f00d6ae Makefile.am: add variable ANDROID_SDK_PLATFORM 2018-01-04 18:47:52 +01:00
Max Kellermann
17b0add058 filter/Observer: pass Reset() to underlying Filter
Wohooooo, the method Filter::Reset() has been broken because no
implementation of it has ever been called for a loooong time.
And nobody ever noticed it.  WTF.
2018-01-02 22:13:14 +01:00
Max Kellermann
c68ed40661 pcm/SoxrResampler: implement method Reset() 2018-01-02 21:53:09 +01:00
Max Kellermann
ff624075a8 storage/State: check if a CompositeStorage exists; fixes nullptr dereference
Fixes another crash bug caused by commit
64d141f71e
2018-01-02 14:13:26 +01:00
Max Kellermann
08db28469d storage/State: make mount errors non-fatal
Fixes crash bug caused by commit
64d141f71e
2018-01-02 14:07:52 +01:00
Max Kellermann
a20b326807 storage/State: fix memory leak after database mount failure
Caused by commit 64d141f71e

This wasn't a serious memory leak, because after a mount failure, MPD
would abort anyway, which is subject to the next commit.
2018-01-02 14:05:07 +01:00
Max Kellermann
4db1b1b250 storage/State: remove useless #ifdef ENABLE_DATABASE
This source file isn't compiled when the database is disabled.
2018-01-02 13:48:16 +01:00
Max Kellermann
ff6b263b48 increment version number to 0.20.15 2018-01-02 13:46:03 +01:00
52 changed files with 744 additions and 281 deletions

View File

@@ -61,8 +61,8 @@ src_mpd_LDADD = \
libnet.a \ libnet.a \
$(FS_LIBS) \ $(FS_LIBS) \
libsystem.a \ libsystem.a \
libutil.a \
$(ICU_LDADD) \ $(ICU_LDADD) \
libutil.a \
$(SYSTEMD_DAEMON_LIBS) $(SYSTEMD_DAEMON_LIBS)
src_mpd_SOURCES = \ src_mpd_SOURCES = \
@@ -285,30 +285,62 @@ libmain_a_CPPFLAGS = $(AM_CPPFLAGS) -Iandroid/build/include
src_mpd_LDADD += libandroid.a libjava.a src_mpd_LDADD += libandroid.a libjava.a
all-local: android/build/bin/$(APK_NAME)-debug.apk all-local: android/build/$(APK_NAME)-debug.apk
clean-local: clean-local:
rm -rf android/build rm -rf android/build
libmpd.so: $(filter %.a,$(src_mpd_LDADD)) libmain.a libmpd.so: $(filter %.a,$(src_mpd_LDADD)) libmain.a
$(AM_V_CXXLD)$(CXXLD) -shared -Wl,--no-undefined,-shared,-Bsymbolic -llog -lz -o $@ $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) src/libmain_a-Main.o $(src_mpd_LDADD) $(LIBS) $(AM_V_CXXLD)$(CXXLD) -shared -Wl,--no-undefined,-shared,-Bsymbolic -llog -lz -o $@ $(AM_CXXFLAGS) $(CXXFLAGS) $(LDFLAGS) src/libmain_a-Main.o $(src_mpd_LDADD) $(LIBS)
android/build/build.xml: android/AndroidManifest.xml ANDROID_SDK_BUILD_TOOLS_VERSION = 27.0.0
rm -rf android/build ANDROID_SDK_PLATFORM = android-17
mkdir -p android/build/include android/build/res android/build/src/org
ln -s $(abs_srcdir)/android/AndroidManifest.xml $(abs_srcdir)/android/custom_rules.xml android/build
ln -s $(abs_srcdir)/android/src android/build/src/org/musicpd
ln -s $(abs_srcdir)/android/res/values $(abs_srcdir)/android/res/layout android/build/res
$(ANDROID_SDK)/tools/android update project --path android/build --target android-17 --name $(APK_NAME)
android/build/bin/classes/org/musicpd/Bridge.class: android/src/Bridge.java android/build/build.xml android/build/res/drawable/icon.png ANDROID_BUILD_TOOLS_DIR = $(ANDROID_SDK)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)
cd android/build && ant compile-jni-classes ANDROID_SDK_PLATFORM_DIR = $(ANDROID_SDK)/platforms/$(ANDROID_SDK_PLATFORM)
android/build/include/org_musicpd_Bridge.h: android/build/bin/classes/org/musicpd/Bridge.class JAVAC = javac
javah -classpath $(ANDROID_SDK)/platforms/android-17/android.jar:android/build/bin/classes -d $(@D) org.musicpd.Bridge AAPT = $(ANDROID_BUILD_TOOLS_DIR)/aapt
DX = $(ANDROID_BUILD_TOOLS_DIR)/dx
ZIPALIGN = $(ANDROID_BUILD_TOOLS_DIR)/zipalign
ANDROID_XML_RES := $(wildcard $(srcdir)/android/res/*/*.xml)
ANDROID_XML_RES_COPIES := $(patsubst $(srcdir)/android/%,android/build/%,$(ANDROID_XML_RES))
JAVA_SOURCE_NAMES = Bridge.java Loader.java Main.java
JAVA_SOURCE_PATHS = $(addprefix $(srcdir)/android/src/,$(JAVA_SOURCE_NAMES))
JAVA_CLASSFILES_DIR = android/build/classes
$(ANDROID_XML_RES_COPIES): $(ANDROID_XML_RES)
@$(MKDIR_P) $(dir $@)
cp $(patsubst android/build/%,$(srcdir)/android/%,$@) $@
android/build/resources.apk: $(ANDROID_XML_RES_COPIES) android/build/res/drawable/icon.png
@$(MKDIR_P) android/build/gen
$(AAPT) package -f -m --auto-add-overlay \
--custom-package org.musicpd \
-M $(srcdir)/android/AndroidManifest.xml \
-S android/build/res \
-J android/build/gen \
-I $(ANDROID_SDK_PLATFORM_DIR)/android.jar \
-F android/build/resources.apk
# R.java is generated by aapt, when resources.apk is generated
android/build/gen/org/musicpd/R.java: android/build/resources.apk
android/build/classes.dex: $(JAVA_SOURCE_PATHS) android/build/gen/org/musicpd/R.java
@$(MKDIR_P) $(JAVA_CLASSFILES_DIR)
$(JAVAC) -source 1.5 -target 1.5 -Xlint:-options \
-cp $(ANDROID_SDK_PLATFORM_DIR)/android.jar:$(JAVA_CLASSFILES_DIR) \
-d $(JAVA_CLASSFILES_DIR) $^
$(DX) --dex --output $@ $(JAVA_CLASSFILES_DIR)
android/build/include/org_musicpd_Bridge.h: android/build/classes.dex
javah -classpath $(ANDROID_SDK_PLATFORM_DIR)/android.jar:$(JAVA_CLASSFILES_DIR) -d $(@D) org.musicpd.Bridge
BUILT_SOURCES = 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 android/build/lib/armeabi-v7a/libmpd.so: libmpd.so
mkdir -p $(@D) mkdir -p $(@D)
rm -f $@ rm -f $@
$(STRIP) -o $@ $< $(STRIP) -o $@ $<
@@ -317,24 +349,19 @@ android/build/res/drawable/icon.png: mpd.svg
mkdir -p $(@D) mkdir -p $(@D)
rsvg-convert --width=48 --height=48 $< -o $@ rsvg-convert --width=48 --height=48 $< -o $@
APK_DEPS = android/build/res/drawable/icon.png \ .DELETE_ON_ERROR: android/build/unsigned.apk
android/build/libs/armeabi-v7a/libmpd.so \ android/build/unsigned.apk: android/build/classes.dex android/build/resources.apk android/build/lib/armeabi-v7a/libmpd.so
$(wildcard $(srcdir)/android/src/*.java) \ cp android/build/resources.apk $@
android/build/build.xml cd $(dir $@) && zip -q -r $(notdir $@) classes.dex lib
android/build/bin/$(APK_NAME)-debug.apk: $(APK_DEPS) android/build/$(APK_NAME)-debug.apk: android/build/unsigned.apk
cd android/build && ant nodeps debug jarsigner -keystore $(HOME)/.android/debug.keystore -storepass android -signedjar $@ $< androiddebugkey
android/build/bin/$(APK_NAME)-release-unsigned.apk: $(APK_DEPS) android/build/$(APK_NAME)-release-unaligned.apk: android/build/unsigned.apk
cd android/build && ant nodeps release
android/build/bin/$(APK_NAME)-release-unaligned.apk: android/build/bin/$(APK_NAME)-release-unsigned.apk
jarsigner -digestalg SHA1 -sigalg MD5withRSA -storepass:env ANDROID_KEYSTORE_PASS -keystore $(ANDROID_KEYSTORE) -signedjar $@ $< $(ANDROID_KEY_ALIAS) jarsigner -digestalg SHA1 -sigalg MD5withRSA -storepass:env ANDROID_KEYSTORE_PASS -keystore $(ANDROID_KEYSTORE) -signedjar $@ $< $(ANDROID_KEY_ALIAS)
ANDROID_SDK_BUILD_TOOLS_VERSION = 20.0.0 android/build/$(APK_NAME).apk: android/build/$(APK_NAME)-release-unaligned.apk
$(ZIPALIGN) -f 4 $< $@
android/build/bin/$(APK_NAME).apk: android/build/bin/$(APK_NAME)-release-unaligned.apk
$(ANDROID_SDK)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)/zipalign -f 4 $< $@
endif endif
@@ -2132,6 +2159,7 @@ test_run_output_LDADD = $(MPD_LIBS) \
libutil.a libutil.a
test_run_output_SOURCES = test/run_output.cxx \ test_run_output_SOURCES = test/run_output.cxx \
test/ScopeIOThread.hxx \ test/ScopeIOThread.hxx \
test/NullMixerListener.hxx \
src/Log.cxx src/LogBackend.cxx \ src/Log.cxx src/LogBackend.cxx \
src/IOThread.cxx \ src/IOThread.cxx \
src/output/Domain.cxx \ src/output/Domain.cxx \
@@ -2155,6 +2183,7 @@ test_read_mixer_LDADD = \
libsystem.a \ libsystem.a \
libutil.a libutil.a
test_read_mixer_SOURCES = test/read_mixer.cxx \ test_read_mixer_SOURCES = test/read_mixer.cxx \
test/NullMixerListener.hxx \
src/Log.cxx src/LogBackend.cxx \ src/Log.cxx src/LogBackend.cxx \
src/mixer/MixerControl.cxx \ src/mixer/MixerControl.cxx \
src/filter/FilterPlugin.cxx \ src/filter/FilterPlugin.cxx \

21
NEWS
View File

@@ -1,3 +1,24 @@
ver 0.20.16 (2018/02/03)
* output
- pulse: fix crash during auto-detection
* database
- simple: fix search within mount points
- upnp: enable IPv6
* archive
- iso9660: libcdio 2.0 compatibility
* fix crash in debug build on Haiku and other operating systems
ver 0.20.15 (2018/01/05)
* queue: fix crash after seek failure
* resampler
- soxr: clear internal state after manual song change
* state file
- make mount point restore errors non-fatal
- fix crash when restoring mounts with incompatible database plugin
* Android
- build without Ant
- fix for SIGSYS crash
ver 0.20.14 (2018/01/01) ver 0.20.14 (2018/01/01)
* database * database
- simple: fix file corruption in the presence of mount points - simple: fix file corruption in the presence of mount points

View File

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

View File

@@ -65,7 +65,9 @@ class AndroidNdkToolchain:
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch) llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
llvm_triple = 'armv7-none-linux-androideabi' llvm_triple = 'armv7-none-linux-androideabi'
common_flags = '-march=armv7-a -mfloat-abi=softfp' common_flags = '-Os -g'
common_flags += ' -fPIC'
common_flags += ' -march=armv7-a -mfloat-abi=softfp'
toolchain_bin = os.path.join(toolchain_path, 'bin') toolchain_bin = os.path.join(toolchain_path, 'bin')
llvm_bin = os.path.join(llvm_path, 'bin') llvm_bin = os.path.join(llvm_path, 'bin')
@@ -73,13 +75,15 @@ class AndroidNdkToolchain:
self.cxx = os.path.join(llvm_bin, 'clang++') self.cxx = os.path.join(llvm_bin, 'clang++')
common_flags += ' -target ' + llvm_triple + ' -integrated-as -gcc-toolchain ' + toolchain_path common_flags += ' -target ' + llvm_triple + ' -integrated-as -gcc-toolchain ' + toolchain_path
common_flags += ' -fvisibility=hidden -fdata-sections -ffunction-sections'
self.ar = os.path.join(toolchain_bin, arch + '-ar') self.ar = os.path.join(toolchain_bin, arch + '-ar')
self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib') self.ranlib = os.path.join(toolchain_bin, arch + '-ranlib')
self.nm = os.path.join(toolchain_bin, arch + '-nm') self.nm = os.path.join(toolchain_bin, arch + '-nm')
self.strip = os.path.join(toolchain_bin, arch + '-strip') self.strip = os.path.join(toolchain_bin, arch + '-strip')
self.cflags = '-Os -g ' + common_flags self.cflags = common_flags
self.cxxflags = '-Os -g ' + common_flags self.cxxflags = common_flags
self.cppflags = '--sysroot=' + sysroot + \ self.cppflags = '--sysroot=' + sysroot + \
' -isystem ' + os.path.join(install_prefix, 'include') + \ ' -isystem ' + os.path.join(install_prefix, 'include') + \
' -isystem ' + os.path.join(sysroot, 'usr', 'include', arch) + \ ' -isystem ' + os.path.join(sysroot, 'usr', 'include', arch) + \
@@ -98,15 +102,13 @@ class AndroidNdkToolchain:
libcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/llvm-libc++') libcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/llvm-libc++')
libcxx_libs_path = os.path.join(libcxx_path, 'libs', android_abi) 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_flags = '-stdlib=libc++'
libstdcxx_ldadd = os.path.join(libcxx_libs_path, 'libc++_static.a') + ' ' + os.path.join(libcxx_libs_path, 'libc++abi.a') libstdcxx_cxxflags = libstdcxx_flags + ' -isystem ' + os.path.join(libcxx_path, 'include') + ' -isystem ' + os.path.join(ndk_path, 'sources/android/support/include')
libstdcxx_ldflags = libstdcxx_flags + ' -static-libstdc++ -L' + libcxx_libs_path
if self.is_armv7:
libstdcxx_ldadd += ' ' + os.path.join(libcxx_libs_path, 'libunwind.a')
if use_cxx: if use_cxx:
self.libs += ' ' + libstdcxx_ldadd self.cxxflags += ' ' + libstdcxx_cxxflags
self.cppflags += ' ' + libstdcxx_cppflags self.ldflags += ' ' + libstdcxx_ldflags
self.env = dict(os.environ) self.env = dict(os.environ)

View File

@@ -1,10 +1,10 @@
AC_PREREQ(2.60) AC_PREREQ(2.60)
AC_INIT(mpd, 0.20.14, musicpd-dev-team@lists.sourceforge.net) AC_INIT(mpd, 0.20.16, musicpd-dev-team@lists.sourceforge.net)
VERSION_MAJOR=0 VERSION_MAJOR=0
VERSION_MINOR=20 VERSION_MINOR=20
VERSION_REVISION=14 VERSION_REVISION=16
VERSION_EXTRA=0 VERSION_EXTRA=0
AC_CONFIG_SRCDIR([src/Main.cxx]) AC_CONFIG_SRCDIR([src/Main.cxx])

View File

@@ -1,21 +1,30 @@
import os.path, subprocess import os.path, subprocess, sys
from build.project import Project from build.makeproject import MakeProject
class AutotoolsProject(Project): class AutotoolsProject(MakeProject):
def __init__(self, url, md5, installed, configure_args=[], def __init__(self, url, md5, installed, configure_args=[],
autogen=False, autogen=False,
cppflags='', cppflags='',
ldflags='',
libs='',
subdirs=None,
**kwargs): **kwargs):
Project.__init__(self, url, md5, installed, **kwargs) MakeProject.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args self.configure_args = configure_args
self.autogen = autogen self.autogen = autogen
self.cppflags = cppflags self.cppflags = cppflags
self.ldflags = ldflags
self.libs = libs
self.subdirs = subdirs
def build(self, toolchain): def configure(self, toolchain):
src = self.unpack(toolchain) src = self.unpack(toolchain)
if self.autogen: if self.autogen:
subprocess.check_call(['libtoolize', '--force'], cwd=src) if sys.platform == 'darwin':
subprocess.check_call(['glibtoolize', '--force'], cwd=src)
else:
subprocess.check_call(['libtoolize', '--force'], cwd=src)
subprocess.check_call(['aclocal'], cwd=src) subprocess.check_call(['aclocal'], cwd=src)
subprocess.check_call(['automake', '--add-missing', '--force-missing', '--foreign'], cwd=src) subprocess.check_call(['automake', '--add-missing', '--force-missing', '--foreign'], cwd=src)
subprocess.check_call(['autoconf'], cwd=src) subprocess.check_call(['autoconf'], cwd=src)
@@ -29,8 +38,8 @@ class AutotoolsProject(Project):
'CFLAGS=' + toolchain.cflags, 'CFLAGS=' + toolchain.cflags,
'CXXFLAGS=' + toolchain.cxxflags, 'CXXFLAGS=' + toolchain.cxxflags,
'CPPFLAGS=' + toolchain.cppflags + ' ' + self.cppflags, 'CPPFLAGS=' + toolchain.cppflags + ' ' + self.cppflags,
'LDFLAGS=' + toolchain.ldflags, 'LDFLAGS=' + toolchain.ldflags + ' ' + self.ldflags,
'LIBS=' + toolchain.libs, 'LIBS=' + toolchain.libs + ' ' + self.libs,
'AR=' + toolchain.ar, 'AR=' + toolchain.ar,
'RANLIB=' + toolchain.ranlib, 'RANLIB=' + toolchain.ranlib,
'STRIP=' + toolchain.strip, 'STRIP=' + toolchain.strip,
@@ -40,7 +49,12 @@ class AutotoolsProject(Project):
] + self.configure_args ] + self.configure_args
subprocess.check_call(configure, cwd=build, env=toolchain.env) subprocess.check_call(configure, cwd=build, env=toolchain.env)
subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'], return build
cwd=build, env=toolchain.env)
subprocess.check_call(['/usr/bin/make', '--quiet', 'install'], def build(self, toolchain):
cwd=build, env=toolchain.env) build = self.configure(toolchain)
if self.subdirs is not None:
for subdir in self.subdirs:
MakeProject.build(self, toolchain, os.path.join(build, subdir))
else:
MakeProject.build(self, toolchain, build)

View File

@@ -1,3 +1,4 @@
import re
from build.project import Project from build.project import Project
from build.zlib import ZlibProject from build.zlib import ZlibProject
from build.autotools import AutotoolsProject from build.autotools import AutotoolsProject
@@ -5,24 +6,35 @@ from build.ffmpeg import FfmpegProject
from build.boost import BoostProject from build.boost import BoostProject
libogg = AutotoolsProject( libogg = AutotoolsProject(
'http://downloads.xiph.org/releases/ogg/libogg-1.3.2.tar.xz', 'http://downloads.xiph.org/releases/ogg/libogg-1.3.3.tar.xz',
'5c3a34309d8b98640827e5d0991a4015', '4f3fc6178a533d392064f14776b23c397ed4b9f48f5de297aba73b643f955c08',
'lib/libogg.a', 'lib/libogg.a',
['--disable-shared', '--enable-static'], [
'--disable-shared', '--enable-static',
],
) )
libvorbis = AutotoolsProject( libvorbis = AutotoolsProject(
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.5.tar.xz', 'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.5.tar.xz',
'28cb28097c07a735d6af56e598e1c90f', '28cb28097c07a735d6af56e598e1c90f',
'lib/libvorbis.a', 'lib/libvorbis.a',
['--disable-shared', '--enable-static'], [
'--disable-shared', '--enable-static',
],
) )
opus = AutotoolsProject( opus = AutotoolsProject(
'https://archive.mozilla.org/pub/opus/opus-1.2.1.tar.gz', 'https://archive.mozilla.org/pub/opus/opus-1.2.1.tar.gz',
'cfafd339ccd9c5ef8d6ab15d7e1a412c054bf4cb4ecbbbcc78c12ef2def70732', 'cfafd339ccd9c5ef8d6ab15d7e1a412c054bf4cb4ecbbbcc78c12ef2def70732',
'lib/libopus.a', 'lib/libopus.a',
['--disable-shared', '--enable-static'], [
'--disable-shared', '--enable-static',
'--disable-doc',
'--disable-extra-programs',
],
# suppress "visibility default" from opus_defines.h
cppflags='-DOPUS_EXPORT=',
) )
flac = AutotoolsProject( flac = AutotoolsProject(
@@ -32,7 +44,9 @@ flac = AutotoolsProject(
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
'--disable-xmms-plugin', '--disable-cpplibs', '--disable-xmms-plugin', '--disable-cpplibs',
'--disable-doxygen-docs',
], ],
subdirs=['include', 'src/libFLAC'],
) )
zlib = ZlibProject( zlib = ZlibProject(
@@ -45,21 +59,36 @@ libid3tag = AutotoolsProject(
'ftp://ftp.mars.org/pub/mpeg/libid3tag-0.15.1b.tar.gz', 'ftp://ftp.mars.org/pub/mpeg/libid3tag-0.15.1b.tar.gz',
'e5808ad997ba32c498803822078748c3', 'e5808ad997ba32c498803822078748c3',
'lib/libid3tag.a', 'lib/libid3tag.a',
['--disable-shared', '--enable-static'], [
'--disable-shared', '--enable-static',
# without this, libid3tag's configure.ac ignores -O* and -f*
'--disable-debugging',
],
autogen=True, autogen=True,
edits={
# fix bug in libid3tag's configure.ac which discards all but the last optimization flag
'configure.ac': lambda data: re.sub(r'optimize="\$1"', r'optimize="$optimize $1"', data, count=1),
}
) )
libmad = AutotoolsProject( libmad = AutotoolsProject(
'ftp://ftp.mars.org/pub/mpeg/libmad-0.15.1b.tar.gz', 'ftp://ftp.mars.org/pub/mpeg/libmad-0.15.1b.tar.gz',
'1be543bc30c56fb6bea1d7bf6a64e66c', '1be543bc30c56fb6bea1d7bf6a64e66c',
'lib/libmad.a', 'lib/libmad.a',
['--disable-shared', '--enable-static'], [
'--disable-shared', '--enable-static',
# without this, libmad's configure.ac ignores -O* and -f*
'--disable-debugging',
],
autogen=True, autogen=True,
) )
liblame = AutotoolsProject( liblame = AutotoolsProject(
'http://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz', 'http://downloads.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz',
'24346b4158e4af3bd9f2e194bb23eb473c75fb7377011523353196b19b9a23ff', 'ddfe36cab873794038ae2c1210557ad34857a4b6bdc515785d1da9e175b1da1e',
'lib/libmp3lame.a', 'lib/libmp3lame.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
@@ -89,21 +118,229 @@ ffmpeg = FfmpegProject(
'--disable-pixelutils', '--disable-pixelutils',
'--disable-network', '--disable-network',
'--disable-encoders', '--disable-encoders',
'--disable-muxers',
'--disable-protocols', '--disable-protocols',
'--disable-devices', '--disable-devices',
'--disable-filters', '--disable-filters',
'--disable-filters',
'--disable-v4l2_m2m', '--disable-v4l2_m2m',
# clang misinterprets the "B0" in hevc_mvs.c as binary '--disable-parser=bmp',
# literal, which breaks the build; but we don't need that '--disable-parser=cavsvideo',
# video codec anyway '--disable-parser=dvbsub',
'--disable-parser=dvdsub',
'--disable-parser=dvd_nav',
'--disable-parser=flac',
'--disable-parser=g729',
'--disable-parser=gsm',
'--disable-parser=h261',
'--disable-parser=h263',
'--disable-parser=h264',
'--disable-parser=hevc',
'--disable-parser=mjpeg',
'--disable-parser=mlp',
'--disable-parser=mpeg4video',
'--disable-parser=mpegaudio',
'--disable-parser=mpegvideo',
'--disable-parser=opus',
'--disable-parser=vc1',
'--disable-parser=vp3',
'--disable-parser=vp8',
'--disable-parser=vp9',
'--disable-parser=png',
'--disable-parser=pnm',
'--disable-parser=xma',
'--disable-demuxer=aqtitle',
'--disable-demuxer=ass',
'--disable-demuxer=bethsoftvid',
'--disable-demuxer=bink',
'--disable-demuxer=cavsvideo',
'--disable-demuxer=cdxl',
'--disable-demuxer=dvbsub',
'--disable-demuxer=dvbtxt',
'--disable-demuxer=h261',
'--disable-demuxer=h263',
'--disable-demuxer=h264',
'--disable-demuxer=ico',
'--disable-demuxer=image2',
'--disable-demuxer=jacosub',
'--disable-demuxer=lrc',
'--disable-demuxer=microdvd',
'--disable-demuxer=mjpeg',
'--disable-demuxer=mjpeg_2000',
'--disable-demuxer=mpegps',
'--disable-demuxer=mpegvideo',
'--disable-demuxer=mpl2',
'--disable-demuxer=mpsub',
'--disable-demuxer=pjs',
'--disable-demuxer=rawvideo',
'--disable-demuxer=realtext',
'--disable-demuxer=sami',
'--disable-demuxer=scc',
'--disable-demuxer=srt',
'--disable-demuxer=stl',
'--disable-demuxer=subviewer',
'--disable-demuxer=subviewer1',
'--disable-demuxer=swf',
'--disable-demuxer=tedcaptions',
'--disable-demuxer=vobsub',
'--disable-demuxer=vplayer',
'--disable-demuxer=webvtt',
'--disable-demuxer=yuv4mpegpipe',
# we don't need these decoders, because we have the dedicated
# libraries
'--disable-decoder=flac',
'--disable-decoder=mp1',
'--disable-decoder=mp1float',
'--disable-decoder=mp2',
'--disable-decoder=mp2float',
'--disable-decoder=mp3',
'--disable-decoder=mp3adu',
'--disable-decoder=mp3adufloat',
'--disable-decoder=mp3float',
'--disable-decoder=mp3on4',
'--disable-decoder=mp3on4float',
'--disable-decoder=opus',
'--disable-decoder=vorbis',
# audio codecs nobody uses
'--disable-decoder=atrac1',
'--disable-decoder=atrac3',
'--disable-decoder=atrac3al',
'--disable-decoder=atrac3p',
'--disable-decoder=atrac3pal',
'--disable-decoder=binkaudio_dct',
'--disable-decoder=binkaudio_rdft',
'--disable-decoder=bmv_audio',
'--disable-decoder=dsicinaudio',
'--disable-decoder=dvaudio',
'--disable-decoder=metasound',
'--disable-decoder=paf_audio',
'--disable-decoder=ra_144',
'--disable-decoder=ra_288',
'--disable-decoder=ralf',
'--disable-decoder=qdm2',
'--disable-decoder=qdmc',
# disable lots of image and video codecs
'--disable-decoder=ass',
'--disable-decoder=asv1',
'--disable-decoder=asv2',
'--disable-decoder=apng',
'--disable-decoder=avrn',
'--disable-decoder=avrp',
'--disable-decoder=bethsoftvid',
'--disable-decoder=bink',
'--disable-decoder=bmp',
'--disable-decoder=bmv_video',
'--disable-decoder=cavs',
'--disable-decoder=ccaption',
'--disable-decoder=cdgraphics',
'--disable-decoder=clearvideo',
'--disable-decoder=dirac',
'--disable-decoder=dsicinvideo',
'--disable-decoder=dvbsub',
'--disable-decoder=dvdsub',
'--disable-decoder=dvvideo',
'--disable-decoder=exr',
'--disable-decoder=ffv1',
'--disable-decoder=ffvhuff',
'--disable-decoder=ffwavesynth',
'--disable-decoder=flic',
'--disable-decoder=flv',
'--disable-decoder=fraps',
'--disable-decoder=gif',
'--disable-decoder=h261',
'--disable-decoder=h263',
'--disable-decoder=h263i',
'--disable-decoder=h263p',
'--disable-decoder=h264',
'--disable-decoder=hevc', '--disable-decoder=hevc',
'--disable-decoder=hnm4_video',
'--disable-decoder=hq_hqa',
'--disable-decoder=hqx',
'--disable-decoder=idcin',
'--disable-decoder=iff_ilbm',
'--disable-decoder=indeo2',
'--disable-decoder=indeo3',
'--disable-decoder=indeo4',
'--disable-decoder=indeo5',
'--disable-decoder=interplay_video',
'--disable-decoder=jacosub',
'--disable-decoder=jpeg2000',
'--disable-decoder=jpegls',
'--disable-decoder=microdvd',
'--disable-decoder=mimic',
'--disable-decoder=mjpeg',
'--disable-decoder=mmvideo',
'--disable-decoder=mpl2',
'--disable-decoder=motionpixels',
'--disable-decoder=mpeg1video',
'--disable-decoder=mpeg2video',
'--disable-decoder=mpeg4',
'--disable-decoder=mpegvideo',
'--disable-decoder=mscc',
'--disable-decoder=msmpeg4_crystalhd',
'--disable-decoder=msmpeg4v1',
'--disable-decoder=msmpeg4v2',
'--disable-decoder=msmpeg4v3',
'--disable-decoder=msvideo1',
'--disable-decoder=mszh',
'--disable-decoder=mvc1',
'--disable-decoder=mvc2',
'--disable-decoder=on2avc',
'--disable-decoder=paf_video',
'--disable-decoder=png',
'--disable-decoder=qdraw',
'--disable-decoder=qpeg',
'--disable-decoder=rawvideo',
'--disable-decoder=realtext',
'--disable-decoder=roq',
'--disable-decoder=roq_dpcm',
'--disable-decoder=rscc',
'--disable-decoder=rv10',
'--disable-decoder=rv20',
'--disable-decoder=rv30',
'--disable-decoder=rv40',
'--disable-decoder=sami',
'--disable-decoder=sheervideo',
'--disable-decoder=snow',
'--disable-decoder=srt',
'--disable-decoder=stl',
'--disable-decoder=subrip',
'--disable-decoder=subviewer',
'--disable-decoder=subviewer1',
'--disable-decoder=svq1',
'--disable-decoder=svq3',
'--disable-decoder=tiff',
'--disable-decoder=mottiertexseqvideo',
'--disable-decoder=truemotion1',
'--disable-decoder=truemotion2',
'--disable-decoder=truemotion2rt',
'--disable-decoder=twinvq',
'--disable-decoder=utvideo',
'--disable-decoder=vc1',
'--disable-decoder=vmdvideo',
'--disable-decoder=vp3',
'--disable-decoder=vp5',
'--disable-decoder=vp6',
'--disable-decoder=vp7',
'--disable-decoder=vp8',
'--disable-decoder=vp9',
'--disable-decoder=vqa',
'--disable-decoder=webvtt',
'--disable-decoder=wmv1',
'--disable-decoder=wmv2',
'--disable-decoder=wmv3',
'--disable-decoder=yuv4',
], ],
) )
curl = AutotoolsProject( curl = AutotoolsProject(
'http://curl.haxx.se/download/curl-7.57.0.tar.xz', 'http://curl.haxx.se/download/curl-7.58.0.tar.xz',
'f5f6fd3c72b7b8389969f4fb671ed8532fa9b5bb7a5cae7ca89bc1cea45c7878', '6a813875243609eb75f37fa72044e4ad618b55ec15a4eafdac2df6a7e800e3e3',
'lib/libcurl.a', 'lib/libcurl.a',
[ [
'--disable-shared', '--enable-static', '--disable-shared', '--enable-static',
@@ -114,6 +351,7 @@ curl = AutotoolsProject(
'--disable-ldap', '--disable-ldaps', '--disable-ldap', '--disable-ldaps',
'--disable-rtsp', '--disable-proxy', '--disable-dict', '--disable-telnet', '--disable-rtsp', '--disable-proxy', '--disable-dict', '--disable-telnet',
'--disable-tftp', '--disable-pop3', '--disable-imap', '--disable-smtp', '--disable-tftp', '--disable-pop3', '--disable-imap', '--disable-smtp',
'--disable-smb',
'--disable-gopher', '--disable-gopher',
'--disable-manual', '--disable-manual',
'--disable-threaded-resolver', '--disable-verbose', '--disable-sspi', '--disable-threaded-resolver', '--disable-verbose', '--disable-sspi',
@@ -123,7 +361,7 @@ curl = AutotoolsProject(
) )
boost = BoostProject( boost = BoostProject(
'http://downloads.sourceforge.net/project/boost/boost/1.65.1/boost_1_65_1.tar.bz2', 'http://downloads.sourceforge.net/project/boost/boost/1.66.0/boost_1_66_0.tar.bz2',
'9807a5d16566c57fd74fb522764e0b134a8bbe6b6e8967b83afefd30dcd3be81', '5721818253e6a0989583192f96782c4a98eb6204965316df9f5ad75819225ca9',
'include/boost/version.hpp', 'include/boost/version.hpp',
) )

View File

@@ -0,0 +1,28 @@
import subprocess
from build.project import Project
class MakeProject(Project):
def __init__(self, url, md5, installed,
install_target='install',
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
self.install_target = install_target
def get_simultaneous_jobs(self):
return 12
def get_make_args(self, toolchain):
return ['--quiet', '-j' + str(self.get_simultaneous_jobs())]
def get_make_install_args(self, toolchain):
return ['--quiet', self.install_target]
def make(self, toolchain, wd, args):
subprocess.check_call(['/usr/bin/make'] + args,
cwd=wd, env=toolchain.env)
def build(self, toolchain, wd, install=True):
self.make(toolchain, wd, self.get_make_args(toolchain))
if install:
self.make(toolchain, wd, self.get_make_install_args(toolchain))

View File

@@ -7,6 +7,7 @@ from build.tar import untar
class Project: class Project:
def __init__(self, url, md5, installed, name=None, version=None, def __init__(self, url, md5, installed, name=None, version=None,
base=None, base=None,
edits=None,
use_cxx=False): use_cxx=False):
if base is None: if base is None:
basename = os.path.basename(url) basename = os.path.basename(url)
@@ -28,6 +29,7 @@ class Project:
self.md5 = md5 self.md5 = md5
self.installed = installed self.installed = installed
self.edits = edits
self.use_cxx = use_cxx self.use_cxx = use_cxx
def download(self, toolchain): def download(self, toolchain):
@@ -47,7 +49,18 @@ class Project:
parent_path = toolchain.src_path parent_path = toolchain.src_path
else: else:
parent_path = toolchain.build_path parent_path = toolchain.build_path
return untar(self.download(toolchain), parent_path, self.base) path = untar(self.download(toolchain), parent_path, self.base)
if self.edits is not None:
for filename, function in self.edits.items():
with open(os.path.join(path, filename), 'r+t') as f:
old_data = f.read()
new_data = function(old_data)
f.seek(0)
f.truncate(0)
f.write(new_data)
return path
def make_build_path(self, toolchain): def make_build_path(self, toolchain):
path = os.path.join(toolchain.build_path, self.base) path = os.path.join(toolchain.build_path, self.base)

View File

@@ -27,12 +27,17 @@
#include <assert.h> #include <assert.h>
static struct { static struct IOThread {
Mutex mutex; Mutex mutex;
Cond cond; Cond cond;
EventLoop *loop; EventLoop *loop;
Thread thread; Thread thread;
IOThread():thread(BIND_THIS_METHOD(Run)) {}
private:
void Run() noexcept;
} io; } io;
void void
@@ -44,15 +49,15 @@ io_thread_run(void)
io.loop->Run(); io.loop->Run();
} }
static void inline void
io_thread_func(gcc_unused void *arg) IOThread::Run() noexcept
{ {
SetThreadName("io"); SetThreadName("io");
/* lock+unlock to synchronize with io_thread_start(), to be /* lock+unlock to synchronize with io_thread_start(), to be
sure that io.thread is set */ sure that io.thread is set */
io.mutex.lock(); mutex.lock();
io.mutex.unlock(); mutex.unlock();
io_thread_run(); io_thread_run();
} }
@@ -73,7 +78,7 @@ io_thread_start()
assert(!io.thread.IsDefined()); assert(!io.thread.IsDefined());
const std::lock_guard<Mutex> protect(io.mutex); const std::lock_guard<Mutex> protect(io.mutex);
io.thread.Start(io_thread_func, nullptr); io.thread.Start();
} }
void void
@@ -103,8 +108,12 @@ io_thread_get() noexcept
return *io.loop; return *io.loop;
} }
#ifndef NDEBUG
bool bool
io_thread_inside() noexcept io_thread_inside() noexcept
{ {
return io.thread.IsInside(); return io.thread.IsInside();
} }
#endif

View File

@@ -20,6 +20,7 @@
#ifndef MPD_IO_THREAD_HXX #ifndef MPD_IO_THREAD_HXX
#define MPD_IO_THREAD_HXX #define MPD_IO_THREAD_HXX
#include "check.h"
#include "Compiler.h" #include "Compiler.h"
class EventLoop; class EventLoop;
@@ -53,6 +54,8 @@ gcc_const
EventLoop & EventLoop &
io_thread_get() noexcept; io_thread_get() noexcept;
#ifndef NDEBUG
/** /**
* Is the current thread the I/O thread? * Is the current thread the I/O thread?
*/ */
@@ -61,3 +64,5 @@ bool
io_thread_inside() noexcept; io_thread_inside() noexcept;
#endif #endif
#endif

View File

@@ -95,6 +95,7 @@ public:
*/ */
gcc_pure gcc_pure
const MusicChunk *Peek() const noexcept { const MusicChunk *Peek() const noexcept {
const std::lock_guard<Mutex> protect(mutex);
return head; return head;
} }
@@ -120,6 +121,7 @@ public:
*/ */
gcc_pure gcc_pure
unsigned GetSize() const noexcept { unsigned GetSize() const noexcept {
const std::lock_guard<Mutex> protect(mutex);
return size; return size;
} }

View File

@@ -24,6 +24,8 @@
#include "tag/Tag.hxx" #include "tag/Tag.hxx"
#include "util/ConstBuffer.hxx" #include "util/ConstBuffer.hxx"
#include "util/StringAPI.hxx" #include "util/StringAPI.hxx"
#include "util/StringCompare.hxx"
#include "util/StringView.hxx"
#include "util/ASCII.hxx" #include "util/ASCII.hxx"
#include "util/TimeParser.hxx" #include "util/TimeParser.hxx"
#include "util/UriUtil.hxx" #include "util/UriUtil.hxx"
@@ -59,7 +61,7 @@ locate_parse_type(const char *str) noexcept
SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case) SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
:tag(_tag), :tag(_tag),
value(AllocatedString<>::Duplicate(_value)), value(_value),
fold_case(_fold_case ? IcuCompare(value.c_str()) : IcuCompare()) fold_case(_fold_case ? IcuCompare(value.c_str()) : IcuCompare())
{ {
} }
@@ -274,3 +276,33 @@ SongFilter::GetBase() const noexcept
return nullptr; return nullptr;
} }
SongFilter
SongFilter::WithoutBasePrefix(const char *_prefix) const noexcept
{
const StringView prefix(_prefix);
SongFilter result;
for (const auto &i : items) {
if (i.GetTag() == LOCATE_TAG_BASE_TYPE) {
const char *s = StringAfterPrefix(i.GetValue(), prefix);
if (s != nullptr) {
if (*s == 0)
continue;
if (*s == '/') {
++s;
if (*s != 0)
result.items.emplace_back(LOCATE_TAG_BASE_TYPE, s);
continue;
}
}
}
result.items.emplace_back(i);
}
return result;
}

View File

@@ -21,9 +21,9 @@
#define MPD_SONG_FILTER_HXX #define MPD_SONG_FILTER_HXX
#include "lib/icu/Compare.hxx" #include "lib/icu/Compare.hxx"
#include "util/AllocatedString.hxx"
#include "Compiler.h" #include "Compiler.h"
#include <string>
#include <list> #include <list>
#include <stdint.h> #include <stdint.h>
@@ -49,7 +49,7 @@ public:
class Item { class Item {
uint8_t tag; uint8_t tag;
AllocatedString<> value; std::string value;
/** /**
* This value is only set if case folding is enabled. * This value is only set if case folding is enabled.
@@ -66,11 +66,6 @@ public:
Item(unsigned tag, const char *value, bool fold_case=false); Item(unsigned tag, const char *value, bool fold_case=false);
Item(unsigned tag, time_t time); Item(unsigned tag, time_t time);
Item(const Item &other) = delete;
Item(Item &&) = default;
Item &operator=(const Item &other) = delete;
unsigned GetTag() const { unsigned GetTag() const {
return tag; return tag;
} }
@@ -157,6 +152,13 @@ public:
*/ */
gcc_pure gcc_pure
const char *GetBase() const noexcept; const char *GetBase() const noexcept;
/**
* Create a copy of the filter with the given prefix stripped
* from all #LOCATE_TAG_BASE_TYPE items. This is used to
* filter songs in mounted databases.
*/
SongFilter WithoutBasePrefix(const char *prefix) const noexcept;
}; };
/** /**

View File

@@ -115,7 +115,12 @@ Iso9660ArchiveFile::Visit(char *path, size_t length, size_t capacity,
visitor.VisitArchiveEntry(path + 1); visitor.VisitArchiveEntry(path + 1);
} }
} }
#if LIBCDIO_VERSION_NUM >= 20000
iso9660_filelist_free(entlist);
#else
_cdio_list_free (entlist, true); _cdio_list_free (entlist, true);
#endif
} }
static ArchiveFile * static ArchiveFile *

View File

@@ -20,18 +20,12 @@
#include "config.h" #include "config.h"
#include "Mount.hxx" #include "Mount.hxx"
#include "PrefixedLightSong.hxx" #include "PrefixedLightSong.hxx"
#include "SongFilter.hxx"
#include "db/Selection.hxx" #include "db/Selection.hxx"
#include "db/LightDirectory.hxx" #include "db/LightDirectory.hxx"
#include "db/Interface.hxx" #include "db/Interface.hxx"
#include "fs/Traits.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> #include <string>
struct PrefixedLightDirectory : LightDirectory { struct PrefixedLightDirectory : LightDirectory {
@@ -93,5 +87,16 @@ WalkMount(const char *base, const Database &db,
vp = std::bind(PrefixVisitPlaylist, vp = std::bind(PrefixVisitPlaylist,
base, std::ref(visit_playlist), _1, _2); base, std::ref(visit_playlist), _1, _2);
SongFilter prefix_filter;
if (base != nullptr && filter != nullptr) {
/* if the SongFilter contains a LOCATE_TAG_BASE_TYPE
item, copy the SongFilter and drop the mount point
from the filter, because the mounted database
doesn't know its own location within MPD's VFS */
prefix_filter = filter->WithoutBasePrefix(base);
filter = &prefix_filter;
}
db.Visit(DatabaseSelection(uri, recursive, filter), vd, vs, vp); db.Visit(DatabaseSelection(uri, recursive, filter), vd, vs, vp);
} }

View File

@@ -49,6 +49,10 @@ struct UpdateQueueItem {
bool IsDefined() const { bool IsDefined() const {
return id != 0; return id != 0;
} }
void Clear() {
id = 0;
}
}; };
class UpdateQueue { class UpdateQueue {

View File

@@ -43,6 +43,7 @@ UpdateService::UpdateService(EventLoop &_loop, SimpleDatabase &_db,
:DeferredMonitor(_loop), :DeferredMonitor(_loop),
db(_db), storage(_storage), db(_db), storage(_storage),
listener(_listener), listener(_listener),
update_thread(BIND_THIS_METHOD(Task)),
update_task_id(0), update_task_id(0),
walk(nullptr) walk(nullptr)
{ {
@@ -140,13 +141,6 @@ UpdateService::Task()
DeferredMonitor::Schedule(); DeferredMonitor::Schedule();
} }
void
UpdateService::Task(void *ctx)
{
UpdateService &service = *(UpdateService *)ctx;
return service.Task();
}
void void
UpdateService::StartThread(UpdateQueueItem &&i) UpdateService::StartThread(UpdateQueueItem &&i)
{ {
@@ -158,7 +152,7 @@ UpdateService::StartThread(UpdateQueueItem &&i)
next = std::move(i); next = std::move(i);
walk = new UpdateWalk(GetEventLoop(), listener, *next.storage); walk = new UpdateWalk(GetEventLoop(), listener, *next.storage);
update_thread.Start(Task, this); update_thread.Start();
FormatDebug(update_domain, FormatDebug(update_domain,
"spawned thread for update job id %i", next.id); "spawned thread for update job id %i", next.id);
@@ -258,7 +252,7 @@ UpdateService::RunDeferred()
delete walk; delete walk;
walk = nullptr; walk = nullptr;
next = UpdateQueueItem(); next.Clear();
idle_add(IDLE_UPDATE); idle_add(IDLE_UPDATE);

View File

@@ -98,7 +98,6 @@ private:
/* the update thread */ /* the update thread */
void Task(); void Task();
static void Task(void *ctx);
void StartThread(UpdateQueueItem &&i); void StartThread(UpdateQueueItem &&i);

View File

@@ -30,7 +30,8 @@
DecoderControl::DecoderControl(Mutex &_mutex, Cond &_client_cond, DecoderControl::DecoderControl(Mutex &_mutex, Cond &_client_cond,
const AudioFormat _configured_audio_format, const AudioFormat _configured_audio_format,
const ReplayGainConfig &_replay_gain_config) const ReplayGainConfig &_replay_gain_config)
:mutex(_mutex), client_cond(_client_cond), :thread(BIND_THIS_METHOD(RunThread)),
mutex(_mutex), client_cond(_client_cond),
configured_audio_format(_configured_audio_format), configured_audio_format(_configured_audio_format),
replay_gain_config(_replay_gain_config) {} replay_gain_config(_replay_gain_config) {}

View File

@@ -415,6 +415,9 @@ public:
* mixramp_start/mixramp_end. * mixramp_start/mixramp_end.
*/ */
void CycleMixRamp(); void CycleMixRamp();
private:
void RunThread();
}; };
#endif #endif

View File

@@ -513,30 +513,28 @@ try {
dc.client_cond.signal(); dc.client_cond.signal();
} }
static void void
decoder_task(void *arg) DecoderControl::RunThread()
{ {
DecoderControl &dc = *(DecoderControl *)arg;
SetThreadName("decoder"); SetThreadName("decoder");
const std::lock_guard<Mutex> protect(dc.mutex); const std::lock_guard<Mutex> protect(mutex);
do { do {
assert(dc.state == DecoderState::STOP || assert(state == DecoderState::STOP ||
dc.state == DecoderState::ERROR); state == DecoderState::ERROR);
switch (dc.command) { switch (command) {
case DecoderCommand::START: case DecoderCommand::START:
dc.CycleMixRamp(); CycleMixRamp();
dc.replay_gain_prev_db = dc.replay_gain_db; replay_gain_prev_db = replay_gain_db;
dc.replay_gain_db = 0; replay_gain_db = 0;
decoder_run(dc); decoder_run(*this);
if (dc.state == DecoderState::ERROR) { if (state == DecoderState::ERROR) {
try { try {
std::rethrow_exception(dc.error); std::rethrow_exception(error);
} catch (const std::exception &e) { } catch (const std::exception &e) {
LogError(e); LogError(e);
} catch (...) { } catch (...) {
@@ -552,20 +550,20 @@ decoder_task(void *arg)
/* we need to clear the pipe here; usually the /* we need to clear the pipe here; usually the
PlayerThread is responsible, but it is not PlayerThread is responsible, but it is not
aware that the decoder has finished */ aware that the decoder has finished */
dc.pipe->Clear(*dc.buffer); pipe->Clear(*buffer);
decoder_run(dc); decoder_run(*this);
break; break;
case DecoderCommand::STOP: case DecoderCommand::STOP:
dc.CommandFinishedLocked(); CommandFinishedLocked();
break; break;
case DecoderCommand::NONE: case DecoderCommand::NONE:
dc.Wait(); Wait();
break; break;
} }
} while (dc.command != DecoderCommand::NONE || !dc.quit); } while (command != DecoderCommand::NONE || !quit);
} }
void void
@@ -574,5 +572,5 @@ decoder_thread_start(DecoderControl &dc)
assert(!dc.thread.IsDefined()); assert(!dc.thread.IsDefined());
dc.quit = false; dc.quit = false;
dc.thread.Start(decoder_task, &dc); dc.thread.Start();
} }

View File

@@ -27,7 +27,7 @@
#include <algorithm> #include <algorithm>
EventLoop::EventLoop() EventLoop::EventLoop()
:SocketMonitor(*this) :SocketMonitor(*this), quit(false)
{ {
SocketMonitor::Open(wake_fd.Get()); SocketMonitor::Open(wake_fd.Get());
SocketMonitor::Schedule(SocketMonitor::READ); SocketMonitor::Schedule(SocketMonitor::READ);
@@ -46,7 +46,9 @@ EventLoop::~EventLoop()
void void
EventLoop::Break() EventLoop::Break()
{ {
quit = true; if (quit.exchange(true))
return;
wake_fd.Write(); wake_fd.Write();
} }

View File

@@ -30,6 +30,7 @@
#include "SocketMonitor.hxx" #include "SocketMonitor.hxx"
#include <chrono> #include <chrono>
#include <atomic>
#include <list> #include <list>
#include <set> #include <set>
@@ -82,7 +83,7 @@ class EventLoop final : SocketMonitor
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
bool quit = false; std::atomic_bool quit;
/** /**
* True when the object has been modified and another check is * True when the object has been modified and another check is

View File

@@ -73,6 +73,10 @@ public:
return filter; return filter;
} }
void Reset() override {
filter->Reset();
}
ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override { ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override {
return filter->FilterPCM(src); return filter->FilterPCM(src);
} }

View File

@@ -54,10 +54,10 @@ ThreadInputStream::Start()
assert(p != nullptr); assert(p != nullptr);
buffer = new CircularBuffer<uint8_t>((uint8_t *)p, buffer_size); buffer = new CircularBuffer<uint8_t>((uint8_t *)p, buffer_size);
thread.Start(ThreadFunc, this); thread.Start();
} }
inline void void
ThreadInputStream::ThreadFunc() ThreadInputStream::ThreadFunc()
{ {
FormatThreadName("input:%s", plugin); FormatThreadName("input:%s", plugin);
@@ -107,13 +107,6 @@ ThreadInputStream::ThreadFunc()
Close(); Close();
} }
void
ThreadInputStream::ThreadFunc(void *ctx)
{
ThreadInputStream &tis = *(ThreadInputStream *)ctx;
tis.ThreadFunc();
}
void void
ThreadInputStream::Check() ThreadInputStream::Check()
{ {

View File

@@ -73,6 +73,7 @@ public:
size_t _buffer_size) size_t _buffer_size)
:InputStream(_uri, _mutex, _cond), :InputStream(_uri, _mutex, _cond),
plugin(_plugin), plugin(_plugin),
thread(BIND_THIS_METHOD(ThreadFunc)),
buffer_size(_buffer_size) {} buffer_size(_buffer_size) {}
virtual ~ThreadInputStream(); virtual ~ThreadInputStream();
@@ -138,7 +139,6 @@ protected:
private: private:
void ThreadFunc(); void ThreadFunc();
static void ThreadFunc(void *ctx);
}; };
#endif #endif

View File

@@ -37,6 +37,18 @@ public:
explicit IcuCompare(const char *needle) noexcept; explicit IcuCompare(const char *needle) noexcept;
IcuCompare(const IcuCompare &src) noexcept
:needle(src
? AllocatedString<>::Duplicate(src.needle.c_str())
: nullptr) {}
IcuCompare &operator=(const IcuCompare &src) noexcept {
needle = src
? AllocatedString<>::Duplicate(src.needle.c_str())
: nullptr;
return *this;
}
IcuCompare(IcuCompare &&) = default; IcuCompare(IcuCompare &&) = default;
IcuCompare &operator=(IcuCompare &&) = default; IcuCompare &operator=(IcuCompare &&) = default;

View File

@@ -34,7 +34,11 @@ static unsigned upnp_ref;
static void static void
DoInit() DoInit()
{ {
auto code = UpnpInit(0, 0); #ifdef UPNP_ENABLE_IPV6
auto code = UpnpInit2(nullptr, 0);
#else
auto code = UpnpInit(nullptr, 0);
#endif
if (code != UPNP_E_SUCCESS) if (code != UPNP_E_SUCCESS)
throw FormatRuntimeError("UpnpInit() failed: %s", throw FormatRuntimeError("UpnpInit() failed: %s",
UpnpGetErrorMessage(code)); UpnpGetErrorMessage(code));

View File

@@ -69,7 +69,8 @@ class SmbclientNeighborExplorer final : public NeighborExplorer {
public: public:
SmbclientNeighborExplorer(NeighborListener &_listener) SmbclientNeighborExplorer(NeighborListener &_listener)
:NeighborExplorer(_listener) {} :NeighborExplorer(_listener),
thread(BIND_THIS_METHOD(ThreadFunc)) {}
/* virtual methods from class NeighborExplorer */ /* virtual methods from class NeighborExplorer */
void Open() override; void Open() override;
@@ -79,14 +80,13 @@ public:
private: private:
void Run(); void Run();
void ThreadFunc(); void ThreadFunc();
static void ThreadFunc(void *ctx);
}; };
void void
SmbclientNeighborExplorer::Open() SmbclientNeighborExplorer::Open()
{ {
quit = false; quit = false;
thread.Start(ThreadFunc, this); thread.Start();
} }
void void
@@ -239,6 +239,8 @@ SmbclientNeighborExplorer::Run()
inline void inline void
SmbclientNeighborExplorer::ThreadFunc() SmbclientNeighborExplorer::ThreadFunc()
{ {
SetThreadName("smbclient");
mutex.lock(); mutex.lock();
while (!quit) { while (!quit) {
@@ -257,15 +259,6 @@ SmbclientNeighborExplorer::ThreadFunc()
mutex.unlock(); mutex.unlock();
} }
void
SmbclientNeighborExplorer::ThreadFunc(void *ctx)
{
SetThreadName("smbclient");
SmbclientNeighborExplorer &e = *(SmbclientNeighborExplorer *)ctx;
e.ThreadFunc();
}
static NeighborExplorer * static NeighborExplorer *
smbclient_neighbor_create(gcc_unused EventLoop &loop, smbclient_neighbor_create(gcc_unused EventLoop &loop,
NeighborListener &listener, NeighborListener &listener,

View File

@@ -51,7 +51,8 @@
AudioOutput::AudioOutput(const AudioOutputPlugin &_plugin, AudioOutput::AudioOutput(const AudioOutputPlugin &_plugin,
const ConfigBlock &block) const ConfigBlock &block)
:plugin(_plugin) :plugin(_plugin),
thread(BIND_THIS_METHOD(Task))
{ {
assert(plugin.finish != nullptr); assert(plugin.finish != nullptr);
assert(plugin.open != nullptr); assert(plugin.open != nullptr);

View File

@@ -515,7 +515,6 @@ private:
* The OutputThread. * The OutputThread.
*/ */
void Task(); void Task();
static void Task(void *arg);
}; };
/** /**

View File

@@ -396,7 +396,7 @@ AudioOutput::Pause()
pause = false; pause = false;
} }
inline void void
AudioOutput::Task() AudioOutput::Task()
{ {
FormatThreadName("output:%s", name); FormatThreadName("output:%s", name);
@@ -512,17 +512,10 @@ AudioOutput::Task()
} }
} }
void
AudioOutput::Task(void *arg)
{
AudioOutput *ao = (AudioOutput *)arg;
ao->Task();
}
void void
AudioOutput::StartThread() AudioOutput::StartThread()
{ {
assert(command == Command::NONE); assert(command == Command::NONE);
thread.Start(Task, this); thread.Start();
} }

View File

@@ -77,7 +77,6 @@ public:
void Close(); void Close();
size_t Play(const void *chunk, size_t size); size_t Play(const void *chunk, size_t size);
void Cancel();
std::chrono::steady_clock::duration Delay() noexcept; std::chrono::steady_clock::duration Delay() noexcept;

View File

@@ -27,6 +27,7 @@
#include "../Wrapper.hxx" #include "../Wrapper.hxx"
#include "mixer/MixerList.hxx" #include "mixer/MixerList.hxx"
#include "mixer/plugins/PulseMixerPlugin.hxx" #include "mixer/plugins/PulseMixerPlugin.hxx"
#include "util/ScopeExit.hxx"
#include "Log.hxx" #include "Log.hxx"
#include <pulse/thread-mainloop.h> #include <pulse/thread-mainloop.h>
@@ -854,7 +855,10 @@ PulseOutput::TestDefaultDevice()
try { try {
const ConfigBlock empty; const ConfigBlock empty;
PulseOutput po(empty); PulseOutput po(empty);
po.Enable();
AtScopeExit(&po) { po.Disable(); };
po.WaitConnection(); po.WaitConnection();
return true; return true;
} catch (const std::runtime_error &e) { } catch (const std::runtime_error &e) {
return false; return false;

View File

@@ -139,6 +139,14 @@ SoxrPcmResampler::Close()
soxr_delete(soxr); soxr_delete(soxr);
} }
void
SoxrPcmResampler::Reset()
{
#if SOXR_THIS_VERSION >= SOXR_VERSION(0,1,2)
soxr_clear(soxr);
#endif
}
ConstBuffer<void> ConstBuffer<void>
SoxrPcmResampler::Resample(ConstBuffer<void> src) SoxrPcmResampler::Resample(ConstBuffer<void> src)
{ {

View File

@@ -41,6 +41,7 @@ class SoxrPcmResampler final : public PcmResampler {
public: public:
AudioFormat Open(AudioFormat &af, unsigned new_sample_rate) override; AudioFormat Open(AudioFormat &af, unsigned new_sample_rate) override;
void Close() override; void Close() override;
void Reset() override;
ConstBuffer<void> Resample(ConstBuffer<void> src) override; ConstBuffer<void> Resample(ConstBuffer<void> src) override;
}; };

View File

@@ -37,6 +37,7 @@ PlayerControl::PlayerControl(PlayerListener &_listener,
buffer_chunks(_buffer_chunks), buffer_chunks(_buffer_chunks),
buffered_before_play(_buffered_before_play), buffered_before_play(_buffered_before_play),
configured_audio_format(_configured_audio_format), configured_audio_format(_configured_audio_format),
thread(BIND_THIS_METHOD(RunThread)),
replay_gain_config(_replay_gain_config) replay_gain_config(_replay_gain_config)
{ {
} }

View File

@@ -537,6 +537,9 @@ public:
void ApplyEnabled() override { void ApplyEnabled() override {
LockUpdateAudio(); LockUpdateAudio();
} }
private:
void RunThread();
}; };
#endif #endif

View File

@@ -1147,91 +1147,89 @@ do_play(PlayerControl &pc, DecoderControl &dc,
player.Run(); player.Run();
} }
static void void
player_task(void *arg) PlayerControl::RunThread()
{ {
PlayerControl &pc = *(PlayerControl *)arg;
SetThreadName("player"); SetThreadName("player");
DecoderControl dc(pc.mutex, pc.cond, DecoderControl dc(mutex, cond,
pc.configured_audio_format, configured_audio_format,
pc.replay_gain_config); replay_gain_config);
decoder_thread_start(dc); decoder_thread_start(dc);
MusicBuffer buffer(pc.buffer_chunks); MusicBuffer buffer(buffer_chunks);
pc.Lock(); Lock();
while (1) { while (1) {
switch (pc.command) { switch (command) {
case PlayerCommand::SEEK: case PlayerCommand::SEEK:
case PlayerCommand::QUEUE: case PlayerCommand::QUEUE:
assert(pc.next_song != nullptr); assert(next_song != nullptr);
pc.Unlock(); Unlock();
do_play(pc, dc, buffer); do_play(*this, dc, buffer);
pc.listener.OnPlayerSync(); listener.OnPlayerSync();
pc.Lock(); Lock();
break; break;
case PlayerCommand::STOP: case PlayerCommand::STOP:
pc.Unlock(); Unlock();
pc.outputs.Cancel(); outputs.Cancel();
pc.Lock(); Lock();
/* fall through */ /* fall through */
case PlayerCommand::PAUSE: case PlayerCommand::PAUSE:
delete pc.next_song; delete next_song;
pc.next_song = nullptr; next_song = nullptr;
pc.CommandFinished(); CommandFinished();
break; break;
case PlayerCommand::CLOSE_AUDIO: case PlayerCommand::CLOSE_AUDIO:
pc.Unlock(); Unlock();
pc.outputs.Release(); outputs.Release();
pc.Lock(); Lock();
pc.CommandFinished(); CommandFinished();
assert(buffer.IsEmptyUnsafe()); assert(buffer.IsEmptyUnsafe());
break; break;
case PlayerCommand::UPDATE_AUDIO: case PlayerCommand::UPDATE_AUDIO:
pc.Unlock(); Unlock();
pc.outputs.EnableDisable(); outputs.EnableDisable();
pc.Lock(); Lock();
pc.CommandFinished(); CommandFinished();
break; break;
case PlayerCommand::EXIT: case PlayerCommand::EXIT:
pc.Unlock(); Unlock();
dc.Quit(); dc.Quit();
pc.outputs.Close(); outputs.Close();
pc.LockCommandFinished(); LockCommandFinished();
return; return;
case PlayerCommand::CANCEL: case PlayerCommand::CANCEL:
delete pc.next_song; delete next_song;
pc.next_song = nullptr; next_song = nullptr;
pc.CommandFinished(); CommandFinished();
break; break;
case PlayerCommand::REFRESH: case PlayerCommand::REFRESH:
/* no-op when not playing */ /* no-op when not playing */
pc.CommandFinished(); CommandFinished();
break; break;
case PlayerCommand::NONE: case PlayerCommand::NONE:
pc.Wait(); Wait();
break; break;
} }
} }
@@ -1242,5 +1240,5 @@ StartPlayerThread(PlayerControl &pc)
{ {
assert(!pc.thread.IsDefined()); assert(!pc.thread.IsDefined());
pc.thread.Start(player_task, &pc); pc.thread.Start();
} }

View File

@@ -313,7 +313,7 @@ soundcloud_open_uri(const char *uri, Mutex &mutex, Cond &cond)
SoundCloudJsonData data; SoundCloudJsonData data;
yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, &data); yajl_handle hand = yajl_alloc(&parse_callbacks, nullptr, &data);
AtScopeExit(hand, &data) { yajl_free(hand); }; AtScopeExit(hand) { yajl_free(hand); };
int ret = soundcloud_parse_json(u, hand, mutex, cond); int ret = soundcloud_parse_json(u, hand, mutex, cond);

View File

@@ -212,8 +212,6 @@ playlist::SeekSongOrder(PlayerControl &pc, unsigned i, SongTime seek_time)
{ {
assert(queue.IsValidOrder(i)); assert(queue.IsValidOrder(i));
const DetachedSong *queued_song = GetQueuedSong();
pc.LockClearError(); pc.LockClearError();
stop_on_error = true; stop_on_error = true;
error_count = 0; error_count = 0;
@@ -226,8 +224,6 @@ playlist::SeekSongOrder(PlayerControl &pc, unsigned i, SongTime seek_time)
playing = true; playing = true;
current = i; current = i;
queued_song = nullptr;
} }
queued = -1; queued = -1;
@@ -235,7 +231,7 @@ playlist::SeekSongOrder(PlayerControl &pc, unsigned i, SongTime seek_time)
try { try {
pc.LockSeek(new DetachedSong(queue.GetOrder(i)), seek_time); pc.LockSeek(new DetachedSong(queue.GetOrder(i)), seek_time);
} catch (...) { } catch (...) {
UpdateQueuedSong(pc, queued_song); UpdateQueuedSong(pc, nullptr);
throw; throw;
} }

View File

@@ -48,6 +48,9 @@ static constexpr Domain storage_domain("storage");
void void
storage_state_save(BufferedOutputStream &os, const Instance &instance) storage_state_save(BufferedOutputStream &os, const Instance &instance)
{ {
if (instance.storage == nullptr)
return;
const auto visitor = [&os](const char *mount_uri, const Storage &storage) { const auto visitor = [&os](const char *mount_uri, const Storage &storage) {
std::string uri = storage.MapUTF8(""); std::string uri = storage.MapUTF8("");
if (uri.empty() || StringIsEmpty(mount_uri)) if (uri.empty() || StringIsEmpty(mount_uri))
@@ -85,6 +88,12 @@ storage_state_restore(const char *line, TextFile &file, Instance &instance)
FormatError(storage_domain, "Unrecognized line in mountpoint state: %s", line); FormatError(storage_domain, "Unrecognized line in mountpoint state: %s", line);
} }
if (instance.storage == nullptr)
/* without storage (a CompositeStorage instance), we
cannot mount, and therefore we silently ignore the
state file */
return true;
if (url.empty() || uri.empty()) { if (url.empty() || uri.empty()) {
LogError(storage_domain, "Missing value in mountpoint state."); LogError(storage_domain, "Missing value in mountpoint state.");
return true; return true;
@@ -99,16 +108,18 @@ storage_state_restore(const char *line, TextFile &file, Instance &instance)
return true; return true;
} }
#ifdef ENABLE_DATABASE
Database *db = instance.database; Database *db = instance.database;
if (db != nullptr && db->IsPlugin(simple_db_plugin)) { if (db != nullptr && db->IsPlugin(simple_db_plugin)) {
try { try {
((SimpleDatabase *)db)->Mount(uri.c_str(), url.c_str()); ((SimpleDatabase *)db)->Mount(uri.c_str(), url.c_str());
} catch (...) { } catch (...) {
throw; delete storage;
FormatError(std::current_exception(),
"Failed to restore mount to %s",
url.c_str());
return true;
} }
} }
#endif
((CompositeStorage*)instance.storage)->Mount(uri.c_str(), storage); ((CompositeStorage*)instance.storage)->Mount(uri.c_str(), storage);
@@ -118,6 +129,9 @@ storage_state_restore(const char *line, TextFile &file, Instance &instance)
unsigned unsigned
storage_state_get_hash(const Instance &instance) storage_state_get_hash(const Instance &instance)
{ {
if (instance.storage == nullptr)
return 0;
std::set<std::string> mounts; std::set<std::string> mounts;
const auto visitor = [&mounts](const char *mount_uri, const Storage &storage) { const auto visitor = [&mounts](const char *mount_uri, const Storage &storage) {

View File

@@ -52,13 +52,11 @@ public:
constexpr ThreadId(pthread_t _id):id(_id) {} constexpr ThreadId(pthread_t _id):id(_id) {}
#endif #endif
gcc_const static constexpr ThreadId Null() noexcept {
static ThreadId Null() noexcept {
#ifdef _WIN32 #ifdef _WIN32
return 0; return 0;
#else #else
static ThreadId null; return pthread_t();
return null;
#endif #endif
} }
@@ -81,11 +79,13 @@ public:
gcc_pure gcc_pure
bool operator==(const ThreadId &other) const noexcept { bool operator==(const ThreadId &other) const noexcept {
#ifdef _WIN32 /* note: not using pthread_equal() because that
function "is undefined if either thread ID is not
valid so we can't safely use it on
default-constructed values" (comment from
libstdc++) - and if both libstdc++ and libc++ get
away with this, we can do it as well */
return id == other.id; return id == other.id;
#else
return pthread_equal(id, other.id);
#endif
} }
/** /**

View File

@@ -25,39 +25,21 @@
#include "java/Global.hxx" #include "java/Global.hxx"
#endif #endif
bool void
Thread::Start(void (*_f)(void *ctx), void *_ctx) Thread::Start()
{ {
assert(!IsDefined()); assert(!IsDefined());
f = _f;
ctx = _ctx;
#ifdef _WIN32 #ifdef _WIN32
handle = ::CreateThread(nullptr, 0, ThreadProc, this, 0, &id); handle = ::CreateThread(nullptr, 0, ThreadProc, this, 0, &id);
if (handle == nullptr) if (handle == nullptr)
throw MakeLastError("Failed to create thread"); throw MakeLastError("Failed to create thread");
#else #else
#ifndef NDEBUG
creating = true;
#endif
int e = pthread_create(&handle, nullptr, ThreadProc, this); int e = pthread_create(&handle, nullptr, ThreadProc, this);
if (e != 0) { if (e != 0)
#ifndef NDEBUG
creating = false;
#endif
throw MakeErrno(e, "Failed to create thread"); throw MakeErrno(e, "Failed to create thread");
}
defined = true;
#ifndef NDEBUG
creating = false;
#endif #endif
#endif
return true;
} }
void void
@@ -72,7 +54,17 @@ Thread::Join()
handle = nullptr; handle = nullptr;
#else #else
pthread_join(handle, nullptr); pthread_join(handle, nullptr);
defined = false; handle = pthread_t();
#endif
}
inline void
Thread::Run()
{
f();
#ifdef ANDROID
Java::DetachCurrentThread();
#endif #endif
} }
@@ -83,7 +75,7 @@ Thread::ThreadProc(LPVOID ctx)
{ {
Thread &thread = *(Thread *)ctx; Thread &thread = *(Thread *)ctx;
thread.f(thread.ctx); thread.Run();
return 0; return 0;
} }
@@ -95,18 +87,10 @@ Thread::ThreadProc(void *ctx)
Thread &thread = *(Thread *)ctx; Thread &thread = *(Thread *)ctx;
#ifndef NDEBUG #ifndef NDEBUG
/* this works around a race condition that causes an assertion thread.inside_handle = pthread_self();
failure due to IsInside() spuriously returning false right
after the thread has been created, and the calling thread
hasn't initialised "defined" yet */
thread.defined = true;
#endif #endif
thread.f(thread.ctx); thread.Run();
#ifdef ANDROID
Java::DetachCurrentThread();
#endif
return nullptr; return nullptr;
} }

View File

@@ -21,6 +21,7 @@
#define MPD_THREAD_HXX #define MPD_THREAD_HXX
#include "check.h" #include "check.h"
#include "util/BindMethod.hxx"
#include "Compiler.h" #include "Compiler.h"
#ifdef _WIN32 #ifdef _WIN32
@@ -32,28 +33,28 @@
#include <assert.h> #include <assert.h>
class Thread { class Thread {
typedef BoundMethod<void()> Function;
const Function f;
#ifdef _WIN32 #ifdef _WIN32
HANDLE handle = nullptr; HANDLE handle = nullptr;
DWORD id; DWORD id;
#else #else
pthread_t handle; pthread_t handle = pthread_t();
bool defined = false;
#ifndef NDEBUG #ifndef NDEBUG
/** /**
* The thread is currently being created. This is a workaround for * This handle is only used by IsInside(), and is set by the
* IsInside(), which may return false until pthread_create() has * thread function. Since #handle is set by pthread_create()
* initialised the #handle. * which is racy, we need this attribute for early checks
* inside the thread function.
*/ */
bool creating = false; pthread_t inside_handle = pthread_t();
#endif #endif
#endif #endif
void (*f)(void *ctx);
void *ctx;
public: public:
Thread() = default; explicit Thread(Function _f):f(_f) {}
Thread(const Thread &) = delete; Thread(const Thread &) = delete;
@@ -69,10 +70,11 @@ public:
#ifdef _WIN32 #ifdef _WIN32
return handle != nullptr; return handle != nullptr;
#else #else
return defined; return handle != pthread_t();
#endif #endif
} }
#ifndef NDEBUG
/** /**
* Check if this thread is the current thread. * Check if this thread is the current thread.
*/ */
@@ -81,18 +83,23 @@ public:
#ifdef _WIN32 #ifdef _WIN32
return GetCurrentThreadId() == id; return GetCurrentThreadId() == id;
#else #else
#ifdef NDEBUG /* note: not using pthread_equal() because that
constexpr bool creating = false; function "is undefined if either thread ID is not
#endif valid so we can't safely use it on
return IsDefined() && (creating || default-constructed values" (comment from
pthread_equal(pthread_self(), handle)); libstdc++) - and if both libstdc++ and libc++ get
away with this, we can do it as well */
return pthread_self() == inside_handle;
#endif #endif
} }
#endif
bool Start(void (*f)(void *ctx), void *ctx); void Start();
void Join(); void Join();
private: private:
void Run();
#ifdef _WIN32 #ifdef _WIN32
static DWORD WINAPI ThreadProc(LPVOID ctx); static DWORD WINAPI ThreadProc(LPVOID ctx);
#else #else

View File

@@ -38,7 +38,7 @@
#include <windows.h> #include <windows.h>
#endif #endif
#ifdef __linux__ #if defined(__linux__) && !defined(ANDROID)
static int static int
ioprio_set(int which, int who, int ioprio) ioprio_set(int which, int who, int ioprio)
@@ -69,7 +69,11 @@ SetThreadIdlePriority()
sched_setscheduler(0, SCHED_IDLE, &sched_param); sched_setscheduler(0, SCHED_IDLE, &sched_param);
#endif #endif
#ifndef ANDROID
/* this system call is forbidden via seccomp on Android 8 and
leads to crash (SIGSYS) */
ioprio_set_idle(); ioprio_set_idle();
#endif
#elif defined(_WIN32) #elif defined(_WIN32)
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);

View File

@@ -42,11 +42,6 @@ class RefCount {
std::atomic_uint n; std::atomic_uint n;
public: public:
#ifndef _LIBCPP_VERSION
/* the "constexpr" is missing in libc++'s "atomic"
implementation */
constexpr
#endif
RefCount():n(1) {} RefCount():n(1) {}
void Increment() { void Increment() {

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2003-2018 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 NULL_MIXER_LISTENER_HXX
#define NULL_MIXER_LISTENER_HXX
#include "mixer/Listener.hxx"
class NullMixerListener : public MixerListener {
public:
void OnMixerVolumeChanged(Mixer &, int) override {}
};
#endif

View File

@@ -18,6 +18,7 @@
*/ */
#include "config.h" #include "config.h"
#include "NullMixerListener.hxx"
#include "mixer/MixerControl.hxx" #include "mixer/MixerControl.hxx"
#include "mixer/MixerList.hxx" #include "mixer/MixerList.hxx"
#include "filter/FilterRegistry.hxx" #include "filter/FilterRegistry.hxx"
@@ -50,9 +51,14 @@ try {
EventLoop event_loop; EventLoop event_loop;
NullMixerListener mixer_listener;
Mixer *mixer = mixer_new(event_loop, alsa_mixer_plugin, Mixer *mixer = mixer_new(event_loop, alsa_mixer_plugin,
*(AudioOutput *)nullptr, /* ugly dangerous dummy pointer to
*(MixerListener *)nullptr, make the compiler happy; this
works with most mixers, because
they don't need the AudioOutput */
*(AudioOutput *)0x1,
mixer_listener,
ConfigBlock()); ConfigBlock());
mixer_open(mixer); mixer_open(mixer);

View File

@@ -18,6 +18,7 @@
*/ */
#include "config.h" #include "config.h"
#include "NullMixerListener.hxx"
#include "output/Internal.hxx" #include "output/Internal.hxx"
#include "output/OutputPlugin.hxx" #include "output/OutputPlugin.hxx"
#include "output/Client.hxx" #include "output/Client.hxx"
@@ -44,6 +45,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
void AudioOutput::Task() {}
class DummyAudioOutputClient final : public AudioOutputClient { class DummyAudioOutputClient final : public AudioOutputClient {
public: public:
/* virtual methods from AudioOutputClient */ /* virtual methods from AudioOutputClient */
@@ -62,7 +65,9 @@ filter_plugin_by_name(gcc_unused const char *name) noexcept
} }
static AudioOutput * static AudioOutput *
load_audio_output(EventLoop &event_loop, AudioOutputClient &client, load_audio_output(EventLoop &event_loop,
NullMixerListener &mixer_listener,
AudioOutputClient &client,
const char *name) const char *name)
{ {
const auto *param = config_find_block(ConfigBlockOption::AUDIO_OUTPUT, const auto *param = config_find_block(ConfigBlockOption::AUDIO_OUTPUT,
@@ -72,7 +77,7 @@ load_audio_output(EventLoop &event_loop, AudioOutputClient &client,
name); name);
return audio_output_new(event_loop, ReplayGainConfig(), *param, return audio_output_new(event_loop, ReplayGainConfig(), *param,
*(MixerListener *)nullptr, mixer_listener,
client); client);
} }
@@ -142,8 +147,10 @@ try {
/* initialize the audio output */ /* initialize the audio output */
NullMixerListener mixer_listener;
DummyAudioOutputClient client; DummyAudioOutputClient client;
AudioOutput *ao = load_audio_output(event_loop, client, argv[2]); AudioOutput *ao = load_audio_output(event_loop, mixer_listener,
client, argv[2]);
/* parse the audio format */ /* parse the audio format */

View File

@@ -50,14 +50,14 @@ class CrossGccToolchain:
self.nm = os.path.join(toolchain_bin, arch + '-nm') self.nm = os.path.join(toolchain_bin, arch + '-nm')
self.strip = os.path.join(toolchain_bin, arch + '-strip') self.strip = os.path.join(toolchain_bin, arch + '-strip')
common_flags = '' common_flags = '-O2 -g'
if not x64: if not x64:
# enable SSE support which is required for LAME # enable SSE support which is required for LAME
common_flags += ' -march=pentium3' common_flags += ' -march=pentium3'
self.cflags = '-O2 -g ' + common_flags self.cflags = common_flags
self.cxxflags = '-O2 -g ' + common_flags self.cxxflags = common_flags
self.cppflags = '-isystem ' + os.path.join(install_prefix, 'include') self.cppflags = '-isystem ' + os.path.join(install_prefix, 'include')
self.ldflags = '-L' + os.path.join(install_prefix, 'lib') self.ldflags = '-L' + os.path.join(install_prefix, 'lib')
self.libs = '' self.libs = ''