diff options
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 @@ -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; |