Compare commits
57 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e888c9e827 | ||
![]() |
2556449b36 | ||
![]() |
446f9973cc | ||
![]() |
596f36bb78 | ||
![]() |
e7abdab58d | ||
![]() |
13cdc9a9f8 | ||
![]() |
a1b8806422 | ||
![]() |
e635d47912 | ||
![]() |
53ac72a878 | ||
![]() |
2be6184c8d | ||
![]() |
8b0b4ff086 | ||
![]() |
60f7ff3de5 | ||
![]() |
e76c752987 | ||
![]() |
042c1abc6e | ||
![]() |
3d12d7de62 | ||
![]() |
87593f95d4 | ||
![]() |
11626e48bf | ||
![]() |
b3df4dc2c9 | ||
![]() |
3db9ab82ea | ||
![]() |
2dc3acc5f0 | ||
![]() |
25686e5bce | ||
![]() |
8d70f808d9 | ||
![]() |
7c887af1ea | ||
![]() |
b7f435b50e | ||
![]() |
d3b15f8fda | ||
![]() |
838f7cd210 | ||
![]() |
13539961b2 | ||
![]() |
a26f2ef17d | ||
![]() |
d97c46bcdc | ||
![]() |
2b6542467c | ||
![]() |
8fa51faa38 | ||
![]() |
b2175629fd | ||
![]() |
2e28ed8f81 | ||
![]() |
4c4f8bf02a | ||
![]() |
e464be5f39 | ||
![]() |
d7d717f2ce | ||
![]() |
d1eeed6a5b | ||
![]() |
736fd0e293 | ||
![]() |
6592ca9f88 | ||
![]() |
762712c756 | ||
![]() |
73f9e17951 | ||
![]() |
7d6a605a85 | ||
![]() |
a6a8bdffc3 | ||
![]() |
296085ff23 | ||
![]() |
36aa8ce3c9 | ||
![]() |
c49c69d6ea | ||
![]() |
d5684f7444 | ||
![]() |
affb4bd923 | ||
![]() |
65772a74e0 | ||
![]() |
cca2c2f4ca | ||
![]() |
52e2fa91c4 | ||
![]() |
dca405a746 | ||
![]() |
3680a6bbbb | ||
![]() |
6aa6a9c272 | ||
![]() |
8d1c7ca206 | ||
![]() |
52b8e0f9ec | ||
![]() |
3c4f4793b5 |
Makefile.amNEWSconfigure.ac
doc
m4
scripts
src
ape.cconf.cdatabase.c
decoder
ffmpeg_decoder_plugin.cflac_metadata.cflac_metadata.hmpcdec_decoder_plugin.cwavpack_decoder_plugin.c
encoder
encoder_plugin.hinput
output
alsa_plugin.cao_plugin.chttpd_client.chttpd_output_plugin.cosx_plugin.cpulse_output_plugin.crecorder_output_plugin.cshout_plugin.c
output_all.coutput_control.coutput_control.houtput_init.coutput_internal.houtput_thread.cpipe.cpipe.hplayer_thread.cplaylist_control.csticker.cupdate_walk.ctest
@@ -11,6 +11,7 @@ noinst_LIBRARIES =
|
||||
|
||||
src_mpd_CFLAGS = $(AM_CFLAGS) $(MPD_CFLAGS)
|
||||
src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \
|
||||
$(AVAHI_CFLAGS) \
|
||||
$(LIBWRAP_CFLAGS) \
|
||||
$(SQLITE_CFLAGS) \
|
||||
$(ARCHIVE_CFLAGS) \
|
||||
@@ -21,6 +22,7 @@ src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \
|
||||
$(FILTER_CFLAGS) \
|
||||
$(OUTPUT_CFLAGS)
|
||||
src_mpd_LDADD = $(MPD_LIBS) \
|
||||
$(AVAHI_LIBS) \
|
||||
$(LIBWRAP_LDFLAGS) \
|
||||
$(SQLITE_LIBS) \
|
||||
$(ARCHIVE_LIBS) \
|
||||
|
24
NEWS
24
NEWS
@@ -1,3 +1,27 @@
|
||||
ver 0.16.4 (2011/09/01)
|
||||
* don't abort configure when avahi is not found
|
||||
* auto-detect libmad without pkg-config
|
||||
* fix memory leaks
|
||||
* don't resume playback when seeking to another song while paused
|
||||
* apply follow_inside_symlinks to absolute symlinks
|
||||
* fix playback discontinuation after seeking
|
||||
* input:
|
||||
- curl: limit the receive buffer size
|
||||
- curl: implement a hard-coded timeout of 10 seconds
|
||||
* decoder:
|
||||
- ffmpeg: workaround for semantic API change in recent ffmpeg versions
|
||||
- flac: validate the sample rate when scanning the tag
|
||||
- wavpack: obey all decoder commands, stop at CUE track border
|
||||
* encoder:
|
||||
- vorbis: don't send end-of-stream on flush
|
||||
* output:
|
||||
- alsa: fix SIGFPE when alsa announces a period size of 0
|
||||
- httpd: don't warn on client disconnect
|
||||
- osx: don't drain the buffer when closing
|
||||
- pulse: fix deadlock when resuming the stream
|
||||
- pulse: fix deadlock when the stream was suspended
|
||||
|
||||
|
||||
ver 0.16.3 (2011/06/04)
|
||||
* fix assertion failure in audio format mask parser
|
||||
* fix NULL pointer dereference in playlist parser
|
||||
|
115
configure.ac
115
configure.ac
@@ -1,5 +1,5 @@
|
||||
AC_PREREQ(2.60)
|
||||
AC_INIT(mpd, 0.16.3, musicpd-dev-team@lists.sourceforge.net)
|
||||
AC_INIT(mpd, 0.16.4, musicpd-dev-team@lists.sourceforge.net)
|
||||
AC_CONFIG_SRCDIR([src/main.c])
|
||||
AM_INIT_AUTOMAKE([foreign 1.10 dist-bzip2 subdir-objects])
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
@@ -171,9 +171,9 @@ AC_ARG_ENABLE(fifo,
|
||||
enable_fifo=yes)
|
||||
|
||||
AC_ARG_ENABLE(flac,
|
||||
AS_HELP_STRING([--disable-flac],
|
||||
[disable flac support (default: enable)]),,
|
||||
enable_flac=yes)
|
||||
AS_HELP_STRING([--enable-flac],
|
||||
[enable FLAC decoder]),,
|
||||
enable_flac=auto)
|
||||
|
||||
AC_ARG_ENABLE(fluidsynth,
|
||||
AS_HELP_STRING([--enable-fluidsynth],
|
||||
@@ -196,9 +196,9 @@ AC_ARG_ENABLE(httpd-output,
|
||||
[enable_httpd_output=auto])
|
||||
|
||||
AC_ARG_ENABLE(id3,
|
||||
AS_HELP_STRING([--disable-id3],
|
||||
[disable id3 support (default: enable)]),,
|
||||
enable_id3=yes)
|
||||
AS_HELP_STRING([--enable-id3],
|
||||
[disable id3 support]),,
|
||||
enable_id3=auto)
|
||||
|
||||
AC_ARG_ENABLE(inotify,
|
||||
AS_HELP_STRING([--disable-inotify],
|
||||
@@ -353,9 +353,9 @@ AC_ARG_ENABLE(un,
|
||||
[enable_un=yes])
|
||||
|
||||
AC_ARG_ENABLE(vorbis,
|
||||
AS_HELP_STRING([--disable-vorbis],
|
||||
[disable Ogg Vorbis support (default: enable)]),,
|
||||
enable_vorbis=yes)
|
||||
AS_HELP_STRING([--enable-vorbis],
|
||||
[enable Ogg Vorbis decoder]),,
|
||||
enable_vorbis=auto)
|
||||
|
||||
AC_ARG_ENABLE(vorbis-encoder,
|
||||
AS_HELP_STRING([--enable-vorbis-encoder],
|
||||
@@ -510,13 +510,8 @@ fi
|
||||
AM_CONDITIONAL(HAVE_CUE, test x$enable_cue = xyes)
|
||||
|
||||
dnl -------------------------------- libid3tag --------------------------------
|
||||
if test x$enable_id3 = xyes; then
|
||||
PKG_CHECK_MODULES([ID3TAG], [id3tag],,
|
||||
AC_CHECK_LIB(id3tag, id3_file_open,
|
||||
[ID3TAG_LIBS="-lid3tag -lz" ID3TAG_CFLAGS=""],
|
||||
enable_id3=no))
|
||||
fi
|
||||
|
||||
MPD_AUTO_PKG_LIB(id3, ID3TAG, id3tag, id3tag, id3_file_open, [-lid3tag -lz], [],
|
||||
[id3tag], [libid3tag not found])
|
||||
if test x$enable_id3 = xyes; then
|
||||
AC_DEFINE(HAVE_ID3TAG, 1, [Define to use id3tag])
|
||||
fi
|
||||
@@ -530,36 +525,39 @@ dnl ---------------------------------------------------------------------------
|
||||
dnl --------------------------------- zeroconf --------------------------------
|
||||
|
||||
case $with_zeroconf in
|
||||
no|avahi|bonjour)
|
||||
no|bonjour)
|
||||
enable_avahi=no
|
||||
;;
|
||||
|
||||
avahi)
|
||||
enable_avahi=yes
|
||||
;;
|
||||
|
||||
*)
|
||||
with_zeroconf=auto
|
||||
enable_avahi=auto
|
||||
;;
|
||||
esac
|
||||
|
||||
MPD_AUTO_PKG(avahi, AVAHI, [avahi-client avahi-glib],
|
||||
[avahi client library], [avahi client+glib not found])
|
||||
if test x$enable_avahi = xyes; then
|
||||
AC_DEFINE([HAVE_AVAHI], 1, [Define to enable Avahi Zeroconf support])
|
||||
with_zeroconf=avahi
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_AVAHI, test x$enable_avahi = xyes)
|
||||
|
||||
enable_bounjour=no
|
||||
if test x$with_zeroconf != xno; then
|
||||
if test x$with_zeroconf = xavahi || test x$with_zeroconf = xauto; then
|
||||
PKG_CHECK_MODULES([AVAHI], [avahi-client avahi-glib],
|
||||
[found_avahi=1;AC_DEFINE([HAVE_AVAHI], 1, [Define to enable Avahi Zeroconf support])]
|
||||
MPD_LIBS="$MPD_LIBS $AVAHI_LIBS" MPD_CFLAGS="$MPD_CFLAGS $AVAHI_CFLAGS",
|
||||
[found_avahi=0])
|
||||
fi
|
||||
|
||||
if test x$found_avahi = x1; then
|
||||
with_zeroconf=avahi
|
||||
elif test x$with_zeroconf = xavahi; then
|
||||
AC_MSG_ERROR([Avahi support requested but not found])
|
||||
fi
|
||||
|
||||
if test x$with_zeroconf = xbonjour || test x$with_zeroconf = xauto; then
|
||||
AC_CHECK_HEADER(dns_sd.h,
|
||||
[found_bonjour=1;AC_DEFINE([HAVE_BONJOUR], 1, [Define to enable Bonjour Zeroconf support])],
|
||||
[found_bonjour=0])
|
||||
[enable_bonjour=yes;AC_DEFINE([HAVE_BONJOUR], 1, [Define to enable Bonjour Zeroconf support])])
|
||||
AC_CHECK_LIB(dns_sd, DNSServiceRegister,
|
||||
MPD_LIBS="$MPD_LIBS -ldns_sd")
|
||||
fi
|
||||
|
||||
if test x$found_bonjour = x1; then
|
||||
if test x$enable_bonjour = xyes; then
|
||||
with_zeroconf=bonjour
|
||||
elif test x$with_zeroconf = xbonjour; then
|
||||
AC_MSG_ERROR([Bonjour support requested but not found])
|
||||
@@ -574,7 +572,6 @@ if test x$with_zeroconf != xno; then
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(HAVE_ZEROCONF, test x$with_zeroconf != xno)
|
||||
AM_CONDITIONAL(HAVE_AVAHI, test x$with_zeroconf = xavahi)
|
||||
AM_CONDITIONAL(HAVE_BONJOUR, test x$with_zeroconf = xbonjour)
|
||||
|
||||
dnl ---------------------------------------------------------------------------
|
||||
@@ -634,11 +631,6 @@ if test x$enable_lastfm = xyes; then
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_LASTFM, test x$enable_lastfm = xyes)
|
||||
|
||||
dnl ---------------------------------- libogg ---------------------------------
|
||||
if test x$with_tremor = xno || test -z $with_tremor; then
|
||||
PKG_CHECK_MODULES(OGG, [ogg], enable_ogg=yes, enable_ogg=no)
|
||||
fi
|
||||
|
||||
dnl ---------------------------------- libmms ---------------------------------
|
||||
MPD_AUTO_PKG(mms, MMS, [libmms >= 0.4],
|
||||
[libmms mms:// protocol support], [libmms not found])
|
||||
@@ -753,10 +745,12 @@ fi
|
||||
AM_CONDITIONAL(HAVE_FFMPEG, test x$enable_ffmpeg = xyes)
|
||||
|
||||
dnl ----------------------------------- FLAC ----------------------------------
|
||||
|
||||
MPD_AUTO_PKG(flac, FLAC, [flac >= 1.1],
|
||||
[FLAC decoder], [libFLAC not found])
|
||||
|
||||
if test x$enable_flac = xyes; then
|
||||
PKG_CHECK_MODULES(FLAC, [flac >= 1.1],
|
||||
AC_DEFINE(HAVE_FLAC, 1, [Define for FLAC support]),
|
||||
enable_flac=no)
|
||||
AC_DEFINE(HAVE_FLAC, 1, [Define for FLAC support])
|
||||
|
||||
oldcflags="$CFLAGS"
|
||||
oldlibs="$LIBS"
|
||||
@@ -771,12 +765,10 @@ if test x$enable_flac = xyes; then
|
||||
LIBS="$oldlibs"
|
||||
|
||||
if test x$enable_oggflac = xflac; then
|
||||
if test x$enable_ogg = xyes; then
|
||||
FLAC_LIBS="${FLAC_LIBS} -logg"
|
||||
else
|
||||
enable_oggflac=yes
|
||||
AC_MSG_WARN("FLAC has the ogg API built in, but couldn't find ogg. Disabling oggflac.")
|
||||
fi
|
||||
PKG_CHECK_MODULES(OGG, [ogg],
|
||||
[FLAC_LIBS="${FLAC_LIBS} ${OGG_LIBS}" FLAC_CFLAGS="${FLAC_CFLAGS} ${OGG_CFLAGS}"],
|
||||
[enable_oggflac=yes;
|
||||
AC_MSG_WARN("FLAC has the ogg API built in, but couldn't find ogg. Disabling oggflac.")])
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -802,7 +794,8 @@ if test x$enable_gme = xyes; then
|
||||
fi
|
||||
|
||||
dnl ---------------------------------- libmad ---------------------------------
|
||||
MPD_AUTO_PKG(mad, MAD, [mad],
|
||||
MPD_AUTO_PKG_LIB(mad, MAD, [mad],
|
||||
mad, mad_stream_init, [-lmad], [],
|
||||
[libmad MP3 decoder plugin], [libmad not found])
|
||||
if test x$enable_mad = xyes; then
|
||||
AC_DEFINE(HAVE_MAD, 1, [Define to use libmad])
|
||||
@@ -943,7 +936,7 @@ fi
|
||||
if test x$enable_tremor = xyes; then
|
||||
AC_DEFINE(HAVE_TREMOR,1,
|
||||
[Define to use tremor (libvorbisidec) for ogg support])
|
||||
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support]),
|
||||
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support])
|
||||
else
|
||||
TREMOR_CFLAGS=
|
||||
TREMOR_LIBS=
|
||||
@@ -972,18 +965,18 @@ fi
|
||||
AM_CONDITIONAL(HAVE_OGGFLAC, test x$enable_oggflac = xyes)
|
||||
|
||||
dnl -------------------------------- Ogg Vorbis -------------------------------
|
||||
if test x$enable_vorbis = xyes; then
|
||||
if test x$enable_tremor = xyes; then
|
||||
|
||||
if test x$enable_tremor = xyes; then
|
||||
if test x$enable_vorbis = xyes; then
|
||||
AC_MSG_WARN(["OggTremor detected, could not enable Vorbis."])
|
||||
enable_vorbis=no
|
||||
elif test x$enable_ogg = xyes; then
|
||||
PKG_CHECK_MODULES(VORBIS, [vorbis vorbisfile],
|
||||
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support]),
|
||||
enable_vorbis=no)
|
||||
else
|
||||
AC_MSG_WARN(["Ogg not detected, could not enable Vorbis."])
|
||||
enable_vorbis=no
|
||||
fi
|
||||
enable_vorbis=no
|
||||
fi
|
||||
|
||||
MPD_AUTO_PKG(vorbis, VORBIS, [vorbis vorbisfile ogg],
|
||||
[Ogg Vorbis decoder], [libvorbis not found])
|
||||
if test x$enable_vorbis = xyes; then
|
||||
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enable_tremor = xyes)
|
||||
|
@@ -57,7 +57,7 @@
|
||||
Some example code:
|
||||
</para>
|
||||
|
||||
<programlisting lang="C">static inline bool
|
||||
<programlisting lang="C">static inline int
|
||||
foo(const char *abc, int xyz)
|
||||
{
|
||||
if (abc == NULL) {
|
||||
|
134
doc/protocol.xml
134
doc/protocol.xml
@@ -8,18 +8,54 @@
|
||||
<title>General protocol syntax</title>
|
||||
|
||||
<section>
|
||||
<title>Requests</title>
|
||||
<title>Protocol overview</title>
|
||||
|
||||
<para>
|
||||
If arguments contain spaces, they should be surrounded by double quotation
|
||||
marks.
|
||||
The MPD command protocol exchanges line-based text records
|
||||
between client and server over TCP. Once the client is
|
||||
connected to the server, they conduct a conversation until the
|
||||
client closes the connection. The conversation flow is always
|
||||
initiated by the client.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The client transmits a command sequence, terminated by the
|
||||
newline character <constant>\n</constant>. The server will
|
||||
respond with one or more lines, the last of which will be a
|
||||
completion code.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When the client connects to the server, the server will answer
|
||||
with the following line:
|
||||
|
||||
<synopsis>OK MPD version</synopsis>
|
||||
|
||||
where <varname>version</varname> is a version identifier such as
|
||||
0.12.2. This version identifier is the version of the protocol
|
||||
spoken, not the real version of the daemon. (There is no way to
|
||||
retrieve this real version identifier from the connection.)
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>Requests</title>
|
||||
|
||||
<cmdsynopsis>
|
||||
<command>COMMAND</command>
|
||||
<arg rep="repeat"><replaceable>ARG</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
|
||||
<para>
|
||||
If arguments contain spaces, they should be surrounded by double
|
||||
quotation marks.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Argument strings are separated from the command and any other
|
||||
arguments by linear white-space (' ' or '\t').
|
||||
</para>
|
||||
|
||||
<para>
|
||||
All data between the client and the server is encoded in
|
||||
UTF-8. (Note: In UTF-8 all standard ansi characters, 0-127 are
|
||||
@@ -38,13 +74,97 @@
|
||||
<title>Responses</title>
|
||||
|
||||
<para>
|
||||
A command returns <returnvalue>OK</returnvalue> on completion
|
||||
or <returnvalue>ACK some error</returnvalue> on failure.
|
||||
These denote the end of command execution.
|
||||
A command returns <returnvalue>OK</returnvalue> on completion or
|
||||
<returnvalue>ACK some error</returnvalue> on failure. These
|
||||
denote the end of command execution.
|
||||
</para>
|
||||
|
||||
<section>
|
||||
<title>Failure responses</title>
|
||||
|
||||
<para>
|
||||
The nature of the error can be gleaned from the information
|
||||
that follows the <returnvalue>ACK</returnvalue>.
|
||||
<returnvalue>ACK</returnvalue> lines are of the form:
|
||||
|
||||
<synopsis>ACK [error@command_listNum] {current_command} message_text\n</synopsis>
|
||||
|
||||
These responses are generated by a call to
|
||||
<function>commandError</function>. They contain four separate
|
||||
terms. Let's look at each of them:
|
||||
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>error</returnvalue>: numeric value of one
|
||||
of the <constant>ACK_ERROR</constant> constants defined
|
||||
in <filename>ack.h</filename>.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>command_listNum</returnvalue>:
|
||||
offset of the command that caused the error in a <link
|
||||
linkend="command_lists">Command List</link>.
|
||||
An error will always cause a command list to terminate
|
||||
at the command that causes the error.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>current_command</returnvalue>:
|
||||
name of the command, in a <link
|
||||
linkend="command_lists">Command List</link>,
|
||||
that was executing when the error occurred.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>
|
||||
<returnvalue>message_text</returnvalue>:
|
||||
some (hopefully) informative text that describes the
|
||||
nature of the error.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
|
||||
<example>
|
||||
<title>foo</title>
|
||||
<para>
|
||||
An example might help. Consider the following sequence
|
||||
sent from the client to the server.
|
||||
|
||||
<synopsis>
|
||||
command_list_begin
|
||||
volume 86
|
||||
play 10240
|
||||
status
|
||||
command_list_end
|
||||
</synopsis>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
The server responds with:
|
||||
|
||||
<returnvalue>
|
||||
ACK [50@1] {play} song doesn't exist: "10240"
|
||||
</returnvalue>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This tells us that the play command, which was the
|
||||
second in the list (the first or only command is
|
||||
numbered 0), failed with error 50. The number 50
|
||||
translates to <constant>ACK_ERROR_NO_EXIST</constant>--the
|
||||
song doesn't exist. This is reiterated by the message text
|
||||
which also tells us which song doesn't exist.
|
||||
</para>
|
||||
|
||||
</example>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<section id="command_lists">
|
||||
<title>Command lists</title>
|
||||
|
||||
<para>
|
||||
|
@@ -446,7 +446,7 @@ cd mpd-version</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To configure a filter, add a
|
||||
To configure a playlist plugin, add a
|
||||
<varname>playlist_plugin</varname> block to
|
||||
<filename>mpd.conf</filename>:
|
||||
</para>
|
||||
|
@@ -63,3 +63,18 @@ AC_DEFUN([MPD_AUTO_PKG], [
|
||||
|
||||
MPD_AUTO_RESULT([$1], [$4], [$5])
|
||||
])
|
||||
|
||||
dnl Check with pkg-config first, fall back to AC_CHECK_LIB.
|
||||
dnl
|
||||
dnl Parameters: varname1, varname2, pkgname, libname, symname, libs, cflags, description, errmsg
|
||||
AC_DEFUN([MPD_AUTO_PKG_LIB], [
|
||||
if eval "test x`echo '$'enable_$1` != xno"; then
|
||||
PKG_CHECK_MODULES([$2], [$3],
|
||||
[eval "found_$1=yes"],
|
||||
AC_CHECK_LIB($4, $5,
|
||||
[eval "found_$1=yes $2_LIBS='$6' $2_CFLAGS='$7'"],
|
||||
[eval "found_$1=no"]))
|
||||
fi
|
||||
|
||||
MPD_AUTO_RESULT([$1], [$8], [$9])
|
||||
])
|
||||
|
@@ -3,7 +3,7 @@ PWD=`pwd`
|
||||
|
||||
## If we're not in the scripts directory
|
||||
## assume the base directory.
|
||||
if test "`basename $PWD`" == "scripts"; then
|
||||
if test "`basename $PWD`" = "scripts"; then
|
||||
cd ../
|
||||
else
|
||||
MYOLDPWD=`pwd`
|
||||
@@ -18,7 +18,7 @@ fi
|
||||
make
|
||||
make dist
|
||||
|
||||
if test "`basename $PWD`" == "scripts"; then
|
||||
if test "`basename $PWD`" = "scripts"; then
|
||||
cd contrib/
|
||||
else
|
||||
cd $MYOLDPWD
|
||||
|
@@ -60,8 +60,10 @@ ape_scan_internal(FILE *fp, tag_ape_callback_t callback, void *ctx)
|
||||
assert(remaining > 10);
|
||||
|
||||
char *buffer = g_malloc(remaining);
|
||||
if (fread(buffer, 1, remaining, fp) != remaining)
|
||||
if (fread(buffer, 1, remaining, fp) != remaining) {
|
||||
g_free(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* read tags */
|
||||
unsigned n = GUINT32_FROM_LE(footer.count);
|
||||
|
11
src/conf.c
11
src/conf.c
@@ -367,6 +367,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
assert(*line != 0);
|
||||
g_propagate_prefixed_error(error_r, error,
|
||||
"line %i: ", count);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -378,6 +379,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"unrecognized parameter in config file at "
|
||||
"line %i: %s\n", count, name);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -387,6 +389,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
"config parameter \"%s\" is first defined "
|
||||
"on line %i and redefined on line %i\n",
|
||||
name, param->line, count);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -398,6 +401,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
if (*line != '{') {
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"line %i: '{' expected", count);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -406,12 +410,15 @@ config_read_file(const char *file, GError **error_r)
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"line %i: Unknown tokens after '{'",
|
||||
count);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
param = config_read_block(fp, &count, string, error_r);
|
||||
if (param == NULL)
|
||||
if (param == NULL) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
/* a string value */
|
||||
|
||||
@@ -428,6 +435,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
g_error_free(error);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -435,6 +443,7 @@ config_read_file(const char *file, GError **error_r)
|
||||
g_set_error(error_r, config_quark(), 0,
|
||||
"line %i: Unknown tokens after value",
|
||||
count);
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -180,7 +180,7 @@ db_check(void)
|
||||
}
|
||||
|
||||
/* Check if we can write to the directory */
|
||||
if (access(dirPath, R_OK | W_OK)) {
|
||||
if (access(dirPath, X_OK | W_OK)) {
|
||||
g_warning("Can't create db file in \"%s\": %s",
|
||||
dirPath, strerror(errno));
|
||||
g_free(dirPath);
|
||||
|
@@ -89,7 +89,11 @@ struct mpd_ffmpeg_stream {
|
||||
struct decoder *decoder;
|
||||
struct input_stream *input;
|
||||
|
||||
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
|
||||
AVIOContext *io;
|
||||
#else
|
||||
ByteIOContext *io;
|
||||
#endif
|
||||
unsigned char buffer[8192];
|
||||
};
|
||||
|
||||
@@ -135,6 +139,33 @@ mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
|
||||
return stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* API compatibility wrapper for av_open_input_stream() and
|
||||
* avformat_open_input().
|
||||
*/
|
||||
static int
|
||||
mpd_ffmpeg_open_input(AVFormatContext **ic_ptr,
|
||||
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
|
||||
AVIOContext *pb,
|
||||
#else
|
||||
ByteIOContext *pb,
|
||||
#endif
|
||||
const char *filename,
|
||||
AVInputFormat *fmt)
|
||||
{
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,1,3)
|
||||
AVFormatContext *context = avformat_alloc_context();
|
||||
if (context == NULL)
|
||||
return AVERROR(ENOMEM);
|
||||
|
||||
context->pb = pb;
|
||||
*ic_ptr = context;
|
||||
return avformat_open_input(ic_ptr, filename, fmt, NULL);
|
||||
#else
|
||||
return av_open_input_stream(ic_ptr, pb, filename, fmt, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
mpd_ffmpeg_stream_close(struct mpd_ffmpeg_stream *stream)
|
||||
{
|
||||
@@ -321,9 +352,9 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
|
||||
}
|
||||
|
||||
//ffmpeg works with ours "fileops" helper
|
||||
AVFormatContext *format_context;
|
||||
if (av_open_input_stream(&format_context, stream->io, input->uri,
|
||||
input_format, NULL) != 0) {
|
||||
AVFormatContext *format_context = NULL;
|
||||
if (mpd_ffmpeg_open_input(&format_context, stream->io, input->uri,
|
||||
input_format) != 0) {
|
||||
g_warning("Open failed\n");
|
||||
mpd_ffmpeg_stream_close(stream);
|
||||
return;
|
||||
@@ -446,13 +477,26 @@ static const ffmpeg_tag_map ffmpeg_tag_maps[] = {
|
||||
};
|
||||
|
||||
static bool
|
||||
ffmpeg_copy_metadata(struct tag *tag, AVMetadata *m,
|
||||
ffmpeg_copy_metadata(struct tag *tag,
|
||||
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,1,0)
|
||||
AVDictionary *m,
|
||||
#else
|
||||
AVMetadata *m,
|
||||
#endif
|
||||
const ffmpeg_tag_map tag_map)
|
||||
{
|
||||
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,1,0)
|
||||
AVDictionaryEntry *mt = NULL;
|
||||
|
||||
while ((mt = av_dict_get(m, tag_map.name, mt, 0)) != NULL)
|
||||
tag_add_item(tag, tag_map.type, mt->value);
|
||||
#else
|
||||
AVMetadataTag *mt = NULL;
|
||||
|
||||
while ((mt = av_metadata_get(m, tag_map.name, mt, 0)) != NULL)
|
||||
tag_add_item(tag, tag_map.type, mt->value);
|
||||
#endif
|
||||
|
||||
return mt != NULL;
|
||||
}
|
||||
|
||||
@@ -470,9 +514,9 @@ ffmpeg_stream_tag(struct input_stream *is)
|
||||
if (stream == NULL)
|
||||
return NULL;
|
||||
|
||||
AVFormatContext *f;
|
||||
if (av_open_input_stream(&f, stream->io, is->uri,
|
||||
input_format, NULL) != 0) {
|
||||
AVFormatContext *f = NULL;
|
||||
if (mpd_ffmpeg_open_input(&f, stream->io, is->uri,
|
||||
input_format) != 0) {
|
||||
mpd_ffmpeg_stream_close(stream);
|
||||
return NULL;
|
||||
}
|
||||
|
@@ -224,7 +224,8 @@ flac_tag_apply_metadata(struct tag *tag, const char *track,
|
||||
break;
|
||||
|
||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||
tag->time = flac_duration(&block->data.stream_info);
|
||||
if (block->data.stream_info.sample_rate > 0)
|
||||
tag->time = flac_duration(&block->data.stream_info);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#ifndef MPD_FLAC_METADATA_H
|
||||
#define MPD_FLAC_METADATA_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <FLAC/metadata.h>
|
||||
|
||||
@@ -29,6 +30,8 @@ struct replay_gain_info;
|
||||
static inline unsigned
|
||||
flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
|
||||
{
|
||||
assert(stream_info->sample_rate > 0);
|
||||
|
||||
return (stream_info->total_samples + stream_info->sample_rate - 1) /
|
||||
stream_info->sample_rate;
|
||||
}
|
||||
|
@@ -153,7 +153,6 @@ mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is)
|
||||
mpc_uint32_t ret;
|
||||
int32_t chunk[G_N_ELEMENTS(sample_buffer)];
|
||||
long bit_rate = 0;
|
||||
mpc_uint32_t vbr_update_acc;
|
||||
mpc_uint32_t vbr_update_bits;
|
||||
enum decoder_command cmd = DECODE_COMMAND_NONE;
|
||||
|
||||
@@ -243,10 +242,11 @@ mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is)
|
||||
decoder_seek_error(mpd_decoder);
|
||||
}
|
||||
|
||||
vbr_update_acc = 0;
|
||||
vbr_update_bits = 0;
|
||||
|
||||
#ifdef MPC_IS_OLD_API
|
||||
mpc_uint32_t vbr_update_acc = 0;
|
||||
|
||||
ret = mpc_decoder_decode(&decoder, sample_buffer,
|
||||
&vbr_update_acc, &vbr_update_bits);
|
||||
if (ret == 0 || ret == (mpc_uint32_t)-1)
|
||||
|
@@ -34,9 +34,6 @@
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "wavpack"
|
||||
|
||||
/* pick 1020 since its devisible for 8,16,24, and 32-bit audio */
|
||||
#define CHUNK_SIZE 1020
|
||||
|
||||
#define ERRORLEN 80
|
||||
|
||||
static struct {
|
||||
@@ -162,8 +159,6 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
|
||||
enum sample_format sample_format;
|
||||
struct audio_format audio_format;
|
||||
format_samples_t format_samples;
|
||||
char chunk[CHUNK_SIZE];
|
||||
int samples_requested, samples_got;
|
||||
float total_time;
|
||||
int bytes_per_sample, output_sample_size;
|
||||
|
||||
@@ -193,12 +188,15 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
|
||||
output_sample_size = audio_format_frame_size(&audio_format);
|
||||
|
||||
/* wavpack gives us all kind of samples in a 32-bit space */
|
||||
samples_requested = sizeof(chunk) / (4 * audio_format.channels);
|
||||
int32_t chunk[1024];
|
||||
const uint32_t samples_requested = G_N_ELEMENTS(chunk) /
|
||||
audio_format.channels;
|
||||
|
||||
decoder_initialized(decoder, &audio_format, can_seek, total_time);
|
||||
|
||||
do {
|
||||
if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK) {
|
||||
enum decoder_command cmd = decoder_get_command(decoder);
|
||||
while (cmd != DECODE_COMMAND_STOP) {
|
||||
if (cmd == DECODE_COMMAND_SEEK) {
|
||||
if (can_seek) {
|
||||
unsigned where = decoder_seek_where(decoder) *
|
||||
audio_format.sample_rate;
|
||||
@@ -213,29 +211,20 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
|
||||
}
|
||||
}
|
||||
|
||||
if (decoder_get_command(decoder) == DECODE_COMMAND_STOP) {
|
||||
uint32_t samples_got = WavpackUnpackSamples(wpc, chunk,
|
||||
samples_requested);
|
||||
if (samples_got == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
samples_got = WavpackUnpackSamples(
|
||||
wpc, (int32_t *)chunk, samples_requested
|
||||
);
|
||||
if (samples_got > 0) {
|
||||
int bitrate = (int)(WavpackGetInstantBitrate(wpc) /
|
||||
1000 + 0.5);
|
||||
int bitrate = (int)(WavpackGetInstantBitrate(wpc) / 1000 +
|
||||
0.5);
|
||||
format_samples(bytes_per_sample, chunk,
|
||||
samples_got * audio_format.channels);
|
||||
|
||||
format_samples(
|
||||
bytes_per_sample, chunk,
|
||||
samples_got * audio_format.channels
|
||||
);
|
||||
|
||||
decoder_data(
|
||||
decoder, NULL, chunk,
|
||||
samples_got * output_sample_size,
|
||||
bitrate
|
||||
);
|
||||
}
|
||||
} while (samples_got > 0);
|
||||
cmd = decoder_data(decoder, NULL, chunk,
|
||||
samples_got * output_sample_size,
|
||||
bitrate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -266,6 +266,15 @@ vorbis_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
|
||||
{
|
||||
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
||||
|
||||
encoder->flush = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
vorbis_encoder_pre_tag(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
|
||||
{
|
||||
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
||||
|
||||
vorbis_analysis_wrote(&encoder->vd, 0);
|
||||
vorbis_encoder_blockout(encoder);
|
||||
|
||||
@@ -366,6 +375,7 @@ vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length)
|
||||
if (ret == 0 && encoder->flush) {
|
||||
encoder->flush = false;
|
||||
ret = ogg_stream_flush(&encoder->os, &page);
|
||||
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
@@ -398,6 +408,7 @@ const struct encoder_plugin vorbis_encoder_plugin = {
|
||||
.open = vorbis_encoder_open,
|
||||
.close = vorbis_encoder_close,
|
||||
.flush = vorbis_encoder_flush,
|
||||
.pre_tag = vorbis_encoder_pre_tag,
|
||||
.tag = vorbis_encoder_tag,
|
||||
.write = vorbis_encoder_write,
|
||||
.read = vorbis_encoder_read,
|
||||
|
@@ -50,6 +50,8 @@ struct encoder_plugin {
|
||||
|
||||
bool (*flush)(struct encoder *encoder, GError **error);
|
||||
|
||||
bool (*pre_tag)(struct encoder *encoder, GError **error);
|
||||
|
||||
bool (*tag)(struct encoder *encoder, const struct tag *tag,
|
||||
GError **error);
|
||||
|
||||
@@ -147,9 +149,31 @@ encoder_flush(struct encoder *encoder, GError **error)
|
||||
: true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare for sending a tag to the encoder. This is used by some
|
||||
* encoders to flush the previous sub-stream, in preparation to begin
|
||||
* a new one.
|
||||
*
|
||||
* @param encoder the encoder
|
||||
* @param tag the tag object
|
||||
* @param error location to store the error occuring, or NULL to ignore errors.
|
||||
* @return true on success
|
||||
*/
|
||||
static inline bool
|
||||
encoder_pre_tag(struct encoder *encoder, GError **error)
|
||||
{
|
||||
/* this method is optional */
|
||||
return encoder->plugin->pre_tag != NULL
|
||||
? encoder->plugin->pre_tag(encoder, error)
|
||||
: true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a tag to the encoder.
|
||||
*
|
||||
* Instructions: call encoder_pre_tag(); then obtain flushed data with
|
||||
* encoder_read(); finally call encoder_tag().
|
||||
*
|
||||
* @param encoder the encoder
|
||||
* @param tag the tag object
|
||||
* @param error location to store the error occuring, or NULL to ignore errors.
|
||||
|
@@ -42,6 +42,13 @@
|
||||
#undef G_LOG_DOMAIN
|
||||
#define G_LOG_DOMAIN "input_curl"
|
||||
|
||||
/**
|
||||
* Do not buffer more than this number of bytes. It should be a
|
||||
* reasonable limit that doesn't make low-end machines suffer too
|
||||
* much, but doesn't cause stuttering on high-latency lines.
|
||||
*/
|
||||
static const size_t CURL_MAX_BUFFERED = 512 * 1024;
|
||||
|
||||
/**
|
||||
* Buffers created by input_curl_writefunction().
|
||||
*/
|
||||
@@ -144,6 +151,25 @@ input_curl_finish(void)
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the total sizes of all buffers, including portions that
|
||||
* have already been consumed.
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
static size_t
|
||||
curl_total_buffer_size(const struct input_curl *c)
|
||||
{
|
||||
size_t total = 0;
|
||||
|
||||
for (GList *i = g_queue_peek_head_link(c->buffers);
|
||||
i != NULL; i = g_list_next(i)) {
|
||||
struct buffer *buffer = i->data;
|
||||
total += buffer->size;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static void
|
||||
buffer_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
|
||||
{
|
||||
@@ -473,6 +499,10 @@ static int
|
||||
input_curl_buffer(struct input_stream *is, GError **error_r)
|
||||
{
|
||||
struct input_curl *c = (struct input_curl *)is;
|
||||
|
||||
if (curl_total_buffer_size(c) >= CURL_MAX_BUFFERED)
|
||||
return 0;
|
||||
|
||||
CURLMcode mcode;
|
||||
int running_handles;
|
||||
bool ret;
|
||||
@@ -650,6 +680,9 @@ input_curl_easy_init(struct input_curl *c, GError **error_r)
|
||||
curl_easy_setopt(c->easy, CURLOPT_MAXREDIRS, 5);
|
||||
curl_easy_setopt(c->easy, CURLOPT_FAILONERROR, true);
|
||||
curl_easy_setopt(c->easy, CURLOPT_ERRORBUFFER, c->error);
|
||||
curl_easy_setopt(c->easy, CURLOPT_NOPROGRESS, 1l);
|
||||
curl_easy_setopt(c->easy, CURLOPT_NOSIGNAL, 1l);
|
||||
curl_easy_setopt(c->easy, CURLOPT_CONNECTTIMEOUT, 10l);
|
||||
|
||||
if (proxy != NULL)
|
||||
curl_easy_setopt(c->easy, CURLOPT_PROXY, proxy);
|
||||
|
@@ -508,6 +508,14 @@ configure_hw:
|
||||
g_debug("buffer_size=%u period_size=%u",
|
||||
(unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
|
||||
|
||||
if (alsa_period_size == 0)
|
||||
/* this works around a SIGFPE bug that occurred when
|
||||
an ALSA driver indicated period_size==0; this
|
||||
caused a division by zero in alsa_play(). By using
|
||||
the fallback "1", we make sure that this won't
|
||||
happen again. */
|
||||
alsa_period_size = 1;
|
||||
|
||||
ad->period_frames = alsa_period_size;
|
||||
ad->period_position = 0;
|
||||
|
||||
|
@@ -106,12 +106,14 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||
g_set_error(error, ao_output_quark(), 0,
|
||||
"\"%s\" is not a valid ao driver",
|
||||
value);
|
||||
g_free(ad);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((ai = ao_driver_info(ad->driver)) == NULL) {
|
||||
g_set_error(error, ao_output_quark(), 0,
|
||||
"problems getting driver info");
|
||||
g_free(ad);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -129,6 +131,7 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||
g_set_error(error, ao_output_quark(), 0,
|
||||
"problems parsing options \"%s\"",
|
||||
options[i]);
|
||||
g_free(ad);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@@ -143,6 +143,8 @@ httpd_client_unref_page(gpointer data, G_GNUC_UNUSED gpointer user_data)
|
||||
void
|
||||
httpd_client_free(struct httpd_client *client)
|
||||
{
|
||||
assert(client != NULL);
|
||||
|
||||
if (client->state == RESPONSE) {
|
||||
if (client->write_source_id != 0)
|
||||
g_source_remove(client->write_source_id);
|
||||
@@ -169,6 +171,8 @@ httpd_client_free(struct httpd_client *client)
|
||||
static void
|
||||
httpd_client_close(struct httpd_client *client)
|
||||
{
|
||||
assert(client != NULL);
|
||||
|
||||
httpd_output_remove_client(client->httpd, client);
|
||||
httpd_client_free(client);
|
||||
}
|
||||
@@ -179,6 +183,9 @@ httpd_client_close(struct httpd_client *client)
|
||||
static void
|
||||
httpd_client_begin_response(struct httpd_client *client)
|
||||
{
|
||||
assert(client != NULL);
|
||||
assert(client->state != RESPONSE);
|
||||
|
||||
client->state = RESPONSE;
|
||||
client->write_source_id = 0;
|
||||
client->pages = g_queue_new();
|
||||
@@ -239,6 +246,9 @@ httpd_client_handle_line(struct httpd_client *client, const char *line)
|
||||
static char *
|
||||
httpd_client_read_line(struct httpd_client *client)
|
||||
{
|
||||
assert(client != NULL);
|
||||
assert(client->state != RESPONSE);
|
||||
|
||||
const char *p, *newline;
|
||||
size_t length;
|
||||
char *line;
|
||||
@@ -271,6 +281,7 @@ httpd_client_send_response(struct httpd_client *client)
|
||||
GIOStatus status;
|
||||
gsize bytes_written;
|
||||
|
||||
assert(client != NULL);
|
||||
assert(client->state == RESPONSE);
|
||||
|
||||
if (!client->metadata_requested) {
|
||||
@@ -334,14 +345,19 @@ httpd_client_send_response(struct httpd_client *client)
|
||||
static bool
|
||||
httpd_client_received(struct httpd_client *client)
|
||||
{
|
||||
assert(client != NULL);
|
||||
assert(client->state != RESPONSE);
|
||||
|
||||
char *line;
|
||||
bool success;
|
||||
|
||||
while ((line = httpd_client_read_line(client)) != NULL) {
|
||||
success = httpd_client_handle_line(client, line);
|
||||
g_free(line);
|
||||
if (!success)
|
||||
if (!success) {
|
||||
assert(client->state != RESPONSE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (client->state == RESPONSE) {
|
||||
if (!fifo_buffer_is_empty(client->input)) {
|
||||
@@ -370,7 +386,14 @@ httpd_client_read(struct httpd_client *client)
|
||||
if (client->state == RESPONSE) {
|
||||
/* the client has already sent the request, and he
|
||||
must not send more */
|
||||
g_warning("unexpected input from client");
|
||||
char buffer[1];
|
||||
|
||||
status = g_io_channel_read_chars(client->channel, buffer,
|
||||
sizeof(buffer), &bytes_read,
|
||||
NULL);
|
||||
if (status == G_IO_STATUS_NORMAL)
|
||||
g_warning("unexpected input from client");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -103,6 +103,7 @@ httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||
if (encoder_plugin == NULL) {
|
||||
g_set_error(error, httpd_output_quark(), 0,
|
||||
"No such encoder: %s", encoder_name);
|
||||
g_free(httpd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -492,7 +493,8 @@ httpd_output_pause(void *data)
|
||||
|
||||
if (has_clients) {
|
||||
static const char silence[1020];
|
||||
return httpd_output_play(data, silence, sizeof(silence), NULL);
|
||||
return httpd_output_play(data, silence, sizeof(silence),
|
||||
NULL) > 0;
|
||||
} else {
|
||||
g_usleep(100000);
|
||||
return true;
|
||||
@@ -521,7 +523,7 @@ httpd_output_tag(void *data, const struct tag *tag)
|
||||
|
||||
/* flush the current stream, and end it */
|
||||
|
||||
encoder_flush(httpd->encoder, NULL);
|
||||
encoder_pre_tag(httpd->encoder, NULL);
|
||||
httpd_output_encoder_to_clients(httpd);
|
||||
|
||||
/* send the tag to the encoder - which starts a new
|
||||
|
@@ -95,12 +95,6 @@ static void osx_output_close(void *data)
|
||||
{
|
||||
struct osx_output *od = data;
|
||||
|
||||
g_mutex_lock(od->mutex);
|
||||
while (od->len) {
|
||||
g_cond_wait(od->condition, od->mutex);
|
||||
}
|
||||
g_mutex_unlock(od->mutex);
|
||||
|
||||
AudioOutputUnitStop(od->au);
|
||||
AudioUnitUninitialize(od->au);
|
||||
CloseComponent(od->au);
|
||||
@@ -143,8 +137,8 @@ osx_render(void *vdata,
|
||||
if (od->pos >= od->buffer_size)
|
||||
od->pos = 0;
|
||||
|
||||
g_mutex_unlock(od->mutex);
|
||||
g_cond_signal(od->condition);
|
||||
g_mutex_unlock(od->mutex);
|
||||
|
||||
buffer->mDataByteSize = buffer_size;
|
||||
|
||||
|
@@ -207,6 +207,9 @@ pulse_output_subscribe_cb(pa_context *context,
|
||||
static bool
|
||||
pulse_output_connect(struct pulse_output *po, GError **error_r)
|
||||
{
|
||||
assert(po != NULL);
|
||||
assert(po->context != NULL);
|
||||
|
||||
int error;
|
||||
|
||||
error = pa_context_connect(po->context, po->server,
|
||||
@@ -221,6 +224,44 @@ pulse_output_connect(struct pulse_output *po, GError **error_r)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees and clears the stream.
|
||||
*/
|
||||
static void
|
||||
pulse_output_delete_stream(struct pulse_output *po)
|
||||
{
|
||||
assert(po != NULL);
|
||||
assert(po->stream != NULL);
|
||||
|
||||
#if PA_CHECK_VERSION(0,9,8)
|
||||
pa_stream_set_suspended_callback(po->stream, NULL, NULL);
|
||||
#endif
|
||||
|
||||
pa_stream_set_state_callback(po->stream, NULL, NULL);
|
||||
pa_stream_set_write_callback(po->stream, NULL, NULL);
|
||||
|
||||
pa_stream_disconnect(po->stream);
|
||||
pa_stream_unref(po->stream);
|
||||
po->stream = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees and clears the context.
|
||||
*/
|
||||
static void
|
||||
pulse_output_delete_context(struct pulse_output *po)
|
||||
{
|
||||
assert(po != NULL);
|
||||
assert(po->context != NULL);
|
||||
|
||||
pa_context_set_state_callback(po->context, NULL, NULL);
|
||||
pa_context_set_subscribe_callback(po->context, NULL, NULL);
|
||||
|
||||
pa_context_disconnect(po->context);
|
||||
pa_context_unref(po->context);
|
||||
po->context = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create, set up and connect a context.
|
||||
*
|
||||
@@ -229,6 +270,9 @@ pulse_output_connect(struct pulse_output *po, GError **error_r)
|
||||
static bool
|
||||
pulse_output_setup_context(struct pulse_output *po, GError **error_r)
|
||||
{
|
||||
assert(po != NULL);
|
||||
assert(po->mainloop != NULL);
|
||||
|
||||
po->context = pa_context_new(pa_threaded_mainloop_get_api(po->mainloop),
|
||||
MPD_PULSE_NAME);
|
||||
if (po->context == NULL) {
|
||||
@@ -243,25 +287,13 @@ pulse_output_setup_context(struct pulse_output *po, GError **error_r)
|
||||
pulse_output_subscribe_cb, po);
|
||||
|
||||
if (!pulse_output_connect(po, error_r)) {
|
||||
pa_context_unref(po->context);
|
||||
po->context = NULL;
|
||||
pulse_output_delete_context(po);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees and clears the context.
|
||||
*/
|
||||
static void
|
||||
pulse_output_delete_context(struct pulse_output *po)
|
||||
{
|
||||
pa_context_disconnect(po->context);
|
||||
pa_context_unref(po->context);
|
||||
po->context = NULL;
|
||||
}
|
||||
|
||||
static void *
|
||||
pulse_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||
const struct config_param *param,
|
||||
@@ -347,6 +379,8 @@ pulse_output_disable(void *data)
|
||||
{
|
||||
struct pulse_output *po = data;
|
||||
|
||||
assert(po->mainloop != NULL);
|
||||
|
||||
pa_threaded_mainloop_stop(po->mainloop);
|
||||
if (po->context != NULL)
|
||||
pulse_output_delete_context(po);
|
||||
@@ -363,6 +397,8 @@ pulse_output_disable(void *data)
|
||||
static bool
|
||||
pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
|
||||
{
|
||||
assert(po->mainloop != NULL);
|
||||
|
||||
pa_context_state_t state;
|
||||
|
||||
pa_threaded_mainloop_lock(po->mainloop);
|
||||
@@ -399,11 +435,32 @@ pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
|
||||
}
|
||||
}
|
||||
|
||||
#if PA_CHECK_VERSION(0,9,8)
|
||||
|
||||
static void
|
||||
pulse_output_stream_suspended_cb(G_GNUC_UNUSED pa_stream *stream, void *userdata)
|
||||
{
|
||||
struct pulse_output *po = userdata;
|
||||
|
||||
assert(stream == po->stream || po->stream == NULL);
|
||||
assert(po->mainloop != NULL);
|
||||
|
||||
/* wake up the main loop to break out of the loop in
|
||||
pulse_output_play() */
|
||||
pa_threaded_mainloop_signal(po->mainloop, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void
|
||||
pulse_output_stream_state_cb(pa_stream *stream, void *userdata)
|
||||
{
|
||||
struct pulse_output *po = userdata;
|
||||
|
||||
assert(stream == po->stream || po->stream == NULL);
|
||||
assert(po->mainloop != NULL);
|
||||
assert(po->context != NULL);
|
||||
|
||||
switch (pa_stream_get_state(stream)) {
|
||||
case PA_STREAM_READY:
|
||||
if (po->mixer != NULL)
|
||||
@@ -432,6 +489,8 @@ pulse_output_stream_write_cb(G_GNUC_UNUSED pa_stream *stream, size_t nbytes,
|
||||
{
|
||||
struct pulse_output *po = userdata;
|
||||
|
||||
assert(po->mainloop != NULL);
|
||||
|
||||
po->writable = nbytes;
|
||||
pa_threaded_mainloop_signal(po->mainloop, 0);
|
||||
}
|
||||
@@ -444,6 +503,8 @@ pulse_output_open(void *data, struct audio_format *audio_format,
|
||||
pa_sample_spec ss;
|
||||
int error;
|
||||
|
||||
assert(po->mainloop != NULL);
|
||||
|
||||
if (po->context != NULL) {
|
||||
switch (pa_context_get_state(po->context)) {
|
||||
case PA_CONTEXT_UNCONNECTED:
|
||||
@@ -487,6 +548,11 @@ pulse_output_open(void *data, struct audio_format *audio_format,
|
||||
return false;
|
||||
}
|
||||
|
||||
#if PA_CHECK_VERSION(0,9,8)
|
||||
pa_stream_set_suspended_callback(po->stream,
|
||||
pulse_output_stream_suspended_cb, po);
|
||||
#endif
|
||||
|
||||
pa_stream_set_state_callback(po->stream,
|
||||
pulse_output_stream_state_cb, po);
|
||||
pa_stream_set_write_callback(po->stream,
|
||||
@@ -497,8 +563,7 @@ pulse_output_open(void *data, struct audio_format *audio_format,
|
||||
error = pa_stream_connect_playback(po->stream, po->sink,
|
||||
NULL, 0, NULL, NULL);
|
||||
if (error < 0) {
|
||||
pa_stream_unref(po->stream);
|
||||
po->stream = NULL;
|
||||
pulse_output_delete_stream(po);
|
||||
|
||||
g_set_error(error_r, pulse_output_quark(), 0,
|
||||
"pa_stream_connect_playback() has failed: %s",
|
||||
@@ -522,6 +587,8 @@ pulse_output_close(void *data)
|
||||
struct pulse_output *po = data;
|
||||
pa_operation *o;
|
||||
|
||||
assert(po->mainloop != NULL);
|
||||
|
||||
pa_threaded_mainloop_lock(po->mainloop);
|
||||
|
||||
if (pa_stream_get_state(po->stream) == PA_STREAM_READY) {
|
||||
@@ -534,9 +601,7 @@ pulse_output_close(void *data)
|
||||
pulse_wait_for_operation(po->mainloop, o);
|
||||
}
|
||||
|
||||
pa_stream_disconnect(po->stream);
|
||||
pa_stream_unref(po->stream);
|
||||
po->stream = NULL;
|
||||
pulse_output_delete_stream(po);
|
||||
|
||||
if (po->context != NULL &&
|
||||
pa_context_get_state(po->context) != PA_CONTEXT_READY)
|
||||
@@ -556,6 +621,8 @@ pulse_output_check_stream(struct pulse_output *po)
|
||||
{
|
||||
pa_stream_state_t state = pa_stream_get_state(po->stream);
|
||||
|
||||
assert(po->mainloop != NULL);
|
||||
|
||||
switch (state) {
|
||||
case PA_STREAM_READY:
|
||||
case PA_STREAM_FAILED:
|
||||
@@ -637,6 +704,8 @@ pulse_output_stream_pause(struct pulse_output *po, bool pause,
|
||||
{
|
||||
pa_operation *o;
|
||||
|
||||
assert(po->mainloop != NULL);
|
||||
assert(po->context != NULL);
|
||||
assert(po->stream != NULL);
|
||||
|
||||
o = pa_stream_cork(po->stream, pause,
|
||||
@@ -667,6 +736,7 @@ pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r)
|
||||
struct pulse_output *po = data;
|
||||
int error;
|
||||
|
||||
assert(po->mainloop != NULL);
|
||||
assert(po->stream != NULL);
|
||||
|
||||
pa_threaded_mainloop_lock(po->mainloop);
|
||||
@@ -683,19 +753,30 @@ pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r)
|
||||
/* unpause if previously paused */
|
||||
|
||||
if (pulse_output_stream_is_paused(po) &&
|
||||
!pulse_output_stream_pause(po, false, error_r))
|
||||
!pulse_output_stream_pause(po, false, error_r)) {
|
||||
pa_threaded_mainloop_unlock(po->mainloop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* wait until the server allows us to write */
|
||||
|
||||
while (po->writable == 0) {
|
||||
#if PA_CHECK_VERSION(0,9,8)
|
||||
if (pa_stream_is_suspended(po->stream)) {
|
||||
pa_threaded_mainloop_unlock(po->mainloop);
|
||||
g_set_error(error_r, pulse_output_quark(), 0,
|
||||
"suspended");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
pa_threaded_mainloop_wait(po->mainloop);
|
||||
|
||||
if (pa_stream_get_state(po->stream) != PA_STREAM_READY) {
|
||||
pa_threaded_mainloop_unlock(po->mainloop);
|
||||
g_set_error(error_r, pulse_output_quark(), 0,
|
||||
"disconnected");
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,6 +806,7 @@ pulse_output_cancel(void *data)
|
||||
struct pulse_output *po = data;
|
||||
pa_operation *o;
|
||||
|
||||
assert(po->mainloop != NULL);
|
||||
assert(po->stream != NULL);
|
||||
|
||||
pa_threaded_mainloop_lock(po->mainloop);
|
||||
@@ -756,6 +838,7 @@ pulse_output_pause(void *data)
|
||||
struct pulse_output *po = data;
|
||||
GError *error = NULL;
|
||||
|
||||
assert(po->mainloop != NULL);
|
||||
assert(po->stream != NULL);
|
||||
|
||||
pa_threaded_mainloop_lock(po->mainloop);
|
||||
|
@@ -79,23 +79,27 @@ recorder_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||
if (encoder_plugin == NULL) {
|
||||
g_set_error(error_r, recorder_output_quark(), 0,
|
||||
"No such encoder: %s", encoder_name);
|
||||
return NULL;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
recorder->path = config_get_block_string(param, "path", NULL);
|
||||
if (recorder->path == NULL) {
|
||||
g_set_error(error_r, recorder_output_quark(), 0,
|
||||
"'path' not configured");
|
||||
return NULL;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* initialize encoder */
|
||||
|
||||
recorder->encoder = encoder_init(encoder_plugin, param, error_r);
|
||||
if (recorder->encoder == NULL)
|
||||
return NULL;
|
||||
goto failure;
|
||||
|
||||
return recorder;
|
||||
|
||||
failure:
|
||||
g_free(recorder);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -152,7 +152,7 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||
if (port == 0) {
|
||||
g_set_error(error, shout_output_quark(), 0,
|
||||
"shout port must be configured");
|
||||
return NULL;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
check_block_param("password");
|
||||
@@ -174,21 +174,21 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||
"shout quality \"%s\" is not a number in the "
|
||||
"range -1 to 10, line %i",
|
||||
value, param->line);
|
||||
return NULL;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
if (config_get_block_string(param, "bitrate", NULL) != NULL) {
|
||||
g_set_error(error, shout_output_quark(), 0,
|
||||
"quality and bitrate are "
|
||||
"both defined");
|
||||
return NULL;
|
||||
goto failure;
|
||||
}
|
||||
} else {
|
||||
value = config_get_block_string(param, "bitrate", NULL);
|
||||
if (value == NULL) {
|
||||
g_set_error(error, shout_output_quark(), 0,
|
||||
"neither bitrate nor quality defined");
|
||||
return NULL;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
sd->bitrate = strtol(value, &test, 10);
|
||||
@@ -196,7 +196,7 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||
if (*test != '\0' || sd->bitrate <= 0) {
|
||||
g_set_error(error, shout_output_quark(), 0,
|
||||
"bitrate must be a positive integer");
|
||||
return NULL;
|
||||
goto failure;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,12 +206,12 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||
g_set_error(error, shout_output_quark(), 0,
|
||||
"couldn't find shout encoder plugin \"%s\"",
|
||||
encoding);
|
||||
return NULL;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
sd->encoder = encoder_init(encoder_plugin, param, error);
|
||||
if (sd->encoder == NULL)
|
||||
return NULL;
|
||||
goto failure;
|
||||
|
||||
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
|
||||
shout_format = SHOUT_FORMAT_MP3;
|
||||
@@ -225,7 +225,7 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||
g_set_error(error, shout_output_quark(), 0,
|
||||
"you cannot stream \"%s\" to shoutcast, use mp3",
|
||||
encoding);
|
||||
return NULL;
|
||||
goto failure;
|
||||
} else if (0 == strcmp(value, "shoutcast"))
|
||||
protocol = SHOUT_PROTOCOL_ICY;
|
||||
else if (0 == strcmp(value, "icecast1"))
|
||||
@@ -237,7 +237,7 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||
"shout protocol \"%s\" is not \"shoutcast\" or "
|
||||
"\"icecast1\"or \"icecast2\"",
|
||||
value);
|
||||
return NULL;
|
||||
goto failure;
|
||||
}
|
||||
} else {
|
||||
protocol = SHOUT_PROTOCOL_HTTP;
|
||||
@@ -256,7 +256,7 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||
shout_set_agent(sd->shout_conn, "MPD") != SHOUTERR_SUCCESS) {
|
||||
g_set_error(error, shout_output_quark(), 0,
|
||||
"%s", shout_get_error(sd->shout_conn));
|
||||
return NULL;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
/* optional paramters */
|
||||
@@ -267,14 +267,14 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||
if (value != NULL && shout_set_genre(sd->shout_conn, value)) {
|
||||
g_set_error(error, shout_output_quark(), 0,
|
||||
"%s", shout_get_error(sd->shout_conn));
|
||||
return NULL;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
value = config_get_block_string(param, "description", NULL);
|
||||
if (value != NULL && shout_set_description(sd->shout_conn, value)) {
|
||||
g_set_error(error, shout_output_quark(), 0,
|
||||
"%s", shout_get_error(sd->shout_conn));
|
||||
return NULL;
|
||||
goto failure;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -300,6 +300,10 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
||||
}
|
||||
|
||||
return sd;
|
||||
|
||||
failure:
|
||||
free_shout_data(sd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -507,7 +511,7 @@ static void my_shout_set_tag(void *data,
|
||||
if (sd->encoder->plugin->tag != NULL) {
|
||||
/* encoder plugin supports stream tags */
|
||||
|
||||
ret = encoder_flush(sd->encoder, &error);
|
||||
ret = encoder_pre_tag(sd->encoder, &error);
|
||||
if (!ret) {
|
||||
g_warning("%s", error->message);
|
||||
g_error_free(error);
|
||||
|
@@ -205,27 +205,14 @@ static void audio_output_wait_all(void)
|
||||
notify_wait(&audio_output_client_notify);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals the audio output if it is open. This function locks the
|
||||
* mutex.
|
||||
*/
|
||||
static void
|
||||
audio_output_lock_signal(struct audio_output *ao)
|
||||
{
|
||||
g_mutex_lock(ao->mutex);
|
||||
if (audio_output_is_open(ao))
|
||||
g_cond_signal(ao->cond);
|
||||
g_mutex_unlock(ao->mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals all audio outputs which are open.
|
||||
*/
|
||||
static void
|
||||
audio_output_signal_all(void)
|
||||
audio_output_allow_play_all(void)
|
||||
{
|
||||
for (unsigned i = 0; i < num_audio_outputs; ++i)
|
||||
audio_output_lock_signal(&audio_outputs[i]);
|
||||
audio_output_allow_play(&audio_outputs[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -530,7 +517,7 @@ audio_output_all_cancel(void)
|
||||
/* the audio outputs are now waiting for a signal, to
|
||||
synchronize the cleared music pipe */
|
||||
|
||||
audio_output_signal_all();
|
||||
audio_output_allow_play_all();
|
||||
|
||||
/* invalidate elapsed_time */
|
||||
|
||||
|
@@ -115,6 +115,8 @@ audio_output_open(struct audio_output *ao,
|
||||
{
|
||||
bool open;
|
||||
|
||||
assert(ao != NULL);
|
||||
assert(ao->allow_play);
|
||||
assert(audio_format_valid(audio_format));
|
||||
assert(mp != NULL);
|
||||
|
||||
@@ -140,10 +142,6 @@ audio_output_open(struct audio_output *ao,
|
||||
/* we're not using audio_output_cancel() here,
|
||||
because that function is asynchronous */
|
||||
ao_command(ao, AO_COMMAND_CANCEL);
|
||||
|
||||
/* the audio output is now waiting for a
|
||||
signal; wake it up immediately */
|
||||
g_cond_signal(ao->cond);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -181,6 +179,7 @@ static void
|
||||
audio_output_close_locked(struct audio_output *ao)
|
||||
{
|
||||
assert(ao != NULL);
|
||||
assert(ao->allow_play);
|
||||
|
||||
if (ao->mixer != NULL)
|
||||
mixer_auto_close(ao->mixer);
|
||||
@@ -223,6 +222,8 @@ audio_output_play(struct audio_output *ao)
|
||||
{
|
||||
g_mutex_lock(ao->mutex);
|
||||
|
||||
assert(ao->allow_play);
|
||||
|
||||
if (audio_output_is_open(ao))
|
||||
g_cond_signal(ao->cond);
|
||||
|
||||
@@ -238,6 +239,7 @@ void audio_output_pause(struct audio_output *ao)
|
||||
mixer_auto_close(ao->mixer);
|
||||
|
||||
g_mutex_lock(ao->mutex);
|
||||
assert(ao->allow_play);
|
||||
if (audio_output_is_open(ao))
|
||||
ao_command_async(ao, AO_COMMAND_PAUSE);
|
||||
g_mutex_unlock(ao->mutex);
|
||||
@@ -247,6 +249,7 @@ void
|
||||
audio_output_drain_async(struct audio_output *ao)
|
||||
{
|
||||
g_mutex_lock(ao->mutex);
|
||||
assert(ao->allow_play);
|
||||
if (audio_output_is_open(ao))
|
||||
ao_command_async(ao, AO_COMMAND_DRAIN);
|
||||
g_mutex_unlock(ao->mutex);
|
||||
@@ -255,8 +258,24 @@ audio_output_drain_async(struct audio_output *ao)
|
||||
void audio_output_cancel(struct audio_output *ao)
|
||||
{
|
||||
g_mutex_lock(ao->mutex);
|
||||
if (audio_output_is_open(ao))
|
||||
|
||||
if (audio_output_is_open(ao)) {
|
||||
ao->allow_play = false;
|
||||
ao_command_async(ao, AO_COMMAND_CANCEL);
|
||||
}
|
||||
|
||||
g_mutex_unlock(ao->mutex);
|
||||
}
|
||||
|
||||
void
|
||||
audio_output_allow_play(struct audio_output *ao)
|
||||
{
|
||||
g_mutex_lock(ao->mutex);
|
||||
|
||||
ao->allow_play = true;
|
||||
if (audio_output_is_open(ao))
|
||||
g_cond_signal(ao->cond);
|
||||
|
||||
g_mutex_unlock(ao->mutex);
|
||||
}
|
||||
|
||||
@@ -287,6 +306,7 @@ void audio_output_finish(struct audio_output *ao)
|
||||
|
||||
if (ao->thread != NULL) {
|
||||
g_mutex_lock(ao->mutex);
|
||||
assert(ao->allow_play);
|
||||
ao_command(ao, AO_COMMAND_KILL);
|
||||
g_mutex_unlock(ao->mutex);
|
||||
g_thread_join(ao->thread);
|
||||
|
@@ -70,8 +70,19 @@ void audio_output_pause(struct audio_output *ao);
|
||||
void
|
||||
audio_output_drain_async(struct audio_output *ao);
|
||||
|
||||
/**
|
||||
* Clear the "allow_play" flag and send the "CANCEL" command
|
||||
* asynchronously. To finish the operation, the caller has to call
|
||||
* audio_output_allow_play().
|
||||
*/
|
||||
void audio_output_cancel(struct audio_output *ao);
|
||||
|
||||
/**
|
||||
* Set the "allow_play" and signal the thread.
|
||||
*/
|
||||
void
|
||||
audio_output_allow_play(struct audio_output *ao);
|
||||
|
||||
void audio_output_close(struct audio_output *ao);
|
||||
|
||||
/**
|
||||
|
@@ -189,6 +189,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
|
||||
ao->really_enabled = false;
|
||||
ao->open = false;
|
||||
ao->pause = false;
|
||||
ao->allow_play = true;
|
||||
ao->fail_timer = NULL;
|
||||
|
||||
pcm_buffer_init(&ao->cross_fade_buffer);
|
||||
|
@@ -109,6 +109,15 @@ struct audio_output {
|
||||
*/
|
||||
bool pause;
|
||||
|
||||
/**
|
||||
* When this flag is set, the output thread will not do any
|
||||
* playback. It will wait until the flag is cleared.
|
||||
*
|
||||
* This is used to synchronize the "clear" operation on the
|
||||
* shared music pipe during the CANCEL command.
|
||||
*/
|
||||
bool allow_play;
|
||||
|
||||
/**
|
||||
* If not NULL, the device has failed, and this timer is used
|
||||
* to estimate how long it should stay disabled (unless
|
||||
|
@@ -640,15 +640,14 @@ static gpointer audio_output_task(gpointer arg)
|
||||
|
||||
case AO_COMMAND_CANCEL:
|
||||
ao->chunk = NULL;
|
||||
if (ao->open)
|
||||
ao_plugin_cancel(ao->plugin, ao->data);
|
||||
ao_command_finished(ao);
|
||||
|
||||
/* the player thread will now clear our music
|
||||
pipe - wait for a notify, to give it some
|
||||
time */
|
||||
if (ao->command == AO_COMMAND_NONE)
|
||||
g_cond_wait(ao->cond, ao->mutex);
|
||||
if (ao->open) {
|
||||
g_mutex_unlock(ao->mutex);
|
||||
ao_plugin_cancel(ao->plugin, ao->data);
|
||||
g_mutex_lock(ao->mutex);
|
||||
}
|
||||
|
||||
ao_command_finished(ao);
|
||||
continue;
|
||||
|
||||
case AO_COMMAND_KILL:
|
||||
@@ -658,7 +657,7 @@ static gpointer audio_output_task(gpointer arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ao->open && ao_play(ao))
|
||||
if (ao->open && ao->allow_play && ao_play(ao))
|
||||
/* don't wait for an event if there are more
|
||||
chunks in the pipe */
|
||||
continue;
|
||||
|
@@ -187,5 +187,8 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk)
|
||||
unsigned
|
||||
music_pipe_size(const struct music_pipe *mp)
|
||||
{
|
||||
return mp->size;
|
||||
g_mutex_lock(mp->mutex);
|
||||
unsigned size = mp->size;
|
||||
g_mutex_unlock(mp->mutex);
|
||||
return size;
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@
|
||||
#ifndef MPD_PIPE_H
|
||||
#define MPD_PIPE_H
|
||||
|
||||
#include <glib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -38,6 +39,7 @@ struct music_pipe;
|
||||
/**
|
||||
* Creates a new #music_pipe object. It is empty.
|
||||
*/
|
||||
G_GNUC_MALLOC
|
||||
struct music_pipe *
|
||||
music_pipe_new(void);
|
||||
|
||||
@@ -70,6 +72,7 @@ music_pipe_contains(const struct music_pipe *mp,
|
||||
* Returns the first #music_chunk from the pipe. Returns NULL if the
|
||||
* pipe is empty.
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
const struct music_chunk *
|
||||
music_pipe_peek(const struct music_pipe *mp);
|
||||
|
||||
@@ -96,9 +99,11 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk);
|
||||
/**
|
||||
* Returns the number of chunks currently in this pipe.
|
||||
*/
|
||||
G_GNUC_PURE
|
||||
unsigned
|
||||
music_pipe_size(const struct music_pipe *mp);
|
||||
|
||||
G_GNUC_PURE
|
||||
static inline bool
|
||||
music_pipe_empty(const struct music_pipe *mp)
|
||||
{
|
||||
|
@@ -618,7 +618,9 @@ play_chunk(struct song *song, struct music_chunk *chunk,
|
||||
return true;
|
||||
}
|
||||
|
||||
player_lock();
|
||||
pc.bit_rate = chunk->bit_rate;
|
||||
player_unlock();
|
||||
|
||||
/* send the chunk to the audio outputs */
|
||||
|
||||
|
@@ -222,10 +222,12 @@ playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time)
|
||||
playlist->error_count = 0;
|
||||
|
||||
if (!playlist->playing || (unsigned)playlist->current != i) {
|
||||
/* seeking is not within the current song - first
|
||||
start playing the new song */
|
||||
/* seeking is not within the current song - prepare
|
||||
song change */
|
||||
|
||||
playlist->playing = true;
|
||||
playlist->current = i;
|
||||
|
||||
playlist_play_order(playlist, i);
|
||||
queued = NULL;
|
||||
}
|
||||
|
||||
|
@@ -579,8 +579,10 @@ sticker_load(const char *type, const char *uri)
|
||||
bool success;
|
||||
|
||||
success = sticker_list_values(sticker->table, type, uri);
|
||||
if (!success)
|
||||
if (!success) {
|
||||
sticker_free(sticker);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (g_hash_table_size(sticker->table) == 0) {
|
||||
/* don't return empty sticker objects */
|
||||
|
@@ -714,8 +714,14 @@ skip_symlink(const struct directory *directory, const char *utf8_name)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer[0] == '/')
|
||||
return !follow_outside_symlinks;
|
||||
if (g_path_is_absolute(buffer)) {
|
||||
/* if the symlink points to an absolute path, see if
|
||||
that path is inside the music directory */
|
||||
const char *relative = map_to_relative_path(buffer);
|
||||
return relative > buffer
|
||||
? !follow_inside_symlinks
|
||||
: !follow_outside_symlinks;
|
||||
}
|
||||
|
||||
p = buffer;
|
||||
while (*p == '.') {
|
||||
|
@@ -37,30 +37,28 @@ my_log_func(G_GNUC_UNUSED const gchar *log_domain,
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *path, *name, *value;
|
||||
GError *error = NULL;
|
||||
bool success;
|
||||
int ret;
|
||||
|
||||
if (argc != 3) {
|
||||
g_printerr("Usage: read_conf FILE SETTING\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
path = argv[1];
|
||||
name = argv[2];
|
||||
const char *path = argv[1];
|
||||
const char *name = argv[2];
|
||||
|
||||
g_log_set_default_handler(my_log_func, NULL);
|
||||
|
||||
config_global_init();
|
||||
success = config_read_file(path, &error);
|
||||
|
||||
GError *error = NULL;
|
||||
bool success = config_read_file(path, &error);
|
||||
if (!success) {
|
||||
g_printerr("%s:", error->message);
|
||||
g_error_free(error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
value = config_get_string(name, NULL);
|
||||
const char *value = config_get_string(name, NULL);
|
||||
int ret;
|
||||
if (value != NULL) {
|
||||
g_print("%s\n", value);
|
||||
ret = 0;
|
||||
@@ -70,5 +68,5 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
config_global_finish();
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
@@ -106,7 +106,6 @@ int main(int argc, char **argv)
|
||||
struct filter *filter;
|
||||
const struct audio_format *out_audio_format;
|
||||
char buffer[4096];
|
||||
size_t frame_size;
|
||||
|
||||
if (argc < 3 || argc > 4) {
|
||||
g_printerr("Usage: run_filter CONFIG NAME [FORMAT] <IN\n");
|
||||
@@ -162,8 +161,6 @@ int main(int argc, char **argv)
|
||||
g_printerr("audio_format=%s\n",
|
||||
audio_format_to_string(out_audio_format, &af_string));
|
||||
|
||||
frame_size = audio_format_frame_size(&audio_format);
|
||||
|
||||
/* play */
|
||||
|
||||
while (true) {
|
||||
|
Reference in New Issue
Block a user