summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am6
-rw-r--r--PROTOCOL11
-rwxr-xr-xbootstrap.sh2
-rw-r--r--configure.ac153
-rw-r--r--libpulse-browse.pc.in9
-rw-r--r--libpulse-mainloop-glib.pc.in9
-rw-r--r--libpulse-simple.pc.in9
-rw-r--r--libpulse.pc.in9
-rw-r--r--man/pulseaudio.1.xml.in6
-rw-r--r--src/Makefile.am1053
-rw-r--r--src/daemon/caps.c2
-rw-r--r--src/daemon/cmdline.c29
-rw-r--r--src/daemon/cpulimit.c2
-rw-r--r--src/daemon/daemon-conf.c133
-rw-r--r--src/daemon/daemon-conf.h5
-rw-r--r--src/daemon/daemon.conf.in3
-rwxr-xr-xsrc/daemon/default.pa.in9
-rw-r--r--src/daemon/ltdl-bind-now.c110
-rw-r--r--src/daemon/main.c58
-rw-r--r--src/map-file4
-rw-r--r--src/modules/alsa-util.c178
-rw-r--r--src/modules/alsa-util.h5
-rw-r--r--src/modules/bluetooth/ipc.c52
-rw-r--r--src/modules/bluetooth/ipc.h190
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c157
-rw-r--r--src/modules/bluetooth/module-bluetooth-discover.c27
-rw-r--r--src/modules/bluetooth/sbc.c537
-rw-r--r--src/modules/bluetooth/sbc.h4
-rw-r--r--src/modules/bluetooth/sbc_math.h16
-rw-r--r--src/modules/bluetooth/sbc_tables.h250
-rw-r--r--src/modules/module-alsa-sink.c59
-rw-r--r--src/modules/module-alsa-source.c59
-rw-r--r--src/modules/module-always-sink.c28
-rw-r--r--src/modules/module-device-restore.c2
-rw-r--r--src/modules/module-flat-volume.c224
-rw-r--r--src/modules/module-hal-detect.c2
-rw-r--r--src/modules/module-ladspa-sink.c11
-rw-r--r--src/modules/module-pipe-sink.c4
-rw-r--r--src/modules/module-pipe-source.c30
-rw-r--r--src/modules/module-raop-discover.c380
-rw-r--r--src/modules/module-raop-sink.c675
-rw-r--r--src/modules/module-remap-sink.c11
-rw-r--r--src/modules/module-stream-restore.c4
-rw-r--r--src/modules/module-suspend-on-idle.c20
-rw-r--r--src/modules/module-tunnel.c4
-rw-r--r--src/modules/module-zeroconf-discover.c4
-rw-r--r--src/modules/raop/base64.c126
-rw-r--r--src/modules/raop/base64.h34
-rw-r--r--src/modules/raop/raop_client.c561
-rw-r--r--src/modules/raop/raop_client.h46
-rw-r--r--src/modules/rtp/headerlist.c186
-rw-r--r--src/modules/rtp/headerlist.h46
-rw-r--r--src/modules/rtp/module-rtp-send.c7
-rw-r--r--src/modules/rtp/rtsp_client.c542
-rw-r--r--src/modules/rtp/rtsp_client.h73
-rw-r--r--src/pulse/def.h20
-rw-r--r--src/pulse/stream.c12
-rw-r--r--src/pulsecore/cli-command.c104
-rw-r--r--src/pulsecore/conf-parser.c18
-rw-r--r--src/pulsecore/conf-parser.h1
-rw-r--r--src/pulsecore/core.h2
-rw-r--r--src/pulsecore/log.c133
-rw-r--r--src/pulsecore/log.h7
-rw-r--r--src/pulsecore/macro.h12
-rw-r--r--src/pulsecore/module.c10
-rw-r--r--src/pulsecore/module.h6
-rw-r--r--src/pulsecore/once.c11
-rw-r--r--src/pulsecore/once.h2
-rw-r--r--src/pulsecore/pid.c11
-rw-r--r--src/pulsecore/proplist-util.c35
-rw-r--r--src/pulsecore/protocol-native.c44
-rw-r--r--src/pulsecore/resampler.c96
-rw-r--r--src/pulsecore/sample-util.c2
-rw-r--r--src/pulsecore/sample-util.h2
-rw-r--r--src/pulsecore/shm.c8
-rw-r--r--src/pulsecore/sink-input.c144
-rw-r--r--src/pulsecore/sink-input.h22
-rw-r--r--src/pulsecore/sink.c54
-rw-r--r--src/pulsecore/sink.h6
-rw-r--r--src/pulsecore/source-output.c50
-rw-r--r--src/pulsecore/source-output.h11
-rw-r--r--src/pulsecore/source.c31
-rw-r--r--src/pulsecore/source.h1
-rw-r--r--src/pulsecore/time-smoother.c6
-rw-r--r--src/utils/pacat.c4
-rw-r--r--src/utils/padsp.c10
86 files changed, 5451 insertions, 1600 deletions
diff --git a/Makefile.am b/Makefile.am
index facce0df..f4dd9989 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -18,7 +18,7 @@
ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = bootstrap.sh LICENSE GPL LGPL doxygen/Makefile.am doxygen/Makefile.in doxygen/doxygen.conf.in README todo
-SUBDIRS=src doxygen man po
+SUBDIRS = libltdl src doxygen man po
MAINTAINERCLEANFILES =
noinst_DATA =
@@ -56,9 +56,9 @@ fedora-snapshot: dist
dist-hook:
if test -d .git ; then \
- git pull ; \
+ test -z $$SKIP_GIT && git pull ; \
chmod u+w ${distdir}/ChangeLog || true ; \
- git-changelog.perl > ${distdir}/ChangeLog ; \
+ ( git-changelog.perl || echo "git-changelog.perl failed." ) > ${distdir}/ChangeLog 2>&1 ; \
fi
.PHONY: homepage distcleancheck doxygen
diff --git a/PROTOCOL b/PROTOCOL
index 1e2a832f..80a0c331 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -153,3 +153,14 @@ New field for PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR at the end:
early_requests (bool)
+
+
+### v15, implemented by >= 0.9.14
+
+PA_COMMAND_CREATE_PLAYBACK_STREAM
+
+ bool muted at the end
+
+PA_COMMAND_CREATE_PLAYBACK_STREAM, PA_COMMAND_CREATE_RECORD_STREAM:
+
+ bool dont_inhibit_auto_suspend ate the end
diff --git a/bootstrap.sh b/bootstrap.sh
index 5dfcdf20..4eacc5a9 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -57,7 +57,7 @@ else
test "x$LIBTOOLIZE" = "x" && LIBTOOLIZE=libtoolize
intltoolize --copy --force --automake
- "$LIBTOOLIZE" -c --force --ltdl
+ "$LIBTOOLIZE" -c --force --ltdl --recursive
run_versioned aclocal "$VERSION" -I m4
run_versioned autoconf 2.62 -Wall
run_versioned autoheader 2.62
diff --git a/configure.ac b/configure.ac
index 2b91a006..277ae2a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -20,11 +20,11 @@
# along with PulseAudio; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-AC_PREREQ(2.62)
+AC_PREREQ(2.63)
m4_define(PA_MAJOR, [0])
m4_define(PA_MINOR, [9])
-m4_define(PA_MICRO, [13])
+m4_define(PA_MICRO, [14])
AC_INIT([pulseaudio],[PA_MAJOR.PA_MINOR.PA_MICRO],[mzchyfrnhqvb (at) 0pointer (dot) net])
AC_CONFIG_SRCDIR([src/daemon/main.c])
@@ -32,11 +32,12 @@ AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign 1.10 -Wall])
-AC_SUBST(PA_MAJORMINOR, "PA_MAJOR.PA_MINOR")
+AC_SUBST(PA_MAJORMINOR, PA_MAJOR.PA_MINOR)
+AC_SUBST(PA_MAJORMINORMICRO, PA_MAJOR.PA_MINOR.PA_MICRO)
AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/])
AC_SUBST(PA_API_VERSION, 12)
-AC_SUBST(PA_PROTOCOL_VERSION, 14)
+AC_SUBST(PA_PROTOCOL_VERSION, 15)
# The stable ABI for client applications, for the version info x:y:z
# always will hold y=z
@@ -54,11 +55,6 @@ AC_SUBST(LIBPULSE_BROWSE_VERSION_INFO, [1:1:1])
# info x:y:z always will hold y=z
AC_SUBST(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO, [0:4:0])
-# An internally used, ABI-unstable library that contains the
-# PulseAudio core, SONAMEs are bumped on every release, version info
-# suffix will always be 0:0
-AC_SUBST(LIBPULSECORE_VERSION_INFO, [8:0:0])
-
AC_CANONICAL_HOST
AC_DEFINE_UNQUOTED([CANONICAL_HOST], "$host", [Canonical host string.])
@@ -99,7 +95,7 @@ if test "x$M4" = xno ; then
fi
dnl Compiler flags
-DESIRED_FLAGS="-Wall -W -Wextra -pedantic -pipe -Wno-long-long -Wvla -Wno-overlength-strings -Wconversion -Wundef -Wformat -Wlogical-op -Wpacked -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wdeclaration-after-statement -Wfloat-equal -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-noreturn -Wshadow -Wendif-labels -Wpointer-arith -Wcast-align -Wwrite-strings -Wno-unused-parameter -ffast-math"
+DESIRED_FLAGS="-Wall -W -Wextra -pedantic -pipe -Wno-long-long -Wvla -Wno-overlength-strings -Wundef -Wformat -Wlogical-op -Wpacked -Wformat-security -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wdeclaration-after-statement -Wfloat-equal -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wmissing-noreturn -Wshadow -Wendif-labels -Wpointer-arith -Wcast-align -Wwrite-strings -Wno-unused-parameter -ffast-math"
for flag in $DESIRED_FLAGS ; do
CC_CHECK_CFLAGS([$flag], [CFLAGS="$CFLAGS $flag"])
@@ -213,29 +209,10 @@ AS_IF([test "$pulseaudio_cv__Bool" = "yes"], [
])
#### libtool stuff ####
-
-AC_LTDL_ENABLE_INSTALL
-AC_LIBLTDL_INSTALLABLE
-AC_LIBTOOL_DLOPEN
-AC_LIBTOOL_WIN32_DLL
-AC_PROG_LIBTOOL
-AC_SUBST(LTDLINCL)
-AC_SUBST(LIBLTDL)
-AC_CONFIG_SUBDIRS(libltdl)
-
-old_LIBS=$LIBS
-LIBS="$LIBS $LIBLTDL"
-AC_CHECK_FUNCS([lt_dlmutex_register])
-LIBS=$old_LIBS
-AC_CHECK_TYPES([struct lt_user_dlloader, lt_dladvise], , , [#include <ltdl.h>])
-
-if test "x$enable_ltdl_install" = "xno" && test "x$ac_cv_lib_ltdl_lt_dlinit" = "xno" ; then
- AC_MSG_ERROR([[
-
- *** Cannot find the libltdl development files.
- *** Maybe you need to install the libltdl-dev package.
- ]])
-fi
+LT_PREREQ(2.2)
+LT_CONFIG_LTDL_DIR([libltdl])
+LT_INIT([dlopen win32-dll disable-static])
+LTDL_INIT([convenience recursive])
#### Determine build environment ####
@@ -297,6 +274,7 @@ AC_CHECK_HEADERS([sys/ioctl.h])
AC_CHECK_HEADERS([byteswap.h])
AC_CHECK_HEADERS([sys/syscall.h])
AC_CHECK_HEADERS([sys/eventfd.h])
+AC_CHECK_HEADERS([execinfo.h])
#### Typdefs, structures, etc. ####
@@ -307,7 +285,7 @@ AC_TYPE_SIZE_T
AC_CHECK_TYPES(ssize_t, , [AC_DEFINE([ssize_t], [signed long],
[Define ssize_t if it is not done by the standard libs.])])
AC_TYPE_OFF_T
-AC_TYPE_SIGNAL
+
AC_TYPE_UID_T
AC_CHECK_DECLS(environ)
@@ -433,21 +411,52 @@ AC_SUBST(pulselocaledir)
# External libraries #
###################################
+#### pkg-config ####
+
+# Check for pkg-config manually first, as if its not installed the
+# PKG_PROG_PKG_CONFIG macro won't be defined.
+AC_CHECK_PROG(have_pkg_config, pkg-config, yes, no)
+
+if test x"$have_pkg_config" = "xno"; then
+ AC_MSG_ERROR(pkg-config is required to install this program)
+fi
+
+PKG_PROG_PKG_CONFIG
+
#### X11 (optional) ####
-HAVE_X11=0
+AC_ARG_ENABLE([x11],
+ AS_HELP_STRING([--disable-x11],[Disable optional X11 support]),
+ [
+ case "${enableval}" in
+ yes) x11=yes ;;
+ no) x11=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-x11) ;;
+ esac
+ ],
+ [x11=auto])
+
+if test "x${x11}" != xno ; then
+ PKG_CHECK_MODULES(X11, [ x11 ice sm ],
+ HAVE_X11=1,
+ [
+ HAVE_X11=0
+ if test "x$x11" = xyes ; then
+ AC_MSG_ERROR([*** X11 not found])
+ fi
+ ])
+else
+ HAVE_X11=0
+fi
-# The macro tests the host, not the build target
-if test "x$os_is_win32" != "x1" ; then
- AC_PATH_XTRA
- test "x$no_x" != "xyes" && HAVE_X11=1
+if test "x${HAVE_X11}" = x1 ; then
+ AC_DEFINE([HAVE_X11], 1, [Have X11?])
fi
+AC_SUBST(X11_CFLAGS)
+AC_SUBST(X11_LIBS)
AC_SUBST(HAVE_X11)
-AM_CONDITIONAL(HAVE_X11, test "x$HAVE_X11" = "x1")
-if test "x$HAVE_X11" = "x1" ; then
- AC_DEFINE([HAVE_X11], 1, [Have X11])
-fi
+AM_CONDITIONAL([HAVE_X11], [test "x$HAVE_X11" = x1])
#### Capabilities (optional) ####
@@ -472,18 +481,6 @@ fi
AC_CHECK_HEADERS([valgrind/memcheck.h])
-#### pkg-config ####
-
-# Check for pkg-config manually first, as if its not installed the
-# PKG_PROG_PKG_CONFIG macro won't be defined.
-AC_CHECK_PROG(have_pkg_config, pkg-config, yes, no)
-
-if test x"$have_pkg_config" = "xno"; then
- AC_MSG_ERROR(pkg-config is required to install this program)
-fi
-
-PKG_PROG_PKG_CONFIG
-
#### Sound file ####
PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.10 ])
@@ -1000,6 +997,41 @@ AC_SUBST(POLKIT_LIBS)
AC_SUBST(HAVE_POLKIT)
AM_CONDITIONAL([HAVE_POLKIT], [test "x$HAVE_POLKIT" = x1])
+#### OpenSSL support (optional) ####
+
+AC_ARG_ENABLE([openssl],
+ AS_HELP_STRING([--disable-openssl],[Disable OpenSSL support (used for Airtunes/RAOP)]),
+ [
+ case "${enableval}" in
+ yes) openssl=yes ;;
+ no) openssl=no ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-openssl) ;;
+ esac
+ ],
+ [openssl=auto])
+
+if test "x${openssl}" != xno ; then
+
+ PKG_CHECK_MODULES(OPENSSL, [ openssl > 0.9 ],
+ [
+ HAVE_OPENSSL=1
+ AC_DEFINE([HAVE_OPENSSL], 1, [Have OpenSSL])
+ ],
+ [
+ HAVE_OPENSSL=0
+ if test "x$openssl" = xyes ; then
+ AC_MSG_ERROR([*** OpenSSL support not found])
+ fi
+ ])
+else
+ HAVE_OPENSSL=0
+fi
+
+AC_SUBST(OPENSSL_CFLAGS)
+AC_SUBST(OPENSSL_LIBS)
+AC_SUBST(HAVE_OPENSSL)
+AM_CONDITIONAL([HAVE_OPENSSL], [test "x$HAVE_OPENSSL" = x1])
+
### Build and Install man pages ###
AC_ARG_ENABLE(manpages,
AS_HELP_STRING([--disable-manpages],[Disable building and installation of man pages]),
@@ -1099,8 +1131,8 @@ fi
AC_ARG_WITH(
[module-dir],
- AS_HELP_STRING([--with-module-dir],[Directory where to install the modules to (defaults to ${libdir}/pulse-${PA_MAJORMINOR}/modules/]),
- [modlibexecdir=$withval], [modlibexecdir="${libdir}/pulse-${PA_MAJORMINOR}/modules/"])
+ AS_HELP_STRING([--with-module-dir],[Directory where to install the modules to (defaults to ${libdir}/pulse-${PA_MAJORMINORMICRO}/modules/]),
+ [modlibexecdir=$withval], [modlibexecdir="${libdir}/pulse-${PA_MAJORMINORMICRO}/modules/"])
AC_SUBST(modlibexecdir)
@@ -1112,6 +1144,7 @@ AM_CONDITIONAL([FORCE_PREOPEN], [test "x$FORCE_PREOPEN" = "x1"])
AC_CONFIG_FILES([
Makefile
+libltdl/Makefile
src/Makefile
man/Makefile
libpulse.pc
@@ -1201,6 +1234,11 @@ if test "x${HAVE_POLKIT}" = "x1" ; then
ENABLE_POLKIT=yes
fi
+ENABLE_OPENSSL=no
+if test "x${HAVE_OPENSSL}" = "x1" ; then
+ ENABLE_OPENSSL=yes
+fi
+
ENABLE_PER_USER_ESOUND_SOCKET=no
if test "x$per_user_esound_socket" = "x1" ; then
ENABLE_PER_USER_ESOUND_SOCKET=yes
@@ -1232,6 +1270,7 @@ echo "
Enable TCP Wrappers: ${ENABLE_TCPWRAP}
Enable libsamplerate: ${ENABLE_LIBSAMPLERATE}
Enable PolicyKit: ${ENABLE_POLKIT}
+ Enable OpenSSL (for Airtunes): ${ENABLE_OPENSSL}
System User: ${PA_SYSTEM_USER}
System Group: ${PA_SYSTEM_GROUP}
Realtime Group: ${PA_REALTIME_GROUP}
diff --git a/libpulse-browse.pc.in b/libpulse-browse.pc.in
index 66438b2a..54705b3f 100644
--- a/libpulse-browse.pc.in
+++ b/libpulse-browse.pc.in
@@ -1,11 +1,12 @@
prefix=@prefix@
-exec_prefix=${prefix}
+exec_prefix=@exec_prefix@
libdir=@libdir@
-includedir=${prefix}/include
+includedir=@includedir@
Name: libpulse-browse
-Description: PulseAudio Network Browsing API
+Description: PulseAudio Network Browsing Interface
Version: @PACKAGE_VERSION@
Libs: -L${libdir} -lpulse-browse @PTHREAD_LIBS@
-Cflags: -D_REENTRANT -I${includedir}
+Libs.private: -lpulsecommon-@PA_MAJORMINORMICRO@
+Cflags: -I${includedir} -D_REENTRANT
Requires: libpulse
diff --git a/libpulse-mainloop-glib.pc.in b/libpulse-mainloop-glib.pc.in
index 1a8bfa40..45ae47c7 100644
--- a/libpulse-mainloop-glib.pc.in
+++ b/libpulse-mainloop-glib.pc.in
@@ -1,11 +1,12 @@
prefix=@prefix@
-exec_prefix=${prefix}
+exec_prefix=@exec_prefix@
libdir=@libdir@
-includedir=${prefix}/include
+includedir=@includedir@
Name: libpulse-mainloop-glib
-Description: GLIB 2.0 Main Loop Wrapper for PulseAudio
+Description: PulseAudio GLib 2.0 Main Loop Wrapper
Version: @PACKAGE_VERSION@
Libs: -L${libdir} -lpulse-mainloop-glib @PTHREAD_LIBS@
-Cflags: -D_REENTRANT -I${includedir}
+Libs.private: -lpulsecommon-@PA_MAJORMINORMICRO@
+Cflags: -I${includedir} -D_REENTRANT
Requires: libpulse glib-2.0
diff --git a/libpulse-simple.pc.in b/libpulse-simple.pc.in
index 00d1245a..443be289 100644
--- a/libpulse-simple.pc.in
+++ b/libpulse-simple.pc.in
@@ -1,11 +1,12 @@
prefix=@prefix@
-exec_prefix=${prefix}
+exec_prefix=@exec_prefix@
libdir=@libdir@
-includedir=${prefix}/include
+includedir=@includedir@
Name: libpulse-simple
-Description: Simplified Synchronous Client Interface to PulseAudio
+Description: PulseAudio Simplified Synchronous Client Interface
Version: @PACKAGE_VERSION@
Libs: -L${libdir} -lpulse-simple @PTHREAD_LIBS@
-Cflags: -D_REENTRANT -I${includedir}
+Libs.private: -lpulsecommon-@PA_MAJORMINORMICRO@
+Cflags: -I${includedir} -D_REENTRANT
Requires: libpulse
diff --git a/libpulse.pc.in b/libpulse.pc.in
index 2193b2b9..161599e1 100644
--- a/libpulse.pc.in
+++ b/libpulse.pc.in
@@ -1,10 +1,11 @@
prefix=@prefix@
-exec_prefix=${prefix}
+exec_prefix=@exec_prefix@
libdir=@libdir@
-includedir=${prefix}/include
+includedir=@includedir@
Name: libpulse
-Description: Client Interface to PulseAudio
+Description: PulseAudio Client Interface
Version: @PACKAGE_VERSION@
Libs: -L${libdir} -lpulse @PTHREAD_LIBS@
-Cflags: -D_REENTRANT -I${includedir}
+Libs.private: -lpulsecommon-@PA_MAJORMINORMICRO@
+Cflags: -I${includedir} -D_REENTRANT
diff --git a/man/pulseaudio.1.xml.in b/man/pulseaudio.1.xml.in
index df828242..8810e90c 100644
--- a/man/pulseaudio.1.xml.in
+++ b/man/pulseaudio.1.xml.in
@@ -72,7 +72,7 @@ USA.
</option>
<option>
- <p><opt>--dump-resampe-methods</opt></p>
+ <p><opt>--dump-resample-methods</opt></p>
<optdesc><p>List available audio resamplers.</p></optdesc>
</option>
@@ -110,7 +110,9 @@ USA.
<p><opt>--check</opt></p>
<optdesc><p>Return 0 as return code when the PulseAudio daemon
- is already running for the calling user.</p></optdesc>
+ is already running for the calling user, or non-zero
+ otherwise. Produces no output on the console except for errors
+ to stderr.</p></optdesc>
</option>
diff --git a/src/Makefile.am b/src/Makefile.am
index f2771980..608e6786 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -45,28 +45,31 @@ endif
# Compiler/linker flags #
###################################
-AM_CFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/src/modules -I$(top_builddir)/src/modules/rtp -I$(top_builddir)/src/modules/gconf -I$(top_builddir)/src/modules/bluetooth
-AM_CFLAGS += $(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS
-AM_CFLAGS += $(LTDLINCL)
-AM_CFLAGS += $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) $(LIBSPEEX_CFLAGS)
-AM_CFLAGS += -DPA_DLSEARCHPATH=\"$(modlibexecdir)\"
-AM_CFLAGS += -DPA_DEFAULT_CONFIG_DIR=\"$(PA_DEFAULT_CONFIG_DIR)\"
-AM_CFLAGS += -DPA_BINARY=\"$(PA_BINARY)\"
-AM_CFLAGS += -DPA_SYSTEM_RUNTIME_PATH=\"$(PA_SYSTEM_RUNTIME_PATH)\"
-AM_CFLAGS += -DPA_SYSTEM_CONFIG_PATH=\"$(PA_SYSTEM_CONFIG_PATH)\"
-AM_CFLAGS += -DPA_SYSTEM_STATE_PATH=\"$(PA_SYSTEM_STATE_PATH)\"
-AM_CFLAGS += -DAO_REQUIRE_CAS
-AM_CFLAGS += -DPULSE_LOCALEDIR=\"$(pulselocaledir)\"
-AM_CFLAGS += -DPA_MACHINE_ID=\"$(localstatedir)/lib/dbus/machine-id\"
-
-# This cool debug trap works on i386/gcc only
-AM_CFLAGS += '-DDEBUG_TRAP=__asm__("int $$3")'
+AM_CFLAGS = \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/modules \
+ -I$(top_srcdir)/src/modules/rtp \
+ -I$(top_srcdir)/src/modules/gconf \
+ -I$(top_srcdir)/src/modules/bluetooth \
+ -I$(top_srcdir)/src/modules/raop \
+ $(PTHREAD_CFLAGS) -D_POSIX_PTHREAD_SEMANTICS \
+ $(LTDLINCL) \
+ $(LIBSAMPLERATE_CFLAGS) \
+ $(LIBSNDFILE_CFLAGS) \
+ $(LIBSPEEX_CFLAGS) \
+ -DPA_DLSEARCHPATH=\"$(modlibexecdir)\" \
+ -DPA_DEFAULT_CONFIG_DIR=\"$(PA_DEFAULT_CONFIG_DIR)\" \
+ -DPA_BINARY=\"$(PA_BINARY)\" \
+ -DPA_SYSTEM_RUNTIME_PATH=\"$(PA_SYSTEM_RUNTIME_PATH)\" \
+ -DPA_SYSTEM_CONFIG_PATH=\"$(PA_SYSTEM_CONFIG_PATH)\" \
+ -DPA_SYSTEM_STATE_PATH=\"$(PA_SYSTEM_STATE_PATH)\" \
+ -DAO_REQUIRE_CAS \
+ -DPULSE_LOCALEDIR=\"$(pulselocaledir)\" \
+ -DPA_MACHINE_ID=\"$(localstatedir)/lib/dbus/machine-id\"
AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS)
AM_LDADD = $(PTHREAD_LIBS) $(INTLLIBS)
-
-# Only required on some platforms but defined for all to avoid errors
-AM_LDFLAGS = -Wl,-no-undefined -Wl,--gc-sections
+AM_LDFLAGS = -Wl,-z,nodelete
if STATIC_BINS
BINLDFLAGS = -static
@@ -77,17 +80,7 @@ AM_LDFLAGS+=-Wl,--export-all-symbols
WINSOCK_LIBS=-lwsock32 -lws2_32 -lwininet
endif
-if OS_IS_WIN32
-PA_THREAD_OBJS = \
- pulsecore/mutex-win32.c pulsecore/mutex.h \
- pulsecore/thread-win32.c pulsecore/thread.h \
- pulsecore/semaphore-win32.c pulsecore/semaphore.h
-else
-PA_THREAD_OBJS = \
- pulsecore/mutex-posix.c pulsecore/mutex.h \
- pulsecore/thread-posix.c pulsecore/thread.h \
- pulsecore/semaphore-posix.c pulsecore/semaphore.h
-endif
+MODULE_LDFLAGS = -module -disable-static -avoid-version
###################################
# Extra files #
@@ -139,10 +132,9 @@ pulseaudio_SOURCES = \
daemon/main.c
pulseaudio_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(CAP_CFLAGS) $(LIBOIL_CFLAGS) $(DBUS_CFLAGS)
-pulseaudio_CPPFLAGS = $(AM_CPPFLAGS)
-pulseaudio_LDADD = $(AM_LDADD) libpulsecore.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS)
+pulseaudio_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(CAP_LIBS) $(LIBOIL_LIBS) $(DBUS_LIBS)
# This is needed because automake doesn't properly expand the foreach below
-pulseaudio_DEPENDENCIES = libpulsecore.la $(PREOPEN_LIBS)
+pulseaudio_DEPENDENCIES = libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la $(PREOPEN_LIBS)
if PREOPEN_MODS
PREOPEN_LIBS = $(PREOPEN_MODS)
@@ -157,14 +149,11 @@ pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -dlopen force $(foreach f,$(PRE
endif
if HAVE_POLKIT
-
policy_DATA = daemon/org.pulseaudio.policy
pulseaudio_SOURCES += daemon/polkit.c daemon/polkit.h
pulseaudio_CFLAGS += $(POLKIT_CFLAGS)
pulseaudio_LDADD += $(POLKIT_LIBS)
-
-
endif
###################################
@@ -201,24 +190,24 @@ paplay_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
paplay_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
paplay_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-pactl_SOURCES = utils/pactl.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/rtclock.c pulsecore/rtclock.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
-pactl_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
+pactl_SOURCES = utils/pactl.c
+pactl_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(LIBSNDFILE_LIBS)
pactl_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
pactl_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-pasuspender_SOURCES = utils/pasuspender.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/rtclock.c pulsecore/rtclock.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
-pasuspender_LDADD = $(AM_LDADD) libpulse.la $(LIBSNDFILE_LIBS)
+pasuspender_SOURCES = utils/pasuspender.c
+pasuspender_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(LIBSNDFILE_LIBS)
pasuspender_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
pasuspender_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-pacmd_SOURCES = utils/pacmd.c pulsecore/pid.c pulsecore/pid.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/rtclock.c pulsecore/rtclock.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
+pacmd_SOURCES = utils/pacmd.c
pacmd_CFLAGS = $(AM_CFLAGS)
-pacmd_LDADD = $(AM_LDADD) libpulse.la
+pacmd_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la
pacmd_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-pax11publish_SOURCES = utils/pax11publish.c pulsecore/x11prop.c pulsecore/x11prop.h pulse/client-conf.c pulse/client-conf.h pulsecore/authkey.h pulsecore/authkey.c pulsecore/random.h pulsecore/random.c pulsecore/conf-parser.c pulsecore/rtclock.c pulsecore/rtclock.h pulsecore/conf-parser.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
-pax11publish_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
-pax11publish_LDADD = $(AM_LDADD) libpulse.la $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
+pax11publish_SOURCES = utils/pax11publish.c
+pax11publish_CFLAGS = $(AM_CFLAGS) $(X11_CFLAGS)
+pax11publish_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(X11_LIBS)
pax11publish_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
pabrowse_SOURCES = utils/pabrowse.c
@@ -230,6 +219,35 @@ pabrowse_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
# Test programs #
###################################
+# missing: mcalign-test flist-test pacat-simple parec-simple sync-playback rtstutter stripnul interpol-test thread-test
+
+TESTS = \
+ mainloop-test \
+ strlist-test \
+ close-test \
+ voltest \
+ memblockq-test \
+ channelmap-test \
+ thread-mainloop-test \
+ utf8-test \
+ get-binary-name-test \
+ ipacl-test \
+ hook-list-test \
+ memblock-test \
+ asyncq-test \
+ asyncmsgq-test \
+ queue-test \
+ rtpoll-test \
+ sig2str-test \
+ resampler-test \
+ smoother-test \
+ mix-test \
+ remix-test \
+ envelope-test \
+ proplist-test \
+ lock-autospawn-test \
+ prioq-test
+
noinst_PROGRAMS = \
mainloop-test \
mcalign-test \
@@ -267,12 +285,17 @@ noinst_PROGRAMS = \
prioq-test
if HAVE_SIGXCPU
+#TESTS += \
+# cpulimit-test \
+# cpulimit-test2
noinst_PROGRAMS += \
cpulimit-test \
cpulimit-test2
endif
if HAVE_GLIB20
+TESTS += \
+ mainloop-test-glib
noinst_PROGRAMS += \
mainloop-test-glib
endif
@@ -284,12 +307,12 @@ mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
thread_mainloop_test_SOURCES = tests/thread-mainloop-test.c
thread_mainloop_test_CFLAGS = $(AM_CFLAGS)
-thread_mainloop_test_LDADD = $(AM_LDADD) libpulsecore.la libpulse.la
+thread_mainloop_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la
thread_mainloop_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
utf8_test_SOURCES = tests/utf8-test.c
utf8_test_CFLAGS = $(AM_CFLAGS)
-utf8_test_LDADD = $(AM_LDADD) libpulsecore.la
+utf8_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
utf8_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
get_binary_name_test_SOURCES = tests/get-binary-name-test.c
@@ -297,56 +320,54 @@ get_binary_name_test_CFLAGS = $(AM_CFLAGS)
get_binary_name_test_LDADD = $(AM_LDADD) libpulse.la
get_binary_name_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
-ipacl_test_SOURCES = tests/ipacl-test.c \
- pulsecore/ipacl.c pulsecore/ipacl.h \
- pulsecore/inet_pton.c pulsecore/inet_pton.h
+ipacl_test_SOURCES = tests/ipacl-test.c
ipacl_test_CFLAGS = $(AM_CFLAGS)
-ipacl_test_LDADD = $(AM_LDADD) libpulsecore.la
+ipacl_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
ipacl_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
hook_list_test_SOURCES = tests/hook-list-test.c
hook_list_test_CFLAGS = $(AM_CFLAGS)
-hook_list_test_LDADD = $(AM_LDADD) libpulsecore.la
+hook_list_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
hook_list_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
memblock_test_SOURCES = tests/memblock-test.c
memblock_test_CFLAGS = $(AM_CFLAGS)
-memblock_test_LDADD = $(AM_LDADD) libpulsecore.la
+memblock_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
memblock_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
thread_test_SOURCES = tests/thread-test.c
thread_test_CFLAGS = $(AM_CFLAGS)
-thread_test_LDADD = $(AM_LDADD) libpulsecore.la
+thread_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
thread_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
flist_test_SOURCES = tests/flist-test.c
flist_test_CFLAGS = $(AM_CFLAGS)
-flist_test_LDADD = $(AM_LDADD) libpulsecore.la
+flist_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
flist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
asyncq_test_SOURCES = tests/asyncq-test.c
asyncq_test_CFLAGS = $(AM_CFLAGS)
-asyncq_test_LDADD = $(AM_LDADD) libpulsecore.la
+asyncq_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
asyncq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
asyncmsgq_test_SOURCES = tests/asyncmsgq-test.c
asyncmsgq_test_CFLAGS = $(AM_CFLAGS)
-asyncmsgq_test_LDADD = $(AM_LDADD) libpulsecore.la
+asyncmsgq_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
asyncmsgq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
queue_test_SOURCES = tests/queue-test.c
queue_test_CFLAGS = $(AM_CFLAGS)
-queue_test_LDADD = $(AM_LDADD) libpulsecore.la
+queue_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
queue_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
rtpoll_test_SOURCES = tests/rtpoll-test.c
rtpoll_test_CFLAGS = $(AM_CFLAGS)
-rtpoll_test_LDADD = $(AM_LDADD) libpulsecore.la
+rtpoll_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
rtpoll_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
mcalign_test_SOURCES = tests/mcalign-test.c
mcalign_test_CFLAGS = $(AM_CFLAGS)
-mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la
+mcalign_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
mcalign_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
pacat_simple_SOURCES = tests/pacat-simple.c
@@ -361,12 +382,12 @@ parec_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
strlist_test_SOURCES = tests/strlist-test.c
strlist_test_CFLAGS = $(AM_CFLAGS)
-strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la libstrlist.la
+strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
strlist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
close_test_SOURCES = tests/close-test.c
close_test_CFLAGS = $(AM_CFLAGS)
-close_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la libstrlist.la
+close_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
close_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
voltest_SOURCES = tests/voltest.c
@@ -381,12 +402,12 @@ channelmap_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
cpulimit_test_SOURCES = tests/cpulimit-test.c daemon/cpulimit.c daemon/cpulimit.h
cpulimit_test_CFLAGS = $(AM_CFLAGS)
-cpulimit_test_LDADD = $(AM_LDADD) libpulsecore.la
+cpulimit_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
cpulimit_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
cpulimit_test2_SOURCES = tests/cpulimit-test.c daemon/cpulimit.c daemon/cpulimit.h
cpulimit_test2_CFLAGS = $(AM_CFLAGS) -DTEST2
-cpulimit_test2_LDADD = $(AM_LDADD) libpulsecore.la
+cpulimit_test2_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
cpulimit_test2_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
mainloop_test_glib_SOURCES = $(mainloop_test_SOURCES)
@@ -396,7 +417,7 @@ mainloop_test_glib_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
memblockq_test_SOURCES = tests/memblockq-test.c
memblockq_test_CFLAGS = $(AM_CFLAGS)
-memblockq_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore.la
+memblockq_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
memblockq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
sync_playback_SOURCES = tests/sync-playback.c
@@ -405,66 +426,163 @@ sync_playback_CFLAGS = $(AM_CFLAGS)
sync_playback_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
interpol_test_SOURCES = tests/interpol-test.c
-interpol_test_LDADD = $(AM_LDADD) libpulse.la libpulsecore.la
+interpol_test_LDADD = $(AM_LDADD) libpulse.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
interpol_test_CFLAGS = $(AM_CFLAGS)
interpol_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
sig2str_test_SOURCES = tests/sig2str-test.c
-sig2str_test_LDADD = $(AM_LDADD) libpulsecore.la
+sig2str_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
sig2str_test_CFLAGS = $(AM_CFLAGS)
sig2str_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
resampler_test_SOURCES = tests/resampler-test.c
-resampler_test_LDADD = $(AM_LDADD) libpulsecore.la
+resampler_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
resampler_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
resampler_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
mix_test_SOURCES = tests/mix-test.c
-mix_test_LDADD = $(AM_LDADD) libpulsecore.la
+mix_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
mix_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
mix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
remix_test_SOURCES = tests/remix-test.c
-remix_test_LDADD = $(AM_LDADD) libpulsecore.la
+remix_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
remix_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
remix_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
smoother_test_SOURCES = tests/smoother-test.c
-smoother_test_LDADD = $(AM_LDADD) libpulsecore.la
+smoother_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
smoother_test_CFLAGS = $(AM_CFLAGS)
smoother_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
envelope_test_SOURCES = tests/envelope-test.c
-envelope_test_LDADD = $(AM_LDADD) libpulsecore.la
+envelope_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
envelope_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
envelope_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
proplist_test_SOURCES = tests/proplist-test.c
-proplist_test_LDADD = $(AM_LDADD) libpulsecore.la
+proplist_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
proplist_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
proplist_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
rtstutter_SOURCES = tests/rtstutter.c
-rtstutter_LDADD = $(AM_LDADD) libpulsecore.la
+rtstutter_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
rtstutter_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
rtstutter_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
stripnul_SOURCES = tests/stripnul.c
-stripnul_LDADD = $(AM_LDADD) libpulsecore.la
+stripnul_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
stripnul_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
stripnul_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
lock_autospawn_test_SOURCES = tests/lock-autospawn-test.c
-lock_autospawn_test_LDADD = $(AM_LDADD) libpulsecore.la
+lock_autospawn_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
lock_autospawn_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
lock_autospawn_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
prioq_test_SOURCES = tests/prioq-test.c
-prioq_test_LDADD = $(AM_LDADD) libpulsecore.la
+prioq_test_LDADD = $(AM_LDADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la
prioq_test_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
prioq_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(LIBOIL_LIBS)
###################################
+# Common library #
+###################################
+
+lib_LTLIBRARIES = \
+ libpulsecommon-@PA_MAJORMINORMICRO@.la
+
+libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES = \
+ pulse/client-conf.c pulse/client-conf.h \
+ pulse/i18n.c pulse/i18n.h \
+ pulsecore/atomic.h \
+ pulsecore/authkey.c pulsecore/authkey.h \
+ pulsecore/conf-parser.c pulsecore/conf-parser.h \
+ pulsecore/core-error.c pulsecore/core-error.h \
+ pulsecore/core-util.c pulsecore/core-util.h \
+ pulsecore/creds.h \
+ pulsecore/dynarray.c pulsecore/dynarray.h \
+ pulsecore/endianmacros.h \
+ pulsecore/flist.c pulsecore/flist.h \
+ pulsecore/hashmap.c pulsecore/hashmap.h \
+ pulsecore/idxset.c pulsecore/idxset.h \
+ pulsecore/inet_ntop.c pulsecore/inet_ntop.h \
+ pulsecore/inet_pton.c pulsecore/inet_pton.h \
+ pulsecore/iochannel.c pulsecore/iochannel.h \
+ pulsecore/ioline.c pulsecore/ioline.h \
+ pulsecore/ipacl.h pulsecore/ipacl.c \
+ pulsecore/llist.h \
+ pulsecore/lock-autospawn.c pulsecore/lock-autospawn.h \
+ pulsecore/log.c pulsecore/log.h \
+ pulsecore/macro.h \
+ pulsecore/mcalign.c pulsecore/mcalign.h \
+ pulsecore/memblock.c pulsecore/memblock.h \
+ pulsecore/memblockq.c pulsecore/memblockq.h \
+ pulsecore/memchunk.c pulsecore/memchunk.h \
+ pulsecore/native-common.h \
+ pulsecore/once.c pulsecore/once.h \
+ pulsecore/packet.c pulsecore/packet.h \
+ pulsecore/parseaddr.c pulsecore/parseaddr.h \
+ pulsecore/pdispatch.c pulsecore/pdispatch.h \
+ pulsecore/pid.c pulsecore/pid.h \
+ pulsecore/pipe.c pulsecore/pipe.h \
+ pulsecore/poll.c pulsecore/poll.h \
+ pulsecore/prioq.c pulsecore/prioq.h \
+ pulsecore/proplist-util.c pulsecore/proplist-util.h \
+ pulsecore/pstream-util.c pulsecore/pstream-util.h \
+ pulsecore/pstream.c pulsecore/pstream.h \
+ pulsecore/queue.c pulsecore/queue.h \
+ pulsecore/random.c pulsecore/random.h \
+ pulsecore/refcnt.h \
+ pulsecore/rtclock.c pulsecore/rtclock.h \
+ pulsecore/shm.c pulsecore/shm.h \
+ pulsecore/socket-client.c pulsecore/socket-client.h \
+ pulsecore/socket-server.c pulsecore/socket-server.h \
+ pulsecore/socket-util.c pulsecore/socket-util.h \
+ pulsecore/strbuf.c pulsecore/strbuf.h \
+ pulsecore/strlist.c pulsecore/strlist.h \
+ pulsecore/tagstruct.c pulsecore/tagstruct.h \
+ pulsecore/time-smoother.c pulsecore/time-smoother.h \
+ pulsecore/tokenizer.c pulsecore/tokenizer.h \
+ pulsecore/winsock.h
+
+libpulsecommon_@PA_MAJORMINORMICRO@_la_CFLAGS = $(AM_CFLAGS)
+libpulsecommon_@PA_MAJORMINORMICRO@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
+libpulsecommon_@PA_MAJORMINORMICRO@_la_LIBADD = $(AM_LIBADD) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV)
+
+## Please note that libpulsecommon implicitly also depends on<
+## libpulse! i.e. we have a cyclic dependancy here. Which is intended
+## since libpulse only includes stable, official APIs, while
+## libpulsecommon only includes unofficial APIs.
+
+if OS_IS_WIN32
+libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += \
+ pulsecore/mutex-win32.c pulsecore/mutex.h \
+ pulsecore/thread-win32.c pulsecore/thread.h \
+ pulsecore/semaphore-win32.c pulsecore/semaphore.h
+else
+libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += \
+ pulsecore/mutex-posix.c pulsecore/mutex.h \
+ pulsecore/thread-posix.c pulsecore/thread.h \
+ pulsecore/semaphore-posix.c pulsecore/semaphore.h
+endif
+
+if HAVE_X11
+libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/x11prop.c pulsecore/x11prop.h
+libpulsecommon_@PA_MAJORMINORMICRO@_la_CFLAGS += $(X11_CFLAGS)
+libpulsecommon_@PA_MAJORMINORMICRO@_la_LDFLAGS += $(X11_LIBS)
+endif
+
+if HAVE_LIBASYNCNS
+libpulsecommon_@PA_MAJORMINORMICRO@_la_CFLAGS += $(LIBASYNCNS_CFLAGS)
+libpulsecommon_@PA_MAJORMINORMICRO@_la_LIBADD += $(LIBASYNCNS_LIBS)
+endif
+
+if OS_IS_WIN32
+libpulsecommon_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/dllmain.c
+endif
+
+###################################
# Client library #
###################################
@@ -474,11 +592,14 @@ pulseinclude_HEADERS = \
pulse/context.h \
pulse/def.h \
pulse/error.h \
+ pulse/ext-stream-restore.h \
+ pulse/gccmacro.h \
pulse/introspect.h \
- pulse/mainloop.h \
pulse/mainloop-api.h \
pulse/mainloop-signal.h \
+ pulse/mainloop.h \
pulse/operation.h \
+ pulse/proplist.h \
pulse/pulseaudio.h \
pulse/sample.h \
pulse/scache.h \
@@ -491,31 +612,24 @@ pulseinclude_HEADERS = \
pulse/util.h \
pulse/version.h \
pulse/volume.h \
- pulse/xmalloc.h \
- pulse/proplist.h \
- pulse/gccmacro.h \
- pulse/ext-stream-restore.h
-
-if HAVE_AVAHI
-pulseinclude_HEADERS += \
- pulse/browser.h
-endif
-
-if HAVE_GLIB20
-pulseinclude_HEADERS += \
- pulse/glib-mainloop.h
-endif
+ pulse/xmalloc.h
-lib_LTLIBRARIES = \
+lib_LTLIBRARIES += \
libpulse.la \
libpulse-simple.la
if HAVE_AVAHI
+pulseinclude_HEADERS += \
+ pulse/browser.h
+
lib_LTLIBRARIES += \
libpulse-browse.la
endif
if HAVE_GLIB20
+pulseinclude_HEADERS += \
+ pulse/glib-mainloop.h
+
lib_LTLIBRARIES += \
libpulse-mainloop-glib.la
endif
@@ -524,16 +638,18 @@ endif
libpulse_la_SOURCES = \
pulse/cdecl.h \
pulse/channelmap.c pulse/channelmap.h \
- pulse/client-conf.c pulse/client-conf.h \
pulse/context.c pulse/context.h \
pulse/def.h \
pulse/error.c pulse/error.h \
+ pulse/ext-stream-restore.c pulse/ext-stream-restore.h \
+ pulse/gccmacro.h \
pulse/internal.h \
pulse/introspect.c pulse/introspect.h \
- pulse/mainloop.c pulse/mainloop.h \
pulse/mainloop-api.c pulse/mainloop-api.h \
pulse/mainloop-signal.c pulse/mainloop-signal.h \
+ pulse/mainloop.c pulse/mainloop.h \
pulse/operation.c pulse/operation.h \
+ pulse/proplist.c pulse/proplist.h \
pulse/pulseaudio.h \
pulse/sample.c pulse/sample.h \
pulse/scache.c pulse/scache.h \
@@ -544,343 +660,121 @@ libpulse_la_SOURCES = \
pulse/utf8.c pulse/utf8.h \
pulse/util.c pulse/util.h \
pulse/volume.c pulse/volume.h \
- pulse/xmalloc.c pulse/xmalloc.h \
- pulse/proplist.c pulse/proplist.h \
- pulse/ext-stream-restore.c pulse/ext-stream-restore.h \
- pulse/i18n.c pulse/i18n.h
-
-# Internal stuff that is shared with libpulsecore
-libpulse_la_SOURCES += \
- pulsecore/authkey.c pulsecore/authkey.h \
- pulsecore/conf-parser.c pulsecore/conf-parser.h \
- pulsecore/core-util.c pulsecore/core-util.h \
- pulsecore/dynarray.c pulsecore/dynarray.h \
- pulsecore/hashmap.c pulsecore/hashmap.h \
- pulsecore/idxset.c pulsecore/idxset.h \
- pulsecore/inet_ntop.c pulsecore/inet_ntop.h \
- pulsecore/iochannel.c pulsecore/iochannel.h \
- pulsecore/llist.h \
- pulsecore/log.c pulsecore/log.h \
- pulsecore/mcalign.c pulsecore/mcalign.h \
- pulsecore/memblock.c pulsecore/memblock.h \
- pulsecore/memblockq.c pulsecore/memblockq.h \
- pulsecore/memchunk.c pulsecore/memchunk.h \
- pulsecore/native-common.h \
- pulsecore/packet.c pulsecore/packet.h \
- pulsecore/parseaddr.c pulsecore/parseaddr.h \
- pulsecore/pdispatch.c pulsecore/pdispatch.h \
- pulsecore/pipe.c pulsecore/pipe.h \
- pulsecore/poll.c pulsecore/poll.h \
- pulsecore/pstream.c pulsecore/pstream.h \
- pulsecore/pstream-util.c pulsecore/pstream-util.h \
- pulsecore/queue.c pulsecore/queue.h \
- pulsecore/random.c pulsecore/random.h \
- pulsecore/socket-client.c pulsecore/socket-client.h \
- pulsecore/socket-util.c pulsecore/socket-util.h \
- pulsecore/strbuf.c pulsecore/strbuf.h \
- pulsecore/strlist.c pulsecore/strlist.h \
- pulsecore/tagstruct.c pulsecore/tagstruct.h \
- pulsecore/core-error.c pulsecore/core-error.h \
- pulsecore/winsock.h pulsecore/creds.h \
- pulsecore/shm.c pulsecore/shm.h \
- pulsecore/flist.c pulsecore/flist.h \
- pulsecore/object.c pulsecore/object.h \
- pulsecore/msgobject.c pulsecore/msgobject.h \
- pulsecore/once.c pulsecore/once.h \
- pulsecore/rtclock.c pulsecore/rtclock.h \
- pulsecore/time-smoother.c pulsecore/time-smoother.h \
- pulsecore/proplist-util.c pulsecore/proplist-util.h \
- $(PA_THREAD_OBJS)
-
-if OS_IS_WIN32
-libpulse_la_SOURCES += \
- pulsecore/dllmain.c
-endif
-
-if HAVE_X11
-libpulse_la_SOURCES += \
- pulse/client-conf-x11.c pulse/client-conf-x11.h \
- pulsecore/x11prop.c pulsecore/x11prop.h
-endif
+ pulse/xmalloc.c pulse/xmalloc.h
libpulse_la_CFLAGS = $(AM_CFLAGS)
-libpulse_la_LDFLAGS = -version-info $(LIBPULSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
-libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV)
+libpulse_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
+libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) libpulsecommon-@PA_MAJORMINORMICRO@.la
if HAVE_X11
-libpulse_la_CFLAGS += $(X_CFLAGS)
-libpulse_la_LDFLAGS += $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
+libpulse_la_SOURCES += pulse/client-conf-x11.c pulse/client-conf-x11.h
+libpulse_la_CFLAGS += $(X11_CFLAGS)
+libpulse_la_LDFLAGS += $(X11_LIBS)
endif
-if HAVE_LIBASYNCNS
-libpulse_la_CFLAGS += $(LIBASYNCNS_CFLAGS)
-libpulse_la_LIBADD += $(LIBASYNCNS_LIBS)
-endif
-
-libpulse_simple_la_SOURCES = \
- pulse/simple.c pulse/simple.h \
- pulsecore/log.c pulsecore/log.h \
- pulsecore/core-util.c pulsecore/core-util.h \
- pulsecore/core-error.c pulsecore/core-error.h \
- pulsecore/once.c pulsecore/once.h \
- pulsecore/rtclock.c pulsecore/rtclock.h \
- $(PA_THREAD_OBJS)
-
+libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h
libpulse_simple_la_CFLAGS = $(AM_CFLAGS)
-libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la
-libpulse_simple_la_LDFLAGS = -version-info $(LIBPULSE_SIMPLE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
+libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la
+libpulse_simple_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_SIMPLE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
-libpulse_browse_la_SOURCES = pulse/browser.c pulse/browser.h pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/rtclock.c pulsecore/rtclock.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
+libpulse_browse_la_SOURCES = pulse/browser.c pulse/browser.h pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h
libpulse_browse_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
-libpulse_browse_la_LIBADD = $(AM_LIBADD) libpulse.la $(AVAHI_LIBS)
-libpulse_browse_la_LDFLAGS = -version-info $(LIBPULSE_BROWSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
+libpulse_browse_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(AVAHI_LIBS)
+libpulse_browse_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_BROWSE_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
-libpulse_mainloop_glib_la_SOURCES = \
- pulse/glib-mainloop.h pulse/glib-mainloop.c \
- pulsecore/log.c pulsecore/log.h \
- pulsecore/rtclock.c pulsecore/rtclock.h \
- pulsecore/core-util.c pulsecore/core-util.h \
- pulsecore/core-error.c pulsecore/core-error.h \
- pulsecore/once.c pulsecore/once.h \
- $(PA_THREAD_OBJS)
+libpulse_mainloop_glib_la_SOURCES = pulse/glib-mainloop.h pulse/glib-mainloop.c
libpulse_mainloop_glib_la_CFLAGS = $(AM_CFLAGS) $(GLIB20_CFLAGS)
-libpulse_mainloop_glib_la_LIBADD = $(AM_LIBADD) libpulse.la $(GLIB20_LIBS)
-libpulse_mainloop_glib_la_LDFLAGS = -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
+libpulse_mainloop_glib_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la $(GLIB20_LIBS)
+libpulse_mainloop_glib_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBPULSE_MAINLOOP_GLIB_VERSION_INFO) -Wl,-version-script=$(srcdir)/map-file
###################################
# OSS emulation #
###################################
if HAVE_OSS
-
lib_LTLIBRARIES += libpulsedsp.la
-
bin_SCRIPTS += utils/padsp
-
endif
-libpulsedsp_la_SOURCES = utils/padsp.c pulsecore/core-util.c pulsecore/core-util.h pulsecore/core-error.c pulsecore/core-error.h pulsecore/log.c pulsecore/log.h pulsecore/rtclock.c pulsecore/rtclock.h pulsecore/once.c pulsecore/once.h $(PA_THREAD_OBJS)
+libpulsedsp_la_SOURCES = utils/padsp.c
libpulsedsp_la_CFLAGS = $(AM_CFLAGS)
-libpulsedsp_la_LIBADD = $(AM_LIBADD) libpulse.la
-libpulsedsp_la_LDFLAGS = -avoid-version
-
-###################################
-# ffmpeg resampler #
-###################################
-
-noinst_LTLIBRARIES = libffmpeg-resampler.la
-
-libffmpeg_resampler_la_CPPFLAGS = $(AM_CPPFLAGS)
-libffmpeg_resampler_la_SOURCES = pulsecore/ffmpeg/resample2.c pulsecore/ffmpeg/avcodec.h pulsecore/ffmpeg/dsputil.h
+libpulsedsp_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon-@PA_MAJORMINORMICRO@.la
+libpulsedsp_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version -disable-static
###################################
# Daemon core library #
###################################
-#pulsecoreinclude_HEADERS =
-noinst_HEADERS = \
- pulsecore/autoload.h \
- pulsecore/atomic.h \
- pulsecore/cli-command.h \
- pulsecore/cli-text.h \
- pulsecore/client.h \
- pulsecore/core.h \
- pulsecore/core-scache.h \
- pulsecore/core-subscribe.h \
- pulsecore/conf-parser.h \
- pulsecore/core-util.h \
- pulsecore/dynarray.h \
- pulsecore/g711.h \
- pulsecore/hashmap.h \
- pulsecore/idxset.h \
- pulsecore/log.h \
- pulsecore/mcalign.h \
- pulsecore/memblock.h \
- pulsecore/memblockq.h \
- pulsecore/memchunk.h \
- pulsecore/modargs.h \
- pulsecore/modinfo.h \
- pulsecore/module.h \
- pulsecore/namereg.h \
- pulsecore/pid.h \
- pulsecore/play-memchunk.h \
- pulsecore/play-memblockq.h \
- pulsecore/shared.h \
- pulsecore/queue.h \
- pulsecore/random.h \
- pulsecore/resampler.h \
- pulsecore/sample-util.h \
- pulsecore/sconv.h \
- pulsecore/sink.h \
- pulsecore/sink-input.h \
- pulsecore/sioman.h \
- pulsecore/sound-file.h \
- pulsecore/sound-file-stream.h \
- pulsecore/source.h \
- pulsecore/source-output.h \
- pulsecore/strbuf.h \
- pulsecore/tokenizer.h \
- pulsecore/creds.h \
- pulsecore/shm.h \
- pulsecore/llist.h \
- pulsecore/refcnt.h \
- pulsecore/mutex.h \
- pulsecore/thread.h \
- pulsecore/semaphore.h \
- pulsecore/once.h
-
-lib_LTLIBRARIES += libpulsecore.la
-
-# Some public stuff is used even in the core
-libpulsecore_la_SOURCES = \
- pulse/channelmap.c pulse/channelmap.h \
- pulse/error.c pulse/error.h \
- pulse/mainloop.c pulse/mainloop.h \
- pulse/mainloop-api.c pulse/mainloop-api.h \
- pulse/mainloop-signal.c pulse/mainloop-signal.h \
- pulse/sample.c pulse/sample.h \
- pulse/timeval.c pulse/timeval.h \
- pulse/utf8.c pulse/utf8.h \
- pulse/util.c pulse/util.h \
- pulse/volume.c pulse/volume.h \
- pulse/xmalloc.c pulse/xmalloc.h \
- pulse/proplist.c pulse/proplist.h \
- pulse/i18n.c pulse/i18n.h
+lib_LTLIBRARIES += libpulsecore-@PA_MAJORMINORMICRO@.la
-# Pure core stuff (some are shared in libpulse though).
-libpulsecore_la_SOURCES += \
+# Pure core stuff
+libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES = \
+ pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h \
+ pulsecore/asyncq.c pulsecore/asyncq.h \
+ pulsecore/auth-cookie.c pulsecore/auth-cookie.h \
pulsecore/autoload.c pulsecore/autoload.h \
pulsecore/cli-command.c pulsecore/cli-command.h \
pulsecore/cli-text.c pulsecore/cli-text.h \
pulsecore/client.c pulsecore/client.h \
- pulsecore/conf-parser.c pulsecore/conf-parser.h \
- pulsecore/core.c pulsecore/core.h \
pulsecore/core-scache.c pulsecore/core-scache.h \
pulsecore/core-subscribe.c pulsecore/core-subscribe.h \
- pulsecore/core-util.c pulsecore/core-util.h \
- pulsecore/dynarray.c pulsecore/dynarray.h \
- pulsecore/endianmacros.h \
+ pulsecore/core.c pulsecore/core.h \
+ pulsecore/envelope.c pulsecore/envelope.h \
+ pulsecore/fdsem.c pulsecore/fdsem.h \
+ pulsecore/ffmpeg/resample2.c pulsecore/ffmpeg/avcodec.h pulsecore/ffmpeg/dsputil.h \
pulsecore/g711.c pulsecore/g711.h \
- pulsecore/hashmap.c pulsecore/hashmap.h \
- pulsecore/idxset.c pulsecore/idxset.h \
- pulsecore/prioq.c pulsecore/prioq.h \
- pulsecore/log.c pulsecore/log.h \
- pulsecore/mcalign.c pulsecore/mcalign.h \
- pulsecore/memblock.c pulsecore/memblock.h \
- pulsecore/memblockq.c pulsecore/memblockq.h \
- pulsecore/memchunk.c pulsecore/memchunk.h \
+ pulsecore/hook-list.c pulsecore/hook-list.h \
+ pulsecore/ltdl-helper.c pulsecore/ltdl-helper.h \
pulsecore/modargs.c pulsecore/modargs.h \
pulsecore/modinfo.c pulsecore/modinfo.h \
- pulsecore/ltdl-helper.c pulsecore/ltdl-helper.h \
pulsecore/module.c pulsecore/module.h \
+ pulsecore/msgobject.c pulsecore/msgobject.h \
pulsecore/namereg.c pulsecore/namereg.h \
- pulsecore/pid.c pulsecore/pid.h \
- pulsecore/pipe.c pulsecore/pipe.h \
- pulsecore/play-memchunk.c pulsecore/play-memchunk.h \
+ pulsecore/object.c pulsecore/object.h \
pulsecore/play-memblockq.c pulsecore/play-memblockq.h \
- pulsecore/poll.c pulsecore/poll.h \
- pulsecore/shared.c pulsecore/shared.h \
- pulsecore/queue.c pulsecore/queue.h \
- pulsecore/random.c pulsecore/random.h \
+ pulsecore/play-memchunk.c pulsecore/play-memchunk.h \
pulsecore/resampler.c pulsecore/resampler.h \
+ pulsecore/rtpoll.c pulsecore/rtpoll.h \
+ pulsecore/rtsig.c pulsecore/rtsig.h \
pulsecore/sample-util.c pulsecore/sample-util.h \
- pulsecore/sconv.c pulsecore/sconv.h \
pulsecore/sconv-s16be.c pulsecore/sconv-s16be.h \
pulsecore/sconv-s16le.c pulsecore/sconv-s16le.h \
- pulsecore/sink.c pulsecore/sink.h \
+ pulsecore/sconv.c pulsecore/sconv.h \
+ pulsecore/shared.c pulsecore/shared.h \
+ pulsecore/shm.c pulsecore/shm.h \
pulsecore/sink-input.c pulsecore/sink-input.h \
+ pulsecore/sink.c pulsecore/sink.h \
pulsecore/sioman.c pulsecore/sioman.h \
- pulsecore/sound-file.c pulsecore/sound-file.h \
pulsecore/sound-file-stream.c pulsecore/sound-file-stream.h \
- pulsecore/source.c pulsecore/source.h \
+ pulsecore/sound-file.c pulsecore/sound-file.h \
pulsecore/source-output.c pulsecore/source-output.h \
- pulsecore/strbuf.c pulsecore/strbuf.h \
- pulsecore/tokenizer.c pulsecore/tokenizer.h \
- pulsecore/winsock.h \
- pulsecore/core-error.c pulsecore/core-error.h \
- pulsecore/hook-list.c pulsecore/hook-list.h \
- pulsecore/shm.c pulsecore/shm.h \
- pulsecore/flist.c pulsecore/flist.h \
- pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h \
- pulsecore/asyncq.c pulsecore/asyncq.h \
- pulsecore/thread-mq.c pulsecore/thread-mq.h \
- pulsecore/fdsem.c pulsecore/fdsem.h \
- pulsecore/object.c pulsecore/object.h \
- pulsecore/msgobject.c pulsecore/msgobject.h \
- pulsecore/rtsig.c pulsecore/rtsig.h \
- pulsecore/rtpoll.c pulsecore/rtpoll.h \
- pulsecore/rtclock.c pulsecore/rtclock.h \
- pulsecore/macro.h \
- pulsecore/once.c pulsecore/once.h \
- pulsecore/time-smoother.c pulsecore/time-smoother.h \
+ pulsecore/source.c pulsecore/source.h \
pulsecore/start-child.c pulsecore/start-child.h \
- pulsecore/envelope.c pulsecore/envelope.h \
- pulsecore/proplist-util.c pulsecore/proplist-util.h \
- pulsecore/lock-autospawn.c pulsecore/lock-autospawn.h \
- $(PA_THREAD_OBJS)
+ pulsecore/thread-mq.c pulsecore/thread-mq.h \
+ pulsecore/time-smoother.c pulsecore/time-smoother.h
-if OS_IS_WIN32
-libpulsecore_la_SOURCES += \
- pulsecore/dllmain.c
-endif
+libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS = $(AM_CFLAGS) $(LIBOIL_CFLAGS)
+libpulsecore_@PA_MAJORMINORMICRO@_la_LDFLAGS = -avoid-version
+libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(LIBSPEEX_LIBS) $(WINSOCK_LIBS) $(LIBOIL_LIBS) $(LTLIBICONV) libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
-libpulsecore_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBOIL_CFLAGS)
-libpulsecore_la_LDFLAGS = -version-info $(LIBPULSECORE_VERSION_INFO)
-libpulsecore_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(LIBSPEEX_LIBS) $(WINSOCK_LIBS) $(LIBOIL_LIBS) $(LTLIBICONV) libffmpeg-resampler.la
+if HAVE_X11
+libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/x11wrap.c pulsecore/x11wrap.h
+libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(X11_CFLAGS)
+libpulsecore_@PA_MAJORMINORMICRO@_la_LDFLAGS += $(X11_LIBS)
+endif
###################################
# Plug-in support libraries #
###################################
-#pulsecoreinclude_HEADERS +=
-noinst_HEADERS += \
- pulsecore/socket-util.h \
- pulsecore/iochannel.h \
- pulsecore/socket-server.h \
- pulsecore/ipacl.h \
- pulsecore/socket-client.h \
- pulsecore/parseaddr.h \
- pulsecore/packet.h \
- pulsecore/pstream.h \
- pulsecore/ioline.h \
- pulsecore/cli.h \
- pulsecore/protocol-cli.h \
- pulsecore/tagstruct.h \
- pulsecore/pstream-util.h \
- pulsecore/pdispatch.h \
- pulsecore/authkey.h \
- pulsecore/auth-cookie.h \
- pulsecore/strlist.h \
- pulsecore/protocol-simple.h \
- pulsecore/esound.h \
- pulsecore/protocol-esound.h \
- pulsecore/native-common.h \
- pulsecore/protocol-native.h \
- pulsecore/protocol-http.h
-
### Warning! Due to an obscure bug in libtool/automake it is required
### that the libraries in modlibexec_LTLIBRARIES are specified in-order,
### i.e. libraries near the end of the list depend on libraries near
### the head, and not the other way!
modlibexec_LTLIBRARIES = \
- libsocket-util.la \
- libiochannel.la \
- libsocket-server.la \
- libipacl.la \
- libparseaddr.la \
- libsocket-client.la \
- libpacket.la \
- libpstream.la \
- libioline.la \
libcli.la \
libprotocol-cli.la \
- libtagstruct.la \
- libpstream-util.la \
- libpdispatch.la \
- libauthkey.la \
- libauth-cookie.la \
- libstrlist.la \
libprotocol-simple.la \
libprotocol-http.la \
libprotocol-native.la \
@@ -892,146 +786,63 @@ modlibexec_LTLIBRARIES += \
librtp.la
endif
-if HAVE_X11
-#pulsecoreinclude_HEADERS +=
-noinst_HEADERS += \
- pulsecore/x11wrap.h \
- pulsecore/x11prop.h
-
-modlibexec_LTLIBRARIES += \
- libx11wrap.la \
- libx11prop.la
-endif
-
if HAVE_AVAHI
-#pulsecoreinclude_HEADERS +=
-noinst_HEADERS += \
- pulsecore/avahi-wrap.h
-
modlibexec_LTLIBRARIES += \
libavahi-wrap.la
endif
libprotocol_simple_la_SOURCES = pulsecore/protocol-simple.c pulsecore/protocol-simple.h
libprotocol_simple_la_LDFLAGS = -avoid-version
-libprotocol_simple_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-server.la libiochannel.la
-
-libsocket_server_la_SOURCES = \
- pulsecore/inet_ntop.c pulsecore/inet_ntop.h \
- pulsecore/inet_pton.c pulsecore/inet_pton.h \
- pulsecore/socket-server.c pulsecore/socket-server.h
-libsocket_server_la_LDFLAGS = -avoid-version
-libsocket_server_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-util.la $(LIBWRAP_LIBS) $(WINSOCK_LIBS)
-
-libipacl_la_SOURCES = pulsecore/ipacl.h pulsecore/ipacl.c \
- pulsecore/inet_pton.c pulsecore/inet_pton.h
-libipacl_la_LDFLAGS = -avoid-version
-libipacl_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(WINSOCK_LIBS)
-
-libsocket_client_la_SOURCES = pulsecore/socket-client.c pulsecore/socket-client.h
-libsocket_client_la_LDFLAGS = -avoid-version
-libsocket_client_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-util.la libparseaddr.la $(LIBASYNCNS_LIBS) $(WINSOCK_LIBS)
-libsocket_client_la_CFLAGS = $(AM_CFLAGS) $(LIBASYNCNS_CFLAGS)
-
-libparseaddr_la_SOURCES = pulsecore/parseaddr.c pulsecore/parseaddr.h
-libparseaddr_la_LDFLAGS = -avoid-version
-libparseaddr_la_LIBADD = $(AM_LIBADD) libpulsecore.la
-
-libpstream_la_SOURCES = pulsecore/pstream.c pulsecore/pstream.h
-libpstream_la_LDFLAGS = -avoid-version
-libpstream_la_LIBADD = $(AM_LIBADD) libpulsecore.la libpacket.la libiochannel.la $(WINSOCK_LIBS)
-
-libpstream_util_la_SOURCES = pulsecore/pstream-util.c pulsecore/pstream-util.h
-libpstream_util_la_LDFLAGS = -avoid-version
-libpstream_util_la_LIBADD = $(AM_LIBADD) libpacket.la libpstream.la libtagstruct.la libpulsecore.la
-
-libpdispatch_la_SOURCES = pulsecore/pdispatch.c pulsecore/pdispatch.h
-libpdispatch_la_LDFLAGS = -avoid-version
-libpdispatch_la_LIBADD = $(AM_LIBADD) libtagstruct.la libpulsecore.la
-
-libiochannel_la_SOURCES = pulsecore/iochannel.c pulsecore/iochannel.h
-libiochannel_la_LDFLAGS = -avoid-version
-libiochannel_la_LIBADD = $(AM_LIBADD) libsocket-util.la libpulsecore.la $(WINSOCK_LIBS)
-
-libpacket_la_SOURCES = pulsecore/packet.c pulsecore/packet.h
-libpacket_la_LDFLAGS = -avoid-version
-libpacket_la_LIBADD = $(AM_LIBADD) libpulsecore.la
-
-libioline_la_SOURCES = pulsecore/ioline.c pulsecore/ioline.h
-libioline_la_LDFLAGS = -avoid-version
-libioline_la_LIBADD = $(AM_LIBADD) libiochannel.la libpulsecore.la
+libprotocol_simple_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
libcli_la_SOURCES = pulsecore/cli.c pulsecore/cli.h
-libcli_la_CPPFLAGS = $(AM_CPPFLAGS)
libcli_la_LDFLAGS = -avoid-version
-libcli_la_LIBADD = $(AM_LIBADD) libiochannel.la libioline.la libpulsecore.la
-
-libstrlist_la_SOURCES = pulsecore/strlist.c pulsecore/strlist.h
-libstrlist_la_LDFLAGS = -avoid-version
-libstrlist_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+libcli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
libprotocol_cli_la_SOURCES = pulsecore/protocol-cli.c pulsecore/protocol-cli.h
libprotocol_cli_la_LDFLAGS = -avoid-version
-libprotocol_cli_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la libcli.la libpulsecore.la
+libprotocol_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
libprotocol_http_la_SOURCES = pulsecore/protocol-http.c pulsecore/protocol-http.h
libprotocol_http_la_LDFLAGS = -avoid-version
-libprotocol_http_la_LIBADD = $(AM_LIBADD) libsocket-server.la libioline.la libpulsecore.la libiochannel.la
+libprotocol_http_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
libprotocol_native_la_SOURCES = pulsecore/protocol-native.c pulsecore/protocol-native.h pulsecore/native-common.h
libprotocol_native_la_LDFLAGS = -avoid-version
-libprotocol_native_la_LIBADD = $(AM_LIBADD) libsocket-server.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauth-cookie.la libstrlist.la libpulsecore.la libiochannel.la libipacl.la
-
-libtagstruct_la_SOURCES = pulsecore/tagstruct.c pulsecore/tagstruct.h
-libtagstruct_la_LDFLAGS = -avoid-version
-libtagstruct_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(WINSOCK_LIBS)
+libprotocol_native_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
libprotocol_esound_la_SOURCES = pulsecore/protocol-esound.c pulsecore/protocol-esound.h pulsecore/esound.h
libprotocol_esound_la_LDFLAGS = -avoid-version
-libprotocol_esound_la_LIBADD = $(AM_LIBADD) libsocket-server.la libiochannel.la libauthkey.la libauth-cookie.la libpulsecore.la libipacl.la
-
-libauthkey_la_SOURCES = pulsecore/authkey.c pulsecore/authkey.h
-libauthkey_la_LDFLAGS = -avoid-version
-libauthkey_la_LIBADD = $(AM_LIBADD) libpulsecore.la
-
-libauth_cookie_la_SOURCES = pulsecore/auth-cookie.c pulsecore/auth-cookie.h
-libauth_cookie_la_LDFLAGS = -avoid-version
-libauth_cookie_la_LIBADD = $(AM_LIBADD) libauthkey.la libpulsecore.la
-
-libsocket_util_la_SOURCES = \
- pulsecore/inet_ntop.c pulsecore/inet_ntop.h \
- pulsecore/socket-util.c pulsecore/socket-util.h
-libsocket_util_la_LDFLAGS = -avoid-version
-libsocket_util_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) libpulsecore.la
-
-librtp_la_SOURCES = modules/rtp/rtp.c modules/rtp/rtp.h modules/rtp/sdp.c modules/rtp/sdp.h modules/rtp/sap.c modules/rtp/sap.h
+libprotocol_esound_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+
+librtp_la_SOURCES = \
+ modules/rtp/rtp.c modules/rtp/rtp.h \
+ modules/rtp/sdp.c modules/rtp/sdp.h \
+ modules/rtp/sap.c modules/rtp/sap.h \
+ modules/rtp/rtsp_client.c modules/rtp/rtsp_client.h \
+ modules/rtp/headerlist.c modules/rtp/headerlist.h
librtp_la_LDFLAGS = -avoid-version
-librtp_la_LIBADD = $(AM_LIBADD) libpulsecore.la
-
-# X11
-
-libx11wrap_la_SOURCES = pulsecore/x11wrap.c pulsecore/x11wrap.h
-libx11wrap_la_LDFLAGS = -avoid-version
-libx11wrap_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
-libx11wrap_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libpulsecore.la
+librtp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
-libx11prop_la_SOURCES = pulsecore/x11prop.c pulsecore/x11prop.h
-libx11prop_la_LDFLAGS = -avoid-version
-libx11prop_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
-libx11prop_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS)
+libraop_la_SOURCES = \
+ modules/raop/raop_client.c modules/raop/raop_client.h \
+ modules/raop/base64.c modules/raop/base64.h
+libraop_la_CFLAGS = $(AM_CFLAGS) $(OPENSSL_CFLAGS)
+libraop_la_LDFLAGS = -avoid-version
+libraop_la_LIBADD = $(AM_LIBADD) $(OPENSSL_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la librtp.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# Avahi
-
libavahi_wrap_la_SOURCES = pulsecore/avahi-wrap.c pulsecore/avahi-wrap.h
libavahi_wrap_la_LDFLAGS = -avoid-version
libavahi_wrap_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
-libavahi_wrap_la_LIBADD = $(AM_LIBADD) $(AVAHI_CFLAGS) libpulsecore.la
+libavahi_wrap_la_LIBADD = $(AM_LIBADD) $(AVAHI_CFLAGS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
###################################
# Plug-in libraries #
###################################
modlibexec_LTLIBRARIES += \
+ module-flat-volume.la \
module-cli.la \
module-cli-protocol-tcp.la \
module-simple-protocol-tcp.la \
@@ -1177,6 +988,17 @@ pulselibexec_PROGRAMS += \
proximity-helper
endif
+if HAVE_OPENSSL
+modlibexec_LTLIBRARIES += \
+ libraop.la \
+ module-raop-sink.la
+if HAVE_AVAHI
+modlibexec_LTLIBRARIES += \
+ module-raop-discover.la
+endif
+endif
+
+
# These are generated by a M4 script
SYMDEF_FILES = \
@@ -1233,9 +1055,12 @@ SYMDEF_FILES = \
modules/bluetooth/module-bluetooth-proximity-symdef.h \
modules/bluetooth/module-bluetooth-discover-symdef.h \
modules/bluetooth/module-bluetooth-device-symdef.h \
+ modules/module-raop-sink-symdef.h \
+ modules/module-raop-discover-symdef.h \
modules/gconf/module-gconf-symdef.h \
modules/module-position-event-sounds-symdef.h \
- modules/module-console-kit-symdef.h
+ modules/module-console-kit-symdef.h \
+ modules/module-flat-volume-symdef.h
EXTRA_DIST += $(SYMDEF_FILES)
BUILT_SOURCES += $(SYMDEF_FILES)
@@ -1247,326 +1072,332 @@ $(SYMDEF_FILES): modules/module-defs.h.m4
$(MKDIR_P) modules/bluetooth
$(M4) -Dfname="$@" $< > $@
+# Flat volume
+
+module_flat_volume_la_SOURCES = modules/module-flat-volume.c
+module_flat_volume_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_flat_volume_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+
# Simple protocol
module_simple_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
module_simple_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
-module_simple_protocol_tcp_la_LDFLAGS = -module -avoid-version
-module_simple_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-simple.la libsocket-server.la
+module_simple_protocol_tcp_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_simple_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-simple.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_simple_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE $(AM_CFLAGS)
-module_simple_protocol_unix_la_LDFLAGS = -module -avoid-version
-module_simple_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-simple.la libsocket-server.la libsocket-util.la
+module_simple_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_simple_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-simple.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# CLI protocol
module_cli_la_SOURCES = modules/module-cli.c
-module_cli_la_LDFLAGS = -module -avoid-version
-module_cli_la_LIBADD = $(AM_LIBADD) libcli.la libiochannel.la libpulsecore.la
+module_cli_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libcli.la
module_cli_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
module_cli_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
-module_cli_protocol_tcp_la_LDFLAGS = -module -avoid-version
-module_cli_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-cli.la libsocket-server.la
+module_cli_protocol_tcp_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_cli_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-cli.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_cli_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
module_cli_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_CLI $(AM_CFLAGS)
-module_cli_protocol_unix_la_LDFLAGS = -module -avoid-version
-module_cli_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-cli.la libsocket-server.la libsocket-util.la
+module_cli_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_cli_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-cli.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# HTTP protocol
module_http_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
module_http_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)
-module_http_protocol_tcp_la_LDFLAGS = -module -avoid-version
-module_http_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-http.la libsocket-server.la
+module_http_protocol_tcp_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_http_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-http.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_http_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
module_http_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_HTTP $(AM_CFLAGS)
-module_http_protocol_unix_la_LDFLAGS = -module -avoid-version
-module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-http.la libsocket-server.la libsocket-util.la
+module_http_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_http_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-http.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# Native protocol
module_native_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
module_native_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
-module_native_protocol_tcp_la_LDFLAGS = -module -avoid-version
-module_native_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-native.la libsocket-server.la
+module_native_protocol_tcp_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_native_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-native.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_native_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
module_native_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_NATIVE $(AM_CFLAGS)
-module_native_protocol_unix_la_LDFLAGS = -module -avoid-version
-module_native_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-native.la libsocket-server.la libsocket-util.la
+module_native_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_native_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-native.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_native_protocol_fd_la_SOURCES = modules/module-native-protocol-fd.c
module_native_protocol_fd_la_CFLAGS = $(AM_CFLAGS)
-module_native_protocol_fd_la_LDFLAGS = -module -avoid-version
-module_native_protocol_fd_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-native.la libsocket-server.la libsocket-util.la libiochannel.la
+module_native_protocol_fd_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_native_protocol_fd_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-native.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# EsounD protocol
module_esound_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
module_esound_protocol_tcp_la_CFLAGS = -DUSE_TCP_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
-module_esound_protocol_tcp_la_LDFLAGS = -module -avoid-version
-module_esound_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-esound.la libsocket-server.la
+module_esound_protocol_tcp_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_esound_protocol_tcp_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-esound.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_esound_protocol_unix_la_SOURCES = modules/module-protocol-stub.c
module_esound_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_ESOUND $(AM_CFLAGS)
-module_esound_protocol_unix_la_LDFLAGS = -module -avoid-version
-module_esound_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore.la libprotocol-esound.la libsocket-server.la libsocket-util.la
+module_esound_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_esound_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libprotocol-esound.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_esound_compat_spawnfd_la_SOURCES = modules/module-esound-compat-spawnfd.c
-module_esound_compat_spawnfd_la_LDFLAGS = -module -avoid-version
-module_esound_compat_spawnfd_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_esound_compat_spawnfd_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_esound_compat_spawnfd_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_esound_compat_spawnpid_la_SOURCES = modules/module-esound-compat-spawnpid.c
-module_esound_compat_spawnpid_la_LDFLAGS = -module -avoid-version
-module_esound_compat_spawnpid_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_esound_compat_spawnpid_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_esound_compat_spawnpid_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_esound_sink_la_SOURCES = modules/module-esound-sink.c
-module_esound_sink_la_LDFLAGS = -module -avoid-version
-module_esound_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la libsocket-client.la libauthkey.la libsocket-util.la
+module_esound_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_esound_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# Pipes
module_pipe_sink_la_SOURCES = modules/module-pipe-sink.c
-module_pipe_sink_la_LDFLAGS = -module -avoid-version
-module_pipe_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la
+module_pipe_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_pipe_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_pipe_source_la_SOURCES = modules/module-pipe-source.c
-module_pipe_source_la_LDFLAGS = -module -avoid-version
-module_pipe_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la libiochannel.la
+module_pipe_source_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_pipe_source_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# Fake sources/sinks
module_sine_la_SOURCES = modules/module-sine.c
-module_sine_la_LDFLAGS = -module -avoid-version
-module_sine_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_sine_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_sine_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_null_sink_la_SOURCES = modules/module-null-sink.c
-module_null_sink_la_LDFLAGS = -module -avoid-version
-module_null_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_null_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_null_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# Couplings
module_combine_la_SOURCES = modules/module-combine.c
-module_combine_la_LDFLAGS = -module -avoid-version
-module_combine_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_combine_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_combine_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_remap_sink_la_SOURCES = modules/module-remap-sink.c
-module_remap_sink_la_LDFLAGS = -module -avoid-version
-module_remap_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_remap_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_remap_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_ladspa_sink_la_SOURCES = modules/module-ladspa-sink.c modules/ladspa.h
module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa\" $(AM_CFLAGS)
-module_ladspa_sink_la_LDFLAGS = -module -avoid-version
-module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore.la
+module_ladspa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_ladspa_sink_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_match_la_SOURCES = modules/module-match.c
-module_match_la_LDFLAGS = -module -avoid-version
-module_match_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_match_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_match_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_tunnel_sink_la_SOURCES = modules/module-tunnel.c
module_tunnel_sink_la_CFLAGS = -DTUNNEL_SINK=1 $(AM_CFLAGS)
-module_tunnel_sink_la_LDFLAGS = -module -avoid-version
-module_tunnel_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauth-cookie.la libsocket-util.la libiochannel.la
+module_tunnel_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_tunnel_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_tunnel_source_la_SOURCES = modules/module-tunnel.c
-module_tunnel_source_la_LDFLAGS = -module -avoid-version
-module_tunnel_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la libsocket-client.la libpstream.la libpstream-util.la libpdispatch.la libtagstruct.la libauthkey.la libauth-cookie.la libsocket-util.la libiochannel.la
+module_tunnel_source_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_tunnel_source_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# X11
module_x11_bell_la_SOURCES = modules/module-x11-bell.c
-module_x11_bell_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
-module_x11_bell_la_LDFLAGS = -module -avoid-version
-module_x11_bell_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libpulsecore.la
+module_x11_bell_la_CFLAGS = $(AM_CFLAGS) $(X11_CFLAGS)
+module_x11_bell_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_x11_bell_la_LIBADD = $(AM_LIBADD) $(X11_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_x11_publish_la_SOURCES = modules/module-x11-publish.c
-module_x11_publish_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
-module_x11_publish_la_LDFLAGS = -module -avoid-version
-module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libauthkey.la libauth-cookie.la libx11prop.la libstrlist.la libprotocol-native.la libpulsecore.la
+module_x11_publish_la_CFLAGS = $(AM_CFLAGS) $(X11_CFLAGS)
+module_x11_publish_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_x11_publish_la_LIBADD = $(AM_LIBADD) $(X11_LIBS) libprotocol-native.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_x11_xsmp_la_SOURCES = modules/module-x11-xsmp.c
-module_x11_xsmp_la_CFLAGS = $(AM_CFLAGS) $(X_CFLAGS)
-module_x11_xsmp_la_LDFLAGS = -module -avoid-version
-module_x11_xsmp_la_LIBADD = $(AM_LIBADD) $(X_PRE_LIBS) -lX11 $(X_LIBS) $(X_EXTRA_LIBS) libx11wrap.la libpulsecore.la
+module_x11_xsmp_la_CFLAGS = $(AM_CFLAGS) $(X11_CFLAGS)
+module_x11_xsmp_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_x11_xsmp_la_LIBADD = $(AM_LIBADD) $(X11_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# OSS
liboss_util_la_SOURCES = modules/oss-util.c modules/oss-util.h
liboss_util_la_LDFLAGS = -avoid-version
-liboss_util_la_LIBADD = libpulsecore.la
+liboss_util_la_LIBADD = libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_oss_la_SOURCES = modules/module-oss.c
-module_oss_la_LDFLAGS = -module -avoid-version
-module_oss_la_LIBADD = $(AM_LIBADD) libiochannel.la liboss-util.la libpulsecore.la
+module_oss_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_oss_la_LIBADD = $(AM_LIBADD) liboss-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# ALSA
libalsa_util_la_SOURCES = modules/alsa-util.c modules/alsa-util.h
libalsa_util_la_LDFLAGS = -avoid-version
-libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libpulsecore.la
+libalsa_util_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
module_alsa_sink_la_SOURCES = modules/module-alsa-sink.c
-module_alsa_sink_la_LDFLAGS = -module -avoid-version
-module_alsa_sink_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore.la
+module_alsa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_alsa_sink_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_alsa_sink_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
module_alsa_source_la_SOURCES = modules/module-alsa-source.c
-module_alsa_source_la_LDFLAGS = -module -avoid-version
-module_alsa_source_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore.la
+module_alsa_source_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_alsa_source_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS)
# Solaris
module_solaris_la_SOURCES = modules/module-solaris.c
-module_solaris_la_LDFLAGS = -module -avoid-version
-module_solaris_la_LIBADD = $(AM_LIBADD) libiochannel.la libpulsecore.la
+module_solaris_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_solaris_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
# Avahi
module_zeroconf_publish_la_SOURCES = modules/module-zeroconf-publish.c
-module_zeroconf_publish_la_LDFLAGS = -module -avoid-version
-module_zeroconf_publish_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore.la
+module_zeroconf_publish_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_zeroconf_publish_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_zeroconf_publish_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
module_zeroconf_discover_la_SOURCES = modules/module-zeroconf-discover.c
-module_zeroconf_discover_la_LDFLAGS = -module -avoid-version
-module_zeroconf_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore.la
+module_zeroconf_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_zeroconf_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_zeroconf_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
# LIRC
module_lirc_la_SOURCES = modules/module-lirc.c
-module_lirc_la_LDFLAGS = -module -avoid-version
-module_lirc_la_LIBADD = $(AM_LIBADD) $(LIRC_LIBS) libpulsecore.la
+module_lirc_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_lirc_la_LIBADD = $(AM_LIBADD) $(LIRC_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_lirc_la_CFLAGS = $(AM_CFLAGS) $(LIRC_CFLAGS)
# Linux evdev
module_mmkbd_evdev_la_SOURCES = modules/module-mmkbd-evdev.c
-module_mmkbd_evdev_la_LDFLAGS = -module -avoid-version
-module_mmkbd_evdev_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_mmkbd_evdev_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_mmkbd_evdev_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_mmkbd_evdev_la_CFLAGS = $(AM_CFLAGS)
# Windows waveout
#module_waveout_la_SOURCES = modules/module-waveout.c
-#module_waveout_la_LDFLAGS = -module -avoid-version
-#module_waveout_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lwinmm
+#module_waveout_la_LDFLAGS = $(MODULE_LDFLAGS)
+#module_waveout_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la -lwinmm libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
#module_waveout_la_CFLAGS = $(AM_CFLAGS)
# Hardware autodetection module
module_detect_la_SOURCES = modules/module-detect.c
-module_detect_la_LDFLAGS = -module -avoid-version
-module_detect_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_detect_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_detect_la_CFLAGS = $(AM_CFLAGS)
# Volume restore module
module_volume_restore_la_SOURCES = modules/module-volume-restore.c
-module_volume_restore_la_LDFLAGS = -module -avoid-version
-module_volume_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_volume_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_volume_restore_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_volume_restore_la_CFLAGS = $(AM_CFLAGS)
# Position event sounds in space
module_position_event_sounds_la_SOURCES = modules/module-position-event-sounds.c
-module_position_event_sounds_la_LDFLAGS = -module -avoid-version
-module_position_event_sounds_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_position_event_sounds_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_position_event_sounds_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_position_event_sounds_CFLAGS = $(AM_CFLAGS)
# Device volume/muted restore module
module_device_restore_la_SOURCES = modules/module-device-restore.c
-module_device_restore_la_LDFLAGS = -module -avoid-version
-module_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la -lgdbm
+module_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la -lgdbm libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_device_restore_la_CFLAGS = $(AM_CFLAGS)
# Stream volume/muted/device restore module
module_stream_restore_la_SOURCES = modules/module-stream-restore.c
-module_stream_restore_la_LDFLAGS = -module -avoid-version
-module_stream_restore_la_LIBADD = $(AM_LIBADD) libtagstruct.la libprotocol-native.la libpulsecore.la -lgdbm
+module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_stream_restore_la_LIBADD = $(AM_LIBADD) libprotocol-native.la libpulsecore-@PA_MAJORMINORMICRO@.la -lgdbm libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_stream_restore_la_CFLAGS = $(AM_CFLAGS)
# Default sink/source restore module
module_default_device_restore_la_SOURCES = modules/module-default-device-restore.c
-module_default_device_restore_la_LDFLAGS = -module -avoid-version
-module_default_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_default_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_default_device_restore_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_default_device_restore_la_CFLAGS = $(AM_CFLAGS)
# Always Sink module
module_always_sink_la_SOURCES = modules/module-always-sink.c
-module_always_sink_la_LDFLAGS = -module -avoid-version
-module_always_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_always_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_always_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_always_sink_la_CFLAGS = $(AM_CFLAGS)
# Rescue streams module
module_rescue_streams_la_SOURCES = modules/module-rescue-streams.c
-module_rescue_streams_la_LDFLAGS = -module -avoid-version
-module_rescue_streams_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_rescue_streams_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_rescue_streams_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_rescue_streams_la_CFLAGS = $(AM_CFLAGS)
# Suspend-on-idle module
module_suspend_on_idle_la_SOURCES = modules/module-suspend-on-idle.c
-module_suspend_on_idle_la_LDFLAGS = -module -avoid-version
-module_suspend_on_idle_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_suspend_on_idle_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_suspend_on_idle_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_suspend_on_idle_la_CFLAGS = $(AM_CFLAGS)
# RTP modules
module_rtp_send_la_SOURCES = modules/rtp/module-rtp-send.c
-module_rtp_send_la_LDFLAGS = -module -avoid-version
-module_rtp_send_la_LIBADD = $(AM_LIBADD) libpulsecore.la librtp.la libsocket-util.la
+module_rtp_send_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_rtp_send_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la librtp.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_rtp_send_la_CFLAGS = $(AM_CFLAGS)
module_rtp_recv_la_SOURCES = modules/rtp/module-rtp-recv.c
-module_rtp_recv_la_LDFLAGS = -module -avoid-version
-module_rtp_recv_la_LIBADD = $(AM_LIBADD) libpulsecore.la librtp.la
+module_rtp_recv_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_rtp_recv_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la librtp.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_rtp_recv_la_CFLAGS = $(AM_CFLAGS)
# JACK
module_jack_sink_la_SOURCES = modules/module-jack-sink.c
-module_jack_sink_la_LDFLAGS = -module -avoid-version
-module_jack_sink_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS)
+module_jack_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_jack_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la $(JACK_LIBS) libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_jack_sink_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)
module_jack_source_la_SOURCES = modules/module-jack-source.c
-module_jack_source_la_LDFLAGS = -module -avoid-version
-module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore.la $(JACK_LIBS)
+module_jack_source_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_jack_source_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la $(JACK_LIBS) libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_jack_source_la_CFLAGS = $(AM_CFLAGS) $(JACK_CFLAGS)
# HAL/D-Bus
libdbus_util_la_SOURCES = modules/dbus-util.c modules/dbus-util.h
libdbus_util_la_LDFLAGS = -avoid-version
-libdbus_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la
+libdbus_util_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
libdbus_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
module_hal_detect_la_SOURCES = modules/module-hal-detect.c
-module_hal_detect_la_LDFLAGS = -module -avoid-version
-module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore.la libdbus-util.la
+module_hal_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_hal_detect_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
module_console_kit_la_SOURCES = modules/module-console-kit.c
-module_console_kit_la_LDFLAGS = -module -avoid-version
-module_console_kit_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
+module_console_kit_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_console_kit_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_console_kit_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
# GConf support
module_gconf_la_SOURCES = modules/gconf/module-gconf.c
-module_gconf_la_LDFLAGS = -module -avoid-version
-module_gconf_la_LIBADD = $(AM_LIBADD) libpulsecore.la
+module_gconf_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_gconf_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_gconf_la_CFLAGS = $(AM_CFLAGS) -DPA_GCONF_HELPER=\"$(pulselibexecdir)/gconf-helper\"
gconf_helper_SOURCES = modules/gconf/gconf-helper.c
-gconf_helper_LDADD = $(AM_LDADD) $(GCONF_LIBS) libpulsecore.la
+gconf_helper_LDADD = $(AM_LDADD) $(GCONF_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
gconf_helper_CFLAGS = $(AM_CFLAGS) $(GCONF_CFLAGS)
gconf_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
# Bluetooth proximity
module_bluetooth_proximity_la_SOURCES = modules/bluetooth/module-bluetooth-proximity.c
-module_bluetooth_proximity_la_LDFLAGS = -module -avoid-version
-module_bluetooth_proximity_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
+module_bluetooth_proximity_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_bluetooth_proximity_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_bluetooth_proximity_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -DPA_BT_PROXIMITY_HELPER=\"$(pulselibexecdir)/proximity-helper\"
proximity_helper_SOURCES = modules/bluetooth/proximity-helper.c
@@ -1576,24 +1407,36 @@ proximity_helper_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
# Bluetooth sink / source
module_bluetooth_discover_la_SOURCES = modules/bluetooth/module-bluetooth-discover.c
-module_bluetooth_discover_la_LDFLAGS = -module -avoid-version
-module_bluetooth_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la
+module_bluetooth_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_bluetooth_discover_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_bluetooth_discover_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
libbluetooth_sbc_la_SOURCES = modules/bluetooth/sbc.c modules/bluetooth/sbc.h modules/bluetooth/sbc_tables.h modules/bluetooth/sbc_math.h
libbluetooth_sbc_la_LDFLAGS = -avoid-version
-libbluetooth_sbc_la_LIBADD = $(AM_LIBADD)
+libbluetooth_sbc_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
libbluetooth_sbc_la_CFLAGS = $(AM_CFLAGS)
+SBC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_sbc_la_SOURCES))
libbluetooth_ipc_la_SOURCES = modules/bluetooth/ipc.c modules/bluetooth/ipc.h
libbluetooth_ipc_la_LDFLAGS = -avoid-version
-libbluetooth_ipc_la_LIBADD = $(AM_LIBADD)
-libbluetooth_ipc_la_CFLAGS = $(AM_CFLAGS)
+libbluetooth_ipc_la_LIBADD = $(AM_LIBADD)libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+libbluetooth_ipc_la_CFLAGS = $(AM_CFLAGS) -w
module_bluetooth_device_la_SOURCES = modules/bluetooth/module-bluetooth-device.c modules/bluetooth/rtp.h
-module_bluetooth_device_la_LDFLAGS = -module -avoid-version
-module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore.la libdbus-util.la libbluetooth-ipc.la libbluetooth-sbc.la libsocket-util.la
-module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+module_bluetooth_device_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_bluetooth_device_la_LIBADD = $(AM_LIBADD) $(DBUS_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libdbus-util.la libbluetooth-ipc.la libbluetooth-sbc.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+module_bluetooth_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) -w
+
+# Apple Airtunes/RAOP
+module_raop_sink_la_SOURCES = modules/module-raop-sink.c
+module_raop_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_raop_sink_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la librtp.la libraop.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+
+module_raop_discover_la_SOURCES = modules/module-raop-discover.c
+module_raop_discover_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_raop_discover_la_LIBADD = $(AM_LIBADD) $(AVAHI_LIBS) libavahi-wrap.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
+module_raop_discover_la_CFLAGS = $(AM_CFLAGS) $(AVAHI_CFLAGS)
+
###################################
# Some minor stuff #
@@ -1644,17 +1487,25 @@ install-exec-hook:
chmod u+s $(DESTDIR)$(bindir)/pulseaudio
-chmod u+s $(DESTDIR)$(pulselibexecdir)/proximity-helper
ln -sf pacat $(DESTDIR)$(bindir)/parec
- rm -f $(DESTDIR)$(modlibexecdir)/*.a
- rm -f $(DESTDIR)$(libdir)/libpulsedsp.a
rm -f $(DESTDIR)$(libdir)/libpulsedsp.la
rm -f $(DESTDIR)$(modlibexecdir)/*.la
+uninstall-hook:
+ rm -f $(DESTDIR)$(bindir)/parec
+ rm -f $(DESTDIR)$(libdir)/libpulsedsp.*
+ rm -f $(DESTDIR)$(modlibexecdir)/*.so
+
massif: pulseaudio
libtool --mode=execute valgrind --tool=massif --depth=6 --alloc-fn=pa_xmalloc --alloc-fn=pa_xmalloc0 --alloc-fn=pa_xrealloc --alloc-fn=dbus_realloc --alloc-fn=pa_xnew0_internal --alloc-fn=pa_xnew_internal ./pulseaudio
update-ffmpeg:
wget -O pulsecore/ffmpeg/resample2.c http://svn.mplayerhq.hu/ffmpeg/trunk/libavcodec/resample2.c?view=co
+update-sbc:
+ for i in $(SBC_FILES); do \
+ wget -O modules/bluetooth/$$i http://git.kernel.org/\?p=bluetooth/bluez.git\;a=blob_plain\;f=sbc/$$i ; \
+ done
+
# Automatically generate linker version script. We use the same one for all public .sos
update-map-file:
( echo "PULSE_0 {" ; \
@@ -1664,4 +1515,6 @@ update-map-file:
echo "*;" ; \
echo "};" ) > $(srcdir)/map-file
-.PHONY: utils/padsp
+update-all: update-ffmpeg update-sbc update-map-file
+
+.PHONY: utils/padsp massif update-all update-ffmpeg update-sbc update-map-file
diff --git a/src/daemon/caps.c b/src/daemon/caps.c
index 707b5323..b5cbbc63 100644
--- a/src/daemon/caps.c
+++ b/src/daemon/caps.c
@@ -60,7 +60,7 @@ void pa_drop_root(void) {
if (uid == 0 || geteuid() != 0)
return;
- pa_log_info(_("Dropping root priviliges."));
+ pa_log_info(_("Dropping root privileges."));
#if defined(HAVE_SETRESUID)
pa_assert_se(setresuid(uid, uid, uid) >= 0);
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
index fbd6dc32..cc3d714b 100644
--- a/src/daemon/cmdline.c
+++ b/src/daemon/cmdline.c
@@ -55,6 +55,9 @@ enum {
ARG_MODULE_IDLE_TIME,
ARG_SCACHE_IDLE_TIME,
ARG_LOG_TARGET,
+ ARG_LOG_META,
+ ARG_LOG_TIME,
+ ARG_LOG_BACKTRACE,
ARG_LOAD,
ARG_FILE,
ARG_DL_SEARCH_PATH,
@@ -88,6 +91,9 @@ static const struct option long_options[] = {
{"module-idle-time", 2, 0, ARG_MODULE_IDLE_TIME},
{"scache-idle-time", 2, 0, ARG_SCACHE_IDLE_TIME},
{"log-target", 1, 0, ARG_LOG_TARGET},
+ {"log-meta", 2, 0, ARG_LOG_META},
+ {"log-time", 2, 0, ARG_LOG_TIME},
+ {"log-backtrace", 1, 0, ARG_LOG_BACKTRACE},
{"load", 1, 0, ARG_LOAD},
{"file", 1, 0, ARG_FILE},
{"dl-search-path", 1, 0, ARG_DL_SEARCH_PATH},
@@ -124,7 +130,7 @@ void pa_cmdline_help(const char *argv0) {
" --cleanup-shm Cleanup stale shared memory segments\n"
" --start Start the daemon if it is not running\n"
" -k --kill Kill a running daemon\n"
- " --check Check for a running daemon\n\n"
+ " --check Check for a running daemon (only returns exit code)\n\n"
"OPTIONS:\n"
" --system[=BOOL] Run as system-wide instance\n"
@@ -148,6 +154,9 @@ void pa_cmdline_help(const char *argv0) {
" --log-level[=LEVEL] Increase or set verbosity level\n"
" -v Increase the verbosity level\n"
" --log-target={auto,syslog,stderr} Specify the log target\n"
+ " --log-meta[=BOOL] Include code location in log messages\n"
+ " --log-time[=BOOL] Include timestamps in log messages\n"
+ " --log-backtrace=FRAMES Include a backtrace in log messages\n"
" -p, --dl-search-path=PATH Set the search path for dynamic shared\n"
" objects (plugins)\n"
" --resample-method=METHOD Use the specified resampling method\n"
@@ -321,6 +330,24 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
}
break;
+ case ARG_LOG_TIME:
+ if ((conf->log_time = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+ pa_log(_("--log-time boolean argument"));
+ goto fail;
+ }
+ break;
+
+ case ARG_LOG_META:
+ if ((conf->log_meta = optarg ? pa_parse_boolean(optarg) : TRUE) < 0) {
+ pa_log(_("--log-meta boolean argument"));
+ goto fail;
+ }
+ break;
+
+ case ARG_LOG_BACKTRACE:
+ conf->log_backtrace = (unsigned) atoi(optarg);
+ break;
+
case ARG_EXIT_IDLE_TIME:
conf->exit_idle_time = atoi(optarg);
break;
diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c
index a909600e..5f24474d 100644
--- a/src/daemon/cpulimit.c
+++ b/src/daemon/cpulimit.c
@@ -167,7 +167,7 @@ static void callback(pa_mainloop_api*m, pa_io_event*e, int fd, pa_io_event_flags
pa_assert(e == io_event);
pa_assert(fd == the_pipe[0]);
- pa_log("Recevied request to terminate due to CPU overload.");
+ pa_log("Received request to terminate due to CPU overload.");
pa_read(the_pipe[0], &c, sizeof(c), NULL);
m->quit(m, 1); /* Quit the main loop */
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index 939b25d7..d7ffc105 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -74,6 +74,9 @@ static const pa_daemon_conf default_conf = {
.default_script_file = NULL,
.log_target = PA_LOG_SYSLOG,
.log_level = PA_LOG_NOTICE,
+ .log_backtrace = 0,
+ .log_meta = FALSE,
+ .log_time = FALSE,
.resample_method = PA_RESAMPLER_AUTO,
.disable_remixing = FALSE,
.disable_lfe_remixing = TRUE,
@@ -399,6 +402,7 @@ static int parse_rtprio(const char *filename, unsigned line, const char *lvalue,
int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
int r = -1;
FILE *f = NULL;
+ unsigned i = 0;
pa_config_item table[] = {
{ "daemonize", pa_config_parse_bool, NULL },
@@ -431,6 +435,9 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ "disable-lfe-remixing", pa_config_parse_bool, NULL },
{ "load-default-script-file", pa_config_parse_bool, NULL },
{ "shm-size-bytes", pa_config_parse_size, NULL },
+ { "log-meta", pa_config_parse_bool, NULL },
+ { "log-time", pa_config_parse_bool, NULL },
+ { "log-backtrace", pa_config_parse_unsigned, NULL },
#ifdef HAVE_SYS_RESOURCE_H
{ "rlimit-fsize", parse_rlimit, NULL },
{ "rlimit-data", parse_rlimit, NULL },
@@ -467,98 +474,75 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ NULL, NULL, NULL },
};
- table[0].data = &c->daemonize;
- table[1].data = &c->fail;
- table[2].data = &c->high_priority;
- table[3].data = &c->realtime_scheduling;
- table[4].data = &c->disallow_module_loading;
- table[5].data = &c->disallow_exit;
- table[6].data = &c->use_pid_file;
- table[7].data = &c->system_instance;
- table[8].data = &c->no_cpu_limit;
- table[9].data = &c->disable_shm;
- table[10].data = &c->exit_idle_time;
- table[11].data = &c->module_idle_time;
- table[12].data = &c->scache_idle_time;
- table[13].data = c;
- table[14].data = &c->dl_search_path;
- table[15].data = &c->default_script_file;
- table[16].data = c;
- table[17].data = c;
- table[18].data = c;
- table[19].data = c;
- table[20].data = c;
- table[21].data = c;
- table[22].data = c;
- table[23].data = c;
- table[24].data = c;
- table[25].data = c;
- table[26].data = &c->disable_remixing;
- table[27].data = &c->disable_lfe_remixing;
- table[28].data = &c->load_default_script_file;
- table[29].data = &c->shm_size;
+ table[i++].data = &c->daemonize;
+ table[i++].data = &c->fail;
+ table[i++].data = &c->high_priority;
+ table[i++].data = &c->realtime_scheduling;
+ table[i++].data = &c->disallow_module_loading;
+ table[i++].data = &c->disallow_exit;
+ table[i++].data = &c->use_pid_file;
+ table[i++].data = &c->system_instance;
+ table[i++].data = &c->no_cpu_limit;
+ table[i++].data = &c->disable_shm;
+ table[i++].data = &c->exit_idle_time;
+ table[i++].data = &c->module_idle_time;
+ table[i++].data = &c->scache_idle_time;
+ table[i++].data = c;
+ table[i++].data = &c->dl_search_path;
+ table[i++].data = &c->default_script_file;
+ table[i++].data = c;
+ table[i++].data = c;
+ table[i++].data = c;
+ table[i++].data = c;
+ table[i++].data = c;
+ table[i++].data = c;
+ table[i++].data = c;
+ table[i++].data = c;
+ table[i++].data = c;
+ table[i++].data = c;
+ table[i++].data = &c->disable_remixing;
+ table[i++].data = &c->disable_lfe_remixing;
+ table[i++].data = &c->load_default_script_file;
+ table[i++].data = &c->shm_size;
+ table[i++].data = &c->log_meta;
+ table[i++].data = &c->log_time;
+ table[i++].data = &c->log_backtrace;
#ifdef HAVE_SYS_RESOURCE_H
- table[30].data = &c->rlimit_fsize;
- table[31].data = &c->rlimit_data;
- table[32].data = &c->rlimit_stack;
- table[33].data = &c->rlimit_as;
- table[34].data = &c->rlimit_core;
- table[35].data = &c->rlimit_nofile;
- table[36].data = &c->rlimit_as;
+ table[i++].data = &c->rlimit_fsize;
+ table[i++].data = &c->rlimit_data;
+ table[i++].data = &c->rlimit_stack;
+ table[i++].data = &c->rlimit_as;
+ table[i++].data = &c->rlimit_core;
+ table[i++].data = &c->rlimit_nofile;
+ table[i++].data = &c->rlimit_as;
#ifdef RLIMIT_NPROC
- table[37].data = &c->rlimit_nproc;
+ table[i++].data = &c->rlimit_nproc;
#endif
-
#ifdef RLIMIT_MEMLOCK
-#ifndef RLIMIT_NPROC
-#error "Houston, we have a numbering problem!"
-#endif
- table[38].data = &c->rlimit_memlock;
+ table[i++].data = &c->rlimit_memlock;
#endif
-
#ifdef RLIMIT_LOCKS
-#ifndef RLIMIT_MEMLOCK
-#error "Houston, we have a numbering problem!"
-#endif
- table[39].data = &c->rlimit_locks;
+ table[i++].data = &c->rlimit_locks;
#endif
-
#ifdef RLIMIT_SIGPENDING
-#ifndef RLIMIT_LOCKS
-#error "Houston, we have a numbering problem!"
-#endif
- table[40].data = &c->rlimit_sigpending;
+ table[i++].data = &c->rlimit_sigpending;
#endif
-
#ifdef RLIMIT_MSGQUEUE
-#ifndef RLIMIT_SIGPENDING
-#error "Houston, we have a numbering problem!"
-#endif
- table[41].data = &c->rlimit_msgqueue;
+ table[i++].data = &c->rlimit_msgqueue;
#endif
-
#ifdef RLIMIT_NICE
-#ifndef RLIMIT_MSGQUEUE
-#error "Houston, we have a numbering problem!"
-#endif
- table[42].data = &c->rlimit_nice;
+ table[i++].data = &c->rlimit_nice;
#endif
-
#ifdef RLIMIT_RTPRIO
-#ifndef RLIMIT_NICE
-#error "Houston, we have a numbering problem!"
-#endif
- table[43].data = &c->rlimit_rtprio;
+ table[i++].data = &c->rlimit_rtprio;
#endif
-
#ifdef RLIMIT_RTTIME
-#ifndef RLIMIT_RTTIME
-#error "Houston, we have a numbering problem!"
-#endif
- table[44].data = &c->rlimit_rttime;
+ table[i++].data = &c->rlimit_rttime;
#endif
#endif
+ pa_assert(i == PA_ELEMENTSOF(table)-1);
+
pa_xfree(c->config_file);
c->config_file = NULL;
@@ -674,6 +658,9 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
pa_strbuf_printf(s, "default-fragments = %u\n", c->default_n_fragments);
pa_strbuf_printf(s, "default-fragment-size-msec = %u\n", c->default_fragment_size_msec);
pa_strbuf_printf(s, "shm-size-bytes = %lu\n", (unsigned long) c->shm_size);
+ pa_strbuf_printf(s, "log-meta = %s\n", pa_yes_no(c->log_meta));
+ pa_strbuf_printf(s, "log-time = %s\n", pa_yes_no(c->log_time));
+ pa_strbuf_printf(s, "log-backtrace = %u\n", c->log_backtrace);
#ifdef HAVE_SYS_RESOURCE_H
pa_strbuf_printf(s, "rlimit-fsize = %li\n", c->rlimit_fsize.is_set ? (long int) c->rlimit_fsize.value : -1);
pa_strbuf_printf(s, "rlimit-data = %li\n", c->rlimit_data.is_set ? (long int) c->rlimit_data.value : -1);
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index 90329268..04a4ebe7 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -68,7 +68,9 @@ typedef struct pa_daemon_conf {
disable_remixing,
disable_lfe_remixing,
load_default_script_file,
- disallow_exit;
+ disallow_exit,
+ log_meta,
+ log_time;
int exit_idle_time,
module_idle_time,
scache_idle_time,
@@ -79,6 +81,7 @@ typedef struct pa_daemon_conf {
char *script_commands, *dl_search_path, *default_script_file;
pa_log_target_t log_target;
pa_log_level_t log_level;
+ unsigned log_backtrace;
char *config_file;
#ifdef HAVE_SYS_RESOURCE_H
diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in
index c672d420..00a95932 100644
--- a/src/daemon/daemon.conf.in
+++ b/src/daemon/daemon.conf.in
@@ -45,6 +45,9 @@
; log-target = auto
; log-level = notice
+; log-meta = no
+; log-time = no
+; log-backtrace = 0
; resample-method = speex-float-3
; disable-remixing = no
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
index 7032038d..7de4c071 100755
--- a/src/daemon/default.pa.in
+++ b/src/daemon/default.pa.in
@@ -29,6 +29,10 @@
.fail
+### Automatically restore the volume of streams and devices
+load-module module-device-restore
+load-module module-stream-restore
+
### Load audio drivers statically (it's probably better to not load
### these drivers manually, but instead use module-hal-detect --
### see below -- for doing this automatically)
@@ -72,9 +76,8 @@ load-module module-native-protocol-unix
#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 description="RTP Multicast Sink"
#load-module module-rtp-send source=rtp.monitor
-### Automatically restore the volume of streams and devices
-load-module module-stream-restore
-load-module module-device-restore
+### Enable flat volumes where possible
+load-module module-flat-volume
### Automatically restore the default sink/source when changed by the user during runtime
load-module module-default-device-restore
diff --git a/src/daemon/ltdl-bind-now.c b/src/daemon/ltdl-bind-now.c
index 92e5d40d..8444cfb4 100644
--- a/src/daemon/ltdl-bind-now.c
+++ b/src/daemon/ltdl-bind-now.c
@@ -1,7 +1,7 @@
/***
This file is part of PulseAudio.
- Copyright 2004-2006 Lennart Poettering
+ Copyright 2004-2008 Lennart Poettering
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
PulseAudio is free software; you can redistribute it and/or modify
@@ -39,8 +39,6 @@
#include <pulse/i18n.h>
#include <pulsecore/macro.h>
-#include <pulsecore/mutex.h>
-#include <pulsecore/thread.h>
#include <pulsecore/log.h>
#include "ltdl-bind-now.h"
@@ -53,30 +51,6 @@
#undef PA_BIND_NOW
#endif
-#ifdef HAVE_LT_DLMUTEX_REGISTER
-
-static pa_mutex *libtool_mutex = NULL;
-
-static void libtool_lock(void) {
- pa_mutex_lock(libtool_mutex);
-}
-
-static void libtool_unlock(void) {
- pa_mutex_unlock(libtool_mutex);
-}
-
-#endif
-
-PA_STATIC_TLS_DECLARE_NO_FREE(libtool_tls);
-
-static void libtool_set_error(const char *error) {
- PA_STATIC_TLS_SET(libtool_tls, (char*) error);
-}
-
-static const char *libtool_get_error(void) {
- return PA_STATIC_TLS_GET(libtool_tls);
-}
-
#ifdef PA_BIND_NOW
/*
@@ -91,20 +65,13 @@ static const char *libtool_get_error(void) {
to set $LT_BIND_NOW before starting the pulsaudio binary.
*/
-#ifndef HAVE_LT_DLADVISE
-static lt_module bind_now_open(lt_user_data d, const char *fname)
-#else
-static lt_module bind_now_open(lt_user_data d, const char *fname, lt_dladvise advise)
-#endif
-{
+static lt_module bind_now_open(lt_user_data d, const char *fname, lt_dladvise advise) {
lt_module m;
pa_assert(fname);
if (!(m = dlopen(fname, PA_BIND_NOW))) {
-#ifdef HAVE_LT_DLMUTEX_REGISTER
- libtool_set_error(dlerror());
-#endif
+ lt_dlseterror(LT_ERROR_CANNOT_OPEN);
return NULL;
}
@@ -116,9 +83,7 @@ static int bind_now_close(lt_user_data d, lt_module m) {
pa_assert(m);
if (dlclose(m) != 0){
-#ifdef HAVE_LT_DLMUTEX_REGISTER
- libtool_set_error(dlerror());
-#endif
+ lt_dlseterror(LT_ERROR_CANNOT_CLOSE);
return 1;
}
@@ -132,78 +97,61 @@ static lt_ptr bind_now_find_sym(lt_user_data d, lt_module m, const char *symbol)
pa_assert(symbol);
if (!(ptr = dlsym(m, symbol))) {
-#ifdef HAVE_LT_DLMUTEX_REGISTER
- libtool_set_error(dlerror());
-#endif
+ lt_dlseterror(LT_ERROR_SYMBOL_NOT_FOUND);
return NULL;
}
return ptr;
}
+static lt_dlvtable *bindnow_loader = NULL;
#endif
void pa_ltdl_init(void) {
#ifdef PA_BIND_NOW
-# ifdef HAVE_STRUCT_LT_USER_DLLOADER
- lt_dlloader *place;
- static const struct lt_user_dlloader loader = {
- .module_open = bind_now_open,
- .module_close = bind_now_close,
- .find_sym = bind_now_find_sym
- };
-# else
- static const lt_dlvtable *dlopen_loader;
- static lt_dlvtable bindnow_loader;
-# endif
+ const lt_dlvtable *dlopen_loader;
#endif
pa_assert_se(lt_dlinit() == 0);
-#ifdef HAVE_LT_DLMUTEX_REGISTER
- pa_assert_se(libtool_mutex = pa_mutex_new(TRUE, FALSE));
- pa_assert_se(lt_dlmutex_register(libtool_lock, libtool_unlock, libtool_set_error, libtool_get_error) == 0);
-#endif
-
#ifdef PA_BIND_NOW
-# ifdef HAVE_STRUCT_LT_USER_DLLOADER
-
- if (!(place = lt_dlloader_find("dlopen")))
- place = lt_dlloader_next(NULL);
-
- /* Add our BIND_NOW loader as the default module loader. */
- if (lt_dlloader_add(place, &loader, "bind-now-loader") != 0)
- pa_log_warn(_("Failed to add bind-now-loader."));
-# else
/* Already initialised */
- if (dlopen_loader)
+ if (bindnow_loader)
return;
- if (!(dlopen_loader = lt_dlloader_find("dlopen"))) {
- pa_log_warn(_("Failed to find original dlopen loader."));
+ if (!(dlopen_loader = lt_dlloader_find((char*) "lt_dlopen"))) {
+ pa_log_warn(_("Failed to find original lt_dlopen loader."));
return;
}
- memcpy(&bindnow_loader, dlopen_loader, sizeof(bindnow_loader));
- bindnow_loader.name = "bind-now-loader";
- bindnow_loader.module_open = bind_now_open;
- bindnow_loader.module_close = bind_now_close;
- bindnow_loader.find_sym = bind_now_find_sym;
- bindnow_loader.priority = LT_DLLOADER_PREPEND;
+ if (!(bindnow_loader = malloc(sizeof(lt_dlvtable)))) {
+ pa_log_error(_("Failed to allocate new dl loader."));
+ return;
+ }
+
+ memcpy(bindnow_loader, dlopen_loader, sizeof(*bindnow_loader));
+ bindnow_loader->name = "bind-now-loader";
+ bindnow_loader->module_open = bind_now_open;
+ bindnow_loader->module_close = bind_now_close;
+ bindnow_loader->find_sym = bind_now_find_sym;
+ bindnow_loader->priority = LT_DLLOADER_PREPEND;
/* Add our BIND_NOW loader as the default module loader. */
- if (lt_dlloader_add(&bindnow_loader) != 0)
+ if (lt_dlloader_add(bindnow_loader) != 0) {
pa_log_warn(_("Failed to add bind-now-loader."));
-# endif
+ free(bindnow_loader);
+ bindnow_loader = NULL;
+ }
#endif
}
void pa_ltdl_done(void) {
pa_assert_se(lt_dlexit() == 0);
-#ifdef HAVE_LT_DLMUTEX_REGISTER
- pa_mutex_free(libtool_mutex);
- libtool_mutex = NULL;
+#ifdef PA_BIND_NOW
+ /* lt_dlexit() will free our loader vtable, hence reset our
+ * pointer to it here */
+ bindnow_loader = NULL;
#endif
}
diff --git a/src/daemon/main.c b/src/daemon/main.c
index bc8bc63e..9c419efc 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -222,7 +222,7 @@ static int change_user(void) {
#elif defined(HAVE_SETREGID)
r = setregid(gr->gr_gid, gr->gr_gid);
#else
-#error "No API to drop priviliges"
+#error "No API to drop privileges"
#endif
if (r < 0) {
@@ -238,7 +238,7 @@ static int change_user(void) {
#elif defined(HAVE_SETREUID)
r = setreuid(pw->pw_uid, pw->pw_uid);
#else
-#error "No API to drop priviliges"
+#error "No API to drop privileges"
#endif
if (r < 0) {
@@ -382,7 +382,7 @@ int main(int argc, char *argv[]) {
/* Drop all capabilities except CAP_SYS_NICE */
pa_limit_caps();
- /* Drop priviliges, but keep CAP_SYS_NICE */
+ /* Drop privileges, but keep CAP_SYS_NICE */
pa_drop_root();
/* After dropping root, the effective set is reset, hence,
@@ -428,10 +428,16 @@ int main(int argc, char *argv[]) {
pa_log_set_maximal_level(conf->log_level);
pa_log_set_target(conf->auto_log_target ? PA_LOG_STDERR : conf->log_target, NULL);
+ pa_log_set_show_meta(conf->log_meta);
+ pa_log_set_show_backtrace(conf->log_backtrace);
+ pa_log_set_show_time(conf->log_time);
pa_log_debug("Started as real root: %s, suid root: %s", pa_yes_no(real_root), pa_yes_no(suid_root));
if (!real_root && pa_have_caps()) {
+#ifdef HAVE_SYS_RESOURCE_H
+ struct rlimit rl;
+#endif
pa_bool_t allow_high_priority = FALSE, allow_realtime = FALSE;
/* Let's better not enable high prio or RT by default */
@@ -474,12 +480,35 @@ int main(int argc, char *argv[]) {
* let's give it up early */
pa_drop_caps();
-
- if (conf->high_priority || conf->realtime_scheduling)
- pa_log_notice(_("Called SUID root and real-time/high-priority scheduling was requested in the configuration. However, we lack the necessary priviliges:\n"
- "We are not in group '"PA_REALTIME_GROUP"' and PolicyKit refuse to grant us priviliges. Dropping SUID again.\n"
- "For enabling real-time scheduling please acquire the appropriate PolicyKit priviliges, or become a member of '"PA_REALTIME_GROUP"', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."));
}
+
+#ifdef RLIMIT_RTPRIO
+ if (getrlimit(RLIMIT_RTPRIO, &rl) >= 0)
+ if (rl.rlim_cur > 0) {
+ pa_log_info("RLIMIT_RTPRIO is set to %u, allowing real-time scheduling.", (unsigned) rl.rlim_cur);
+ allow_realtime = TRUE;
+ }
+#endif
+#ifdef RLIMIT_NICE
+ if (getrlimit(RLIMIT_NICE, &rl) >= 0)
+ if (rl.rlim_cur > 20 ) {
+ pa_log_info("RLIMIT_NICE is set to %u, allowing high-priority scheduling.", (unsigned) rl.rlim_cur);
+ allow_high_priority = TRUE;
+ }
+#endif
+
+ if ((conf->high_priority && !allow_high_priority) ||
+ (conf->realtime_scheduling && !allow_realtime))
+ pa_log_notice(_("Called SUID root and real-time and/or high-priority scheduling was requested in the configuration. However, we lack the necessary privileges:\n"
+ "We are not in group '"PA_REALTIME_GROUP"', PolicyKit refuse to grant us the requested privileges and we have no increase RLIMIT_NICE/RLIMIT_RTPRIO resource limits.\n"
+ "For enabling real-time/high-priority scheduling please acquire the appropriate PolicyKit privileges, or become a member of '"PA_REALTIME_GROUP"', or increase the RLIMIT_NICE/RLIMIT_RTPRIO resource limits for this user."));
+
+
+ if (!allow_realtime)
+ conf->realtime_scheduling = FALSE;
+
+ if (!allow_high_priority)
+ conf->high_priority = FALSE;
}
#ifdef HAVE_SYS_RESOURCE_H
@@ -493,12 +522,16 @@ int main(int argc, char *argv[]) {
set_all_rlimits(conf);
#endif
- if (conf->high_priority && !pa_can_high_priority())
+ if (conf->high_priority && !pa_can_high_priority()) {
pa_log_warn(_("High-priority scheduling enabled in configuration but not allowed by policy."));
+ conf->high_priority = FALSE;
+ }
if (conf->high_priority && (conf->cmd == PA_CMD_DAEMON || conf->cmd == PA_CMD_START))
pa_raise_priority(conf->nice_level);
+ pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority()));
+
if (!real_root && pa_have_caps()) {
pa_bool_t drop;
@@ -535,8 +568,10 @@ int main(int argc, char *argv[]) {
}
}
- if (conf->realtime_scheduling && !pa_can_realtime())
+ if (conf->realtime_scheduling && !pa_can_realtime()) {
pa_log_warn(_("Real-time scheduling enabled in configuration but not allowed by policy."));
+ conf->realtime_scheduling = FALSE;
+ }
pa_log_debug("Can realtime: %s, can high-priority: %s", pa_yes_no(pa_can_realtime()), pa_yes_no(pa_can_high_priority()));
@@ -577,6 +612,7 @@ int main(int argc, char *argv[]) {
if (pa_resample_method_supported(i))
printf("%s\n", pa_resample_method_to_string(i));
+ retval = 0;
goto finish;
}
@@ -626,7 +662,7 @@ int main(int argc, char *argv[]) {
if (real_root && !conf->system_instance)
pa_log_warn(_("This program is not intended to be run as root (unless --system is specified)."));
else if (!real_root && conf->system_instance) {
- pa_log(_("Root priviliges required."));
+ pa_log(_("Root privileges required."));
goto finish;
}
diff --git a/src/map-file b/src/map-file
index 7211914a..ca523b91 100644
--- a/src/map-file
+++ b/src/map-file
@@ -105,7 +105,6 @@ pa_cvolume_max;
pa_cvolume_remap;
pa_cvolume_set;
pa_cvolume_snprint;
-pa_sw_cvolume_snprint_dB;
pa_cvolume_valid;
pa_ext_stream_restore_delete;
pa_ext_stream_restore_read;
@@ -228,7 +227,10 @@ pa_stream_update_timing_info;
pa_stream_writable_size;
pa_stream_write;
pa_strerror;
+pa_sw_cvolume_divide;
pa_sw_cvolume_multiply;
+pa_sw_cvolume_snprint_dB;
+pa_sw_volume_divide;
pa_sw_volume_from_dB;
pa_sw_volume_from_linear;
pa_sw_volume_multiply;
diff --git a/src/modules/alsa-util.c b/src/modules/alsa-util.c
index ffe7795e..ff3af19d 100644
--- a/src/modules/alsa-util.c
+++ b/src/modules/alsa-util.c
@@ -30,6 +30,7 @@
#include <pulse/sample.h>
#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
@@ -555,6 +556,7 @@ snd_pcm_t *pa_alsa_open_by_device_id(
for (i = 0;; i += direction) {
pa_sample_spec try_ss;
+ pa_bool_t reformat;
if (i < 0) {
pa_assert(direction == -1);
@@ -579,8 +581,9 @@ snd_pcm_t *pa_alsa_open_by_device_id(
d = pa_sprintf_malloc("%s:%s", device_table[i].name, dev_id);
+ reformat = FALSE;
for (;;) {
- pa_log_debug("Trying %s...", d);
+ pa_log_debug("Trying %s %s SND_PCM_NO_AUTO_FORMAT ...", d, reformat ? "without" : "with");
/* We don't pass SND_PCM_NONBLOCK here, since alsa-lib <=
* 1.0.17a would then ignore the SND_PCM_NO_xxx
@@ -592,7 +595,7 @@ snd_pcm_t *pa_alsa_open_by_device_id(
/* SND_PCM_NONBLOCK| */
SND_PCM_NO_AUTO_RESAMPLE|
SND_PCM_NO_AUTO_CHANNELS|
- SND_PCM_NO_AUTO_FORMAT)) < 0) {
+ (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) {
pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err));
break;
}
@@ -603,6 +606,12 @@ snd_pcm_t *pa_alsa_open_by_device_id(
if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) {
+ if (!reformat) {
+ reformat = TRUE;
+ snd_pcm_close(pcm_handle);
+ continue;
+ }
+
if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) {
char *t;
@@ -610,6 +619,8 @@ snd_pcm_t *pa_alsa_open_by_device_id(
pa_xfree(d);
d = t;
+ reformat = FALSE;
+
snd_pcm_close(pcm_handle);
continue;
}
@@ -654,6 +665,7 @@ snd_pcm_t *pa_alsa_open_by_device_string(
int err;
char *d;
snd_pcm_t *pcm_handle;
+ pa_bool_t reformat = FALSE;
pa_assert(device);
pa_assert(dev);
@@ -665,7 +677,7 @@ snd_pcm_t *pa_alsa_open_by_device_string(
d = pa_xstrdup(device);
for (;;) {
- pa_log_debug("Trying %s...", d);
+ pa_log_debug("Trying %s %s SND_PCM_NO_AUTO_FORMAT ...", d, reformat ? "without" : "with");
/* We don't pass SND_PCM_NONBLOCK here, since alsa-lib <=
* 1.0.17a would then ignore the SND_PCM_NO_xxx flags. Instead
@@ -677,7 +689,7 @@ snd_pcm_t *pa_alsa_open_by_device_string(
/*SND_PCM_NONBLOCK|*/
SND_PCM_NO_AUTO_RESAMPLE|
SND_PCM_NO_AUTO_CHANNELS|
- SND_PCM_NO_AUTO_FORMAT)) < 0) {
+ (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) {
pa_log("Error opening PCM device %s: %s", d, snd_strerror(err));
pa_xfree(d);
return NULL;
@@ -685,6 +697,13 @@ snd_pcm_t *pa_alsa_open_by_device_string(
if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE)) < 0) {
+ if (!reformat) {
+ reformat = TRUE;
+
+ snd_pcm_close(pcm_handle);
+ continue;
+ }
+
/* Hmm, some hw is very exotic, so we retry with plug, if without it didn't work */
if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) {
@@ -694,6 +713,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
pa_xfree(d);
d = t;
+ reformat = FALSE;
+
snd_pcm_close(pcm_handle);
continue;
}
@@ -739,8 +760,32 @@ int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) {
return 0;
}
-snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback) {
- snd_mixer_elem_t *elem;
+static pa_bool_t elem_has_volume(snd_mixer_elem_t *elem, pa_bool_t playback) {
+ pa_assert(elem);
+
+ if (playback && snd_mixer_selem_has_playback_volume(elem))
+ return TRUE;
+
+ if (!playback && snd_mixer_selem_has_capture_volume(elem))
+ return TRUE;
+
+ return FALSE;
+}
+
+static pa_bool_t elem_has_switch(snd_mixer_elem_t *elem, pa_bool_t playback) {
+ pa_assert(elem);
+
+ if (playback && snd_mixer_selem_has_playback_switch(elem))
+ return TRUE;
+
+ if (!playback && snd_mixer_selem_has_capture_switch(elem))
+ return TRUE;
+
+ return FALSE;
+}
+
+snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback) {
+ snd_mixer_elem_t *elem = NULL, *fallback_elem = NULL;
snd_mixer_selem_id_t *sid = NULL;
snd_mixer_selem_id_alloca(&sid);
@@ -750,17 +795,57 @@ snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const
snd_mixer_selem_id_set_name(sid, name);
- if (!(elem = snd_mixer_find_selem(mixer, sid))) {
- pa_log_info("Cannot find mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
+ if ((elem = snd_mixer_find_selem(mixer, sid))) {
+
+ if (elem_has_volume(elem, playback) &&
+ elem_has_switch(elem, playback))
+ goto success;
- if (fallback) {
- snd_mixer_selem_id_set_name(sid, fallback);
+ if (!elem_has_volume(elem, playback) &&
+ !elem_has_switch(elem, playback))
+ elem = NULL;
+ }
+
+ pa_log_info("Cannot find mixer control \"%s\" or mixer control is no combination of switch/volume.", snd_mixer_selem_id_get_name(sid));
+
+ if (fallback) {
+ snd_mixer_selem_id_set_name(sid, fallback);
+
+ if ((fallback_elem = snd_mixer_find_selem(mixer, sid))) {
- if (!(elem = snd_mixer_find_selem(mixer, sid)))
- pa_log_warn("Cannot find fallback mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
+ if (elem_has_volume(fallback_elem, playback) &&
+ elem_has_switch(fallback_elem, playback)) {
+ elem = fallback_elem;
+ goto success;
+ }
+
+ if (!elem_has_volume(fallback_elem, playback) &&
+ !elem_has_switch(fallback_elem, playback))
+ fallback_elem = NULL;
}
+
+ pa_log_warn("Cannot find fallback mixer control \"%s\" or mixer control is no combination of switch/volume.", snd_mixer_selem_id_get_name(sid));
}
+ if (elem && fallback_elem) {
+
+ /* Hmm, so we have both elements, but neither has both mute
+ * and volume. Let's prefer the one with the volume */
+
+ if (elem_has_volume(elem, playback))
+ goto success;
+
+ if (elem_has_volume(fallback_elem, playback)) {
+ elem = fallback_elem;
+ goto success;
+ }
+ }
+
+ if (!elem && fallback_elem)
+ elem = fallback_elem;
+
+success:
+
if (elem)
pa_log_info("Using mixer control \"%s\".", snd_mixer_selem_id_get_name(sid));
@@ -1045,14 +1130,20 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) {
pa_assert(pcm);
if (revents & POLLERR)
- pa_log_warn("Got POLLERR from ALSA");
+ pa_log_debug("Got POLLERR from ALSA");
if (revents & POLLNVAL)
pa_log_warn("Got POLLNVAL from ALSA");
if (revents & POLLHUP)
pa_log_warn("Got POLLHUP from ALSA");
+ if (revents & POLLPRI)
+ pa_log_warn("Got POLLPRI from ALSA");
+ if (revents & POLLIN)
+ pa_log_debug("Got POLLIN from ALSA");
+ if (revents & POLLOUT)
+ pa_log_debug("Got POLLOUT from ALSA");
state = snd_pcm_state(pcm);
- pa_log_warn("PCM state is %s", snd_pcm_state_name(state));
+ pa_log_debug("PCM state is %s", snd_pcm_state_name(state));
/* Try to recover from this error */
@@ -1109,3 +1200,62 @@ pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll) {
return item;
}
+
+snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss) {
+ snd_pcm_sframes_t n;
+ size_t k;
+
+ pa_assert(pcm);
+ pa_assert(hwbuf_size > 0);
+ pa_assert(ss);
+
+ /* Some ALSA driver expose weird bugs, let's inform the user about
+ * what is going on */
+
+ n = snd_pcm_avail_update(pcm);
+
+ if (n <= 0)
+ return n;
+
+ k = (size_t) n * pa_frame_size(ss);
+
+ if (k >= hwbuf_size * 3 ||
+ k >= pa_bytes_per_second(ss)*10)
+ pa_log("snd_pcm_avail_update() returned a value that is exceptionally large: %lu bytes (%lu ms) "
+ "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.",
+ (unsigned long) k, (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC));
+
+ return n;
+}
+
+int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss) {
+ int r;
+ snd_pcm_uframes_t before;
+ size_t k;
+
+ pa_assert(pcm);
+ pa_assert(areas);
+ pa_assert(offset);
+ pa_assert(frames);
+ pa_assert(hwbuf_size > 0);
+ pa_assert(ss);
+
+ before = *frames;
+
+ r = snd_pcm_mmap_begin(pcm, areas, offset, frames);
+
+ if (r < 0)
+ return r;
+
+ k = (size_t) *frames * pa_frame_size(ss);
+
+ if (*frames > before ||
+ k >= hwbuf_size * 3 ||
+ k >= pa_bytes_per_second(ss)*10)
+
+ pa_log("snd_pcm_mmap_begin() returned a value that is exceptionally large: %lu bytes (%lu ms) "
+ "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.",
+ (unsigned long) k, (unsigned long) (pa_bytes_to_usec(k, ss) / PA_USEC_PER_MSEC));
+
+ return r;
+}
diff --git a/src/modules/alsa-util.h b/src/modules/alsa-util.h
index b66adc13..95bb983a 100644
--- a/src/modules/alsa-util.h
+++ b/src/modules/alsa-util.h
@@ -52,7 +52,7 @@ int pa_alsa_set_hw_params(
int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min);
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);
-snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback);
+snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback);
snd_pcm_t *pa_alsa_open_by_device_id(
const char *dev_id,
@@ -92,4 +92,7 @@ int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents);
pa_rtpoll_item* pa_alsa_build_pollfd(snd_pcm_t *pcm, pa_rtpoll *rtpoll);
+snd_pcm_sframes_t pa_alsa_safe_avail_update(snd_pcm_t *pcm, size_t hwbuf_size, const pa_sample_spec *ss);
+int pa_alsa_safe_mmap_begin(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames, size_t hwbuf_size, const pa_sample_spec *ss);
+
#endif
diff --git a/src/modules/bluetooth/ipc.c b/src/modules/bluetooth/ipc.c
index 98256998..67785309 100644
--- a/src/modules/bluetooth/ipc.c
+++ b/src/modules/bluetooth/ipc.c
@@ -22,22 +22,26 @@
#include "ipc.h"
-/* This table contains the string representation for messages */
-static const char *strmsg[] = {
- "BT_GETCAPABILITIES_REQ",
- "BT_GETCAPABILITIES_RSP",
- "BT_SETCONFIGURATION_REQ",
- "BT_SETCONFIGURATION_RSP",
- "BT_STREAMSTART_REQ",
- "BT_STREAMSTART_RSP",
- "BT_STREAMSTOP_REQ",
- "BT_STREAMSTOP_RSP",
- "BT_STREAMSUSPEND_IND",
- "BT_STREAMRESUME_IND",
- "BT_CONTROL_REQ",
- "BT_CONTROL_RSP",
- "BT_CONTROL_IND",
- "BT_STREAMFD_IND",
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+/* This table contains the string representation for messages types */
+static const char *strtypes[] = {
+ "BT_REQUEST",
+ "BT_RESPONSE",
+ "BT_INDICATION",
+ "BT_ERROR",
+};
+
+/* This table contains the string representation for messages names */
+static const char *strnames[] = {
+ "BT_GET_CAPABILITIES",
+ "BT_SET_CONFIGURATION",
+ "BT_NEW_STREAM",
+ "BT_START_STREAM",
+ "BT_STOP_STREAM",
+ "BT_SUSPEND_STREAM",
+ "BT_RESUME_STREAM",
+ "BT_CONTROL",
};
int bt_audio_service_open(void)
@@ -88,7 +92,7 @@ int bt_audio_service_get_data_fd(int sk)
msgh.msg_control = &cmsg_b;
msgh.msg_controllen = CMSG_LEN(sizeof(int));
- ret = (int) recvmsg(sk, &msgh, 0);
+ ret = recvmsg(sk, &msgh, 0);
if (ret < 0) {
err = errno;
fprintf(stderr, "%s: Unable to receive fd: %s (%d)\n",
@@ -109,10 +113,18 @@ int bt_audio_service_get_data_fd(int sk)
return -1;
}
-const char *bt_audio_strmsg(int type)
+const char *bt_audio_strtype(uint8_t type)
+{
+ if (type >= ARRAY_SIZE(strtypes))
+ return NULL;
+
+ return strtypes[type];
+}
+
+const char *bt_audio_strname(uint8_t name)
{
- if (type < 0 || (size_t) type > (sizeof(strmsg) / sizeof(strmsg[0])))
+ if (name >= ARRAY_SIZE(strnames))
return NULL;
- return strmsg[type];
+ return strnames[name];
}
diff --git a/src/modules/bluetooth/ipc.h b/src/modules/bluetooth/ipc.h
index ae85e727..0e985c3a 100644
--- a/src/modules/bluetooth/ipc.h
+++ b/src/modules/bluetooth/ipc.h
@@ -23,36 +23,36 @@
/*
Message sequence chart of streaming sequence for A2DP transport
- Audio daemon User
- on snd_pcm_open
- <--BT_GETCAPABILITIES_REQ
+ Audio daemon User
+ on snd_pcm_open
+ <--BT_GET_CAPABILITIES_REQ
- BT_GETCAPABILITIES_RSP-->
+ BT_GET_CAPABILITIES_RSP-->
- on snd_pcm_hw_params
- <--BT_SETCONFIGURATION_REQ
+ on snd_pcm_hw_params
+ <--BT_SETCONFIGURATION_REQ
- BT_SETCONFIGURATION_RSP-->
+ BT_SET_CONFIGURATION_RSP-->
- on snd_pcm_prepare
- <--BT_STREAMSTART_REQ
+ on snd_pcm_prepare
+ <--BT_START_STREAM_REQ
<Moves to streaming state>
- BT_STREAMSTART_RSP-->
+ BT_START_STREAM_RSP-->
- BT_STREAMFD_IND -->
+ BT_NEW_STREAM_IND -->
- < streams data >
- ..........
+ < streams data >
+ ..........
- on snd_pcm_drop/snd_pcm_drain
+ on snd_pcm_drop/snd_pcm_drain
- <--BT_STREAMSTOP_REQ
+ <--BT_STOP_STREAM_REQ
<Moves to open state>
- BT_STREAMSTOP_RSP-->
+ BT_STOP_STREAM_RSP-->
- on IPC close or appl crash
+ on IPC close or appl crash
<Moves to idle>
*/
@@ -71,43 +71,36 @@ extern "C" {
#include <sys/un.h>
#include <errno.h>
-#define BT_AUDIO_IPC_PACKET_SIZE 128
+#define BT_SUGGESTED_BUFFER_SIZE 128
#define BT_IPC_SOCKET_NAME "\0/org/bluez/audio"
-/* Generic message header definition, except for RSP messages */
+/* Generic message header definition, except for RESPONSE messages */
typedef struct {
- uint8_t msg_type;
+ uint8_t type;
+ uint8_t name;
+ uint16_t length;
} __attribute__ ((packed)) bt_audio_msg_header_t;
-/* Generic message header definition, for all RSP messages */
typedef struct {
- bt_audio_msg_header_t msg_h;
- uint8_t posix_errno;
-} __attribute__ ((packed)) bt_audio_rsp_msg_header_t;
-
-/* Messages list */
-#define BT_GETCAPABILITIES_REQ 0
-#define BT_GETCAPABILITIES_RSP 1
-
-#define BT_SETCONFIGURATION_REQ 2
-#define BT_SETCONFIGURATION_RSP 3
-
-#define BT_STREAMSTART_REQ 4
-#define BT_STREAMSTART_RSP 5
-
-#define BT_STREAMSTOP_REQ 6
-#define BT_STREAMSTOP_RSP 7
-
-#define BT_STREAMSUSPEND_IND 8
-#define BT_STREAMRESUME_IND 9
-
-#define BT_CONTROL_REQ 10
-#define BT_CONTROL_RSP 11
-#define BT_CONTROL_IND 12
-
-#define BT_STREAMFD_IND 13
-
-/* BT_GETCAPABILITIES_REQ */
+ bt_audio_msg_header_t h;
+ uint8_t posix_errno;
+} __attribute__ ((packed)) bt_audio_error_t;
+
+/* Message types */
+#define BT_REQUEST 0
+#define BT_RESPONSE 1
+#define BT_INDICATION 2
+#define BT_ERROR 3
+
+/* Messages names */
+#define BT_GET_CAPABILITIES 0
+#define BT_SET_CONFIGURATION 1
+#define BT_NEW_STREAM 2
+#define BT_START_STREAM 3
+#define BT_STOP_STREAM 4
+#define BT_SUSPEND_STREAM 5
+#define BT_RESUME_STREAM 6
+#define BT_CONTROL 7
#define BT_CAPABILITIES_TRANSPORT_A2DP 0
#define BT_CAPABILITIES_TRANSPORT_SCO 1
@@ -119,19 +112,22 @@ typedef struct {
#define BT_FLAG_AUTOCONNECT 1
-struct bt_getcapabilities_req {
+struct bt_get_capabilities_req {
bt_audio_msg_header_t h;
char device[18]; /* Address of the remote Device */
uint8_t transport; /* Requested transport */
uint8_t flags; /* Requested flags */
} __attribute__ ((packed));
-/* BT_GETCAPABILITIES_RSP */
-
/**
* SBC Codec parameters as per A2DP profile 1.0 § 4.3
*/
+#define BT_A2DP_CODEC_SBC 0x00
+#define BT_A2DP_CODEC_MPEG12 0x01
+#define BT_A2DP_CODEC_MPEG24 0x02
+#define BT_A2DP_CODEC_ATRAC 0x03
+
#define BT_SBC_SAMPLING_FREQ_16000 (1 << 3)
#define BT_SBC_SAMPLING_FREQ_32000 (1 << 2)
#define BT_SBC_SAMPLING_FREQ_44100 (1 << 1)
@@ -164,7 +160,19 @@ struct bt_getcapabilities_req {
#define BT_MPEG_LAYER_2 (1 << 1)
#define BT_MPEG_LAYER_3 1
+#define BT_HFP_CODEC_PCM 0x00
+
+#define BT_PCM_FLAG_NREC 1
+
+typedef struct {
+ uint8_t transport;
+ uint8_t type;
+ uint8_t length;
+ uint8_t data[0];
+} __attribute__ ((packed)) codec_capabilities_t;
+
typedef struct {
+ codec_capabilities_t capability;
uint8_t channel_mode;
uint8_t frequency;
uint8_t allocation_method;
@@ -175,6 +183,7 @@ typedef struct {
} __attribute__ ((packed)) sbc_capabilities_t;
typedef struct {
+ codec_capabilities_t capability;
uint8_t channel_mode;
uint8_t crc;
uint8_t layer;
@@ -183,75 +192,65 @@ typedef struct {
uint16_t bitrate;
} __attribute__ ((packed)) mpeg_capabilities_t;
-struct bt_getcapabilities_rsp {
- bt_audio_rsp_msg_header_t rsp_h;
- uint8_t transport; /* Granted transport */
- sbc_capabilities_t sbc_capabilities; /* A2DP only */
- mpeg_capabilities_t mpeg_capabilities; /* A2DP only */
- uint16_t sampling_rate; /* SCO only */
+typedef struct {
+ codec_capabilities_t capability;
+ uint8_t flags;
+ uint16_t sampling_rate;
+} __attribute__ ((packed)) pcm_capabilities_t;
+
+
+struct bt_get_capabilities_rsp {
+ bt_audio_msg_header_t h;
+ uint8_t data[0]; /* First codec_capabilities_t */
} __attribute__ ((packed));
-/* BT_SETCONFIGURATION_REQ */
-struct bt_setconfiguration_req {
+struct bt_set_configuration_req {
bt_audio_msg_header_t h;
- char device[18]; /* Address of the remote Device */
- uint8_t transport; /* Requested transport */
- uint8_t access_mode; /* Requested access mode */
- sbc_capabilities_t sbc_capabilities; /* A2DP only - only one of this field
- and next one must be filled */
- mpeg_capabilities_t mpeg_capabilities; /* A2DP only */
+ char device[18]; /* Address of the remote Device */
+ uint8_t access_mode; /* Requested access mode */
+ codec_capabilities_t codec; /* Requested codec */
} __attribute__ ((packed));
-/* BT_SETCONFIGURATION_RSP */
-struct bt_setconfiguration_rsp {
- bt_audio_rsp_msg_header_t rsp_h;
- uint8_t transport; /* Granted transport */
- uint8_t access_mode; /* Granted access mode */
- uint16_t link_mtu; /* Max length that transport supports */
+struct bt_set_configuration_rsp {
+ bt_audio_msg_header_t h;
+ uint8_t transport; /* Granted transport */
+ uint8_t access_mode; /* Granted access mode */
+ uint16_t link_mtu; /* Max length that transport supports */
} __attribute__ ((packed));
-/* BT_STREAMSTART_REQ */
#define BT_STREAM_ACCESS_READ 0
#define BT_STREAM_ACCESS_WRITE 1
#define BT_STREAM_ACCESS_READWRITE 2
-struct bt_streamstart_req {
+struct bt_start_stream_req {
bt_audio_msg_header_t h;
} __attribute__ ((packed));
-/* BT_STREAMSTART_RSP */
-struct bt_streamstart_rsp {
- bt_audio_rsp_msg_header_t rsp_h;
+struct bt_start_stream_rsp {
+ bt_audio_msg_header_t h;
} __attribute__ ((packed));
-/* BT_STREAMFD_IND */
/* This message is followed by one byte of data containing the stream data fd
as ancilliary data */
-struct bt_streamfd_ind {
+struct bt_new_stream_ind {
bt_audio_msg_header_t h;
} __attribute__ ((packed));
-/* BT_STREAMSTOP_REQ */
-struct bt_streamstop_req {
+struct bt_stop_stream_req {
bt_audio_msg_header_t h;
} __attribute__ ((packed));
-/* BT_STREAMSTOP_RSP */
-struct bt_streamstop_rsp {
- bt_audio_rsp_msg_header_t rsp_h;
+struct bt_stop_stream_rsp {
+ bt_audio_msg_header_t h;
} __attribute__ ((packed));
-/* BT_STREAMSUSPEND_IND */
-struct bt_streamsuspend_ind {
+struct bt_suspend_stream_ind {
bt_audio_msg_header_t h;
} __attribute__ ((packed));
-/* BT_STREAMRESUME_IND */
-struct bt_streamresume_ind {
+struct bt_resume_stream_ind {
bt_audio_msg_header_t h;
} __attribute__ ((packed));
-/* BT_CONTROL_REQ */
-
#define BT_CONTROL_KEY_POWER 0x40
#define BT_CONTROL_KEY_VOL_UP 0x41
#define BT_CONTROL_KEY_VOL_DOWN 0x42
@@ -272,14 +271,12 @@ struct bt_control_req {
uint8_t key; /* Control Key */
} __attribute__ ((packed));
-/* BT_CONTROL_RSP */
struct bt_control_rsp {
- bt_audio_rsp_msg_header_t rsp_h;
- uint8_t mode; /* Control Mode */
- uint8_t key; /* Control Key */
+ bt_audio_msg_header_t h;
+ uint8_t mode; /* Control Mode */
+ uint8_t key; /* Control Key */
} __attribute__ ((packed));
-/* BT_CONTROL_IND */
struct bt_control_ind {
bt_audio_msg_header_t h;
uint8_t mode; /* Control Mode */
@@ -299,7 +296,10 @@ BT_STREAMFD_IND message is returned */
int bt_audio_service_get_data_fd(int sk);
/* Human readable message type string */
-const char *bt_audio_strmsg(int type);
+const char *bt_audio_strtype(uint8_t type);
+
+/* Human readable message name string */
+const char *bt_audio_strname(uint8_t name);
#ifdef __cplusplus
}
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 3460fe9a..cb4746a4 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -122,8 +122,14 @@ static const char* const valid_modargs[] = {
static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
int e;
- pa_log_debug("sending %s", bt_audio_strmsg(msg->msg_type));
- if (send(sk, msg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0)
+ const char *type, *name;
+ uint16_t length;
+
+ length = msg->length ? msg->length : BT_SUGGESTED_BUFFER_SIZE;
+ type = bt_audio_strtype(msg->type);
+ name = bt_audio_strname(msg->name);
+ pa_log_debug("sending: %s -> %s", type, name);
+ if (send(sk, msg, length, 0) > 0)
e = 0;
else {
e = -errno;
@@ -132,20 +138,25 @@ static int bt_audioservice_send(int sk, const bt_audio_msg_header_t *msg) {
return e;
}
-static int bt_audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) {
+static int bt_audioservice_recv(int sk, bt_audio_msg_header_t *inmsg, uint16_t expected_length) {
int e;
- const char *type;
+ const char *type, *name;
+ uint16_t length;
+
+ length = expected_length ? expected_length : BT_SUGGESTED_BUFFER_SIZE;
pa_log_debug("trying to receive msg from audio service...");
- if (recv(sk, inmsg, BT_AUDIO_IPC_PACKET_SIZE, 0) > 0) {
- type = bt_audio_strmsg(inmsg->msg_type);
- if (type) {
- pa_log_debug("Received %s", type);
+ if (recv(sk, inmsg, length, 0) > 0) {
+ type = bt_audio_strtype(inmsg->type);
+ name = bt_audio_strname(inmsg->name);
+ if (type && name) {
+ pa_log_debug("Received: %s <- %s", type, name);
e = 0;
}
else {
e = -EINVAL;
- pa_log_error("Bogus message type %d received from audio service", inmsg->msg_type);
+ pa_log_error("Bogus message type %d name %d received from audio service",
+ inmsg->type, inmsg->name);
}
}
else {
@@ -156,29 +167,66 @@ static int bt_audioservice_recv(int sk, bt_audio_msg_header_t *inmsg) {
return e;
}
-static int bt_audioservice_expect(int sk, bt_audio_msg_header_t *rsp_hdr, int expected_type) {
- int e = bt_audioservice_recv(sk, rsp_hdr);
- if (e == 0) {
- if (rsp_hdr->msg_type != expected_type) {
+static int bt_audioservice_expect(int sk, bt_audio_msg_header_t *rsp, uint8_t expected_name, uint16_t expected_length) {
+ int e = bt_audioservice_recv(sk, rsp, expected_length);
+
+ if (e < 0) {
+ if (rsp->name != expected_name) {
e = -EINVAL;
- pa_log_error("Bogus message %s received while %s was expected", bt_audio_strmsg(rsp_hdr->msg_type),
- bt_audio_strmsg(expected_type));
+ pa_log_error("Bogus message %s received while %s was expected",
+ bt_audio_strname(rsp->name),
+ bt_audio_strname(expected_name));
}
}
+
+ if (rsp->type == BT_ERROR) {
+ bt_audio_error_t *error = (void *) rsp;
+ pa_log_error("%s failed : %s(%d)", bt_audio_strname(rsp->name), pa_cstrerror(error->posix_errno), error->posix_errno);
+ return -error->posix_errno;
+ }
+
return e;
}
+static int bt_parsecaps(struct userdata *u, struct bt_get_capabilities_rsp *rsp) {
+ uint16_t bytes_left = rsp->h.length - sizeof(*rsp);
+ codec_capabilities_t *codec = (void *) rsp->data;
+
+ u->transport = codec->transport;
+
+ if (codec->transport != BT_CAPABILITIES_TRANSPORT_A2DP)
+ return 0;
+
+ while (bytes_left > 0) {
+ if (codec->type == BT_A2DP_CODEC_SBC)
+ break;
+
+ bytes_left -= codec->length;
+ codec = (void *) (codec + codec->length);
+ }
+
+ if (bytes_left <= 0 || codec->length != sizeof(u->a2dp.sbc_capabilities))
+ return -EINVAL;
+
+ memcpy(&u->a2dp.sbc_capabilities, codec, codec->length);
+
+ return 0;
+}
+
static int bt_getcaps(struct userdata *u) {
int e;
union {
- bt_audio_rsp_msg_header_t rsp_hdr;
- struct bt_getcapabilities_req getcaps_req;
- struct bt_getcapabilities_rsp getcaps_rsp;
- uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE];
+ bt_audio_msg_header_t rsp;
+ struct bt_get_capabilities_req getcaps_req;
+ struct bt_get_capabilities_rsp getcaps_rsp;
+ uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
} msg;
- memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE);
- msg.getcaps_req.h.msg_type = BT_GETCAPABILITIES_REQ;
+ memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
+ msg.getcaps_req.h.type = BT_REQUEST;
+ msg.getcaps_req.h.name = BT_GET_CAPABILITIES;
+ msg.getcaps_req.h.length = sizeof(msg.getcaps_req);
+
strncpy(msg.getcaps_req.device, u->addr, 18);
if (strcasecmp(u->profile, "a2dp") == 0)
msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
@@ -196,20 +244,13 @@ static int bt_getcaps(struct userdata *u) {
return e;
}
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_GETCAPABILITIES_RSP);
+ e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp, BT_GET_CAPABILITIES, 0);
if (e < 0) {
pa_log_error("Failed to expect for GETCAPABILITIES_RSP");
return e;
}
- if (msg.rsp_hdr.posix_errno != 0) {
- pa_log_error("BT_GETCAPABILITIES failed : %s (%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno);
- return -msg.rsp_hdr.posix_errno;
- }
-
- if ((u->transport = msg.getcaps_rsp.transport) == BT_CAPABILITIES_TRANSPORT_A2DP)
- u->a2dp.sbc_capabilities = msg.getcaps_rsp.sbc_capabilities;
- return 0;
+ return bt_parsecaps(u, &msg.getcaps_rsp);
}
static uint8_t default_bitpool(uint8_t freq, uint8_t mode) {
@@ -393,10 +434,10 @@ static void bt_a2dp_setup(struct bt_a2dp *a2dp) {
static int bt_setconf(struct userdata *u) {
int e;
union {
- bt_audio_rsp_msg_header_t rsp_hdr;
- struct bt_setconfiguration_req setconf_req;
- struct bt_setconfiguration_rsp setconf_rsp;
- uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE];
+ bt_audio_msg_header_t rsp;
+ struct bt_set_configuration_req setconf_req;
+ struct bt_set_configuration_rsp setconf_rsp;
+ uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
} msg;
if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
@@ -410,12 +451,19 @@ static int bt_setconf(struct userdata *u) {
else
u->ss.format = PA_SAMPLE_U8;
- memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE);
- msg.setconf_req.h.msg_type = BT_SETCONFIGURATION_REQ;
+ memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
+ msg.setconf_req.h.type = BT_REQUEST;
+ msg.setconf_req.h.name = BT_SET_CONFIGURATION;
+ msg.setconf_req.h.length = sizeof(msg.setconf_req);
+
strncpy(msg.setconf_req.device, u->addr, 18);
- msg.setconf_req.transport = u->transport;
- if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP)
- msg.setconf_req.sbc_capabilities = u->a2dp.sbc_capabilities;
+ msg.setconf_req.codec.transport = u->transport;
+ if (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP) {
+ memcpy(&msg.setconf_req.codec, &u->a2dp.sbc_capabilities,
+ sizeof(u->a2dp.sbc_capabilities));
+ msg.setconf_req.h.length += msg.setconf_req.codec.length
+ - sizeof(msg.setconf_req.codec);
+ }
msg.setconf_req.access_mode = BT_CAPABILITIES_ACCESS_MODE_WRITE;
e = bt_audioservice_send(u->audioservice_fd, &msg.setconf_req.h);
@@ -424,17 +472,12 @@ static int bt_setconf(struct userdata *u) {
return e;
}
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_SETCONFIGURATION_RSP);
+ e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp, BT_SET_CONFIGURATION, sizeof(msg.setconf_rsp));
if (e < 0) {
pa_log_error("Failed to expect BT_SETCONFIGURATION_RSP");
return e;
}
- if (msg.rsp_hdr.posix_errno != 0) {
- pa_log_error("BT_SETCONFIGURATION failed : %s(%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno);
- return -msg.rsp_hdr.posix_errno;
- }
-
u->transport = msg.setconf_rsp.transport;
u->strtransport = (u->transport == BT_CAPABILITIES_TRANSPORT_A2DP ? pa_xstrdup("A2DP") : pa_xstrdup("SCO"));
u->link_mtu = msg.setconf_rsp.link_mtu;
@@ -456,14 +499,17 @@ static int bt_getstreamfd(struct userdata *u) {
int e;
// uint32_t period_count = io->buffer_size / io->period_size;
union {
- bt_audio_rsp_msg_header_t rsp_hdr;
- struct bt_streamstart_req start_req;
- struct bt_streamfd_ind streamfd_ind;
- uint8_t buf[BT_AUDIO_IPC_PACKET_SIZE];
+ bt_audio_msg_header_t rsp;
+ struct bt_start_stream_req start_req;
+ struct bt_start_stream_rsp start_rsp;
+ struct bt_new_stream_ind streamfd_ind;
+ uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
} msg;
- memset(msg.buf, 0, BT_AUDIO_IPC_PACKET_SIZE);
- msg.start_req.h.msg_type = BT_STREAMSTART_REQ;
+ memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
+ msg.start_req.h.type = BT_REQUEST;
+ msg.start_req.h.name = BT_START_STREAM;
+ msg.start_req.h.length = sizeof(msg.start_req);
e = bt_audioservice_send(u->audioservice_fd, &msg.start_req.h);
if (e < 0) {
@@ -471,18 +517,13 @@ static int bt_getstreamfd(struct userdata *u) {
return e;
}
- e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp_hdr.msg_h, BT_STREAMSTART_RSP);
+ e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp, BT_START_STREAM, sizeof(msg.start_rsp));
if (e < 0) {
pa_log_error("Failed to expect BT_STREAMSTART_RSP");
return e;
}
- if (msg.rsp_hdr.posix_errno != 0) {
- pa_log_error("BT_START failed : %s(%d)", pa_cstrerror(msg.rsp_hdr.posix_errno), msg.rsp_hdr.posix_errno);
- return -msg.rsp_hdr.posix_errno;
- }
-
- e = bt_audioservice_expect(u->audioservice_fd, &msg.streamfd_ind.h, BT_STREAMFD_IND);
+ e = bt_audioservice_expect(u->audioservice_fd, &msg.rsp, BT_NEW_STREAM, sizeof(msg.streamfd_ind));
if (e < 0) {
pa_log_error("Failed to expect BT_STREAMFD_IND");
return e;
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index a33ca648..2fe09370 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -44,7 +44,7 @@ PA_MODULE_USAGE("");
struct module {
char *profile;
- pa_module *pa_m;
+ uint32_t index;
PA_LLIST_FIELDS(struct module);
};
@@ -78,7 +78,7 @@ static struct module *module_new(const char *profile, pa_module *pa_m) {
m = pa_xnew(struct module, 1);
m->profile = pa_xstrdup(profile);
- m->pa_m = pa_m;
+ m->index = pa_m->index;
PA_LLIST_INIT(struct module, m);
return m;
@@ -94,7 +94,7 @@ static void module_free(struct module *m) {
static struct module* module_find(struct device *d, const char *profile) {
struct module *m;
- for (m = d->module_list; d; d = d->next)
+ for (m = d->module_list; m; m = m->next)
if (pa_streq(m->profile, profile))
return m;
@@ -346,7 +346,7 @@ static void load_module_for_device(struct userdata *u, struct device *d, const c
pa_m = pa_module_load(u->module->core, "module-bluetooth-device", args);
pa_xfree(args);
- if (!m) {
+ if (!pa_m) {
pa_log_debug("Failed to load module for device %s", d->object_path);
return;
}
@@ -364,7 +364,7 @@ static void unload_module_for_device(struct userdata *u, struct device *d, const
if (!(m = module_find(d, profile)))
return;
- pa_module_unload_request(m->pa_m, TRUE);
+ pa_module_unload_request_by_index(u->module->core, m->index, TRUE);
PA_LLIST_REMOVE(struct module, d->module_list, m);
module_free(m);
@@ -485,8 +485,23 @@ void pa__done(pa_module* m) {
device_free(i);
}
- if (u->conn)
+ if (u->conn) {
+ DBusError error;
+ dbus_error_init(&error);
+
+ dbus_bus_remove_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", &error);
+ dbus_error_free(&error);
+
+ dbus_bus_remove_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'", &error);
+ dbus_error_free(&error);
+
+ dbus_bus_remove_match(pa_dbus_connection_get(u->conn), "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'", &error);
+ dbus_error_free(&error);
+
+ dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), filter_cb, u);
+
pa_dbus_connection_unref(u->conn);
+ }
pa_xfree(u);
}
diff --git a/src/modules/bluetooth/sbc.c b/src/modules/bluetooth/sbc.c
index 02a6143d..651981fa 100644
--- a/src/modules/bluetooth/sbc.c
+++ b/src/modules/bluetooth/sbc.c
@@ -2,7 +2,7 @@
*
* Bluetooth low-complexity, subband codec (SBC) library
*
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
* Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
* Copyright (C) 2005-2008 Brad Midgley <bmidgley@xmission.com>
*
@@ -40,6 +40,7 @@
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
+#include <limits.h>
#include "sbc_math.h"
#include "sbc_tables.h"
@@ -68,7 +69,7 @@ struct sbc_frame {
uint8_t subband_mode;
uint8_t subbands;
uint8_t bitpool;
- uint8_t codesize;
+ uint16_t codesize;
uint8_t length;
/* bit number x set means joint stereo has been used in subband x */
@@ -93,7 +94,11 @@ struct sbc_decoder_state {
struct sbc_encoder_state {
int subbands;
int position[2];
- int32_t X[2][160];
+ int16_t X[2][256];
+ void (*sbc_analyze_4b_4s)(int16_t *pcm, int16_t *x,
+ int32_t *out, int out_stride);
+ void (*sbc_analyze_4b_8s)(int16_t *pcm, int16_t *x,
+ int32_t *out, int out_stride);
};
/*
@@ -145,7 +150,7 @@ static uint8_t sbc_crc8(const uint8_t *data, size_t len)
octet = data[i];
for (i = 0; i < len % 8; i++) {
- unsigned char bit = ((octet ^ crc) & 0x80) >> 7;
+ char bit = ((octet ^ crc) & 0x80) >> 7;
crc = ((crc & 0x7f) << 1) ^ (bit ? 0x1d : 0);
@@ -563,7 +568,7 @@ static inline void sbc_synthesize_four(struct sbc_decoder_state *state,
k = (i + 4) & 0xf;
/* Store in output, Q0 */
- frame->pcm_sample[ch][blk * 4 + i] = SCALE4_STAGED2(
+ frame->pcm_sample[ch][blk * 4 + i] = SCALE4_STAGED1(
MULA(v[offset[i] + 0], sbc_proto_4_40m0[idx + 0],
MULA(v[offset[k] + 1], sbc_proto_4_40m1[idx + 0],
MULA(v[offset[i] + 2], sbc_proto_4_40m0[idx + 1],
@@ -609,7 +614,7 @@ static inline void sbc_synthesize_eight(struct sbc_decoder_state *state,
k = (i + 8) & 0xf;
/* Store in output */
- frame->pcm_sample[ch][blk * 8 + i] = SCALE8_STAGED2( // Q0
+ frame->pcm_sample[ch][blk * 8 + i] = SCALE8_STAGED1( // Q0
MULA(state->V[ch][offset[i] + 0], sbc_proto_8_80m0[idx + 0],
MULA(state->V[ch][offset[k] + 1], sbc_proto_8_80m1[idx + 0],
MULA(state->V[ch][offset[i] + 2], sbc_proto_8_80m0[idx + 1],
@@ -648,242 +653,144 @@ static int sbc_synthesize_audio(struct sbc_decoder_state *state,
}
}
-static void sbc_encoder_init(struct sbc_encoder_state *state,
- const struct sbc_frame *frame)
+static inline void _sbc_analyze_four(const int16_t *in, int32_t *out)
{
- memset(&state->X, 0, sizeof(state->X));
- state->subbands = frame->subbands;
- state->position[0] = state->position[1] = 9 * frame->subbands;
-}
+ FIXED_A t1[4];
+ FIXED_T t2[4];
+ int i = 0, hop = 0;
+
+ /* rounding coefficient */
+ t1[0] = t1[1] = t1[2] = t1[3] =
+ (FIXED_A) 1 << (SBC_PROTO_FIXED4_SCALE - 1);
+
+ /* low pass polyphase filter */
+ for (hop = 0; hop < 40; hop += 8) {
+ t1[0] += (FIXED_A) in[hop] * _sbc_proto_fixed4[hop];
+ t1[1] += (FIXED_A) in[hop + 1] * _sbc_proto_fixed4[hop + 1];
+ t1[2] += (FIXED_A) in[hop + 2] * _sbc_proto_fixed4[hop + 2];
+ t1[1] += (FIXED_A) in[hop + 3] * _sbc_proto_fixed4[hop + 3];
+ t1[0] += (FIXED_A) in[hop + 4] * _sbc_proto_fixed4[hop + 4];
+ t1[3] += (FIXED_A) in[hop + 5] * _sbc_proto_fixed4[hop + 5];
+ t1[3] += (FIXED_A) in[hop + 7] * _sbc_proto_fixed4[hop + 7];
+ }
-static inline void _sbc_analyze_four(const int32_t *in, int32_t *out)
-{
- sbc_fixed_t t[8], s[5];
-
- t[0] = SCALE4_STAGE1( /* Q8 */
- MULA(_sbc_proto_4[0], in[8] - in[32], /* Q18 */
- MUL( _sbc_proto_4[1], in[16] - in[24])));
-
- t[1] = SCALE4_STAGE1(
- MULA(_sbc_proto_4[2], in[1],
- MULA(_sbc_proto_4[3], in[9],
- MULA(_sbc_proto_4[4], in[17],
- MULA(_sbc_proto_4[5], in[25],
- MUL( _sbc_proto_4[6], in[33]))))));
-
- t[2] = SCALE4_STAGE1(
- MULA(_sbc_proto_4[7], in[2],
- MULA(_sbc_proto_4[8], in[10],
- MULA(_sbc_proto_4[9], in[18],
- MULA(_sbc_proto_4[10], in[26],
- MUL( _sbc_proto_4[11], in[34]))))));
-
- t[3] = SCALE4_STAGE1(
- MULA(_sbc_proto_4[12], in[3],
- MULA(_sbc_proto_4[13], in[11],
- MULA(_sbc_proto_4[14], in[19],
- MULA(_sbc_proto_4[15], in[27],
- MUL( _sbc_proto_4[16], in[35]))))));
-
- t[4] = SCALE4_STAGE1(
- MULA(_sbc_proto_4[17], in[4] + in[36],
- MULA(_sbc_proto_4[18], in[12] + in[28],
- MUL( _sbc_proto_4[19], in[20]))));
-
- t[5] = SCALE4_STAGE1(
- MULA(_sbc_proto_4[16], in[5],
- MULA(_sbc_proto_4[15], in[13],
- MULA(_sbc_proto_4[14], in[21],
- MULA(_sbc_proto_4[13], in[29],
- MUL( _sbc_proto_4[12], in[37]))))));
-
- /* don't compute t[6]... this term always multiplies
- * with cos(pi/2) = 0 */
-
- t[7] = SCALE4_STAGE1(
- MULA(_sbc_proto_4[6], in[7],
- MULA(_sbc_proto_4[5], in[15],
- MULA(_sbc_proto_4[4], in[23],
- MULA(_sbc_proto_4[3], in[31],
- MUL( _sbc_proto_4[2], in[39]))))));
-
- s[0] = MUL( _anamatrix4[0], t[0] + t[4]);
- s[1] = MUL( _anamatrix4[2], t[2]);
- s[2] = MULA(_anamatrix4[1], t[1] + t[3],
- MUL(_anamatrix4[3], t[5]));
- s[3] = MULA(_anamatrix4[3], t[1] + t[3],
- MUL(_anamatrix4[1], -t[5] + t[7]));
- s[4] = MUL( _anamatrix4[3], t[7]);
-
- out[0] = SCALE4_STAGE2( s[0] + s[1] + s[2] + s[4]); /* Q0 */
- out[1] = SCALE4_STAGE2(-s[0] + s[1] + s[3]);
- out[2] = SCALE4_STAGE2(-s[0] + s[1] - s[3]);
- out[3] = SCALE4_STAGE2( s[0] + s[1] - s[2] - s[4]);
+ /* scaling */
+ t2[0] = t1[0] >> SBC_PROTO_FIXED4_SCALE;
+ t2[1] = t1[1] >> SBC_PROTO_FIXED4_SCALE;
+ t2[2] = t1[2] >> SBC_PROTO_FIXED4_SCALE;
+ t2[3] = t1[3] >> SBC_PROTO_FIXED4_SCALE;
+
+ /* do the cos transform */
+ for (i = 0, hop = 0; i < 4; hop += 8, i++) {
+ out[i] = ((FIXED_A) t2[0] * cos_table_fixed_4[0 + hop] +
+ (FIXED_A) t2[1] * cos_table_fixed_4[1 + hop] +
+ (FIXED_A) t2[2] * cos_table_fixed_4[2 + hop] +
+ (FIXED_A) t2[3] * cos_table_fixed_4[5 + hop]) >>
+ (SBC_COS_TABLE_FIXED4_SCALE - SCALE_OUT_BITS);
+ }
}
-static inline void sbc_analyze_four(struct sbc_encoder_state *state,
- struct sbc_frame *frame, int ch, int blk)
+static void sbc_analyze_4b_4s(int16_t *pcm, int16_t *x,
+ int32_t *out, int out_stride)
{
- int32_t *x = &state->X[ch][state->position[ch]];
- int16_t *pcm = &frame->pcm_sample[ch][blk * 4];
-
- /* Input 4 Audio Samples */
- x[40] = x[0] = pcm[3];
- x[41] = x[1] = pcm[2];
- x[42] = x[2] = pcm[1];
- x[43] = x[3] = pcm[0];
-
- _sbc_analyze_four(x, frame->sb_sample_f[blk][ch]);
+ int i;
+
+ /* Input 4 x 4 Audio Samples */
+ for (i = 0; i < 16; i += 4) {
+ x[64 + i] = x[0 + i] = pcm[15 - i];
+ x[65 + i] = x[1 + i] = pcm[14 - i];
+ x[66 + i] = x[2 + i] = pcm[13 - i];
+ x[67 + i] = x[3 + i] = pcm[12 - i];
+ }
- state->position[ch] -= 4;
- if (state->position[ch] < 0)
- state->position[ch] = 36;
+ /* Analyze four blocks */
+ _sbc_analyze_four(x + 12, out);
+ out += out_stride;
+ _sbc_analyze_four(x + 8, out);
+ out += out_stride;
+ _sbc_analyze_four(x + 4, out);
+ out += out_stride;
+ _sbc_analyze_four(x, out);
}
-static inline void _sbc_analyze_eight(const int32_t *in, int32_t *out)
+static inline void _sbc_analyze_eight(const int16_t *in, int32_t *out)
{
- sbc_fixed_t t[8], s[8];
-
- t[0] = SCALE8_STAGE1( /* Q10 */
- MULA(_sbc_proto_8[0], (in[16] - in[64]), /* Q18 = Q18 * Q0 */
- MULA(_sbc_proto_8[1], (in[32] - in[48]),
- MULA(_sbc_proto_8[2], in[4],
- MULA(_sbc_proto_8[3], in[20],
- MULA(_sbc_proto_8[4], in[36],
- MUL( _sbc_proto_8[5], in[52])))))));
-
- t[1] = SCALE8_STAGE1(
- MULA(_sbc_proto_8[6], in[2],
- MULA(_sbc_proto_8[7], in[18],
- MULA(_sbc_proto_8[8], in[34],
- MULA(_sbc_proto_8[9], in[50],
- MUL(_sbc_proto_8[10], in[66]))))));
-
- t[2] = SCALE8_STAGE1(
- MULA(_sbc_proto_8[11], in[1],
- MULA(_sbc_proto_8[12], in[17],
- MULA(_sbc_proto_8[13], in[33],
- MULA(_sbc_proto_8[14], in[49],
- MULA(_sbc_proto_8[15], in[65],
- MULA(_sbc_proto_8[16], in[3],
- MULA(_sbc_proto_8[17], in[19],
- MULA(_sbc_proto_8[18], in[35],
- MULA(_sbc_proto_8[19], in[51],
- MUL( _sbc_proto_8[20], in[67])))))))))));
-
- t[3] = SCALE8_STAGE1(
- MULA( _sbc_proto_8[21], in[5],
- MULA( _sbc_proto_8[22], in[21],
- MULA( _sbc_proto_8[23], in[37],
- MULA( _sbc_proto_8[24], in[53],
- MULA( _sbc_proto_8[25], in[69],
- MULA(-_sbc_proto_8[15], in[15],
- MULA(-_sbc_proto_8[14], in[31],
- MULA(-_sbc_proto_8[13], in[47],
- MULA(-_sbc_proto_8[12], in[63],
- MUL( -_sbc_proto_8[11], in[79])))))))))));
-
- t[4] = SCALE8_STAGE1(
- MULA( _sbc_proto_8[26], in[6],
- MULA( _sbc_proto_8[27], in[22],
- MULA( _sbc_proto_8[28], in[38],
- MULA( _sbc_proto_8[29], in[54],
- MULA( _sbc_proto_8[30], in[70],
- MULA(-_sbc_proto_8[10], in[14],
- MULA(-_sbc_proto_8[9], in[30],
- MULA(-_sbc_proto_8[8], in[46],
- MULA(-_sbc_proto_8[7], in[62],
- MUL( -_sbc_proto_8[6], in[78])))))))))));
-
- t[5] = SCALE8_STAGE1(
- MULA( _sbc_proto_8[31], in[7],
- MULA( _sbc_proto_8[32], in[23],
- MULA( _sbc_proto_8[33], in[39],
- MULA( _sbc_proto_8[34], in[55],
- MULA( _sbc_proto_8[35], in[71],
- MULA(-_sbc_proto_8[20], in[13],
- MULA(-_sbc_proto_8[19], in[29],
- MULA(-_sbc_proto_8[18], in[45],
- MULA(-_sbc_proto_8[17], in[61],
- MUL( -_sbc_proto_8[16], in[77])))))))))));
-
- t[6] = SCALE8_STAGE1(
- MULA( _sbc_proto_8[36], (in[8] + in[72]),
- MULA( _sbc_proto_8[37], (in[24] + in[56]),
- MULA( _sbc_proto_8[38], in[40],
- MULA(-_sbc_proto_8[39], in[12],
- MULA(-_sbc_proto_8[5], in[28],
- MULA(-_sbc_proto_8[4], in[44],
- MULA(-_sbc_proto_8[3], in[60],
- MUL( -_sbc_proto_8[2], in[76])))))))));
-
- t[7] = SCALE8_STAGE1(
- MULA( _sbc_proto_8[35], in[9],
- MULA( _sbc_proto_8[34], in[25],
- MULA( _sbc_proto_8[33], in[41],
- MULA( _sbc_proto_8[32], in[57],
- MULA( _sbc_proto_8[31], in[73],
- MULA(-_sbc_proto_8[25], in[11],
- MULA(-_sbc_proto_8[24], in[27],
- MULA(-_sbc_proto_8[23], in[43],
- MULA(-_sbc_proto_8[22], in[59],
- MUL( -_sbc_proto_8[21], in[75])))))))))));
-
- s[0] = MULA( _anamatrix8[0], t[0],
- MUL( _anamatrix8[1], t[6]));
- s[1] = MUL( _anamatrix8[7], t[1]);
- s[2] = MULA( _anamatrix8[2], t[2],
- MULA( _anamatrix8[3], t[3],
- MULA( _anamatrix8[4], t[5],
- MUL( _anamatrix8[5], t[7]))));
- s[3] = MUL( _anamatrix8[6], t[4]);
- s[4] = MULA( _anamatrix8[3], t[2],
- MULA(-_anamatrix8[5], t[3],
- MULA(-_anamatrix8[2], t[5],
- MUL( -_anamatrix8[4], t[7]))));
- s[5] = MULA( _anamatrix8[4], t[2],
- MULA(-_anamatrix8[2], t[3],
- MULA( _anamatrix8[5], t[5],
- MUL( _anamatrix8[3], t[7]))));
- s[6] = MULA( _anamatrix8[1], t[0],
- MUL( -_anamatrix8[0], t[6]));
- s[7] = MULA( _anamatrix8[5], t[2],
- MULA(-_anamatrix8[4], t[3],
- MULA( _anamatrix8[3], t[5],
- MUL( -_anamatrix8[2], t[7]))));
-
- out[0] = SCALE8_STAGE2( s[0] + s[1] + s[2] + s[3]);
- out[1] = SCALE8_STAGE2( s[1] - s[3] + s[4] + s[6]);
- out[2] = SCALE8_STAGE2( s[1] - s[3] + s[5] - s[6]);
- out[3] = SCALE8_STAGE2(-s[0] + s[1] + s[3] + s[7]);
- out[4] = SCALE8_STAGE2(-s[0] + s[1] + s[3] - s[7]);
- out[5] = SCALE8_STAGE2( s[1] - s[3] - s[5] - s[6]);
- out[6] = SCALE8_STAGE2( s[1] - s[3] - s[4] + s[6]);
- out[7] = SCALE8_STAGE2( s[0] + s[1] - s[2] + s[3]);
+ FIXED_A t1[8];
+ FIXED_T t2[8];
+ int i, hop;
+
+ /* rounding coefficient */
+ t1[0] = t1[1] = t1[2] = t1[3] = t1[4] = t1[5] = t1[6] = t1[7] =
+ (FIXED_A) 1 << (SBC_PROTO_FIXED8_SCALE-1);
+
+ /* low pass polyphase filter */
+ for (hop = 0; hop < 80; hop += 16) {
+ t1[0] += (FIXED_A) in[hop] * _sbc_proto_fixed8[hop];
+ t1[1] += (FIXED_A) in[hop + 1] * _sbc_proto_fixed8[hop + 1];
+ t1[2] += (FIXED_A) in[hop + 2] * _sbc_proto_fixed8[hop + 2];
+ t1[3] += (FIXED_A) in[hop + 3] * _sbc_proto_fixed8[hop + 3];
+ t1[4] += (FIXED_A) in[hop + 4] * _sbc_proto_fixed8[hop + 4];
+ t1[3] += (FIXED_A) in[hop + 5] * _sbc_proto_fixed8[hop + 5];
+ t1[2] += (FIXED_A) in[hop + 6] * _sbc_proto_fixed8[hop + 6];
+ t1[1] += (FIXED_A) in[hop + 7] * _sbc_proto_fixed8[hop + 7];
+ t1[0] += (FIXED_A) in[hop + 8] * _sbc_proto_fixed8[hop + 8];
+ t1[5] += (FIXED_A) in[hop + 9] * _sbc_proto_fixed8[hop + 9];
+ t1[6] += (FIXED_A) in[hop + 10] * _sbc_proto_fixed8[hop + 10];
+ t1[7] += (FIXED_A) in[hop + 11] * _sbc_proto_fixed8[hop + 11];
+ t1[7] += (FIXED_A) in[hop + 13] * _sbc_proto_fixed8[hop + 13];
+ t1[6] += (FIXED_A) in[hop + 14] * _sbc_proto_fixed8[hop + 14];
+ t1[5] += (FIXED_A) in[hop + 15] * _sbc_proto_fixed8[hop + 15];
+ }
+
+ /* scaling */
+ t2[0] = t1[0] >> SBC_PROTO_FIXED8_SCALE;
+ t2[1] = t1[1] >> SBC_PROTO_FIXED8_SCALE;
+ t2[2] = t1[2] >> SBC_PROTO_FIXED8_SCALE;
+ t2[3] = t1[3] >> SBC_PROTO_FIXED8_SCALE;
+ t2[4] = t1[4] >> SBC_PROTO_FIXED8_SCALE;
+ t2[5] = t1[5] >> SBC_PROTO_FIXED8_SCALE;
+ t2[6] = t1[6] >> SBC_PROTO_FIXED8_SCALE;
+ t2[7] = t1[7] >> SBC_PROTO_FIXED8_SCALE;
+
+ /* do the cos transform */
+ for (i = 0, hop = 0; i < 8; hop += 16, i++) {
+ out[i] = ((FIXED_A) t2[0] * cos_table_fixed_8[0 + hop] +
+ (FIXED_A) t2[1] * cos_table_fixed_8[1 + hop] +
+ (FIXED_A) t2[2] * cos_table_fixed_8[2 + hop] +
+ (FIXED_A) t2[3] * cos_table_fixed_8[3 + hop] +
+ (FIXED_A) t2[4] * cos_table_fixed_8[4 + hop] +
+ (FIXED_A) t2[5] * cos_table_fixed_8[9 + hop] +
+ (FIXED_A) t2[6] * cos_table_fixed_8[10 + hop] +
+ (FIXED_A) t2[7] * cos_table_fixed_8[11 + hop]) >>
+ (SBC_COS_TABLE_FIXED8_SCALE - SCALE_OUT_BITS);
+ }
}
-static inline void sbc_analyze_eight(struct sbc_encoder_state *state,
- struct sbc_frame *frame, int ch,
- int blk)
+static void sbc_analyze_4b_8s(int16_t *pcm, int16_t *x,
+ int32_t *out, int out_stride)
{
- int32_t *x = &state->X[ch][state->position[ch]];
- int16_t *pcm = &frame->pcm_sample[ch][blk * 8];
-
- /* Input 8 Audio Samples */
- x[80] = x[0] = pcm[7];
- x[81] = x[1] = pcm[6];
- x[82] = x[2] = pcm[5];
- x[83] = x[3] = pcm[4];
- x[84] = x[4] = pcm[3];
- x[85] = x[5] = pcm[2];
- x[86] = x[6] = pcm[1];
- x[87] = x[7] = pcm[0];
-
- _sbc_analyze_eight(x, frame->sb_sample_f[blk][ch]);
-
- state->position[ch] -= 8;
- if (state->position[ch] < 0)
- state->position[ch] = 72;
+ int i;
+
+ /* Input 4 x 8 Audio Samples */
+ for (i = 0; i < 32; i += 8) {
+ x[128 + i] = x[0 + i] = pcm[31 - i];
+ x[129 + i] = x[1 + i] = pcm[30 - i];
+ x[130 + i] = x[2 + i] = pcm[29 - i];
+ x[131 + i] = x[3 + i] = pcm[28 - i];
+ x[132 + i] = x[4 + i] = pcm[27 - i];
+ x[133 + i] = x[5 + i] = pcm[26 - i];
+ x[134 + i] = x[6 + i] = pcm[25 - i];
+ x[135 + i] = x[7 + i] = pcm[24 - i];
+ }
+
+ /* Analyze four blocks */
+ _sbc_analyze_eight(x + 24, out);
+ out += out_stride;
+ _sbc_analyze_eight(x + 16, out);
+ out += out_stride;
+ _sbc_analyze_eight(x + 8, out);
+ out += out_stride;
+ _sbc_analyze_eight(x, out);
}
static int sbc_analyze_audio(struct sbc_encoder_state *state,
@@ -894,14 +801,32 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
switch (frame->subbands) {
case 4:
for (ch = 0; ch < frame->channels; ch++)
- for (blk = 0; blk < frame->blocks; blk++)
- sbc_analyze_four(state, frame, ch, blk);
+ for (blk = 0; blk < frame->blocks; blk += 4) {
+ state->sbc_analyze_4b_4s(
+ &frame->pcm_sample[ch][blk * 4],
+ &state->X[ch][state->position[ch]],
+ frame->sb_sample_f[blk][ch],
+ frame->sb_sample_f[blk + 1][ch] -
+ frame->sb_sample_f[blk][ch]);
+ state->position[ch] -= 16;
+ if (state->position[ch] < 0)
+ state->position[ch] = 64 - 16;
+ }
return frame->blocks * 4;
case 8:
for (ch = 0; ch < frame->channels; ch++)
- for (blk = 0; blk < frame->blocks; blk++)
- sbc_analyze_eight(state, frame, ch, blk);
+ for (blk = 0; blk < frame->blocks; blk += 4) {
+ state->sbc_analyze_4b_8s(
+ &frame->pcm_sample[ch][blk * 8],
+ &state->X[ch][state->position[ch]],
+ frame->sb_sample_f[blk][ch],
+ frame->sb_sample_f[blk + 1][ch] -
+ frame->sb_sample_f[blk][ch]);
+ state->position[ch] -= 32;
+ if (state->position[ch] < 0)
+ state->position[ch] = 128 - 32;
+ }
return frame->blocks * 8;
default:
@@ -909,6 +834,26 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
}
}
+/* Supplementary bitstream writing macros for 'sbc_pack_frame' */
+
+#define PUT_BITS(v, n)\
+ bits_cache = (v) | (bits_cache << (n));\
+ bits_count += (n);\
+ if (bits_count >= 16) {\
+ bits_count -= 8;\
+ *data_ptr++ = (uint8_t) (bits_cache >> bits_count);\
+ bits_count -= 8;\
+ *data_ptr++ = (uint8_t) (bits_cache >> bits_count);\
+ }\
+
+#define FLUSH_BITS()\
+ while (bits_count >= 8) {\
+ bits_count -= 8;\
+ *data_ptr++ = (uint8_t) (bits_cache >> bits_count);\
+ }\
+ if (bits_count > 0)\
+ *data_ptr++ = (uint8_t) (bits_cache << (8 - bits_count));\
+
/*
* Packs the SBC frame from frame into the memory at data. At most len
* bytes will be used, should more memory be needed an appropriate
@@ -926,16 +871,21 @@ static int sbc_analyze_audio(struct sbc_encoder_state *state,
static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
{
- int produced;
+ /* Bitstream writer starts from the fourth byte */
+ uint8_t *data_ptr = data + 4;
+ uint32_t bits_cache = 0;
+ uint32_t bits_count = 0;
+
/* Will copy the header parts for CRC-8 calculation here */
uint8_t crc_header[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int crc_pos = 0;
- uint16_t audio_sample;
+ uint32_t audio_sample;
- int ch, sb, blk, bit; /* channel, subband, block and bit counters */
+ int ch, sb, blk; /* channel, subband, block and bit counters */
int bits[2][8]; /* bits distribution */
- int levels[2][8]; /* levels are derived from that */
+ uint32_t levels[2][8]; /* levels are derived from that */
+ uint32_t sb_sample_delta[2][8];
u_int32_t scalefactor[2][8]; /* derived from frame->scale_factor */
@@ -973,8 +923,6 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
/* Can't fill in crc yet */
- produced = 32;
-
crc_header[0] = data[1];
crc_header[1] = data[2];
crc_pos = 16;
@@ -982,7 +930,7 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
for (ch = 0; ch < frame->channels; ch++) {
for (sb = 0; sb < frame->subbands; sb++) {
frame->scale_factor[ch][sb] = 0;
- scalefactor[ch][sb] = 2;
+ scalefactor[ch][sb] = 2 << SCALE_OUT_BITS;
for (blk = 0; blk < frame->blocks; blk++) {
while (scalefactor[ch][sb] < fabs(frame->sb_sample_f[blk][ch][sb])) {
frame->scale_factor[ch][sb]++;
@@ -999,22 +947,23 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
u_int32_t scalefactor_j[2];
uint8_t scale_factor_j[2];
+ uint8_t joint = 0;
frame->joint = 0;
for (sb = 0; sb < frame->subbands - 1; sb++) {
scale_factor_j[0] = 0;
- scalefactor_j[0] = 2;
+ scalefactor_j[0] = 2 << SCALE_OUT_BITS;
scale_factor_j[1] = 0;
- scalefactor_j[1] = 2;
+ scalefactor_j[1] = 2 << SCALE_OUT_BITS;
for (blk = 0; blk < frame->blocks; blk++) {
/* Calculate joint stereo signal */
sb_sample_j[blk][0] =
- (frame->sb_sample_f[blk][0][sb] +
- frame->sb_sample_f[blk][1][sb]) >> 1;
+ ASR(frame->sb_sample_f[blk][0][sb], 1) +
+ ASR(frame->sb_sample_f[blk][1][sb], 1);
sb_sample_j[blk][1] =
- (frame->sb_sample_f[blk][0][sb] -
- frame->sb_sample_f[blk][1][sb]) >> 1;
+ ASR(frame->sb_sample_f[blk][0][sb], 1) -
+ ASR(frame->sb_sample_f[blk][1][sb], 1);
/* calculate scale_factor_j and scalefactor_j for joint case */
while (scalefactor_j[0] < fabs(sb_sample_j[blk][0])) {
@@ -1028,14 +977,15 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
}
/* decide whether to join this subband */
- if ((scalefactor[0][sb] + scalefactor[1][sb]) >
- (scalefactor_j[0] + scalefactor_j[1]) ) {
+ if ((frame->scale_factor[0][sb] +
+ frame->scale_factor[1][sb]) >
+ (scale_factor_j[0] +
+ scale_factor_j[1])) {
/* use joint stereo for this subband */
+ joint |= 1 << (frame->subbands - 1 - sb);
frame->joint |= 1 << sb;
frame->scale_factor[0][sb] = scale_factor_j[0];
frame->scale_factor[1][sb] = scale_factor_j[1];
- scalefactor[0][sb] = scalefactor_j[0];
- scalefactor[1][sb] = scalefactor_j[1];
for (blk = 0; blk < frame->blocks; blk++) {
frame->sb_sample_f[blk][0][sb] =
sb_sample_j[blk][0];
@@ -1045,24 +995,16 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
}
}
- data[4] = 0;
- for (sb = 0; sb < frame->subbands - 1; sb++)
- data[4] |= ((frame->joint >> sb) & 0x01) << (frame->subbands - 1 - sb);
-
- crc_header[crc_pos >> 3] = data[4];
-
- produced += frame->subbands;
+ PUT_BITS(joint, frame->subbands);
+ crc_header[crc_pos >> 3] = joint;
crc_pos += frame->subbands;
}
for (ch = 0; ch < frame->channels; ch++) {
for (sb = 0; sb < frame->subbands; sb++) {
- data[produced >> 3] <<= 4;
+ PUT_BITS(frame->scale_factor[ch][sb] & 0x0F, 4);
crc_header[crc_pos >> 3] <<= 4;
- data[produced >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
crc_header[crc_pos >> 3] |= frame->scale_factor[ch][sb] & 0x0F;
-
- produced += 4;
crc_pos += 4;
}
}
@@ -1076,37 +1018,47 @@ static int sbc_pack_frame(uint8_t *data, struct sbc_frame *frame, size_t len)
sbc_calculate_bits(frame, bits);
for (ch = 0; ch < frame->channels; ch++) {
- for (sb = 0; sb < frame->subbands; sb++)
- levels[ch][sb] = (1 << bits[ch][sb]) - 1;
+ for (sb = 0; sb < frame->subbands; sb++) {
+ levels[ch][sb] = ((1 << bits[ch][sb]) - 1) <<
+ (32 - (frame->scale_factor[ch][sb] +
+ SCALE_OUT_BITS + 2));
+ sb_sample_delta[ch][sb] = (uint32_t) 1 <<
+ (frame->scale_factor[ch][sb] +
+ SCALE_OUT_BITS + 1);
+ }
}
for (blk = 0; blk < frame->blocks; blk++) {
for (ch = 0; ch < frame->channels; ch++) {
for (sb = 0; sb < frame->subbands; sb++) {
- if (levels[ch][sb] > 0) {
- audio_sample =
- (uint16_t) ((((frame->sb_sample_f[blk][ch][sb]*levels[ch][sb]) >>
- (frame->scale_factor[ch][sb] + 1)) +
- levels[ch][sb]) >> 1);
- audio_sample <<= 16 - bits[ch][sb];
- for (bit = 0; bit < bits[ch][sb]; bit++) {
- data[produced >> 3] <<= 1;
- if (audio_sample & 0x8000)
- data[produced >> 3] |= 0x1;
- audio_sample <<= 1;
- produced++;
- }
- }
+
+ if (bits[ch][sb] == 0)
+ continue;
+
+ audio_sample = ((uint64_t) levels[ch][sb] *
+ (sb_sample_delta[ch][sb] +
+ frame->sb_sample_f[blk][ch][sb])) >> 32;
+
+ PUT_BITS(audio_sample, bits[ch][sb]);
}
}
}
- /* align the last byte */
- if (produced % 8) {
- data[produced >> 3] <<= 8 - (produced % 8);
- }
+ FLUSH_BITS();
+
+ return data_ptr - data;
+}
+
+static void sbc_encoder_init(struct sbc_encoder_state *state,
+ const struct sbc_frame *frame)
+{
+ memset(&state->X, 0, sizeof(state->X));
+ state->subbands = frame->subbands;
+ state->position[0] = state->position[1] = 12 * frame->subbands;
- return (produced + 7) >> 3;
+ /* Default implementation for analyze function */
+ state->sbc_analyze_4b_4s = sbc_analyze_4b_4s;
+ state->sbc_analyze_4b_8s = sbc_analyze_4b_8s;
}
struct sbc_priv {
@@ -1190,6 +1142,9 @@ int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output,
if (written)
*written = 0;
+ if (framelen <= 0)
+ return framelen;
+
samples = sbc_synthesize_audio(&priv->dec_state, &priv->frame);
ptr = output;
@@ -1202,13 +1157,7 @@ int sbc_decode(sbc_t *sbc, void *input, int input_len, void *output,
int16_t s;
s = priv->frame.pcm_sample[ch][i];
-#if __BYTE_ORDER == __LITTLE_ENDIAN
if (sbc->endian == SBC_BE) {
-#elif __BYTE_ORDER == __BIG_ENDIAN
- if (sbc->endian == SBC_LE) {
-#else
-#error "Unknown byte order"
-#endif
*ptr++ = (s & 0xff00) >> 8;
*ptr++ = (s & 0x00ff);
} else {
@@ -1269,13 +1218,7 @@ int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
for (i = 0; i < priv->frame.subbands * priv->frame.blocks; i++) {
for (ch = 0; ch < priv->frame.channels; ch++) {
int16_t s;
-#if __BYTE_ORDER == __LITTLE_ENDIAN
if (sbc->endian == SBC_BE)
-#elif __BYTE_ORDER == __BIG_ENDIAN
- if (sbc->endian == SBC_LE)
-#else
-#error "Unknown byte order"
-#endif
s = (ptr[0] & 0xff) << 8 | (ptr[1] & 0xff);
else
s = (ptr[0] & 0xff) | (ptr[1] & 0xff) << 8;
@@ -1374,9 +1317,9 @@ int sbc_get_frame_duration(sbc_t *sbc)
return (1000000 * blocks * subbands) / frequency;
}
-int sbc_get_codesize(sbc_t *sbc)
+uint16_t sbc_get_codesize(sbc_t *sbc)
{
- uint8_t subbands, channels, blocks;
+ uint16_t subbands, channels, blocks;
struct sbc_priv *priv;
priv = sbc->priv;
diff --git a/src/modules/bluetooth/sbc.h b/src/modules/bluetooth/sbc.h
index ab47e329..8ac59309 100644
--- a/src/modules/bluetooth/sbc.h
+++ b/src/modules/bluetooth/sbc.h
@@ -2,7 +2,7 @@
*
* Bluetooth low-complexity, subband codec (SBC) library
*
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
* Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
* Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
*
@@ -87,7 +87,7 @@ int sbc_encode(sbc_t *sbc, void *input, int input_len, void *output,
int output_len, int *written);
int sbc_get_frame_length(sbc_t *sbc);
int sbc_get_frame_duration(sbc_t *sbc);
-int sbc_get_codesize(sbc_t *sbc);
+uint16_t sbc_get_codesize(sbc_t *sbc);
void sbc_finish(sbc_t *sbc);
#ifdef __cplusplus
diff --git a/src/modules/bluetooth/sbc_math.h b/src/modules/bluetooth/sbc_math.h
index b3d87a62..6ca4f526 100644
--- a/src/modules/bluetooth/sbc_math.h
+++ b/src/modules/bluetooth/sbc_math.h
@@ -2,7 +2,7 @@
*
* Bluetooth low-complexity, subband codec (SBC) library
*
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
* Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
* Copyright (C) 2005-2008 Brad Midgley <bmidgley@xmission.com>
*
@@ -29,31 +29,21 @@
#define ASR(val, bits) ((-2 >> 1 == -1) ? \
((int32_t)(val)) >> (bits) : ((int32_t) (val)) / (1 << (bits)))
-#define SCALE_PROTO4_TBL 15
-#define SCALE_ANA4_TBL 17
-#define SCALE_PROTO8_TBL 16
-#define SCALE_ANA8_TBL 17
+#define SCALE_OUT_BITS 15
+
#define SCALE_SPROTO4_TBL 12
#define SCALE_SPROTO8_TBL 14
#define SCALE_NPROTO4_TBL 11
#define SCALE_NPROTO8_TBL 11
-#define SCALE4_STAGE1_BITS 15
-#define SCALE4_STAGE2_BITS 16
#define SCALE4_STAGED1_BITS 15
#define SCALE4_STAGED2_BITS 16
-#define SCALE8_STAGE1_BITS 15
-#define SCALE8_STAGE2_BITS 15
#define SCALE8_STAGED1_BITS 15
#define SCALE8_STAGED2_BITS 16
typedef int32_t sbc_fixed_t;
-#define SCALE4_STAGE1(src) ASR(src, SCALE4_STAGE1_BITS)
-#define SCALE4_STAGE2(src) ASR(src, SCALE4_STAGE2_BITS)
#define SCALE4_STAGED1(src) ASR(src, SCALE4_STAGED1_BITS)
#define SCALE4_STAGED2(src) ASR(src, SCALE4_STAGED2_BITS)
-#define SCALE8_STAGE1(src) ASR(src, SCALE8_STAGE1_BITS)
-#define SCALE8_STAGE2(src) ASR(src, SCALE8_STAGE2_BITS)
#define SCALE8_STAGED1(src) ASR(src, SCALE8_STAGED1_BITS)
#define SCALE8_STAGED2(src) ASR(src, SCALE8_STAGED2_BITS)
diff --git a/src/modules/bluetooth/sbc_tables.h b/src/modules/bluetooth/sbc_tables.h
index 7ac4e68b..f1dfe6c0 100644
--- a/src/modules/bluetooth/sbc_tables.h
+++ b/src/modules/bluetooth/sbc_tables.h
@@ -2,7 +2,7 @@
*
* Bluetooth low-complexity, subband codec (SBC) library
*
- * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
* Copyright (C) 2004-2005 Henryk Ploetz <henryk@ploetzli.ch>
* Copyright (C) 2005-2006 Brad Midgley <bmidgley@xmission.com>
*
@@ -39,40 +39,12 @@ static const int sbc_offset8[4][8] = {
{ -4, 0, 0, 0, 0, 0, 1, 2 }
};
-#define SP4(val) ASR(val, SCALE_PROTO4_TBL)
-#define SA4(val) ASR(val, SCALE_ANA4_TBL)
-#define SP8(val) ASR(val, SCALE_PROTO8_TBL)
-#define SA8(val) ASR(val, SCALE_ANA8_TBL)
+
#define SS4(val) ASR(val, SCALE_SPROTO4_TBL)
#define SS8(val) ASR(val, SCALE_SPROTO8_TBL)
#define SN4(val) ASR(val, SCALE_NPROTO4_TBL)
#define SN8(val) ASR(val, SCALE_NPROTO8_TBL)
-static const int32_t _sbc_proto_4[20] = {
- SP4(0x02cb3e8c), SP4(0x22b63dc0), SP4(0x002329cc), SP4(0x053b7548),
- SP4(0x31eab940), SP4(0xec1f5e60), SP4(0xff3773a8), SP4(0x0061c5a7),
- SP4(0x07646680), SP4(0x3f239480), SP4(0xf89f23a8), SP4(0x007a4737),
- SP4(0x00b32807), SP4(0x083ddc80), SP4(0x4825e480), SP4(0x0191e578),
- SP4(0x00ff11ca), SP4(0x00fb7991), SP4(0x069fdc58), SP4(0x4b584000)
-};
-
-static const int32_t _anamatrix4[4] = {
- SA4(0x2d413cc0), SA4(0x3b20d780), SA4(0x40000000), SA4(0x187de2a0)
-};
-
-static const int32_t _sbc_proto_8[40] = {
- SP8(0x02e5cd20), SP8(0x22d0c200), SP8(0x006bfe27), SP8(0x07808930),
- SP8(0x3f1c8800), SP8(0xf8810d70), SP8(0x002cfdc6), SP8(0x055acf28),
- SP8(0x31f566c0), SP8(0xebfe57e0), SP8(0xff27c437), SP8(0x001485cc),
- SP8(0x041c6e58), SP8(0x2a7cfa80), SP8(0xe4c4a240), SP8(0xfe359e4c),
- SP8(0x0048b1f8), SP8(0x0686ce30), SP8(0x38eec5c0), SP8(0xf2a1b9f0),
- SP8(0xffe8904a), SP8(0x0095698a), SP8(0x0824a480), SP8(0x443b3c00),
- SP8(0xfd7badc8), SP8(0x00d3e2d9), SP8(0x00c183d2), SP8(0x084e1950),
- SP8(0x4810d800), SP8(0x017f43fe), SP8(0x01056dd8), SP8(0x00e9cb9f),
- SP8(0x07d7d090), SP8(0x4a708980), SP8(0x0488fae8), SP8(0x0113bd20),
- SP8(0x0107b1a8), SP8(0x069fb3c0), SP8(0x4b3db200), SP8(0x00763f48)
-};
-
static const int32_t sbc_proto_4_40m0[] = {
SS4(0x00000000), SS4(0xffa6982f), SS4(0xfba93848), SS4(0x0456c7b8),
SS4(0x005967d1), SS4(0xfffb9ac7), SS4(0xff589157), SS4(0xf9c2a8d8),
@@ -115,11 +87,6 @@ static const int32_t sbc_proto_8_80m1[] = {
SS8(0x0d9daee0), SS8(0xeac182c0), SS8(0xfdf1c8d4), SS8(0xfff5bd1a)
};
-static const int32_t _anamatrix8[8] = {
- SA8(0x3b20d780), SA8(0x187de2a0), SA8(0x3ec52f80), SA8(0x3536cc40),
- SA8(0x238e7680), SA8(0x0c7c5c20), SA8(0x2d413cc0), SA8(0x40000000)
-};
-
static const int32_t synmatrix4[8][4] = {
{ SN4(0x05a82798), SN4(0xfa57d868), SN4(0xfa57d868), SN4(0x05a82798) },
{ SN4(0x030fbc54), SN4(0xf89be510), SN4(0x07641af0), SN4(0xfcf043ac) },
@@ -165,3 +132,216 @@ static const int32_t synmatrix8[16][8] = {
{ SN8(0xf9592678), SN8(0x018f8b84), SN8(0x07d8a5f0), SN8(0x0471ced0),
SN8(0xfb8e3130), SN8(0xf8275a10), SN8(0xfe70747c), SN8(0x06a6d988) }
};
+
+/* Uncomment the following line to enable high precision build of SBC encoder */
+
+/* #define SBC_HIGH_PRECISION */
+
+#ifdef SBC_HIGH_PRECISION
+#define FIXED_A int64_t /* data type for fixed point accumulator */
+#define FIXED_T int32_t /* data type for fixed point constants */
+#define SBC_FIXED_EXTRA_BITS 16
+#else
+#define FIXED_A int32_t /* data type for fixed point accumulator */
+#define FIXED_T int16_t /* data type for fixed point constants */
+#define SBC_FIXED_EXTRA_BITS 0
+#endif
+
+/* A2DP specification: Section 12.8 Tables
+ *
+ * Original values are premultiplied by 2 for better precision (that is the
+ * maximum which is possible without overflows)
+ *
+ * Note: in each block of 8 numbers sign was changed for elements 2 and 7
+ * in order to compensate the same change applied to cos_table_fixed_4
+ */
+#define SBC_PROTO_FIXED4_SCALE \
+ ((sizeof(FIXED_T) * CHAR_BIT - 1) - SBC_FIXED_EXTRA_BITS + 1)
+#define F(x) (FIXED_A) ((x * 2) * \
+ ((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+static const FIXED_T _sbc_proto_fixed4[40] = {
+ F(0.00000000E+00), F(5.36548976E-04),
+ -F(1.49188357E-03), F(2.73370904E-03),
+ F(3.83720193E-03), F(3.89205149E-03),
+ F(1.86581691E-03), F(3.06012286E-03),
+
+ F(1.09137620E-02), F(2.04385087E-02),
+ -F(2.88757392E-02), F(3.21939290E-02),
+ F(2.58767811E-02), F(6.13245186E-03),
+ -F(2.88217274E-02), F(7.76463494E-02),
+
+ F(1.35593274E-01), F(1.94987841E-01),
+ -F(2.46636662E-01), F(2.81828203E-01),
+ F(2.94315332E-01), F(2.81828203E-01),
+ F(2.46636662E-01), -F(1.94987841E-01),
+
+ -F(1.35593274E-01), -F(7.76463494E-02),
+ F(2.88217274E-02), F(6.13245186E-03),
+ F(2.58767811E-02), F(3.21939290E-02),
+ F(2.88757392E-02), -F(2.04385087E-02),
+
+ -F(1.09137620E-02), -F(3.06012286E-03),
+ -F(1.86581691E-03), F(3.89205149E-03),
+ F(3.83720193E-03), F(2.73370904E-03),
+ F(1.49188357E-03), -F(5.36548976E-04),
+};
+#undef F
+
+/*
+ * To produce this cosine matrix in Octave:
+ *
+ * b = zeros(4, 8);
+ * for i = 0:3
+ * for j = 0:7 b(i+1, j+1) = cos((i + 0.5) * (j - 2) * (pi/4))
+ * endfor
+ * endfor;
+ * printf("%.10f, ", b');
+ *
+ * Note: in each block of 8 numbers sign was changed for elements 2 and 7
+ *
+ * Change of sign for element 2 allows to replace constant 1.0 (not
+ * representable in Q15 format) with -1.0 (fine with Q15).
+ * Changed sign for element 7 allows to have more similar constants
+ * and simplify subband filter function code.
+ */
+#define SBC_COS_TABLE_FIXED4_SCALE \
+ ((sizeof(FIXED_T) * CHAR_BIT - 1) + SBC_FIXED_EXTRA_BITS)
+#define F(x) (FIXED_A) ((x) * \
+ ((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+static const FIXED_T cos_table_fixed_4[32] = {
+ F(0.7071067812), F(0.9238795325), -F(1.0000000000), F(0.9238795325),
+ F(0.7071067812), F(0.3826834324), F(0.0000000000), F(0.3826834324),
+
+ -F(0.7071067812), F(0.3826834324), -F(1.0000000000), F(0.3826834324),
+ -F(0.7071067812), -F(0.9238795325), -F(0.0000000000), -F(0.9238795325),
+
+ -F(0.7071067812), -F(0.3826834324), -F(1.0000000000), -F(0.3826834324),
+ -F(0.7071067812), F(0.9238795325), F(0.0000000000), F(0.9238795325),
+
+ F(0.7071067812), -F(0.9238795325), -F(1.0000000000), -F(0.9238795325),
+ F(0.7071067812), -F(0.3826834324), -F(0.0000000000), -F(0.3826834324),
+};
+#undef F
+
+/* A2DP specification: Section 12.8 Tables
+ *
+ * Original values are premultiplied by 4 for better precision (that is the
+ * maximum which is possible without overflows)
+ *
+ * Note: in each block of 16 numbers sign was changed for elements 4, 13, 14, 15
+ * in order to compensate the same change applied to cos_table_fixed_8
+ */
+#define SBC_PROTO_FIXED8_SCALE \
+ ((sizeof(FIXED_T) * CHAR_BIT - 1) - SBC_FIXED_EXTRA_BITS + 2)
+#define F(x) (FIXED_A) ((x * 4) * \
+ ((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+static const FIXED_T _sbc_proto_fixed8[80] = {
+ F(0.00000000E+00), F(1.56575398E-04),
+ F(3.43256425E-04), F(5.54620202E-04),
+ -F(8.23919506E-04), F(1.13992507E-03),
+ F(1.47640169E-03), F(1.78371725E-03),
+ F(2.01182542E-03), F(2.10371989E-03),
+ F(1.99454554E-03), F(1.61656283E-03),
+ F(9.02154502E-04), F(1.78805361E-04),
+ F(1.64973098E-03), F(3.49717454E-03),
+
+ F(5.65949473E-03), F(8.02941163E-03),
+ F(1.04584443E-02), F(1.27472335E-02),
+ -F(1.46525263E-02), F(1.59045603E-02),
+ F(1.62208471E-02), F(1.53184106E-02),
+ F(1.29371806E-02), F(8.85757540E-03),
+ F(2.92408442E-03), -F(4.91578024E-03),
+ -F(1.46404076E-02), F(2.61098752E-02),
+ F(3.90751381E-02), F(5.31873032E-02),
+
+ F(6.79989431E-02), F(8.29847578E-02),
+ F(9.75753918E-02), F(1.11196689E-01),
+ -F(1.23264548E-01), F(1.33264415E-01),
+ F(1.40753505E-01), F(1.45389847E-01),
+ F(1.46955068E-01), F(1.45389847E-01),
+ F(1.40753505E-01), F(1.33264415E-01),
+ F(1.23264548E-01), -F(1.11196689E-01),
+ -F(9.75753918E-02), -F(8.29847578E-02),
+
+ -F(6.79989431E-02), -F(5.31873032E-02),
+ -F(3.90751381E-02), -F(2.61098752E-02),
+ F(1.46404076E-02), -F(4.91578024E-03),
+ F(2.92408442E-03), F(8.85757540E-03),
+ F(1.29371806E-02), F(1.53184106E-02),
+ F(1.62208471E-02), F(1.59045603E-02),
+ F(1.46525263E-02), -F(1.27472335E-02),
+ -F(1.04584443E-02), -F(8.02941163E-03),
+
+ -F(5.65949473E-03), -F(3.49717454E-03),
+ -F(1.64973098E-03), -F(1.78805361E-04),
+ -F(9.02154502E-04), F(1.61656283E-03),
+ F(1.99454554E-03), F(2.10371989E-03),
+ F(2.01182542E-03), F(1.78371725E-03),
+ F(1.47640169E-03), F(1.13992507E-03),
+ F(8.23919506E-04), -F(5.54620202E-04),
+ -F(3.43256425E-04), -F(1.56575398E-04),
+};
+#undef F
+
+/*
+ * To produce this cosine matrix in Octave:
+ *
+ * b = zeros(8, 16);
+ * for i = 0:7
+ * for j = 0:15 b(i+1, j+1) = cos((i + 0.5) * (j - 4) * (pi/8))
+ * endfor endfor;
+ * printf("%.10f, ", b');
+ *
+ * Note: in each block of 16 numbers sign was changed for elements 4, 13, 14, 15
+ *
+ * Change of sign for element 4 allows to replace constant 1.0 (not
+ * representable in Q15 format) with -1.0 (fine with Q15).
+ * Changed signs for elements 13, 14, 15 allow to have more similar constants
+ * and simplify subband filter function code.
+ */
+#define SBC_COS_TABLE_FIXED8_SCALE \
+ ((sizeof(FIXED_T) * CHAR_BIT - 1) + SBC_FIXED_EXTRA_BITS)
+#define F(x) (FIXED_A) ((x) * \
+ ((FIXED_A) 1 << (sizeof(FIXED_T) * CHAR_BIT - 1)) + 0.5)
+static const FIXED_T cos_table_fixed_8[128] = {
+ F(0.7071067812), F(0.8314696123), F(0.9238795325), F(0.9807852804),
+ -F(1.0000000000), F(0.9807852804), F(0.9238795325), F(0.8314696123),
+ F(0.7071067812), F(0.5555702330), F(0.3826834324), F(0.1950903220),
+ F(0.0000000000), F(0.1950903220), F(0.3826834324), F(0.5555702330),
+
+ -F(0.7071067812), -F(0.1950903220), F(0.3826834324), F(0.8314696123),
+ -F(1.0000000000), F(0.8314696123), F(0.3826834324), -F(0.1950903220),
+ -F(0.7071067812), -F(0.9807852804), -F(0.9238795325), -F(0.5555702330),
+ -F(0.0000000000), -F(0.5555702330), -F(0.9238795325), -F(0.9807852804),
+
+ -F(0.7071067812), -F(0.9807852804), -F(0.3826834324), F(0.5555702330),
+ -F(1.0000000000), F(0.5555702330), -F(0.3826834324), -F(0.9807852804),
+ -F(0.7071067812), F(0.1950903220), F(0.9238795325), F(0.8314696123),
+ F(0.0000000000), F(0.8314696123), F(0.9238795325), F(0.1950903220),
+
+ F(0.7071067812), -F(0.5555702330), -F(0.9238795325), F(0.1950903220),
+ -F(1.0000000000), F(0.1950903220), -F(0.9238795325), -F(0.5555702330),
+ F(0.7071067812), F(0.8314696123), -F(0.3826834324), -F(0.9807852804),
+ -F(0.0000000000), -F(0.9807852804), -F(0.3826834324), F(0.8314696123),
+
+ F(0.7071067812), F(0.5555702330), -F(0.9238795325), -F(0.1950903220),
+ -F(1.0000000000), -F(0.1950903220), -F(0.9238795325), F(0.5555702330),
+ F(0.7071067812), -F(0.8314696123), -F(0.3826834324), F(0.9807852804),
+ F(0.0000000000), F(0.9807852804), -F(0.3826834324), -F(0.8314696123),
+
+ -F(0.7071067812), F(0.9807852804), -F(0.3826834324), -F(0.5555702330),
+ -F(1.0000000000), -F(0.5555702330), -F(0.3826834324), F(0.9807852804),
+ -F(0.7071067812), -F(0.1950903220), F(0.9238795325), -F(0.8314696123),
+ -F(0.0000000000), -F(0.8314696123), F(0.9238795325), -F(0.1950903220),
+
+ -F(0.7071067812), F(0.1950903220), F(0.3826834324), -F(0.8314696123),
+ -F(1.0000000000), -F(0.8314696123), F(0.3826834324), F(0.1950903220),
+ -F(0.7071067812), F(0.9807852804), -F(0.9238795325), F(0.5555702330),
+ -F(0.0000000000), F(0.5555702330), -F(0.9238795325), F(0.9807852804),
+
+ F(0.7071067812), -F(0.8314696123), F(0.9238795325), -F(0.9807852804),
+ -F(1.0000000000), -F(0.9807852804), F(0.9238795325), -F(0.8314696123),
+ F(0.7071067812), -F(0.5555702330), F(0.3826834324), -F(0.1950903220),
+ -F(0.0000000000), -F(0.1950903220), F(0.3826834324), -F(0.5555702330),
+};
+#undef F
diff --git a/src/modules/module-alsa-sink.c b/src/modules/module-alsa-sink.c
index 0e15da3c..95a8c972 100644
--- a/src/modules/module-alsa-sink.c
+++ b/src/modules/module-alsa-sink.c
@@ -241,7 +241,7 @@ static size_t check_left_to_play(struct userdata *u, snd_pcm_sframes_t n) {
return left_to_play;
}
-static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {
+static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
int work_done = 0;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_play;
@@ -261,7 +261,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {
/* First we determine how many samples are missing to fill the
* buffer up to 100% */
- if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+ if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
continue;
@@ -279,14 +279,23 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {
* need to guarantee that clients only have to keep around
* a single hw buffer length. */
- if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
+ if (!polled &&
+ pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
break;
- if (PA_UNLIKELY(n <= u->hwbuf_unused_frames))
+ if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) {
+
+ if (polled)
+ pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
+ "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.");
+
break;
+ }
n -= u->hwbuf_unused_frames;
+ polled = FALSE;
+
/* pa_log_debug("Filling up"); */
for (;;) {
@@ -299,7 +308,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {
/* pa_log_debug("%lu frames to write", (unsigned long) frames); */
- if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) {
+ if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
continue;
@@ -357,7 +366,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec) {
return work_done;
}
-static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) {
+static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
int work_done = 0;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_play;
@@ -374,7 +383,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) {
snd_pcm_hwsync(u->pcm_handle);
- if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+ if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
continue;
@@ -392,14 +401,23 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec) {
* need to guarantee that clients only have to keep around
* a single hw buffer length. */
- if (pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
+ if (!polled &&
+ pa_bytes_to_usec(left_to_play, &u->sink->sample_spec) > process_usec+max_sleep_usec/2)
break;
- if (PA_UNLIKELY(n <= u->hwbuf_unused_frames))
+ if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) {
+
+ if (polled)
+ pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
+ "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.");
+
break;
+ }
n -= u->hwbuf_unused_frames;
+ polled = FALSE;
+
for (;;) {
snd_pcm_sframes_t frames;
void *p;
@@ -796,7 +814,7 @@ static int sink_get_volume_cb(pa_sink *s) {
VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
#endif
- r.values[i] = pa_sw_volume_from_dB((double) alsa_vol / 100.0);
+ r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
} else {
if ((err = snd_mixer_selem_get_playback_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
@@ -818,7 +836,7 @@ static int sink_get_volume_cb(pa_sink *s) {
VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
#endif
- pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) alsa_vol / 100.0));
+ pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
} else {
@@ -875,6 +893,7 @@ static int sink_set_volume_cb(pa_sink *s) {
if (u->hw_dB_supported) {
alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
+ alsa_vol += u->hw_dB_max;
alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
if ((err = snd_mixer_selem_set_playback_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, 1)) < 0)
@@ -883,7 +902,7 @@ static int sink_set_volume_cb(pa_sink *s) {
if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
- r.values[i] = pa_sw_volume_from_dB((double) alsa_vol / 100.0);
+ r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
} else {
alsa_vol = to_alsa_volume(u, vol);
@@ -906,6 +925,7 @@ static int sink_set_volume_cb(pa_sink *s) {
if (u->hw_dB_supported) {
alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
+ alsa_vol += u->hw_dB_max;
alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
if ((err = snd_mixer_selem_set_playback_dB_all(u->mixer_elem, alsa_vol, 1)) < 0)
@@ -914,7 +934,7 @@ static int sink_set_volume_cb(pa_sink *s) {
if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
- pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) alsa_vol / 100.0));
+ pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
} else {
alsa_vol = to_alsa_volume(u, vol);
@@ -1082,6 +1102,7 @@ finish:
static void thread_func(void *userdata) {
struct userdata *u = userdata;
+ unsigned short revents = 0;
pa_assert(u);
@@ -1108,9 +1129,9 @@ static void thread_func(void *userdata) {
goto fail;
if (u->use_mmap)
- work_done = mmap_write(u, &sleep_usec);
+ work_done = mmap_write(u, &sleep_usec, revents & POLLOUT);
else
- work_done = unix_write(u, &sleep_usec);
+ work_done = unix_write(u, &sleep_usec, revents & POLLOUT);
if (work_done < 0)
goto fail;
@@ -1178,7 +1199,6 @@ static void thread_func(void *userdata) {
/* Tell ALSA about this and process its response */
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
struct pollfd *pollfd;
- unsigned short revents = 0;
int err;
unsigned n;
@@ -1189,7 +1209,7 @@ static void thread_func(void *userdata) {
goto fail;
}
- if (revents & (POLLERR|POLLNVAL|POLLHUP|POLLPRI)) {
+ if (revents & (POLLIN|POLLERR|POLLNVAL|POLLHUP|POLLPRI)) {
if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
goto fail;
@@ -1199,7 +1219,8 @@ static void thread_func(void *userdata) {
if (revents && u->use_tsched)
pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : "");
- }
+ } else
+ revents = 0;
}
fail:
@@ -1388,7 +1409,7 @@ int pa__init(pa_module*m) {
}
if (found)
- if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Master", "PCM")))
+ if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Master", "PCM", TRUE)))
found = FALSE;
if (!found) {
diff --git a/src/modules/module-alsa-source.c b/src/modules/module-alsa-source.c
index 2827ecfe..b6c6ed1a 100644
--- a/src/modules/module-alsa-source.c
+++ b/src/modules/module-alsa-source.c
@@ -238,7 +238,7 @@ static size_t check_left_to_record(struct userdata *u, snd_pcm_sframes_t n) {
return left_to_record;
}
-static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {
+static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
int work_done = 0;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_record;
@@ -255,7 +255,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {
snd_pcm_hwsync(u->pcm_handle);
- if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+ if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
continue;
@@ -266,11 +266,20 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {
left_to_record = check_left_to_record(u, n);
if (u->use_tsched)
- if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
+ if (!polled &&
+ pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
break;
- if (PA_UNLIKELY(n <= 0))
+ if (PA_UNLIKELY(n <= 0)) {
+
+ if (polled)
+ pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
+ "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio device.");
+
break;
+ }
+
+ polled = FALSE;
for (;;) {
int err;
@@ -282,7 +291,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {
/* pa_log_debug("%lu frames to read", (unsigned long) frames); */
- if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) {
+ if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
continue;
@@ -336,7 +345,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec) {
return work_done;
}
-static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) {
+static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) {
int work_done = 0;
pa_usec_t max_sleep_usec = 0, process_usec = 0;
size_t left_to_record;
@@ -353,7 +362,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) {
snd_pcm_hwsync(u->pcm_handle);
- if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
+ if (PA_UNLIKELY((n = pa_alsa_safe_avail_update(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
if ((r = try_recover(u, "snd_pcm_avail_update", (int) n)) == 0)
continue;
@@ -364,11 +373,20 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec) {
left_to_record = check_left_to_record(u, n);
if (u->use_tsched)
- if (pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
+ if (!polled &&
+ pa_bytes_to_usec(left_to_record, &u->source->sample_spec) > process_usec+max_sleep_usec/2)
break;
- if (PA_UNLIKELY(n <= 0))
+ if (PA_UNLIKELY(n <= 0)) {
+
+ if (polled)
+ pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
+ "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.");
+
return work_done;
+ }
+
+ polled = FALSE;
for (;;) {
void *p;
@@ -742,7 +760,7 @@ static int source_get_volume_cb(pa_source *s) {
VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
#endif
- r.values[i] = pa_sw_volume_from_dB((double) alsa_vol / 100.0);
+ r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
} else {
if ((err = snd_mixer_selem_get_capture_volume(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
@@ -764,7 +782,7 @@ static int source_get_volume_cb(pa_source *s) {
VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
#endif
- pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) alsa_vol / 100.0));
+ pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
} else {
@@ -821,6 +839,7 @@ static int source_set_volume_cb(pa_source *s) {
if (u->hw_dB_supported) {
alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
+ alsa_vol += u->hw_dB_max;
alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
if ((err = snd_mixer_selem_set_capture_dB(u->mixer_elem, u->mixer_map[i], alsa_vol, 1)) < 0)
@@ -829,7 +848,7 @@ static int source_set_volume_cb(pa_source *s) {
if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
- r.values[i] = pa_sw_volume_from_dB((double) alsa_vol / 100.0);
+ r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
} else {
alsa_vol = to_alsa_volume(u, vol);
@@ -852,6 +871,7 @@ static int source_set_volume_cb(pa_source *s) {
if (u->hw_dB_supported) {
alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
+ alsa_vol += u->hw_dB_max;
alsa_vol = PA_CLAMP_UNLIKELY(alsa_vol, u->hw_dB_min, u->hw_dB_max);
if ((err = snd_mixer_selem_set_capture_dB_all(u->mixer_elem, alsa_vol, 1)) < 0)
@@ -860,7 +880,7 @@ static int source_set_volume_cb(pa_source *s) {
if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
- pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) alsa_vol / 100.0));
+ pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
} else {
alsa_vol = to_alsa_volume(u, vol);
@@ -948,6 +968,7 @@ static void source_update_requested_latency_cb(pa_source *s) {
static void thread_func(void *userdata) {
struct userdata *u = userdata;
+ unsigned short revents = 0;
pa_assert(u);
@@ -970,9 +991,9 @@ static void thread_func(void *userdata) {
pa_usec_t sleep_usec = 0;
if (u->use_mmap)
- work_done = mmap_read(u, &sleep_usec);
+ work_done = mmap_read(u, &sleep_usec, revents & POLLIN);
else
- work_done = unix_read(u, &sleep_usec);
+ work_done = unix_read(u, &sleep_usec, revents & POLLIN);
if (work_done < 0)
goto fail;
@@ -1014,7 +1035,6 @@ static void thread_func(void *userdata) {
/* Tell ALSA about this and process its response */
if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) {
struct pollfd *pollfd;
- unsigned short revents = 0;
int err;
unsigned n;
@@ -1025,7 +1045,7 @@ static void thread_func(void *userdata) {
goto fail;
}
- if (revents & (POLLERR|POLLNVAL|POLLHUP|POLLPRI)) {
+ if (revents & (POLLOUT|POLLERR|POLLNVAL|POLLHUP|POLLPRI)) {
if (pa_alsa_recover_from_poll(u->pcm_handle, revents) < 0)
goto fail;
@@ -1034,7 +1054,8 @@ static void thread_func(void *userdata) {
if (revents && u->use_tsched)
pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : "");
- }
+ } else
+ revents = 0;
}
fail:
@@ -1215,7 +1236,7 @@ int pa__init(pa_module*m) {
}
if (found)
- if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", "Mic")))
+ if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", "Mic", FALSE)))
found = FALSE;
if (!found) {
diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c
index 9d60c29e..cd3f3112 100644
--- a/src/modules/module-always-sink.c
+++ b/src/modules/module-always-sink.c
@@ -50,7 +50,7 @@ static const char* const valid_modargs[] = {
struct userdata {
pa_hook_slot *put_slot, *unlink_slot;
- pa_module* null_module;
+ uint32_t null_module;
pa_bool_t ignore;
char *sink_name;
};
@@ -59,10 +59,11 @@ static void load_null_sink_if_needed(pa_core *c, pa_sink *sink, struct userdata*
pa_sink *target;
uint32_t idx;
char *t;
+ pa_module *m;
pa_assert(c);
pa_assert(u);
- pa_assert(!u->null_module);
+ pa_assert(u->null_module == PA_INVALID_INDEX);
/* Loop through all sinks and check to see if we have *any*
* sinks. Ignore the sink passed in (if it's not null) */
@@ -78,12 +79,13 @@ static void load_null_sink_if_needed(pa_core *c, pa_sink *sink, struct userdata*
u->ignore = TRUE;
t = pa_sprintf_malloc("sink_name=%s", u->sink_name);
- u->null_module = pa_module_load(c, "module-null-sink", t);
+ m = pa_module_load(c, "module-null-sink", t);
+ u->null_module = m ? m->index : PA_INVALID_INDEX;
pa_xfree(t);
u->ignore = FALSE;
- if (!u->null_module)
+ if (!m)
pa_log_warn("Unable to load module-null-sink");
}
@@ -99,17 +101,17 @@ static pa_hook_result_t put_hook_callback(pa_core *c, pa_sink *sink, void* userd
return PA_HOOK_OK;
/* Auto-loaded null-sink not active, so ignoring newly detected sink. */
- if (!u->null_module)
+ if (u->null_module == PA_INVALID_INDEX)
return PA_HOOK_OK;
/* This is us detecting ourselves on load in a different way... just ignore this too. */
- if (sink->module == u->null_module)
+ if (sink->module && sink->module->index == u->null_module)
return PA_HOOK_OK;
pa_log_info("A new sink has been discovered. Unloading null-sink.");
- pa_module_unload_request(u->null_module, TRUE);
- u->null_module = NULL;
+ pa_module_unload_request_by_index(c, u->null_module, TRUE);
+ u->null_module = PA_INVALID_INDEX;
return PA_HOOK_OK;
}
@@ -122,9 +124,9 @@ static pa_hook_result_t unlink_hook_callback(pa_core *c, pa_sink *sink, void* us
pa_assert(u);
/* First check to see if it's our own null-sink that's been removed... */
- if (u->null_module && sink->module == u->null_module) {
+ if (u->null_module != PA_INVALID_INDEX && sink->module && sink->module->index == u->null_module) {
pa_log_debug("Autoloaded null-sink removed");
- u->null_module = NULL;
+ u->null_module = PA_INVALID_INDEX;
return PA_HOOK_OK;
}
@@ -148,7 +150,7 @@ int pa__init(pa_module*m) {
u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
u->put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) put_hook_callback, u);
u->unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) unlink_hook_callback, u);
- u->null_module = NULL;
+ u->null_module = PA_INVALID_INDEX;
u->ignore = FALSE;
pa_modargs_free(ma);
@@ -170,8 +172,8 @@ void pa__done(pa_module*m) {
pa_hook_slot_free(u->put_slot);
if (u->unlink_slot)
pa_hook_slot_free(u->unlink_slot);
- if (u->null_module)
- pa_module_unload_request(u->null_module, TRUE);
+ if (u->null_module != PA_INVALID_INDEX)
+ pa_module_unload_request_by_index(m->core, u->null_module, TRUE);
pa_xfree(u->sink_name);
pa_xfree(u);
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 86a78810..c0cb0dc5 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -332,7 +332,7 @@ int pa__init(pa_module*m) {
if (!fname)
goto fail;
- if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) {
+ if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT|GDBM_NOLOCK, 0600, NULL))) {
pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
pa_xfree(fname);
goto fail;
diff --git a/src/modules/module-flat-volume.c b/src/modules/module-flat-volume.c
new file mode 100644
index 00000000..9bc8055a
--- /dev/null
+++ b/src/modules/module-flat-volume.c
@@ -0,0 +1,224 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ Copyright 2004-2006, 2008 Lennart Poettering
+
+ Contact: Marc-Andre Lureau <marc-andre.lureau@nokia.com>
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "module-flat-volume-symdef.h"
+
+PA_MODULE_AUTHOR("Marc-Andre Lureau");
+PA_MODULE_DESCRIPTION("Flat volume");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE("");
+
+struct userdata {
+ pa_subscription *subscription;
+ pa_hook_slot *sink_input_set_volume_hook_slot;
+ pa_hook_slot *sink_input_fixate_hook_slot;
+};
+
+static void process_input_volume_change(
+ pa_cvolume *dest_volume,
+ const pa_cvolume *dest_virtual_volume,
+ pa_channel_map *dest_channel_map,
+ pa_sink_input *this,
+ pa_sink *sink) {
+
+ pa_sink_input *i;
+ uint32_t idx;
+ pa_cvolume max_volume, sink_volume;
+
+ pa_assert(dest_volume);
+ pa_assert(dest_virtual_volume);
+ pa_assert(dest_channel_map);
+ pa_assert(sink);
+
+ if (!(sink->flags & PA_SINK_DECIBEL_VOLUME))
+ return;
+
+ pa_log_debug("Sink input volume changed");
+
+ max_volume = *dest_virtual_volume;
+ pa_cvolume_remap(&max_volume, dest_channel_map, &sink->channel_map);
+
+ for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &idx))) {
+ /* skip this sink-input if we are processing a volume change request */
+ if (this && this == i)
+ continue;
+
+ if (pa_cvolume_max(&i->virtual_volume) > pa_cvolume_max(&max_volume)) {
+ max_volume = i->virtual_volume;
+ pa_cvolume_remap(&max_volume, &i->channel_map, &sink->channel_map);
+ }
+ }
+
+ /* Set the master volume, and normalize inputs */
+ if (!pa_cvolume_equal(&max_volume, &sink->volume)) {
+
+ pa_sink_set_volume(sink, &max_volume);
+
+ pa_log_debug("sink = %.2f (changed)", (double)pa_cvolume_avg(&sink->volume)/PA_VOLUME_NORM);
+
+ /* Now, normalize each of the internal volume (client sink-input volume / sink master volume) */
+ for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &idx))) {
+ /* skip this sink-input if we are processing a volume change request */
+ if (this && this == i)
+ continue;
+
+ sink_volume = max_volume;
+ pa_cvolume_remap(&sink_volume, &sink->channel_map, &i->channel_map);
+ pa_sw_cvolume_divide(&i->volume, &i->virtual_volume, &sink_volume);
+ pa_log_debug("sink input { id = %d, flat = %.2f, true = %.2f }",
+ i->index,
+ (double)pa_cvolume_avg(&i->virtual_volume)/PA_VOLUME_NORM,
+ (double)pa_cvolume_avg(&i->volume)/PA_VOLUME_NORM);
+ pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, &i->volume, 1), 0, NULL, pa_xfree);
+ }
+ } else
+ pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(&sink->volume)/PA_VOLUME_NORM);
+
+ /* and this one */
+
+ sink_volume = max_volume;
+ pa_cvolume_remap(&sink_volume, &sink->channel_map, dest_channel_map);
+ pa_sw_cvolume_divide(dest_volume, dest_virtual_volume, &sink_volume);
+ pa_log_debug("caller sink input: { id = %d, flat = %.2f, true = %.2f }",
+ this ? (int)this->index : -1,
+ (double)pa_cvolume_avg(dest_virtual_volume)/PA_VOLUME_NORM,
+ (double)pa_cvolume_avg(dest_volume)/PA_VOLUME_NORM);
+}
+
+static pa_hook_result_t sink_input_set_volume_hook_callback(pa_core *c, pa_sink_input_set_volume_data *this, struct userdata *u) {
+ pa_assert(this);
+ pa_assert(this->sink_input);
+
+ process_input_volume_change(&this->volume, &this->virtual_volume, &this->sink_input->channel_map,
+ this->sink_input, this->sink_input->sink);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *this, struct userdata *u) {
+ pa_assert(this);
+ pa_assert(this->sink);
+
+ process_input_volume_change(&this->volume, &this->virtual_volume, &this->channel_map,
+ NULL, this->sink);
+
+ return PA_HOOK_OK;
+}
+
+static void subscribe_callback(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+ struct userdata *u = userdata;
+ pa_sink *sink;
+ pa_sink_input *i;
+ uint32_t iidx;
+ pa_cvolume sink_volume;
+
+ pa_assert(core);
+ pa_assert(u);
+
+ if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
+ t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE))
+ return;
+
+ if (!(sink = pa_idxset_get_by_index(core->sinks, idx)))
+ return;
+
+ if (!(sink->flags & PA_SINK_DECIBEL_VOLUME))
+ return;
+
+ pa_log_debug("Sink volume changed");
+ pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(pa_sink_get_volume(sink, FALSE)) / PA_VOLUME_NORM);
+
+ sink_volume = *pa_sink_get_volume(sink, FALSE);
+
+ for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &iidx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &iidx))) {
+ pa_cvolume si_volume;
+
+ si_volume = sink_volume;
+ pa_cvolume_remap(&si_volume, &sink->channel_map, &i->channel_map);
+ pa_sw_cvolume_multiply(&i->virtual_volume, &i->volume, &si_volume);
+ pa_log_debug("sink input = { id = %d, flat = %.2f, true = %.2f }",
+ i->index,
+ (double)pa_cvolume_avg(&i->virtual_volume)/PA_VOLUME_NORM,
+ (double)pa_cvolume_avg(&i->volume)/PA_VOLUME_NORM);
+ pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ }
+}
+
+int pa__init(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ u = pa_xnew(struct userdata, 1);
+ m->userdata = u;
+
+ u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
+ u->sink_input_set_volume_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_set_volume_hook_callback, u);
+
+ u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK, subscribe_callback, u);
+
+ return 0;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata* u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->subscription)
+ pa_subscription_free(u->subscription);
+
+ if (u->sink_input_set_volume_hook_slot)
+ pa_hook_slot_free(u->sink_input_set_volume_hook_slot);
+ if (u->sink_input_fixate_hook_slot)
+ pa_hook_slot_free(u->sink_input_fixate_hook_slot);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
index c76a366c..8c1ab329 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -511,7 +511,7 @@ static void device_removed_cb(LibHalContext* context, const char *udi) {
pa_log_debug("Device removed: %s", udi);
if ((d = pa_hashmap_remove(u->devices, udi))) {
- pa_module_unload_by_index(u->core, d->index, TRUE);
+ pa_module_unload_request_by_index(u->core, d->index, TRUE);
hal_device_free(d);
}
}
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index 9127af01..a27ed712 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -359,6 +359,16 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
}
}
+/* Called from main context */
+static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ return u->sink != dest;
+}
+
int pa__init(pa_module*m) {
struct userdata *u;
pa_sample_spec ss;
@@ -737,6 +747,7 @@ int pa__init(pa_module*m) {
u->sink_input->attach = sink_input_attach_cb;
u->sink_input->detach = sink_input_detach_cb;
u->sink_input->state_change = sink_input_state_change_cb;
+ u->sink_input->may_move_to = sink_input_may_move_to_cb;
u->sink_input->userdata = u;
pa_sink_put(u->sink);
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index ae230b2c..2b55c823 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -101,8 +101,8 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
size_t n = 0;
int l;
-#ifdef TIOCINQ
- if (ioctl(u->fd, TIOCINQ, &l) >= 0 && l > 0)
+#ifdef FIONREAD
+ if (ioctl(u->fd, FIONREAD, &l) >= 0 && l > 0)
n = (size_t) l;
#endif
diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
index 25151d95..e6437a05 100644
--- a/src/modules/module-pipe-source.c
+++ b/src/modules/module-pipe-source.c
@@ -31,6 +31,7 @@
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
+#include <sys/ioctl.h>
#include <sys/poll.h>
#include <pulse/xmalloc.h>
@@ -89,6 +90,34 @@ static const char* const valid_modargs[] = {
NULL
};
+static int source_process_msg(
+ pa_msgobject *o,
+ int code,
+ void *data,
+ int64_t offset,
+ pa_memchunk *chunk) {
+
+ struct userdata *u = PA_SOURCE(o)->userdata;
+
+ switch (code) {
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ size_t n = 0;
+ int l;
+
+#ifdef FIONREAD
+ if (ioctl(u->fd, FIONREAD, &l) >= 0 && l > 0)
+ n = (size_t) l;
+#endif
+
+ *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->source->sample_spec);
+ return 0;
+ }
+ }
+
+ return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
static void thread_func(void *userdata) {
struct userdata *u = userdata;
int read_type = 0;
@@ -243,6 +272,7 @@ int pa__init(pa_module*m) {
goto fail;
}
+ u->source->parent.process_msg = source_process_msg;
u->source->userdata = u;
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
diff --git a/src/modules/module-raop-discover.c b/src/modules/module-raop-discover.c
new file mode 100644
index 00000000..3706d921
--- /dev/null
+++ b/src/modules/module-raop-discover.c
@@ -0,0 +1,380 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2008 Colin Guthrie
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/namereg.h>
+#include <pulsecore/avahi-wrap.h>
+
+#include "module-raop-discover-symdef.h"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("mDNS/DNS-SD Service Discovery of Airtunes");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define SERVICE_TYPE_SINK "_raop._tcp"
+
+static const char* const valid_modargs[] = {
+ NULL
+};
+
+struct tunnel {
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+ char *name, *type, *domain;
+ uint32_t module_index;
+};
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ AvahiPoll *avahi_poll;
+ AvahiClient *client;
+ AvahiServiceBrowser *sink_browser;
+
+ pa_hashmap *tunnels;
+};
+
+static unsigned tunnel_hash(const void *p) {
+ const struct tunnel *t = p;
+
+ return
+ (unsigned) t->interface +
+ (unsigned) t->protocol +
+ pa_idxset_string_hash_func(t->name) +
+ pa_idxset_string_hash_func(t->type) +
+ pa_idxset_string_hash_func(t->domain);
+}
+
+static int tunnel_compare(const void *a, const void *b) {
+ const struct tunnel *ta = a, *tb = b;
+ int r;
+
+ if (ta->interface != tb->interface)
+ return 1;
+ if (ta->protocol != tb->protocol)
+ return 1;
+ if ((r = strcmp(ta->name, tb->name)))
+ return r;
+ if ((r = strcmp(ta->type, tb->type)))
+ return r;
+ if ((r = strcmp(ta->domain, tb->domain)))
+ return r;
+
+ return 0;
+}
+
+static struct tunnel *tunnel_new(
+ AvahiIfIndex interface, AvahiProtocol protocol,
+ const char *name, const char *type, const char *domain) {
+
+ struct tunnel *t;
+ t = pa_xnew(struct tunnel, 1);
+ t->interface = interface;
+ t->protocol = protocol;
+ t->name = pa_xstrdup(name);
+ t->type = pa_xstrdup(type);
+ t->domain = pa_xstrdup(domain);
+ t->module_index = PA_IDXSET_INVALID;
+ return t;
+}
+
+static void tunnel_free(struct tunnel *t) {
+ pa_assert(t);
+ pa_xfree(t->name);
+ pa_xfree(t->type);
+ pa_xfree(t->domain);
+ pa_xfree(t);
+}
+
+static void resolver_cb(
+ AvahiServiceResolver *r,
+ AvahiIfIndex interface, AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *name, const char *type, const char *domain,
+ const char *host_name, const AvahiAddress *a, uint16_t port,
+ AvahiStringList *txt,
+ AvahiLookupResultFlags flags,
+ void *userdata) {
+
+ struct userdata *u = userdata;
+ struct tunnel *tnl;
+
+ pa_assert(u);
+
+ tnl = tunnel_new(interface, protocol, name, type, domain);
+
+ if (event != AVAHI_RESOLVER_FOUND)
+ pa_log("Resolving of '%s' failed: %s", name, avahi_strerror(avahi_client_errno(u->client)));
+ else {
+ char *device = NULL, *dname, *vname, *args;
+ char at[AVAHI_ADDRESS_STR_MAX];
+ AvahiStringList *l;
+ pa_module *m;
+
+ for (l = txt; l; l = l->next) {
+ char *key, *value;
+ pa_assert_se(avahi_string_list_get_pair(l, &key, &value, NULL) == 0);
+
+ pa_log_debug("Found key: '%s' with value: '%s'", key, value);
+ if (strcmp(key, "device") == 0) {
+ pa_xfree(device);
+ device = value;
+ value = NULL;
+ }
+ avahi_free(key);
+ avahi_free(value);
+ }
+
+ if (device)
+ dname = pa_sprintf_malloc("airtunes.%s.%s", host_name, device);
+ else
+ dname = pa_sprintf_malloc("airtunes.%s", host_name);
+
+ if (!(vname = pa_namereg_make_valid_name(dname))) {
+ pa_log("Cannot construct valid device name from '%s'.", dname);
+ avahi_free(device);
+ pa_xfree(dname);
+ goto finish;
+ }
+ pa_xfree(dname);
+
+ /*
+ TODO: allow this syntax of server name in things....
+ args = pa_sprintf_malloc("server=[%s]:%u "
+ "sink_name=%s",
+ avahi_address_snprint(at, sizeof(at), a), port,
+ vname);*/
+ args = pa_sprintf_malloc("server=%s "
+ "sink_name=%s",
+ avahi_address_snprint(at, sizeof(at), a),
+ vname);
+
+ pa_log_debug("Loading module-raop-sink with arguments '%s'", args);
+
+ if ((m = pa_module_load(u->core, "module-raop-sink", args))) {
+ tnl->module_index = m->index;
+ pa_hashmap_put(u->tunnels, tnl, tnl);
+ tnl = NULL;
+ }
+
+ pa_xfree(vname);
+ pa_xfree(args);
+ avahi_free(device);
+ }
+
+finish:
+
+ avahi_service_resolver_free(r);
+
+ if (tnl)
+ tunnel_free(tnl);
+}
+
+static void browser_cb(
+ AvahiServiceBrowser *b,
+ AvahiIfIndex interface, AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name, const char *type, const char *domain,
+ AvahiLookupResultFlags flags,
+ void *userdata) {
+
+ struct userdata *u = userdata;
+ struct tunnel *t;
+
+ pa_assert(u);
+
+ if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
+ return;
+
+ t = tunnel_new(interface, protocol, name, type, domain);
+
+ if (event == AVAHI_BROWSER_NEW) {
+
+ if (!pa_hashmap_get(u->tunnels, t))
+ if (!(avahi_service_resolver_new(u->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolver_cb, u)))
+ pa_log("avahi_service_resolver_new() failed: %s", avahi_strerror(avahi_client_errno(u->client)));
+
+ /* We ignore the returned resolver object here, since the we don't
+ * need to attach any special data to it, and we can still destory
+ * it from the callback */
+
+ } else if (event == AVAHI_BROWSER_REMOVE) {
+ struct tunnel *t2;
+
+ if ((t2 = pa_hashmap_get(u->tunnels, t))) {
+ pa_module_unload_by_index(u->core, t2->module_index, TRUE);
+ pa_hashmap_remove(u->tunnels, t2);
+ tunnel_free(t2);
+ }
+ }
+
+ tunnel_free(t);
+}
+
+static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(c);
+ pa_assert(u);
+
+ u->client = c;
+
+ switch (state) {
+ case AVAHI_CLIENT_S_REGISTERING:
+ case AVAHI_CLIENT_S_RUNNING:
+ case AVAHI_CLIENT_S_COLLISION:
+
+ if (!u->sink_browser) {
+
+ if (!(u->sink_browser = avahi_service_browser_new(
+ c,
+ AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
+ SERVICE_TYPE_SINK,
+ NULL,
+ 0,
+ browser_cb, u))) {
+
+ pa_log("avahi_service_browser_new() failed: %s", avahi_strerror(avahi_client_errno(c)));
+ pa_module_unload_request(u->module, TRUE);
+ }
+ }
+
+ break;
+
+ case AVAHI_CLIENT_FAILURE:
+ if (avahi_client_errno(c) == AVAHI_ERR_DISCONNECTED) {
+ int error;
+
+ pa_log_debug("Avahi daemon disconnected.");
+
+ if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+ pa_log("avahi_client_new() failed: %s", avahi_strerror(error));
+ pa_module_unload_request(u->module, TRUE);
+ }
+ }
+
+ /* Fall through */
+
+ case AVAHI_CLIENT_CONNECTING:
+
+ if (u->sink_browser) {
+ avahi_service_browser_free(u->sink_browser);
+ u->sink_browser = NULL;
+ }
+
+ break;
+
+ default: ;
+ }
+}
+
+int pa__init(pa_module*m) {
+
+ struct userdata *u;
+ pa_modargs *ma = NULL;
+ int error;
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments.");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ u->sink_browser = NULL;
+
+ u->tunnels = pa_hashmap_new(tunnel_hash, tunnel_compare);
+
+ u->avahi_poll = pa_avahi_poll_new(m->core->mainloop);
+
+ if (!(u->client = avahi_client_new(u->avahi_poll, AVAHI_CLIENT_NO_FAIL, client_callback, u, &error))) {
+ pa_log("pa_avahi_client_new() failed: %s", avahi_strerror(error));
+ goto fail;
+ }
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ pa__done(m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata*u;
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->client)
+ avahi_client_free(u->client);
+
+ if (u->avahi_poll)
+ pa_avahi_poll_free(u->avahi_poll);
+
+ if (u->tunnels) {
+ struct tunnel *t;
+
+ while ((t = pa_hashmap_steal_first(u->tunnels))) {
+ pa_module_unload_by_index(u->core, t->module_index, TRUE);
+ tunnel_free(t);
+ }
+
+ pa_hashmap_free(u->tunnels, NULL, NULL);
+ }
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
new file mode 100644
index 00000000..62f0a73c
--- /dev/null
+++ b/src/modules/module-raop-sink.c
@@ -0,0 +1,675 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2008 Colin Guthrie
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_LINUX_SOCKIOS_H
+#include <linux/sockios.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/socket-client.h>
+#include <pulsecore/authkey.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/socket-util.h>
+
+#include "module-raop-sink-symdef.h"
+#include "rtp.h"
+#include "sdp.h"
+#include "sap.h"
+#include "raop_client.h"
+
+PA_MODULE_AUTHOR("Colin Guthrie");
+PA_MODULE_DESCRIPTION("RAOP Sink (Apple Airtunes)");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+ "sink_name=<name for the sink> "
+ "server=<address> "
+ "format=<sample format> "
+ "channels=<number of channels> "
+ "rate=<sample rate>");
+
+#define DEFAULT_SINK_NAME "airtunes"
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ pa_sink *sink;
+
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+ pa_rtpoll_item *rtpoll_item;
+ pa_thread *thread;
+
+ pa_memchunk raw_memchunk;
+ pa_memchunk encoded_memchunk;
+
+ void *write_data;
+ size_t write_length, write_index;
+
+ void *read_data;
+ size_t read_length, read_index;
+
+ pa_usec_t latency;
+
+ pa_volume_t volume;
+ pa_bool_t muted;
+
+ /*esd_format_t format;*/
+ int32_t rate;
+
+ pa_smoother *smoother;
+ int fd;
+
+ int64_t offset;
+ int64_t encoding_overhead;
+ int32_t next_encoding_overhead;
+ double encoding_ratio;
+
+ pa_raop_client *raop;
+
+ size_t block_size;
+};
+
+static const char* const valid_modargs[] = {
+ "server",
+ "rate",
+ "format",
+ "channels",
+ "sink_name",
+ NULL
+};
+
+enum {
+ SINK_MESSAGE_PASS_SOCKET = PA_SINK_MESSAGE_MAX,
+ SINK_MESSAGE_RIP_SOCKET
+};
+
+static void on_connection(PA_GCC_UNUSED int fd, void*userdata) {
+ struct userdata *u = userdata;
+ pa_assert(u);
+
+ pa_assert(u->fd < 0);
+ u->fd = fd;
+
+ /* Set the initial volume */
+ pa_raop_client_set_volume(u->raop, u->volume);
+
+ pa_log_debug("Connection authenticated, handing fd to IO thread...");
+
+ pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_PASS_SOCKET, NULL, 0, NULL, NULL);
+}
+
+static void on_close(void*userdata) {
+ struct userdata *u = userdata;
+ pa_assert(u);
+
+ pa_log_debug("Connection closed, informing IO thread...");
+
+ pa_asyncmsgq_post(u->thread_mq.inq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RIP_SOCKET, NULL, 0, NULL, NULL);
+}
+
+static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+ struct userdata *u = PA_SINK(o)->userdata;
+
+ switch (code) {
+
+ case PA_SINK_MESSAGE_SET_STATE:
+
+ switch ((pa_sink_state_t) PA_PTR_TO_UINT(data)) {
+
+ case PA_SINK_SUSPENDED:
+ pa_assert(PA_SINK_IS_OPENED(u->sink->thread_info.state));
+
+ pa_smoother_pause(u->smoother, pa_rtclock_usec());
+
+ /* Issue a FLUSH if we are connected */
+ if (u->fd >= 0) {
+ pa_raop_flush(u->raop);
+ }
+ break;
+
+ case PA_SINK_IDLE:
+ case PA_SINK_RUNNING:
+
+ if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+ pa_smoother_resume(u->smoother, pa_rtclock_usec());
+
+ /* The connection can be closed when idle, so check to
+ see if we need to reestablish it */
+ if (u->fd < 0)
+ pa_raop_connect(u->raop);
+ else
+ pa_raop_flush(u->raop);
+ }
+
+ break;
+
+ case PA_SINK_UNLINKED:
+ case PA_SINK_INIT:
+ ;
+ }
+
+ break;
+
+ case PA_SINK_MESSAGE_GET_LATENCY: {
+ pa_usec_t w, r;
+
+ r = pa_smoother_get(u->smoother, pa_rtclock_usec());
+ w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec);
+
+ *((pa_usec_t*) data) = w > r ? w - r : 0;
+ break;
+ }
+
+ case SINK_MESSAGE_PASS_SOCKET: {
+ struct pollfd *pollfd;
+
+ pa_assert(!u->rtpoll_item);
+
+ u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+ pollfd->fd = u->fd;
+ pollfd->events = POLLOUT;
+ /*pollfd->events = */pollfd->revents = 0;
+
+ if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+ /* Our stream has been suspended so we just flush it.... */
+ pa_raop_flush(u->raop);
+ }
+ return 0;
+ }
+
+ case SINK_MESSAGE_RIP_SOCKET: {
+ pa_assert(u->fd >= 0);
+
+ pa_close(u->fd);
+ u->fd = -1;
+
+ if (u->sink->thread_info.state == PA_SINK_SUSPENDED) {
+
+ pa_log_debug("RTSP control connection closed, but we're suspended so let's not worry about it... we'll open it again later");
+
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+ u->rtpoll_item = NULL;
+ } else {
+ /* Quesiton: is this valid here: or should we do some sort of:
+ return pa_sink_process_msg(PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL);
+ ?? */
+ pa_module_unload_request(u->module, TRUE);
+ }
+ return 0;
+ }
+ }
+
+ return pa_sink_process_msg(o, code, data, offset, chunk);
+}
+
+static int sink_get_volume_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ int i;
+
+ pa_assert(u);
+
+ for (i = 0; i < s->sample_spec.channels; i++) {
+ s->volume.values[i] = u->volume;
+ }
+
+ return 0;
+}
+
+static int sink_set_volume_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ int rv;
+
+ pa_assert(u);
+
+ /* If we're muted, we fake it */
+ if (u->muted)
+ return 0;
+
+ pa_assert(s->sample_spec.channels > 0);
+
+ /* Avoid pointless volume sets */
+ if (u->volume == s->volume.values[0])
+ return 0;
+
+ rv = pa_raop_client_set_volume(u->raop, s->volume.values[0]);
+ if (0 == rv)
+ u->volume = s->volume.values[0];
+
+ return rv;
+}
+
+static int sink_get_mute_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+
+ pa_assert(u);
+
+ s->muted = u->muted;
+ return 0;
+}
+
+static int sink_set_mute_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ int rv;
+
+ pa_assert(u);
+
+ rv = pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume));
+ u->muted = s->muted;
+ return rv;
+}
+
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+ int write_type = 0;
+ pa_memchunk silence;
+ uint32_t silence_overhead = 0;
+ double silence_ratio = 0;
+
+ pa_assert(u);
+
+ pa_log_debug("Thread starting up");
+
+ pa_thread_mq_install(&u->thread_mq);
+ pa_rtpoll_install(u->rtpoll);
+
+ pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
+
+ /* Create a chunk of memory that is our encoded silence sample. */
+ pa_memchunk_reset(&silence);
+
+ for (;;) {
+ int ret;
+
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ if (u->sink->thread_info.rewind_requested)
+ pa_sink_process_rewind(u->sink, 0);
+
+ if (u->rtpoll_item) {
+ struct pollfd *pollfd;
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ /* Render some data and write it to the fifo */
+ if (/*PA_SINK_IS_OPENED(u->sink->thread_info.state) && */pollfd->revents) {
+ pa_usec_t usec;
+ int64_t n;
+ void *p;
+
+ if (!silence.memblock) {
+ pa_memchunk silence_tmp;
+
+ pa_memchunk_reset(&silence_tmp);
+ silence_tmp.memblock = pa_memblock_new(u->core->mempool, 4096);
+ silence_tmp.length = 4096;
+ p = pa_memblock_acquire(silence_tmp.memblock);
+ memset(p, 0, 4096);
+ pa_memblock_release(silence_tmp.memblock);
+ pa_raop_client_encode_sample(u->raop, &silence_tmp, &silence);
+ pa_assert(0 == silence_tmp.length);
+ silence_overhead = silence_tmp.length - 4096;
+ silence_ratio = silence_tmp.length / 4096;
+ pa_memblock_unref(silence_tmp.memblock);
+ }
+
+ for (;;) {
+ ssize_t l;
+
+ if (u->encoded_memchunk.length <= 0) {
+ if (u->encoded_memchunk.memblock)
+ pa_memblock_unref(u->encoded_memchunk.memblock);
+ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
+ size_t rl;
+
+ /* We render real data */
+ if (u->raw_memchunk.length <= 0) {
+ if (u->raw_memchunk.memblock)
+ pa_memblock_unref(u->raw_memchunk.memblock);
+ pa_memchunk_reset(&u->raw_memchunk);
+
+ /* Grab unencoded data */
+ pa_sink_render(u->sink, u->block_size, &u->raw_memchunk);
+ }
+ pa_assert(u->raw_memchunk.length > 0);
+
+ /* Encode it */
+ rl = u->raw_memchunk.length;
+ u->encoding_overhead += u->next_encoding_overhead;
+ pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk);
+ u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length));
+ u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length);
+ } else {
+ /* We render some silence into our memchunk */
+ memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk));
+ pa_memblock_ref(silence.memblock);
+
+ /* Calculate/store some values to be used with the smoother */
+ u->next_encoding_overhead = silence_overhead;
+ u->encoding_ratio = silence_ratio;
+ }
+ }
+ pa_assert(u->encoded_memchunk.length > 0);
+
+ p = pa_memblock_acquire(u->encoded_memchunk.memblock);
+ l = pa_write(u->fd, (uint8_t*) p + u->encoded_memchunk.index, u->encoded_memchunk.length, &write_type);
+ pa_memblock_release(u->encoded_memchunk.memblock);
+
+ pa_assert(l != 0);
+
+ if (l < 0) {
+
+ if (errno == EINTR)
+ continue;
+ else if (errno == EAGAIN) {
+
+ /* OK, we filled all socket buffers up
+ * now. */
+ goto filled_up;
+
+ } else {
+ pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+
+ } else {
+ u->offset += l;
+
+ u->encoded_memchunk.index += l;
+ u->encoded_memchunk.length -= l;
+
+ pollfd->revents = 0;
+
+ if (u->encoded_memchunk.length > 0) {
+ /* we've completely written the encoded data, so update our overhead */
+ u->encoding_overhead += u->next_encoding_overhead;
+
+ /* OK, we wrote less that we asked for,
+ * hence we can assume that the socket
+ * buffers are full now */
+ goto filled_up;
+ }
+ }
+ }
+
+ filled_up:
+
+ /* At this spot we know that the socket buffers are
+ * fully filled up. This is the best time to estimate
+ * the playback position of the server */
+
+ n = u->offset - u->encoding_overhead;
+
+#ifdef SIOCOUTQ
+ {
+ int l;
+ if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0)
+ n -= (l / u->encoding_ratio);
+ }
+#endif
+
+ usec = pa_bytes_to_usec(n, &u->sink->sample_spec);
+
+ if (usec > u->latency)
+ usec -= u->latency;
+ else
+ usec = 0;
+
+ pa_smoother_put(u->smoother, pa_rtclock_usec(), usec);
+ }
+
+ /* Hmm, nothing to do. Let's sleep */
+ pollfd->events = POLLOUT; /*PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;*/
+ }
+
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+ goto fail;
+
+ if (ret == 0)
+ goto finish;
+
+ if (u->rtpoll_item) {
+ struct pollfd* pollfd;
+
+ pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+ if (pollfd->revents & ~POLLOUT) {
+ if (u->sink->thread_info.state != PA_SINK_SUSPENDED) {
+ pa_log("FIFO shutdown.");
+ goto fail;
+ }
+
+ /* We expect this to happen on occasion if we are not sending data.
+ It's perfectly natural and normal and natural */
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+ u->rtpoll_item = NULL;
+ }
+ }
+ }
+
+fail:
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ if (silence.memblock)
+ pa_memblock_unref(silence.memblock);
+ pa_log_debug("Thread shutting down");
+}
+
+int pa__init(pa_module*m) {
+ struct userdata *u = NULL;
+ pa_sample_spec ss;
+ pa_modargs *ma = NULL;
+ const char *server;
+ pa_sink_new_data data;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("failed to parse module arguments");
+ goto fail;
+ }
+
+ ss = m->core->default_sample_spec;
+ if (pa_modargs_get_sample_spec(ma, &ss) < 0) {
+ pa_log("invalid sample format specification");
+ goto fail;
+ }
+
+ if ((/*ss.format != PA_SAMPLE_U8 &&*/ ss.format != PA_SAMPLE_S16NE) ||
+ (ss.channels > 2)) {
+ pa_log("sample type support is limited to mono/stereo and U8 or S16NE sample data");
+ goto fail;
+ }
+
+ u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ m->userdata = u;
+ u->fd = -1;
+ u->smoother = pa_smoother_new(PA_USEC_PER_SEC, PA_USEC_PER_SEC*2, TRUE, 10);
+ pa_memchunk_reset(&u->raw_memchunk);
+ pa_memchunk_reset(&u->encoded_memchunk);
+ u->offset = 0;
+ u->encoding_overhead = 0;
+ u->next_encoding_overhead = 0;
+ u->encoding_ratio = 1.0;
+
+ u->volume = roundf(0.7 * PA_VOLUME_NORM);
+ u->muted = FALSE;
+
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+ u->rtpoll_item = NULL;
+
+ /*u->format =
+ (ss.format == PA_SAMPLE_U8 ? ESD_BITS8 : ESD_BITS16) |
+ (ss.channels == 2 ? ESD_STEREO : ESD_MONO);*/
+ u->rate = ss.rate;
+ u->block_size = pa_usec_to_bytes(PA_USEC_PER_SEC/20, &ss);
+
+ u->read_data = u->write_data = NULL;
+ u->read_index = u->write_index = u->read_length = u->write_length = 0;
+
+ /*u->state = STATE_AUTH;*/
+ u->latency = 0;
+
+ if (!(server = pa_modargs_get_value(ma, "server", NULL))) {
+ pa_log("No server argument given.");
+ goto fail;
+ }
+
+ pa_sink_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
+ pa_sink_new_data_set_sample_spec(&data, &ss);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, server);
+ pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Airtunes sink '%s'", server);
+
+ u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_NETWORK);
+ pa_sink_new_data_done(&data);
+
+ if (!u->sink) {
+ pa_log("Failed to create sink.");
+ goto fail;
+ }
+
+ u->sink->parent.process_msg = sink_process_msg;
+ u->sink->userdata = u;
+ u->sink->get_volume = sink_get_volume_cb;
+ u->sink->set_volume = sink_set_volume_cb;
+ u->sink->get_mute = sink_get_mute_cb;
+ u->sink->set_mute = sink_set_mute_cb;
+ u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL;
+
+ pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+ pa_sink_set_rtpoll(u->sink, u->rtpoll);
+
+ if (!(u->raop = pa_raop_client_new(u->core, server))) {
+ pa_log("Failed to connect to server.");
+ goto fail;
+ }
+
+ pa_raop_client_set_callback(u->raop, on_connection, u);
+ pa_raop_client_set_closed_callback(u->raop, on_close, u);
+
+ if (!(u->thread = pa_thread_new(thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
+
+ pa_sink_put(u->sink);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->sink)
+ pa_sink_unlink(u->sink);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ }
+
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->sink)
+ pa_sink_unref(u->sink);
+
+ if (u->rtpoll_item)
+ pa_rtpoll_item_free(u->rtpoll_item);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ if (u->raw_memchunk.memblock)
+ pa_memblock_unref(u->raw_memchunk.memblock);
+
+ if (u->encoded_memchunk.memblock)
+ pa_memblock_unref(u->encoded_memchunk.memblock);
+
+ if (u->raop)
+ pa_raop_client_free(u->raop);
+
+ pa_xfree(u->read_data);
+ pa_xfree(u->write_data);
+
+ if (u->smoother)
+ pa_smoother_free(u->smoother);
+
+ if (u->fd >= 0)
+ pa_close(u->fd);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index 5b2be118..976a8ce5 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -274,6 +274,16 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
}
}
+/* Called from main context */
+static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
+ struct userdata *u;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert_se(u = i->userdata);
+
+ return u->sink != dest;
+}
+
int pa__init(pa_module*m) {
struct userdata *u;
pa_sample_spec ss;
@@ -386,6 +396,7 @@ int pa__init(pa_module*m) {
u->sink_input->detach = sink_input_detach_cb;
u->sink_input->kill = sink_input_kill_cb;
u->sink_input->state_change = sink_input_state_change_cb;
+ u->sink_input->may_move_to = sink_input_may_move_to_cb;
u->sink_input->userdata = u;
pa_sink_put(u->sink);
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index fe79291e..fdf69a20 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -134,7 +134,7 @@ static char *get_name(pa_proplist *p, const char *prefix) {
else if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_NAME)))
return pa_sprintf_malloc("%s-by-media-name:%s", prefix, r);
- return NULL;
+ return pa_sprintf_malloc("%s-fallback:%s", prefix, r);
}
static struct entry* read_entry(struct userdata *u, char *name) {
@@ -741,7 +741,7 @@ int pa__init(pa_module*m) {
if (!fname)
goto fail;
- if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT, 0600, NULL))) {
+ if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT|GDBM_NOLOCK, 0600, NULL))) {
pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
pa_xfree(fname);
goto fail;
diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index 6cc28ec5..8ab84e08 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -83,12 +83,12 @@ static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval
d->userdata->core->mainloop->time_restart(d->time_event, NULL);
- if (d->sink && pa_sink_used_by(d->sink) <= 0 && pa_sink_get_state(d->sink) != PA_SINK_SUSPENDED) {
+ if (d->sink && pa_sink_check_suspend(d->sink) <= 0 && pa_sink_get_state(d->sink) != PA_SINK_SUSPENDED) {
pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name);
pa_sink_suspend(d->sink, TRUE);
}
- if (d->source && pa_source_used_by(d->source) <= 0 && pa_source_get_state(d->source) != PA_SOURCE_SUSPENDED) {
+ if (d->source && pa_source_check_suspend(d->source) <= 0 && pa_source_get_state(d->source) != PA_SOURCE_SUSPENDED) {
pa_log_info("Source %s idle for too long, suspending ...", d->source->name);
pa_source_suspend(d->source, TRUE);
}
@@ -158,7 +158,7 @@ static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *s,
pa_sink_input_assert_ref(s);
pa_assert(u);
- if (pa_sink_used_by(s->sink) <= 0) {
+ if (pa_sink_check_suspend(s->sink) <= 0) {
struct device_info *d;
if ((d = pa_hashmap_get(u->device_infos, s->sink)))
restart(d);
@@ -172,7 +172,7 @@ static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_outpu
pa_source_output_assert_ref(s);
pa_assert(u);
- if (pa_source_used_by(s->source) <= 0) {
+ if (pa_source_check_suspend(s->source) <= 0) {
struct device_info *d;
if ((d = pa_hashmap_get(u->device_infos, s->source)))
restart(d);
@@ -191,7 +191,7 @@ static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input_move_h
if ((d = pa_hashmap_get(u->device_infos, data->destination)))
resume(d);
- if (pa_sink_used_by(data->sink_input->sink) <= 1)
+ if (pa_sink_check_suspend(data->sink_input->sink) <= 1)
if ((d = pa_hashmap_get(u->device_infos, data->sink_input->sink)))
restart(d);
@@ -208,7 +208,7 @@ static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output_
if ((d = pa_hashmap_get(u->device_infos, data->destination)))
resume(d);
- if (pa_source_used_by(data->source_output->source) <= 1)
+ if (pa_source_check_suspend(data->source_output->source) <= 1)
if ((d = pa_hashmap_get(u->device_infos, data->source_output->source)))
restart(d);
@@ -266,8 +266,8 @@ static pa_hook_result_t device_new_hook_cb(pa_core *c, pa_object *o, struct user
d->time_event = c->mainloop->time_new(c->mainloop, NULL, timeout_cb, d);
pa_hashmap_put(u->device_infos, o, d);
- if ((d->sink && pa_sink_used_by(d->sink) <= 0) ||
- (d->source && pa_source_used_by(d->source) <= 0))
+ if ((d->sink && pa_sink_check_suspend(d->sink) <= 0) ||
+ (d->source && pa_source_check_suspend(d->source) <= 0))
restart(d);
return PA_HOOK_OK;
@@ -313,7 +313,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
pa_sink *s = PA_SINK(o);
pa_sink_state_t state = pa_sink_get_state(s);
- if (pa_sink_used_by(s) <= 0) {
+ if (pa_sink_check_suspend(s) <= 0) {
if (PA_SINK_IS_OPENED(state))
restart(d);
@@ -324,7 +324,7 @@ static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, s
pa_source *s = PA_SOURCE(o);
pa_source_state_t state = pa_source_get_state(s);
- if (pa_source_used_by(s) <= 0) {
+ if (pa_source_check_suspend(s) <= 0) {
if (PA_SOURCE_IS_OPENED(state))
restart(d);
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index 4bbb11a5..a46d6e59 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -508,7 +508,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
switch (code) {
- case PA_SINK_MESSAGE_SET_STATE: {
+ case PA_SOURCE_MESSAGE_SET_STATE: {
int r;
if ((r = pa_source_process_msg(o, code, data, offset, chunk)) >= 0)
@@ -520,7 +520,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
case PA_SOURCE_MESSAGE_GET_LATENCY: {
pa_usec_t yr, yl, *usec = data;
- yl = pa_bytes_to_usec((uint64_t) u->counter, &PA_SINK(o)->sample_spec);
+ yl = pa_bytes_to_usec((uint64_t) u->counter, &PA_SOURCE(o)->sample_spec);
yr = pa_smoother_get(u->smoother, pa_rtclock_usec());
*usec = yr > yl ? yr - yl : 0;
diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c
index c8087abb..9a867cb5 100644
--- a/src/modules/module-zeroconf-discover.c
+++ b/src/modules/module-zeroconf-discover.c
@@ -286,7 +286,7 @@ static void browser_cb(
struct tunnel *t2;
if ((t2 = pa_hashmap_get(u->tunnels, t))) {
- pa_module_unload_by_index(u->core, t2->module_index, TRUE);
+ pa_module_unload_request_by_index(u->core, t2->module_index, TRUE);
pa_hashmap_remove(u->tunnels, t2);
tunnel_free(t2);
}
@@ -427,7 +427,7 @@ void pa__done(pa_module*m) {
struct tunnel *t;
while ((t = pa_hashmap_steal_first(u->tunnels))) {
- pa_module_unload_by_index(u->core, t->module_index, TRUE);
+ pa_module_unload_request_by_index(u->core, t->module_index, TRUE);
tunnel_free(t);
}
diff --git a/src/modules/raop/base64.c b/src/modules/raop/base64.c
new file mode 100644
index 00000000..8918def8
--- /dev/null
+++ b/src/modules/raop/base64.c
@@ -0,0 +1,126 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Colin Guthrie
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+/*
+ This file was originally inspired by a file developed by
+ Kungliga Tekniska H�gskolan
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include "base64.h"
+
+static const char base64_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int pos(char c)
+{
+ if (c >= 'A' && c <= 'Z') return c - 'A' + 0;
+ if (c >= 'a' && c <= 'z') return c - 'a' + 26;
+ if (c >= '0' && c <= '9') return c - '0' + 52;
+ if (c == '+') return 62;
+ if (c == '/') return 63;
+}
+
+int pa_base64_encode(const void *data, int size, char **str)
+{
+ char *s, *p;
+ int i;
+ int c;
+ const unsigned char *q;
+
+ p = s = pa_xnew(char, size * 4 / 3 + 4);
+ q = (const unsigned char *) data;
+ i = 0;
+ for (i = 0; i < size;) {
+ c = q[i++];
+ c *= 256;
+ if (i < size)
+ c += q[i];
+ i++;
+ c *= 256;
+ if (i < size)
+ c += q[i];
+ i++;
+ p[0] = base64_chars[(c & 0x00fc0000) >> 18];
+ p[1] = base64_chars[(c & 0x0003f000) >> 12];
+ p[2] = base64_chars[(c & 0x00000fc0) >> 6];
+ p[3] = base64_chars[(c & 0x0000003f) >> 0];
+ if (i > size)
+ p[3] = '=';
+ if (i > size + 1)
+ p[2] = '=';
+ p += 4;
+ }
+ *p = 0;
+ *str = s;
+ return strlen(s);
+}
+
+#define DECODE_ERROR 0xffffffff
+
+static unsigned int token_decode(const char *token)
+{
+ int i;
+ unsigned int val = 0;
+ int marker = 0;
+ if (strlen(token) < 4)
+ return DECODE_ERROR;
+ for (i = 0; i < 4; i++) {
+ val *= 64;
+ if (token[i] == '=')
+ marker++;
+ else if (marker > 0)
+ return DECODE_ERROR;
+ else
+ val += pos(token[i]);
+ }
+ if (marker > 2)
+ return DECODE_ERROR;
+ return (marker << 24) | val;
+}
+
+int pa_base64_decode(const char *str, void *data)
+{
+ const char *p;
+ unsigned char *q;
+
+ q = data;
+ for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
+ unsigned int val = token_decode(p);
+ unsigned int marker = (val >> 24) & 0xff;
+ if (val == DECODE_ERROR)
+ return -1;
+ *q++ = (val >> 16) & 0xff;
+ if (marker < 2)
+ *q++ = (val >> 8) & 0xff;
+ if (marker < 1)
+ *q++ = val & 0xff;
+ }
+ return q - (unsigned char *) data;
+}
diff --git a/src/modules/raop/base64.h b/src/modules/raop/base64.h
new file mode 100644
index 00000000..dac0e707
--- /dev/null
+++ b/src/modules/raop/base64.h
@@ -0,0 +1,34 @@
+#ifndef foobase64hfoo
+#define foobase64hfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Colin Guthrie
+ Copyright Kungliga Tekniska Høgskolan
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+/*
+ This file was originally inspired by a file developed by
+ Kungliga Tekniska Høgskolan
+*/
+
+int pa_base64_encode(const void *data, int size, char **str);
+int pa_base64_decode(const char *str, void *data);
+
+#endif
diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
new file mode 100644
index 00000000..4627545e
--- /dev/null
+++ b/src/modules/raop/raop_client.c
@@ -0,0 +1,561 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Colin Guthrie
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+/* TODO: Replace OpenSSL with NSS */
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/aes.h>
+#include <openssl/rsa.h>
+#include <openssl/engine.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/random.h>
+#include <pulsecore/poll.h>
+
+#include "raop_client.h"
+#include "rtsp_client.h"
+#include "base64.h"
+
+#define AES_CHUNKSIZE 16
+
+#define JACK_STATUS_DISCONNECTED 0
+#define JACK_STATUS_CONNECTED 1
+
+#define JACK_TYPE_ANALOG 0
+#define JACK_TYPE_DIGITAL 1
+
+#define VOLUME_DEF -30
+#define VOLUME_MIN -144
+#define VOLUME_MAX 0
+
+
+struct pa_raop_client {
+ pa_core *core;
+ char *host;
+ char *sid;
+ pa_rtsp_client *rtsp;
+
+ uint8_t jack_type;
+ uint8_t jack_status;
+
+ /* Encryption Related bits */
+ AES_KEY aes;
+ uint8_t aes_iv[AES_CHUNKSIZE]; /* initialization vector for aes-cbc */
+ uint8_t aes_nv[AES_CHUNKSIZE]; /* next vector for aes-cbc */
+ uint8_t aes_key[AES_CHUNKSIZE]; /* key for aes-cbc */
+
+ pa_socket_client *sc;
+ int fd;
+
+ uint16_t seq;
+ uint32_t rtptime;
+
+ pa_raop_client_cb_t callback;
+ void* userdata;
+ pa_raop_client_closed_cb_t closed_callback;
+ void* closed_userdata;
+};
+
+/**
+ * Function to write bits into a buffer.
+ * @param buffer Handle to the buffer. It will be incremented if new data requires it.
+ * @param bit_pos A pointer to a position buffer to keep track the current write location (0 for MSB, 7 for LSB)
+ * @param size A pointer to the byte size currently written. This allows the calling function to do simple buffer overflow checks
+ * @param data The data to write
+ * @param data_bit_len The number of bits from data to write
+ */
+static inline void bit_writer(uint8_t **buffer, uint8_t *bit_pos, int *size, uint8_t data, uint8_t data_bit_len) {
+ int bits_left, bit_overflow;
+ uint8_t bit_data;
+
+ if (!data_bit_len)
+ return;
+
+ /* If bit pos is zero, we will definatly use at least one bit from the current byte so size increments. */
+ if (!*bit_pos)
+ *size += 1;
+
+ /* Calc the number of bits left in the current byte of buffer */
+ bits_left = 7 - *bit_pos + 1;
+ /* Calc the overflow of bits in relation to how much space we have left... */
+ bit_overflow = bits_left - data_bit_len;
+ if (bit_overflow >= 0) {
+ /* We can fit the new data in our current byte */
+ /* As we write from MSB->LSB we need to left shift by the overflow amount */
+ bit_data = data << bit_overflow;
+ if (*bit_pos)
+ **buffer |= bit_data;
+ else
+ **buffer = bit_data;
+ /* If our data fits exactly into the current byte, we need to increment our pointer */
+ if (0 == bit_overflow) {
+ /* Do not increment size as it will be incremeneted on next call as bit_pos is zero */
+ *buffer += 1;
+ *bit_pos = 0;
+ } else {
+ *bit_pos += data_bit_len;
+ }
+ } else {
+ /* bit_overflow is negative, there for we will need a new byte from our buffer */
+ /* Firstly fill up what's left in the current byte */
+ bit_data = data >> -bit_overflow;
+ **buffer |= bit_data;
+ /* Increment our buffer pointer and size counter*/
+ *buffer += 1;
+ *size += 1;
+ **buffer = data << (8 + bit_overflow);
+ *bit_pos = -bit_overflow;
+ }
+}
+
+static int rsa_encrypt(uint8_t *text, int len, uint8_t *res) {
+ const char n[] =
+ "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC"
+ "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR"
+ "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB"
+ "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ"
+ "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh"
+ "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew==";
+ const char e[] = "AQAB";
+ uint8_t modules[256];
+ uint8_t exponent[8];
+ int size;
+ RSA *rsa;
+
+ rsa = RSA_new();
+ size = pa_base64_decode(n, modules);
+ rsa->n = BN_bin2bn(modules, size, NULL);
+ size = pa_base64_decode(e, exponent);
+ rsa->e = BN_bin2bn(exponent, size, NULL);
+
+ size = RSA_public_encrypt(len, text, res, rsa, RSA_PKCS1_OAEP_PADDING);
+ RSA_free(rsa);
+ return size;
+}
+
+static int aes_encrypt(pa_raop_client* c, uint8_t *data, int size)
+{
+ uint8_t *buf;
+ int i=0, j;
+
+ pa_assert(c);
+
+ memcpy(c->aes_nv, c->aes_iv, AES_CHUNKSIZE);
+ while (i+AES_CHUNKSIZE <= size) {
+ buf = data + i;
+ for (j=0; j<AES_CHUNKSIZE; ++j)
+ buf[j] ^= c->aes_nv[j];
+
+ AES_encrypt(buf, buf, &c->aes);
+ memcpy(c->aes_nv, buf, AES_CHUNKSIZE);
+ i += AES_CHUNKSIZE;
+ }
+ return i;
+}
+
+static inline void rtrimchar(char *str, char rc)
+{
+ char *sp = str + strlen(str) - 1;
+ while (sp >= str && *sp == rc) {
+ *sp = '\0';
+ sp -= 1;
+ }
+}
+
+static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
+ pa_raop_client *c = userdata;
+
+ pa_assert(sc);
+ pa_assert(c);
+ pa_assert(c->sc == sc);
+ pa_assert(c->fd < 0);
+ pa_assert(c->callback);
+
+ pa_socket_client_unref(c->sc);
+ c->sc = NULL;
+
+ if (!io) {
+ pa_log("Connection failed: %s", pa_cstrerror(errno));
+ return;
+ }
+
+ c->fd = pa_iochannel_get_send_fd(io);
+
+ pa_iochannel_set_noclose(io, TRUE);
+ pa_iochannel_socket_set_sndbuf(io, 1024);
+ pa_iochannel_free(io);
+
+ pa_make_tcp_socket_low_delay(c->fd);
+
+ pa_log_debug("Connection established");
+ c->callback(c->fd, c->userdata);
+}
+
+static void rtsp_cb(pa_rtsp_client *rtsp, pa_rtsp_state state, pa_headerlist* headers, void *userdata)
+{
+ pa_raop_client* c = userdata;
+ pa_assert(c);
+ pa_assert(rtsp);
+ pa_assert(rtsp == c->rtsp);
+
+ switch (state) {
+ case STATE_CONNECT: {
+ int i;
+ uint8_t rsakey[512];
+ char *key, *iv, *sac, *sdp;
+ uint16_t rand_data;
+ const char *ip;
+ char *url;
+
+ pa_log_debug("RAOP: CONNECTED");
+ ip = pa_rtsp_localip(c->rtsp);
+ /* First of all set the url properly */
+ url = pa_sprintf_malloc("rtsp://%s/%s", ip, c->sid);
+ pa_rtsp_set_url(c->rtsp, url);
+ pa_xfree(url);
+
+ /* Now encrypt our aes_public key to send to the device */
+ i = rsa_encrypt(c->aes_key, AES_CHUNKSIZE, rsakey);
+ pa_base64_encode(rsakey, i, &key);
+ rtrimchar(key, '=');
+ pa_base64_encode(c->aes_iv, AES_CHUNKSIZE, &iv);
+ rtrimchar(iv, '=');
+
+ pa_random(&rand_data, sizeof(rand_data));
+ pa_base64_encode(&rand_data, AES_CHUNKSIZE, &sac);
+ rtrimchar(sac, '=');
+ pa_rtsp_add_header(c->rtsp, "Apple-Challenge", sac);
+ sdp = pa_sprintf_malloc(
+ "v=0\r\n"
+ "o=iTunes %s 0 IN IP4 %s\r\n"
+ "s=iTunes\r\n"
+ "c=IN IP4 %s\r\n"
+ "t=0 0\r\n"
+ "m=audio 0 RTP/AVP 96\r\n"
+ "a=rtpmap:96 AppleLossless\r\n"
+ "a=fmtp:96 4096 0 16 40 10 14 2 255 0 0 44100\r\n"
+ "a=rsaaeskey:%s\r\n"
+ "a=aesiv:%s\r\n",
+ c->sid, ip, c->host, key, iv);
+ pa_rtsp_announce(c->rtsp, sdp);
+ pa_xfree(key);
+ pa_xfree(iv);
+ pa_xfree(sac);
+ pa_xfree(sdp);
+ break;
+ }
+
+ case STATE_ANNOUNCE:
+ pa_log_debug("RAOP: ANNOUNCED");
+ pa_rtsp_remove_header(c->rtsp, "Apple-Challenge");
+ pa_rtsp_setup(c->rtsp);
+ break;
+
+ case STATE_SETUP: {
+ char *aj = pa_xstrdup(pa_headerlist_gets(headers, "Audio-Jack-Status"));
+ pa_log_debug("RAOP: SETUP");
+ if (aj) {
+ char *token, *pc;
+ char delimiters[] = ";";
+ const char* token_state = NULL;
+ c->jack_type = JACK_TYPE_ANALOG;
+ c->jack_status = JACK_STATUS_DISCONNECTED;
+
+ while ((token = pa_split(aj, delimiters, &token_state))) {
+ if ((pc = strstr(token, "="))) {
+ *pc = 0;
+ if (!strcmp(token, "type") && !strcmp(pc+1, "digital")) {
+ c->jack_type = JACK_TYPE_DIGITAL;
+ }
+ } else {
+ if (!strcmp(token,"connected"))
+ c->jack_status = JACK_STATUS_CONNECTED;
+ }
+ pa_xfree(token);
+ }
+ pa_xfree(aj);
+ } else {
+ pa_log_warn("Audio Jack Status missing");
+ }
+ pa_rtsp_record(c->rtsp, &c->seq, &c->rtptime);
+ break;
+ }
+
+ case STATE_RECORD: {
+ uint32_t port = pa_rtsp_serverport(c->rtsp);
+ pa_log_debug("RAOP: RECORDED");
+
+ if (!(c->sc = pa_socket_client_new_string(c->core->mainloop, c->host, port))) {
+ pa_log("failed to connect to server '%s:%d'", c->host, port);
+ return;
+ }
+ pa_socket_client_set_callback(c->sc, on_connection, c);
+ break;
+ }
+
+ case STATE_FLUSH:
+ pa_log_debug("RAOP: FLUSHED");
+ break;
+
+ case STATE_TEARDOWN:
+ case STATE_SET_PARAMETER:
+ pa_log_debug("RAOP: SET_PARAMETER");
+ break;
+ case STATE_DISCONNECTED:
+ pa_assert(c->closed_callback);
+ pa_assert(c->rtsp);
+
+ pa_log_debug("RTSP control channel closed");
+ pa_rtsp_client_free(c->rtsp);
+ c->rtsp = NULL;
+ if (c->fd > 0) {
+ /* We do not close the fd, we leave it to the closed callback to do that */
+ c->fd = -1;
+ }
+ if (c->sc) {
+ pa_socket_client_unref(c->sc);
+ c->sc = NULL;
+ }
+ pa_xfree(c->sid);
+ c->sid = NULL;
+ c->closed_callback(c->closed_userdata);
+ break;
+ }
+}
+
+pa_raop_client* pa_raop_client_new(pa_core *core, const char* host)
+{
+ pa_raop_client* c = pa_xnew0(pa_raop_client, 1);
+
+ pa_assert(core);
+ pa_assert(host);
+
+ c->core = core;
+ c->fd = -1;
+ c->host = pa_xstrdup(host);
+
+ if (pa_raop_connect(c)) {
+ pa_raop_client_free(c);
+ return NULL;
+ }
+ return c;
+}
+
+
+void pa_raop_client_free(pa_raop_client* c)
+{
+ pa_assert(c);
+
+ if (c->rtsp)
+ pa_rtsp_client_free(c->rtsp);
+ pa_xfree(c->host);
+ pa_xfree(c);
+}
+
+
+int pa_raop_connect(pa_raop_client* c)
+{
+ char *sci;
+ struct {
+ uint32_t a;
+ uint32_t b;
+ uint32_t c;
+ } rand_data;
+
+ pa_assert(c);
+
+ if (c->rtsp) {
+ pa_log_debug("Connection already in progress");
+ return 0;
+ }
+
+ c->rtsp = pa_rtsp_client_new(c->core->mainloop, c->host, 5000, "iTunes/4.6 (Macintosh; U; PPC Mac OS X 10.3)");
+
+ /* Initialise the AES encryption system */
+ pa_random(c->aes_iv, sizeof(c->aes_iv));
+ pa_random(c->aes_key, sizeof(c->aes_key));
+ memcpy(c->aes_nv, c->aes_iv, sizeof(c->aes_nv));
+ AES_set_encrypt_key(c->aes_key, 128, &c->aes);
+
+ /* Generate random instance id */
+ pa_random(&rand_data, sizeof(rand_data));
+ c->sid = pa_sprintf_malloc("%u", rand_data.a);
+ sci = pa_sprintf_malloc("%08x%08x",rand_data.b, rand_data.c);
+ pa_rtsp_add_header(c->rtsp, "Client-Instance", sci);
+ pa_xfree(sci);
+ pa_rtsp_set_callback(c->rtsp, rtsp_cb, c);
+ return pa_rtsp_connect(c->rtsp);
+}
+
+
+int pa_raop_flush(pa_raop_client* c)
+{
+ pa_assert(c);
+
+ pa_rtsp_flush(c->rtsp, c->seq, c->rtptime);
+ return 0;
+}
+
+
+int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume)
+{
+ int rv;
+ double db;
+ char *param;
+
+ pa_assert(c);
+
+ db = pa_sw_volume_to_dB(volume);
+ if (db < VOLUME_MIN)
+ db = VOLUME_MIN;
+ else if (db > VOLUME_MAX)
+ db = VOLUME_MAX;
+
+ param = pa_sprintf_malloc("volume: %0.6f\r\n", db);
+
+ /* We just hit and hope, cannot wait for the callback */
+ rv = pa_rtsp_setparameter(c->rtsp, param);
+ pa_xfree(param);
+ return rv;
+}
+
+
+int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded)
+{
+ uint16_t len;
+ size_t bufmax;
+ uint8_t *bp, bpos;
+ uint8_t *ibp, *maxibp;
+ int size;
+ uint8_t *b, *p;
+ uint32_t bsize;
+ size_t length;
+ static uint8_t header[] = {
+ 0x24, 0x00, 0x00, 0x00,
+ 0xF0, 0xFF, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+ int header_size = sizeof(header);
+
+ pa_assert(c);
+ pa_assert(c->fd > 0);
+ pa_assert(raw);
+ pa_assert(raw->memblock);
+ pa_assert(raw->length > 0);
+ pa_assert(encoded);
+
+ /* We have to send 4 byte chunks */
+ bsize = (int)(raw->length / 4);
+ length = bsize * 4;
+
+ /* Leave 16 bytes extra to allow for the ALAC header which is about 55 bits */
+ bufmax = length + header_size + 16;
+ pa_memchunk_reset(encoded);
+ encoded->memblock = pa_memblock_new(c->core->mempool, bufmax);
+ b = pa_memblock_acquire(encoded->memblock);
+ memcpy(b, header, header_size);
+
+ /* Now write the actual samples */
+ bp = b + header_size;
+ size = bpos = 0;
+ bit_writer(&bp,&bpos,&size,1,3); /* channel=1, stereo */
+ bit_writer(&bp,&bpos,&size,0,4); /* unknown */
+ bit_writer(&bp,&bpos,&size,0,8); /* unknown */
+ bit_writer(&bp,&bpos,&size,0,4); /* unknown */
+ bit_writer(&bp,&bpos,&size,1,1); /* hassize */
+ bit_writer(&bp,&bpos,&size,0,2); /* unused */
+ bit_writer(&bp,&bpos,&size,1,1); /* is-not-compressed */
+
+ /* size of data, integer, big endian */
+ bit_writer(&bp,&bpos,&size,(bsize>>24)&0xff,8);
+ bit_writer(&bp,&bpos,&size,(bsize>>16)&0xff,8);
+ bit_writer(&bp,&bpos,&size,(bsize>>8)&0xff,8);
+ bit_writer(&bp,&bpos,&size,(bsize)&0xff,8);
+
+ ibp = p = pa_memblock_acquire(raw->memblock);
+ maxibp = p + raw->length - 4;
+ while (ibp <= maxibp) {
+ /* Byte swap stereo data */
+ bit_writer(&bp,&bpos,&size,*(ibp+1),8);
+ bit_writer(&bp,&bpos,&size,*(ibp+0),8);
+ bit_writer(&bp,&bpos,&size,*(ibp+3),8);
+ bit_writer(&bp,&bpos,&size,*(ibp+2),8);
+ ibp += 4;
+ raw->index += 4;
+ raw->length -= 4;
+ }
+ pa_memblock_release(raw->memblock);
+ encoded->length = header_size + size;
+
+ /* store the lenght (endian swapped: make this better) */
+ len = size + header_size - 4;
+ *(b + 2) = len >> 8;
+ *(b + 3) = len & 0xff;
+
+ /* encrypt our data */
+ aes_encrypt(c, (b + header_size), size);
+
+ /* We're done with the chunk */
+ pa_memblock_release(encoded->memblock);
+
+ return 0;
+}
+
+
+void pa_raop_client_set_callback(pa_raop_client* c, pa_raop_client_cb_t callback, void *userdata)
+{
+ pa_assert(c);
+
+ c->callback = callback;
+ c->userdata = userdata;
+}
+
+void pa_raop_client_set_closed_callback(pa_raop_client* c, pa_raop_client_closed_cb_t callback, void *userdata)
+{
+ pa_assert(c);
+
+ c->closed_callback = callback;
+ c->closed_userdata = userdata;
+}
diff --git a/src/modules/raop/raop_client.h b/src/modules/raop/raop_client.h
new file mode 100644
index 00000000..ec3136a7
--- /dev/null
+++ b/src/modules/raop/raop_client.h
@@ -0,0 +1,46 @@
+#ifndef fooraopclientfoo
+#define fooraopclientfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Colin Guthrie
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulse/mainloop-api.h>
+#include <pulsecore/iochannel.h>
+#include <pulsecore/core.h>
+
+typedef struct pa_raop_client pa_raop_client;
+
+pa_raop_client* pa_raop_client_new(pa_core *core, const char* host);
+void pa_raop_client_free(pa_raop_client* c);
+
+int pa_raop_connect(pa_raop_client* c);
+int pa_raop_flush(pa_raop_client* c);
+
+int pa_raop_client_set_volume(pa_raop_client* c, pa_volume_t volume);
+int pa_raop_client_encode_sample(pa_raop_client* c, pa_memchunk* raw, pa_memchunk* encoded);
+
+typedef void (*pa_raop_client_cb_t)(int fd, void *userdata);
+void pa_raop_client_set_callback(pa_raop_client* c, pa_raop_client_cb_t callback, void *userdata);
+
+typedef void (*pa_raop_client_closed_cb_t)(void *userdata);
+void pa_raop_client_set_closed_callback(pa_raop_client* c, pa_raop_client_closed_cb_t callback, void *userdata);
+
+#endif
diff --git a/src/modules/rtp/headerlist.c b/src/modules/rtp/headerlist.c
new file mode 100644
index 00000000..0fef835b
--- /dev/null
+++ b/src/modules/rtp/headerlist.c
@@ -0,0 +1,186 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Colin Guthrie
+ Copyright 2007 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/hashmap.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/core-util.h>
+
+#include "headerlist.h"
+
+struct header {
+ char *key;
+ void *value;
+ size_t nbytes;
+};
+
+#define MAKE_HASHMAP(p) ((pa_hashmap*) (p))
+#define MAKE_HEADERLIST(p) ((pa_headerlist*) (p))
+
+static void header_free(struct header *hdr) {
+ pa_assert(hdr);
+
+ pa_xfree(hdr->key);
+ pa_xfree(hdr->value);
+ pa_xfree(hdr);
+}
+
+pa_headerlist* pa_headerlist_new(void) {
+ return MAKE_HEADERLIST(pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func));
+}
+
+void pa_headerlist_free(pa_headerlist* p) {
+ struct header *hdr;
+
+ while ((hdr = pa_hashmap_steal_first(MAKE_HASHMAP(p))))
+ header_free(hdr);
+
+ pa_hashmap_free(MAKE_HASHMAP(p), NULL, NULL);
+}
+
+int pa_headerlist_puts(pa_headerlist *p, const char *key, const char *value) {
+ struct header *hdr;
+ pa_bool_t add = FALSE;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+ hdr = pa_xnew(struct header, 1);
+ hdr->key = pa_xstrdup(key);
+ add = TRUE;
+ } else
+ pa_xfree(hdr->value);
+
+ hdr->value = pa_xstrdup(value);
+ hdr->nbytes = strlen(value)+1;
+
+ if (add)
+ pa_hashmap_put(MAKE_HASHMAP(p), hdr->key, hdr);
+
+ return 0;
+}
+
+int pa_headerlist_putsappend(pa_headerlist *p, const char *key, const char *value) {
+ struct header *hdr;
+ pa_bool_t add = FALSE;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key))) {
+ hdr = pa_xnew(struct header, 1);
+ hdr->key = pa_xstrdup(key);
+ hdr->value = pa_xstrdup(value);
+ add = TRUE;
+ } else {
+ void *newval = pa_sprintf_malloc("%s%s", (char*)hdr->value, value);
+ pa_xfree(hdr->value);
+ hdr->value = newval;
+ }
+ hdr->nbytes = strlen(hdr->value)+1;
+
+ if (add)
+ pa_hashmap_put(MAKE_HASHMAP(p), hdr->key, hdr);
+
+ return 0;
+}
+
+const char *pa_headerlist_gets(pa_headerlist *p, const char *key) {
+ struct header *hdr;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!(hdr = pa_hashmap_get(MAKE_HASHMAP(p), key)))
+ return NULL;
+
+ if (hdr->nbytes <= 0)
+ return NULL;
+
+ if (((char*) hdr->value)[hdr->nbytes-1] != 0)
+ return NULL;
+
+ if (strlen((char*) hdr->value) != hdr->nbytes-1)
+ return NULL;
+
+ return (char*) hdr->value;
+}
+
+int pa_headerlist_remove(pa_headerlist *p, const char *key) {
+ struct header *hdr;
+
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!(hdr = pa_hashmap_remove(MAKE_HASHMAP(p), key)))
+ return -1;
+
+ header_free(hdr);
+ return 0;
+}
+
+const char *pa_headerlist_iterate(pa_headerlist *p, void **state) {
+ struct header *hdr;
+
+ if (!(hdr = pa_hashmap_iterate(MAKE_HASHMAP(p), state, NULL)))
+ return NULL;
+
+ return hdr->key;
+}
+
+char *pa_headerlist_to_string(pa_headerlist *p) {
+ const char *key;
+ void *state = NULL;
+ pa_strbuf *buf;
+
+ pa_assert(p);
+
+ buf = pa_strbuf_new();
+
+ while ((key = pa_headerlist_iterate(p, &state))) {
+
+ const char *v;
+
+ if ((v = pa_headerlist_gets(p, key)))
+ pa_strbuf_printf(buf, "%s: %s\r\n", key, v);
+ }
+
+ return pa_strbuf_tostring_free(buf);
+}
+
+int pa_headerlist_contains(pa_headerlist *p, const char *key) {
+ pa_assert(p);
+ pa_assert(key);
+
+ if (!(pa_hashmap_get(MAKE_HASHMAP(p), key)))
+ return 0;
+
+ return 1;
+}
diff --git a/src/modules/rtp/headerlist.h b/src/modules/rtp/headerlist.h
new file mode 100644
index 00000000..4b9c6433
--- /dev/null
+++ b/src/modules/rtp/headerlist.h
@@ -0,0 +1,46 @@
+#ifndef foopulseheaderlisthfoo
+#define foopulseheaderlisthfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Colin Guthrie
+ Copyright 2007 Lennart Poettering
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <pulsecore/macro.h>
+
+typedef struct pa_headerlist pa_headerlist;
+
+pa_headerlist* pa_headerlist_new(void);
+void pa_headerlist_free(pa_headerlist* p);
+
+int pa_headerlist_puts(pa_headerlist *p, const char *key, const char *value);
+int pa_headerlist_putsappend(pa_headerlist *p, const char *key, const char *value);
+
+const char *pa_headerlist_gets(pa_headerlist *p, const char *key);
+
+int pa_headerlist_remove(pa_headerlist *p, const char *key);
+
+const char *pa_headerlist_iterate(pa_headerlist *p, void **state);
+
+char *pa_headerlist_to_string(pa_headerlist *p);
+
+int pa_headerlist_contains(pa_headerlist *p, const char *key);
+
+#endif
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index 280067a5..9c0f07f1 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -296,6 +296,11 @@ int pa__init(pa_module*m) {
pa_log("IP_MULTICAST_TTL failed: %s", pa_cstrerror(errno));
goto fail;
}
+
+ if (setsockopt(sap_fd, IPPROTO_IP, IP_MULTICAST_TTL, &_ttl, sizeof(_ttl)) < 0) {
+ pa_log("IP_MULTICAST_TTL (sap) failed: %s", pa_cstrerror(errno));
+ goto fail;
+ }
}
/* If the socket queue is full, let's drop packets */
@@ -316,7 +321,7 @@ int pa__init(pa_module*m) {
pa_source_output_new_data_set_sample_spec(&data, &ss);
pa_source_output_new_data_set_channel_map(&data, &cm);
- o = pa_source_output_new(m->core, &data, 0);
+ o = pa_source_output_new(m->core, &data, PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND);
pa_source_output_new_data_done(&data);
if (!o) {
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
new file mode 100644
index 00000000..9eb3d964
--- /dev/null
+++ b/src/modules/rtp/rtsp_client.c
@@ -0,0 +1,542 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Colin Guthrie
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/socket-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/strbuf.h>
+#include <pulsecore/poll.h>
+#include <pulsecore/ioline.h>
+
+#include "rtsp_client.h"
+
+struct pa_rtsp_client {
+ pa_mainloop_api *mainloop;
+ char *hostname;
+ uint16_t port;
+
+ pa_socket_client *sc;
+ pa_iochannel *io;
+ pa_ioline *ioline;
+
+ pa_rtsp_cb_t callback;
+
+ void *userdata;
+ const char *useragent;
+
+ pa_rtsp_state state;
+ uint8_t waiting;
+
+ pa_headerlist* headers;
+ char *last_header;
+ pa_strbuf *header_buffer;
+ pa_headerlist* response_headers;
+
+ char *localip;
+ char *url;
+ uint16_t rtp_port;
+ uint32_t cseq;
+ char *session;
+ char *transport;
+};
+
+pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char* hostname, uint16_t port, const char* useragent) {
+ pa_rtsp_client *c;
+
+ pa_assert(mainloop);
+ pa_assert(hostname);
+ pa_assert(port > 0);
+
+ c = pa_xnew0(pa_rtsp_client, 1);
+ c->mainloop = mainloop;
+ c->hostname = pa_xstrdup(hostname);
+ c->port = port;
+ c->headers = pa_headerlist_new();
+
+ if (useragent)
+ c->useragent = useragent;
+ else
+ c->useragent = "PulseAudio RTSP Client";
+
+ return c;
+}
+
+
+void pa_rtsp_client_free(pa_rtsp_client* c) {
+ if (c) {
+ if (c->sc)
+ pa_socket_client_unref(c->sc);
+ if (c->ioline)
+ pa_ioline_close(c->ioline);
+ else if (c->io)
+ pa_iochannel_free(c->io);
+
+ pa_xfree(c->hostname);
+ pa_xfree(c->url);
+ pa_xfree(c->localip);
+ pa_xfree(c->session);
+ pa_xfree(c->transport);
+ pa_xfree(c->last_header);
+ if (c->header_buffer)
+ pa_strbuf_free(c->header_buffer);
+ if (c->response_headers)
+ pa_headerlist_free(c->response_headers);
+ pa_headerlist_free(c->headers);
+ }
+ pa_xfree(c);
+}
+
+
+static void headers_read(pa_rtsp_client *c) {
+ char* token;
+ char delimiters[] = ";";
+
+ pa_assert(c);
+ pa_assert(c->response_headers);
+ pa_assert(c->callback);
+
+ /* Deal with a SETUP response */
+ if (STATE_SETUP == c->state) {
+ const char* token_state = NULL;
+ const char* pc = NULL;
+ c->session = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Session"));
+ c->transport = pa_xstrdup(pa_headerlist_gets(c->response_headers, "Transport"));
+
+ if (!c->session || !c->transport) {
+ pa_headerlist_free(c->response_headers);
+ c->response_headers = NULL;
+ pa_log("Invalid SETUP response.");
+ return;
+ }
+
+ /* Now parse out the server port component of the response. */
+ while ((token = pa_split(c->transport, delimiters, &token_state))) {
+ if ((pc = strstr(token, "="))) {
+ if (0 == strncmp(token, "server_port", 11)) {
+ pa_atou(pc+1, (uint32_t*)(&c->rtp_port));
+ pa_xfree(token);
+ break;
+ }
+ }
+ pa_xfree(token);
+ }
+ if (0 == c->rtp_port) {
+ /* Error no server_port in response */
+ pa_headerlist_free(c->response_headers);
+ c->response_headers = NULL;
+ pa_log("Invalid SETUP response (no port number).");
+ return;
+ }
+ }
+
+ /* Call our callback */
+ c->callback(c, c->state, c->response_headers, c->userdata);
+
+ pa_headerlist_free(c->response_headers);
+ c->response_headers = NULL;
+}
+
+
+static void line_callback(pa_ioline *line, const char *s, void *userdata) {
+ char *delimpos;
+ char *s2, *s2p;
+
+ pa_rtsp_client *c = userdata;
+ pa_assert(line);
+ pa_assert(c);
+ pa_assert(c->callback);
+
+ if (!s) {
+ /* Keep the ioline/iochannel open as they will be freed automatically */
+ c->ioline = NULL;
+ c->io = NULL;
+ c->callback(c, STATE_DISCONNECTED, NULL, c->userdata);
+ return;
+ }
+
+ s2 = pa_xstrdup(s);
+ /* Trim trailing carriage returns */
+ s2p = s2 + strlen(s2) - 1;
+ while (s2p >= s2 && '\r' == *s2p) {
+ *s2p = '\0';
+ s2p -= 1;
+ }
+ if (c->waiting && 0 == strcmp("RTSP/1.0 200 OK", s2)) {
+ c->waiting = 0;
+ pa_assert(!c->response_headers);
+ c->response_headers = pa_headerlist_new();
+ goto exit;
+ }
+ if (c->waiting) {
+ pa_log_warn("Unexpected response: %s", s2);
+ goto exit;;
+ }
+ if (!strlen(s2)) {
+ /* End of headers */
+ /* We will have a header left from our looping itteration, so add it in :) */
+ if (c->last_header) {
+ /* This is not a continuation header so let's dump it into our proplist */
+ pa_headerlist_puts(c->response_headers, c->last_header, pa_strbuf_tostring_free(c->header_buffer));
+ pa_xfree(c->last_header);
+ c->last_header = NULL;
+ c->header_buffer= NULL;
+ }
+
+ pa_log_debug("Full response received. Dispatching");
+ headers_read(c);
+ c->waiting = 1;
+ goto exit;
+ }
+
+ /* Read and parse a header (we know it's not empty) */
+ /* TODO: Move header reading into the headerlist. */
+
+ /* If the first character is a space, it's a continuation header */
+ if (c->last_header && ' ' == s2[0]) {
+ pa_assert(c->header_buffer);
+
+ /* Add this line to the buffer (sans the space. */
+ pa_strbuf_puts(c->header_buffer, &(s2[1]));
+ goto exit;
+ }
+
+ if (c->last_header) {
+ /* This is not a continuation header so let's dump the full
+ header/value into our proplist */
+ pa_headerlist_puts(c->response_headers, c->last_header, pa_strbuf_tostring_free(c->header_buffer));
+ pa_xfree(c->last_header);
+ c->last_header = NULL;
+ c->header_buffer = NULL;
+ }
+
+ delimpos = strstr(s2, ":");
+ if (!delimpos) {
+ pa_log_warn("Unexpected response when expecting header: %s", s);
+ goto exit;
+ }
+
+ pa_assert(!c->header_buffer);
+ pa_assert(!c->last_header);
+
+ c->header_buffer = pa_strbuf_new();
+ if (strlen(delimpos) > 1) {
+ /* Cut our line off so we can copy the header name out */
+ *delimpos++ = '\0';
+
+ /* Trim the front of any spaces */
+ while (' ' == *delimpos)
+ ++delimpos;
+
+ pa_strbuf_puts(c->header_buffer, delimpos);
+ } else {
+ /* Cut our line off so we can copy the header name out */
+ *delimpos = '\0';
+ }
+
+ /* Save the header name */
+ c->last_header = pa_xstrdup(s2);
+ exit:
+ pa_xfree(s2);
+}
+
+
+static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
+ pa_rtsp_client *c = userdata;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+ } sa;
+ socklen_t sa_len = sizeof(sa);
+
+ pa_assert(sc);
+ pa_assert(c);
+ pa_assert(STATE_CONNECT == c->state);
+ pa_assert(c->sc == sc);
+ pa_socket_client_unref(c->sc);
+ c->sc = NULL;
+
+ if (!io) {
+ pa_log("Connection failed: %s", pa_cstrerror(errno));
+ return;
+ }
+ pa_assert(!c->io);
+ c->io = io;
+
+ c->ioline = pa_ioline_new(io);
+ pa_ioline_set_callback(c->ioline, line_callback, c);
+
+ /* Get the local IP address for use externally */
+ if (0 == getsockname(pa_iochannel_get_recv_fd(io), &sa.sa, &sa_len)) {
+ char buf[INET6_ADDRSTRLEN];
+ const char *res = NULL;
+
+ if (AF_INET == sa.sa.sa_family) {
+ if ((res = inet_ntop(sa.sa.sa_family, &sa.in.sin_addr, buf, sizeof(buf)))) {
+ c->localip = pa_xstrdup(res);
+ }
+ } else if (AF_INET6 == sa.sa.sa_family) {
+ if ((res = inet_ntop(AF_INET6, &sa.in6.sin6_addr, buf, sizeof(buf)))) {
+ c->localip = pa_sprintf_malloc("[%s]", res);
+ }
+ }
+ }
+ pa_log_debug("Established RTSP connection from local ip %s", c->localip);
+
+ if (c->callback)
+ c->callback(c, c->state, NULL, c->userdata);
+}
+
+int pa_rtsp_connect(pa_rtsp_client *c) {
+ pa_assert(c);
+ pa_assert(!c->sc);
+
+ pa_xfree(c->session);
+ c->session = NULL;
+
+ if (!(c->sc = pa_socket_client_new_string(c->mainloop, c->hostname, c->port))) {
+ pa_log("failed to connect to server '%s:%d'", c->hostname, c->port);
+ return -1;
+ }
+
+ pa_socket_client_set_callback(c->sc, on_connection, c);
+ c->waiting = 1;
+ c->state = STATE_CONNECT;
+ return 0;
+}
+
+void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata) {
+ pa_assert(c);
+
+ c->callback = callback;
+ c->userdata = userdata;
+}
+
+void pa_rtsp_disconnect(pa_rtsp_client *c) {
+ pa_assert(c);
+
+ if (c->io)
+ pa_iochannel_free(c->io);
+ c->io = NULL;
+}
+
+
+const char* pa_rtsp_localip(pa_rtsp_client* c) {
+ pa_assert(c);
+
+ return c->localip;
+}
+
+uint32_t pa_rtsp_serverport(pa_rtsp_client* c) {
+ pa_assert(c);
+
+ return c->rtp_port;
+}
+
+void pa_rtsp_set_url(pa_rtsp_client* c, const char* url) {
+ pa_assert(c);
+
+ c->url = pa_xstrdup(url);
+}
+
+void pa_rtsp_add_header(pa_rtsp_client *c, const char* key, const char* value)
+{
+ pa_assert(c);
+ pa_assert(key);
+ pa_assert(value);
+
+ pa_headerlist_puts(c->headers, key, value);
+}
+
+void pa_rtsp_remove_header(pa_rtsp_client *c, const char* key)
+{
+ pa_assert(c);
+ pa_assert(key);
+
+ pa_headerlist_remove(c->headers, key);
+}
+
+static int rtsp_exec(pa_rtsp_client* c, const char* cmd,
+ const char* content_type, const char* content,
+ int expect_response,
+ pa_headerlist* headers) {
+ pa_strbuf* buf;
+ char* hdrs;
+ ssize_t l;
+
+ pa_assert(c);
+ pa_assert(c->url);
+
+ if (!cmd)
+ return -1;
+
+ pa_log_debug("Sending command: %s", cmd);
+
+ buf = pa_strbuf_new();
+ pa_strbuf_printf(buf, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, c->url, ++c->cseq);
+ if (c->session)
+ pa_strbuf_printf(buf, "Session: %s\r\n", c->session);
+
+ /* Add the headers */
+ if (headers) {
+ hdrs = pa_headerlist_to_string(headers);
+ pa_strbuf_puts(buf, hdrs);
+ pa_xfree(hdrs);
+ }
+
+ if (content_type && content) {
+ pa_strbuf_printf(buf, "Content-Type: %s\r\nContent-Length: %d\r\n",
+ content_type, (int)strlen(content));
+ }
+
+ pa_strbuf_printf(buf, "User-Agent: %s\r\n", c->useragent);
+
+ if (c->headers) {
+ hdrs = pa_headerlist_to_string(c->headers);
+ pa_strbuf_puts(buf, hdrs);
+ pa_xfree(hdrs);
+ }
+
+ pa_strbuf_puts(buf, "\r\n");
+
+ if (content_type && content) {
+ pa_strbuf_puts(buf, content);
+ }
+
+ /* Our packet is created... now we can send it :) */
+ hdrs = pa_strbuf_tostring_free(buf);
+ /*pa_log_debug("Submitting request:");
+ pa_log_debug(hdrs);*/
+ l = pa_iochannel_write(c->io, hdrs, strlen(hdrs));
+ pa_xfree(hdrs);
+
+ return 0;
+}
+
+
+int pa_rtsp_announce(pa_rtsp_client *c, const char* sdp) {
+ pa_assert(c);
+ if (!sdp)
+ return -1;
+
+ c->state = STATE_ANNOUNCE;
+ return rtsp_exec(c, "ANNOUNCE", "application/sdp", sdp, 1, NULL);
+}
+
+
+int pa_rtsp_setup(pa_rtsp_client* c) {
+ pa_headerlist* headers;
+ int rv;
+
+ pa_assert(c);
+
+ headers = pa_headerlist_new();
+ pa_headerlist_puts(headers, "Transport", "RTP/AVP/TCP;unicast;interleaved=0-1;mode=record");
+
+ c->state = STATE_SETUP;
+ rv = rtsp_exec(c, "SETUP", NULL, NULL, 1, headers);
+ pa_headerlist_free(headers);
+ return rv;
+}
+
+
+int pa_rtsp_record(pa_rtsp_client* c, uint16_t* seq, uint32_t* rtptime) {
+ pa_headerlist* headers;
+ int rv;
+ char *info;
+
+ pa_assert(c);
+ if (!c->session) {
+ /* No seesion in progres */
+ return -1;
+ }
+
+ /* Todo: Generate these values randomly as per spec */
+ *seq = *rtptime = 0;
+
+ headers = pa_headerlist_new();
+ pa_headerlist_puts(headers, "Range", "npt=0-");
+ info = pa_sprintf_malloc("seq=%u;rtptime=%u", *seq, *rtptime);
+ pa_headerlist_puts(headers, "RTP-Info", info);
+ pa_xfree(info);
+
+ c->state = STATE_RECORD;
+ rv = rtsp_exec(c, "RECORD", NULL, NULL, 1, headers);
+ pa_headerlist_free(headers);
+ return rv;
+}
+
+
+int pa_rtsp_teardown(pa_rtsp_client *c) {
+ pa_assert(c);
+
+ c->state = STATE_TEARDOWN;
+ return rtsp_exec(c, "TEARDOWN", NULL, NULL, 0, NULL);
+}
+
+
+int pa_rtsp_setparameter(pa_rtsp_client *c, const char* param) {
+ pa_assert(c);
+ if (!param)
+ return -1;
+
+ c->state = STATE_SET_PARAMETER;
+ return rtsp_exec(c, "SET_PARAMETER", "text/parameters", param, 1, NULL);
+}
+
+
+int pa_rtsp_flush(pa_rtsp_client *c, uint16_t seq, uint32_t rtptime) {
+ pa_headerlist* headers;
+ int rv;
+ char *info;
+
+ pa_assert(c);
+
+ headers = pa_headerlist_new();
+ info = pa_sprintf_malloc("seq=%u;rtptime=%u", seq, rtptime);
+ pa_headerlist_puts(headers, "RTP-Info", info);
+ pa_xfree(info);
+
+ c->state = STATE_FLUSH;
+ rv = rtsp_exec(c, "FLUSH", NULL, NULL, 1, headers);
+ pa_headerlist_free(headers);
+ return rv;
+}
diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h
new file mode 100644
index 00000000..88fb3839
--- /dev/null
+++ b/src/modules/rtp/rtsp_client.h
@@ -0,0 +1,73 @@
+#ifndef foortspclienthfoo
+#define foortspclienthfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2008 Colin Guthrie
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#include <inttypes.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netdb.h>
+
+#include <pulsecore/memblockq.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/socket-client.h>
+#include <pulse/mainloop-api.h>
+
+#include "headerlist.h"
+
+typedef struct pa_rtsp_client pa_rtsp_client;
+typedef enum {
+ STATE_CONNECT,
+ STATE_ANNOUNCE,
+ STATE_SETUP,
+ STATE_RECORD,
+ STATE_FLUSH,
+ STATE_TEARDOWN,
+ STATE_SET_PARAMETER,
+ STATE_DISCONNECTED
+} pa_rtsp_state;
+typedef void (*pa_rtsp_cb_t)(pa_rtsp_client *c, pa_rtsp_state state, pa_headerlist* hl, void *userdata);
+
+pa_rtsp_client* pa_rtsp_client_new(pa_mainloop_api *mainloop, const char* hostname, uint16_t port, const char* useragent);
+void pa_rtsp_client_free(pa_rtsp_client* c);
+
+int pa_rtsp_connect(pa_rtsp_client* c);
+void pa_rtsp_set_callback(pa_rtsp_client *c, pa_rtsp_cb_t callback, void *userdata);
+
+void pa_rtsp_disconnect(pa_rtsp_client* c);
+
+const char* pa_rtsp_localip(pa_rtsp_client* c);
+uint32_t pa_rtsp_serverport(pa_rtsp_client* c);
+void pa_rtsp_set_url(pa_rtsp_client* c, const char* url);
+void pa_rtsp_add_header(pa_rtsp_client *c, const char* key, const char* value);
+void pa_rtsp_remove_header(pa_rtsp_client *c, const char* key);
+
+int pa_rtsp_announce(pa_rtsp_client* c, const char* sdp);
+
+int pa_rtsp_setup(pa_rtsp_client* c);
+int pa_rtsp_record(pa_rtsp_client* c, uint16_t* seq, uint32_t* rtptime);
+int pa_rtsp_teardown(pa_rtsp_client* c);
+
+int pa_rtsp_setparameter(pa_rtsp_client* c, const char* param);
+int pa_rtsp_flush(pa_rtsp_client* c, uint16_t seq, uint32_t rtptime);
+
+#endif
diff --git a/src/pulse/def.h b/src/pulse/def.h
index 66d9aff8..ace56574 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -194,7 +194,10 @@ typedef enum pa_stream_flags {
/**< Find peaks instead of resampling. \since 0.9.11 */
PA_STREAM_START_MUTED = 0x1000U,
- /**< Create in muted state. \since 0.9.11 */
+ /**< Create in muted state. If neither PA_STREAM_START_UNMUTED nor
+ * PA_STREAM_START_MUTED it is left to the server to decide
+ * whether to create the stream in muted or in unmuted
+ * state. \since 0.9.11 */
PA_STREAM_ADJUST_LATENCY = 0x2000U,
/**< Try to adjust the latency of the sink/source based on the
@@ -203,7 +206,7 @@ typedef enum pa_stream_flags {
* specified at the same time as PA_STREAM_EARLY_REQUESTS. \since
* 0.9.11 */
- PA_STREAM_EARLY_REQUESTS = 0x4000U
+ PA_STREAM_EARLY_REQUESTS = 0x4000U,
/**< Enable compatibility mode for legacy clients that rely on a
* "classic" hardware device fragment-style playback model. If
* this option is set, the minreq value of the buffer metrics gets
@@ -220,6 +223,17 @@ typedef enum pa_stream_flags {
* not be specified at the same time as
* PA_STREAM_ADJUST_LATENCY. \since 0.9.12 */
+ PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND = 0x8000U,
+ /**< If set this stream won't be taken into account when we it is
+ * checked whether the device this stream is connected to should
+ * auto-suspend. \ since 0.9.14 */
+
+ PA_STREAM_START_UNMUTED = 0x10000U
+ /**< Create in unmuted state. If neither PA_STREAM_START_UNMUTED
+ * nor PA_STREAM_START_MUTED it is left to the server to decide
+ * whether to create the stream in muted or in unmuted
+ * state. \since 0.9.14 */
+
} pa_stream_flags_t;
/** \cond fulldocs */
@@ -243,6 +257,8 @@ typedef enum pa_stream_flags {
#define PA_STREAM_START_MUTED PA_STREAM_START_MUTED
#define PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
#define PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
+#define PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
+#define PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
/** \endcond */
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 00aa1cf9..c0ae4ac2 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -887,7 +887,9 @@ static int create_stream(
PA_STREAM_PEAK_DETECT|
PA_STREAM_START_MUTED|
PA_STREAM_ADJUST_LATENCY|
- PA_STREAM_EARLY_REQUESTS)), PA_ERR_INVALID);
+ PA_STREAM_EARLY_REQUESTS|
+ PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND|
+ PA_STREAM_START_UNMUTED)), PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, s->context->version >= 12 || !(flags & PA_STREAM_VARIABLE_RATE), PA_ERR_NOTSUPPORTED);
PA_CHECK_VALIDITY(s->context, s->context->version >= 13 || !(flags & PA_STREAM_PEAK_DETECT), PA_ERR_NOTSUPPORTED);
@@ -1007,6 +1009,14 @@ static int create_stream(
pa_tagstruct_put_boolean(t, flags & PA_STREAM_EARLY_REQUESTS);
}
+ if (s->context->version >= 15) {
+
+ if (s->direction == PA_STREAM_PLAYBACK)
+ pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_MUTED|PA_STREAM_START_UNMUTED));
+
+ pa_tagstruct_put_boolean(t, flags & PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND);
+ }
+
pa_pstream_send_tagstruct(s->context->pstream, t);
pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 1624165d..b5ff98db 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -117,6 +117,10 @@ static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa
static int pa_cli_command_suspend_sink(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_suspend_source(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
/* A method table for all available commands */
@@ -167,6 +171,10 @@ static const struct command commands[] = {
{ "suspend-sink", pa_cli_command_suspend_sink, "Suspend sink (args: index|name, bool)", 3},
{ "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3},
{ "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2},
+ { "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2},
+ { "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2},
+ { "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2},
+ { "set-log-backtrace", pa_cli_command_log_backtrace, "Show bakctrace in log messages (args: frames)", 2},
{ NULL, NULL, NULL, 0 }
};
@@ -1203,6 +1211,102 @@ static int pa_cli_command_suspend(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, p
return 0;
}
+static int pa_cli_command_log_level(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *m;
+ uint32_t level;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(m = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a log level (0..4).\n");
+ return -1;
+ }
+
+ if (pa_atou(m, &level) < 0 || level >= PA_LOG_LEVEL_MAX) {
+ pa_strbuf_puts(buf, "Failed to parse log level.\n");
+ return -1;
+ }
+
+ pa_log_set_maximal_level(level);
+
+ return 0;
+}
+
+static int pa_cli_command_log_meta(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *m;
+ pa_bool_t b;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(m = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a boolean.\n");
+ return -1;
+ }
+
+ if ((b = pa_parse_boolean(m)) < 0) {
+ pa_strbuf_puts(buf, "Failed to parse log meta switch.\n");
+ return -1;
+ }
+
+ pa_log_set_show_meta(b);
+
+ return 0;
+}
+
+static int pa_cli_command_log_time(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *m;
+ pa_bool_t b;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(m = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a boolean.\n");
+ return -1;
+ }
+
+ if ((b = pa_parse_boolean(m)) < 0) {
+ pa_strbuf_puts(buf, "Failed to parse log meta switch.\n");
+ return -1;
+ }
+
+ pa_log_set_show_time(b);
+
+ return 0;
+}
+
+static int pa_cli_command_log_backtrace(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *m;
+ uint32_t nframes;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(m = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a backtrace level.\n");
+ return -1;
+ }
+
+ if (pa_atou(m, &nframes) < 0 || nframes >= 1000) {
+ pa_strbuf_puts(buf, "Failed to parse backtrace level.\n");
+ return -1;
+ }
+
+ pa_log_set_show_backtrace(nframes);
+
+ return 0;
+}
+
static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m;
pa_sink *sink;
diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c
index 58ceab91..ef6d6bb6 100644
--- a/src/pulsecore/conf-parser.c
+++ b/src/pulsecore/conf-parser.c
@@ -166,6 +166,24 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue,
return 0;
}
+int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
+ unsigned *u = data;
+ uint32_t k;
+
+ pa_assert(filename);
+ pa_assert(lvalue);
+ pa_assert(rvalue);
+ pa_assert(data);
+
+ if (pa_atou(rvalue, &k) < 0) {
+ pa_log("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
+ return -1;
+ }
+
+ *u = (unsigned) k;
+ return 0;
+}
+
int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata) {
size_t *i = data;
uint32_t k;
diff --git a/src/pulsecore/conf-parser.h b/src/pulsecore/conf-parser.h
index a5174fce..48a0fd26 100644
--- a/src/pulsecore/conf-parser.h
+++ b/src/pulsecore/conf-parser.h
@@ -41,6 +41,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
/* Generic parsers for integers, size_t, booleans and strings */
int pa_config_parse_int(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
+int pa_config_parse_unsigned(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_size(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_bool(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_string(const char *filename, unsigned line, const char *lvalue, const char *rvalue, void *data, void *userdata);
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 39559082..f796fb93 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -49,6 +49,7 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SINK_UNLINK_POST,
PA_CORE_HOOK_SINK_STATE_CHANGED,
PA_CORE_HOOK_SINK_PROPLIST_CHANGED,
+ PA_CORE_HOOK_SINK_SET_VOLUME,
PA_CORE_HOOK_SOURCE_NEW,
PA_CORE_HOOK_SOURCE_FIXATE,
PA_CORE_HOOK_SOURCE_PUT,
@@ -65,6 +66,7 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_SINK_INPUT_MOVE_POST,
PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
+ PA_CORE_HOOK_SINK_INPUT_SET_VOLUME,
PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index b1de6966..adf2f112 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -30,6 +30,10 @@
#include <string.h>
#include <errno.h>
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
@@ -49,11 +53,15 @@
#define ENV_LOGLEVEL "PULSE_LOG"
#define ENV_LOGMETA "PULSE_LOG_META"
#define ENV_LOGTIME "PULSE_LOG_TIME"
+#define ENV_LOGBACKTRACE "PULSE_LOG_BACKTRACE"
static char *log_ident = NULL, *log_ident_local = NULL;
static pa_log_target_t log_target = PA_LOG_STDERR;
static pa_log_func_t user_log_func = NULL;
static pa_log_level_t maximal_level = PA_LOG_ERROR;
+static unsigned show_backtrace = 0;
+static pa_bool_t show_meta = FALSE;
+static pa_bool_t show_time = FALSE;
#ifdef HAVE_SYSLOG_H
static const int level_to_syslog[] = {
@@ -105,6 +113,74 @@ void pa_log_set_target(pa_log_target_t t, pa_log_func_t func) {
user_log_func = func;
}
+void pa_log_set_show_meta(pa_bool_t b) {
+ show_meta = b;
+}
+
+void pa_log_set_show_time(pa_bool_t b) {
+ show_time = b;
+}
+
+void pa_log_set_show_backtrace(unsigned nlevels) {
+ show_backtrace = nlevels;
+}
+
+#ifdef HAVE_EXECINFO_H
+
+static char* get_backtrace(unsigned show_nframes) {
+ void* trace[32];
+ int n_frames;
+ char **symbols, *e, *r;
+ unsigned j, n;
+ size_t a;
+
+ if (show_nframes <= 0)
+ return NULL;
+
+ n_frames = backtrace(trace, PA_ELEMENTSOF(trace));
+
+ if (n_frames <= 0)
+ return NULL;
+
+ symbols = backtrace_symbols(trace, n_frames);
+
+ if (!symbols)
+ return NULL;
+
+ n = PA_MIN((unsigned) n_frames, show_nframes);
+
+ a = 4;
+
+ for (j = 0; j < n; j++) {
+ if (j > 0)
+ a += 2;
+ a += strlen(symbols[j]);
+ }
+
+ r = pa_xnew(char, a);
+
+ strcpy(r, " (");
+ e = r + 2;
+
+ for (j = 0; j < n; j++) {
+ if (j > 0) {
+ strcpy(e, "<<");
+ e += 2;
+ }
+
+ strcpy(e, symbols[j]);
+ e += strlen(symbols[j]);
+ }
+
+ strcpy(e, ")");
+
+ free(symbols);
+
+ return r;
+}
+
+#endif
+
void pa_log_levelv_meta(
pa_log_level_t level,
const char*file,
@@ -116,32 +192,43 @@ void pa_log_levelv_meta(
const char *e;
char *t, *n;
int saved_errno = errno;
+ char *bt = NULL;
+ pa_log_level_t ml;
+#ifdef HAVE_EXECINFO_H
+ unsigned show_bt;
+#endif
/* We don't use dynamic memory allocation here to minimize the hit
* in RT threads */
- char text[1024], location[128], timestamp[32];
+ char text[4096], location[128], timestamp[32];
pa_assert(level < PA_LOG_LEVEL_MAX);
pa_assert(format);
- if ((e = getenv(ENV_LOGLEVEL)))
- maximal_level = atoi(e);
+ ml = maximal_level;
+
+ if (PA_UNLIKELY((e = getenv(ENV_LOGLEVEL)))) {
+ pa_log_level_t eml = (pa_log_level_t) atoi(e);
- if (level > maximal_level) {
+ if (eml > ml)
+ ml = eml;
+ }
+
+ if (PA_LIKELY(level > ml)) {
errno = saved_errno;
return;
}
pa_vsnprintf(text, sizeof(text), format, ap);
- if (getenv(ENV_LOGMETA) && file && line > 0 && func)
+ if ((show_meta || getenv(ENV_LOGMETA)) && file && line > 0 && func)
pa_snprintf(location, sizeof(location), "[%s:%i %s()] ", file, line, func);
else if (file)
pa_snprintf(location, sizeof(location), "%s: ", pa_path_get_filename(file));
else
location[0] = 0;
- if (getenv(ENV_LOGTIME)) {
+ if (show_time || getenv(ENV_LOGTIME)) {
static pa_usec_t start, last;
pa_usec_t u, a, r;
@@ -168,6 +255,19 @@ void pa_log_levelv_meta(
} else
timestamp[0] = 0;
+#ifdef HAVE_EXECINFO_H
+ show_bt = show_backtrace;
+
+ if ((e = getenv(ENV_LOGBACKTRACE))) {
+ unsigned ebt = (unsigned) atoi(e);
+
+ if (ebt > show_bt)
+ show_bt = ebt;
+ }
+
+ bt = get_backtrace(show_bt);
+#endif
+
if (!pa_utf8_valid(text))
pa_log_level(level, __FILE__": invalid UTF-8 string following below:");
@@ -182,19 +282,22 @@ void pa_log_levelv_meta(
switch (log_target) {
case PA_LOG_STDERR: {
- const char *prefix = "", *suffix = "";
+ const char *prefix = "", *suffix = "", *grey = "";
char *local_t;
#ifndef OS_IS_WIN32
/* Yes indeed. Useless, but fun! */
if (isatty(STDERR_FILENO)) {
- if (level <= PA_LOG_ERROR) {
+ if (level <= PA_LOG_ERROR)
prefix = "\x1B[1;31m";
- suffix = "\x1B[0m";
- } else if (level <= PA_LOG_WARN) {
+ else if (level <= PA_LOG_WARN)
prefix = "\x1B[1m";
+
+ if (bt)
+ grey = "\x1B[2m";
+
+ if (grey[0] || prefix[0])
suffix = "\x1B[0m";
- }
}
#endif
@@ -202,9 +305,9 @@ void pa_log_levelv_meta(
* minimize the hit in RT threads */
local_t = pa_utf8_to_locale(t);
if (!local_t)
- fprintf(stderr, "%s%c: %s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, suffix);
+ fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, t, grey, pa_strempty(bt), suffix);
else {
- fprintf(stderr, "%s%c: %s%s%s%s\n", timestamp, level_to_char[level], location, prefix, local_t, suffix);
+ fprintf(stderr, "%s%c: %s%s%s%s%s%s\n", timestamp, level_to_char[level], location, prefix, local_t, grey, pa_strempty(bt), suffix);
pa_xfree(local_t);
}
@@ -219,9 +322,9 @@ void pa_log_levelv_meta(
local_t = pa_utf8_to_locale(t);
if (!local_t)
- syslog(level_to_syslog[level], "%s%s%s", timestamp, location, t);
+ syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, t, pa_strempty(bt));
else {
- syslog(level_to_syslog[level], "%s%s%s", timestamp, location, local_t);
+ syslog(level_to_syslog[level], "%s%s%s%s", timestamp, location, local_t, pa_strempty(bt));
pa_xfree(local_t);
}
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
index 633227f3..3d66e903 100644
--- a/src/pulsecore/log.h
+++ b/src/pulsecore/log.h
@@ -25,6 +25,8 @@
#include <stdarg.h>
#include <stdlib.h>
+
+#include <pulsecore/macro.h>
#include <pulse/gccmacro.h>
/* A simple logging subsystem */
@@ -54,8 +56,11 @@ typedef void (*pa_log_func_t)(pa_log_level_t t, const char*s);
/* Set another log target. If t is PA_LOG_USER you may specify a function that is called every log string */
void pa_log_set_target(pa_log_target_t t, pa_log_func_t func);
-/* Minimal log level */
+/* Maximal log level */
void pa_log_set_maximal_level(pa_log_level_t l);
+void pa_log_set_show_meta(pa_bool_t b);
+void pa_log_set_show_time(pa_bool_t b);
+void pa_log_set_show_backtrace(unsigned nlevels);
void pa_log_level_meta(
pa_log_level_t level,
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index 39e9b587..f9ce949a 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -30,7 +30,6 @@
#include <stdio.h>
#include <stdlib.h>
-#include <pulsecore/log.h>
#include <pulse/gccmacro.h>
#ifndef PACKAGE
@@ -40,7 +39,7 @@
#ifndef PA_LIKELY
#ifdef __GNUC__
#define PA_LIKELY(x) (__builtin_expect(!!(x),1))
-#define PA_UNLIKELY(x) (__builtin_expect((x),0))
+#define PA_UNLIKELY(x) (__builtin_expect(!!(x),0))
#else
#define PA_LIKELY(x) (x)
#define PA_UNLIKELY(x) (x)
@@ -221,4 +220,13 @@ typedef int pa_bool_t;
#endif
+#if defined(__i386__) || defined(__x86_64__)
+#define PA_DEBUG_TRAP __asm__("int $3")
+#else
+#define PA_DEBUG_TRAP raise(SIGTRAP)
+#endif
+
+/* We include this at the very last place */
+#include <pulsecore/log.h>
+
#endif
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index 9b17cb91..56ed2c5d 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -280,6 +280,16 @@ void pa_module_unload_request(pa_module *m, pa_bool_t force) {
m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1);
}
+void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force) {
+ pa_module *m;
+ pa_assert(c);
+
+ if (!(m = pa_idxset_get_by_index(c->modules, idx)))
+ return;
+
+ pa_module_unload_request(m, force);
+}
+
void pa_module_set_used(pa_module*m, int used) {
pa_assert(m);
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index 365ab67e..661b2dd6 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -52,14 +52,16 @@ struct pa_module {
};
pa_module* pa_module_load(pa_core *c, const char *name, const char*argument);
+
void pa_module_unload(pa_core *c, pa_module *m, pa_bool_t force);
void pa_module_unload_by_index(pa_core *c, uint32_t idx, pa_bool_t force);
+void pa_module_unload_request(pa_module *m, pa_bool_t force);
+void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force);
+
void pa_module_unload_all(pa_core *c);
void pa_module_unload_unused(pa_core *c);
-void pa_module_unload_request(pa_module *m, pa_bool_t force);
-
void pa_module_set_used(pa_module*m, int used);
#define PA_MODULE_AUTHOR(s) \
diff --git a/src/pulsecore/once.c b/src/pulsecore/once.c
index 989741dc..3d4543cb 100644
--- a/src/pulsecore/once.c
+++ b/src/pulsecore/once.c
@@ -28,13 +28,13 @@
#include "once.h"
-int pa_once_begin(pa_once *control) {
+pa_bool_t pa_once_begin(pa_once *control) {
pa_mutex *m;
pa_assert(control);
if (pa_atomic_load(&control->done))
- return 0;
+ return FALSE;
pa_atomic_inc(&control->ref);
@@ -50,15 +50,17 @@ int pa_once_begin(pa_once *control) {
* wait until it is unlocked */
pa_mutex_lock(m);
+ pa_assert(pa_atomic_load(&control->done));
+
pa_once_end(control);
- return 0;
+ return FALSE;
}
pa_assert_se(m = pa_mutex_new(FALSE, FALSE));
pa_mutex_lock(m);
if (pa_atomic_ptr_cmpxchg(&control->mutex, NULL, m))
- return 1;
+ return TRUE;
pa_mutex_unlock(m);
pa_mutex_free(m);
@@ -91,4 +93,3 @@ void pa_run_once(pa_once *control, pa_once_func_t func) {
pa_once_end(control);
}
}
-
diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h
index 576d40fa..c0191ef0 100644
--- a/src/pulsecore/once.h
+++ b/src/pulsecore/once.h
@@ -38,7 +38,7 @@ typedef struct pa_once {
}
/* Not to be called directly, use the macros defined below instead */
-int pa_once_begin(pa_once *o);
+pa_bool_t pa_once_begin(pa_once *o);
void pa_once_end(pa_once *o);
#define PA_ONCE_BEGIN \
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
index ce8ef19b..bf9ba983 100644
--- a/src/pulsecore/pid.c
+++ b/src/pulsecore/pid.c
@@ -171,14 +171,14 @@ static int proc_name_ours(pid_t pid, const char *procname) {
good = pa_startswith(stored, expected);
pa_xfree(expected);
-#if !defined(__OPTIMIZE__)
+/*#if !defined(__OPTIMIZE__)*/
if (!good) {
/* libtool likes to rename our binary names ... */
expected = pa_sprintf_malloc("%lu (lt-%s)", (unsigned long) pid, procname);
good = pa_startswith(stored, expected);
pa_xfree(expected);
}
-#endif
+/*#endif*/
return !!good;
}
@@ -211,6 +211,7 @@ int pa_pid_file_create(const char *procname) {
if ((pid = read_pid(fn, fd)) == (pid_t) -1)
pa_log_warn("Corrupt PID file, overwriting.");
else if (pid > 0) {
+ int ours = 1;
#ifdef OS_IS_WIN32
if ((process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)) != NULL) {
@@ -218,11 +219,13 @@ int pa_pid_file_create(const char *procname) {
#else
if (kill(pid, 0) >= 0 || errno != ESRCH) {
#endif
- int ours = 1;
if (procname)
- if ((ours = proc_name_ours(pid, procname)) < 0)
+ if ((ours = proc_name_ours(pid, procname)) < 0) {
+ pa_log_warn("Could not check to see if pid %lu is a pulseaudio process. "
+ "Asssuming it is and the daemon is already running.", (unsigned long) pid);
goto fail;
+ }
if (ours) {
pa_log("Daemon already running.");
diff --git a/src/pulsecore/proplist-util.c b/src/pulsecore/proplist-util.c
index 4d505f57..35c9985a 100644
--- a/src/pulsecore/proplist-util.c
+++ b/src/pulsecore/proplist-util.c
@@ -44,27 +44,34 @@ void pa_init_proplist(pa_proplist *p) {
pa_assert(p);
- for (e = environ; *e; e++) {
+ if (environ) {
- if (pa_startswith(*e, "PULSE_PROP_")) {
- size_t kl = strcspn(*e+11, "=");
- char *k;
+ /* Some applications seem to reset environ to NULL for various
+ * reasons, hence we need to check for this explicitly. See
+ * rhbz #473080 */
- if ((*e)[11+kl] != '=')
- continue;
+ for (e = environ; *e; e++) {
- if (!pa_utf8_valid(*e+11+kl+1))
- continue;
+ if (pa_startswith(*e, "PULSE_PROP_")) {
+ size_t kl = strcspn(*e+11, "=");
+ char *k;
- k = pa_xstrndup(*e+11, kl);
+ if ((*e)[11+kl] != '=')
+ continue;
- if (pa_proplist_contains(p, k)) {
+ if (!pa_utf8_valid(*e+11+kl+1))
+ continue;
+
+ k = pa_xstrndup(*e+11, kl);
+
+ if (pa_proplist_contains(p, k)) {
+ pa_xfree(k);
+ continue;
+ }
+
+ pa_proplist_sets(p, k, *e+11+kl+1);
pa_xfree(k);
- continue;
}
-
- pa_proplist_sets(p, k, *e+11+kl+1);
- pa_xfree(k);
}
}
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 778aab57..56e86cb4 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -959,6 +959,7 @@ static playback_stream* playback_stream_new(
uint32_t *minreq,
pa_cvolume *volume,
pa_bool_t muted,
+ pa_bool_t muted_set,
uint32_t syncid,
uint32_t *missing,
pa_sink_input_flags_t flags,
@@ -1013,7 +1014,8 @@ static playback_stream* playback_stream_new(
pa_sink_input_new_data_set_channel_map(&data, map);
if (volume)
pa_sink_input_new_data_set_volume(&data, volume);
- pa_sink_input_new_data_set_muted(&data, muted);
+ if (muted_set)
+ pa_sink_input_new_data_set_muted(&data, muted);
data.sync_base = ssync ? ssync->sink_input : NULL;
sink_input = pa_sink_input_new(c->protocol->core, &data, flags);
@@ -1688,7 +1690,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
variable_rate = FALSE,
muted = FALSE,
adjust_latency = FALSE,
- early_requests = FALSE;
+ early_requests = FALSE,
+ dont_inhibit_auto_suspend = FALSE,
+ muted_set = FALSE;
pa_sink_input_flags_t flags = 0;
pa_proplist *p;
@@ -1769,6 +1773,16 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
}
}
+ if (c->version >= 15) {
+
+ if (pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
+ pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
pa_proplist_free(p);
@@ -1800,9 +1814,14 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
(fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) |
(fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) |
(no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
- (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0);
+ (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) |
+ (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0);
+
+ /* Only since protocol version 15 there's a seperate muted_set
+ * flag. For older versions we synthesize it here */
+ muted_set = muted_set || muted;
- s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, syncid, &missing, flags, p, adjust_latency, early_requests);
+ s = playback_stream_new(c, sink, &ss, &map, &maxlength, &tlength, &prebuf, &minreq, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests);
pa_proplist_free(p);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
@@ -1923,7 +1942,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
variable_rate = FALSE,
adjust_latency = FALSE,
peak_detect = FALSE,
- early_requests = FALSE;
+ early_requests = FALSE,
+ dont_inhibit_auto_suspend = FALSE;
pa_source_output_flags_t flags = 0;
pa_proplist *p;
uint32_t direct_on_input_idx = PA_INVALID_INDEX;
@@ -1995,6 +2015,15 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
}
}
+ if (c->version >= 15) {
+
+ if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0) {
+ protocol_error(c);
+ pa_proplist_free(p);
+ return;
+ }
+ }
+
if (!pa_tagstruct_eof(t)) {
protocol_error(c);
pa_proplist_free(p);
@@ -2035,7 +2064,8 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
(fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) |
(fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
(no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
- (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0);
+ (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) |
+ (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0);
s = record_stream_new(c, source, &ss, &map, peak_detect, &maxlength, &fragment_size, flags, p, adjust_latency, direct_on_input, early_requests);
pa_proplist_free(p);
@@ -2722,7 +2752,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t,
pa_tagstruct_putu32(t, s->sink->index);
pa_tagstruct_put_sample_spec(t, &fixed_ss);
pa_tagstruct_put_channel_map(t, &s->channel_map);
- pa_tagstruct_put_cvolume(t, &s->volume);
+ pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s));
pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
pa_tagstruct_put_usec(t, sink_latency);
pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index b2d512c8..f0515ebe 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -510,6 +510,52 @@ static pa_bool_t on_lfe(pa_channel_position_t p) {
p == PA_CHANNEL_POSITION_LFE;
}
+static pa_bool_t on_front(pa_channel_position_t p) {
+ return
+ p == PA_CHANNEL_POSITION_FRONT_LEFT ||
+ p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
+ p == PA_CHANNEL_POSITION_FRONT_CENTER ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER ||
+ p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
+ p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
+}
+
+static pa_bool_t on_rear(pa_channel_position_t p) {
+ return
+ p == PA_CHANNEL_POSITION_REAR_LEFT ||
+ p == PA_CHANNEL_POSITION_REAR_RIGHT ||
+ p == PA_CHANNEL_POSITION_REAR_CENTER ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_LEFT ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+}
+
+static pa_bool_t on_side(pa_channel_position_t p) {
+ return
+ p == PA_CHANNEL_POSITION_SIDE_LEFT ||
+ p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
+ p == PA_CHANNEL_POSITION_TOP_CENTER;
+}
+
+enum {
+ ON_FRONT,
+ ON_REAR,
+ ON_SIDE,
+ ON_OTHER
+};
+
+static int front_rear_side(pa_channel_position_t p) {
+ if (on_front(p))
+ return ON_FRONT;
+ if (on_rear(p))
+ return ON_REAR;
+ if (on_side(p))
+ return ON_SIDE;
+ return ON_OTHER;
+}
+
static void calc_map_table(pa_resampler *r) {
unsigned oc, ic;
pa_bool_t ic_connected[PA_CHANNELS_MAX];
@@ -601,7 +647,9 @@ static void calc_map_table(pa_resampler *r) {
* D:left, all D:right, all D:center channels, gain is
* 0.375. The current (as result of 1..6) factors
* should be multiplied by 0.75. (Alt. suggestion: 0.25
- * vs. 0.5)
+ * vs. 0.5) If C-front is only mixed into
+ * L-front/R-front if available, otherwise into all L/R
+ * channels. Similarly for C-rear.
*
* S: and D: shall relate to the source resp. destination channels.
*
@@ -629,6 +677,8 @@ static void calc_map_table(pa_resampler *r) {
if (!oc_connected && remix) {
/* OK, we shall remix */
+ /* Try to find matching input ports for this output port */
+
if (on_left(b)) {
unsigned n = 0;
@@ -830,17 +880,54 @@ static void calc_map_table(pa_resampler *r) {
}
if (!mixed_in) {
+ unsigned ncenter[PA_CHANNELS_MAX];
+ pa_bool_t found_frs[PA_CHANNELS_MAX];
+
+ memset(ncenter, 0, sizeof(ncenter));
+ memset(found_frs, 0, sizeof(found_frs));
/* Hmm, as it appears there was no center channel we
could mix our center channel in. In this case, mix
it into left and right. Using .375 and 0.75 as
factors. */
+ for (ic = 0; ic < r->i_ss.channels; ic++) {
+
+ if (ic_connected[ic])
+ continue;
+
+ if (!on_center(r->i_cm.map[ic]))
+ continue;
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
+ continue;
+
+ if (front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) {
+ found_frs[ic] = TRUE;
+ break;
+ }
+ }
+
+ for (oc = 0; oc < r->o_ss.channels; oc++) {
+
+ if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
+ continue;
+
+ if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc]))
+ ncenter[oc]++;
+ }
+ }
+
for (oc = 0; oc < r->o_ss.channels; oc++) {
if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc]))
continue;
+ if (ncenter[oc] <= 0)
+ continue;
+
for (ic = 0; ic < r->i_ss.channels; ic++) {
if (ic_connected[ic]) {
@@ -848,8 +935,11 @@ static void calc_map_table(pa_resampler *r) {
continue;
}
- if (on_center(r->i_cm.map[ic]))
- r->map_table[oc][ic] = .375f / (float) ic_unconnected_center;
+ if (!on_center(r->i_cm.map[ic]))
+ continue;
+
+ if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc]))
+ r->map_table[oc][ic] = .375f / (float) ncenter[oc];
}
}
}
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index 7b9ac7bc..9f0f795c 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -777,7 +777,7 @@ size_t pa_frame_align(size_t l, const pa_sample_spec *ss) {
return (l/fs) * fs;
}
-int pa_frame_aligned(size_t l, const pa_sample_spec *ss) {
+pa_bool_t pa_frame_aligned(size_t l, const pa_sample_spec *ss) {
size_t fs;
pa_assert(ss);
diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h
index 06ecb724..2fe2c81d 100644
--- a/src/pulsecore/sample-util.h
+++ b/src/pulsecore/sample-util.h
@@ -71,7 +71,7 @@ void pa_volume_memchunk(
size_t pa_frame_align(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
-int pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
+pa_bool_t pa_frame_aligned(size_t l, const pa_sample_spec *ss) PA_GCC_PURE;
void pa_interleave(const void *src[], unsigned channels, void *dst, size_t ss, unsigned n);
void pa_deinterleave(const void *src, void *dst[], unsigned channels, size_t ss, unsigned n);
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
index b2997575..c59d247c 100644
--- a/src/pulsecore/shm.c
+++ b/src/pulsecore/shm.c
@@ -73,10 +73,10 @@
struct shm_marker PA_GCC_PACKED {
pa_atomic_t marker; /* 0xbeefcafe */
pa_atomic_t pid;
- uint64_t *_reserverd1;
- uint64_t *_reserverd2;
- uint64_t *_reserverd3;
- uint64_t *_reserverd4;
+ uint64_t _reserved1;
+ uint64_t _reserved2;
+ uint64_t _reserved3;
+ uint64_t _reserved4;
};
static char *segment_name(char *fn, size_t l, unsigned id) {
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 4f70347f..d25cd797 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -75,7 +75,7 @@ void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cv
pa_assert(data);
if ((data->volume_is_set = !!volume))
- data->volume = *volume;
+ data->volume = data->virtual_volume = *volume;
}
void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute) {
@@ -108,6 +108,7 @@ static void reset_callbacks(pa_sink_input *i) {
i->kill = NULL;
i->get_latency = NULL;
i->state_change = NULL;
+ i->may_move_to = NULL;
}
/* Called from main context */
@@ -119,6 +120,7 @@ pa_sink_input* pa_sink_input_new(
pa_sink_input *i;
pa_resampler *resampler = NULL;
char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+ pa_channel_map original_cm;
pa_assert(core);
pa_assert(data);
@@ -141,20 +143,25 @@ pa_sink_input* pa_sink_input_new(
pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));
if (!data->channel_map_is_set) {
- if (data->sink->channel_map.channels == data->sample_spec.channels)
+ if (pa_channel_map_compatible(&data->sink->channel_map, &data->sample_spec))
data->channel_map = data->sink->channel_map;
else
- pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+ pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
}
pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
- pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+ pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec));
- if (!data->volume_is_set)
+ if (!data->volume_is_set) {
pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+ pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
+ }
pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
- pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels);
+ pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
+
+ pa_return_null_if_fail(pa_cvolume_valid(&data->virtual_volume));
+ pa_return_null_if_fail(pa_cvolume_compatible(&data->virtual_volume, &data->sample_spec));
if (!data->muted_is_set)
data->muted = FALSE;
@@ -165,6 +172,8 @@ pa_sink_input* pa_sink_input_new(
if (flags & PA_SINK_INPUT_FIX_RATE)
data->sample_spec.rate = data->sink->sample_spec.rate;
+ original_cm = data->channel_map;
+
if (flags & PA_SINK_INPUT_FIX_CHANNELS) {
data->sample_spec.channels = data->sink->sample_spec.channels;
data->channel_map = data->sink->channel_map;
@@ -174,8 +183,7 @@ pa_sink_input* pa_sink_input_new(
pa_assert(pa_channel_map_valid(&data->channel_map));
/* Due to the fixing of the sample spec the volume might not match anymore */
- if (data->volume.channels != data->sample_spec.channels)
- pa_cvolume_set(&data->volume, data->sample_spec.channels, pa_cvolume_avg(&data->volume));
+ pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map);
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
@@ -227,7 +235,9 @@ pa_sink_input* pa_sink_input_new(
i->sample_spec = data->sample_spec;
i->channel_map = data->channel_map;
+ i->virtual_volume = data->virtual_volume;
i->volume = data->volume;
+
i->muted = data->muted;
if (data->sync_base) {
@@ -292,8 +302,6 @@ static void update_n_corked(pa_sink_input *i, pa_sink_input_state_t state) {
pa_assert_se(i->sink->n_corked -- >= 1);
else if (i->state != PA_SINK_INPUT_CORKED && state == PA_SINK_INPUT_CORKED)
i->sink->n_corked++;
-
- pa_sink_update_status(i->sink);
}
/* Called from main context */
@@ -331,6 +339,8 @@ static int sink_input_set_state(pa_sink_input *i, pa_sink_input_state_t state) {
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], ssync);
}
+ pa_sink_update_status(i->sink);
+
return 0;
}
@@ -381,6 +391,8 @@ void pa_sink_input_unlink(pa_sink_input *i) {
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], i);
}
+ pa_sink_update_status(i->sink);
+
i->sink = NULL;
pa_sink_input_unref(i);
}
@@ -442,6 +454,8 @@ void pa_sink_input_put(pa_sink_input *i) {
pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, i->index);
pa_hook_fire(&i->sink->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], i);
+
+ pa_sink_update_status(i->sink);
}
/* Called from main context */
@@ -784,17 +798,34 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {
/* Called from main context */
void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume) {
+ pa_sink_input_set_volume_data data;
+
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
pa_assert(volume);
+ pa_assert(pa_cvolume_valid(volume));
+ pa_assert(pa_cvolume_compatible(volume, &i->sample_spec));
+
+ data.sink_input = i;
+ data.virtual_volume = *volume;
+ data.volume = *volume;
+
+ /* If you change something here, consider looking into
+ * module-flat-volume.c as well since it uses very similar
+ * code. */
- if (pa_cvolume_equal(&i->volume, volume))
+ if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], &data) < 0)
return;
- i->volume = *volume;
+ if (!pa_cvolume_equal(&i->volume, &data.volume)) {
+ i->volume = data.volume;
+ pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &data.volume, 0, NULL) == 0);
+ }
- pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, &i->volume, 0, NULL) == 0);
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ if (!pa_cvolume_equal(&i->virtual_volume, &data.virtual_volume)) {
+ i->virtual_volume = data.virtual_volume;
+ pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ }
}
/* Called from main context */
@@ -802,7 +833,7 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- return &i->volume;
+ return &i->virtual_volume;
}
/* Called from main context */
@@ -885,6 +916,35 @@ pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
}
/* Called from main context */
+pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_sink_assert_ref(dest);
+
+ if (dest == i->sink)
+ return TRUE;
+
+ if (i->flags & PA_SINK_INPUT_DONT_MOVE)
+ return FALSE;
+
+ if (i->sync_next || i->sync_prev) {
+ pa_log_warn("Moving synchronised streams not supported.");
+ return FALSE;
+ }
+
+ if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) {
+ pa_log_warn("Failed to move sink input: too many inputs per sink.");
+ return FALSE;
+ }
+
+ if (i->may_move_to)
+ if (!i->may_move_to(i, dest))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Called from main context */
int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
pa_resampler *new_resampler;
pa_sink *origin;
@@ -900,19 +960,9 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest) {
if (dest == origin)
return 0;
- if (i->flags & PA_SINK_INPUT_DONT_MOVE)
+ if (!pa_sink_input_may_move_to(i, dest))
return -1;
- if (i->sync_next || i->sync_prev) {
- pa_log_warn("Moving synchronised streams not supported.");
- return -1;
- }
-
- if (pa_idxset_size(dest->inputs) >= PA_MAX_INPUTS_PER_SINK) {
- pa_log_warn("Failed to move sink input: too many inputs per sink.");
- return -1;
- }
-
/* Kill directly connected outputs */
while ((o = pa_idxset_first(i->direct_outputs, NULL))) {
pa_assert(o != p);
@@ -1144,7 +1194,8 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
* implementor. This implies 'flush' is TRUE. */
pa_sink_input_assert_ref(i);
- pa_assert(i->thread_info.rewrite_nbytes == 0);
+
+ nbytes = PA_MAX(i->thread_info.rewrite_nbytes, nbytes);
/* pa_log_debug("request rewrite %lu", (unsigned long) nbytes); */
@@ -1172,26 +1223,33 @@ void pa_sink_input_request_rewind(pa_sink_input *i, size_t nbytes /* in our sam
nbytes = pa_resampler_request(i->thread_info.resampler, nbytes);
}
- if (rewrite) {
- /* Make sure to not overwrite over underruns */
- if (nbytes > i->thread_info.playing_for)
- nbytes = (size_t) i->thread_info.playing_for;
+ if (i->thread_info.rewrite_nbytes != (size_t) -1) {
+ if (rewrite) {
+ /* Make sure to not overwrite over underruns */
+ if (nbytes > i->thread_info.playing_for)
+ nbytes = (size_t) i->thread_info.playing_for;
- i->thread_info.rewrite_nbytes = nbytes;
- } else
- i->thread_info.rewrite_nbytes = (size_t) -1;
+ i->thread_info.rewrite_nbytes = nbytes;
+ } else
+ i->thread_info.rewrite_nbytes = (size_t) -1;
+ }
- i->thread_info.rewrite_flush = flush && i->thread_info.rewrite_nbytes != 0;
+ i->thread_info.rewrite_flush =
+ i->thread_info.rewrite_flush ||
+ (flush && i->thread_info.rewrite_nbytes != 0);
- /* Transform to sink domain */
- if (i->thread_info.resampler)
- nbytes = pa_resampler_result(i->thread_info.resampler, nbytes);
+ if (nbytes != (size_t) -1) {
- if (nbytes > lbq)
- pa_sink_request_rewind(i->sink, nbytes - lbq);
- else
- /* This call will make sure process_rewind() is called later */
- pa_sink_request_rewind(i->sink, 0);
+ /* Transform to sink domain */
+ if (i->thread_info.resampler)
+ nbytes = pa_resampler_result(i->thread_info.resampler, nbytes);
+
+ if (nbytes > lbq)
+ pa_sink_request_rewind(i->sink, nbytes - lbq);
+ else
+ /* This call will make sure process_rewind() is called later */
+ pa_sink_request_rewind(i->sink, 0);
+ }
}
/* Called from main context */
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 7663f22c..27125988 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -56,7 +56,8 @@ typedef enum pa_sink_input_flags {
PA_SINK_INPUT_NO_REMIX = 16,
PA_SINK_INPUT_FIX_FORMAT = 32,
PA_SINK_INPUT_FIX_RATE = 64,
- PA_SINK_INPUT_FIX_CHANNELS = 128
+ PA_SINK_INPUT_FIX_CHANNELS = 128,
+ PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
} pa_sink_input_flags_t;
struct pa_sink_input {
@@ -89,6 +90,8 @@ struct pa_sink_input {
pa_sink_input *sync_prev, *sync_next;
+ pa_cvolume virtual_volume;
+
pa_cvolume volume;
pa_bool_t muted;
@@ -154,10 +157,15 @@ struct pa_sink_input {
returns */
pa_usec_t (*get_latency) (pa_sink_input *i); /* may be NULL */
- /* If non_NULL this function is called from thread context if the
+ /* If non-NULL this function is called from thread context if the
* state changes. The old state is found in thread_info.state. */
void (*state_change) (pa_sink_input *i, pa_sink_input_state_t state); /* may be NULL */
+ /* If non-NULL this function is called before this sink input is
+ * move to a sink and if it returns FALSE the move will not
+ * be allowed */
+ pa_bool_t (*may_move_to) (pa_sink_input *i, pa_sink *s); /* may be NULL */
+
struct {
pa_sink_input_state_t state;
pa_atomic_t drained;
@@ -218,6 +226,9 @@ typedef struct pa_sink_input_new_data {
pa_sample_spec sample_spec;
pa_channel_map channel_map;
+
+ pa_cvolume virtual_volume;
+
pa_cvolume volume;
pa_bool_t muted:1;
@@ -239,6 +250,12 @@ typedef struct pa_sink_input_move_hook_data {
pa_sink *destination;
} pa_sink_input_move_hook_data;
+typedef struct pa_sink_set_input_volume_data {
+ pa_sink_input *sink_input;
+ pa_cvolume virtual_volume;
+ pa_cvolume volume;
+} pa_sink_input_set_volume_data;
+
/* To be called by the implementing module only */
pa_sink_input* pa_sink_input_new(
@@ -281,6 +298,7 @@ pa_bool_t pa_sink_input_get_mute(pa_sink_input *i);
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest);
+pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest);
pa_sink_input_state_t pa_sink_input_get_state(pa_sink_input *i);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index e04fc08a..1580cf2e 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -843,13 +843,27 @@ pa_usec_t pa_sink_get_latency(pa_sink *s) {
/* Called from main thread */
void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume) {
pa_bool_t changed;
+ pa_sink_set_volume_data data;
pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(volume);
+ pa_assert(pa_cvolume_valid(volume));
+ pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
- changed = !pa_cvolume_equal(volume, &s->volume);
- s->volume = *volume;
+ data.sink = s;
+ data.volume = *volume;
+
+ changed = !pa_cvolume_equal(&data.volume, &s->volume);
+
+ if (changed) {
+ if (pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_SET_VOLUME], &data) < 0)
+ return;
+
+ changed = !pa_cvolume_equal(&data.volume, &s->volume);
+ }
+
+ s->volume = data.volume;
if (s->set_volume && s->set_volume(s) < 0)
s->set_volume = NULL;
@@ -977,7 +991,7 @@ unsigned pa_sink_linked_by(pa_sink *s) {
ret = pa_idxset_size(s->inputs);
/* We add in the number of streams connected to us here. Please
- * not the asymmmetry to pa_sink_used_by()! */
+ * note the asymmmetry to pa_sink_used_by()! */
if (s->monitor_source)
ret += pa_source_linked_by(s->monitor_source);
@@ -1001,6 +1015,40 @@ unsigned pa_sink_used_by(pa_sink *s) {
return ret - s->n_corked;
}
+/* Called from main thread */
+unsigned pa_sink_check_suspend(pa_sink *s) {
+ unsigned ret;
+ pa_sink_input *i;
+ uint32_t idx;
+
+ pa_sink_assert_ref(s);
+
+ if (!PA_SINK_IS_LINKED(s->state))
+ return 0;
+
+ ret = 0;
+
+ for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
+ pa_sink_input_state_t st;
+
+ st = pa_sink_input_get_state(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(st));
+
+ if (st == PA_SINK_INPUT_CORKED)
+ continue;
+
+ if (i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND)
+ continue;
+
+ ret ++;
+ }
+
+ if (s->monitor_source)
+ ret += pa_source_check_suspend(s->monitor_source);
+
+ return ret;
+}
+
/* Called from IO thread, except when it is not */
int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
pa_sink *s = PA_SINK(o);
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 672bdd39..c5a73214 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -206,6 +206,11 @@ void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volum
void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute);
void pa_sink_new_data_done(pa_sink_new_data *data);
+typedef struct pa_sink_set_volume_data {
+ pa_sink *sink;
+ pa_cvolume volume;
+} pa_sink_set_volume_data;
+
/* To be called exclusively by the sink driver, from main context */
pa_sink* pa_sink_new(
@@ -247,6 +252,7 @@ pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refres);
unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */
unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */
+unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are active that don't allow suspensions */
#define pa_sink_get_state(s) ((s)->state)
/* To be called exclusively by the sink driver, from IO context */
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index d76f6e4e..c92c5ab7 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -90,6 +90,7 @@ static void reset_callbacks(pa_source_output *o) {
o->kill = NULL;
o->get_latency = NULL;
o->state_change = NULL;
+ o->may_move_to = NULL;
}
/* Called from main context */
@@ -124,14 +125,14 @@ pa_source_output* pa_source_output_new(
pa_return_null_if_fail(pa_sample_spec_valid(&data->sample_spec));
if (!data->channel_map_is_set) {
- if (data->source->channel_map.channels == data->sample_spec.channels)
+ if (pa_channel_map_compatible(&data->source->channel_map, &data->sample_spec))
data->channel_map = data->source->channel_map;
else
- pa_return_null_if_fail(pa_channel_map_init_auto(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT));
+ pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
}
pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
- pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
+ pa_return_null_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec));
if (flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
data->sample_spec.format = data->source->sample_spec.format;
@@ -246,7 +247,6 @@ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state)
else if (o->state != PA_SOURCE_OUTPUT_CORKED && state == PA_SOURCE_OUTPUT_CORKED)
o->source->n_corked++;
- pa_source_update_status(o->source);
}
/* Called from main context */
@@ -264,6 +264,8 @@ static int source_output_set_state(pa_source_output *o, pa_source_output_state_t
if (state != PA_SOURCE_OUTPUT_UNLINKED)
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
+ pa_source_update_status(o->source);
+
return 0;
}
@@ -302,6 +304,8 @@ void pa_source_output_unlink(pa_source_output*o) {
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o);
}
+ pa_source_update_status(o->source);
+
o->source = NULL;
pa_source_output_unref(o);
}
@@ -353,6 +357,8 @@ void pa_source_output_put(pa_source_output *o) {
pa_subscription_post(o->source->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
pa_hook_fire(&o->source->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o);
+
+ pa_source_update_status(o->source);
}
/* Called from main context */
@@ -593,6 +599,32 @@ pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
return o->resample_method;
}
+pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
+ pa_source_output_assert_ref(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+ pa_source_assert_ref(dest);
+
+ if (dest == o->source)
+ return TRUE;
+
+ if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
+ return FALSE;
+
+ if (o->direct_on_input)
+ return FALSE;
+
+ if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
+ pa_log_warn("Failed to move source output: too many outputs per source.");
+ return FALSE;
+ }
+
+ if (o->may_move_to)
+ if (!o->may_move_to(o, dest))
+ return FALSE;
+
+ return TRUE;
+}
+
/* Called from main context */
int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
pa_source *origin;
@@ -608,16 +640,8 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest) {
if (dest == origin)
return 0;
- if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
- return -1;
-
- if (o->direct_on_input)
- return -1;
-
- if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
- pa_log_warn("Failed to move source output: too many outputs per source.");
+ if (!pa_source_output_may_move_to(o, dest))
return -1;
- }
if (o->thread_info.resampler &&
pa_sample_spec_equal(&origin->sample_spec, &dest->sample_spec) &&
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index a7aac814..f011f9bd 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -52,7 +52,8 @@ typedef enum pa_source_output_flags {
PA_SOURCE_OUTPUT_NO_REMIX = 16,
PA_SOURCE_OUTPUT_FIX_FORMAT = 32,
PA_SOURCE_OUTPUT_FIX_RATE = 64,
- PA_SOURCE_OUTPUT_FIX_CHANNELS = 128
+ PA_SOURCE_OUTPUT_FIX_CHANNELS = 128,
+ PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND = 256
} pa_source_output_flags_t;
struct pa_source_output {
@@ -126,10 +127,15 @@ struct pa_source_output {
returns */
pa_usec_t (*get_latency) (pa_source_output *o); /* may be NULL */
- /* If non_NULL this function is called from thread context if the
+ /* If non-NULL this function is called from thread context if the
* state changes. The old state is found in thread_info.state. */
void (*state_change) (pa_source_output *o, pa_source_output_state_t state); /* may be NULL */
+ /* If non-NULL this function is called before this source output
+ * is moved to a source and if it returns FALSE the move
+ * will not be allowed */
+ pa_bool_t (*may_move_to) (pa_source_output *o, pa_source *s); /* may be NULL */
+
struct {
pa_source_output_state_t state;
@@ -220,6 +226,7 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *i, pa_usec_t *source_la
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
+pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest);
int pa_source_output_move_to(pa_source_output *o, pa_source *dest);
#define pa_source_output_get_state(o) ((o)->state)
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index edbbf017..f113e295 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -645,6 +645,37 @@ unsigned pa_source_used_by(pa_source *s) {
return ret - s->n_corked;
}
+/* Called from main thread */
+unsigned pa_source_check_suspend(pa_source *s) {
+ unsigned ret;
+ pa_source_output *o;
+ uint32_t idx;
+
+ pa_source_assert_ref(s);
+
+ if (!PA_SOURCE_IS_LINKED(s->state))
+ return 0;
+
+ ret = 0;
+
+ for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx))) {
+ pa_source_output_state_t st;
+
+ st = pa_source_output_get_state(o);
+ pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(st));
+
+ if (st == PA_SOURCE_OUTPUT_CORKED)
+ continue;
+
+ if (o->flags & PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND)
+ continue;
+
+ ret ++;
+ }
+
+ return ret;
+}
+
/* Called from IO thread, except when it is not */
int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
pa_source *s = PA_SOURCE(object);
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index cae78693..aaf904b4 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -234,6 +234,7 @@ pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh);
unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */
unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */
+unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are active that don't allow suspensions */
#define pa_source_get_state(s) ((pa_source_state_t) (s)->state)
/* To be called exclusively by the source driver, from IO context */
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
index 6a2ffaa6..65621948 100644
--- a/src/pulsecore/time-smoother.c
+++ b/src/pulsecore/time-smoother.c
@@ -313,7 +313,7 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
/* Move back from origin */
ty += (double) s->ey;
- *y = ty >= 0 ? (pa_usec_t) lrint(ty) : 0;
+ *y = ty >= 0 ? (pa_usec_t) llrint(ty) : 0;
/* Horner scheme */
if (deriv)
@@ -360,7 +360,7 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
/* And calculate when we want to be on track again */
s->px = s->ex + s->adjust_time;
- s->py = s->ry + (pa_usec_t) lrint(s->dp * (double) s->adjust_time);
+ s->py = s->ry + (pa_usec_t) llrint(s->dp * (double) s->adjust_time);
s->abc_valid = FALSE;
@@ -456,7 +456,7 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay)
/* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */
- return (pa_usec_t) lrint((double) y_delay / nde);
+ return (pa_usec_t) llrint((double) y_delay / nde);
}
void pa_smoother_reset(pa_smoother *s) {
diff --git a/src/utils/pacat.c b/src/utils/pacat.c
index 99df5b9e..ea736e23 100644
--- a/src/utils/pacat.c
+++ b/src/utils/pacat.c
@@ -57,6 +57,7 @@ static char *stream_name = NULL, *client_name = NULL, *device = NULL;
static int verbose = 0;
static pa_volume_t volume = PA_VOLUME_NORM;
+static int volume_is_set = 0;
static pa_sample_spec sample_spec = {
.format = PA_SAMPLE_S16LE,
@@ -283,7 +284,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
if (mode == PLAYBACK) {
pa_cvolume cv;
- if ((r = pa_stream_connect_playback(stream, device, latency > 0 ? &buffer_attr : NULL, flags, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) {
+ if ((r = pa_stream_connect_playback(stream, device, latency > 0 ? &buffer_attr : NULL, flags, volume_is_set ? pa_cvolume_set(&cv, sample_spec.channels, volume) : NULL, NULL)) < 0) {
fprintf(stderr, _("pa_stream_connect_playback() failed: %s\n"), pa_strerror(pa_context_errno(c)));
goto fail;
}
@@ -627,6 +628,7 @@ int main(int argc, char *argv[]) {
case ARG_VOLUME: {
int v = atoi(optarg);
volume = v < 0 ? 0U : (pa_volume_t) v;
+ volume_is_set = 1;
break;
}
diff --git a/src/utils/padsp.c b/src/utils/padsp.c
index 2e6e5575..046bae45 100644
--- a/src/utils/padsp.c
+++ b/src/utils/padsp.c
@@ -2382,15 +2382,15 @@ int access(const char *pathname, int mode) {
debug(DEBUG_LEVEL_VERBOSE, __FILE__": access(%s)\n", pathname?pathname:"NULL");
if (!pathname ||
- ( strcmp(pathname, "/dev/dsp") != 0 &&
- strcmp(pathname, "/dev/adsp") != 0 &&
- strcmp(pathname, "/dev/sndstat") != 0 &&
- strcmp(pathname, "/dev/mixer") != 0 )) {
+ (strcmp(pathname, "/dev/dsp") != 0 &&
+ strcmp(pathname, "/dev/adsp") != 0 &&
+ strcmp(pathname, "/dev/sndstat") != 0 &&
+ strcmp(pathname, "/dev/mixer") != 0 )) {
LOAD_ACCESS_FUNC();
return _access(pathname, mode);
}
- if (mode & (W_OK | X_OK)) {
+ if (mode & X_OK) {
debug(DEBUG_LEVEL_NORMAL, __FILE__": access(%s, %x) = EACCESS\n", pathname, mode);
errno = EACCES;
return -1;