Compare commits
79 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
04ed50fb0f | ||
![]() |
c9553411bb | ||
![]() |
62221adf55 | ||
![]() |
a6bf4746c6 | ||
![]() |
72637d00e8 | ||
![]() |
27d4b15925 | ||
![]() |
7a77767e66 | ||
![]() |
1b26621860 | ||
![]() |
3db5f4d0aa | ||
![]() |
b2a6e327bf | ||
![]() |
9aec5fe907 | ||
![]() |
c731a82b71 | ||
![]() |
e6fad97edc | ||
![]() |
70495aada1 | ||
![]() |
f243f615ef | ||
![]() |
807c72b2f1 | ||
![]() |
74dbaade6f | ||
![]() |
53677172f2 | ||
![]() |
bef0ccf42a | ||
![]() |
ff35aa07dc | ||
![]() |
a3afd5178c | ||
![]() |
f1285a6dfd | ||
![]() |
cf7c1afb93 | ||
![]() |
e140a28073 | ||
![]() |
de61c3b962 | ||
![]() |
c46fc4531b | ||
![]() |
065a9ed10f | ||
![]() |
e44c0254f7 | ||
![]() |
13f9f0315f | ||
![]() |
1532ffe215 | ||
![]() |
b24cbc68ba | ||
![]() |
976fdd76c1 | ||
![]() |
bbda335e02 | ||
![]() |
d2dd6f7c70 | ||
![]() |
e9a544fa98 | ||
![]() |
79f2f8cddc | ||
![]() |
39fa949345 | ||
![]() |
e1d7a5cbf5 | ||
![]() |
f3cefaf043 | ||
![]() |
b3460f3f54 | ||
![]() |
1e0ad1f6bf | ||
![]() |
4abcb08cc9 | ||
![]() |
81e7833711 | ||
![]() |
82e261ad33 | ||
![]() |
cae2811762 | ||
![]() |
09112c6869 | ||
![]() |
77aaf1baee | ||
![]() |
6626c2d00d | ||
![]() |
315f9d98f6 | ||
![]() |
f087518e7a | ||
![]() |
db9997a106 | ||
![]() |
0cbfb610f2 | ||
![]() |
f901cd042b | ||
![]() |
5719207dfa | ||
![]() |
a84fbbe327 | ||
![]() |
93c97972b9 | ||
![]() |
ac61d43720 | ||
![]() |
1958f78cc1 | ||
![]() |
a7ee64a25b | ||
![]() |
2a58f22649 | ||
![]() |
f066bb7716 | ||
![]() |
4e3d182189 | ||
![]() |
205fba74cf | ||
![]() |
a9bcf8d50d | ||
![]() |
b0ff3bc7a3 | ||
![]() |
06301e279c | ||
![]() |
6d6f274648 | ||
![]() |
9acefcb256 | ||
![]() |
e4d0293a31 | ||
![]() |
ae77542a11 | ||
![]() |
980187f856 | ||
![]() |
327a8e6c59 | ||
![]() |
d11e2724c4 | ||
![]() |
f768ca3a2d | ||
![]() |
947e902288 | ||
![]() |
3436a646b5 | ||
![]() |
aed0af1e00 | ||
![]() |
0d7ee2b014 | ||
![]() |
2f5fd91bd8 |
Makefile.amNEWSconfigure.ac
doc
src
DetachedSong.hxxMain.cxxPlayerThread.cxx
archive
plugins
decoder
encoder
filter
plugins
input
mixer
notify.hxxoutput
playlist
plugins
protocol
queue
storage
plugins
system
tag
thread
unix
util
systemd
test
@@ -1219,6 +1219,7 @@ liboutput_plugins_a_SOURCES = \
|
||||
|
||||
MIXER_LIBS = \
|
||||
libmixer_plugins.a \
|
||||
$(ALSA_LIBS) \
|
||||
$(PULSE_LIBS)
|
||||
|
||||
MIXER_API_SRC = \
|
||||
@@ -2108,7 +2109,9 @@ developer_DATA = $(wildcard doc/developer/*.html)
|
||||
|
||||
DOCBOOK_HTML = $(patsubst %.xml,%/index.html,$(DOCBOOK_FILES))
|
||||
|
||||
$(DOCBOOK_HTML): %/index.html: %.xml
|
||||
DOCBOOK_INCLUDES = $(wildcard $(srcdir)/doc/include/*.xml)
|
||||
|
||||
$(DOCBOOK_HTML): %/index.html: %.xml $(DOCBOOK_INCLUDES)
|
||||
$(XMLTO) -o $(@D) --stringparam chunker.output.encoding=utf-8 html --stringparam use.id.as.filename=1 $<
|
||||
|
||||
doc/api/html/index.html: doc/doxygen.conf
|
||||
|
57
NEWS
57
NEWS
@@ -1,3 +1,60 @@
|
||||
ver 0.19.15 (2016/04/30)
|
||||
* decoder
|
||||
- ffmpeg: support FFmpeg 3.0
|
||||
- ffmpeg: use as fallback instead of "mad" if no plugin matches
|
||||
- opus: support bigger OpusTags packets
|
||||
* fix more build failures on non-glibc builds due to constexpr Mutex
|
||||
* fix build failure due to missing include
|
||||
* fix unit test on Alpha
|
||||
|
||||
ver 0.19.14 (2016/03/18)
|
||||
* decoder
|
||||
- dsdiff: fix off-by-one buffer overflow
|
||||
- opus: limit tag size to 64 kB
|
||||
* archive
|
||||
- iso9660: fix buffer overflow
|
||||
* fix quadratic runtime bug in the tag pool
|
||||
* fix build failures on non-glibc builds due to constexpr Mutex
|
||||
|
||||
ver 0.19.13 (2016/02/23)
|
||||
* tags
|
||||
- aiff, riff: fix ID3 chunk padding
|
||||
* decoder
|
||||
- ffmpeg: support the TAK codec
|
||||
* fix disappearing duration of remote songs during playback
|
||||
* initialize supplementary groups with glibc 2.19+
|
||||
|
||||
ver 0.19.12 (2015/12/15)
|
||||
* fix assertion failure on malformed UTF-8 tag
|
||||
* fix build failure on non-Linux systems
|
||||
* fix LimitRTTIME in systemd unit file
|
||||
|
||||
ver 0.19.11 (2015/10/27)
|
||||
* tags
|
||||
- ape: fix buffer overflow
|
||||
* decoder
|
||||
- ffmpeg: fix crash due to wrong avio_alloc_context() call
|
||||
- gme: don't loop forever, fall back to GME's default play length
|
||||
* encoder
|
||||
- flac: fix crash with 32 bit playback
|
||||
* mixer
|
||||
- fix mixer lag after enabling/disabling output
|
||||
|
||||
ver 0.19.10 (2015/06/21)
|
||||
* input
|
||||
- curl: fix deadlock on small responses
|
||||
- smbclient: fix DFF playback
|
||||
* decoder
|
||||
- ffmpeg: improve seeking accuracy
|
||||
- fix stuck stream tags
|
||||
* encoder
|
||||
- opus: fix bogus granulepos
|
||||
* output
|
||||
- fix failure to open device right after booting
|
||||
* neighbor
|
||||
- nfs: fix deadlock when connecting
|
||||
* fix "single" mode breakage due to queue edits
|
||||
|
||||
ver 0.19.9 (2015/02/06)
|
||||
* decoder
|
||||
- dsdiff, dsf: raise ID3 tag limit to 1 MB
|
||||
|
@@ -1,10 +1,10 @@
|
||||
AC_PREREQ(2.60)
|
||||
|
||||
AC_INIT(mpd, 0.19.9, musicpd-dev-team@lists.sourceforge.net)
|
||||
AC_INIT(mpd, 0.19.15, musicpd-dev-team@lists.sourceforge.net)
|
||||
|
||||
VERSION_MAJOR=0
|
||||
VERSION_MINOR=19
|
||||
VERSION_REVISION=9
|
||||
VERSION_REVISION=15
|
||||
VERSION_EXTRA=0
|
||||
|
||||
AC_CONFIG_SRCDIR([src/Main.cxx])
|
||||
@@ -206,6 +206,7 @@ if test x$host_is_linux = xyes; then
|
||||
fi
|
||||
|
||||
AC_CHECK_FUNCS(getpwnam_r getpwuid_r)
|
||||
AC_CHECK_FUNCS(initgroups)
|
||||
AC_CHECK_FUNCS(strndup)
|
||||
|
||||
if test x$host_is_linux = xyes; then
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<?xml version='1.0' encoding="utf-8"?>
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"docbook/dtd/xml/4.2/docbookx.dtd">
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
|
||||
<book>
|
||||
<title>The Music Player Daemon - Developer's Manual</title>
|
||||
|
||||
|
154
doc/include/tags.xml
Normal file
154
doc/include/tags.xml
Normal file
@@ -0,0 +1,154 @@
|
||||
<?xml version='1.0' encoding="utf-8"?>
|
||||
<!DOCTYPE itemizedlist PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>artist</varname>: the artist name. Its meaning is not
|
||||
well-defined; see <varname>composer</varname> and
|
||||
<varname>performer</varname> for more specific tags.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>artistsort</varname>: same as
|
||||
<varname>artist</varname>, but for sorting. This usually omits
|
||||
prefixes such as "The".
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>album</varname>: the album name.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>albumsort</varname>: same as <varname>album</varname>,
|
||||
but for sorting.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>albumartist</varname>: on multi-artist albums, this is
|
||||
the artist name which shall be used for the whole album. The
|
||||
exact meaning of this tag is not well-defined.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>albumartistsort</varname>: same as
|
||||
<varname>albumartist</varname>, but for sorting.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>title</varname>: the song title.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>track</varname>: the track number within the album.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>name</varname>: a name for this song. This is not the
|
||||
song title. The exact meaning of this tag is not well-defined.
|
||||
It is often used by badly configured internet radio stations
|
||||
with broken tags to squeeze both the artist name and the song
|
||||
title in one tag.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>genre</varname>: the music genre.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>date</varname>: the song's release date. This is
|
||||
usually a 4-digit year.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>composer</varname>: the artist who composed the song.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>performer</varname>: the artist who performed the song.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>comment</varname>: a human-readable comment about this
|
||||
song. The exact meaning of this tag is not well-defined.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>disc</varname>: the disc number in a multi-disc album.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_artistid</varname>: the artist id in the
|
||||
<ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_albumid</varname>: the album id in the
|
||||
<ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_albumartistid</varname>: the album artist
|
||||
id in the <ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_trackid</varname>: the track id in the
|
||||
<ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
<varname>musicbrainz_releasetrackid</varname>: the release track
|
||||
id in the <ulink
|
||||
url="http://musicbrainz.org/doc/MusicBrainzTag">MusicBrainz</ulink>
|
||||
database.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
@@ -174,18 +174,6 @@ MP3 playback.
|
||||
This specifies whether relative or absolute paths for song filenames are used
|
||||
when saving playlists. The default is "no".
|
||||
.TP
|
||||
.B metadata_to_use <tags>
|
||||
This specifies the tag types that will be scanned for and made available to
|
||||
clients. Note that you must recreate (not update) your database for changes to
|
||||
this parameter to take effect. Possible values are artist, album, title,
|
||||
track, name, genre, date, composer, performer, comment, disc,
|
||||
musicbrainz_artistid, musicbrainz_albumid, musicbrainz_albumartistid,
|
||||
musicbrainz_releasetrackid, musicbrainz_trackid. Multiple tags may be specified
|
||||
as a comma separated list.
|
||||
An example value is "artist,album,title,track". The special value "none" may
|
||||
be used alone to disable all metadata. The default is to use all known tag
|
||||
types except for comments and those starting with "musicbrainz".
|
||||
.TP
|
||||
.B auto_update <yes or no>
|
||||
This specifies the whether to support automatic update of music database when
|
||||
files are changed in music_directory. The default is to disable autoupdate
|
||||
|
@@ -115,7 +115,7 @@
|
||||
#
|
||||
# This setting defines a list of tag types that will be extracted during the
|
||||
# audio file discovery process. The complete list of possible values can be
|
||||
# found in the mpd.conf man page.
|
||||
# found in the user manual.
|
||||
#metadata_to_use "artist,album,title,track,name,genre,date,composer,performer,disc"
|
||||
#
|
||||
# This setting enables automatic update of MPD's database when files in
|
||||
|
@@ -1,6 +1,7 @@
|
||||
<?xml version='1.0' encoding="utf-8"?>
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"docbook/dtd/xml/4.2/docbookx.dtd">
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
|
||||
<book>
|
||||
<title>The Music Player Daemon protocol</title>
|
||||
|
||||
@@ -201,6 +202,25 @@
|
||||
omitted, then the maximum possible value is assumed.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="tags">
|
||||
<title>Tags</title>
|
||||
|
||||
<para>
|
||||
The following tags are supported by
|
||||
<application>MPD</application>:
|
||||
</para>
|
||||
|
||||
<xi:include href="include/tags.xml"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
||||
|
||||
<para>
|
||||
There can be multiple values for some of these tags. For
|
||||
example, <application>MPD</application> may return multiple
|
||||
lines with a <varname>performer</varname> tag. A tag value is
|
||||
a UTF-8 string.
|
||||
</para>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
<chapter id="recipes">
|
||||
|
102
doc/user.xml
102
doc/user.xml
@@ -1,6 +1,7 @@
|
||||
<?xml version='1.0' encoding="utf-8"?>
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
|
||||
"docbook/dtd/xml/4.2/docbookx.dtd">
|
||||
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
|
||||
|
||||
<book>
|
||||
<title>The Music Player Daemon - User's Manual</title>
|
||||
|
||||
@@ -16,7 +17,7 @@
|
||||
<application>MPD</application> (Music Player Daemon) is, as the
|
||||
name suggests, a server software allowing you to remotely play
|
||||
your music, handle playlists, deliver music (HTTP streams with
|
||||
various sub-protocols) and organizze playlists.
|
||||
various sub-protocols) and organize playlists.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@@ -944,6 +945,33 @@ systemctl start mpd.socket</programlisting>
|
||||
<section id="config_other">
|
||||
<title>Other Settings</title>
|
||||
|
||||
<informaltable>
|
||||
<tgroup cols="2">
|
||||
<thead>
|
||||
<row>
|
||||
<entry>Setting</entry>
|
||||
<entry>Description</entry>
|
||||
</row>
|
||||
</thead>
|
||||
<tbody>
|
||||
<row>
|
||||
<entry>
|
||||
<varname>metadata_to_use</varname>
|
||||
<parameter>TAG1,TAG2,...</parameter>
|
||||
</entry>
|
||||
<entry>
|
||||
Use only the specified tags, and ignore the others.
|
||||
This setting can reduce the database size and
|
||||
<application>MPD</application>'s memory usage by
|
||||
omitting unused tags. By default, all tags but
|
||||
<varname>comment</varname> are enabled. The special
|
||||
value "none" disables all tags.
|
||||
</entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</informaltable>
|
||||
|
||||
<section>
|
||||
<title>The State File</title>
|
||||
|
||||
@@ -1190,6 +1218,58 @@ database {
|
||||
plugin).
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="realtime">
|
||||
<title>Real-Time Scheduling</title>
|
||||
|
||||
<para>
|
||||
On Linux, <application>MPD</application> attempts to configure
|
||||
<ulink
|
||||
url="https://en.wikipedia.org/wiki/Real-time_computing">real-time
|
||||
scheduling</ulink> for some threads that benefit from it.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This is only possible you allow <application>MPD</application>
|
||||
to do it. This privilege is controlled by
|
||||
<varname>RLIMIT_RTPRIO</varname>
|
||||
<varname>RLIMIT_RTTIME</varname>. You can configure this
|
||||
privilege with <command>ulimit</command> before launching
|
||||
<application>MPD</application>:
|
||||
</para>
|
||||
|
||||
<programlisting>ulimit -HS -r 50; mpd</programlisting>
|
||||
|
||||
<para>
|
||||
Or you can use the <command>prlimit</command> program from the
|
||||
<application>util-linux</application> package:
|
||||
</para>
|
||||
|
||||
<programlisting>prlimit --rtprio=50 --rttime=unlimited mpd</programlisting>
|
||||
|
||||
<para>
|
||||
The <application>systemd</application> service file shipped
|
||||
with <application>MPD</application> comes with this setting.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This works only if the Linux kernel was compiled with
|
||||
<varname>CONFIG_RT_GROUP_SCHED</varname> disabled. Use the
|
||||
following command to check this option for your current
|
||||
kernel:
|
||||
</para>
|
||||
|
||||
<programlisting>zgrep ^CONFIG_RT_GROUP_SCHED /proc/config.gz</programlisting>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
There is a rumor that real-time scheduling improves audio
|
||||
quality. That is not true. All it does is reduce the
|
||||
probability of skipping (audio buffer xruns) when the
|
||||
computer is under heavy load.
|
||||
</para>
|
||||
</note>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
<chapter id="use">
|
||||
@@ -1245,6 +1325,19 @@ database {
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="tags">
|
||||
<title>Metadata</title>
|
||||
|
||||
<para>
|
||||
When scanning or playing a song,
|
||||
<application>MPD</application> parses its metadata. The
|
||||
following tags are supported:
|
||||
</para>
|
||||
|
||||
<xi:include href="include/tags.xml"
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
||||
</section>
|
||||
|
||||
<section id="queue">
|
||||
<title>The queue</title>
|
||||
|
||||
@@ -2595,7 +2688,8 @@ buffer_size: 16384</programlisting>
|
||||
/ <ulink
|
||||
url="http://icecast.org/"><application>IceCast</application></ulink>.
|
||||
HTTP streaming clients like
|
||||
<application>mplayer</application> can connect to it.
|
||||
<application>mplayer</application>, <application>VLC</application>,
|
||||
and <application>mpv</application> can connect to it.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
@@ -188,6 +188,14 @@ public:
|
||||
tag = std::move(other.tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to the MoveTagFrom(), but move only the #TagItem
|
||||
* array.
|
||||
*/
|
||||
void MoveTagItemsFrom(DetachedSong &&other) {
|
||||
tag.MoveItemsFrom(std::move(other.tag));
|
||||
}
|
||||
|
||||
time_t GetLastModified() const {
|
||||
return mtime;
|
||||
}
|
||||
|
@@ -54,7 +54,6 @@
|
||||
#include "system/FatalError.hxx"
|
||||
#include "util/UriUtil.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "thread/Id.hxx"
|
||||
#include "thread/Slack.hxx"
|
||||
#include "lib/icu/Init.hxx"
|
||||
@@ -123,8 +122,6 @@
|
||||
static constexpr unsigned DEFAULT_BUFFER_SIZE = 4096;
|
||||
static constexpr unsigned DEFAULT_BUFFER_BEFORE_PLAY = 10;
|
||||
|
||||
static constexpr Domain main_domain("main");
|
||||
|
||||
#ifdef ANDROID
|
||||
Context *context;
|
||||
#endif
|
||||
@@ -633,7 +630,7 @@ static int mpd_main_after_fork(struct options options)
|
||||
config_get_unsigned(CONF_AUTO_UPDATE_DEPTH,
|
||||
INT_MAX));
|
||||
#else
|
||||
FormatWarning(main_domain,
|
||||
FormatWarning(config_domain,
|
||||
"inotify: auto_update was disabled. enable during compilation phase");
|
||||
#endif
|
||||
}
|
||||
|
@@ -612,6 +612,12 @@ Player::ProcessCommand()
|
||||
|
||||
queued = true;
|
||||
pc.CommandFinished();
|
||||
|
||||
pc.Unlock();
|
||||
if (dc.LockIsIdle())
|
||||
StartDecoder(*new MusicPipe());
|
||||
pc.Lock();
|
||||
|
||||
break;
|
||||
|
||||
case PlayerCommand::PAUSE:
|
||||
|
@@ -66,7 +66,11 @@ public:
|
||||
return iso9660_iso_seek_read(iso, ptr, start, i_size);
|
||||
}
|
||||
|
||||
void Visit(const char *path, ArchiveVisitor &visitor);
|
||||
/**
|
||||
* @param capacity the path buffer size
|
||||
*/
|
||||
void Visit(char *path, size_t length, size_t capacity,
|
||||
ArchiveVisitor &visitor);
|
||||
|
||||
virtual void Close() override {
|
||||
Unref();
|
||||
@@ -84,32 +88,36 @@ static constexpr Domain iso9660_domain("iso9660");
|
||||
/* archive open && listing routine */
|
||||
|
||||
inline void
|
||||
Iso9660ArchiveFile::Visit(const char *psz_path, ArchiveVisitor &visitor)
|
||||
Iso9660ArchiveFile::Visit(char *path, size_t length, size_t capacity,
|
||||
ArchiveVisitor &visitor)
|
||||
{
|
||||
CdioList_t *entlist;
|
||||
CdioListNode_t *entnode;
|
||||
iso9660_stat_t *statbuf;
|
||||
char pathname[4096];
|
||||
|
||||
entlist = iso9660_ifs_readdir (iso, psz_path);
|
||||
auto *entlist = iso9660_ifs_readdir(iso, path);
|
||||
if (!entlist) {
|
||||
return;
|
||||
}
|
||||
/* Iterate over the list of nodes that iso9660_ifs_readdir gives */
|
||||
CdioListNode_t *entnode;
|
||||
_CDIO_LIST_FOREACH (entnode, entlist) {
|
||||
statbuf = (iso9660_stat_t *) _cdio_list_node_data (entnode);
|
||||
auto *statbuf = (iso9660_stat_t *)
|
||||
_cdio_list_node_data(entnode);
|
||||
const char *filename = statbuf->filename;
|
||||
if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0)
|
||||
continue;
|
||||
|
||||
strcpy(pathname, psz_path);
|
||||
strcat(pathname, statbuf->filename);
|
||||
size_t filename_length = strlen(filename);
|
||||
if (length + filename_length + 1 >= capacity)
|
||||
/* file name is too long */
|
||||
continue;
|
||||
|
||||
memcpy(path + length, filename, filename_length + 1);
|
||||
size_t new_length = length + filename_length;
|
||||
|
||||
if (iso9660_stat_s::_STAT_DIR == statbuf->type ) {
|
||||
if (strcmp(statbuf->filename, ".") && strcmp(statbuf->filename, "..")) {
|
||||
strcat(pathname, "/");
|
||||
Visit(pathname, visitor);
|
||||
}
|
||||
memcpy(path + new_length, "/", 2);
|
||||
Visit(path, new_length + 1, capacity, visitor);
|
||||
} else {
|
||||
//remove leading /
|
||||
visitor.VisitArchiveEntry(pathname + 1);
|
||||
visitor.VisitArchiveEntry(path + 1);
|
||||
}
|
||||
}
|
||||
_cdio_list_free (entlist, true);
|
||||
@@ -133,7 +141,8 @@ iso9660_archive_open(Path pathname, Error &error)
|
||||
void
|
||||
Iso9660ArchiveFile::Visit(ArchiveVisitor &visitor)
|
||||
{
|
||||
Visit("/", visitor);
|
||||
char path[4096] = "/";
|
||||
Visit(path, 1, sizeof(path), visitor);
|
||||
}
|
||||
|
||||
/* single archive handling */
|
||||
|
@@ -433,8 +433,11 @@ update_stream_tag(Decoder &decoder, InputStream *is)
|
||||
|
||||
/* no stream tag present - submit the song tag
|
||||
instead */
|
||||
decoder.song_tag = nullptr;
|
||||
}
|
||||
} else
|
||||
/* discard the song tag; we don't need it */
|
||||
delete decoder.song_tag;
|
||||
|
||||
decoder.song_tag = nullptr;
|
||||
|
||||
delete decoder.stream_tag;
|
||||
decoder.stream_tag = tag;
|
||||
@@ -566,7 +569,7 @@ decoder_tag(Decoder &decoder, InputStream *is,
|
||||
/* save the tag */
|
||||
|
||||
delete decoder.decoder_tag;
|
||||
decoder.decoder_tag = new Tag(tag);
|
||||
decoder.decoder_tag = new Tag(std::move(tag));
|
||||
|
||||
/* check for a new stream tag */
|
||||
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include "util/ConstBuffer.hxx"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct Decoder;
|
||||
class InputStream;
|
||||
|
@@ -255,7 +255,11 @@ decoder_run_stream_fallback(Decoder &decoder, InputStream &is)
|
||||
{
|
||||
const struct DecoderPlugin *plugin;
|
||||
|
||||
#ifdef HAVE_FFMPEG
|
||||
plugin = decoder_plugin_from_name("ffmpeg");
|
||||
#else
|
||||
plugin = decoder_plugin_from_name("mad");
|
||||
#endif
|
||||
return plugin != nullptr && plugin->stream_decode != nullptr &&
|
||||
decoder_stream_decode(*plugin, decoder, is);
|
||||
}
|
||||
@@ -380,7 +384,11 @@ decoder_run_song(DecoderControl &dc,
|
||||
const DetachedSong &song, const char *uri, Path path_fs)
|
||||
{
|
||||
Decoder decoder(dc, dc.start_time.IsPositive(),
|
||||
new Tag(song.GetTag()));
|
||||
/* pass the song tag only if it's
|
||||
authoritative, i.e. if it's a local file -
|
||||
tags on "stream" songs are just remembered
|
||||
from the last time we played it*/
|
||||
song.IsFile() ? new Tag(song.GetTag()) : nullptr);
|
||||
int ret;
|
||||
|
||||
dc.state = DecoderState::START;
|
||||
|
@@ -205,7 +205,7 @@ dsdiff_handle_native_tag(InputStream &is,
|
||||
if (length == 0 || length > 60)
|
||||
return;
|
||||
|
||||
char string[length];
|
||||
char string[length + 1];
|
||||
char *label;
|
||||
label = string;
|
||||
|
||||
|
@@ -92,14 +92,14 @@ struct AvioStream {
|
||||
|
||||
AVIOContext *io;
|
||||
|
||||
unsigned char buffer[8192];
|
||||
|
||||
AvioStream(Decoder *_decoder, InputStream &_input)
|
||||
:decoder(_decoder), input(_input), io(nullptr) {}
|
||||
|
||||
~AvioStream() {
|
||||
if (io != nullptr)
|
||||
if (io != nullptr) {
|
||||
av_free(io->buffer);
|
||||
av_free(io);
|
||||
}
|
||||
}
|
||||
|
||||
bool Open();
|
||||
@@ -153,11 +153,20 @@ mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
|
||||
bool
|
||||
AvioStream::Open()
|
||||
{
|
||||
io = avio_alloc_context(buffer, sizeof(buffer),
|
||||
constexpr size_t BUFFER_SIZE = 8192;
|
||||
auto buffer = (unsigned char *)av_malloc(BUFFER_SIZE);
|
||||
if (buffer == nullptr)
|
||||
return false;
|
||||
|
||||
io = avio_alloc_context(buffer, BUFFER_SIZE,
|
||||
false, this,
|
||||
mpd_ffmpeg_stream_read, nullptr,
|
||||
input.IsSeekable()
|
||||
? mpd_ffmpeg_stream_seek : nullptr);
|
||||
/* If avio_alloc_context() fails, who frees the buffer? The
|
||||
libavformat API documentation does not specify this, it
|
||||
only says that AVIOContext.buffer must be freed in the end,
|
||||
however no AVIOContext exists in that failure code path. */
|
||||
return io != nullptr;
|
||||
}
|
||||
|
||||
@@ -190,10 +199,10 @@ ffmpeg_init(gcc_unused const config_param ¶m)
|
||||
}
|
||||
|
||||
static int
|
||||
ffmpeg_find_audio_stream(const AVFormatContext *format_context)
|
||||
ffmpeg_find_audio_stream(const AVFormatContext &format_context)
|
||||
{
|
||||
for (unsigned i = 0; i < format_context->nb_streams; ++i)
|
||||
if (format_context->streams[i]->codec->codec_type ==
|
||||
for (unsigned i = 0; i < format_context.nb_streams; ++i)
|
||||
if (format_context.streams[i]->codec->codec_type ==
|
||||
AVMEDIA_TYPE_AUDIO)
|
||||
return i;
|
||||
|
||||
@@ -267,22 +276,22 @@ copy_interleave_frame2(uint8_t *dest, uint8_t **src,
|
||||
* Copy PCM data from a AVFrame to an interleaved buffer.
|
||||
*/
|
||||
static int
|
||||
copy_interleave_frame(const AVCodecContext *codec_context,
|
||||
const AVFrame *frame,
|
||||
copy_interleave_frame(const AVCodecContext &codec_context,
|
||||
const AVFrame &frame,
|
||||
uint8_t **output_buffer,
|
||||
uint8_t **global_buffer, int *global_buffer_size)
|
||||
{
|
||||
int plane_size;
|
||||
const int data_size =
|
||||
av_samples_get_buffer_size(&plane_size,
|
||||
codec_context->channels,
|
||||
frame->nb_samples,
|
||||
codec_context->sample_fmt, 1);
|
||||
codec_context.channels,
|
||||
frame.nb_samples,
|
||||
codec_context.sample_fmt, 1);
|
||||
if (data_size <= 0)
|
||||
return data_size;
|
||||
|
||||
if (av_sample_fmt_is_planar(codec_context->sample_fmt) &&
|
||||
codec_context->channels > 1) {
|
||||
if (av_sample_fmt_is_planar(codec_context.sample_fmt) &&
|
||||
codec_context.channels > 1) {
|
||||
if(*global_buffer_size < data_size) {
|
||||
av_freep(global_buffer);
|
||||
|
||||
@@ -294,47 +303,85 @@ copy_interleave_frame(const AVCodecContext *codec_context,
|
||||
*global_buffer_size = data_size;
|
||||
}
|
||||
*output_buffer = *global_buffer;
|
||||
copy_interleave_frame2(*output_buffer, frame->extended_data,
|
||||
frame->nb_samples,
|
||||
codec_context->channels,
|
||||
av_get_bytes_per_sample(codec_context->sample_fmt));
|
||||
copy_interleave_frame2(*output_buffer, frame.extended_data,
|
||||
frame.nb_samples,
|
||||
codec_context.channels,
|
||||
av_get_bytes_per_sample(codec_context.sample_fmt));
|
||||
} else {
|
||||
*output_buffer = frame->extended_data[0];
|
||||
*output_buffer = frame.extended_data[0];
|
||||
}
|
||||
|
||||
return data_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert AVPacket::pts to a stream-relative time stamp (still in
|
||||
* AVStream::time_base units). Returns a negative value on error.
|
||||
*/
|
||||
gcc_pure
|
||||
static int64_t
|
||||
StreamRelativePts(const AVPacket &packet, const AVStream &stream)
|
||||
{
|
||||
auto pts = packet.pts;
|
||||
if (pts < 0 || pts == int64_t(AV_NOPTS_VALUE))
|
||||
return -1;
|
||||
|
||||
auto start = start_time_fallback(stream);
|
||||
return pts - start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a non-negative stream-relative time stamp in
|
||||
* AVStream::time_base units to a PCM frame number.
|
||||
*/
|
||||
gcc_pure
|
||||
static uint64_t
|
||||
PtsToPcmFrame(uint64_t pts, const AVStream &stream,
|
||||
const AVCodecContext &codec_context)
|
||||
{
|
||||
return av_rescale_q(pts, stream.time_base, codec_context.time_base);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param min_frame skip all data before this PCM frame number; this
|
||||
* is used after seeking to skip data in an AVPacket until the exact
|
||||
* desired time stamp has been reached
|
||||
*/
|
||||
static DecoderCommand
|
||||
ffmpeg_send_packet(Decoder &decoder, InputStream &is,
|
||||
const AVPacket *packet,
|
||||
AVCodecContext *codec_context,
|
||||
const AVStream *stream,
|
||||
AVPacket &&packet,
|
||||
AVCodecContext &codec_context,
|
||||
const AVStream &stream,
|
||||
AVFrame *frame,
|
||||
uint64_t min_frame, size_t pcm_frame_size,
|
||||
uint8_t **buffer, int *buffer_size)
|
||||
{
|
||||
if (packet->pts >= 0 && packet->pts != (int64_t)AV_NOPTS_VALUE) {
|
||||
auto start = start_time_fallback(*stream);
|
||||
if (packet->pts >= start)
|
||||
decoder_timestamp(decoder,
|
||||
time_from_ffmpeg(packet->pts - start,
|
||||
stream->time_base));
|
||||
}
|
||||
size_t skip_bytes = 0;
|
||||
|
||||
AVPacket packet2 = *packet;
|
||||
const auto pts = StreamRelativePts(packet, stream);
|
||||
if (pts >= 0) {
|
||||
if (min_frame > 0) {
|
||||
auto cur_frame = PtsToPcmFrame(pts, stream,
|
||||
codec_context);
|
||||
if (cur_frame < min_frame)
|
||||
skip_bytes = pcm_frame_size * (min_frame - cur_frame);
|
||||
} else
|
||||
decoder_timestamp(decoder,
|
||||
time_from_ffmpeg(pts, stream.time_base));
|
||||
}
|
||||
|
||||
uint8_t *output_buffer;
|
||||
|
||||
DecoderCommand cmd = DecoderCommand::NONE;
|
||||
while (packet2.size > 0 && cmd == DecoderCommand::NONE) {
|
||||
while (packet.size > 0 && cmd == DecoderCommand::NONE) {
|
||||
int audio_size = 0;
|
||||
int got_frame = 0;
|
||||
int len = avcodec_decode_audio4(codec_context,
|
||||
int len = avcodec_decode_audio4(&codec_context,
|
||||
frame, &got_frame,
|
||||
&packet2);
|
||||
&packet);
|
||||
if (len >= 0 && got_frame) {
|
||||
audio_size = copy_interleave_frame(codec_context,
|
||||
frame,
|
||||
*frame,
|
||||
&output_buffer,
|
||||
buffer, buffer_size);
|
||||
if (audio_size < 0)
|
||||
@@ -348,15 +395,27 @@ ffmpeg_send_packet(Decoder &decoder, InputStream &is,
|
||||
break;
|
||||
}
|
||||
|
||||
packet2.data += len;
|
||||
packet2.size -= len;
|
||||
packet.data += len;
|
||||
packet.size -= len;
|
||||
|
||||
if (audio_size <= 0)
|
||||
continue;
|
||||
|
||||
const uint8_t *data = output_buffer;
|
||||
if (skip_bytes > 0) {
|
||||
if (skip_bytes >= size_t(audio_size)) {
|
||||
skip_bytes -= audio_size;
|
||||
continue;
|
||||
}
|
||||
|
||||
data += skip_bytes;
|
||||
audio_size -= skip_bytes;
|
||||
skip_bytes = 0;
|
||||
}
|
||||
|
||||
cmd = decoder_data(decoder, is,
|
||||
output_buffer, audio_size,
|
||||
codec_context->bit_rate / 1000);
|
||||
data, audio_size,
|
||||
codec_context.bit_rate / 1000);
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
@@ -474,7 +533,7 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
|
||||
return;
|
||||
}
|
||||
|
||||
int audio_stream = ffmpeg_find_audio_stream(format_context);
|
||||
int audio_stream = ffmpeg_find_audio_stream(*format_context);
|
||||
if (audio_stream == -1) {
|
||||
LogError(ffmpeg_domain, "No audio stream inside");
|
||||
avformat_close_input(&format_context);
|
||||
@@ -559,6 +618,8 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
|
||||
uint8_t *interleaved_buffer = nullptr;
|
||||
int interleaved_buffer_size = 0;
|
||||
|
||||
uint64_t min_frame = 0;
|
||||
|
||||
DecoderCommand cmd;
|
||||
do {
|
||||
AVPacket packet;
|
||||
@@ -566,16 +627,23 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
|
||||
/* end of file */
|
||||
break;
|
||||
|
||||
if (packet.stream_index == audio_stream)
|
||||
if (packet.stream_index == audio_stream) {
|
||||
cmd = ffmpeg_send_packet(decoder, input,
|
||||
&packet, codec_context,
|
||||
av_stream,
|
||||
std::move(packet),
|
||||
*codec_context,
|
||||
*av_stream,
|
||||
frame,
|
||||
min_frame, audio_format.GetFrameSize(),
|
||||
&interleaved_buffer, &interleaved_buffer_size);
|
||||
else
|
||||
min_frame = 0;
|
||||
} else
|
||||
cmd = decoder_get_command(decoder);
|
||||
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(56, 25, 100)
|
||||
av_packet_unref(&packet);
|
||||
#else
|
||||
av_free_packet(&packet);
|
||||
#endif
|
||||
|
||||
if (cmd == DecoderCommand::SEEK) {
|
||||
int64_t where =
|
||||
@@ -583,11 +651,16 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
|
||||
av_stream->time_base) +
|
||||
start_time_fallback(*av_stream);
|
||||
|
||||
/* AVSEEK_FLAG_BACKWARD asks FFmpeg to seek to
|
||||
the packet boundary before the seek time
|
||||
stamp, not after */
|
||||
|
||||
if (av_seek_frame(format_context, audio_stream, where,
|
||||
AVSEEK_FLAG_ANY) < 0)
|
||||
AVSEEK_FLAG_ANY|AVSEEK_FLAG_BACKWARD) < 0)
|
||||
decoder_seek_error(decoder);
|
||||
else {
|
||||
avcodec_flush_buffers(codec_context);
|
||||
min_frame = decoder_seek_where_frame(decoder);
|
||||
decoder_command_finished(decoder);
|
||||
}
|
||||
}
|
||||
@@ -639,7 +712,7 @@ ffmpeg_scan_stream(InputStream &is,
|
||||
}
|
||||
|
||||
ffmpeg_scan_dictionary(f->metadata, handler, handler_ctx);
|
||||
int idx = ffmpeg_find_audio_stream(f);
|
||||
int idx = ffmpeg_find_audio_stream(*f);
|
||||
if (idx >= 0)
|
||||
ffmpeg_scan_dictionary(f->streams[idx]->metadata,
|
||||
handler, handler_ctx);
|
||||
@@ -668,7 +741,7 @@ static const char *const ffmpeg_suffixes[] = {
|
||||
"mve", "mvi", "mxf", "nc", "nsv", "nut", "nuv", "oga", "ogm", "ogv",
|
||||
"ogx", "oma", "ogg", "omg", "opus", "psp", "pva", "qcp", "qt", "r3d", "ra",
|
||||
"ram", "rl2", "rm", "rmvb", "roq", "rpl", "rvc", "shn", "smk", "snd",
|
||||
"sol", "son", "spx", "str", "swf", "tgi", "tgq", "tgv", "thp", "ts",
|
||||
"sol", "son", "spx", "str", "swf", "tak", "tgi", "tgq", "tgv", "thp", "ts",
|
||||
"tsp", "tta", "xa", "xvid", "uv", "uv2", "vb", "vid", "vob", "voc",
|
||||
"vp6", "vmd", "wav", "webm", "wma", "wmv", "wsaud", "wsvga", "wv",
|
||||
"wve",
|
||||
|
@@ -156,8 +156,11 @@ gme_file_decode(Decoder &decoder, Path path_fs)
|
||||
return;
|
||||
}
|
||||
|
||||
const SignedSongTime song_len = ti->length > 0
|
||||
? SignedSongTime::FromMS(ti->length)
|
||||
const int length = ti->play_length;
|
||||
gme_free_info(ti);
|
||||
|
||||
const SignedSongTime song_len = length > 0
|
||||
? SignedSongTime::FromMS(length)
|
||||
: SignedSongTime::Negative();
|
||||
|
||||
/* initialize the MPD decoder */
|
||||
@@ -168,7 +171,6 @@ gme_file_decode(Decoder &decoder, Path path_fs)
|
||||
SampleFormat::S16, GME_CHANNELS,
|
||||
error)) {
|
||||
LogError(error);
|
||||
gme_free_info(ti);
|
||||
gme_delete(emu);
|
||||
return;
|
||||
}
|
||||
@@ -179,8 +181,8 @@ gme_file_decode(Decoder &decoder, Path path_fs)
|
||||
if (gme_err != nullptr)
|
||||
LogWarning(gme_domain, gme_err);
|
||||
|
||||
if (ti->length > 0)
|
||||
gme_set_fade(emu, ti->length);
|
||||
if (length > 0)
|
||||
gme_set_fade(emu, length);
|
||||
|
||||
/* play */
|
||||
DecoderCommand cmd;
|
||||
@@ -196,16 +198,17 @@ gme_file_decode(Decoder &decoder, Path path_fs)
|
||||
if (cmd == DecoderCommand::SEEK) {
|
||||
unsigned where = decoder_seek_time(decoder).ToMS();
|
||||
gme_err = gme_seek(emu, where);
|
||||
if (gme_err != nullptr)
|
||||
if (gme_err != nullptr) {
|
||||
LogWarning(gme_domain, gme_err);
|
||||
decoder_command_finished(decoder);
|
||||
decoder_seek_error(decoder);
|
||||
} else
|
||||
decoder_command_finished(decoder);
|
||||
}
|
||||
|
||||
if (gme_track_ended(emu))
|
||||
break;
|
||||
} while (cmd != DecoderCommand::STOP);
|
||||
|
||||
gme_free_info(ti);
|
||||
gme_delete(emu);
|
||||
}
|
||||
|
||||
@@ -236,9 +239,9 @@ gme_scan_file(Path path_fs,
|
||||
|
||||
assert(ti != nullptr);
|
||||
|
||||
if (ti->length > 0)
|
||||
if (ti->play_length > 0)
|
||||
tag_handler_invoke_duration(handler, handler_ctx,
|
||||
SongTime::FromMS(ti->length));
|
||||
SongTime::FromMS(ti->play_length));
|
||||
|
||||
if (ti->song != nullptr) {
|
||||
if (gme_track_count(emu) > 1) {
|
||||
|
@@ -22,10 +22,12 @@
|
||||
#include "../DecoderAPI.hxx"
|
||||
#include "input/InputStream.hxx"
|
||||
#include "CheckAudioFormat.hxx"
|
||||
#include "pcm/Traits.hxx"
|
||||
#include "tag/TagHandler.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/Macros.hxx"
|
||||
#include "util/Clamp.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <mpc/mpcdec.h>
|
||||
@@ -42,6 +44,9 @@ struct mpc_decoder_data {
|
||||
|
||||
static constexpr Domain mpcdec_domain("mpcdec");
|
||||
|
||||
static constexpr SampleFormat mpcdec_sample_format = SampleFormat::S24_P32;
|
||||
typedef SampleTraits<mpcdec_sample_format> MpcdecSampleTraits;
|
||||
|
||||
static mpc_int32_t
|
||||
mpc_read_cb(mpc_reader *reader, void *ptr, mpc_int32_t size)
|
||||
{
|
||||
@@ -91,18 +96,15 @@ mpc_getsize_cb(mpc_reader *reader)
|
||||
}
|
||||
|
||||
/* this _looks_ performance-critical, don't de-inline -- eric */
|
||||
static inline int32_t
|
||||
static inline MpcdecSampleTraits::value_type
|
||||
mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
|
||||
{
|
||||
/* only doing 16-bit audio for now */
|
||||
int32_t val;
|
||||
MpcdecSampleTraits::value_type val;
|
||||
|
||||
enum {
|
||||
bits = 24,
|
||||
};
|
||||
|
||||
const int clip_min = -1 << (bits - 1);
|
||||
const int clip_max = (1 << (bits - 1)) - 1;
|
||||
constexpr int bits = MpcdecSampleTraits::BITS;
|
||||
constexpr auto clip_min = MpcdecSampleTraits::MIN;
|
||||
constexpr auto clip_max = MpcdecSampleTraits::MAX;
|
||||
|
||||
#ifdef MPC_FIXED_POINT
|
||||
const int shift = bits - MPC_FIXED_POINT_SCALE_SHIFT;
|
||||
@@ -117,16 +119,12 @@ mpc_to_mpd_sample(MPC_SAMPLE_FORMAT sample)
|
||||
val = sample * float_scale;
|
||||
#endif
|
||||
|
||||
if (val < clip_min)
|
||||
val = clip_min;
|
||||
else if (val > clip_max)
|
||||
val = clip_max;
|
||||
|
||||
return val;
|
||||
return Clamp(val, clip_min, clip_max);
|
||||
}
|
||||
|
||||
static void
|
||||
mpc_to_mpd_buffer(int32_t *dest, const MPC_SAMPLE_FORMAT *src,
|
||||
mpc_to_mpd_buffer(MpcdecSampleTraits::pointer_type dest,
|
||||
const MPC_SAMPLE_FORMAT *src,
|
||||
unsigned num_samples)
|
||||
{
|
||||
while (num_samples-- > 0)
|
||||
@@ -162,7 +160,7 @@ mpcdec_decode(Decoder &mpd_decoder, InputStream &is)
|
||||
Error error;
|
||||
AudioFormat audio_format;
|
||||
if (!audio_format_init_checked(audio_format, info.sample_freq,
|
||||
SampleFormat::S24_P32,
|
||||
mpcdec_sample_format,
|
||||
info.channels, error)) {
|
||||
LogError(error);
|
||||
mpc_demux_exit(demux);
|
||||
@@ -214,7 +212,7 @@ mpcdec_decode(Decoder &mpd_decoder, InputStream &is)
|
||||
mpc_uint32_t ret = frame.samples;
|
||||
ret *= info.channels;
|
||||
|
||||
int32_t chunk[ARRAY_SIZE(sample_buffer)];
|
||||
MpcdecSampleTraits::value_type chunk[ARRAY_SIZE(sample_buffer)];
|
||||
mpc_to_mpd_buffer(chunk, sample_buffer, ret);
|
||||
|
||||
long bit_rate = vbr_update_bits * audio_format.sample_rate
|
||||
|
@@ -441,13 +441,15 @@ mpd_opus_scan_stream(InputStream &is,
|
||||
if (!oy.ExpectFirstPage(os))
|
||||
return false;
|
||||
|
||||
/* read at most two more pages */
|
||||
unsigned remaining_pages = 2;
|
||||
/* read at most 64 more pages */
|
||||
unsigned remaining_pages = 64;
|
||||
|
||||
unsigned remaining_packets = 4;
|
||||
|
||||
bool result = false;
|
||||
|
||||
ogg_packet packet;
|
||||
while (true) {
|
||||
while (remaining_packets > 0) {
|
||||
int r = ogg_stream_packetout(&os, &packet);
|
||||
if (r < 0) {
|
||||
result = false;
|
||||
@@ -466,6 +468,8 @@ mpd_opus_scan_stream(InputStream &is,
|
||||
continue;
|
||||
}
|
||||
|
||||
--remaining_packets;
|
||||
|
||||
if (packet.b_o_s) {
|
||||
if (!IsOpusHead(packet))
|
||||
break;
|
||||
|
@@ -85,7 +85,7 @@ public:
|
||||
|
||||
char *ReadString() {
|
||||
uint32_t length;
|
||||
if (!ReadWord(length))
|
||||
if (!ReadWord(length) || length >= 65536)
|
||||
return nullptr;
|
||||
|
||||
const char *src = (const char *)Read(length);
|
||||
|
@@ -157,8 +157,6 @@ flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
|
||||
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
|
||||
unsigned bits_per_sample;
|
||||
|
||||
encoder->audio_format = audio_format;
|
||||
|
||||
/* FIXME: flac should support 32bit as well */
|
||||
switch (audio_format.format) {
|
||||
case SampleFormat::S8:
|
||||
@@ -178,6 +176,8 @@ flac_encoder_open(Encoder *_encoder, AudioFormat &audio_format, Error &error)
|
||||
audio_format.format = SampleFormat::S24_P32;
|
||||
}
|
||||
|
||||
encoder->audio_format = audio_format;
|
||||
|
||||
/* allocate the encoder */
|
||||
encoder->fse = FLAC__stream_encoder_new();
|
||||
if (encoder->fse == nullptr) {
|
||||
|
@@ -66,7 +66,7 @@ struct opus_encoder {
|
||||
|
||||
ogg_int64_t granulepos;
|
||||
|
||||
opus_encoder():encoder(opus_encoder_plugin) {}
|
||||
opus_encoder():encoder(opus_encoder_plugin), granulepos(0) {}
|
||||
};
|
||||
|
||||
static constexpr Domain opus_encoder_domain("opus_encoder");
|
||||
|
@@ -26,7 +26,6 @@
|
||||
#include "util/NumberParser.hxx"
|
||||
#include "util/DynamicFifoBuffer.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@@ -60,8 +59,6 @@ struct ShineEncoder {
|
||||
bool WriteChunk(bool flush);
|
||||
};
|
||||
|
||||
static constexpr Domain shine_encoder_domain("shine_encoder");
|
||||
|
||||
inline bool
|
||||
ShineEncoder::Configure(const config_param ¶m,
|
||||
gcc_unused Error &error)
|
||||
|
@@ -27,6 +27,8 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
static constexpr uint16_t WAVE_FORMAT_PCM = 1;
|
||||
|
||||
struct WaveEncoder {
|
||||
Encoder encoder;
|
||||
unsigned bits;
|
||||
@@ -64,15 +66,15 @@ fill_wave_header(struct wave_header *header, int channels, int bits,
|
||||
header->id_fmt = ToLE32(0x20746d66);
|
||||
header->id_data = ToLE32(0x61746164);
|
||||
|
||||
/* wave format */
|
||||
header->format = ToLE16(1); // PCM_FORMAT
|
||||
/* wave format */
|
||||
header->format = ToLE16(WAVE_FORMAT_PCM);
|
||||
header->channels = ToLE16(channels);
|
||||
header->bits = ToLE16(bits);
|
||||
header->freq = ToLE32(freq);
|
||||
header->blocksize = ToLE16(block_size);
|
||||
header->byterate = ToLE32(freq * block_size);
|
||||
|
||||
/* chunk sizes (fake data length) */
|
||||
/* chunk sizes (fake data length) */
|
||||
header->fmt_size = ToLE32(16);
|
||||
header->data_size = ToLE32(data_size);
|
||||
header->riff_size = ToLE32(4 + (8 + 16) + (8 + data_size));
|
||||
|
@@ -26,7 +26,6 @@
|
||||
#include "AudioFormat.hxx"
|
||||
#include "util/ConstBuffer.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
@@ -50,8 +49,6 @@ public:
|
||||
Error &error) override;
|
||||
};
|
||||
|
||||
static constexpr Domain volume_domain("pcm_volume");
|
||||
|
||||
static Filter *
|
||||
volume_filter_init(gcc_unused const config_param ¶m,
|
||||
gcc_unused Error &error)
|
||||
|
@@ -453,6 +453,8 @@ CurlInputStream::RequestDone(CURLcode result, long status)
|
||||
SeekDone();
|
||||
else if (!IsReady())
|
||||
SetReady();
|
||||
else
|
||||
cond.broadcast();
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -132,6 +132,7 @@ SmbclientInputStream::Read(void *ptr, size_t read_size, Error &error)
|
||||
nbytes = 0;
|
||||
}
|
||||
|
||||
offset += nbytes;
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
|
@@ -25,13 +25,10 @@
|
||||
#include "output/Internal.hxx"
|
||||
#include "pcm/Volume.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
static constexpr Domain mixer_domain("mixer");
|
||||
|
||||
static int
|
||||
output_mixer_get_volume(const AudioOutput &ao)
|
||||
{
|
||||
|
@@ -218,7 +218,7 @@ PulseMixer::SetVolume(unsigned new_volume, Error &error)
|
||||
|
||||
struct pa_cvolume cvolume;
|
||||
pa_cvolume_set(&cvolume, volume.channels,
|
||||
(pa_volume_t)new_volume * PA_VOLUME_NORM / 100 + 0.5);
|
||||
(new_volume * PA_VOLUME_NORM + 50) / 100);
|
||||
bool success = pulse_output_set_volume(output, &cvolume, error);
|
||||
if (success)
|
||||
volume = cvolume;
|
||||
|
@@ -28,7 +28,7 @@ struct notify {
|
||||
Cond cond;
|
||||
bool pending;
|
||||
|
||||
#if !defined(WIN32) && !defined(__NetBSD__) && !defined(__BIONIC__)
|
||||
#ifdef __GLIBC__
|
||||
constexpr
|
||||
#endif
|
||||
notify():pending(false) {}
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include "Internal.hxx"
|
||||
#include "PlayerControl.hxx"
|
||||
#include "mixer/MixerControl.hxx"
|
||||
#include "mixer/Volume.hxx"
|
||||
#include "Idle.hxx"
|
||||
|
||||
extern unsigned audio_output_state_version;
|
||||
@@ -47,6 +48,11 @@ audio_output_enable_index(MultipleOutputs &outputs, unsigned idx)
|
||||
ao.enabled = true;
|
||||
idle_add(IDLE_OUTPUT);
|
||||
|
||||
if (ao.mixer != nullptr) {
|
||||
InvalidateHardwareVolume();
|
||||
idle_add(IDLE_MIXER);
|
||||
}
|
||||
|
||||
ao.player_control->UpdateAudio();
|
||||
|
||||
++audio_output_state_version;
|
||||
@@ -70,6 +76,7 @@ audio_output_disable_index(MultipleOutputs &outputs, unsigned idx)
|
||||
Mixer *mixer = ao.mixer;
|
||||
if (mixer != nullptr) {
|
||||
mixer_close(mixer);
|
||||
InvalidateHardwareVolume();
|
||||
idle_add(IDLE_MIXER);
|
||||
}
|
||||
|
||||
@@ -94,6 +101,7 @@ audio_output_toggle_index(MultipleOutputs &outputs, unsigned idx)
|
||||
Mixer *mixer = ao.mixer;
|
||||
if (mixer != nullptr) {
|
||||
mixer_close(mixer);
|
||||
InvalidateHardwareVolume();
|
||||
idle_add(IDLE_MIXER);
|
||||
}
|
||||
}
|
||||
|
@@ -184,7 +184,8 @@ AudioOutput::LockUpdate(const AudioFormat audio_format,
|
||||
const ScopeLock protect(mutex);
|
||||
|
||||
if (enabled && really_enabled) {
|
||||
if (fail_timer.Check(REOPEN_AFTER * 1000)) {
|
||||
if (!fail_timer.IsDefined() ||
|
||||
fail_timer.Check(REOPEN_AFTER * 1000)) {
|
||||
return Open(audio_format, mp);
|
||||
}
|
||||
} else if (IsOpen())
|
||||
|
@@ -22,7 +22,6 @@
|
||||
#include "../OutputAPI.hxx"
|
||||
#include "config/ConfigError.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -44,8 +43,6 @@ struct PipeOutput {
|
||||
bool Configure(const config_param ¶m, Error &error);
|
||||
};
|
||||
|
||||
static constexpr Domain pipe_output_domain("pipe_output");
|
||||
|
||||
inline bool
|
||||
PipeOutput::Configure(const config_param ¶m, Error &error)
|
||||
{
|
||||
|
@@ -25,14 +25,11 @@
|
||||
#include "input/InputStream.hxx"
|
||||
#include "tag/TagBuilder.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "lib/expat/ExpatParser.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static constexpr Domain xspf_domain("xspf");
|
||||
|
||||
/**
|
||||
* This is the state object for the GLib XML parser.
|
||||
*/
|
||||
|
@@ -92,7 +92,7 @@ check_range(Client &client, unsigned *value_r1, unsigned *value_r2,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unsigned(value) > std::numeric_limits<unsigned>::max()) {
|
||||
if (value > std::numeric_limits<int>::max()) {
|
||||
command_error(client, ACK_ERROR_ARG,
|
||||
"Number too large: %s", s);
|
||||
return false;
|
||||
@@ -117,7 +117,7 @@ check_range(Client &client, unsigned *value_r1, unsigned *value_r2,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (unsigned(value) > std::numeric_limits<unsigned>::max()) {
|
||||
if (value > std::numeric_limits<int>::max()) {
|
||||
command_error(client, ACK_ERROR_ARG,
|
||||
"Number too large: %s", s);
|
||||
return false;
|
||||
|
@@ -37,7 +37,7 @@ playlist::TagModified(DetachedSong &&song)
|
||||
|
||||
DetachedSong ¤t_song = queue.GetOrder(current);
|
||||
if (song.IsSame(current_song))
|
||||
current_song.MoveTagFrom(std::move(song));
|
||||
current_song.MoveTagItemsFrom(std::move(song));
|
||||
|
||||
queue.ModifyAtOrder(current);
|
||||
queue.IncrementVersion();
|
||||
|
@@ -177,6 +177,8 @@ private:
|
||||
mutex.unlock();
|
||||
DeferredMonitor::Schedule();
|
||||
mutex.lock();
|
||||
if (state == State::INITIAL)
|
||||
cond.wait(mutex);
|
||||
break;
|
||||
|
||||
case State::CONNECTING:
|
||||
@@ -188,8 +190,6 @@ private:
|
||||
error.Set(last_error);
|
||||
return false;
|
||||
}
|
||||
|
||||
cond.wait(mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -63,7 +63,7 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
bool IsDefined() const {
|
||||
constexpr bool IsDefined() const {
|
||||
return last != 0;
|
||||
}
|
||||
|
||||
|
@@ -84,14 +84,14 @@ aiff_seek_id3(FILE *file)
|
||||
underflow when casting to off_t */
|
||||
return 0;
|
||||
|
||||
if (size % 2 != 0)
|
||||
/* pad byte */
|
||||
++size;
|
||||
|
||||
if (memcmp(chunk.id, "ID3 ", 4) == 0)
|
||||
/* found it! */
|
||||
return size;
|
||||
|
||||
if (size % 2 != 0)
|
||||
/* pad byte */
|
||||
++size;
|
||||
|
||||
if (fseek(file, size, SEEK_CUR) != 0)
|
||||
return 0;
|
||||
}
|
||||
|
@@ -78,12 +78,12 @@ ape_scan_internal(FILE *fp, ApeTagCallback callback)
|
||||
|
||||
/* get the key */
|
||||
const char *key = p;
|
||||
while (remaining > size && *p != '\0') {
|
||||
p++;
|
||||
remaining--;
|
||||
}
|
||||
p++;
|
||||
remaining--;
|
||||
const char *key_end = (const char *)memchr(p, '\0', remaining);
|
||||
if (key_end == nullptr)
|
||||
break;
|
||||
|
||||
p = key_end + 1;
|
||||
remaining -= p - key;
|
||||
|
||||
/* get the value */
|
||||
if (remaining < size)
|
||||
|
@@ -82,15 +82,15 @@ riff_seek_id3(FILE *file)
|
||||
underflow when casting to off_t */
|
||||
return 0;
|
||||
|
||||
if (size % 2 != 0)
|
||||
/* pad byte */
|
||||
++size;
|
||||
|
||||
if (memcmp(chunk.id, "id3 ", 4) == 0 ||
|
||||
memcmp(chunk.id, "ID3 ", 4) == 0)
|
||||
/* found it! */
|
||||
return size;
|
||||
|
||||
if (size % 2 != 0)
|
||||
/* pad byte */
|
||||
++size;
|
||||
|
||||
if (fseek(file, size, SEEK_CUR) != 0)
|
||||
return 0;
|
||||
}
|
||||
|
@@ -80,9 +80,17 @@ struct Tag {
|
||||
Tag &operator=(Tag &&other) {
|
||||
duration = other.duration;
|
||||
has_playlist = other.has_playlist;
|
||||
MoveItemsFrom(std::move(other));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to the move operator, but move only the #TagItem
|
||||
* array.
|
||||
*/
|
||||
void MoveItemsFrom(Tag &&other) {
|
||||
std::swap(items, other.items);
|
||||
std::swap(num_items, other.num_items);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -23,19 +23,23 @@
|
||||
#include "util/Cast.hxx"
|
||||
#include "util/VarSize.hxx"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
Mutex tag_pool_lock;
|
||||
|
||||
static constexpr size_t NUM_SLOTS = 4096;
|
||||
static constexpr size_t NUM_SLOTS = 4093;
|
||||
|
||||
struct TagPoolSlot {
|
||||
TagPoolSlot *next;
|
||||
unsigned char ref;
|
||||
TagItem item;
|
||||
|
||||
static constexpr unsigned MAX_REF = std::numeric_limits<decltype(ref)>::max();
|
||||
|
||||
TagPoolSlot(TagPoolSlot *_next, TagType type,
|
||||
const char *value, size_t length)
|
||||
:next(_next), ref(1) {
|
||||
@@ -116,7 +120,7 @@ tag_pool_get_item(TagType type, const char *value, size_t length)
|
||||
if (slot->item.type == type &&
|
||||
length == strlen(slot->item.value) &&
|
||||
memcmp(value, slot->item.value, length) == 0 &&
|
||||
slot->ref < 0xff) {
|
||||
slot->ref < TagPoolSlot::MAX_REF) {
|
||||
assert(slot->ref > 0);
|
||||
++slot->ref;
|
||||
return &slot->item;
|
||||
@@ -135,19 +139,15 @@ tag_pool_dup_item(TagItem *item)
|
||||
|
||||
assert(slot->ref > 0);
|
||||
|
||||
if (slot->ref < 0xff) {
|
||||
if (slot->ref < TagPoolSlot::MAX_REF) {
|
||||
++slot->ref;
|
||||
return item;
|
||||
} else {
|
||||
/* the reference counter overflows above 0xff;
|
||||
duplicate the item, and start with 1 */
|
||||
/* the reference counter overflows above MAX_REF;
|
||||
obtain a reference to a different TagPoolSlot which
|
||||
isn't yet "full" */
|
||||
size_t length = strlen(item->value);
|
||||
auto slot_p = tag_value_slot_p(item->type,
|
||||
item->value, length);
|
||||
slot = TagPoolSlot::Create(*slot_p, item->type,
|
||||
item->value, strlen(item->value));
|
||||
*slot_p = slot;
|
||||
return &slot->item;
|
||||
return tag_pool_get_item(item->type, item->value, length);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -40,9 +40,9 @@ FindInvalidUTF8(const char *p, const char *const end)
|
||||
/* now call the other SequenceLengthUTF8() overload
|
||||
which also validates the continuations */
|
||||
const size_t t = SequenceLengthUTF8(p);
|
||||
assert(s == t);
|
||||
if (t == 0)
|
||||
return p;
|
||||
assert(s == t);
|
||||
|
||||
p += s;
|
||||
}
|
||||
|
@@ -41,9 +41,13 @@ class PosixCond {
|
||||
pthread_cond_t cond;
|
||||
|
||||
public:
|
||||
#if defined(__NetBSD__) || defined(__BIONIC__)
|
||||
/* NetBSD's PTHREAD_COND_INITIALIZER is not compatible with
|
||||
"constexpr" */
|
||||
#ifdef __GLIBC__
|
||||
/* optimized constexpr constructor for pthread implementations
|
||||
that support it */
|
||||
constexpr PosixCond():cond(PTHREAD_COND_INITIALIZER) {}
|
||||
#else
|
||||
/* slow fallback for pthread implementations that are not
|
||||
compatible with "constexpr" */
|
||||
PosixCond() {
|
||||
pthread_cond_init(&cond, nullptr);
|
||||
}
|
||||
@@ -51,10 +55,6 @@ public:
|
||||
~PosixCond() {
|
||||
pthread_cond_destroy(&cond);
|
||||
}
|
||||
#else
|
||||
/* optimized constexpr constructor for sane POSIX
|
||||
implementations */
|
||||
constexpr PosixCond():cond(PTHREAD_COND_INITIALIZER) {}
|
||||
#endif
|
||||
|
||||
PosixCond(const PosixCond &other) = delete;
|
||||
|
@@ -41,9 +41,13 @@ class PosixMutex {
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
public:
|
||||
#if defined(__NetBSD__) || defined(__BIONIC__)
|
||||
/* NetBSD's PTHREAD_MUTEX_INITIALIZER is not compatible with
|
||||
"constexpr" */
|
||||
#ifdef __GLIBC__
|
||||
/* optimized constexpr constructor for pthread implementations
|
||||
that support it */
|
||||
constexpr PosixMutex():mutex(PTHREAD_MUTEX_INITIALIZER) {}
|
||||
#else
|
||||
/* slow fallback for pthread implementations that are not
|
||||
compatible with "constexpr" */
|
||||
PosixMutex() {
|
||||
pthread_mutex_init(&mutex, nullptr);
|
||||
}
|
||||
@@ -51,10 +55,6 @@ public:
|
||||
~PosixMutex() {
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
#else
|
||||
/* optimized constexpr constructor for sane POSIX
|
||||
implementations */
|
||||
constexpr PosixMutex():mutex(PTHREAD_MUTEX_INITIALIZER) {}
|
||||
#endif
|
||||
|
||||
PosixMutex(const PosixMutex &other) = delete;
|
||||
|
@@ -22,7 +22,6 @@
|
||||
#include "system/FatalError.hxx"
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "fs/FileSystem.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "PidFile.hxx"
|
||||
#include "Log.hxx"
|
||||
|
||||
@@ -37,8 +36,6 @@
|
||||
#include <grp.h>
|
||||
#endif
|
||||
|
||||
static constexpr Domain daemon_domain("daemon");
|
||||
|
||||
#ifndef WIN32
|
||||
|
||||
/** the Unix user name which MPD runs as */
|
||||
@@ -113,7 +110,7 @@ daemonize_set_user(void)
|
||||
(int)user_gid);
|
||||
}
|
||||
|
||||
#ifdef _BSD_SOURCE
|
||||
#ifdef HAVE_INITGROUPS
|
||||
/* init supplementary groups
|
||||
* (must be done before we change our uid)
|
||||
*/
|
||||
|
@@ -46,7 +46,7 @@ static size_t
|
||||
AlignToPageSize(size_t size)
|
||||
{
|
||||
static const long page_size = sysconf(_SC_PAGESIZE);
|
||||
if (page_size > 0)
|
||||
if (page_size == 0)
|
||||
return size;
|
||||
|
||||
size_t ps(page_size);
|
||||
|
@@ -7,16 +7,7 @@ ExecStart=@prefix@/bin/mpd --no-daemon
|
||||
|
||||
# allow MPD to use real-time priority 50
|
||||
LimitRTPRIO=50
|
||||
LimitRTTIME=-1
|
||||
|
||||
# move MPD to a top-level cgroup, as real-time budget assignment fails
|
||||
# in cgroup /system/mpd.service, because /system has a zero real-time
|
||||
# budget; see
|
||||
# http://www.freedesktop.org/wiki/Software/systemd/MyServiceCantGetRealtime/
|
||||
ControlGroup=cpu:/mpd
|
||||
|
||||
# assign a real-time budget
|
||||
ControlGroupAttribute=cpu.rt_runtime_us 500000
|
||||
LimitRTTIME=infinity
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
@@ -49,9 +49,9 @@ CPPUNIT_TEST_SUITE_REGISTRATION(ByteReverseTest);
|
||||
void
|
||||
ByteReverseTest::TestByteReverse2()
|
||||
{
|
||||
static const char src[] = "123456";
|
||||
static const char src[] gcc_alignas(uint16_t, 2) = "123456";
|
||||
static const char result[] = "214365";
|
||||
static uint8_t dest[ARRAY_SIZE(src)];
|
||||
static uint8_t dest[ARRAY_SIZE(src)] gcc_alignas(uint16_t, 2);
|
||||
|
||||
reverse_bytes(dest, (const uint8_t *)src,
|
||||
(const uint8_t *)(src + ARRAY_SIZE(src) - 1), 2);
|
||||
@@ -73,9 +73,9 @@ ByteReverseTest::TestByteReverse3()
|
||||
void
|
||||
ByteReverseTest::TestByteReverse4()
|
||||
{
|
||||
static const char src[] = "12345678";
|
||||
static const char src[] gcc_alignas(uint32_t, 4) = "12345678";
|
||||
static const char result[] = "43218765";
|
||||
static uint8_t dest[ARRAY_SIZE(src)];
|
||||
static uint8_t dest[ARRAY_SIZE(src)] gcc_alignas(uint32_t, 4);
|
||||
|
||||
reverse_bytes(dest, (const uint8_t *)src,
|
||||
(const uint8_t *)(src + ARRAY_SIZE(src) - 1), 4);
|
||||
|
Reference in New Issue
Block a user