Compare commits
201 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
98afae2520 | ||
![]() |
ddc85c620f | ||
![]() |
12bc625fe1 | ||
![]() |
6b407356b9 | ||
![]() |
a4e0b52468 | ||
![]() |
98efb4f6d5 | ||
![]() |
36edb4886c | ||
![]() |
76290f786d | ||
![]() |
c6299c26b5 | ||
![]() |
fb5f9baf9c | ||
![]() |
dee591d970 | ||
![]() |
a5cc13b0c5 | ||
![]() |
aaf588aeaa | ||
![]() |
533a3def9f | ||
![]() |
fcf487f4e0 | ||
![]() |
906972973e | ||
![]() |
116edf5fce | ||
![]() |
8581013911 | ||
![]() |
b1e073bacd | ||
![]() |
501e48daba | ||
![]() |
643ecd1edd | ||
![]() |
7393e1cba1 | ||
![]() |
ceee47fda8 | ||
![]() |
6f3c0d0a60 | ||
![]() |
466625f7ad | ||
![]() |
b8259e604a | ||
![]() |
86e2075c63 | ||
![]() |
30900b2fe2 | ||
![]() |
fd7ae7ea4c | ||
![]() |
60d5bf0240 | ||
![]() |
41cdc4e14b | ||
![]() |
87dfca0477 | ||
![]() |
e1ee8e7812 | ||
![]() |
63406efcd8 | ||
![]() |
d5c132fca0 | ||
![]() |
5f082a2739 | ||
![]() |
7d6a762845 | ||
![]() |
8dcb1f805d | ||
![]() |
a8b9e5b9b9 | ||
![]() |
04f928e2b0 | ||
![]() |
c7a803c922 | ||
![]() |
ab197b6d43 | ||
![]() |
16b0e53a36 | ||
![]() |
bc14a6038e | ||
![]() |
626329a1cc | ||
![]() |
8bf250c228 | ||
![]() |
62127bbb12 | ||
![]() |
786ac87b76 | ||
![]() |
c76f4ac89b | ||
![]() |
d495ec71a8 | ||
![]() |
b763852f57 | ||
![]() |
6522d2f722 | ||
![]() |
ac61fd1d78 | ||
![]() |
c44d1566fa | ||
![]() |
80dc7c2f74 | ||
![]() |
7b94f0e36b | ||
![]() |
504e8d564a | ||
![]() |
ac395429c3 | ||
![]() |
388768b3a6 | ||
![]() |
5c4169e64e | ||
![]() |
d40e9de2d2 | ||
![]() |
1e54297be8 | ||
![]() |
44b200240f | ||
![]() |
a2340c313f | ||
![]() |
37b07a5e7c | ||
![]() |
73013a3c04 | ||
![]() |
e8099f01b5 | ||
![]() |
672bdd3a56 | ||
![]() |
c2c2c29658 | ||
![]() |
c745e14f47 | ||
![]() |
e8f08cda53 | ||
![]() |
8266ab5588 | ||
![]() |
ea552208fc | ||
![]() |
e86015a72a | ||
![]() |
cf7ec2c9d3 | ||
![]() |
dadd3ca671 | ||
![]() |
79535212c8 | ||
![]() |
ef5f96a193 | ||
![]() |
418f71ec0f | ||
![]() |
0ebeaa9ac2 | ||
![]() |
25cd47b8dc | ||
![]() |
cd48d981b5 | ||
![]() |
774d26b982 | ||
![]() |
f3e683bd6f | ||
![]() |
50ce0c0d9d | ||
![]() |
5b80711d75 | ||
![]() |
666e456551 | ||
![]() |
31794ac376 | ||
![]() |
2141fdf06e | ||
![]() |
3f3e0739c4 | ||
![]() |
ebed7e2147 | ||
![]() |
53f5d4c710 | ||
![]() |
139a4054c5 | ||
![]() |
a4de96508d | ||
![]() |
a7582aaf15 | ||
![]() |
c5c1c64a81 | ||
![]() |
992c52ce7f | ||
![]() |
026aef7465 | ||
![]() |
b53a23b51b | ||
![]() |
2aad015392 | ||
![]() |
986ec877b0 | ||
![]() |
c43ea74b30 | ||
![]() |
79981f3cda | ||
![]() |
c2940a8385 | ||
![]() |
bede564618 | ||
![]() |
e0ca4b865a | ||
![]() |
31c206bf80 | ||
![]() |
9187a08106 | ||
![]() |
3859a50466 | ||
![]() |
927071e085 | ||
![]() |
6ba918b203 | ||
![]() |
e8b70dbca4 | ||
![]() |
0f8d223c7f | ||
![]() |
19a2885fd5 | ||
![]() |
b8a094470b | ||
![]() |
2988bb77e8 | ||
![]() |
738317bf34 | ||
![]() |
e46fbd0780 | ||
![]() |
56b74ad990 | ||
![]() |
6de92bb42b | ||
![]() |
c801936e53 | ||
![]() |
817656504d | ||
![]() |
6f00f97b66 | ||
![]() |
5acb978f8f | ||
![]() |
975a4ae871 | ||
![]() |
56aaf3c73e | ||
![]() |
12fd1cad0c | ||
![]() |
e573cbf032 | ||
![]() |
dead461542 | ||
![]() |
3d5da1ac73 | ||
![]() |
ec408ca6a6 | ||
![]() |
ea66cdd6a5 | ||
![]() |
f762e8034f | ||
![]() |
bb1e369f30 | ||
![]() |
8376578921 | ||
![]() |
ed2354cd9d | ||
![]() |
386688b87a | ||
![]() |
38d56dddf1 | ||
![]() |
e8975942ec | ||
![]() |
3ca80a7336 | ||
![]() |
d029dae7ad | ||
![]() |
9e058732ee | ||
![]() |
cad5d11261 | ||
![]() |
fcaedec2ab | ||
![]() |
ead9d59e88 | ||
![]() |
34b8a17ccd | ||
![]() |
a53d081c39 | ||
![]() |
823134e4ba | ||
![]() |
272167b4fc | ||
![]() |
92f09bba94 | ||
![]() |
1f50bdb230 | ||
![]() |
2eef4e6716 | ||
![]() |
d989dbfec4 | ||
![]() |
ca9fcec364 | ||
![]() |
354104f9a9 | ||
![]() |
8649ea3d6f | ||
![]() |
752ff12c37 | ||
![]() |
4bb89b1755 | ||
![]() |
0ef553d30e | ||
![]() |
43a62aef07 | ||
![]() |
ed4d0aa909 | ||
![]() |
023ce4e720 | ||
![]() |
368d9359dd | ||
![]() |
d98c19d561 | ||
![]() |
cab77e35e0 | ||
![]() |
e3e90b4b93 | ||
![]() |
f8c69893e1 | ||
![]() |
49678a0893 | ||
![]() |
d667b5b48c | ||
![]() |
9cba55b39c | ||
![]() |
c2cbb7b8ce | ||
![]() |
8217d75ca1 | ||
![]() |
1ca70d9759 | ||
![]() |
4303aaa9b8 | ||
![]() |
7b56bae289 | ||
![]() |
4183416b3e | ||
![]() |
a60dee57ce | ||
![]() |
5724656acb | ||
![]() |
329f9cd9fe | ||
![]() |
fbdb8b406e | ||
![]() |
85d0bbd957 | ||
![]() |
414f00d6ae | ||
![]() |
17b0add058 | ||
![]() |
c68ed40661 | ||
![]() |
ff624075a8 | ||
![]() |
08db28469d | ||
![]() |
a20b326807 | ||
![]() |
4db1b1b250 | ||
![]() |
ff6b263b48 | ||
![]() |
c0bf052fa9 | ||
![]() |
5419cff925 | ||
![]() |
eee10ad2ed | ||
![]() |
98472a8104 | ||
![]() |
d094c168aa | ||
![]() |
4b18460bc6 | ||
![]() |
412c0a965c | ||
![]() |
2becf79223 | ||
![]() |
43ec96d4a0 | ||
![]() |
3d1d779da7 | ||
![]() |
c88056ba83 | ||
![]() |
e769751221 |
AUTHORSMakefile.amNEWS
android
configure.acdoc
python/build
src
AudioFormat.cxxAudioFormat.hxxIOThread.cxxIOThread.hxxLocateUri.cxxMain.cxxMusicPipe.hxxSongFilter.cxxSongFilter.hxxSongSave.cxxls.cxx
archive
command
db
plugins
update
decoder
encoder
plugins
event
filter
fs
input
AsyncInputStream.cxxError.cxxError.hxxInputStream.cxxOffset.hxxOpen.cxxThreadInputStream.cxxThreadInputStream.hxx
plugins
lib
curl
ffmpeg
icu
nfs
upnp
mixer
plugins
neighbor
plugins
net
output
pcm
ConfiguredResampler.cxxOrder.cxxPcmDop.cxxPcmExport.cxxSampleFormat.hxxSoxrResampler.cxxSoxrResampler.hxx
player
playlist
protocol
queue
storage
system
tag
thread
util
test
win32
5
AUTHORS
5
AUTHORS
@@ -31,3 +31,8 @@ The following people have contributed code to MPD:
|
||||
Jean-Francois Dockes <jf@dockes.org>
|
||||
Yue Wang <yuleopen@gmail.com>
|
||||
Matthew Leon Grinshpun <ml@matthewleon.com>
|
||||
Dimitris Papastamos <sin@2f30.org>
|
||||
Florian Schlichting <fsfs@debian.org>
|
||||
François Revol <revol@free.fr>
|
||||
Jacob Vosmaer <contact@jacobvosmaer.nl>
|
||||
Thomas Guillem <thomas@gllm.fr>
|
||||
|
93
Makefile.am
93
Makefile.am
@@ -61,8 +61,8 @@ src_mpd_LDADD = \
|
||||
libnet.a \
|
||||
$(FS_LIBS) \
|
||||
libsystem.a \
|
||||
libutil.a \
|
||||
$(ICU_LDADD) \
|
||||
libutil.a \
|
||||
$(SYSTEMD_DAEMON_LIBS)
|
||||
|
||||
src_mpd_SOURCES = \
|
||||
@@ -234,6 +234,7 @@ libmpd_a_SOURCES += \
|
||||
endif
|
||||
|
||||
CURL_SOURCES = \
|
||||
src/lib/curl/Error.hxx \
|
||||
src/lib/curl/Version.cxx src/lib/curl/Version.hxx \
|
||||
src/lib/curl/Global.cxx src/lib/curl/Global.hxx \
|
||||
src/lib/curl/Request.cxx src/lib/curl/Request.hxx \
|
||||
@@ -285,30 +286,62 @@ libmain_a_CPPFLAGS = $(AM_CPPFLAGS) -Iandroid/build/include
|
||||
|
||||
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:
|
||||
rm -rf android/build
|
||||
|
||||
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)
|
||||
|
||||
android/build/build.xml: android/AndroidManifest.xml
|
||||
rm -rf android/build
|
||||
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_SDK_BUILD_TOOLS_VERSION = 27.0.0
|
||||
ANDROID_SDK_PLATFORM = android-21
|
||||
|
||||
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_TOOLS_DIR = $(ANDROID_SDK)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)
|
||||
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
|
||||
javah -classpath $(ANDROID_SDK)/platforms/android-17/android.jar:android/build/bin/classes -d $(@D) org.musicpd.Bridge
|
||||
JAVAC = javac
|
||||
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.6 -target 1.6 -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
|
||||
|
||||
android/build/libs/armeabi-v7a/libmpd.so: libmpd.so android/build/build.xml
|
||||
android/build/lib/$(ANDROID_ABI)/libmpd.so: libmpd.so
|
||||
mkdir -p $(@D)
|
||||
rm -f $@
|
||||
$(STRIP) -o $@ $<
|
||||
@@ -317,24 +350,19 @@ android/build/res/drawable/icon.png: mpd.svg
|
||||
mkdir -p $(@D)
|
||||
rsvg-convert --width=48 --height=48 $< -o $@
|
||||
|
||||
APK_DEPS = android/build/res/drawable/icon.png \
|
||||
android/build/libs/armeabi-v7a/libmpd.so \
|
||||
$(wildcard $(srcdir)/android/src/*.java) \
|
||||
android/build/build.xml
|
||||
.DELETE_ON_ERROR: android/build/unsigned.apk
|
||||
android/build/unsigned.apk: android/build/classes.dex android/build/resources.apk android/build/lib/$(ANDROID_ABI)/libmpd.so
|
||||
cp android/build/resources.apk $@
|
||||
cd $(dir $@) && zip -q -r $(notdir $@) classes.dex lib
|
||||
|
||||
android/build/bin/$(APK_NAME)-debug.apk: $(APK_DEPS)
|
||||
cd android/build && ant nodeps debug
|
||||
android/build/$(APK_NAME)-debug.apk: android/build/unsigned.apk
|
||||
jarsigner -keystore $(HOME)/.android/debug.keystore -storepass android -signedjar $@ $< androiddebugkey
|
||||
|
||||
android/build/bin/$(APK_NAME)-release-unsigned.apk: $(APK_DEPS)
|
||||
cd android/build && ant nodeps release
|
||||
|
||||
android/build/bin/$(APK_NAME)-release-unaligned.apk: android/build/bin/$(APK_NAME)-release-unsigned.apk
|
||||
android/build/$(APK_NAME)-release-unaligned.apk: android/build/unsigned.apk
|
||||
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/bin/$(APK_NAME).apk: android/build/bin/$(APK_NAME)-release-unaligned.apk
|
||||
$(ANDROID_SDK)/build-tools/$(ANDROID_SDK_BUILD_TOOLS_VERSION)/zipalign -f 4 $< $@
|
||||
android/build/$(APK_NAME).apk: android/build/$(APK_NAME)-release-unaligned.apk
|
||||
$(ZIPALIGN) -f 4 $< $@
|
||||
|
||||
endif
|
||||
|
||||
@@ -417,6 +445,7 @@ libutil_a_SOURCES = \
|
||||
src/util/NumberParser.hxx \
|
||||
src/util/MimeType.cxx src/util/MimeType.hxx \
|
||||
src/util/StringBuffer.hxx \
|
||||
src/util/StringFormat.hxx \
|
||||
src/util/StringPointer.hxx \
|
||||
src/util/StringView.cxx src/util/StringView.hxx \
|
||||
src/util/AllocatedString.cxx src/util/AllocatedString.hxx \
|
||||
@@ -470,6 +499,7 @@ libthread_a_SOURCES = \
|
||||
|
||||
libnet_a_SOURCES = \
|
||||
src/net/Features.hxx \
|
||||
src/net/Init.hxx \
|
||||
src/net/ToString.cxx src/net/ToString.hxx \
|
||||
src/net/Resolver.cxx src/net/Resolver.hxx \
|
||||
src/net/StaticSocketAddress.cxx src/net/StaticSocketAddress.hxx \
|
||||
@@ -684,6 +714,7 @@ NFS_SOURCES = \
|
||||
src/lib/nfs/Cancellable.hxx \
|
||||
src/lib/nfs/Lease.hxx \
|
||||
src/lib/nfs/Connection.cxx src/lib/nfs/Connection.hxx \
|
||||
src/lib/nfs/Error.cxx src/lib/nfs/Error.hxx \
|
||||
src/lib/nfs/Manager.cxx src/lib/nfs/Manager.hxx \
|
||||
src/lib/nfs/Glue.cxx src/lib/nfs/Glue.hxx \
|
||||
src/lib/nfs/Base.cxx src/lib/nfs/Base.hxx \
|
||||
@@ -706,6 +737,8 @@ libstorage_a_SOURCES = \
|
||||
src/storage/FileInfo.hxx
|
||||
|
||||
libstorage_a_CPPFLAGS = $(AM_CPPFLAGS) \
|
||||
$(CURL_CFLAGS) \
|
||||
$(EXPAT_CFLAGS) \
|
||||
$(NFS_CFLAGS) \
|
||||
$(SMBCLIENT_CFLAGS)
|
||||
|
||||
@@ -1264,7 +1297,7 @@ endif
|
||||
#
|
||||
|
||||
libinput_a_SOURCES = \
|
||||
src/input/Domain.cxx src/input/Domain.hxx \
|
||||
src/input/Error.cxx src/input/Error.hxx \
|
||||
src/input/Init.cxx src/input/Init.hxx \
|
||||
src/input/Registry.cxx src/input/Registry.hxx \
|
||||
src/input/Open.cxx \
|
||||
@@ -2132,6 +2165,7 @@ test_run_output_LDADD = $(MPD_LIBS) \
|
||||
libutil.a
|
||||
test_run_output_SOURCES = test/run_output.cxx \
|
||||
test/ScopeIOThread.hxx \
|
||||
test/NullMixerListener.hxx \
|
||||
src/Log.cxx src/LogBackend.cxx \
|
||||
src/IOThread.cxx \
|
||||
src/output/Domain.cxx \
|
||||
@@ -2155,6 +2189,7 @@ test_read_mixer_LDADD = \
|
||||
libsystem.a \
|
||||
libutil.a
|
||||
test_read_mixer_SOURCES = test/read_mixer.cxx \
|
||||
test/NullMixerListener.hxx \
|
||||
src/Log.cxx src/LogBackend.cxx \
|
||||
src/mixer/MixerControl.cxx \
|
||||
src/filter/FilterPlugin.cxx \
|
||||
|
86
NEWS
86
NEWS
@@ -1,3 +1,89 @@
|
||||
ver 0.20.21 (2018/08/17)
|
||||
* database
|
||||
- proxy: add "password" setting
|
||||
- proxy: support tags "ArtistSort", "AlbumArtistSort", "AlbumSort"
|
||||
- simple: allow .mpdignore comments only at start of line
|
||||
* output
|
||||
- httpd: remove broken DLNA support code
|
||||
* playlist
|
||||
- cue: support file type declaration "FLAC" (non-standard)
|
||||
* URI schemes are case insensitive
|
||||
* Android, Windows
|
||||
- enable the "curl" storage plugin
|
||||
|
||||
ver 0.20.20 (2018/05/22)
|
||||
* protocol
|
||||
- fix "modified-since" filter regression
|
||||
* output
|
||||
- pulse: cork stream when paused due to "single" mode
|
||||
* decoder
|
||||
- dsdiff, dsf: support more MIME types
|
||||
- dsdiff, dsf: allow 4 MB ID3 tags
|
||||
- opus: support R128_ALBUM_GAIN tag
|
||||
* Android, Windows
|
||||
- enable the "proxy" database plugin
|
||||
|
||||
ver 0.20.19 (2018/04/26)
|
||||
* protocol
|
||||
- validate absolute seek time, reject negative values
|
||||
* database
|
||||
- proxy: fix "search already in progress" errors
|
||||
- proxy: implement "list ... group"
|
||||
* input
|
||||
- mms: fix lockup bug and a crash bug
|
||||
* decoder
|
||||
- ffmpeg: fix av_register_all() deprecation warning (FFmpeg 4.0)
|
||||
* player
|
||||
- fix spurious "Not seekable" error when switching radio streams
|
||||
* macOS: fix crash bug
|
||||
|
||||
ver 0.20.18 (2018/02/24)
|
||||
* input
|
||||
- curl: allow authentication methods other than "Basic"
|
||||
* decoder
|
||||
- flac: improve seeking precision
|
||||
* fix gapless CUE song transitions
|
||||
* Android, Windows
|
||||
- enable the NFS storage plugin
|
||||
|
||||
ver 0.20.17 (2018/02/11)
|
||||
* output
|
||||
- alsa: fix crash bug with 8 channels
|
||||
* mixer
|
||||
- alsa: fix rounding error at volume 0
|
||||
* fix real-time and idle scheduling with Musl
|
||||
* Android
|
||||
- fix compatibility with Android 4.0
|
||||
|
||||
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)
|
||||
* database
|
||||
- simple: fix file corruption in the presence of mount points
|
||||
* archive
|
||||
- bz2: fix deadlock
|
||||
- reduce lock contention, fixing lots of xrun problems
|
||||
* fix Solaris build failure
|
||||
|
||||
ver 0.20.13 (2017/12/18)
|
||||
* output
|
||||
- osx: set up ring buffer to hold at least 100ms
|
||||
|
@@ -2,10 +2,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.musicpd"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="13"
|
||||
android:versionName="0.19.9">
|
||||
android:versionCode="20"
|
||||
android:versionName="0.20.21">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="17"/>
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21"/>
|
||||
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name">
|
||||
<activity android:name=".Main"
|
||||
|
@@ -3,13 +3,14 @@
|
||||
import os, os.path
|
||||
import sys, subprocess
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: build.py SDK_PATH NDK_PATH [configure_args...]", file=sys.stderr)
|
||||
if len(sys.argv) < 4:
|
||||
print("Usage: build.py SDK_PATH NDK_PATH ABI [configure_args...]", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
sdk_path = sys.argv[1]
|
||||
ndk_path = sys.argv[2]
|
||||
configure_args = sys.argv[3:]
|
||||
android_abi = sys.argv[3]
|
||||
configure_args = sys.argv[4:]
|
||||
|
||||
if not os.path.isfile(os.path.join(sdk_path, 'tools', 'android')):
|
||||
print("SDK not found in", ndk_path, file=sys.stderr)
|
||||
@@ -19,8 +20,36 @@ if not os.path.isdir(ndk_path):
|
||||
print("NDK not found in", ndk_path, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
android_abis = {
|
||||
'armeabi-v7a': {
|
||||
'arch': 'arm-linux-androideabi',
|
||||
'ndk_arch': 'arm',
|
||||
'toolchain_arch': 'arm-linux-androideabi',
|
||||
'llvm_triple': 'armv7-none-linux-androideabi',
|
||||
'cflags': '-march=armv7-a -mfpu=vfp -mfloat-abi=softfp',
|
||||
},
|
||||
|
||||
'arm64-v8a': {
|
||||
'android_api_level': '21',
|
||||
'arch': 'aarch64-linux-android',
|
||||
'ndk_arch': 'arm64',
|
||||
'toolchain_arch': 'aarch64-linux-android',
|
||||
'llvm_triple': 'aarch64-none-linux-android',
|
||||
'cflags': '',
|
||||
},
|
||||
|
||||
'x86': {
|
||||
'arch': 'i686-linux-android',
|
||||
'ndk_arch': 'x86',
|
||||
'toolchain_arch': 'x86',
|
||||
'llvm_triple': 'i686-none-linux-android',
|
||||
'cflags': '-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32',
|
||||
},
|
||||
}
|
||||
|
||||
# select the NDK target
|
||||
arch = 'arm-linux-androideabi'
|
||||
abi_info = android_abis[android_abi]
|
||||
arch = abi_info['arch']
|
||||
|
||||
# the path to the MPD sources
|
||||
mpd_path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]) or '.', '..'))
|
||||
@@ -44,16 +73,16 @@ class AndroidNdkToolchain:
|
||||
self.src_path = src_path
|
||||
self.build_path = build_path
|
||||
|
||||
self.ndk_arch = 'arm'
|
||||
android_abi = 'armeabi-v7a'
|
||||
ndk_platform = 'android-21'
|
||||
ndk_arch = abi_info['ndk_arch']
|
||||
android_api_level = '21'
|
||||
ndk_platform = 'android-' + android_api_level
|
||||
|
||||
# select the NDK compiler
|
||||
gcc_version = '4.9'
|
||||
|
||||
ndk_platform_path = os.path.join(ndk_path, 'platforms', ndk_platform)
|
||||
sysroot = os.path.join(ndk_path, 'sysroot')
|
||||
target_root = os.path.join(ndk_platform_path, 'arch-' + self.ndk_arch)
|
||||
target_root = os.path.join(ndk_platform_path, 'arch-' + ndk_arch)
|
||||
|
||||
install_prefix = os.path.join(arch_path, 'root')
|
||||
|
||||
@@ -61,11 +90,13 @@ class AndroidNdkToolchain:
|
||||
self.install_prefix = install_prefix
|
||||
self.sysroot = sysroot
|
||||
|
||||
toolchain_path = os.path.join(ndk_path, 'toolchains', arch + '-' + gcc_version, 'prebuilt', build_arch)
|
||||
toolchain_path = os.path.join(ndk_path, 'toolchains', abi_info['toolchain_arch'] + '-' + gcc_version, 'prebuilt', build_arch)
|
||||
llvm_path = os.path.join(ndk_path, 'toolchains', 'llvm', 'prebuilt', build_arch)
|
||||
llvm_triple = 'armv7-none-linux-androideabi'
|
||||
llvm_triple = abi_info['llvm_triple']
|
||||
|
||||
common_flags = '-march=armv7-a -mfloat-abi=softfp'
|
||||
common_flags = '-Os -g'
|
||||
common_flags += ' -fPIC'
|
||||
common_flags += ' ' + abi_info['cflags']
|
||||
|
||||
toolchain_bin = os.path.join(toolchain_path, 'bin')
|
||||
llvm_bin = os.path.join(llvm_path, 'bin')
|
||||
@@ -73,17 +104,19 @@ class AndroidNdkToolchain:
|
||||
self.cxx = os.path.join(llvm_bin, 'clang++')
|
||||
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.ranlib = os.path.join(toolchain_bin, arch + '-ranlib')
|
||||
self.nm = os.path.join(toolchain_bin, arch + '-nm')
|
||||
self.strip = os.path.join(toolchain_bin, arch + '-strip')
|
||||
|
||||
self.cflags = '-Os -g ' + common_flags
|
||||
self.cxxflags = '-Os -g ' + common_flags
|
||||
self.cflags = common_flags
|
||||
self.cxxflags = common_flags
|
||||
self.cppflags = '--sysroot=' + sysroot + \
|
||||
' -isystem ' + os.path.join(install_prefix, 'include') + \
|
||||
' -isystem ' + os.path.join(sysroot, 'usr', 'include', arch) + \
|
||||
' -D__ANDROID_API__=21'
|
||||
' -D__ANDROID_API__=' + android_api_level
|
||||
self.ldflags = '--sysroot=' + sysroot + \
|
||||
' -L' + os.path.join(install_prefix, 'lib') + \
|
||||
' -L' + os.path.join(target_root, 'usr', 'lib') + \
|
||||
@@ -91,22 +124,21 @@ class AndroidNdkToolchain:
|
||||
' ' + common_flags
|
||||
self.libs = ''
|
||||
|
||||
self.is_arm = self.ndk_arch == 'arm'
|
||||
self.is_arm = ndk_arch == 'arm'
|
||||
self.is_armv7 = self.is_arm and 'armv7' in self.cflags
|
||||
self.is_aarch64 = ndk_arch == 'arm64'
|
||||
self.is_windows = False
|
||||
|
||||
libcxx_path = os.path.join(ndk_path, 'sources/cxx-stl/llvm-libc++')
|
||||
libcxx_libs_path = os.path.join(libcxx_path, 'libs', android_abi)
|
||||
|
||||
libstdcxx_cppflags = '-nostdinc++ -isystem ' + os.path.join(libcxx_path, 'include') + ' -isystem ' + os.path.join(ndk_path, 'sources/android/support/include')
|
||||
libstdcxx_ldadd = os.path.join(libcxx_libs_path, 'libc++_static.a') + ' ' + os.path.join(libcxx_libs_path, 'libc++abi.a')
|
||||
|
||||
if self.is_armv7:
|
||||
libstdcxx_ldadd += ' ' + os.path.join(libcxx_libs_path, 'libunwind.a')
|
||||
libstdcxx_flags = '-stdlib=libc++'
|
||||
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 use_cxx:
|
||||
self.libs += ' ' + libstdcxx_ldadd
|
||||
self.cppflags += ' ' + libstdcxx_cppflags
|
||||
self.cxxflags += ' ' + libstdcxx_cxxflags
|
||||
self.ldflags += ' ' + libstdcxx_ldflags
|
||||
|
||||
self.env = dict(os.environ)
|
||||
|
||||
@@ -117,14 +149,16 @@ class AndroidNdkToolchain:
|
||||
# a list of third-party libraries to be used by MPD on Android
|
||||
from build.libs import *
|
||||
thirdparty_libs = [
|
||||
libmpdclient,
|
||||
libogg,
|
||||
libvorbis,
|
||||
opus,
|
||||
flac,
|
||||
libid3tag,
|
||||
libmad,
|
||||
ffmpeg,
|
||||
curl,
|
||||
libexpat,
|
||||
libnfs,
|
||||
boost,
|
||||
]
|
||||
|
||||
|
@@ -49,7 +49,7 @@ public class Main extends Activity implements Runnable {
|
||||
TextView tv = new TextView(this);
|
||||
tv.setText("Failed to load the native MPD libary.\n" +
|
||||
"Report this problem to us, and include the following information:\n" +
|
||||
"ABI=" + Build.CPU_ABI + "\n" +
|
||||
"SUPPORTED_ABIS=" + String.join(", ", Build.SUPPORTED_ABIS) + "\n" +
|
||||
"PRODUCT=" + Build.PRODUCT + "\n" +
|
||||
"FINGERPRINT=" + Build.FINGERPRINT + "\n" +
|
||||
"error=" + Loader.error);
|
||||
|
26
configure.ac
26
configure.ac
@@ -1,10 +1,10 @@
|
||||
AC_PREREQ(2.60)
|
||||
|
||||
AC_INIT(mpd, 0.20.13, musicpd-dev-team@lists.sourceforge.net)
|
||||
AC_INIT(mpd, 0.20.21, musicpd-dev-team@lists.sourceforge.net)
|
||||
|
||||
VERSION_MAJOR=0
|
||||
VERSION_MINOR=20
|
||||
VERSION_REVISION=13
|
||||
VERSION_REVISION=21
|
||||
VERSION_EXTRA=0
|
||||
|
||||
AC_CONFIG_SRCDIR([src/Main.cxx])
|
||||
@@ -16,7 +16,7 @@ AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AC_DEFINE(PROTOCOL_VERSION, "0.20.0", [The MPD protocol version])
|
||||
|
||||
GIT_COMMIT=`GIT_DIR="$srcdir/.git" git describe --dirty --always 2>/dev/null`
|
||||
GIT_COMMIT=`cd "$srcdir" && git describe --dirty --always 2>/dev/null`
|
||||
if test x$GIT_COMMIT != x; then
|
||||
AC_DEFINE_UNQUOTED(GIT_COMMIT, ["$GIT_COMMIT"], [The current git commit])
|
||||
fi
|
||||
@@ -186,6 +186,7 @@ AC_ARG_WITH([android-sdk],
|
||||
[Directory for Android SDK]),
|
||||
[], [with_android_sdk=no])
|
||||
|
||||
android_abi=""
|
||||
if test x$host_is_android = xyes; then
|
||||
if test x$with_android_sdk = xno; then
|
||||
AC_MSG_ERROR([Android build requires option --with-android-sdk=DIR])
|
||||
@@ -194,9 +195,15 @@ if test x$host_is_android = xyes; then
|
||||
if ! test -x $with_android_sdk/tools/android; then
|
||||
AC_MSG_ERROR([Android SDK not found in $with_android_sdk])
|
||||
fi
|
||||
|
||||
AS_CASE([$host_cpu],
|
||||
[i686], [android_abi="x86"],
|
||||
[aarch64], [android_abi="arm64-v8a"],
|
||||
[android_abi="armeabi-v7a"])
|
||||
fi
|
||||
|
||||
AC_SUBST(ANDROID_SDK, [$with_android_sdk])
|
||||
AC_SUBST(ANDROID_ABI, [$android_abi])
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
dnl Language Checks
|
||||
@@ -315,7 +322,7 @@ else
|
||||
fi
|
||||
|
||||
default_enable_daemon=yes
|
||||
if test x$host_is_android = xyes || test x$host_is_android = xyes; then
|
||||
if test x$host_is_android = xyes || test x$host_is_windows = xyes; then
|
||||
default_enable_daemon=no
|
||||
fi
|
||||
AC_ARG_ENABLE(daemon,
|
||||
@@ -402,7 +409,7 @@ AC_ARG_ENABLE(recorder-output,
|
||||
|
||||
AC_ARG_ENABLE(sidplay,
|
||||
AS_HELP_STRING([--enable-sidplay],
|
||||
[enable C64 SID support via libsidplay2]),,
|
||||
[enable C64 SID support via libsidplayfp or libsidplay2]),,
|
||||
enable_sidplay=auto)
|
||||
|
||||
AC_ARG_ENABLE(shout,
|
||||
@@ -454,7 +461,7 @@ dnl ---------------------------------------------------------------------------
|
||||
dnl Mandatory Libraries
|
||||
dnl ---------------------------------------------------------------------------
|
||||
|
||||
AX_BOOST_BASE([1.46],, [AC_MSG_ERROR([Boost not found])])
|
||||
AX_BOOST_BASE([1.54],, [AC_MSG_ERROR([Boost not found])])
|
||||
|
||||
AC_ARG_ENABLE(icu,
|
||||
AS_HELP_STRING([--enable-icu],
|
||||
@@ -1005,7 +1012,7 @@ if test x$enable_sidplay != xno && test x$found_sidplayfp = xno; then
|
||||
[found_sidplay=no])
|
||||
|
||||
MPD_AUTO_PRE(sidplay, [sidplay decoder plugin],
|
||||
[libsidplay2 not found])
|
||||
[libsidplay2 or libsidutils not found])
|
||||
fi
|
||||
|
||||
if test x$enable_sidplay != xno && test x$found_sidplayfp = xno; then
|
||||
@@ -1017,10 +1024,11 @@ if test x$enable_sidplay != xno && test x$found_sidplayfp = xno; then
|
||||
fi
|
||||
|
||||
if test x$enable_sidplay = xyes; then
|
||||
SIDPLAY_LIBS="$SIDPLAY_LIBS -lresid-builder"
|
||||
AC_DEFINE(ENABLE_SIDPLAY, 1, [Define for libsidplay2 support])
|
||||
AC_DEFINE(ENABLE_SIDPLAY, 1, [Define for libsidplayfp or libsidplay2 support])
|
||||
if test x$found_sidplayfp = xyes; then
|
||||
AC_DEFINE(HAVE_SIDPLAYFP, 1, [Define if libsidplayfp is used instead of libsidplay2])
|
||||
else
|
||||
SIDPLAY_LIBS="$SIDPLAY_LIBS -lresid-builder"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@@ -113,7 +113,7 @@
|
||||
<para>
|
||||
<varname>musicbrainz_artistid</varname>: the artist id in the
|
||||
<ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
@@ -122,7 +122,7 @@
|
||||
<para>
|
||||
<varname>musicbrainz_albumid</varname>: the album id in the
|
||||
<ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
@@ -131,7 +131,7 @@
|
||||
<para>
|
||||
<varname>musicbrainz_albumartistid</varname>: the album artist
|
||||
id in the <ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
@@ -140,7 +140,7 @@
|
||||
<para>
|
||||
<varname>musicbrainz_trackid</varname>: the track id in the
|
||||
<ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
@@ -149,7 +149,7 @@
|
||||
<para>
|
||||
<varname>musicbrainz_releasetrackid</varname>: the release track
|
||||
id in the <ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
url="https://picard.musicbrainz.org/docs/mappings/">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
95
doc/user.xml
95
doc/user.xml
@@ -66,6 +66,26 @@
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="install_android">
|
||||
<title>Installing on Android</title>
|
||||
|
||||
<para>
|
||||
An experimental Android build is available on <ulink
|
||||
url="https://play.google.com/store/apps/details?id=org.musicpd">Google
|
||||
Play</ulink>. After installing and launching it, MPD will
|
||||
scan the music in your <filename>Music</filename> directory
|
||||
and you can control it as usual with a MPD client.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you need to tweak the configuration, you can create a file
|
||||
called <filename>mpd.conf</filename> on the data partition
|
||||
(the directory which is returned by Android's <ulink
|
||||
url="https://developer.android.com/reference/android/os/Environment.html#getExternalStorageDirectory()">getExternalStorageDirectory()</ulink>
|
||||
API function).
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="install_source">
|
||||
<title>Compiling from source</title>
|
||||
|
||||
@@ -94,7 +114,7 @@ cd mpd-version</programlisting>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<ulink url="http://www.boost.org/">Boost 1.46</ulink>
|
||||
<ulink url="http://www.boost.org/">Boost 1.54</ulink>
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
@@ -248,6 +268,59 @@ apt-get install g++ \
|
||||
script.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="android_build">
|
||||
<title>Compiling for Android</title>
|
||||
|
||||
<para>
|
||||
MPD can be compiled as an Android app. It can be installed
|
||||
easily with <link linkend="install_android">Google
|
||||
Play</link>, but if you want to build it from source, follow
|
||||
this section.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
You need:
|
||||
</para>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
Android SDK
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<ulink
|
||||
url="https://developer.android.com/ndk/downloads/index.html">Android
|
||||
NDK</ulink>
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para>
|
||||
Just like with the native build, unpack the
|
||||
<application>MPD</application> source tarball and change
|
||||
into the directory. Then, instead of
|
||||
<command>./configure</command>, type:
|
||||
</para>
|
||||
|
||||
<programlisting>./android/build.py SDK_PATH NDK_PATH ABI
|
||||
make android/build/mpd-debug.apk</programlisting>
|
||||
|
||||
<para>
|
||||
<varname>SDK_PATH</varname> is the absolute path where you
|
||||
installed the Android SDK; <varname>NDK_PATH</varname> is
|
||||
the Android NDK installation path; <varname>ABI</varname> is
|
||||
the Android ABI to be built, e.g. "armeabi-v7a".
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This downloads various library sources, and then configures
|
||||
and builds <application>MPD</application>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section id="systemd_socket">
|
||||
@@ -323,7 +396,9 @@ systemctl start mpd.socket</programlisting>
|
||||
<application>MPD</application> as a user daemon (and not as a
|
||||
system daemon), the configuration is read from
|
||||
<filename>$XDG_CONFIG_HOME/mpd/mpd.conf</filename> (usually
|
||||
<filename>~/.config/mpd/mpd.conf</filename>).
|
||||
<filename>~/.config/mpd/mpd.conf</filename>). On Android,
|
||||
<filename>mpd.conf</filename> will be loaded from the
|
||||
top-level directory of the data partition.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -2012,13 +2087,6 @@ run</programlisting>
|
||||
database.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Note that unless overridden by the below settings (e.g. by
|
||||
setting them to a blank value), general curl configuration
|
||||
from environment variables such as http_proxy or specified
|
||||
in ~/.curlrc will be in effect.
|
||||
</para>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
@@ -2046,6 +2114,15 @@ run</programlisting>
|
||||
<application>MPD</application> instance.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>password</varname>
|
||||
</entry>
|
||||
<entry>
|
||||
The password used to log in to the "master"
|
||||
<application>MPD</application> instance.
|
||||
</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>keepalive</varname>
|
||||
|
@@ -1,24 +1,37 @@
|
||||
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=[],
|
||||
autogen=False,
|
||||
autoreconf=False,
|
||||
cppflags='',
|
||||
ldflags='',
|
||||
libs='',
|
||||
subdirs=None,
|
||||
**kwargs):
|
||||
Project.__init__(self, url, md5, installed, **kwargs)
|
||||
MakeProject.__init__(self, url, md5, installed, **kwargs)
|
||||
self.configure_args = configure_args
|
||||
self.autogen = autogen
|
||||
self.autoreconf = autoreconf
|
||||
self.cppflags = cppflags
|
||||
self.ldflags = ldflags
|
||||
self.libs = libs
|
||||
self.subdirs = subdirs
|
||||
|
||||
def build(self, toolchain):
|
||||
def configure(self, toolchain):
|
||||
src = self.unpack(toolchain)
|
||||
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(['automake', '--add-missing', '--force-missing', '--foreign'], cwd=src)
|
||||
subprocess.check_call(['autoconf'], cwd=src)
|
||||
if self.autoreconf:
|
||||
subprocess.check_call(['autoreconf', '-vif'], cwd=src)
|
||||
|
||||
build = self.make_build_path(toolchain)
|
||||
|
||||
@@ -29,8 +42,8 @@ class AutotoolsProject(Project):
|
||||
'CFLAGS=' + toolchain.cflags,
|
||||
'CXXFLAGS=' + toolchain.cxxflags,
|
||||
'CPPFLAGS=' + toolchain.cppflags + ' ' + self.cppflags,
|
||||
'LDFLAGS=' + toolchain.ldflags,
|
||||
'LIBS=' + toolchain.libs,
|
||||
'LDFLAGS=' + toolchain.ldflags + ' ' + self.ldflags,
|
||||
'LIBS=' + toolchain.libs + ' ' + self.libs,
|
||||
'AR=' + toolchain.ar,
|
||||
'RANLIB=' + toolchain.ranlib,
|
||||
'STRIP=' + toolchain.strip,
|
||||
@@ -40,7 +53,12 @@ class AutotoolsProject(Project):
|
||||
] + self.configure_args
|
||||
|
||||
subprocess.check_call(configure, cwd=build, env=toolchain.env)
|
||||
subprocess.check_call(['/usr/bin/make', '--quiet', '-j12'],
|
||||
cwd=build, env=toolchain.env)
|
||||
subprocess.check_call(['/usr/bin/make', '--quiet', 'install'],
|
||||
cwd=build, env=toolchain.env)
|
||||
return build
|
||||
|
||||
def build(self, toolchain):
|
||||
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)
|
||||
|
@@ -21,6 +21,8 @@ class FfmpegProject(Project):
|
||||
|
||||
if toolchain.is_arm:
|
||||
arch = 'arm'
|
||||
elif toolchain.is_aarch64:
|
||||
arch = 'aarch64'
|
||||
else:
|
||||
arch = 'x86'
|
||||
|
||||
|
@@ -1,28 +1,54 @@
|
||||
import re
|
||||
from os.path import abspath
|
||||
|
||||
from build.project import Project
|
||||
from build.zlib import ZlibProject
|
||||
from build.meson import MesonProject
|
||||
from build.autotools import AutotoolsProject
|
||||
from build.ffmpeg import FfmpegProject
|
||||
from build.boost import BoostProject
|
||||
|
||||
libmpdclient = MesonProject(
|
||||
'https://www.musicpd.org/download/libmpdclient/2/libmpdclient-2.14.tar.xz',
|
||||
'0a84e2791bfe3077cf22ee1784c805d5bb550803dffe56a39aa3690a38061372',
|
||||
'lib/libmpdclient.a',
|
||||
)
|
||||
|
||||
libogg = AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/ogg/libogg-1.3.2.tar.xz',
|
||||
'5c3a34309d8b98640827e5d0991a4015',
|
||||
'http://downloads.xiph.org/releases/ogg/libogg-1.3.3.tar.xz',
|
||||
'4f3fc6178a533d392064f14776b23c397ed4b9f48f5de297aba73b643f955c08',
|
||||
'lib/libogg.a',
|
||||
['--disable-shared', '--enable-static'],
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
],
|
||||
)
|
||||
|
||||
libvorbis = AutotoolsProject(
|
||||
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.5.tar.xz',
|
||||
'28cb28097c07a735d6af56e598e1c90f',
|
||||
'http://downloads.xiph.org/releases/vorbis/libvorbis-1.3.6.tar.xz',
|
||||
'af00bb5a784e7c9e69f56823de4637c350643deedaf333d0fa86ecdba6fcb415',
|
||||
'lib/libvorbis.a',
|
||||
['--disable-shared', '--enable-static'],
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
],
|
||||
|
||||
edits={
|
||||
# this option is not understood by clang
|
||||
'configure': lambda data: data.replace('-mno-ieee-fp', ' '),
|
||||
}
|
||||
)
|
||||
|
||||
opus = AutotoolsProject(
|
||||
'https://archive.mozilla.org/pub/opus/opus-1.2.1.tar.gz',
|
||||
'cfafd339ccd9c5ef8d6ab15d7e1a412c054bf4cb4ecbbbcc78c12ef2def70732',
|
||||
'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(
|
||||
@@ -32,7 +58,9 @@ flac = AutotoolsProject(
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--disable-xmms-plugin', '--disable-cpplibs',
|
||||
'--disable-doxygen-docs',
|
||||
],
|
||||
subdirs=['include', 'src/libFLAC'],
|
||||
)
|
||||
|
||||
zlib = ZlibProject(
|
||||
@@ -45,21 +73,36 @@ libid3tag = AutotoolsProject(
|
||||
'ftp://ftp.mars.org/pub/mpeg/libid3tag-0.15.1b.tar.gz',
|
||||
'e5808ad997ba32c498803822078748c3',
|
||||
'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,
|
||||
|
||||
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(
|
||||
'ftp://ftp.mars.org/pub/mpeg/libmad-0.15.1b.tar.gz',
|
||||
'1be543bc30c56fb6bea1d7bf6a64e66c',
|
||||
'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,
|
||||
)
|
||||
|
||||
liblame = AutotoolsProject(
|
||||
'http://downloads.sourceforge.net/project/lame/lame/3.99/lame-3.99.5.tar.gz',
|
||||
'24346b4158e4af3bd9f2e194bb23eb473c75fb7377011523353196b19b9a23ff',
|
||||
'http://downloads.sourceforge.net/project/lame/lame/3.100/lame-3.100.tar.gz',
|
||||
'ddfe36cab873794038ae2c1210557ad34857a4b6bdc515785d1da9e175b1da1e',
|
||||
'lib/libmp3lame.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -69,8 +112,8 @@ liblame = AutotoolsProject(
|
||||
)
|
||||
|
||||
ffmpeg = FfmpegProject(
|
||||
'http://ffmpeg.org/releases/ffmpeg-3.4.1.tar.xz',
|
||||
'5a77278a63741efa74e26bf197b9bb09ac6381b9757391b922407210f0f991c0',
|
||||
'http://ffmpeg.org/releases/ffmpeg-4.0.2.tar.xz',
|
||||
'a95c0cc9eb990e94031d2183f2e6e444cc61c99f6f182d1575c433d62afb2f97',
|
||||
'lib/libavcodec.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -89,21 +132,217 @@ ffmpeg = FfmpegProject(
|
||||
'--disable-pixelutils',
|
||||
'--disable-network',
|
||||
'--disable-encoders',
|
||||
'--disable-muxers',
|
||||
'--disable-protocols',
|
||||
'--disable-devices',
|
||||
'--disable-filters',
|
||||
'--disable-v4l2_m2m',
|
||||
|
||||
# clang misinterprets the "B0" in hevc_mvs.c as binary
|
||||
# literal, which breaks the build; but we don't need that
|
||||
# video codec anyway
|
||||
'--disable-parser=bmp',
|
||||
'--disable-parser=cavsvideo',
|
||||
'--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=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=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=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=tiertexseqvideo',
|
||||
'--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(
|
||||
'http://curl.haxx.se/download/curl-7.57.0.tar.xz',
|
||||
'f5f6fd3c72b7b8389969f4fb671ed8532fa9b5bb7a5cae7ca89bc1cea45c7878',
|
||||
'http://curl.haxx.se/download/curl-7.61.0.tar.xz',
|
||||
'ef6e55192d04713673b4409ccbcb4cb6cd723137d6e10ca45b0c593a454e1720',
|
||||
'lib/libcurl.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
@@ -114,16 +353,46 @@ curl = AutotoolsProject(
|
||||
'--disable-ldap', '--disable-ldaps',
|
||||
'--disable-rtsp', '--disable-proxy', '--disable-dict', '--disable-telnet',
|
||||
'--disable-tftp', '--disable-pop3', '--disable-imap', '--disable-smtp',
|
||||
'--disable-smb',
|
||||
'--disable-gopher',
|
||||
'--disable-manual',
|
||||
'--disable-threaded-resolver', '--disable-verbose', '--disable-sspi',
|
||||
'--disable-crypto-auth', '--disable-ntlm-wb', '--disable-tls-srp', '--disable-cookies',
|
||||
'--without-ssl', '--without-gnutls', '--without-nss', '--without-libssh2',
|
||||
],
|
||||
|
||||
patches='src/lib/curl/patches',
|
||||
)
|
||||
|
||||
libexpat = AutotoolsProject(
|
||||
'https://github.com/libexpat/libexpat/releases/download/R_2_2_6/expat-2.2.6.tar.bz2',
|
||||
'17b43c2716d521369f82fc2dc70f359860e90fa440bea65b3b85f0b246ea81f2',
|
||||
'lib/libexpat.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--without-docbook',
|
||||
],
|
||||
)
|
||||
|
||||
libnfs = AutotoolsProject(
|
||||
'https://github.com/sahlberg/libnfs/archive/libnfs-3.0.0.tar.gz',
|
||||
'445d92c5fc55e4a5b115e358e60486cf8f87ee50e0103d46a02e7fb4618566a5',
|
||||
'lib/libnfs.a',
|
||||
[
|
||||
'--disable-shared', '--enable-static',
|
||||
'--disable-debug',
|
||||
|
||||
# work around -Wtautological-compare
|
||||
'--disable-werror',
|
||||
|
||||
'--disable-utils', '--disable-examples',
|
||||
],
|
||||
base='libnfs-libnfs-3.0.0',
|
||||
autoreconf=True,
|
||||
)
|
||||
|
||||
boost = BoostProject(
|
||||
'http://downloads.sourceforge.net/project/boost/boost/1.65.1/boost_1_65_1.tar.bz2',
|
||||
'9807a5d16566c57fd74fb522764e0b134a8bbe6b6e8967b83afefd30dcd3be81',
|
||||
'http://downloads.sourceforge.net/project/boost/boost/1.68.0/boost_1_68_0.tar.bz2',
|
||||
'7f6130bc3cf65f56a618888ce9d5ea704fa10b462be126ad053e80e553d6d8b7',
|
||||
'include/boost/version.hpp',
|
||||
)
|
||||
|
28
python/build/makeproject.py
Normal file
28
python/build/makeproject.py
Normal 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))
|
102
python/build/meson.py
Normal file
102
python/build/meson.py
Normal file
@@ -0,0 +1,102 @@
|
||||
import os.path, subprocess, sys
|
||||
|
||||
from build.project import Project
|
||||
|
||||
class MesonProject(Project):
|
||||
def __init__(self, url, md5, installed, configure_args=[],
|
||||
**kwargs):
|
||||
Project.__init__(self, url, md5, installed, **kwargs)
|
||||
self.configure_args = configure_args
|
||||
|
||||
def _make_cross_file(self, toolchain):
|
||||
if toolchain.is_windows:
|
||||
system = 'windows'
|
||||
else:
|
||||
system = 'linux'
|
||||
|
||||
if toolchain.is_arm:
|
||||
cpu_family = 'arm'
|
||||
if toolchain.is_armv7:
|
||||
cpu = 'armv7'
|
||||
else:
|
||||
cpu = 'armv6'
|
||||
elif toolchain.is_aarch64:
|
||||
cpu_family = 'aarch64'
|
||||
cpu = 'arm64-v8a'
|
||||
else:
|
||||
cpu_family = 'x86'
|
||||
if 'x86_64' in toolchain.arch:
|
||||
cpu = 'x86_64'
|
||||
else:
|
||||
cpu = 'i686'
|
||||
|
||||
# TODO: support more CPUs
|
||||
endian = 'little'
|
||||
|
||||
# TODO: write pkg-config wrapper
|
||||
|
||||
path = os.path.join(toolchain.build_path, 'meson.cross')
|
||||
os.makedirs(toolchain.build_path, exist_ok=True)
|
||||
with open(path, 'w') as f:
|
||||
f.write("""
|
||||
[binaries]
|
||||
c = '%s'
|
||||
cpp = '%s'
|
||||
ar = '%s'
|
||||
strip = '%s'
|
||||
|
||||
[properties]
|
||||
root = '%s'
|
||||
|
||||
c_args = %s
|
||||
c_link_args = %s
|
||||
|
||||
cpp_args = %s
|
||||
cpp_link_args = %s
|
||||
|
||||
# Keep Meson from executing Android-x86 test binariees
|
||||
needs_exe_wrapper = true
|
||||
|
||||
[host_machine]
|
||||
system = '%s'
|
||||
cpu_family = '%s'
|
||||
cpu = '%s'
|
||||
endian = '%s'
|
||||
""" % (toolchain.cc, toolchain.cxx, toolchain.ar, toolchain.strip,
|
||||
toolchain.install_prefix,
|
||||
repr((toolchain.cppflags + ' ' + toolchain.cflags).split()),
|
||||
repr(toolchain.ldflags.split()),
|
||||
repr((toolchain.cppflags + ' ' + toolchain.cxxflags).split()),
|
||||
repr(toolchain.ldflags.split()),
|
||||
system, cpu_family, cpu, endian))
|
||||
return path
|
||||
|
||||
def configure(self, toolchain):
|
||||
src = self.unpack(toolchain)
|
||||
cross_file = self._make_cross_file(toolchain)
|
||||
build = self.make_build_path(toolchain)
|
||||
configure = [
|
||||
'meson',
|
||||
src, build,
|
||||
|
||||
'--prefix', toolchain.install_prefix,
|
||||
|
||||
# this is necessary because Meson uses Debian's build machine
|
||||
# MultiArch path (e.g. "lib/x86_64-linux-gnu") for cross
|
||||
# builds, which is obviously wrong
|
||||
'--libdir', 'lib',
|
||||
|
||||
'--buildtype', 'plain',
|
||||
|
||||
'--default-library=static',
|
||||
|
||||
'--cross-file', cross_file,
|
||||
] + self.configure_args
|
||||
|
||||
subprocess.check_call(configure, env=toolchain.env)
|
||||
return build
|
||||
|
||||
def build(self, toolchain):
|
||||
build = self.configure(toolchain)
|
||||
subprocess.check_call(['ninja', 'install'],
|
||||
cwd=build, env=toolchain.env)
|
@@ -3,10 +3,13 @@ import re
|
||||
|
||||
from build.download import download_and_verify
|
||||
from build.tar import untar
|
||||
from build.quilt import push_all
|
||||
|
||||
class Project:
|
||||
def __init__(self, url, md5, installed, name=None, version=None,
|
||||
base=None,
|
||||
patches=None,
|
||||
edits=None,
|
||||
use_cxx=False):
|
||||
if base is None:
|
||||
basename = os.path.basename(url)
|
||||
@@ -17,7 +20,7 @@ class Project:
|
||||
self.base = base
|
||||
|
||||
if name is None or version is None:
|
||||
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?)$', self.base)
|
||||
m = re.match(r'^([-\w]+)-(\d[\d.]*[a-z]?[\d.]*)$', self.base)
|
||||
if name is None: name = m.group(1)
|
||||
if version is None: version = m.group(2)
|
||||
|
||||
@@ -28,6 +31,11 @@ class Project:
|
||||
self.md5 = md5
|
||||
self.installed = installed
|
||||
|
||||
if patches is not None:
|
||||
srcdir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
patches = os.path.join(srcdir, patches)
|
||||
self.patches = patches
|
||||
self.edits = edits
|
||||
self.use_cxx = use_cxx
|
||||
|
||||
def download(self, toolchain):
|
||||
@@ -47,7 +55,21 @@ class Project:
|
||||
parent_path = toolchain.src_path
|
||||
else:
|
||||
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.patches is not None:
|
||||
push_all(toolchain, path, self.patches)
|
||||
|
||||
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):
|
||||
path = os.path.join(toolchain.build_path, self.base)
|
||||
|
9
python/build/quilt.py
Normal file
9
python/build/quilt.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import subprocess
|
||||
|
||||
def run_quilt(toolchain, cwd, patches_path, *args):
|
||||
env = dict(toolchain.env)
|
||||
env['QUILT_PATCHES'] = patches_path
|
||||
subprocess.check_call(['quilt'] + list(args), cwd=cwd, env=env)
|
||||
|
||||
def push_all(toolchain, src_path, patches_path):
|
||||
run_quilt(toolchain, src_path, patches_path, 'push', '-a')
|
@@ -19,9 +19,9 @@
|
||||
|
||||
#include "AudioFormat.hxx"
|
||||
#include "util/StringBuffer.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void
|
||||
AudioFormat::ApplyMask(AudioFormat mask) noexcept
|
||||
@@ -44,21 +44,16 @@ AudioFormat::ApplyMask(AudioFormat mask) noexcept
|
||||
StringBuffer<24>
|
||||
ToString(const AudioFormat af) noexcept
|
||||
{
|
||||
StringBuffer<24> buffer;
|
||||
|
||||
if (af.format == SampleFormat::DSD && af.sample_rate > 0 &&
|
||||
af.sample_rate % 44100 == 0) {
|
||||
/* use shortcuts such as "dsd64" which implies the
|
||||
sample rate */
|
||||
snprintf(buffer.data(), buffer.capacity(), "dsd%u:%u",
|
||||
af.sample_rate * 8 / 44100,
|
||||
af.channels);
|
||||
return buffer;
|
||||
return StringFormat<24>("dsd%u:%u",
|
||||
af.sample_rate * 8 / 44100,
|
||||
af.channels);
|
||||
}
|
||||
|
||||
snprintf(buffer.data(), buffer.capacity(), "%u:%s:%u",
|
||||
af.sample_rate, sample_format_to_string(af.format),
|
||||
af.channels);
|
||||
|
||||
return buffer;
|
||||
return StringFormat<24>("%u:%s:%u",
|
||||
af.sample_rate, sample_format_to_string(af.format),
|
||||
af.channels);
|
||||
}
|
||||
|
@@ -23,7 +23,6 @@
|
||||
#include "pcm/SampleFormat.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
@@ -27,12 +27,17 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
static struct {
|
||||
static struct IOThread {
|
||||
Mutex mutex;
|
||||
Cond cond;
|
||||
|
||||
EventLoop *loop;
|
||||
Thread thread;
|
||||
|
||||
IOThread():thread(BIND_THIS_METHOD(Run)) {}
|
||||
|
||||
private:
|
||||
void Run() noexcept;
|
||||
} io;
|
||||
|
||||
void
|
||||
@@ -44,15 +49,15 @@ io_thread_run(void)
|
||||
io.loop->Run();
|
||||
}
|
||||
|
||||
static void
|
||||
io_thread_func(gcc_unused void *arg)
|
||||
inline void
|
||||
IOThread::Run() noexcept
|
||||
{
|
||||
SetThreadName("io");
|
||||
|
||||
/* lock+unlock to synchronize with io_thread_start(), to be
|
||||
sure that io.thread is set */
|
||||
io.mutex.lock();
|
||||
io.mutex.unlock();
|
||||
mutex.lock();
|
||||
mutex.unlock();
|
||||
|
||||
io_thread_run();
|
||||
}
|
||||
@@ -73,7 +78,7 @@ io_thread_start()
|
||||
assert(!io.thread.IsDefined());
|
||||
|
||||
const std::lock_guard<Mutex> protect(io.mutex);
|
||||
io.thread.Start(io_thread_func, nullptr);
|
||||
io.thread.Start();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -103,8 +108,12 @@ io_thread_get() noexcept
|
||||
return *io.loop;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
bool
|
||||
io_thread_inside() noexcept
|
||||
{
|
||||
return io.thread.IsInside();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#ifndef MPD_IO_THREAD_HXX
|
||||
#define MPD_IO_THREAD_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "Compiler.h"
|
||||
|
||||
class EventLoop;
|
||||
@@ -53,6 +54,8 @@ gcc_const
|
||||
EventLoop &
|
||||
io_thread_get() noexcept;
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
/**
|
||||
* Is the current thread the I/O thread?
|
||||
*/
|
||||
@@ -61,3 +64,5 @@ bool
|
||||
io_thread_inside() noexcept;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@@ -23,7 +23,7 @@
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "ls.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
|
||||
#ifdef ENABLE_DATABASE
|
||||
#include "storage/StorageInterface.hxx"
|
||||
@@ -83,7 +83,7 @@ LocateUri(const char *uri, const Client *client
|
||||
)
|
||||
{
|
||||
/* skip the obsolete "file://" prefix */
|
||||
const char *path_utf8 = StringAfterPrefix(uri, "file://");
|
||||
const char *path_utf8 = StringAfterPrefixCaseASCII(uri, "file://");
|
||||
if (path_utf8 != nullptr) {
|
||||
if (!PathTraitsUTF8::IsAbsolute(path_utf8))
|
||||
throw std::runtime_error("Malformed file:// URI");
|
||||
|
51
src/Main.cxx
51
src/Main.cxx
@@ -50,6 +50,7 @@
|
||||
#include "unix/SignalHandlers.hxx"
|
||||
#include "system/FatalError.hxx"
|
||||
#include "thread/Slack.hxx"
|
||||
#include "net/Init.hxx"
|
||||
#include "lib/icu/Init.hxx"
|
||||
#include "config/ConfigGlobal.hxx"
|
||||
#include "config/Param.hxx"
|
||||
@@ -106,15 +107,6 @@
|
||||
#include <locale.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#ifdef __BLOCKS__
|
||||
#include <dispatch/dispatch.h>
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
static constexpr size_t KILOBYTE = 1024;
|
||||
@@ -284,25 +276,6 @@ glue_state_file_init()
|
||||
instance->state_file->Read();
|
||||
}
|
||||
|
||||
/**
|
||||
* Windows-only initialization of the Winsock2 library.
|
||||
*/
|
||||
static void winsock_init(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA sockinfo;
|
||||
|
||||
int retval = WSAStartup(MAKEWORD(2, 2), &sockinfo);
|
||||
if(retval != 0)
|
||||
FormatFatalError("Attempt to open Winsock2 failed; error code %d",
|
||||
retval);
|
||||
|
||||
if (LOBYTE(sockinfo.wVersion) != 2)
|
||||
FatalError("We use Winsock2 but your version is either too new "
|
||||
"or old; please install Winsock 2.x");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the decoder and player core, including the music pipe.
|
||||
*/
|
||||
@@ -451,7 +424,8 @@ try {
|
||||
|
||||
IcuInit();
|
||||
|
||||
winsock_init();
|
||||
const ScopeNetInit net_init;
|
||||
|
||||
io_thread_init();
|
||||
config_global_init();
|
||||
|
||||
@@ -505,21 +479,8 @@ try {
|
||||
daemonize_begin(options.daemon);
|
||||
#endif
|
||||
|
||||
#ifdef __BLOCKS__
|
||||
/* Runs the OS X native event loop in the main thread, and runs
|
||||
the rest of mpd_main on a new thread. This lets CoreAudio receive
|
||||
route change notifications (e.g. plugging or unplugging headphones).
|
||||
All hardware output on OS X ultimately uses CoreAudio internally.
|
||||
This must be run after forking; if dispatch is called before forking,
|
||||
the child process will have a broken internal dispatch state. */
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
exit(mpd_main_after_fork(config));
|
||||
});
|
||||
dispatch_main();
|
||||
return EXIT_FAILURE; // unreachable, because dispatch_main never returns
|
||||
#else
|
||||
return mpd_main_after_fork(config);
|
||||
#endif
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
LogError(e);
|
||||
return EXIT_FAILURE;
|
||||
@@ -702,10 +663,6 @@ try {
|
||||
daemonize_finish();
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
||||
IcuFinish();
|
||||
|
||||
log_deinit();
|
||||
|
@@ -95,6 +95,7 @@ public:
|
||||
*/
|
||||
gcc_pure
|
||||
const MusicChunk *Peek() const noexcept {
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
return head;
|
||||
}
|
||||
|
||||
@@ -120,6 +121,7 @@ public:
|
||||
*/
|
||||
gcc_pure
|
||||
unsigned GetSize() const noexcept {
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
@@ -24,6 +24,8 @@
|
||||
#include "tag/Tag.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
#include "util/StringAPI.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/StringView.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/TimeParser.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
@@ -59,13 +61,13 @@ locate_parse_type(const char *str) noexcept
|
||||
|
||||
SongFilter::Item::Item(unsigned _tag, const char *_value, bool _fold_case)
|
||||
:tag(_tag),
|
||||
value(AllocatedString<>::Duplicate(_value)),
|
||||
value(_value),
|
||||
fold_case(_fold_case ? IcuCompare(value.c_str()) : IcuCompare())
|
||||
{
|
||||
}
|
||||
|
||||
SongFilter::Item::Item(unsigned _tag, time_t _time)
|
||||
:tag(_tag), value(nullptr), time(_time)
|
||||
:tag(_tag), time(_time)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -274,3 +276,33 @@ SongFilter::GetBase() const noexcept
|
||||
|
||||
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;
|
||||
}
|
||||
|
@@ -21,9 +21,9 @@
|
||||
#define MPD_SONG_FILTER_HXX
|
||||
|
||||
#include "lib/icu/Compare.hxx"
|
||||
#include "util/AllocatedString.hxx"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
#include <stdint.h>
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
class Item {
|
||||
uint8_t tag;
|
||||
|
||||
AllocatedString<> value;
|
||||
std::string value;
|
||||
|
||||
/**
|
||||
* 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, time_t time);
|
||||
|
||||
Item(const Item &other) = delete;
|
||||
Item(Item &&) = default;
|
||||
|
||||
Item &operator=(const Item &other) = delete;
|
||||
|
||||
unsigned GetTag() const {
|
||||
return tag;
|
||||
}
|
||||
@@ -157,6 +152,13 @@ public:
|
||||
*/
|
||||
gcc_pure
|
||||
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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "tag/TagBuilder.hxx"
|
||||
#include "util/StringUtil.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/NumberParser.hxx"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@@ -94,7 +95,7 @@ song_load(TextFile &file, const char *uri)
|
||||
if ((type = tag_name_parse(line)) != TAG_NUM_OF_ITEM_TYPES) {
|
||||
tag.AddItem(type, value);
|
||||
} else if (strcmp(line, "Time") == 0) {
|
||||
tag.SetDuration(SignedSongTime::FromS(atof(value)));
|
||||
tag.SetDuration(SignedSongTime::FromS(ParseDouble(value)));
|
||||
} else if (strcmp(line, "Playlist") == 0) {
|
||||
tag.SetHasPlaylist(strcmp(value, "yes") == 0);
|
||||
} else if (strcmp(line, SONG_MTIME) == 0) {
|
||||
|
@@ -162,7 +162,7 @@ Bzip2InputStream::FillBuffer()
|
||||
if (bzstream.avail_in > 0)
|
||||
return true;
|
||||
|
||||
size_t count = archive->istream->Read(buffer, sizeof(buffer));
|
||||
size_t count = archive->istream->LockRead(buffer, sizeof(buffer));
|
||||
if (count == 0)
|
||||
return false;
|
||||
|
||||
@@ -174,6 +174,8 @@ Bzip2InputStream::FillBuffer()
|
||||
size_t
|
||||
Bzip2InputStream::Read(void *ptr, size_t length)
|
||||
{
|
||||
const ScopeUnlock unlock(mutex);
|
||||
|
||||
int bz_result;
|
||||
size_t nbytes = 0;
|
||||
|
||||
@@ -224,4 +226,3 @@ const ArchivePlugin bz2_archive_plugin = {
|
||||
bz2_open,
|
||||
bz2_extensions,
|
||||
};
|
||||
|
||||
|
@@ -115,7 +115,12 @@ Iso9660ArchiveFile::Visit(char *path, size_t length, size_t capacity,
|
||||
visitor.VisitArchiveEntry(path + 1);
|
||||
}
|
||||
}
|
||||
|
||||
#if LIBCDIO_VERSION_NUM >= 20000
|
||||
iso9660_filelist_free(entlist);
|
||||
#else
|
||||
_cdio_list_free (entlist, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
static ArchiveFile *
|
||||
@@ -182,6 +187,8 @@ Iso9660ArchiveFile::OpenStream(const char *pathname,
|
||||
size_t
|
||||
Iso9660InputStream::Read(void *ptr, size_t read_size)
|
||||
{
|
||||
const ScopeUnlock unlock(mutex);
|
||||
|
||||
int readed = 0;
|
||||
int no_blocks, cur_block;
|
||||
size_t left_bytes = statbuf->size - offset;
|
||||
|
@@ -138,6 +138,8 @@ ZzipArchiveFile::OpenStream(const char *pathname,
|
||||
size_t
|
||||
ZzipInputStream::Read(void *ptr, size_t read_size)
|
||||
{
|
||||
const ScopeUnlock unlock(mutex);
|
||||
|
||||
int ret = zzip_file_read(file, ptr, read_size);
|
||||
if (ret < 0)
|
||||
throw std::runtime_error("zzip_file_read() has failed");
|
||||
@@ -155,6 +157,8 @@ ZzipInputStream::IsEOF() noexcept
|
||||
void
|
||||
ZzipInputStream::Seek(offset_type new_offset)
|
||||
{
|
||||
const ScopeUnlock unlock(mutex);
|
||||
|
||||
zzip_off_t ofs = zzip_seek(file, new_offset, SEEK_SET);
|
||||
if (ofs < 0)
|
||||
throw std::runtime_error("zzip_seek() has failed");
|
||||
|
@@ -123,6 +123,10 @@ ToAck(std::exception_ptr ep) noexcept
|
||||
return ACK_ERROR_SYSTEM;
|
||||
} catch (const std::invalid_argument &e) {
|
||||
return ACK_ERROR_ARG;
|
||||
} catch (const std::length_error &e) {
|
||||
return ACK_ERROR_ARG;
|
||||
} catch (const std::out_of_range &e) {
|
||||
return ACK_ERROR_ARG;
|
||||
#ifdef GLIBCXX_49X
|
||||
} catch (const std::exception &e) {
|
||||
#else
|
||||
|
@@ -82,6 +82,7 @@ class ProxyDatabase final : public Database, SocketMonitor, IdleMonitor {
|
||||
DatabaseListener &listener;
|
||||
|
||||
const std::string host;
|
||||
const std::string password;
|
||||
const unsigned port;
|
||||
const bool keepalive;
|
||||
|
||||
@@ -169,6 +170,13 @@ static constexpr struct {
|
||||
#if LIBMPDCLIENT_CHECK_VERSION(2,10,0)
|
||||
{ TAG_MUSICBRAINZ_RELEASETRACKID,
|
||||
MPD_TAG_MUSICBRAINZ_RELEASETRACKID },
|
||||
#endif
|
||||
#if LIBMPDCLIENT_CHECK_VERSION(2,11,0)
|
||||
{ TAG_ARTIST_SORT, MPD_TAG_ARTIST_SORT },
|
||||
{ TAG_ALBUM_ARTIST_SORT, MPD_TAG_ALBUM_ARTIST_SORT },
|
||||
#endif
|
||||
#if LIBMPDCLIENT_CHECK_VERSION(2,12,0)
|
||||
{ TAG_ALBUM_SORT, MPD_TAG_ALBUM_SORT },
|
||||
#endif
|
||||
{ TAG_NUM_OF_ITEM_TYPES, MPD_TAG_COUNT }
|
||||
};
|
||||
@@ -325,12 +333,41 @@ SendConstraints(mpd_connection *connection, const DatabaseSelection &selection)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
SendGroupMask(mpd_connection *connection, tag_mask_t mask)
|
||||
{
|
||||
#if LIBMPDCLIENT_CHECK_VERSION(2,12,0)
|
||||
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
|
||||
if ((mask & (tag_mask_t(1) << i)) == 0)
|
||||
continue;
|
||||
|
||||
const auto tag = Convert(TagType(i));
|
||||
if (tag == MPD_TAG_COUNT)
|
||||
throw std::runtime_error("Unsupported tag");
|
||||
|
||||
if (!mpd_search_add_group_tag(connection, tag))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#else
|
||||
(void)connection;
|
||||
(void)mask;
|
||||
|
||||
if (mask != 0)
|
||||
throw std::runtime_error("Grouping requires libmpdclient 2.12");
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
ProxyDatabase::ProxyDatabase(EventLoop &_loop, DatabaseListener &_listener,
|
||||
const ConfigBlock &block)
|
||||
:Database(proxy_db_plugin),
|
||||
SocketMonitor(_loop), IdleMonitor(_loop),
|
||||
listener(_listener),
|
||||
host(block.GetBlockValue("host", "")),
|
||||
password(block.GetBlockValue("password", "")),
|
||||
port(block.GetBlockValue("port", 0u)),
|
||||
keepalive(block.GetBlockValue("keepalive", false))
|
||||
{
|
||||
@@ -374,6 +411,10 @@ ProxyDatabase::Connect()
|
||||
|
||||
try {
|
||||
CheckError(connection);
|
||||
|
||||
if (!password.empty() &&
|
||||
!mpd_run_password(connection, password.c_str()))
|
||||
ThrowError(connection);
|
||||
} catch (...) {
|
||||
mpd_connection_free(connection);
|
||||
connection = nullptr;
|
||||
@@ -682,7 +723,7 @@ static void
|
||||
SearchSongs(struct mpd_connection *connection,
|
||||
const DatabaseSelection &selection,
|
||||
VisitSong visit_song)
|
||||
{
|
||||
try {
|
||||
assert(selection.recursive);
|
||||
assert(visit_song);
|
||||
|
||||
@@ -709,6 +750,11 @@ SearchSongs(struct mpd_connection *connection,
|
||||
|
||||
if (!mpd_response_finish(connection))
|
||||
ThrowError(connection);
|
||||
} catch (...) {
|
||||
if (connection != nullptr)
|
||||
mpd_search_cancel(connection);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -756,9 +802,9 @@ ProxyDatabase::Visit(const DatabaseSelection &selection,
|
||||
void
|
||||
ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
|
||||
TagType tag_type,
|
||||
gcc_unused tag_mask_t group_mask,
|
||||
tag_mask_t group_mask,
|
||||
VisitTag visit_tag) const
|
||||
{
|
||||
try {
|
||||
// TODO: eliminate the const_cast
|
||||
const_cast<ProxyDatabase *>(this)->EnsureConnected();
|
||||
|
||||
@@ -767,32 +813,47 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
|
||||
throw std::runtime_error("Unsupported tag");
|
||||
|
||||
if (!mpd_search_db_tags(connection, tag_type2) ||
|
||||
!SendConstraints(connection, selection))
|
||||
!SendConstraints(connection, selection) ||
|
||||
!SendGroupMask(connection, group_mask))
|
||||
ThrowError(connection);
|
||||
|
||||
// TODO: use group_mask
|
||||
|
||||
if (!mpd_search_commit(connection))
|
||||
ThrowError(connection);
|
||||
|
||||
while (auto *pair = mpd_recv_pair_tag(connection, tag_type2)) {
|
||||
TagBuilder builder;
|
||||
|
||||
while (auto *pair = mpd_recv_pair(connection)) {
|
||||
AtScopeExit(this, pair) {
|
||||
mpd_return_pair(connection, pair);
|
||||
};
|
||||
|
||||
TagBuilder tag;
|
||||
tag.AddItem(tag_type, pair->value);
|
||||
const auto current_type = tag_name_parse_i(pair->name);
|
||||
if (current_type == TAG_NUM_OF_ITEM_TYPES)
|
||||
continue;
|
||||
|
||||
if (tag.IsEmpty())
|
||||
if (current_type == tag_type && !builder.IsEmpty()) {
|
||||
try {
|
||||
visit_tag(builder.Commit());
|
||||
} catch (...) {
|
||||
mpd_response_finish(connection);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
builder.AddItem(current_type, pair->value);
|
||||
|
||||
if (!builder.HasType(current_type))
|
||||
/* if no tag item has been added, then the
|
||||
given value was not acceptable
|
||||
(e.g. empty); forcefully insert an empty
|
||||
tag in this case, as the caller expects the
|
||||
given tag type to be present */
|
||||
tag.AddEmptyItem(tag_type);
|
||||
builder.AddEmptyItem(current_type);
|
||||
}
|
||||
|
||||
if (!builder.IsEmpty()) {
|
||||
try {
|
||||
visit_tag(tag.Commit());
|
||||
visit_tag(builder.Commit());
|
||||
} catch (...) {
|
||||
mpd_response_finish(connection);
|
||||
throw;
|
||||
@@ -801,6 +862,11 @@ ProxyDatabase::VisitUniqueTags(const DatabaseSelection &selection,
|
||||
|
||||
if (!mpd_response_finish(connection))
|
||||
ThrowError(connection);
|
||||
} catch (...) {
|
||||
if (connection != nullptr)
|
||||
mpd_search_cancel(connection);
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
DatabaseStats
|
||||
|
@@ -82,10 +82,11 @@ directory_save(BufferedOutputStream &os, const Directory &directory)
|
||||
}
|
||||
|
||||
for (const auto &child : directory.children) {
|
||||
os.Format(DIRECTORY_DIR "%s\n", child.GetName());
|
||||
if (child.IsMount())
|
||||
continue;
|
||||
|
||||
if (!child.IsMount())
|
||||
directory_save(os, child);
|
||||
os.Format(DIRECTORY_DIR "%s\n", child.GetName());
|
||||
directory_save(os, child);
|
||||
}
|
||||
|
||||
for (const auto &song : directory.songs)
|
||||
|
@@ -20,18 +20,12 @@
|
||||
#include "config.h"
|
||||
#include "Mount.hxx"
|
||||
#include "PrefixedLightSong.hxx"
|
||||
#include "SongFilter.hxx"
|
||||
#include "db/Selection.hxx"
|
||||
#include "db/LightDirectory.hxx"
|
||||
#include "db/Interface.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
|
||||
#ifdef _LIBCPP_VERSION
|
||||
/* workaround for "error: incomplete type 'PlaylistInfo' used in type
|
||||
trait expression" with libc++ version 3900 (from Android NDK
|
||||
r13b) */
|
||||
#include "db/PlaylistInfo.hxx"
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
struct PrefixedLightDirectory : LightDirectory {
|
||||
@@ -93,5 +87,16 @@ WalkMount(const char *base, const Database &db,
|
||||
vp = std::bind(PrefixVisitPlaylist,
|
||||
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);
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -47,10 +48,6 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
|
||||
unsigned &didreadp,
|
||||
unsigned &totalp) const
|
||||
{
|
||||
// Create request
|
||||
char ofbuf[100], cntbuf[100];
|
||||
sprintf(ofbuf, "%u", offset);
|
||||
sprintf(cntbuf, "%u", count);
|
||||
// Some devices require an empty SortCriteria, else bad params
|
||||
IXML_Document *request =
|
||||
MakeActionHelper("Browse", m_serviceType.c_str(),
|
||||
@@ -58,8 +55,10 @@ ContentDirectoryService::readDirSlice(UpnpClient_Handle hdl,
|
||||
"BrowseFlag", "BrowseDirectChildren",
|
||||
"Filter", "*",
|
||||
"SortCriteria", "",
|
||||
"StartingIndex", ofbuf,
|
||||
"RequestedCount", cntbuf);
|
||||
"StartingIndex",
|
||||
StringFormat<32>("%u", offset).c_str(),
|
||||
"RequestedCount",
|
||||
StringFormat<32>("%u", count).c_str());
|
||||
if (request == nullptr)
|
||||
throw std::runtime_error("UpnpMakeAction() failed");
|
||||
|
||||
@@ -112,15 +111,13 @@ ContentDirectoryService::search(UpnpClient_Handle hdl,
|
||||
unsigned offset = 0, total = -1, count;
|
||||
|
||||
do {
|
||||
char ofbuf[100];
|
||||
sprintf(ofbuf, "%d", offset);
|
||||
|
||||
UniqueIxmlDocument request(MakeActionHelper("Search", m_serviceType.c_str(),
|
||||
"ContainerID", objectId,
|
||||
"SearchCriteria", ss,
|
||||
"Filter", "*",
|
||||
"SortCriteria", "",
|
||||
"StartingIndex", ofbuf,
|
||||
"StartingIndex",
|
||||
StringFormat<32>("%u", offset).c_str(),
|
||||
"RequestedCount", "0")); // Setting a value here gets twonky into fits
|
||||
if (!request)
|
||||
throw std::runtime_error("UpnpMakeAction() failed");
|
||||
|
@@ -26,8 +26,8 @@
|
||||
#include "ExcludeList.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/NarrowPath.hxx"
|
||||
#include "fs/io/TextFile.hxx"
|
||||
#include "system/Error.hxx"
|
||||
#include "input/TextInputStream.hxx"
|
||||
#include "util/StringUtil.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <stdexcept>
|
||||
@@ -35,35 +35,29 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
inline void
|
||||
ExcludeList::ParseLine(char *line) noexcept
|
||||
{
|
||||
char *p = Strip(line);
|
||||
if (*p != 0 && *p != '#')
|
||||
patterns.emplace_front(p);
|
||||
}
|
||||
|
||||
bool
|
||||
ExcludeList::LoadFile(Path path_fs) noexcept
|
||||
try {
|
||||
ExcludeList::Load(InputStreamPtr is)
|
||||
{
|
||||
#ifdef HAVE_CLASS_GLOB
|
||||
TextFile file(path_fs);
|
||||
TextInputStream tis(std::move(is));
|
||||
|
||||
char *line;
|
||||
while ((line = file.ReadLine()) != nullptr) {
|
||||
char *p = strchr(line, '#');
|
||||
if (p != nullptr)
|
||||
*p = 0;
|
||||
|
||||
p = Strip(line);
|
||||
if (*p != 0)
|
||||
patterns.emplace_front(p);
|
||||
}
|
||||
while ((line = tis.ReadLine()) != nullptr)
|
||||
ParseLine(line);
|
||||
#else
|
||||
/* not implemented */
|
||||
(void)path_fs;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
} catch (const std::system_error &e) {
|
||||
if (!IsFileNotFound(e))
|
||||
LogError(e);
|
||||
return false;
|
||||
} catch (const std::exception &e) {
|
||||
LogError(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "check.h"
|
||||
#include "Compiler.h"
|
||||
#include "fs/Glob.hxx"
|
||||
#include "input/Ptr.hxx"
|
||||
|
||||
#ifdef HAVE_CLASS_GLOB
|
||||
#include <forward_list>
|
||||
@@ -62,13 +63,16 @@ public:
|
||||
/**
|
||||
* Loads and parses a .mpdignore file.
|
||||
*/
|
||||
bool LoadFile(Path path_fs) noexcept;
|
||||
bool Load(InputStreamPtr is);
|
||||
|
||||
/**
|
||||
* Checks whether one of the patterns in the .mpdignore file matches
|
||||
* the specified file name.
|
||||
*/
|
||||
bool Check(Path name_fs) const noexcept;
|
||||
|
||||
private:
|
||||
void ParseLine(char *line) noexcept;
|
||||
};
|
||||
|
||||
|
||||
|
@@ -49,6 +49,10 @@ struct UpdateQueueItem {
|
||||
bool IsDefined() const {
|
||||
return id != 0;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
id = 0;
|
||||
}
|
||||
};
|
||||
|
||||
class UpdateQueue {
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include "Idle.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "thread/Thread.hxx"
|
||||
#include "thread/Name.hxx"
|
||||
#include "thread/Util.hxx"
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -43,6 +44,7 @@ UpdateService::UpdateService(EventLoop &_loop, SimpleDatabase &_db,
|
||||
:DeferredMonitor(_loop),
|
||||
db(_db), storage(_storage),
|
||||
listener(_listener),
|
||||
update_thread(BIND_THIS_METHOD(Task)),
|
||||
update_task_id(0),
|
||||
walk(nullptr)
|
||||
{
|
||||
@@ -112,6 +114,8 @@ UpdateService::Task()
|
||||
{
|
||||
assert(walk != nullptr);
|
||||
|
||||
SetThreadName("update");
|
||||
|
||||
if (!next.path_utf8.empty())
|
||||
FormatDebug(update_domain, "starting: %s",
|
||||
next.path_utf8.c_str());
|
||||
@@ -140,13 +144,6 @@ UpdateService::Task()
|
||||
DeferredMonitor::Schedule();
|
||||
}
|
||||
|
||||
void
|
||||
UpdateService::Task(void *ctx)
|
||||
{
|
||||
UpdateService &service = *(UpdateService *)ctx;
|
||||
return service.Task();
|
||||
}
|
||||
|
||||
void
|
||||
UpdateService::StartThread(UpdateQueueItem &&i)
|
||||
{
|
||||
@@ -158,7 +155,7 @@ UpdateService::StartThread(UpdateQueueItem &&i)
|
||||
next = std::move(i);
|
||||
walk = new UpdateWalk(GetEventLoop(), listener, *next.storage);
|
||||
|
||||
update_thread.Start(Task, this);
|
||||
update_thread.Start();
|
||||
|
||||
FormatDebug(update_domain,
|
||||
"spawned thread for update job id %i", next.id);
|
||||
@@ -258,7 +255,7 @@ UpdateService::RunDeferred()
|
||||
delete walk;
|
||||
walk = nullptr;
|
||||
|
||||
next = UpdateQueueItem();
|
||||
next.Clear();
|
||||
|
||||
idle_add(IDLE_UPDATE);
|
||||
|
||||
|
@@ -98,7 +98,6 @@ private:
|
||||
|
||||
/* the update thread */
|
||||
void Task();
|
||||
static void Task(void *ctx);
|
||||
|
||||
void StartThread(UpdateQueueItem &&i);
|
||||
|
||||
|
@@ -36,6 +36,9 @@
|
||||
#include "fs/Traits.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "storage/FileInfo.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
#include "input/Error.hxx"
|
||||
#include "thread/Cond.hxx"
|
||||
#include "util/Alloc.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
@@ -345,11 +348,16 @@ UpdateWalk::UpdateDirectory(Directory &directory,
|
||||
|
||||
ExcludeList child_exclude_list(exclude_list);
|
||||
|
||||
{
|
||||
const auto exclude_path_fs =
|
||||
storage.MapChildFS(directory.GetPath(), ".mpdignore");
|
||||
if (!exclude_path_fs.IsNull())
|
||||
child_exclude_list.LoadFile(exclude_path_fs);
|
||||
try {
|
||||
Mutex mutex;
|
||||
Cond cond;
|
||||
auto is = InputStream::OpenReady(PathTraitsUTF8::Build(storage.MapUTF8(directory.GetPath()).c_str(),
|
||||
".mpdignore").c_str(),
|
||||
mutex, cond);
|
||||
child_exclude_list.Load(std::move(is));
|
||||
} catch (...) {
|
||||
if (!IsFileNotFound(std::current_exception()))
|
||||
LogError(std::current_exception());
|
||||
}
|
||||
|
||||
if (!child_exclude_list.IsEmpty())
|
||||
|
@@ -300,6 +300,7 @@ DecoderBridge::CommandFinished()
|
||||
|
||||
initial_seek_running = false;
|
||||
timestamp = dc.start_time.ToDoubleS();
|
||||
absolute_frame = dc.start_time.ToScale<uint64_t>(dc.in_audio_format.sample_rate);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -319,6 +320,7 @@ DecoderBridge::CommandFinished()
|
||||
convert->Reset();
|
||||
|
||||
timestamp = dc.seek_time.ToDoubleS();
|
||||
absolute_frame = dc.seek_time.ToScale<uint64_t>(dc.in_audio_format.sample_rate);
|
||||
}
|
||||
|
||||
dc.command = DecoderCommand::NONE;
|
||||
@@ -427,6 +429,7 @@ DecoderBridge::SubmitTimestamp(double t)
|
||||
assert(t >= 0);
|
||||
|
||||
timestamp = t;
|
||||
absolute_frame = uint64_t(t * dc.in_audio_format.sample_rate);
|
||||
}
|
||||
|
||||
DecoderCommand
|
||||
@@ -464,6 +467,29 @@ DecoderBridge::SubmitData(InputStream *is,
|
||||
return cmd;
|
||||
}
|
||||
|
||||
cmd = DecoderCommand::NONE;
|
||||
|
||||
const size_t frame_size = dc.in_audio_format.GetFrameSize();
|
||||
size_t data_frames = length / frame_size;
|
||||
|
||||
if (dc.end_time.IsPositive()) {
|
||||
/* enforce the given end time */
|
||||
|
||||
const uint64_t end_frame =
|
||||
dc.end_time.ToScale<uint64_t>(dc.in_audio_format.sample_rate);
|
||||
if (absolute_frame >= end_frame)
|
||||
return DecoderCommand::STOP;
|
||||
|
||||
const uint64_t remaining_frames = end_frame - absolute_frame;
|
||||
if (data_frames >= remaining_frames) {
|
||||
/* past the end of the range: truncate this
|
||||
data submission and stop the decoder */
|
||||
data_frames = remaining_frames;
|
||||
length = data_frames * frame_size;
|
||||
cmd = DecoderCommand::STOP;
|
||||
}
|
||||
}
|
||||
|
||||
if (convert != nullptr) {
|
||||
assert(dc.in_audio_format != dc.out_audio_format);
|
||||
|
||||
@@ -521,15 +547,11 @@ DecoderBridge::SubmitData(InputStream *is,
|
||||
|
||||
timestamp += (double)nbytes /
|
||||
dc.out_audio_format.GetTimeToSize();
|
||||
|
||||
if (dc.end_time.IsPositive() &&
|
||||
timestamp >= dc.end_time.ToDoubleS())
|
||||
/* the end of this range has been reached:
|
||||
stop decoding */
|
||||
return DecoderCommand::STOP;
|
||||
}
|
||||
|
||||
return DecoderCommand::NONE;
|
||||
absolute_frame += data_frames;
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
DecoderCommand
|
||||
|
@@ -49,6 +49,11 @@ public:
|
||||
*/
|
||||
double timestamp = 0;
|
||||
|
||||
/**
|
||||
* The time stamp of the next data chunk, in PCM frames.
|
||||
*/
|
||||
uint64_t absolute_frame = 0;
|
||||
|
||||
/**
|
||||
* Is the initial seek (to the start position of the sub-song)
|
||||
* pending, or has it been performed already?
|
||||
|
@@ -30,7 +30,8 @@
|
||||
DecoderControl::DecoderControl(Mutex &_mutex, Cond &_client_cond,
|
||||
const AudioFormat _configured_audio_format,
|
||||
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),
|
||||
replay_gain_config(_replay_gain_config) {}
|
||||
|
||||
|
@@ -308,9 +308,14 @@ struct DecoderControl {
|
||||
bool IsCurrentSong(const DetachedSong &_song) const noexcept;
|
||||
|
||||
gcc_pure
|
||||
bool LockIsCurrentSong(const DetachedSong &_song) const noexcept {
|
||||
bool IsSeekableCurrentSong(const DetachedSong &_song) const noexcept {
|
||||
return seekable && IsCurrentSong(_song);
|
||||
}
|
||||
|
||||
gcc_pure
|
||||
bool LockIsSeeakbleCurrentSong(const DetachedSong &_song) const noexcept {
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
return IsCurrentSong(_song);
|
||||
return IsSeekableCurrentSong(_song);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -415,6 +420,9 @@ public:
|
||||
* mixramp_start/mixramp_end.
|
||||
*/
|
||||
void CycleMixRamp();
|
||||
|
||||
private:
|
||||
void RunThread();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -513,30 +513,28 @@ try {
|
||||
dc.client_cond.signal();
|
||||
}
|
||||
|
||||
static void
|
||||
decoder_task(void *arg)
|
||||
void
|
||||
DecoderControl::RunThread()
|
||||
{
|
||||
DecoderControl &dc = *(DecoderControl *)arg;
|
||||
|
||||
SetThreadName("decoder");
|
||||
|
||||
const std::lock_guard<Mutex> protect(dc.mutex);
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
|
||||
do {
|
||||
assert(dc.state == DecoderState::STOP ||
|
||||
dc.state == DecoderState::ERROR);
|
||||
assert(state == DecoderState::STOP ||
|
||||
state == DecoderState::ERROR);
|
||||
|
||||
switch (dc.command) {
|
||||
switch (command) {
|
||||
case DecoderCommand::START:
|
||||
dc.CycleMixRamp();
|
||||
dc.replay_gain_prev_db = dc.replay_gain_db;
|
||||
dc.replay_gain_db = 0;
|
||||
CycleMixRamp();
|
||||
replay_gain_prev_db = replay_gain_db;
|
||||
replay_gain_db = 0;
|
||||
|
||||
decoder_run(dc);
|
||||
decoder_run(*this);
|
||||
|
||||
if (dc.state == DecoderState::ERROR) {
|
||||
if (state == DecoderState::ERROR) {
|
||||
try {
|
||||
std::rethrow_exception(dc.error);
|
||||
std::rethrow_exception(error);
|
||||
} catch (const std::exception &e) {
|
||||
LogError(e);
|
||||
} catch (...) {
|
||||
@@ -552,20 +550,20 @@ decoder_task(void *arg)
|
||||
/* we need to clear the pipe here; usually the
|
||||
PlayerThread is responsible, but it is not
|
||||
aware that the decoder has finished */
|
||||
dc.pipe->Clear(*dc.buffer);
|
||||
pipe->Clear(*buffer);
|
||||
|
||||
decoder_run(dc);
|
||||
decoder_run(*this);
|
||||
break;
|
||||
|
||||
case DecoderCommand::STOP:
|
||||
dc.CommandFinishedLocked();
|
||||
CommandFinishedLocked();
|
||||
break;
|
||||
|
||||
case DecoderCommand::NONE:
|
||||
dc.Wait();
|
||||
Wait();
|
||||
break;
|
||||
}
|
||||
} while (dc.command != DecoderCommand::NONE || !dc.quit);
|
||||
} while (command != DecoderCommand::NONE || !quit);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -574,5 +572,5 @@ decoder_thread_start(DecoderControl &dc)
|
||||
assert(!dc.thread.IsDefined());
|
||||
|
||||
dc.quit = false;
|
||||
dc.thread.Start(decoder_task, &dc);
|
||||
dc.thread.Start();
|
||||
}
|
||||
|
@@ -128,7 +128,7 @@ dsdlib_tag_id3(InputStream &is,
|
||||
return;
|
||||
|
||||
const auto count64 = size - tagoffset;
|
||||
if (count64 < 10 || count64 > 1024 * 1024)
|
||||
if (count64 < 10 || count64 > 4 * 1024 * 1024)
|
||||
return;
|
||||
|
||||
if (!dsdlib_skip_to(nullptr, is, tagoffset))
|
||||
|
@@ -484,6 +484,8 @@ static const char *const dsdiff_suffixes[] = {
|
||||
|
||||
static const char *const dsdiff_mime_types[] = {
|
||||
"application/x-dff",
|
||||
"audio/x-dff",
|
||||
"audio/x-dsd",
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
@@ -358,6 +358,8 @@ static const char *const dsf_suffixes[] = {
|
||||
|
||||
static const char *const dsf_mime_types[] = {
|
||||
"application/x-dsf",
|
||||
"audio/x-dsf",
|
||||
"audio/x-dsd",
|
||||
nullptr
|
||||
};
|
||||
|
||||
|
@@ -24,7 +24,6 @@
|
||||
#include "config.h"
|
||||
#include "FlacCommon.hxx"
|
||||
#include "FlacMetadata.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <stdexcept>
|
||||
@@ -143,25 +142,10 @@ FlacDecoder::OnWrite(const FLAC__Frame &frame,
|
||||
if (!initialized && !OnFirstFrame(frame.header))
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||
|
||||
const auto data = pcm_import.Import(buf, frame.header.blocksize);
|
||||
chunk = pcm_import.Import(buf, frame.header.blocksize);
|
||||
|
||||
unsigned bit_rate = nbytes * 8 * frame.header.sample_rate /
|
||||
kbit_rate = nbytes * 8 * frame.header.sample_rate /
|
||||
(1000 * frame.header.blocksize);
|
||||
|
||||
auto cmd = GetClient()->SubmitData(GetInputStream(),
|
||||
data.data, data.size,
|
||||
bit_rate);
|
||||
switch (cmd) {
|
||||
case DecoderCommand::NONE:
|
||||
case DecoderCommand::START:
|
||||
break;
|
||||
|
||||
case DecoderCommand::STOP:
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
||||
|
||||
case DecoderCommand::SEEK:
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include "FlacInput.hxx"
|
||||
#include "FlacPcm.hxx"
|
||||
#include "../DecoderAPI.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
|
||||
#include <FLAC/stream_decoder.h>
|
||||
|
||||
@@ -41,6 +42,12 @@ struct FlacDecoder : public FlacInput {
|
||||
*/
|
||||
bool unsupported = false;
|
||||
|
||||
/**
|
||||
* The kbit_rate parameter for the next
|
||||
* DecoderBridge::SubmitData() call.
|
||||
*/
|
||||
uint16_t kbit_rate;
|
||||
|
||||
FlacPcmImport pcm_import;
|
||||
|
||||
/**
|
||||
@@ -51,6 +58,13 @@ struct FlacDecoder : public FlacInput {
|
||||
|
||||
Tag tag;
|
||||
|
||||
/**
|
||||
* Decoded PCM data obtained by our libFLAC write callback.
|
||||
* If this is non-empty, then DecoderBridge::SubmitData()
|
||||
* should be called.
|
||||
*/
|
||||
ConstBuffer<void> chunk = nullptr;
|
||||
|
||||
FlacDecoder(DecoderClient &_client, InputStream &_input_stream)
|
||||
:FlacInput(_input_stream, &_client) {}
|
||||
|
||||
|
@@ -139,19 +139,40 @@ flac_decoder_initialize(FlacDecoder *data, FLAC__StreamDecoder *sd)
|
||||
return data->initialized;
|
||||
}
|
||||
|
||||
static DecoderCommand
|
||||
FlacSubmitToClient(DecoderClient &client, FlacDecoder &d) noexcept
|
||||
{
|
||||
if (d.tag.IsEmpty() && d.chunk.IsEmpty())
|
||||
return client.GetCommand();
|
||||
|
||||
if (!d.tag.IsEmpty()) {
|
||||
auto cmd = client.SubmitTag(d.GetInputStream(),
|
||||
std::move(d.tag));
|
||||
d.tag.Clear();
|
||||
if (cmd != DecoderCommand::NONE)
|
||||
return cmd;
|
||||
}
|
||||
|
||||
if (!d.chunk.IsEmpty()) {
|
||||
auto cmd = client.SubmitData(d.GetInputStream(),
|
||||
d.chunk.data,
|
||||
d.chunk.size,
|
||||
d.kbit_rate);
|
||||
d.chunk = nullptr;
|
||||
if (cmd != DecoderCommand::NONE)
|
||||
return cmd;
|
||||
}
|
||||
|
||||
return DecoderCommand::NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
|
||||
{
|
||||
DecoderClient &client = *data->GetClient();
|
||||
|
||||
while (true) {
|
||||
DecoderCommand cmd;
|
||||
if (!data->tag.IsEmpty()) {
|
||||
cmd = client.SubmitTag(data->GetInputStream(),
|
||||
std::move(data->tag));
|
||||
data->tag.Clear();
|
||||
} else
|
||||
cmd = client.GetCommand();
|
||||
DecoderCommand cmd = FlacSubmitToClient(client, *data);
|
||||
|
||||
if (cmd == DecoderCommand::SEEK) {
|
||||
FLAC__uint64 seek_sample = client.GetSeekFrame();
|
||||
@@ -160,6 +181,11 @@ flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
|
||||
client.CommandFinished();
|
||||
} else
|
||||
client.SeekError();
|
||||
|
||||
/* FLAC__stream_decoder_seek_absolute()
|
||||
decodes one frame and may have provided
|
||||
data to be submitted to the client */
|
||||
continue;
|
||||
} else if (cmd == DecoderCommand::STOP)
|
||||
break;
|
||||
|
||||
|
@@ -28,7 +28,7 @@
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "util/FormatString.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
@@ -38,7 +38,6 @@
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define SUBTUNE_PREFIX "tune_"
|
||||
|
||||
@@ -191,20 +190,17 @@ ScanGmeInfo(const gme_info_t &info, unsigned song_num, int track_count,
|
||||
tag_handler_invoke_duration(handler, handler_ctx,
|
||||
SongTime::FromMS(info.play_length));
|
||||
|
||||
if (track_count > 1) {
|
||||
char track[16];
|
||||
sprintf(track, "%u", song_num + 1);
|
||||
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track);
|
||||
}
|
||||
if (track_count > 1)
|
||||
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK,
|
||||
StringFormat<16>("%u", song_num + 1));
|
||||
|
||||
if (info.song != nullptr) {
|
||||
if (track_count > 1) {
|
||||
/* start numbering subtunes from 1 */
|
||||
char tag_title[1024];
|
||||
snprintf(tag_title, sizeof(tag_title),
|
||||
"%s (%u/%d)",
|
||||
info.song, song_num + 1,
|
||||
track_count);
|
||||
const auto tag_title =
|
||||
StringFormat<1024>("%s (%u/%d)",
|
||||
info.song, song_num + 1,
|
||||
track_count);
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
TAG_TITLE, tag_title);
|
||||
} else
|
||||
@@ -297,9 +293,9 @@ gme_container_scan(Path path_fs)
|
||||
ScanMusicEmu(emu, i,
|
||||
add_tag_handler, &tag_builder);
|
||||
|
||||
char track_name[64];
|
||||
snprintf(track_name, sizeof(track_name),
|
||||
SUBTUNE_PREFIX "%03u.%s", i+1, subtune_suffix);
|
||||
const auto track_name =
|
||||
StringFormat<64>(SUBTUNE_PREFIX "%03u.%s", i+1,
|
||||
subtune_suffix);
|
||||
tail = list.emplace_after(tail, track_name,
|
||||
tag_builder.Commit());
|
||||
}
|
||||
|
@@ -53,6 +53,14 @@ ScanOneOpusTag(const char *name, const char *value,
|
||||
long l = strtol(value, &endptr, 10);
|
||||
if (endptr > value && *endptr == 0)
|
||||
rgi->track.gain = double(l) / 256.;
|
||||
} else if (rgi != nullptr && strcmp(name, "R128_ALBUM_GAIN") == 0) {
|
||||
/* R128_ALBUM_GAIN is a Q7.8 fixed point number in
|
||||
dB */
|
||||
|
||||
char *endptr;
|
||||
long l = strtol(value, &endptr, 10);
|
||||
if (endptr > value && *endptr == 0)
|
||||
rgi->album.gain = double(l) / 256.;
|
||||
}
|
||||
|
||||
tag_handler_invoke_pair(handler, ctx, name, value);
|
||||
|
@@ -26,7 +26,7 @@
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "util/Macros.hxx"
|
||||
#include "util/FormatString.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "system/ByteOrder.hxx"
|
||||
#include "Log.hxx"
|
||||
@@ -413,10 +413,9 @@ ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks,
|
||||
title = "";
|
||||
|
||||
if (n_tracks > 1) {
|
||||
char tag_title[1024];
|
||||
snprintf(tag_title, sizeof(tag_title),
|
||||
"%s (%u/%u)",
|
||||
title, track, n_tracks);
|
||||
const auto tag_title =
|
||||
StringFormat<1024>("%s (%u/%u)",
|
||||
title, track, n_tracks);
|
||||
tag_handler_invoke_tag(handler, handler_ctx,
|
||||
TAG_TITLE, tag_title);
|
||||
} else
|
||||
@@ -435,9 +434,8 @@ ScanSidTuneInfo(const SidTuneInfo &info, unsigned track, unsigned n_tracks,
|
||||
date);
|
||||
|
||||
/* track */
|
||||
char track_buffer[16];
|
||||
sprintf(track_buffer, "%d", track);
|
||||
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track_buffer);
|
||||
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK,
|
||||
StringFormat<16>("%u", track));
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@@ -52,7 +52,7 @@ class OpusEncoder final : public OggEncoder {
|
||||
|
||||
ogg_int64_t packetno = 0;
|
||||
|
||||
ogg_int64_t granulepos;
|
||||
ogg_int64_t granulepos = 0;
|
||||
|
||||
public:
|
||||
OpusEncoder(AudioFormat &_audio_format, ::OpusEncoder *_enc);
|
||||
|
@@ -27,7 +27,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
EventLoop::EventLoop()
|
||||
:SocketMonitor(*this)
|
||||
:SocketMonitor(*this), quit(false)
|
||||
{
|
||||
SocketMonitor::Open(wake_fd.Get());
|
||||
SocketMonitor::Schedule(SocketMonitor::READ);
|
||||
@@ -46,7 +46,9 @@ EventLoop::~EventLoop()
|
||||
void
|
||||
EventLoop::Break()
|
||||
{
|
||||
quit = true;
|
||||
if (quit.exchange(true))
|
||||
return;
|
||||
|
||||
wake_fd.Write();
|
||||
}
|
||||
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include "SocketMonitor.hxx"
|
||||
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <set>
|
||||
|
||||
@@ -82,7 +83,7 @@ class EventLoop final : SocketMonitor
|
||||
|
||||
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
|
||||
|
@@ -73,6 +73,10 @@ public:
|
||||
return filter;
|
||||
}
|
||||
|
||||
void Reset() override {
|
||||
filter->Reset();
|
||||
}
|
||||
|
||||
ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override {
|
||||
return filter->FilterPCM(src);
|
||||
}
|
||||
|
@@ -53,10 +53,16 @@ public:
|
||||
void Set(const AudioFormat &_out_audio_format);
|
||||
|
||||
void Reset() override {
|
||||
state.Reset();
|
||||
if (IsActive())
|
||||
state.Reset();
|
||||
}
|
||||
|
||||
ConstBuffer<void> FilterPCM(ConstBuffer<void> src) override;
|
||||
|
||||
private:
|
||||
bool IsActive() const noexcept {
|
||||
return out_audio_format != in_audio_format;
|
||||
}
|
||||
};
|
||||
|
||||
class PreparedConvertFilter final : public PreparedFilter {
|
||||
@@ -80,7 +86,7 @@ ConvertFilter::Set(const AudioFormat &_out_audio_format)
|
||||
/* no change */
|
||||
return;
|
||||
|
||||
if (out_audio_format != in_audio_format) {
|
||||
if (IsActive()) {
|
||||
out_audio_format = in_audio_format;
|
||||
state.Close();
|
||||
}
|
||||
@@ -111,7 +117,7 @@ ConvertFilter::~ConvertFilter()
|
||||
{
|
||||
assert(in_audio_format.IsValid());
|
||||
|
||||
if (out_audio_format != in_audio_format)
|
||||
if (IsActive())
|
||||
state.Close();
|
||||
}
|
||||
|
||||
@@ -120,11 +126,10 @@ ConvertFilter::FilterPCM(ConstBuffer<void> src)
|
||||
{
|
||||
assert(in_audio_format.IsValid());
|
||||
|
||||
if (out_audio_format == in_audio_format)
|
||||
return IsActive()
|
||||
? state.Convert(src)
|
||||
/* optimized special case: no-op */
|
||||
return src;
|
||||
|
||||
return state.Convert(src);
|
||||
: src;
|
||||
}
|
||||
|
||||
const FilterPlugin convert_filter_plugin = {
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#include "config.h"
|
||||
#include "FileOutputStream.hxx"
|
||||
#include "system/Error.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
|
||||
FileOutputStream::FileOutputStream(Path _path, Mode _mode)
|
||||
:path(_path), mode(_mode)
|
||||
@@ -212,10 +213,9 @@ FileOutputStream::Commit()
|
||||
unlink(GetPath().c_str());
|
||||
|
||||
/* hard-link the temporary file to the final path */
|
||||
char fd_path[64];
|
||||
snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d",
|
||||
fd.Get());
|
||||
if (linkat(AT_FDCWD, fd_path, AT_FDCWD, path.c_str(),
|
||||
if (linkat(AT_FDCWD,
|
||||
StringFormat<64>("/proc/self/fd/%d", fd.Get()),
|
||||
AT_FDCWD, path.c_str(),
|
||||
AT_SYMLINK_FOLLOW) < 0)
|
||||
throw FormatErrno("Failed to commit %s",
|
||||
path.c_str());
|
||||
|
@@ -19,7 +19,6 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "AsyncInputStream.hxx"
|
||||
#include "Domain.hxx"
|
||||
#include "tag/Tag.hxx"
|
||||
#include "thread/Cond.hxx"
|
||||
#include "IOThread.hxx"
|
||||
|
53
src/input/Error.cxx
Normal file
53
src/input/Error.cxx
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "Error.hxx"
|
||||
#include "system/Error.hxx"
|
||||
|
||||
#ifdef ENABLE_CURL
|
||||
#include "lib/curl/Error.hxx"
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NFS
|
||||
#include "lib/nfs/Error.hxx"
|
||||
#include <nfsc/libnfs-raw-nfs.h>
|
||||
#endif
|
||||
|
||||
bool
|
||||
IsFileNotFound(std::exception_ptr ep)
|
||||
{
|
||||
try {
|
||||
std::rethrow_exception(ep);
|
||||
} catch (const std::system_error &e) {
|
||||
return IsFileNotFound(e);
|
||||
#ifdef ENABLE_CURL
|
||||
} catch (const HttpStatusError &e) {
|
||||
return e.GetStatus() == 404;
|
||||
#endif
|
||||
#ifdef ENABLE_NFS
|
||||
} catch (const NfsClientError &e) {
|
||||
return e.GetCode() == NFS3ERR_NOENT;
|
||||
#endif
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2017 The Music Player Daemon Project
|
||||
* Copyright 2003-2018 The Music Player Daemon Project
|
||||
* http://www.musicpd.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
@@ -17,8 +17,21 @@
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "Domain.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#ifndef INPUT_ERROR_HXX
|
||||
#define INPUT_ERROR_HXX
|
||||
|
||||
const Domain input_domain("input");
|
||||
#include "check.h"
|
||||
#include "Compiler.h"
|
||||
|
||||
#include <exception>
|
||||
|
||||
/**
|
||||
* Was this exception thrown because the requested file does not
|
||||
* exist? This function attempts to recognize exceptions thrown by
|
||||
* various input plugins.
|
||||
*/
|
||||
gcc_pure
|
||||
bool
|
||||
IsFileNotFound(std::exception_ptr e);
|
||||
|
||||
#endif
|
@@ -20,7 +20,7 @@
|
||||
#include "config.h"
|
||||
#include "InputStream.hxx"
|
||||
#include "thread/Cond.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
@@ -77,8 +77,8 @@ gcc_pure
|
||||
static bool
|
||||
ExpensiveSeeking(const char *uri) noexcept
|
||||
{
|
||||
return StringStartsWith(uri, "http://") ||
|
||||
StringStartsWith(uri, "https://");
|
||||
return StringStartsWithCaseASCII(uri, "http://") ||
|
||||
StringStartsWithCaseASCII(uri, "https://");
|
||||
}
|
||||
|
||||
bool
|
||||
|
@@ -29,4 +29,10 @@
|
||||
*/
|
||||
typedef uint64_t offset_type;
|
||||
|
||||
/**
|
||||
* To format an offset_type with printf(). To use this, include
|
||||
* <cinttypes>.
|
||||
*/
|
||||
#define PRIoffset PRIu64
|
||||
|
||||
#endif
|
||||
|
@@ -22,7 +22,6 @@
|
||||
#include "Registry.hxx"
|
||||
#include "InputPlugin.hxx"
|
||||
#include "LocalOpen.hxx"
|
||||
#include "Domain.hxx"
|
||||
#include "plugins/RewindInputPlugin.hxx"
|
||||
#include "fs/Traits.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
|
@@ -26,8 +26,12 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
ThreadInputStream::~ThreadInputStream()
|
||||
void
|
||||
ThreadInputStream::Stop() noexcept
|
||||
{
|
||||
if (!thread.IsDefined())
|
||||
return;
|
||||
|
||||
{
|
||||
const std::lock_guard<Mutex> lock(mutex);
|
||||
close = true;
|
||||
@@ -42,6 +46,7 @@ ThreadInputStream::~ThreadInputStream()
|
||||
buffer->Clear();
|
||||
HugeFree(buffer->Write().data, buffer_size);
|
||||
delete buffer;
|
||||
buffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,10 +59,10 @@ ThreadInputStream::Start()
|
||||
assert(p != nullptr);
|
||||
|
||||
buffer = new CircularBuffer<uint8_t>((uint8_t *)p, buffer_size);
|
||||
thread.Start(ThreadFunc, this);
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
inline void
|
||||
void
|
||||
ThreadInputStream::ThreadFunc()
|
||||
{
|
||||
FormatThreadName("input:%s", plugin);
|
||||
@@ -68,7 +73,7 @@ ThreadInputStream::ThreadFunc()
|
||||
Open();
|
||||
} catch (...) {
|
||||
postponed_exception = std::current_exception();
|
||||
cond.broadcast();
|
||||
SetReady();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -107,13 +112,6 @@ ThreadInputStream::ThreadFunc()
|
||||
Close();
|
||||
}
|
||||
|
||||
void
|
||||
ThreadInputStream::ThreadFunc(void *ctx)
|
||||
{
|
||||
ThreadInputStream &tis = *(ThreadInputStream *)ctx;
|
||||
tis.ThreadFunc();
|
||||
}
|
||||
|
||||
void
|
||||
ThreadInputStream::Check()
|
||||
{
|
||||
|
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <exception>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
template<typename T> class CircularBuffer;
|
||||
@@ -39,6 +40,11 @@ template<typename T> class CircularBuffer;
|
||||
* manages the thread and the buffer.
|
||||
*
|
||||
* This works only for "streams": unknown length, no seeking, no tags.
|
||||
*
|
||||
* The implementation must call Stop() before its destruction
|
||||
* completes. This cannot be done in ~ThreadInputStream() because at
|
||||
* this point, the class has been morphed back to #ThreadInputStream
|
||||
* and the still-running thread will crash due to pure method call.
|
||||
*/
|
||||
class ThreadInputStream : public InputStream {
|
||||
const char *const plugin;
|
||||
@@ -73,9 +79,16 @@ public:
|
||||
size_t _buffer_size)
|
||||
:InputStream(_uri, _mutex, _cond),
|
||||
plugin(_plugin),
|
||||
thread(BIND_THIS_METHOD(ThreadFunc)),
|
||||
buffer_size(_buffer_size) {}
|
||||
|
||||
virtual ~ThreadInputStream();
|
||||
#ifndef NDEBUG
|
||||
~ThreadInputStream() override {
|
||||
/* Stop() must have been called already */
|
||||
assert(!thread.IsDefined());
|
||||
assert(buffer == nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initialize the object and start the thread.
|
||||
@@ -89,6 +102,12 @@ public:
|
||||
size_t Read(void *ptr, size_t size) override final;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Stop the thread and free the buffer. This must be called
|
||||
* before destruction of this object completes.
|
||||
*/
|
||||
void Stop() noexcept;
|
||||
|
||||
void SetMimeType(const char *_mime) {
|
||||
assert(thread.IsInside());
|
||||
|
||||
@@ -138,7 +157,6 @@ protected:
|
||||
|
||||
private:
|
||||
void ThreadFunc();
|
||||
static void ThreadFunc(void *ctx);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -33,7 +33,7 @@
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/ReusableArray.hxx"
|
||||
|
||||
#include "util/ASCII.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "event/MultiSocketMonitor.hxx"
|
||||
#include "event/DeferredMonitor.hxx"
|
||||
@@ -147,7 +147,7 @@ private:
|
||||
inline InputStream *
|
||||
AlsaInputStream::Create(const char *uri, Mutex &mutex, Cond &cond)
|
||||
{
|
||||
const char *device = StringAfterPrefix(uri, "alsa://");
|
||||
const char *device = StringAfterPrefixCaseASCII(uri, "alsa://");
|
||||
if (device == nullptr)
|
||||
return nullptr;
|
||||
|
||||
@@ -270,6 +270,12 @@ AlsaInputStream::Recover(int err)
|
||||
/* this is no error, so just keep running */
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* this default case is just here to work around
|
||||
-Wswitch due to SND_PCM_STATE_PRIVATE1 (libasound
|
||||
1.1.6) */
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -26,7 +26,7 @@
|
||||
#include "../InputStream.hxx"
|
||||
#include "../InputPlugin.hxx"
|
||||
#include "util/StringUtil.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "system/ByteOrder.hxx"
|
||||
@@ -128,7 +128,7 @@ struct cdio_uri {
|
||||
static bool
|
||||
parse_cdio_uri(struct cdio_uri *dest, const char *src)
|
||||
{
|
||||
if (!StringStartsWith(src, "cdda://"))
|
||||
if (!StringStartsWithCaseASCII(src, "cdda://"))
|
||||
return false;
|
||||
|
||||
src += 7;
|
||||
|
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "CurlInputPlugin.hxx"
|
||||
#include "lib/curl/Error.hxx"
|
||||
#include "lib/curl/Easy.hxx"
|
||||
#include "lib/curl/Global.hxx"
|
||||
#include "lib/curl/Request.hxx"
|
||||
@@ -34,12 +35,15 @@
|
||||
#include "IOThread.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/StringUtil.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
#include "util/NumberParser.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "PluginUnavailable.hxx"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -185,7 +189,9 @@ CurlInputStream::OnHeaders(unsigned status,
|
||||
assert(!postponed_exception);
|
||||
|
||||
if (status < 200 || status >= 300)
|
||||
throw FormatRuntimeError("got HTTP status %ld", status);
|
||||
throw HttpStatusError(status,
|
||||
StringFormat<40>("got HTTP status %u",
|
||||
status).c_str());
|
||||
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
|
||||
@@ -266,6 +272,7 @@ CurlInputStream::OnData(ConstBuffer<void> data)
|
||||
void
|
||||
CurlInputStream::OnEnd()
|
||||
{
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
cond.broadcast();
|
||||
|
||||
AsyncInputStream::SetClosed();
|
||||
@@ -274,6 +281,7 @@ CurlInputStream::OnEnd()
|
||||
void
|
||||
CurlInputStream::OnError(std::exception_ptr e)
|
||||
{
|
||||
const std::lock_guard<Mutex> protect(mutex);
|
||||
postponed_exception = std::move(e);
|
||||
|
||||
if (IsSeekPending())
|
||||
@@ -369,13 +377,10 @@ CurlInputStream::InitEasy()
|
||||
if (proxy_port > 0)
|
||||
request->SetOption(CURLOPT_PROXYPORT, (long)proxy_port);
|
||||
|
||||
if (proxy_user != nullptr && proxy_password != nullptr) {
|
||||
char proxy_auth_str[1024];
|
||||
snprintf(proxy_auth_str, sizeof(proxy_auth_str),
|
||||
"%s:%s",
|
||||
proxy_user, proxy_password);
|
||||
request->SetOption(CURLOPT_PROXYUSERPWD, proxy_auth_str);
|
||||
}
|
||||
if (proxy_user != nullptr && proxy_password != nullptr)
|
||||
request->SetOption(CURLOPT_PROXYUSERPWD,
|
||||
StringFormat<1024>("%s:%s", proxy_user,
|
||||
proxy_password).c_str());
|
||||
|
||||
request->SetOption(CURLOPT_SSL_VERIFYPEER, verify_peer ? 1l : 0l);
|
||||
request->SetOption(CURLOPT_SSL_VERIFYHOST, verify_host ? 2l : 0l);
|
||||
@@ -412,16 +417,10 @@ CurlInputStream::SeekInternal(offset_type new_offset)
|
||||
|
||||
/* send the "Range" header */
|
||||
|
||||
if (offset > 0) {
|
||||
char range[32];
|
||||
#ifdef _WIN32
|
||||
// TODO: what can we use on Windows to format 64 bit?
|
||||
sprintf(range, "%lu-", (long)offset);
|
||||
#else
|
||||
sprintf(range, "%llu-", (unsigned long long)offset);
|
||||
#endif
|
||||
request->SetOption(CURLOPT_RANGE, range);
|
||||
}
|
||||
if (offset > 0)
|
||||
request->SetOption(CURLOPT_RANGE,
|
||||
StringFormat<40>("%" PRIoffset "-",
|
||||
offset).c_str());
|
||||
|
||||
StartRequest();
|
||||
}
|
||||
@@ -459,8 +458,8 @@ CurlInputStream::Open(const char *url, Mutex &mutex, Cond &cond)
|
||||
static InputStream *
|
||||
input_curl_open(const char *url, Mutex &mutex, Cond &cond)
|
||||
{
|
||||
if (strncmp(url, "http://", 7) != 0 &&
|
||||
strncmp(url, "https://", 8) != 0)
|
||||
if (!StringStartsWithCaseASCII(url, "http://") &&
|
||||
!StringStartsWithCaseASCII(url, "https://"))
|
||||
return nullptr;
|
||||
|
||||
return CurlInputStream::Open(url, mutex, cond);
|
||||
|
@@ -28,7 +28,7 @@
|
||||
#include "../InputStream.hxx"
|
||||
#include "../InputPlugin.hxx"
|
||||
#include "PluginUnavailable.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
|
||||
extern "C" {
|
||||
#include <libavformat/avio.h>
|
||||
@@ -85,12 +85,12 @@ static InputStream *
|
||||
input_ffmpeg_open(const char *uri,
|
||||
Mutex &mutex, Cond &cond)
|
||||
{
|
||||
if (!StringStartsWith(uri, "gopher://") &&
|
||||
!StringStartsWith(uri, "rtp://") &&
|
||||
!StringStartsWith(uri, "rtsp://") &&
|
||||
!StringStartsWith(uri, "rtmp://") &&
|
||||
!StringStartsWith(uri, "rtmpt://") &&
|
||||
!StringStartsWith(uri, "rtmps://"))
|
||||
if (!StringStartsWithCaseASCII(uri, "gopher://") &&
|
||||
!StringStartsWithCaseASCII(uri, "rtp://") &&
|
||||
!StringStartsWithCaseASCII(uri, "rtsp://") &&
|
||||
!StringStartsWithCaseASCII(uri, "rtmp://") &&
|
||||
!StringStartsWithCaseASCII(uri, "rtmpt://") &&
|
||||
!StringStartsWithCaseASCII(uri, "rtmps://"))
|
||||
return nullptr;
|
||||
|
||||
AVIOContext *h;
|
||||
|
@@ -22,7 +22,7 @@
|
||||
#include "input/ThreadInputStream.hxx"
|
||||
#include "input/InputPlugin.hxx"
|
||||
#include "system/Error.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
|
||||
#include <libmms/mmsx.h>
|
||||
|
||||
@@ -39,6 +39,10 @@ public:
|
||||
MMS_BUFFER_SIZE) {
|
||||
}
|
||||
|
||||
~MmsInputStream() noexcept override {
|
||||
Stop();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void Open() override;
|
||||
virtual size_t ThreadRead(void *ptr, size_t size) override;
|
||||
@@ -70,10 +74,10 @@ static InputStream *
|
||||
input_mms_open(const char *url,
|
||||
Mutex &mutex, Cond &cond)
|
||||
{
|
||||
if (!StringStartsWith(url, "mms://") &&
|
||||
!StringStartsWith(url, "mmsh://") &&
|
||||
!StringStartsWith(url, "mmst://") &&
|
||||
!StringStartsWith(url, "mmsu://"))
|
||||
if (!StringStartsWithCaseASCII(url, "mms://") &&
|
||||
!StringStartsWithCaseASCII(url, "mmsh://") &&
|
||||
!StringStartsWithCaseASCII(url, "mmst://") &&
|
||||
!StringStartsWithCaseASCII(url, "mmsu://"))
|
||||
return nullptr;
|
||||
|
||||
auto m = new MmsInputStream(url, mutex, cond);
|
||||
|
@@ -23,9 +23,7 @@
|
||||
#include "../InputPlugin.hxx"
|
||||
#include "lib/nfs/Glue.hxx"
|
||||
#include "lib/nfs/FileReader.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
|
||||
#include <string.h>
|
||||
#include "util/ASCII.hxx"
|
||||
|
||||
/**
|
||||
* Do not buffer more than this number of bytes. It should be a
|
||||
@@ -219,7 +217,7 @@ static InputStream *
|
||||
input_nfs_open(const char *uri,
|
||||
Mutex &mutex, Cond &cond)
|
||||
{
|
||||
if (!StringStartsWith(uri, "nfs://"))
|
||||
if (!StringStartsWithCaseASCII(uri, "nfs://"))
|
||||
return nullptr;
|
||||
|
||||
NfsInputStream *is = new NfsInputStream(uri, mutex, cond);
|
||||
|
@@ -25,7 +25,7 @@
|
||||
#include "../InputPlugin.hxx"
|
||||
#include "PluginUnavailable.hxx"
|
||||
#include "system/Error.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
|
||||
#include <libsmbclient.h>
|
||||
|
||||
@@ -87,7 +87,7 @@ static InputStream *
|
||||
input_smbclient_open(const char *uri,
|
||||
Mutex &mutex, Cond &cond)
|
||||
{
|
||||
if (!StringStartsWith(uri, "smb://"))
|
||||
if (!StringStartsWithCaseASCII(uri, "smb://"))
|
||||
return nullptr;
|
||||
|
||||
const std::lock_guard<Mutex> protect(smbclient_mutex);
|
||||
|
42
src/lib/curl/Error.hxx
Normal file
42
src/lib/curl/Error.hxx
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 CURL_ERROR_HXX
|
||||
#define CURL_ERROR_HXX
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
/**
|
||||
* Thrown when an unsuccessful status was received from the HTTP
|
||||
* server.
|
||||
*/
|
||||
class HttpStatusError : public std::runtime_error {
|
||||
unsigned status;
|
||||
|
||||
public:
|
||||
template<typename M>
|
||||
explicit HttpStatusError(unsigned _status, M &&_msg) noexcept
|
||||
:std::runtime_error(std::forward<M>(_msg)), status(_status) {}
|
||||
|
||||
unsigned GetStatus() const noexcept {
|
||||
return status;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@@ -62,6 +62,7 @@ CurlRequest::CurlRequest(CurlGlobal &_global, const char *url,
|
||||
easy.SetOption(CURLOPT_NOPROGRESS, 1l);
|
||||
easy.SetOption(CURLOPT_NOSIGNAL, 1l);
|
||||
easy.SetOption(CURLOPT_CONNECTTIMEOUT, 10l);
|
||||
easy.SetOption(CURLOPT_HTTPAUTH, (long) CURLAUTH_ANY);
|
||||
easy.SetOption(CURLOPT_URL, url);
|
||||
}
|
||||
|
||||
|
20
src/lib/curl/patches/no_netrc.patch
Normal file
20
src/lib/curl/patches/no_netrc.patch
Normal file
@@ -0,0 +1,20 @@
|
||||
Index: curl-7.58.0/lib/url.c
|
||||
===================================================================
|
||||
--- curl-7.58.0.orig/lib/url.c
|
||||
+++ curl-7.58.0/lib/url.c
|
||||
@@ -3503,6 +3503,7 @@ static CURLcode override_login(struct Cu
|
||||
}
|
||||
|
||||
conn->bits.netrc = FALSE;
|
||||
+#ifndef __BIONIC__
|
||||
if(data->set.use_netrc != CURL_NETRC_IGNORED) {
|
||||
int ret = Curl_parsenetrc(conn->host.name,
|
||||
userp, passwdp,
|
||||
@@ -3524,6 +3525,7 @@ static CURLcode override_login(struct Cu
|
||||
conn->bits.user_passwd = TRUE; /* enable user+password */
|
||||
}
|
||||
}
|
||||
+#endif
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
15
src/lib/curl/patches/only_lib.patch
Normal file
15
src/lib/curl/patches/only_lib.patch
Normal file
@@ -0,0 +1,15 @@
|
||||
Index: curl-7.58.0/Makefile.in
|
||||
===================================================================
|
||||
--- curl-7.58.0.orig/Makefile.in
|
||||
+++ curl-7.58.0/Makefile.in
|
||||
@@ -641,8 +641,8 @@ CLEANFILES = $(VC6_LIBDSP) $(VC6_SRCDSP)
|
||||
$(VC14_LIBVCXPROJ) $(VC14_SRCVCXPROJ) $(VC15_LIBVCXPROJ) $(VC15_SRCVCXPROJ)
|
||||
|
||||
bin_SCRIPTS = curl-config
|
||||
-SUBDIRS = lib src
|
||||
-DIST_SUBDIRS = $(SUBDIRS) tests packages scripts include docs
|
||||
+SUBDIRS = lib
|
||||
+DIST_SUBDIRS = $(SUBDIRS) include
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libcurl.pc
|
||||
LIB_VAUTH_CFILES = vauth/vauth.c vauth/cleartext.c vauth/cram.c \
|
2
src/lib/curl/patches/series
Normal file
2
src/lib/curl/patches/series
Normal file
@@ -0,0 +1,2 @@
|
||||
only_lib.patch
|
||||
no_netrc.patch
|
@@ -33,6 +33,9 @@ FfmpegInit()
|
||||
{
|
||||
av_log_set_callback(FfmpegLogCallback);
|
||||
|
||||
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 9, 100)
|
||||
/* deprecated as of FFmpeg 4.0 */
|
||||
av_register_all();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include "Domain.hxx"
|
||||
#include "LogV.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/log.h>
|
||||
@@ -57,9 +58,10 @@ FfmpegLogCallback(gcc_unused void *ptr, int level, const char *fmt, va_list vl)
|
||||
cls = *(const AVClass *const*)ptr;
|
||||
|
||||
if (cls != nullptr) {
|
||||
char domain[64];
|
||||
snprintf(domain, sizeof(domain), "%s/%s",
|
||||
ffmpeg_domain.GetName(), cls->item_name(ptr));
|
||||
const auto domain =
|
||||
StringFormat<64>("%s/%s",
|
||||
ffmpeg_domain.GetName(),
|
||||
cls->item_name(ptr));
|
||||
const Domain d(domain);
|
||||
LogFormatV(d, FfmpegImportLogLevel(level), fmt, vl);
|
||||
}
|
||||
|
@@ -37,6 +37,18 @@ public:
|
||||
|
||||
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 &operator=(IcuCompare &&) = default;
|
||||
|
||||
|
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "Connection.hxx"
|
||||
#include "Error.hxx"
|
||||
#include "Lease.hxx"
|
||||
#include "Callback.hxx"
|
||||
#include "event/Loop.hxx"
|
||||
@@ -31,7 +32,11 @@ extern "C" {
|
||||
|
||||
#include <utility>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <poll.h> /* for POLLIN, POLLOUT */
|
||||
#endif
|
||||
|
||||
static constexpr std::chrono::steady_clock::duration NFS_MOUNT_TIMEOUT =
|
||||
std::chrono::minutes(1);
|
||||
@@ -135,7 +140,7 @@ NfsConnection::CancellableCallback::Callback(int err, void *data)
|
||||
if (err >= 0)
|
||||
cb.OnNfsCallback((unsigned)err, data);
|
||||
else
|
||||
cb.OnNfsError(std::make_exception_ptr(std::runtime_error((const char *)data)));
|
||||
cb.OnNfsError(std::make_exception_ptr(NfsClientError(-err, (const char *)data)));
|
||||
} else {
|
||||
if (open) {
|
||||
/* a nfs_open_async() call was cancelled - to
|
||||
|
76
src/lib/nfs/Error.cxx
Normal file
76
src/lib/nfs/Error.cxx
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2007-2017 Content Management AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* author: Max Kellermann <mk@cm4all.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "Error.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
|
||||
extern "C" {
|
||||
#include <nfsc/libnfs.h>
|
||||
}
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static StringBuffer<256>
|
||||
FormatNfsClientError(struct nfs_context *nfs, const char *msg) noexcept
|
||||
{
|
||||
assert(msg != nullptr);
|
||||
|
||||
const char *msg2 = nfs_get_error(nfs);
|
||||
return StringFormat<256>("%s: %s", msg, msg2);
|
||||
}
|
||||
|
||||
NfsClientError::NfsClientError(struct nfs_context *nfs, const char *msg) noexcept
|
||||
:std::runtime_error(FormatNfsClientError(nfs, msg).c_str()),
|
||||
code(0) {}
|
||||
|
||||
static StringBuffer<256>
|
||||
FormatNfsClientError(int err, struct nfs_context *nfs, void *data,
|
||||
const char *msg) noexcept
|
||||
{
|
||||
assert(msg != nullptr);
|
||||
assert(err < 0);
|
||||
|
||||
const char *msg2 = (const char *)data;
|
||||
if (data == nullptr || *(const char *)data == 0) {
|
||||
msg2 = nfs_get_error(nfs);
|
||||
if (msg2 == nullptr)
|
||||
msg2 = strerror(-err);
|
||||
}
|
||||
|
||||
return StringFormat<256>("%s: %s", msg, msg2);
|
||||
}
|
||||
|
||||
NfsClientError::NfsClientError(int err, struct nfs_context *nfs, void *data,
|
||||
const char *msg) noexcept
|
||||
:std::runtime_error(FormatNfsClientError(err, nfs, data, msg).c_str()),
|
||||
code(-err) {}
|
58
src/lib/nfs/Error.hxx
Normal file
58
src/lib/nfs/Error.hxx
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2007-2017 Content Management AG
|
||||
* All rights reserved.
|
||||
*
|
||||
* author: Max Kellermann <mk@cm4all.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef NFS_ERROR_HXX
|
||||
#define NFS_ERROR_HXX
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
class NfsClientError : public std::runtime_error {
|
||||
int code;
|
||||
|
||||
public:
|
||||
explicit NfsClientError(const char *_msg) noexcept
|
||||
:std::runtime_error(_msg), code(0) {}
|
||||
|
||||
NfsClientError(int _code, const char *_msg) noexcept
|
||||
:std::runtime_error(_msg), code(_code) {}
|
||||
|
||||
NfsClientError(struct nfs_context *nfs, const char *msg) noexcept;
|
||||
|
||||
NfsClientError(int err, struct nfs_context *nfs, void *data,
|
||||
const char *msg) noexcept;
|
||||
|
||||
int GetCode() const noexcept {
|
||||
return code;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@@ -24,14 +24,13 @@
|
||||
#include "Connection.hxx"
|
||||
#include "event/Call.hxx"
|
||||
#include "IOThread.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
NfsFileReader::NfsFileReader()
|
||||
:DeferredMonitor(io_thread_get()), state(State::INITIAL)
|
||||
@@ -94,7 +93,7 @@ NfsFileReader::Open(const char *uri)
|
||||
{
|
||||
assert(state == State::INITIAL);
|
||||
|
||||
if (!StringStartsWith(uri, "nfs://"))
|
||||
if (!StringStartsWithCaseASCII(uri, "nfs://"))
|
||||
throw std::runtime_error("Malformed nfs:// URI");
|
||||
|
||||
uri += 6;
|
||||
|
@@ -31,6 +31,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
struct nfsfh;
|
||||
class NfsConnection;
|
||||
|
@@ -34,7 +34,11 @@ static unsigned upnp_ref;
|
||||
static void
|
||||
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)
|
||||
throw FormatRuntimeError("UpnpInit() failed: %s",
|
||||
UpnpGetErrorMessage(code));
|
||||
|
@@ -20,7 +20,7 @@
|
||||
#include "config.h"
|
||||
#include "ls.hxx"
|
||||
#include "client/Response.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
@@ -97,7 +97,7 @@ uri_supported_scheme(const char *uri) noexcept
|
||||
assert(uri_has_scheme(uri));
|
||||
|
||||
while (*urlPrefixes) {
|
||||
if (StringStartsWith(uri, *urlPrefixes))
|
||||
if (StringStartsWithCaseASCII(uri, *urlPrefixes))
|
||||
return true;
|
||||
urlPrefixes++;
|
||||
}
|
||||
|
@@ -139,6 +139,13 @@ static int set_normalized_volume(snd_mixer_elem_t *elem,
|
||||
return set_raw[ctl_dir](elem, value);
|
||||
}
|
||||
|
||||
/* two special cases to avoid rounding errors at 0% and
|
||||
100% */
|
||||
if (volume <= 0)
|
||||
return set_dB[ctl_dir](elem, min, dir);
|
||||
else if (volume >= 100)
|
||||
return set_dB[ctl_dir](elem, max, dir);
|
||||
|
||||
if (use_linear_dB_scale(min, max)) {
|
||||
value = lrint_dir(volume * (max - min), dir) + min;
|
||||
return set_dB[ctl_dir](elem, value, dir);
|
||||
|
@@ -69,7 +69,8 @@ class SmbclientNeighborExplorer final : public NeighborExplorer {
|
||||
|
||||
public:
|
||||
SmbclientNeighborExplorer(NeighborListener &_listener)
|
||||
:NeighborExplorer(_listener) {}
|
||||
:NeighborExplorer(_listener),
|
||||
thread(BIND_THIS_METHOD(ThreadFunc)) {}
|
||||
|
||||
/* virtual methods from class NeighborExplorer */
|
||||
void Open() override;
|
||||
@@ -79,14 +80,13 @@ public:
|
||||
private:
|
||||
void Run();
|
||||
void ThreadFunc();
|
||||
static void ThreadFunc(void *ctx);
|
||||
};
|
||||
|
||||
void
|
||||
SmbclientNeighborExplorer::Open()
|
||||
{
|
||||
quit = false;
|
||||
thread.Start(ThreadFunc, this);
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -239,6 +239,8 @@ SmbclientNeighborExplorer::Run()
|
||||
inline void
|
||||
SmbclientNeighborExplorer::ThreadFunc()
|
||||
{
|
||||
SetThreadName("smbclient");
|
||||
|
||||
mutex.lock();
|
||||
|
||||
while (!quit) {
|
||||
@@ -257,15 +259,6 @@ SmbclientNeighborExplorer::ThreadFunc()
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
void
|
||||
SmbclientNeighborExplorer::ThreadFunc(void *ctx)
|
||||
{
|
||||
SetThreadName("smbclient");
|
||||
|
||||
SmbclientNeighborExplorer &e = *(SmbclientNeighborExplorer *)ctx;
|
||||
e.ThreadFunc();
|
||||
}
|
||||
|
||||
static NeighborExplorer *
|
||||
smbclient_neighbor_create(gcc_unused EventLoop &loop,
|
||||
NeighborListener &listener,
|
||||
|
49
src/net/Init.hxx
Normal file
49
src/net/Init.hxx
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2003-2017 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 NET_INIT_HXX
|
||||
#define NET_INIT_HXX
|
||||
|
||||
#include "check.h"
|
||||
#include "SocketError.hxx"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
class ScopeNetInit {
|
||||
#ifdef _WIN32
|
||||
public:
|
||||
ScopeNetInit() {
|
||||
WSADATA sockinfo;
|
||||
int retval = WSAStartup(MAKEWORD(2, 2), &sockinfo);
|
||||
if (retval != 0)
|
||||
throw MakeSocketError(retval, "WSAStartup() failed");
|
||||
}
|
||||
|
||||
~ScopeNetInit() noexcept {
|
||||
WSACleanup();
|
||||
}
|
||||
#else
|
||||
public:
|
||||
ScopeNetInit() {}
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
@@ -37,6 +37,7 @@
|
||||
#include "config/ConfigGlobal.hxx"
|
||||
#include "config/Block.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/StringFormat.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <stdexcept>
|
||||
@@ -51,7 +52,8 @@
|
||||
|
||||
AudioOutput::AudioOutput(const AudioOutputPlugin &_plugin,
|
||||
const ConfigBlock &block)
|
||||
:plugin(_plugin)
|
||||
:plugin(_plugin),
|
||||
thread(BIND_THIS_METHOD(Task))
|
||||
{
|
||||
assert(plugin.finish != nullptr);
|
||||
assert(plugin.open != nullptr);
|
||||
|
@@ -515,7 +515,6 @@ private:
|
||||
* The OutputThread.
|
||||
*/
|
||||
void Task();
|
||||
static void Task(void *arg);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -396,7 +396,7 @@ AudioOutput::Pause()
|
||||
pause = false;
|
||||
}
|
||||
|
||||
inline void
|
||||
void
|
||||
AudioOutput::Task()
|
||||
{
|
||||
FormatThreadName("output:%s", name);
|
||||
@@ -512,17 +512,10 @@ AudioOutput::Task()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioOutput::Task(void *arg)
|
||||
{
|
||||
AudioOutput *ao = (AudioOutput *)arg;
|
||||
ao->Task();
|
||||
}
|
||||
|
||||
void
|
||||
AudioOutput::StartThread()
|
||||
{
|
||||
assert(command == Command::NONE);
|
||||
|
||||
thread.Start(Task, this);
|
||||
thread.Start();
|
||||
}
|
||||
|
@@ -846,6 +846,12 @@ AlsaOutput::Recover(int err)
|
||||
case SND_PCM_STATE_DRAINING:
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* this default case is just here to work around
|
||||
-Wswitch due to SND_PCM_STATE_PRIVATE1 (libasound
|
||||
1.1.6) */
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
@@ -77,7 +77,6 @@ public:
|
||||
void Close();
|
||||
|
||||
size_t Play(const void *chunk, size_t size);
|
||||
void Cancel();
|
||||
|
||||
std::chrono::steady_clock::duration Delay() noexcept;
|
||||
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include "../Wrapper.hxx"
|
||||
#include "mixer/MixerList.hxx"
|
||||
#include "mixer/plugins/PulseMixerPlugin.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <pulse/thread-mainloop.h>
|
||||
@@ -854,7 +855,10 @@ PulseOutput::TestDefaultDevice()
|
||||
try {
|
||||
const ConfigBlock empty;
|
||||
PulseOutput po(empty);
|
||||
po.Enable();
|
||||
AtScopeExit(&po) { po.Disable(); };
|
||||
po.WaitConnection();
|
||||
|
||||
return true;
|
||||
} catch (const std::runtime_error &e) {
|
||||
return false;
|
||||
|
@@ -36,6 +36,8 @@
|
||||
#include <roaraudio.h>
|
||||
#undef new
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
class RoarOutput {
|
||||
friend struct AudioOutputWrapper<RoarOutput>;
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user