Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work

Conflicts:
	src/Makefile.am
	src/daemon/daemon-conf.c
	src/daemon/daemon.conf.in
	src/modules/module-stream-restore.c
	src/pulse/client-conf.c
	src/pulsecore/namereg.c
This commit is contained in:
Tanu Kaskinen 2009-08-16 21:25:48 +03:00
commit bcaba0b1b4
134 changed files with 15076 additions and 12812 deletions

View file

@ -17,7 +17,7 @@
ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = bootstrap.sh LICENSE GPL LGPL doxygen/Makefile.am doxygen/Makefile.in doxygen/doxygen.conf.in README todo
EXTRA_DIST = bootstrap.sh git-version-gen LICENSE GPL LGPL doxygen/Makefile.am doxygen/Makefile.in doxygen/doxygen.conf.in README todo
SUBDIRS = src doxygen man po
MAINTAINERCLEANFILES =

View file

@ -40,10 +40,21 @@ run_versioned() {
set -ex
case $(uname) in
*Darwin*)
LIBTOOLIZE="glibtoolize"
;;
esac
if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then
echo "Activating pre-commit hook."
cp -av .git/hooks/pre-commit.sample .git/hooks/pre-commit
chmod -c +x .git/hooks/pre-commit
cp -pv .git/hooks/pre-commit.sample .git/hooks/pre-commit
chmod -v +x .git/hooks/pre-commit
fi
if [ -f .tarball-version ]; then
echo "Marking tarball version as modified."
echo -n `cat .tarball-version | sed 's/-rebootstrapped$//'`-rebootstrapped >.tarball-version
fi
# We check for this here, because if pkg-config is not found in the
@ -83,7 +94,7 @@ else
run_versioned automake "$VERSION" --copy --foreign --add-missing
if test "x$NOCONFIGURE" = "x"; then
CFLAGS="-g -O0" ./configure --sysconfdir=/etc --localstatedir=/var --enable-force-preopen "$@"
CFLAGS="$CFLAGS -g -O0" ./configure --sysconfdir=/etc --localstatedir=/var --enable-force-preopen "$@"
make clean
fi
fi

View file

@ -44,11 +44,11 @@ AC_SUBST(PA_PROTOCOL_VERSION, 16)
# The stable ABI for client applications, for the version info x:y:z
# always will hold y=z
AC_SUBST(LIBPULSE_VERSION_INFO, [8:0:8])
AC_SUBST(LIBPULSE_VERSION_INFO, [9:0:9])
# A simplified, synchronous, ABI-stable interface for client
# applications, for the version info x:y:z always will hold y=z
AC_SUBST(LIBPULSE_SIMPLE_VERSION_INFO, [0:2:0])
AC_SUBST(LIBPULSE_SIMPLE_VERSION_INFO, [0:3:0])
# The ABI-stable network browsing interface for client applications,
# for the version info x:y:z always will hold y=z
@ -73,6 +73,10 @@ case $host in
AC_DEFINE(_XOPEN_SOURCE, 600, Needed to get declarations for msg_control and msg_controllen on Solaris)
AC_DEFINE(__EXTENSIONS__, 1, Needed to get declarations for msg_control and msg_controllen on Solaris)
;;
*-*-darwin* )
AC_DEFINE([_POSIX_C_SOURCE], [200112L], [Needed to get clock_gettime on Mac OS X])
AC_DEFINE([_DARWIN_C_SOURCE], [200112L], [Needed to get NSIG on Mac OS X])
;;
esac
AM_SILENT_RULES([yes])
@ -112,6 +116,21 @@ CC_CHECK_LDFLAGS([${tmp_ldflag}],
[VERSIONING_LDFLAGS='-Wl,-version-script=$(srcdir)/map-file'])
AC_SUBST([VERSIONING_LDFLAGS])
dnl Use immediate (now) bindings; avoids the funky re-call in itself
dnl the -z now syntax is lifted from Sun's linker and works with GNU's too
dnl other linkes might be added later
CC_CHECK_LDFLAGS([-Wl,-z,now], [IMMEDIATE_LDFLAGS="-Wl,-z,now"])
AC_SUBST([IMMEDIATE_LDFLAGS])
dnl On ELF systems we don't want the libraries to be unloaded since we
dnl don't clean them up properly, so we request the nodelete flag to be
dnl enabled.
dnl
dnl On other systems, we don't really know how to do that, but it's
dnl welcome if somebody can tell.
CC_CHECK_LDFLAGS([-Wl,-z,nodelete], [NODELETE_LDFLAGS="-Wl,-z,nodelete"])
AC_SUBST([NODELETE_LDFLAGS])
dnl Check for the proper way to build libraries that have no undefined
dnl symbols; on some hosts this needs to be avoided but the macro
dnl takes care of it.
@ -387,6 +406,7 @@ AC_SEARCH_LIBS([timer_create], [rt])
# BSD
AC_SEARCH_LIBS([connect], [socket])
AC_SEARCH_LIBS([backtrace], [execinfo])
# Non-standard
@ -605,10 +625,11 @@ AM_CONDITIONAL([HAVE_LIBSAMPLERATE], [test "x$HAVE_LIBSAMPLERATE" = x1])
HAVE_TDB=0
HAVE_GDBM=0
HAVE_SIMPLEDB=0
AC_ARG_WITH(
[database],
AS_HELP_STRING([--with-database=auto|tdb|gdbm],[Choose database backend.]),[],[with_database=auto])
AS_HELP_STRING([--with-database=auto|tdb|gdbm|simple],[Choose database backend.]),[],[with_database=auto])
if test "x${with_database}" = "xauto" -o "x${with_database}" = "xtdb" ; then
PKG_CHECK_MODULES(TDB, [ tdb ],
@ -638,7 +659,12 @@ if test "x${with_database}" = "xauto" -o "x${with_database}" = "xgdbm" ; then
fi
fi
if test "x${HAVE_TDB}" != x1 -a "x${HAVE_GDBM}" != x1; then
if test "x${with_database}" = "xauto" -o "x${with_database}" = "xsimple" ; then
HAVE_SIMPLEDB=1
with_database=simple
fi
if test "x${HAVE_TDB}" != x1 -a "x${HAVE_GDBM}" != x1 -a "x${HAVE_SIMPLEDB}" != x1; then
AC_MSG_ERROR([*** missing database backend])
fi
@ -650,6 +676,10 @@ if test "x${HAVE_GDBM}" = x1 ; then
AC_DEFINE([HAVE_GDBM], 1, [Have gdbm?])
fi
if test "x${HAVE_SIMPLEDB}" = x1 ; then
AC_DEFINE([HAVE_SIMPLEDB], 1, [Have simple?])
fi
AC_SUBST(TDB_CFLAGS)
AC_SUBST(TDB_LIBS)
AC_SUBST(HAVE_TDB)
@ -660,28 +690,47 @@ AC_SUBST(GDBM_LIBS)
AC_SUBST(HAVE_GDBM)
AM_CONDITIONAL([HAVE_GDBM], [test "x$HAVE_GDBM" = x1])
AC_SUBST(HAVE_SIMPLEDB)
AM_CONDITIONAL([HAVE_SIMPLEDB], [test "x$HAVE_SIMPLEDB" = x1])
#### OSS support (optional) ####
AC_ARG_ENABLE([oss],
AS_HELP_STRING([--disable-oss],[Disable optional OSS support]),
AC_ARG_ENABLE([oss-output],
AS_HELP_STRING([--disable-oss-output],[Disable optional OSS output support]),
[
case "${enableval}" in
yes) oss=yes ;;
no) oss=no ;;
*) AC_MSG_ERROR(bad value ${enableval} for --disable-oss) ;;
yes) oss_output=yes ;;
no) oss_output=no ;;
*) AC_MSG_ERROR(bad value ${enableval} for --disable-oss-output) ;;
esac
],
[oss=auto])
[oss_output=auto])
if test "x${oss}" != xno ; then
AC_ARG_ENABLE([oss-wrapper],
AS_HELP_STRING([--disable-oss-wrapper],[Disable optional OSS wrapper support]),
[
case "${enableval}" in
yes) oss_wrapper=yes ;;
no) oss_wrapper=no ;;
*) AC_MSG_ERROR(bad value ${enableval} for --disable-oss-wrapper) ;;
esac
],
[oss_wrapper=auto])
if test "x${oss_output}" != xno || test "x${oss_wrapper}" != "xno"; then
AC_CHECK_HEADERS([sys/soundcard.h],
[
if test "x${oss_output}" != "xno"; then
AC_DEFINE([HAVE_OSS_OUTPUT], 1, [Have OSS output?])
fi
if test "x${oss_wrapper}" != "xno"; then
AC_DEFINE([HAVE_OSS_WRAPPER], 1, [Have OSS wrapper (padsp)?])
fi
HAVE_OSS=1
AC_DEFINE([HAVE_OSS], 1, [Have OSS?])
],
[
HAVE_OSS=0
if test "x$oss" = xyes ; then
if test "x$oss_output" = xyes || test "x$oss_wrapper" = "xyes"; then
AC_MSG_ERROR([*** OSS support not found])
fi
])
@ -690,8 +739,8 @@ else
fi
AC_SUBST(HAVE_OSS)
AM_CONDITIONAL([HAVE_OSS], [test "x$HAVE_OSS" = x1])
AM_CONDITIONAL([HAVE_OSS_OUTPUT], [test "x$HAVE_OSS" = x1 && test "x${oss_output}" != "xno"])
AM_CONDITIONAL([HAVE_OSS_WRAPPER], [test "x$HAVE_OSS" = x1 && test "x${oss_wrapper}" != "xno"])
#### ALSA support (optional) ####
@ -1079,7 +1128,27 @@ AC_SUBST(UDEV_LIBS)
AC_SUBST(HAVE_UDEV)
AM_CONDITIONAL([HAVE_UDEV], [test "x$HAVE_UDEV" = x1])
AC_DEFINE([LIBUDEV_I_KNOW_THE_API_IS_SUBJECT_TO_CHANGE], 1, [I know the API is subject to change.])
#### HAL compat support (optional) ####
AC_ARG_ENABLE([hal-compat],
AS_HELP_STRING([--disable-hal-compat],[Disable optional HAL->udev transition compatibility support]),
[
case "${enableval}" in
yes) halcompat=yes ;;
no) halcompat=no ;;
*) AC_MSG_ERROR(bad value ${enableval} for --disable-hal-compat) ;;
esac
],
[halcompat=auto])
if test "x${halcompat}" != xno -a "x$HAVE_HAL" = "x0" -a "x$HAVE_UDEV" = "x1" ; then
HAVE_HAL_COMPAT=1
AC_DEFINE([HAVE_HAL_COMPAT], 1, [Have HAL compatibility.])
else
HAVE_HAL_COMPAT=0
fi
AC_SUBST(HAVE_HAL_COMPAT)
AM_CONDITIONAL([HAVE_HAL_COMPAT], [test "x$HAVE_HAL_COMPAT" = x1])
#### BlueZ support (optional) ####
@ -1338,9 +1407,15 @@ if test "x$HAVE_X11" = "x1" ; then
ENABLE_X11=yes
fi
ENABLE_OSS=no
ENABLE_OSS_OUTPUT=no
ENABLE_OSS_WRAPPER=no
if test "x$HAVE_OSS" = "x1" ; then
ENABLE_OSS=yes
if test "x$enable_oss_output" != "xno"; then
ENABLE_OSS_OUTPUT=yes
fi
if test "x$enable_oss_wrapper" != "xno"; then
ENABLE_OSS_WRAPPER=yes
fi
fi
ENABLE_ALSA=no
@ -1398,6 +1473,11 @@ if test "x$HAVE_UDEV" = "x1" ; then
ENABLE_UDEV=yes
fi
ENABLE_HAL_COMPAT=no
if test "x$HAVE_HAL_COMPAT" = "x1" ; then
ENABLE_HAL_COMPAT=yes
fi
ENABLE_TCPWRAP=no
if test "x${LIBWRAP_LIBS}" != x ; then
ENABLE_TCPWRAP=yes
@ -1423,6 +1503,11 @@ if test "x${HAVE_TDB}" = "x1" ; then
ENABLE_TDB=yes
fi
ENABLE_SIMPLEDB=no
if test "x${HAVE_SIMPLEDB}" = "x1" ; then
ENABLE_SIMPLEDB=yes
fi
ENABLE_OPENSSL=no
if test "x${HAVE_OPENSSL}" = "x1" ; then
ENABLE_OPENSSL=yes
@ -1451,7 +1536,8 @@ echo "
CFLAGS: ${CFLAGS}
Have X11: ${ENABLE_X11}
Enable OSS: ${ENABLE_OSS}
Enable OSS Output: ${ENABLE_OSS_OUTPUT}
Enable OSS Wrapper: ${ENABLE_OSS_WRAPPER}
Enable Alsa: ${ENABLE_ALSA}
Enable Solaris: ${ENABLE_SOLARIS}
Enable GLib 2.0: ${ENABLE_GLIB20}
@ -1463,6 +1549,7 @@ echo "
Enable LIRC: ${ENABLE_LIRC}
Enable HAL: ${ENABLE_HAL}
Enable udev: ${ENABLE_UDEV}
Enable HAL->udev compat: ${ENABLE_HAL_COMPAT}
Enable BlueZ: ${ENABLE_BLUEZ}
Enable TCP Wrappers: ${ENABLE_TCPWRAP}
Enable libsamplerate: ${ENABLE_LIBSAMPLERATE}
@ -1470,6 +1557,7 @@ echo "
Enable OpenSSL (for Airtunes): ${ENABLE_OPENSSL}
Enable tdb: ${ENABLE_TDB}
Enable gdbm: ${ENABLE_GDBM}
Enable simple database: ${ENABLE_SIMPLEDB}
System User: ${PA_SYSTEM_USER}
System Group: ${PA_SYSTEM_GROUP}

View file

@ -92,9 +92,9 @@ USA.
</option>
<option>
<p><opt>disable-shm=</opt> Disable data transfer via POSIX
<p><opt>enable-shm=</opt> Enable data transfer via POSIX
shared memory. Takes a boolean argument, defaults to
<opt>no</opt>.</p>
<opt>yes</opt>.</p>
</option>
<option>

View file

@ -65,20 +65,21 @@ USA.
</option>
<option>
<p><opt>disallow-module-loading=</opt> Disallow module loading
after startup. This is a security feature that makes sure that
no further modules may be loaded into the PulseAudio server
after startup completed. It is recommended to enable this when
<opt>system-instance</opt> is enabled. Please note that certain
features like automatic hot-plug support will not work if this
option is enabled. Takes a boolean argument, defaults to
<opt>no</opt>. The <opt>--disallow-module-loading</opt> command line
option takes precedence.</p>
<p><opt>allow-module-loading=</opt> Allow/disallow module
loading after startup. This is a security feature that if
dsabled makes sure that no further modules may be loaded into
the PulseAudio server after startup completed. It is recommended
to disable this when <opt>system-instance</opt> is
enabled. Please note that certain features like automatic
hot-plug support will not work if this option is enabled. Takes
a boolean argument, defaults to <opt>yes</opt>. The
<opt>--disallow-module-loading</opt> command line option takes
precedence.</p>
</option>
<option>
<p><opt>disallow-exit=</opt> Disallow exit on user
request. Defaults to <opt>no</opt>.</p>
<p><opt>allow-exit=</opt> Allow/disallow exit on user
request. Defaults to <opt>yes</opt>.</p>
</option>
<option>
@ -105,19 +106,19 @@ USA.
</option>
<option>
<p><opt>disable-remixing=</opt> Never upmix or downmix channels
to different channel maps. Instead, do a simple name-based
matching only.</p>
<p><opt>enable-remixing=</opt> If disabled never upmix or
downmix channels to different channel maps. Instead, do a simple
name-based matching only. Defaults to <opt>yes.</opt></p>
</option>
<option>
<p><opt>disable-lfe-remixing=</opt> When upmixing or downmixing
ignore LFE channels. When this option is on the output LFE
channel will only get a signal when an input LFE channel is
available as well. If no input LFE channel is available the
output LFE channel will always be 0. If no output LFE channel is
available the signal on the input LFE channel will be
ignored. Defaults to "on".</p>
<p><opt>enable-lfe-remixing=</opt> if disabeld when upmixing or
downmixing ignore LFE channels. When this option is dsabled the
output LFE channel will only get a signal when an input LFE
channel is available as well. If no input LFE channel is
available the output LFE channel will always be 0. If no output
LFE channel is available the signal on the input LFE channel
will be ignored. Defaults to <opt>no</opt>.</p>
</option>
<option>
@ -132,12 +133,12 @@ USA.
</option>
<option>
<p><opt>no-cpu-limit=</opt> Do not install the CPU load limiter,
even on platforms where it is supported. This option is useful
when debugging/profiling PulseAudio to disable disturbing
SIGXCPU signals. Takes a boolean argument, defaults to <opt>no</opt>. The
<opt>--no-cpu-limit</opt> command line argument takes
precedence.</p>
<p><opt>cpu-limit=</opt> If disabled d not install the CPU load
limiter, even on platforms where it is supported. This option is
useful when debugging/profiling PulseAudio to disable disturbing
SIGXCPU signals. Takes a boolean argument, defaults to
<opt>yes</opt>. The <opt>--no-cpu-limit</opt> command line
argument takes precedence.</p>
</option>
<option>
@ -148,9 +149,9 @@ USA.
</option>
<option>
<p><opt>disable-shm=</opt> Disable data transfer via POSIX
<p><opt>enable-shm=</opt> Enable data transfer via POSIX
shared memory. Takes a boolean argument, defaults to
<opt>no</opt>. The <opt>--disable-shm</opt> command line
<opt>yes</opt>. The <opt>--disable-shm</opt> command line
argument takes precedence.</p>
</option>
@ -204,7 +205,7 @@ USA.
real-time. The controlling thread is left a normally scheduled
thread. Thus enabling the high-priority option is orthogonal.
See <manref section="1" name="pulseaudio"/> for more
information. Takes a boolean argument, defaults to "no". The
information. Takes a boolean argument, defaults to "yes". The
<opt>--realtime</opt> command line option takes precedence.</p>
</option>
@ -230,7 +231,7 @@ USA.
<option>
<p><opt>exit-idle-time=</opt> Terminate the daemon after the
last client quit and this time in seconds passed. Use a negative value to
disable this feature. Defaults to -1. The
disable this feature. Defaults to 20. The
<opt>--exit-idle-time</opt> command line option takes
precedence.</p>
</option>

View file

@ -55,7 +55,6 @@ src/pulsecore/queue.c
src/pulsecore/core.c
#src/pulsecore/shmasyncq.c
src/pulsecore/x11wrap.c
src/pulsecore/rtclock.c
src/pulsecore/ioline.c
src/pulsecore/asyncq.c
src/pulsecore/mutex-posix.c
@ -99,7 +98,6 @@ src/pulsecore/strlist.c
src/pulsecore/msgobject.c
src/pulsecore/mutex-win32.c
src/pulsecore/dynarray.c
src/pulsecore/rtsig.c
src/pulsecore/once.c
src/pulsecore/source.c
src/pulsecore/memchunk.c
@ -149,14 +147,12 @@ src/pulsecore/protocol-http.c
src/pulsecore/semaphore-win32.c
src/daemon/cpulimit.c
src/daemon/ltdl-bind-now.c
src/daemon/polkit.c
src/daemon/main.c
src/daemon/cmdline.c
src/daemon/dumpmodules.c
src/daemon/daemon-conf.c
src/daemon/caps.c
src/daemon/pulseaudio.desktop.in
src/daemon/org.pulseaudio.policy.in
src/pulse/channelmap.c
src/pulse/error.c
src/pulse/proplist.c

760
po/as.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

918
po/ca.po

File diff suppressed because it is too large Load diff

851
po/cs.po

File diff suppressed because it is too large Load diff

799
po/de.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

548
po/el.po

File diff suppressed because it is too large Load diff

1356
po/es.po

File diff suppressed because it is too large Load diff

768
po/fi.po

File diff suppressed because it is too large Load diff

782
po/fr.po

File diff suppressed because it is too large Load diff

757
po/gu.po

File diff suppressed because it is too large Load diff

751
po/hi.po

File diff suppressed because it is too large Load diff

774
po/it.po

File diff suppressed because it is too large Load diff

767
po/kn.po

File diff suppressed because it is too large Load diff

754
po/mr.po

File diff suppressed because it is too large Load diff

758
po/nl.po

File diff suppressed because it is too large Load diff

751
po/or.po

File diff suppressed because it is too large Load diff

751
po/pa.po

File diff suppressed because it is too large Load diff

771
po/pl.po

File diff suppressed because it is too large Load diff

834
po/pt.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

775
po/sr.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

640
po/sv.po

File diff suppressed because it is too large Load diff

770
po/ta.po

File diff suppressed because it is too large Load diff

748
po/te.po

File diff suppressed because it is too large Load diff

767
po/uk.po

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -32,6 +32,7 @@ xdgautostartdir=$(sysconfdir)/xdg/autostart
alsaprofilesetsdir=$(datadir)/pulseaudio/alsa-mixer/profile-sets
alsapathsdir=$(datadir)/pulseaudio/alsa-mixer/paths
udevrulesdir=/lib/udev/rules.d
dbuspolicydir=$(sysconfdir)/dbus-1/system.d
###################################
# Defines #
@ -87,7 +88,7 @@ AM_CFLAGS = \
AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS)
AM_LDADD = $(PTHREAD_LIBS) $(INTLLIBS)
AM_LDFLAGS = -Wl,-z,nodelete
AM_LDFLAGS = $(NODELETE_LDFLAGS)
if STATIC_BINS
BINLDFLAGS = -static
@ -119,6 +120,7 @@ EXTRA_DIST = \
modules/module-defs.h.m4 \
daemon/pulseaudio.desktop.in \
map-file \
daemon/pulseaudio-system.conf \
modules/alsa/mixer/profile-sets/default.conf \
modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf \
modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf \
@ -145,6 +147,9 @@ pulseconf_DATA = \
daemon.conf \
client.conf
dbuspolicy_DATA = \
daemon/pulseaudio-system.conf
if HAVE_X11
xdgautostart_in_files = \
daemon/pulseaudio.desktop.in
@ -184,9 +189,9 @@ PREOPEN_LIBS = $(modlibexec_LTLIBRARIES)
endif
if FORCE_PREOPEN
pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -dlpreopen force $(foreach f,$(PREOPEN_LIBS),-dlpreopen $(f))
pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(IMMEDIATE_LDFLAGS) -dlpreopen force $(foreach f,$(PREOPEN_LIBS),-dlpreopen $(f))
else
pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -dlopen force $(foreach f,$(PREOPEN_LIBS),-dlopen $(f))
pulseaudio_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(IMMEDIATE_LDFLAGS) -dlopen force $(foreach f,$(PREOPEN_LIBS),-dlopen $(f))
endif
###################################
@ -777,7 +782,7 @@ libpulse_mainloop_glib_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version
# OSS emulation #
###################################
if HAVE_OSS
if HAVE_OSS_WRAPPER
lib_LTLIBRARIES += libpulsedsp.la
bin_SCRIPTS += utils/padsp
endif
@ -869,6 +874,9 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(TDB_CFLAGS)
libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(TDB_LIBS)
endif
if HAVE_SIMPLEDB
libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/database-simple.c
endif
# We split the foreign code off to not be annoyed by warnings we don't care about
noinst_LTLIBRARIES = libpulsecore-foreign.la
@ -1035,7 +1043,7 @@ modlibexec_LTLIBRARIES += \
module-x11-cork-request.la
endif
if HAVE_OSS
if HAVE_OSS_OUTPUT
modlibexec_LTLIBRARIES += \
liboss-util.la \
module-oss.la
@ -1055,8 +1063,10 @@ alsaprofilesets_DATA = \
modules/alsa/mixer/profile-sets/native-instruments-audio4dj.conf \
modules/alsa/mixer/profile-sets/native-instruments-audio8dj.conf
if HAVE_UDEV
udevrules_DATA = \
modules/alsa/mixer/profile-sets/90-pulseaudio.rules
endif
alsapaths_DATA = \
modules/alsa/mixer/paths/analog-input-aux.conf \
@ -1122,6 +1132,11 @@ modlibexec_LTLIBRARIES += \
module-hal-detect.la
endif
if HAVE_HAL_COMPAT
modlibexec_LTLIBRARIES += \
module-hal-detect.la
endif
if HAVE_UDEV
modlibexec_LTLIBRARIES += \
module-udev-detect.la
@ -1250,7 +1265,7 @@ module_simple_protocol_unix_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR
module_cli_la_SOURCES = modules/module-cli.c
module_cli_la_LDFLAGS = $(MODULE_LDFLAGS)
module_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libcli.la libpulsecommon-@PA_MAJORMINORMICRO@.la
module_cli_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libcli.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.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)
@ -1603,10 +1618,16 @@ 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)
if HAVE_HAL_COMPAT
module_hal_detect_la_SOURCES = modules/module-hal-detect-compat.c
module_hal_detect_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_hal_detect_la_CFLAGS = $(AM_CFLAGS)
else
module_hal_detect_la_SOURCES = modules/module-hal-detect.c
module_hal_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
module_hal_detect_la_LIBADD = $(AM_LIBADD) $(HAL_LIBS) libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la
module_hal_detect_la_CFLAGS = $(AM_CFLAGS) $(HAL_CFLAGS)
endif
module_hal_detect_la_LDFLAGS = $(MODULE_LDFLAGS)
module_udev_detect_la_SOURCES = modules/module-udev-detect.c
module_udev_detect_la_LDFLAGS = $(MODULE_LDFLAGS)

View file

@ -57,24 +57,29 @@ void pa_drop_root(void) {
#ifdef HAVE_GETUID
uid_t uid;
gid_t gid;
pa_log_debug(_("Cleaning up privileges."));
uid = getuid();
if (uid == 0 || geteuid() != 0)
return;
pa_log_info(_("Dropping root privileges."));
gid = getgid();
#if defined(HAVE_SETRESUID)
pa_assert_se(setresuid(uid, uid, uid) >= 0);
pa_assert_se(setresgid(gid, gid, gid) >= 0);
#elif defined(HAVE_SETREUID)
pa_assert_se(setreuid(uid, uid) >= 0);
pa_assert_se(setregid(gid, gid) >= 0);
#else
pa_assert_se(setuid(uid) >= 0);
pa_assert_se(seteuid(uid) >= 0);
pa_assert_se(setgid(gid) >= 0);
pa_assert_se(setegid(gid) >= 0);
#endif
pa_assert_se(getuid() == uid);
pa_assert_se(geteuid() == uid);
pa_assert_se(getgid() == gid);
pa_assert_se(getegid() == gid);
#endif
#ifdef HAVE_SYS_PRCTL_H
@ -82,7 +87,7 @@ void pa_drop_root(void) {
#endif
#ifdef HAVE_SYS_CAPABILITY_H
{
if (uid != 0) {
cap_t caps;
pa_assert_se(caps = cap_init());
pa_assert_se(cap_clear(caps) == 0);

View file

@ -476,14 +476,18 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ "high-priority", pa_config_parse_bool, &c->high_priority, NULL },
{ "realtime-scheduling", pa_config_parse_bool, &c->realtime_scheduling, NULL },
{ "disallow-module-loading", pa_config_parse_bool, &c->disallow_module_loading, NULL },
{ "allow-module-loading", pa_config_parse_not_bool, &c->disallow_module_loading, NULL },
{ "disallow-exit", pa_config_parse_bool, &c->disallow_exit, NULL },
{ "allow-exit", pa_config_parse_not_bool, &c->disallow_exit, NULL },
{ "use-pid-file", pa_config_parse_bool, &c->use_pid_file, NULL },
{ "system-instance", pa_config_parse_bool, &c->system_instance, NULL },
#ifdef HAVE_DBUS
{ "local-server-type", parse_server_type, c, NULL },
#endif
{ "no-cpu-limit", pa_config_parse_bool, &c->no_cpu_limit, NULL },
{ "cpu-limit", pa_config_parse_not_bool, &c->no_cpu_limit, NULL },
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
{ "enable-shm", pa_config_parse_not_bool, &c->disable_shm, NULL },
{ "flat-volumes", pa_config_parse_bool, &c->flat_volumes, NULL },
{ "lock-memory", pa_config_parse_bool, &c->lock_memory, NULL },
{ "exit-idle-time", pa_config_parse_int, &c->exit_idle_time, NULL },
@ -503,7 +507,9 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ "default-fragment-size-msec", parse_fragment_size_msec, c, NULL },
{ "nice-level", parse_nice_level, c, NULL },
{ "disable-remixing", pa_config_parse_bool, &c->disable_remixing, NULL },
{ "enable-remixing", pa_config_parse_not_bool, &c->disable_remixing, NULL },
{ "disable-lfe-remixing", pa_config_parse_bool, &c->disable_lfe_remixing, NULL },
{ "enable-lfe-remixing", pa_config_parse_not_bool, &c->disable_lfe_remixing, NULL },
{ "load-default-script-file", pa_config_parse_bool, &c->load_default_script_file, NULL },
{ "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL },
{ "log-meta", pa_config_parse_bool, &c->log_meta, NULL },
@ -669,15 +675,15 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
pa_strbuf_printf(s, "nice-level = %i\n", c->nice_level);
pa_strbuf_printf(s, "realtime-scheduling = %s\n", pa_yes_no(c->realtime_scheduling));
pa_strbuf_printf(s, "realtime-priority = %i\n", c->realtime_priority);
pa_strbuf_printf(s, "disallow-module-loading = %s\n", pa_yes_no(c->disallow_module_loading));
pa_strbuf_printf(s, "disallow-exit = %s\n", pa_yes_no(c->disallow_exit));
pa_strbuf_printf(s, "allow-module-loading = %s\n", pa_yes_no(!c->disallow_module_loading));
pa_strbuf_printf(s, "allow-exit = %s\n", pa_yes_no(!c->disallow_exit));
pa_strbuf_printf(s, "use-pid-file = %s\n", pa_yes_no(c->use_pid_file));
pa_strbuf_printf(s, "system-instance = %s\n", pa_yes_no(c->system_instance));
#ifdef HAVE_DBUS
pa_strbuf_printf(s, "local-server-type = %s\n", server_type_to_string[c->local_server_type]);
#endif
pa_strbuf_printf(s, "no-cpu-limit = %s\n", pa_yes_no(c->no_cpu_limit));
pa_strbuf_printf(s, "disable-shm = %s\n", pa_yes_no(c->disable_shm));
pa_strbuf_printf(s, "cpu-limit = %s\n", pa_yes_no(!c->no_cpu_limit));
pa_strbuf_printf(s, "enable-shm = %s\n", pa_yes_no(!c->disable_shm));
pa_strbuf_printf(s, "flat-volumes = %s\n", pa_yes_no(c->flat_volumes));
pa_strbuf_printf(s, "lock-memory = %s\n", pa_yes_no(c->lock_memory));
pa_strbuf_printf(s, "exit-idle-time = %i\n", c->exit_idle_time);
@ -688,8 +694,8 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) {
pa_strbuf_printf(s, "log-target = %s\n", c->auto_log_target ? "auto" : (c->log_target == PA_LOG_SYSLOG ? "syslog" : "stderr"));
pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]);
pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method));
pa_strbuf_printf(s, "disable-remixing = %s\n", pa_yes_no(c->disable_remixing));
pa_strbuf_printf(s, "disable-lfe-remixing = %s\n", pa_yes_no(c->disable_lfe_remixing));
pa_strbuf_printf(s, "enable-remixing = %s\n", pa_yes_no(!c->disable_remixing));
pa_strbuf_printf(s, "enable-lfe-remixing = %s\n", pa_yes_no(!c->disable_lfe_remixing));
pa_strbuf_printf(s, "default-sample-format = %s\n", pa_sample_format_to_string(c->default_sample_spec.format));
pa_strbuf_printf(s, "default-sample-rate = %u\n", c->default_sample_spec.rate);
pa_strbuf_printf(s, "default-sample-channels = %u\n", c->default_sample_spec.channels);

View file

@ -21,15 +21,15 @@
; daemonize = no
; fail = yes
; disallow-module-loading = no
; disallow-exit = no
; allow-module-loading = yes
; allow-exit = yes
; use-pid-file = yes
; system-instance = no
; local-server-type = user
; disable-shm = no
; enable-shm = yes
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
; lock-memory = no
; no-cpu-limit = no
; cpu-limit = yes
; high-priority = yes
; nice-level = -11
@ -52,8 +52,8 @@
; log-backtrace = 0
; resample-method = speex-float-3
; disable-remixing = no
; disable-lfe-remixing = yes
; enable-remixing = yes
; enable-lfe-remixing = no
; flat-volumes = yes

View file

@ -110,7 +110,7 @@ int allow_severity = LOG_INFO;
int deny_severity = LOG_WARNING;
#endif
#ifdef HAVE_OSS
#ifdef HAVE_OSS_WRAPPER
/* padsp looks for this symbol in the running process and disables
* itself if it finds it and it is set to 7 (which is actually a bit
* mask). For details see padsp. */
@ -407,7 +407,8 @@ int main(int argc, char *argv[]) {
/*
Disable lazy relocations to make usage of external libraries
more deterministic for our RT threads. We abuse __OPTIMIZE__ as
a check whether we are a debug build or not.
a check whether we are a debug build or not. This all is
admittedly a bit snake-oilish.
*/
if (!getenv("LD_BIND_NOW")) {
@ -418,9 +419,16 @@ int main(int argc, char *argv[]) {
pa_set_env("LD_BIND_NOW", "1");
if ((rp = pa_readlink("/proc/self/exe")))
if ((rp = pa_readlink("/proc/self/exe"))) {
if (pa_streq(rp, PA_BINARY))
pa_assert_se(execv(rp, argv) == 0);
else
pa_log_warn("/proc/self/exe does not point to " PA_BINARY ", cannot self execute. Are you playing games?");
pa_xfree(rp);
} else
pa_log_warn("Couldn't read /proc/self/exe, cannot self execute. Running in a chroot()?");
}
#endif
@ -435,10 +443,13 @@ int main(int argc, char *argv[]) {
/* We might be autospawned, in which case have no idea in which
* context we have been started. Let's cleanup our execution
* context as good as possible */
pa_reset_personality();
pa_drop_root();
pa_close_all(passed_fd, -1);
pa_reset_sigs(-1);
pa_unblock_sigs(-1);
pa_reset_priority();
setlocale(LC_ALL, "");
pa_init_i18n();

View file

@ -0,0 +1,37 @@
<?xml version="1.0"?><!--*-nxml-*-->
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<!--
This file is part of PulseAudio.
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.
-->
<busconfig>
<!-- System-wide PulseAudio runs as 'pulse' user. This fragment is
not necessary for user PulseAudio instances. -->
<policy user="pulse">
<allow own="org.pulseaudio.Server"/>
<!-- Allow pulseaudio to talk to HAL for device detection -->
<allow send_destination="org.freedesktop.Hal" send_interface="org.freedesktop.Hal.Manager"/>
<allow send_destination="org.freedesktop.Hal" send_interface="org.freedesktop.Hal.Device"/>
</policy>
</busconfig>

View file

@ -220,6 +220,8 @@ pa_simple_get_latency;
pa_simple_new;
pa_simple_read;
pa_simple_write;
pa_stream_begin_write;
pa_stream_cancel_write;
pa_stream_connect_playback;
pa_stream_connect_record;
pa_stream_connect_upload;

View file

@ -940,7 +940,6 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
PA_LLIST_FOREACH(e, p->elements) {
switch (e->switch_use) {
case PA_ALSA_SWITCH_MUTE:
case PA_ALSA_SWITCH_OFF:
r = element_set_switch(e, m, FALSE);
break;
@ -949,6 +948,7 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
r = element_set_switch(e, m, TRUE);
break;
case PA_ALSA_SWITCH_MUTE:
case PA_ALSA_SWITCH_IGNORE:
case PA_ALSA_SWITCH_SELECT:
r = 0;
@ -960,7 +960,6 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
switch (e->volume_use) {
case PA_ALSA_VOLUME_OFF:
case PA_ALSA_VOLUME_MERGE:
r = element_mute_volume(e, m);
break;
@ -968,6 +967,7 @@ int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m) {
r = element_zero_volume(e, m);
break;
case PA_ALSA_VOLUME_MERGE:
case PA_ALSA_VOLUME_IGNORE:
r = 0;
break;
@ -2838,9 +2838,9 @@ static int mapping_verify(pa_alsa_mapping *m, const pa_channel_map *bonus) {
if (bonus) {
if (pa_channel_map_equal(&m->channel_map, bonus))
m->priority += 5000;
m->priority += 50;
else if (m->channel_map.channels == bonus->channels)
m->priority += 4000;
m->priority += 30;
}
return 0;

View file

@ -169,10 +169,10 @@ static int reserve_init(struct userdata *u, const char *dname) {
if (pa_in_system_mode())
return 0;
/* We are resuming, try to lock the device */
if (!(rname = pa_alsa_get_reserve_name(dname)))
return 0;
/* We are resuming, try to lock the device */
u->reserve = pa_reserve_wrapper_get(u->core, rname);
pa_xfree(rname);
@ -222,7 +222,6 @@ static int reserve_monitor_init(struct userdata *u, const char *dname) {
if (pa_in_system_mode())
return 0;
/* We are resuming, try to lock the device */
if (!(rname = pa_alsa_get_reserve_name(dname)))
return 0;
@ -341,6 +340,9 @@ static int try_recover(struct userdata *u, const char *call, int err) {
if (err == -EPIPE)
pa_log_debug("%s: Buffer underrun!", call);
if (err == -ESTRPIPE)
pa_log_debug("%s: System suspended!", call);
if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) {
pa_log("%s: %s", call, pa_alsa_strerror(err));
return -1;
@ -402,6 +404,7 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
snd_pcm_sframes_t n;
size_t n_bytes;
int r;
pa_bool_t after_avail = TRUE;
/* First we determine how many samples are missing to fill the
* buffer up to 100% */
@ -485,6 +488,9 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->sink->sample_spec)) < 0)) {
if (!after_avail && err == -EAGAIN)
break;
if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
continue;
@ -495,6 +501,12 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
if (frames > pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size)
frames = pa_mempool_block_size_max(u->sink->core->mempool)/u->frame_size;
if (!after_avail && frames == 0)
break;
pa_assert(frames > 0);
after_avail = FALSE;
/* Check these are multiples of 8 bit */
pa_assert((areas[0].first & 7) == 0);
pa_assert((areas[0].step & 7)== 0);
@ -615,6 +627,7 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
for (;;) {
snd_pcm_sframes_t frames;
void *p;
pa_bool_t after_avail = TRUE;
/* pa_log_debug("%lu frames to write", (unsigned long) frames); */
@ -632,16 +645,23 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
frames = snd_pcm_writei(u->pcm_handle, (const uint8_t*) p + u->memchunk.index, (snd_pcm_uframes_t) frames);
pa_memblock_release(u->memchunk.memblock);
pa_assert(frames != 0);
if (PA_UNLIKELY(frames < 0)) {
if (!after_avail && (int) frames == -EAGAIN)
break;
if ((r = try_recover(u, "snd_pcm_writei", (int) frames)) == 0)
continue;
return r;
}
if (!after_avail && frames == 0)
break;
pa_assert(frames > 0);
after_avail = FALSE;
u->memchunk.index += (size_t) frames * u->frame_size;
u->memchunk.length -= (size_t) frames * u->frame_size;
@ -882,9 +902,13 @@ static int unsuspend(struct userdata *u) {
if (build_pollfd(u) < 0)
goto fail;
u->write_count = 0;
pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE);
u->first = TRUE;
u->since_start = 0;
pa_log_info("Resumed successfully...");
return 0;
@ -1178,8 +1202,11 @@ static int process_rewind(struct userdata *u) {
pa_log_debug("before: %lu", (unsigned long) in_frames);
if ((out_frames = snd_pcm_rewind(u->pcm_handle, (snd_pcm_uframes_t) in_frames)) < 0) {
pa_log("snd_pcm_rewind() failed: %s", pa_alsa_strerror((int) out_frames));
if (try_recover(u, "process_rewind", out_frames) < 0)
return -1;
out_frames = 0;
}
pa_log_debug("after: %lu", (unsigned long) out_frames);
rewind_nbytes = (size_t) out_frames * u->frame_size;
@ -1187,7 +1214,7 @@ static int process_rewind(struct userdata *u) {
if (rewind_nbytes <= 0)
pa_log_info("Tried rewind, but was apparently not possible.");
else {
u->write_count -= out_frames * u->frame_size;
u->write_count -= rewind_nbytes;
pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
pa_sink_process_rewind(u->sink, rewind_nbytes);
@ -1265,6 +1292,7 @@ static void thread_func(void *userdata) {
* we have filled the buffer at least once
* completely.*/
if (pa_log_ratelimit())
pa_log_debug("Cutting sleep time for the initial iterations by half.");
sleep_usec /= 2;
}

View file

@ -333,6 +333,9 @@ static int try_recover(struct userdata *u, const char *call, int err) {
if (err == -EPIPE)
pa_log_debug("%s: Buffer overrun!", call);
if (err == -ESTRPIPE)
pa_log_debug("%s: System suspended!", call);
if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) {
pa_log("%s: %s", call, pa_alsa_strerror(err));
return -1;
@ -391,6 +394,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
snd_pcm_sframes_t n;
size_t n_bytes;
int r;
pa_bool_t after_avail = TRUE;
if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
@ -463,6 +467,9 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
if (!after_avail && err == -EAGAIN)
break;
if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0)
continue;
@ -473,6 +480,12 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size)
frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size;
if (!after_avail && frames == 0)
break;
pa_assert(frames > 0);
after_avail = FALSE;
/* Check these are multiples of 8 bit */
pa_assert((areas[0].first & 7) == 0);
pa_assert((areas[0].step & 7)== 0);
@ -539,6 +552,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
snd_pcm_sframes_t n;
size_t n_bytes;
int r;
pa_bool_t after_avail = TRUE;
if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
@ -599,17 +613,26 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, (snd_pcm_uframes_t) frames);
pa_memblock_release(chunk.memblock);
pa_assert(frames != 0);
if (PA_UNLIKELY(frames < 0)) {
pa_memblock_unref(chunk.memblock);
if ((r = try_recover(u, "snd_pcm_readi", (int) (frames))) == 0)
if (!after_avail && (int) frames == -EAGAIN)
break;
if ((r = try_recover(u, "snd_pcm_readi", (int) frames)) == 0)
continue;
return r;
}
if (!after_avail && frames == 0) {
pa_memblock_unref(chunk.memblock);
break;
}
pa_assert(frames > 0);
after_avail = FALSE;
chunk.index = 0;
chunk.length = (size_t) frames * u->frame_size;
@ -834,7 +857,9 @@ static int unsuspend(struct userdata *u) {
/* FIXME: We need to reload the volume somehow */
snd_pcm_start(u->pcm_handle);
pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE);
u->read_count = 0;
pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE);
pa_log_info("Resumed successfully...");

View file

@ -233,14 +233,16 @@ int pa_alsa_set_hw_params(
goto finish;
}
if (_period_size && tsched_size && _periods) {
if (_period_size > 0 && tsched_size > 0 && _periods > 0) {
snd_pcm_uframes_t buffer_size;
unsigned int p;
/* Adjust the buffer sizes, if we didn't get the rate we were asking for */
_period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate);
tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate);
if (_use_tsched) {
snd_pcm_uframes_t buffer_size = 0;
buffer_size = 0;
if ((ret = snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size)) < 0)
pa_log_warn("snd_pcm_hw_params_get_buffer_size_max() failed: %s", pa_alsa_strerror(ret));
@ -251,32 +253,33 @@ int pa_alsa_set_hw_params(
_periods = 1;
}
if (_period_size > 0 && _periods > 0) {
snd_pcm_uframes_t buffer_size;
buffer_size = _periods * _period_size;
if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret));
}
if (_periods > 0) {
/* Some ALSA drivers really don't like if we set the buffer
* size first and the number of periods second. (which would
* make a lot more sense to me) So, follow this rule and
* adjust the periods first and the buffer size second */
/* First we pass 0 as direction to get exactly what we
* asked for. That this is necessary is presumably a bug
* in ALSA. All in all this is mostly a hint to ALSA, so
* we don't care if this fails. */
p = _periods;
dir = 0;
if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir) < 0) {
if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir) < 0) {
p = _periods;
dir = 1;
if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir) < 0) {
if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir) < 0) {
p = _periods;
dir = -1;
if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0)
if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir)) < 0)
pa_log_info("snd_pcm_hw_params_set_periods_near() failed: %s", pa_alsa_strerror(ret));
}
}
}
/* Now set the buffer size */
buffer_size = _periods * _period_size;
if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret));
}
if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)

View file

@ -0,0 +1 @@
../../../pulse/Makefile

View file

@ -0,0 +1 @@
../../../../pulse/Makefile

View file

@ -44,6 +44,10 @@ volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Speaker]
switch = off
volume = off
[Element Front]
switch = off
volume = off

View file

@ -45,6 +45,12 @@ override-map.2 = lfe,lfe
switch = off
volume = off
[Element Speaker]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Front]
switch = off
volume = off

View file

@ -42,6 +42,12 @@ override-map.2 = all-left,all-right
switch = off
volume = off
[Element Speaker]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Front]
switch = off
volume = off

View file

@ -41,6 +41,12 @@ volume = off
switch = off
volume = off
[Element Speaker]
switch = mute
volume = merge
override-map.1 = all
override-map.2 = all-left,all-right
[Element Front]
switch = mute
volume = merge

View file

@ -104,8 +104,8 @@ switch = select
[Option External Amplifier:on]
name = output-amplifier-on
priority = 0
priority = 10
[Option External Amplifier:off]
name = output-amplifier-off
priority = 10
priority = 0

View file

@ -0,0 +1 @@
../../../../pulse/Makefile

View file

@ -122,9 +122,9 @@ static pa_bool_t device_is_audio(pa_bluetooth_device *d) {
return
d->device_info_valid &&
(d->audio_state != PA_BT_AUDIO_STATE_INVALID ||
d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID ||
d->headset_state != PA_BT_AUDIO_STATE_INVALID);
(d->audio_state != PA_BT_AUDIO_STATE_INVALID &&
(d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID ||
d->headset_state != PA_BT_AUDIO_STATE_INVALID));
}
static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessageIter *i) {
@ -226,10 +226,6 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
node = uuid_new(value);
PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node);
/* this might eventually be racy if .Audio is not there yet, but the State change will come anyway later, so this call is for cold-detection mostly */
pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties"));
send_and_add_to_pending(y, d, m, get_properties_reply);
/* Vudentz said the interfaces are here when the UUIDs are announced */
if (strcasecmp(HSP_HS_UUID, value) == 0 || strcasecmp(HFP_HS_UUID, value) == 0) {
pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Headset", "GetProperties"));
@ -239,6 +235,10 @@ static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device
send_and_add_to_pending(y, d, m, get_properties_reply);
}
/* this might eventually be racy if .Audio is not there yet, but the State change will come anyway later, so this call is for cold-detection mostly */
pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties"));
send_and_add_to_pending(y, d, m, get_properties_reply);
if (!dbus_message_iter_next(&ai))
break;
}
@ -309,6 +309,17 @@ static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_b
pa_hook_fire(&y->hook, d);
}
static void remove_all_devices(pa_bluetooth_discovery *y) {
pa_bluetooth_device *d;
pa_assert(y);
while ((d = pa_hashmap_steal_first(y->devices))) {
run_callback(y, d, TRUE);
device_free(d);
}
}
static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
DBusMessage *r;
DBusMessageIter arg_i, element_i;
@ -332,6 +343,12 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties"))
d->device_info_valid = valid;
if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
pa_log_debug("Bluetooth daemon is apparently not available.");
remove_all_devices(y);
goto finish2;
}
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
if (!dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD))
@ -383,6 +400,7 @@ static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
finish:
run_callback(y, d, FALSE);
finish2:
dbus_message_unref(r);
PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
@ -412,6 +430,9 @@ static void found_device(pa_bluetooth_discovery *y, const char* path) {
pa_assert(y);
pa_assert(path);
if (pa_hashmap_get(y->devices, path))
return;
d = device_new(path);
pa_hashmap_put(y->devices, d->path, d);
@ -439,9 +460,15 @@ static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
pa_assert_se(y = p->context_data);
pa_assert_se(r = dbus_pending_call_steal_reply(pending));
if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
pa_log_debug("Bluetooth daemon is apparently not available.");
remove_all_devices(y);
goto finish;
}
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r));
goto end;
goto finish;
}
if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
@ -454,7 +481,7 @@ static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
found_device(y, paths[i]);
}
end:
finish:
if (paths)
dbus_free_string_array (paths);
@ -487,9 +514,15 @@ static void list_adapters_reply(DBusPendingCall *pending, void *userdata) {
pa_assert_se(y = p->context_data);
pa_assert_se(r = dbus_pending_call_steal_reply(pending));
if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
pa_log_debug("Bluetooth daemon is apparently not available.");
remove_all_devices(y);
goto finish;
}
if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r));
goto end;
goto finish;
}
if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
@ -502,7 +535,7 @@ static void list_adapters_reply(DBusPendingCall *pending, void *userdata) {
found_adapter(y, paths[i]);
}
end:
finish:
if (paths)
dbus_free_string_array (paths);
@ -615,6 +648,32 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
run_callback(y, d, FALSE);
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
} else if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
const char *name, *old_owner, *new_owner;
if (!dbus_message_get_args(m, &err,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_STRING, &old_owner,
DBUS_TYPE_STRING, &new_owner,
DBUS_TYPE_INVALID)) {
pa_log("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message);
goto fail;
}
if (pa_streq(name, "org.bluez")) {
if (old_owner && *old_owner) {
pa_log_debug("Bluetooth daemon disappeared.");
remove_all_devices(y);
}
if (new_owner && *new_owner) {
pa_log_debug("Bluetooth daemon appeared.");
list_adapters(y);
}
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
@ -699,6 +758,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
if (pa_dbus_add_matches(
pa_dbus_connection_get(y->connection), &err,
"type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'",
"type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
@ -734,8 +794,6 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
}
void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
pa_bluetooth_device *d;
pa_assert(y);
pa_assert(PA_REFCNT_VALUE(y) > 0);
@ -745,16 +803,13 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
pa_dbus_free_pending_list(&y->pending);
if (y->devices) {
while ((d = pa_hashmap_steal_first(y->devices))) {
run_callback(y, d, TRUE);
device_free(d);
}
remove_all_devices(y);
pa_hashmap_free(y->devices, NULL, NULL);
}
if (y->connection) {
pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
"type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'",
"type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
"type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'",
"type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",

View file

@ -59,8 +59,7 @@ typedef enum pa_bt_audio_state {
PA_BT_AUDIO_STATE_DISCONNECTED,
PA_BT_AUDIO_STATE_CONNECTING,
PA_BT_AUDIO_STATE_CONNECTED,
PA_BT_AUDIO_STATE_PLAYING,
PA_BT_AUDIO_STATE_LAST
PA_BT_AUDIO_STATE_PLAYING
} pa_bt_audio_state_t;
struct pa_bluetooth_device {

View file

@ -881,7 +881,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
*((pa_usec_t*) data) = wi > ri ? wi - ri : 0;
}
*((pa_usec_t*) data) += u->sink->fixed_latency;
*((pa_usec_t*) data) += u->sink->thread_info.fixed_latency;
return 0;
}
}
@ -943,7 +943,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
wi = pa_smoother_get(u->read_smoother, pa_rtclock_now());
ri = pa_bytes_to_usec(u->read_index, &u->sample_spec);
*((pa_usec_t*) data) = (wi > ri ? wi - ri : 0) + u->source->fixed_latency;
*((pa_usec_t*) data) = (wi > ri ? wi - ri : 0) + u->source->thread_info.fixed_latency;
return 0;
}
@ -1262,11 +1262,11 @@ static void thread_func(void *userdata) {
if (u->core->realtime_scheduling)
pa_make_realtime(u->core->realtime_priority);
pa_thread_mq_install(&u->thread_mq);
if (start_stream_fd(u) < 0)
goto fail;
pa_thread_mq_install(&u->thread_mq);
for (;;) {
struct pollfd *pollfd;
int ret;
@ -1319,11 +1319,13 @@ static void thread_func(void *userdata) {
if (u->write_index > 0 && audio_to_send > MAX_PLAYBACK_CATCH_UP_USEC) {
pa_usec_t skip_usec;
uint64_t skip_bytes;
pa_memchunk tmp;
skip_usec = audio_to_send - MAX_PLAYBACK_CATCH_UP_USEC;
skip_bytes = pa_usec_to_bytes(skip_usec, &u->sample_spec);
if (skip_bytes > 0) {
pa_memchunk tmp;
pa_log_warn("Skipping %llu us (= %llu bytes) in audio stream",
(unsigned long long) skip_usec,
(unsigned long long) skip_bytes);
@ -1332,6 +1334,7 @@ static void thread_func(void *userdata) {
pa_memblock_unref(tmp.memblock);
u->write_index += skip_bytes;
}
}
do_write = 1;
}
@ -1351,11 +1354,14 @@ static void thread_func(void *userdata) {
goto fail;
}
if (n_written == 0)
pa_log("Broken kernel: we got EAGAIN on write() after POLLOUT!");
do_write -= n_written;
writable = FALSE;
}
if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && do_write <= 0) {
if ((!u->source || !PA_SOURCE_IS_LINKED(u->source->thread_info.state)) && do_write <= 0 && writable) {
pa_usec_t time_passed, next_write_at, sleep_for;
/* Hmm, there is no input stream we could synchronize
@ -1443,12 +1449,12 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *us
if (u->sink && dbus_message_is_signal(m, "org.bluez.Headset", "SpeakerGainChanged")) {
pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
pa_sink_volume_changed(u->sink, &v, TRUE);
pa_sink_volume_changed(u->sink, &v);
} else if (u->source && dbus_message_is_signal(m, "org.bluez.Headset", "MicrophoneGainChanged")) {
pa_cvolume_set(&v, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
pa_source_volume_changed(u->source, &v, TRUE);
pa_source_volume_changed(u->source, &v);
}
}
}
@ -1733,7 +1739,8 @@ static void shutdown_bt(struct userdata *u) {
if (u->service_fd >= 0) {
pa_close(u->service_fd);
u->service_fd = -1;
u->service_write_type = u->service_write_type = 0;
u->service_write_type = 0;
u->service_read_type = 0;
}
if (u->write_memchunk.memblock) {
@ -1749,7 +1756,8 @@ static int init_bt(struct userdata *u) {
shutdown_bt(u);
u->stream_write_type = 0;
u->service_write_type = u->service_write_type = 0;
u->service_write_type = 0;
u->service_read_type = 0;
if ((u->service_fd = bt_audio_service_open()) < 0) {
pa_log_error("Couldn't connect to bluetooth audio service");
@ -2082,6 +2090,15 @@ static int add_card(struct userdata *u, const pa_bluetooth_device *device) {
u->card->userdata = u;
u->card->set_profile = card_set_profile;
d = PA_CARD_PROFILE_DATA(u->card->active_profile);
if ((device->headset_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_HSP) ||
(device->audio_sink_state < PA_BT_AUDIO_STATE_CONNECTED && *d == PROFILE_A2DP)) {
pa_log_warn("Default profile not connected, selecting off profile");
u->card->active_profile = pa_hashmap_get(u->card->profiles, "off");
u->card->save_profile = FALSE;
}
d = PA_CARD_PROFILE_DATA(u->card->active_profile);
u->profile = *d;

View file

@ -92,6 +92,8 @@ struct output {
pa_sink *sink;
pa_sink_input *sink_input;
pa_bool_t ignore_state_change;
pa_asyncmsgq *inq, /* Message queue from the sink thread to this sink input */
*outq; /* Message queue from this sink input to the sink thread */
pa_rtpoll_item *inq_rtpoll_item_read, *inq_rtpoll_item_write;
@ -99,9 +101,12 @@ struct output {
pa_memblockq *memblockq;
/* For communication of the stream latencies to the main thread */
pa_usec_t total_latency;
/* For coomunication of the stream parameters to the sink thread */
pa_atomic_t max_request;
pa_atomic_t requested_latency;
PA_LLIST_FIELDS(struct output);
};
@ -119,13 +124,12 @@ struct userdata {
uint32_t adjust_time;
pa_bool_t automatic;
pa_bool_t auto_desc;
pa_hook_slot *sink_put_slot, *sink_unlink_slot, *sink_state_changed_slot;
pa_resample_method_t resample_method;
struct timeval adjust_timestamp;
pa_usec_t block_usec;
pa_idxset* outputs; /* managed in main context */
@ -145,13 +149,16 @@ enum {
SINK_MESSAGE_REMOVE_OUTPUT,
SINK_MESSAGE_NEED,
SINK_MESSAGE_UPDATE_LATENCY,
SINK_MESSAGE_UPDATE_MAX_REQUEST
SINK_MESSAGE_UPDATE_MAX_REQUEST,
SINK_MESSAGE_UPDATE_REQUESTED_LATENCY
};
enum {
SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX,
};
static void output_disable(struct output *o);
static void output_enable(struct output *o);
static void output_free(struct output *o);
static int output_create_sink_input(struct output *o);
@ -171,7 +178,7 @@ static void adjust_rates(struct userdata *u) {
if (!PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)))
return;
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
PA_IDXSET_FOREACH(o, u->outputs, idx) {
pa_usec_t sink_latency;
if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
@ -188,6 +195,11 @@ static void adjust_rates(struct userdata *u) {
avg_total_latency += o->total_latency;
n++;
pa_log_debug("[%s] total=%0.2fms sink=%0.2fms ", o->sink->name, (double) o->total_latency / PA_USEC_PER_MSEC, (double) sink_latency / PA_USEC_PER_MSEC);
if (o->total_latency > 10*PA_USEC_PER_SEC)
pa_log_warn("[%s] Total latency of output is very high (%0.2fms), most likely the audio timing in one of your drivers is broken.", o->sink->name, (double) o->total_latency / PA_USEC_PER_MSEC);
}
if (min_total_latency == (pa_usec_t) -1)
@ -202,7 +214,7 @@ static void adjust_rates(struct userdata *u) {
base_rate = u->sink->sample_spec.rate;
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
PA_IDXSET_FOREACH(o, u->outputs, idx) {
uint32_t r = base_rate;
if (!o->sink_input || !PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
@ -214,10 +226,10 @@ static void adjust_rates(struct userdata *u) {
r += (uint32_t) ((((double) (o->total_latency - target_latency))/(double)u->adjust_time)*(double)r/PA_USEC_PER_SEC);
if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) {
pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), base_rate, r);
pa_log_warn("[%s] sample rates too different, not adjusting (%u vs. %u).", o->sink_input->sink->name, base_rate, r);
pa_sink_input_set_rate(o->sink_input, base_rate);
} else {
pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", pa_proplist_gets(o->sink_input->proplist, PA_PROP_MEDIA_NAME), r, (double) r / base_rate, (float) o->total_latency);
pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.", o->sink_input->sink->name, r, (double) r / base_rate, (float) o->total_latency);
pa_sink_input_set_rate(o->sink_input, r);
}
}
@ -354,18 +366,15 @@ static void render_memblock(struct userdata *u, struct output *o, size_t length)
u->thread_info.counter += chunk.length;
/* OK, let's send this data to the other threads */
for (j = u->thread_info.active_outputs; j; j = j->next)
PA_LLIST_FOREACH(j, u->thread_info.active_outputs) {
if (j == o)
continue;
/* Send to other outputs, which are not the requesting
* one */
if (j != o)
pa_asyncmsgq_post(j->inq, PA_MSGOBJECT(j->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, &chunk, NULL);
}
/* And place it directly into the requesting output's queue */
if (o)
pa_memblockq_push_align(o->memblockq, &chunk);
pa_memblock_unref(chunk.memblock);
}
}
@ -401,10 +410,18 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
/* If necessary, get some new data */
request_memblock(o, nbytes);
/* pa_log("%s q size is %u + %u (%u/%u)", */
/* i->sink->name, */
/* pa_memblockq_get_nblocks(o->memblockq), */
/* pa_memblockq_get_nblocks(i->thread_info.render_memblockq), */
/* pa_memblockq_get_maxrewind(o->memblockq), */
/* pa_memblockq_get_maxrewind(i->thread_info.render_memblockq)); */
if (pa_memblockq_peek(o->memblockq, chunk) < 0)
return -1;
pa_memblockq_drop(o->memblockq, chunk->length);
return 0;
}
@ -439,13 +456,35 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
return;
pa_atomic_store(&o->max_request, (int) nbytes);
pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_MAX_REQUEST, NULL, 0, NULL, NULL);
}
/* Called from thread context */
static void sink_input_update_sink_requested_latency_cb(pa_sink_input *i) {
struct output *o;
pa_usec_t c;
pa_assert(i);
pa_sink_input_assert_ref(i);
pa_assert_se(o = i->userdata);
c = pa_sink_get_requested_latency_within_thread(i->sink);
if (c == (pa_usec_t) -1)
c = i->sink->thread_info.max_latency;
if (pa_atomic_load(&o->requested_latency) == (int) c)
return;
pa_atomic_store(&o->requested_latency, (int) c);
pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_REQUESTED_LATENCY, NULL, 0, NULL, NULL);
}
/* Called from I/O thread context */
static void sink_input_attach_cb(pa_sink_input *i) {
struct output *o;
pa_usec_t c;
pa_sink_input_assert_ref(i);
pa_assert_se(o = i->userdata);
@ -454,14 +493,24 @@ static void sink_input_attach_cb(pa_sink_input *i) {
pa_assert(!o->inq_rtpoll_item_read && !o->outq_rtpoll_item_write);
o->inq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
i->sink->rtpoll,
i->sink->thread_info.rtpoll,
PA_RTPOLL_LATE, /* This one is not that important, since we check for data in _peek() anyway. */
o->inq);
o->outq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
i->sink->rtpoll,
i->sink->thread_info.rtpoll,
PA_RTPOLL_EARLY,
o->outq);
pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
pa_atomic_store(&o->max_request, (int) pa_sink_input_get_max_request(i));
c = pa_sink_get_requested_latency_within_thread(i->sink);
pa_atomic_store(&o->requested_latency, (int) (c == (pa_usec_t) -1 ? 0 : c));
pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_MAX_REQUEST, NULL, 0, NULL, NULL);
pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_REQUESTED_LATENCY, NULL, 0, NULL, NULL);
}
/* Called from I/O thread context */
@ -471,14 +520,15 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(o = i->userdata);
/* Shut down the queue from the sink thread to us */
pa_assert(o->inq_rtpoll_item_read && o->outq_rtpoll_item_write);
if (o->inq_rtpoll_item_read) {
pa_rtpoll_item_free(o->inq_rtpoll_item_read);
o->inq_rtpoll_item_read = NULL;
}
if (o->outq_rtpoll_item_write) {
pa_rtpoll_item_free(o->outq_rtpoll_item_write);
o->outq_rtpoll_item_write = NULL;
}
}
/* Called from main context */
@ -492,20 +542,6 @@ static void sink_input_kill_cb(pa_sink_input *i) {
output_free(o);
}
/* Called from IO thread context */
static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
/* If we are added for the first time, ask for a rewinding so that
* we are heard right-away. */
if (PA_SINK_INPUT_IS_LINKED(state) &&
i->thread_info.state == PA_SINK_INPUT_INIT)
pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
}
/* Called from thread context */
static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) {
struct output *o = PA_SINK_INPUT(obj)->userdata;
@ -535,37 +571,6 @@ static int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64
return pa_sink_input_process_msg(obj, code, data, offset, chunk);
}
/* Called from main context */
static void disable_output(struct output *o) {
pa_assert(o);
if (!o->sink_input)
return;
pa_sink_input_unlink(o->sink_input);
pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
pa_sink_input_unref(o->sink_input);
o->sink_input = NULL;
}
/* Called from main context */
static void enable_output(struct output *o) {
pa_assert(o);
if (o->sink_input)
return;
if (output_create_sink_input(o) >= 0) {
pa_memblockq_flush_write(o->memblockq);
pa_sink_input_put(o->sink_input);
if (o->userdata->sink && PA_SINK_IS_LINKED(pa_sink_get_state(o->userdata->sink)))
pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
}
}
/* Called from main context */
static void suspend(struct userdata *u) {
struct output *o;
@ -574,8 +579,8 @@ static void suspend(struct userdata *u) {
pa_assert(u);
/* Let's suspend by unlinking all streams */
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
disable_output(o);
PA_IDXSET_FOREACH(o, u->outputs, idx)
output_disable(o);
pa_log_info("Device suspended...");
}
@ -588,13 +593,8 @@ static void unsuspend(struct userdata *u) {
pa_assert(u);
/* Let's resume */
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
pa_sink_suspend(o->sink, FALSE, PA_SUSPEND_IDLE);
if (PA_SINK_IS_OPENED(pa_sink_get_state(o->sink)))
enable_output(o);
}
PA_IDXSET_FOREACH(o, u->outputs, idx)
output_enable(o);
pa_log_info("Resumed successfully...");
}
@ -638,7 +638,13 @@ static void update_max_request(struct userdata *u) {
size_t max_request = 0;
struct output *o;
for (o = u->thread_info.active_outputs; o; o = o->next) {
pa_assert(u);
pa_sink_assert_io_context(u->sink);
/* Collects the max_request values of all streams and sets the
* largest one locally */
PA_LLIST_FOREACH(o, u->thread_info.active_outputs) {
size_t mr = (size_t) pa_atomic_load(&o->max_request);
if (mr > max_request)
@ -651,6 +657,67 @@ static void update_max_request(struct userdata *u) {
pa_sink_set_max_request_within_thread(u->sink, max_request);
}
/* Called from IO context */
static void update_fixed_latency(struct userdata *u) {
pa_usec_t fixed_latency = 0;
struct output *o;
pa_assert(u);
pa_sink_assert_io_context(u->sink);
/* Collects the requested_latency values of all streams and sets
* the largest one as fixed_latency locally */
PA_LLIST_FOREACH(o, u->thread_info.active_outputs) {
pa_usec_t rl = (size_t) pa_atomic_load(&o->requested_latency);
if (rl > fixed_latency)
fixed_latency = rl;
}
if (fixed_latency <= 0)
fixed_latency = u->block_usec;
pa_sink_set_fixed_latency_within_thread(u->sink, fixed_latency);
}
/* Called from thread context of the io thread */
static void output_add_within_thread(struct output *o) {
pa_assert(o);
pa_sink_assert_io_context(o->sink);
PA_LLIST_PREPEND(struct output, o->userdata->thread_info.active_outputs, o);
pa_assert(!o->outq_rtpoll_item_read && !o->inq_rtpoll_item_write);
o->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
o->userdata->rtpoll,
PA_RTPOLL_EARLY-1, /* This item is very important */
o->outq);
o->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
o->userdata->rtpoll,
PA_RTPOLL_EARLY,
o->inq);
}
/* Called from thread context of the io thread */
static void output_remove_within_thread(struct output *o) {
pa_assert(o);
pa_sink_assert_io_context(o->sink);
PA_LLIST_REMOVE(struct output, o->userdata->thread_info.active_outputs, o);
if (o->outq_rtpoll_item_read) {
pa_rtpoll_item_free(o->outq_rtpoll_item_read);
o->outq_rtpoll_item_read = NULL;
}
if (o->inq_rtpoll_item_write) {
pa_rtpoll_item_free(o->inq_rtpoll_item_write);
o->inq_rtpoll_item_write = NULL;
}
}
/* Called from thread context of the io thread */
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;
@ -683,42 +750,17 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return 0;
}
case SINK_MESSAGE_ADD_OUTPUT: {
struct output *op = data;
PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, op);
pa_assert(!op->outq_rtpoll_item_read && !op->inq_rtpoll_item_write);
op->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
u->rtpoll,
PA_RTPOLL_EARLY-1, /* This item is very important */
op->outq);
op->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
u->rtpoll,
PA_RTPOLL_EARLY,
op->inq);
case SINK_MESSAGE_ADD_OUTPUT:
output_add_within_thread(data);
update_max_request(u);
update_fixed_latency(u);
return 0;
}
case SINK_MESSAGE_REMOVE_OUTPUT: {
struct output *op = data;
PA_LLIST_REMOVE(struct output, u->thread_info.active_outputs, op);
pa_assert(op->outq_rtpoll_item_read && op->inq_rtpoll_item_write);
pa_rtpoll_item_free(op->outq_rtpoll_item_read);
op->outq_rtpoll_item_read = NULL;
pa_rtpoll_item_free(op->inq_rtpoll_item_write);
op->inq_rtpoll_item_write = NULL;
case SINK_MESSAGE_REMOVE_OUTPUT:
output_remove_within_thread(data);
update_max_request(u);
update_fixed_latency(u);
return 0;
}
case SINK_MESSAGE_NEED:
render_memblock(u, (struct output*) data, (size_t) offset);
@ -740,10 +782,13 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
}
case SINK_MESSAGE_UPDATE_MAX_REQUEST:
update_max_request(u);
break;
}
case SINK_MESSAGE_UPDATE_REQUESTED_LATENCY:
update_fixed_latency(u);
break;
}
return pa_sink_process_msg(o, code, data, offset, chunk);
}
@ -756,6 +801,9 @@ static void update_description(struct userdata *u) {
pa_assert(u);
if (!u->auto_desc)
return;
if (pa_idxset_isempty(u->outputs)) {
pa_sink_set_description(u->sink, "Simultaneous output");
return;
@ -763,7 +811,7 @@ static void update_description(struct userdata *u) {
t = pa_xstrdup("Simultaneous output to");
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx)) {
PA_IDXSET_FOREACH(o, u->outputs, idx) {
char *e;
if (first) {
@ -798,7 +846,7 @@ static int output_create_sink_input(struct output *o) {
data.module = o->userdata->module;
data.resample_method = o->userdata->resample_method;
pa_sink_input_new(&o->sink_input, o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE);
pa_sink_input_new(&o->sink_input, o->userdata->core, &data, PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE|PA_SINK_INPUT_NO_CREATE_ON_SUSPEND);
pa_sink_input_new_data_done(&data);
@ -808,9 +856,9 @@ static int output_create_sink_input(struct output *o) {
o->sink_input->parent.process_msg = sink_input_process_msg;
o->sink_input->pop = sink_input_pop_cb;
o->sink_input->process_rewind = sink_input_process_rewind_cb;
o->sink_input->state_change = sink_input_state_change_cb;
o->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
o->sink_input->update_max_request = sink_input_update_max_request_cb;
o->sink_input->update_sink_requested_latency = sink_input_update_sink_requested_latency_cb;
o->sink_input->attach = sink_input_attach_cb;
o->sink_input->detach = sink_input_detach_cb;
o->sink_input->kill = sink_input_kill_cb;
@ -821,22 +869,19 @@ static int output_create_sink_input(struct output *o) {
return 0;
}
/* Called from main context */
static struct output *output_new(struct userdata *u, pa_sink *sink) {
struct output *o;
pa_sink_state_t state;
pa_assert(u);
pa_assert(sink);
pa_assert(u->sink);
o = pa_xnew(struct output, 1);
o = pa_xnew0(struct output, 1);
o->userdata = u;
o->inq = pa_asyncmsgq_new(0);
o->outq = pa_asyncmsgq_new(0);
o->inq_rtpoll_item_write = o->inq_rtpoll_item_read = NULL;
o->outq_rtpoll_item_write = o->outq_rtpoll_item_read = NULL;
o->sink = sink;
o->sink_input = NULL;
o->memblockq = pa_memblockq_new(
0,
MEMBLOCKQ_MAXLENGTH,
@ -846,53 +891,31 @@ static struct output *output_new(struct userdata *u, pa_sink *sink) {
0,
0,
NULL);
pa_atomic_store(&o->max_request, 0);
PA_LLIST_INIT(struct output, o);
pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0);
state = pa_sink_get_state(u->sink);
if (state != PA_SINK_INIT)
pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
else {
/* If the sink is not yet started, we need to do the activation ourselves */
PA_LLIST_PREPEND(struct output, u->thread_info.active_outputs, o);
o->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read(
u->rtpoll,
PA_RTPOLL_EARLY-1, /* This item is very important */
o->outq);
o->inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write(
u->rtpoll,
PA_RTPOLL_EARLY,
o->inq);
}
if (PA_SINK_IS_OPENED(state) || state == PA_SINK_INIT) {
pa_sink_suspend(sink, FALSE, PA_SUSPEND_IDLE);
if (PA_SINK_IS_OPENED(pa_sink_get_state(sink)))
if (output_create_sink_input(o) < 0)
goto fail;
}
update_description(u);
return o;
}
fail:
/* Called from main context */
static void output_free(struct output *o) {
pa_assert(o);
if (o) {
pa_idxset_remove_by_data(u->outputs, o, NULL);
output_disable(o);
if (o->sink_input) {
pa_sink_input_unlink(o->sink_input);
pa_sink_input_unref(o->sink_input);
}
pa_assert_se(pa_idxset_remove_by_data(o->userdata->outputs, o, NULL));
update_description(o->userdata);
if (o->memblockq)
pa_memblockq_free(o->memblockq);
if (o->inq_rtpoll_item_read)
pa_rtpoll_item_free(o->inq_rtpoll_item_read);
if (o->inq_rtpoll_item_write)
pa_rtpoll_item_free(o->inq_rtpoll_item_write);
if (o->outq_rtpoll_item_read)
pa_rtpoll_item_free(o->outq_rtpoll_item_read);
if (o->outq_rtpoll_item_write)
pa_rtpoll_item_free(o->outq_rtpoll_item_write);
if (o->inq)
pa_asyncmsgq_unref(o->inq);
@ -900,30 +923,103 @@ fail:
if (o->outq)
pa_asyncmsgq_unref(o->outq);
pa_xfree(o);
}
if (o->memblockq)
pa_memblockq_free(o->memblockq);
return NULL;
pa_xfree(o);
}
/* Called from main context */
static void output_enable(struct output *o) {
pa_assert(o);
if (o->sink_input)
return;
/* This might cause the sink to be resumed. The state change hook
* of the sink might hence be called from here, which might then
* cause us to be called in a loop. Make sure that state changes
* for this output don't cause this loop by setting a flag here */
o->ignore_state_change = TRUE;
if (output_create_sink_input(o) >= 0) {
if (pa_sink_get_state(o->sink) != PA_SINK_INIT) {
/* First we register the output. That means that the sink
* will start to pass data to this output. */
pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL);
/* Then we enable the sink input. That means that the sink
* is now asked for new data. */
pa_sink_input_put(o->sink_input);
} else
/* Hmm the sink is not yet started, do things right here */
output_add_within_thread(o);
}
o->ignore_state_change = FALSE;
}
/* Called from main context */
static void output_disable(struct output *o) {
pa_assert(o);
if (!o->sink_input)
return;
/* First we disable the sink input. That means that the sink is
* not asked for new data anymore */
pa_sink_input_unlink(o->sink_input);
/* Then we unregister the output. That means that the sink doesn't
* pass any further data to this output */
pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL);
/* Now dellocate the stream */
pa_sink_input_unref(o->sink_input);
o->sink_input = NULL;
/* Finally, drop all queued data */
pa_memblockq_flush_write(o->memblockq);
pa_asyncmsgq_flush(o->inq, FALSE);
pa_asyncmsgq_flush(o->outq, FALSE);
}
/* Called from main context */
static void output_verify(struct output *o) {
pa_assert(o);
if (PA_SINK_IS_OPENED(pa_sink_get_state(o->userdata->sink)))
output_enable(o);
else
output_disable(o);
}
/* Called from main context */
static pa_bool_t is_suitable_sink(struct userdata *u, pa_sink *s) {
const char *t;
pa_sink_assert_ref(s);
if (!(s->flags & PA_SINK_HARDWARE))
return FALSE;
if (s == u->sink)
return FALSE;
if (!(s->flags & PA_SINK_HARDWARE))
return FALSE;
if (!(s->flags & PA_SINK_LATENCY))
return FALSE;
if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS)))
if (strcmp(t, "sound"))
if (!pa_streq(t, "sound"))
return FALSE;
return TRUE;
}
/* Called from main context */
static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
struct output *o;
@ -936,18 +1032,17 @@ static pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata
return PA_HOOK_OK;
pa_log_info("Configuring new sink: %s", s->name);
if (!(o = output_new(u, s))) {
pa_log("Failed to create sink input on sink '%s'.", s->name);
return PA_HOOK_OK;
}
if (o->sink_input)
pa_sink_input_put(o->sink_input);
output_verify(o);
return PA_HOOK_OK;
}
/* Called from main context */
static struct output* find_output(struct userdata *u, pa_sink *s) {
struct output *o;
uint32_t idx;
@ -958,13 +1053,14 @@ static struct output* find_output(struct userdata *u, pa_sink *s) {
if (u->sink == s)
return NULL;
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
PA_IDXSET_FOREACH(o, u->outputs, idx)
if (o->sink == s)
return o;
return NULL;
}
/* Called from main context */
static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
struct output *o;
@ -976,26 +1072,25 @@ static pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userd
return PA_HOOK_OK;
pa_log_info("Unconfiguring sink: %s", s->name);
output_free(o);
return PA_HOOK_OK;
}
/* Called from main context */
static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) {
struct output *o;
pa_sink_state_t state;
if (!(o = find_output(u, s)))
return PA_HOOK_OK;
state = pa_sink_get_state(s);
/* This state change might be triggered because we are creating a
* stream here, in that case we don't want to create it a second
* time here and enter a loop */
if (o->ignore_state_change)
return PA_HOOK_OK;
if (PA_SINK_IS_OPENED(state) && PA_SINK_IS_OPENED(pa_sink_get_state(u->sink)) && !o->sink_input)
enable_output(o);
if (state == PA_SINK_SUSPENDED && o->sink_input)
disable_output(o);
output_verify(o);
return PA_HOOK_OK;
}
@ -1025,18 +1120,14 @@ int pa__init(pa_module*m) {
}
}
m->userdata = u = pa_xnew(struct userdata, 1);
m->userdata = u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
u->sink = NULL;
u->time_event = NULL;
u->adjust_time = DEFAULT_ADJUST_TIME;
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
u->thread = NULL;
u->resample_method = resample_method;
u->outputs = pa_idxset_new(NULL, NULL);
memset(&u->adjust_timestamp, 0, sizeof(u->adjust_timestamp));
u->sink_put_slot = u->sink_unlink_slot = u->sink_state_changed_slot = NULL;
PA_LLIST_HEAD_INIT(struct output, u->thread_info.active_outputs);
pa_atomic_store(&u->thread_info.running, FALSE);
@ -1061,6 +1152,55 @@ int pa__init(pa_module*m) {
ss = m->core->default_sample_spec;
map = m->core->default_channel_map;
/* Check the specified slave sinks for sample_spec and channel_map to use for the combined sink */
if (!u->automatic) {
const char*split_state = NULL;
char *n = NULL;
pa_sample_spec slaves_spec;
pa_channel_map slaves_map;
pa_bool_t is_first_slave = TRUE;
while ((n = pa_split(slaves, ",", &split_state))) {
pa_sink *slave_sink;
if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK))) {
pa_log("Invalid slave sink '%s'", n);
pa_xfree(n);
goto fail;
}
pa_xfree(n);
if (is_first_slave) {
slaves_spec = slave_sink->sample_spec;
slaves_map = slave_sink->channel_map;
is_first_slave = FALSE;
} else {
if (slaves_spec.format != slave_sink->sample_spec.format)
slaves_spec.format = PA_SAMPLE_INVALID;
if (slaves_spec.rate < slave_sink->sample_spec.rate)
slaves_spec.rate = slave_sink->sample_spec.rate;
if (!pa_channel_map_equal(&slaves_map, &slave_sink->channel_map))
slaves_spec.channels = 0;
}
}
if (!is_first_slave) {
if (slaves_spec.format != PA_SAMPLE_INVALID)
ss.format = slaves_spec.format;
ss.rate = slaves_spec.rate;
if (slaves_spec.channels > 0) {
map = slaves_map;
ss.channels = slaves_map.channels;
}
}
}
if ((pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0)) {
pa_log("Invalid sample specification.");
goto fail;
@ -1073,7 +1213,6 @@ int pa__init(pa_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_sink_new_data_set_channel_map(&data, &map);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Simultaneous Output");
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "filter");
if (slaves)
@ -1085,6 +1224,13 @@ int pa__init(pa_module*m) {
goto fail;
}
/* Check proplist for a description & fill in a default value if not */
u->auto_desc = FALSE;
if (NULL == pa_proplist_gets(data.proplist, PA_PROP_DEVICE_DESCRIPTION)) {
u->auto_desc = TRUE;
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Simultaneous Output");
}
u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY);
pa_sink_new_data_done(&data);
@ -1138,7 +1284,7 @@ int pa__init(pa_module*m) {
/* We're in automatic mode, we add every sink that matches our needs */
for (s = pa_idxset_first(m->core->sinks, &idx); s; s = pa_idxset_next(m->core->sinks, &idx)) {
PA_IDXSET_FOREACH(s, m->core->sinks, idx) {
if (!is_suitable_sink(u, s))
continue;
@ -1163,9 +1309,8 @@ int pa__init(pa_module*m) {
/* Activate the sink and the sink inputs */
pa_sink_put(u->sink);
for (o = pa_idxset_first(u->outputs, &idx); o; o = pa_idxset_next(u->outputs, &idx))
if (o->sink_input)
pa_sink_input_put(o->sink_input);
PA_IDXSET_FOREACH(o, u->outputs, idx)
output_verify(o);
if (u->adjust_time > 0)
u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time * PA_USEC_PER_SEC, time_callback, u);
@ -1184,37 +1329,6 @@ fail:
return -1;
}
static void output_free(struct output *o) {
pa_assert(o);
disable_output(o);
pa_assert_se(pa_idxset_remove_by_data(o->userdata->outputs, o, NULL));
update_description(o->userdata);
if (o->inq_rtpoll_item_read)
pa_rtpoll_item_free(o->inq_rtpoll_item_read);
if (o->inq_rtpoll_item_write)
pa_rtpoll_item_free(o->inq_rtpoll_item_write);
if (o->outq_rtpoll_item_read)
pa_rtpoll_item_free(o->outq_rtpoll_item_read);
if (o->outq_rtpoll_item_write)
pa_rtpoll_item_free(o->outq_rtpoll_item_write);
if (o->inq)
pa_asyncmsgq_unref(o->inq);
if (o->outq)
pa_asyncmsgq_unref(o->outq);
if (o->memblockq)
pa_memblockq_free(o->memblockq);
pa_xfree(o);
}
void pa__done(pa_module*m) {
struct userdata *u;
struct output *o;

View file

@ -119,7 +119,7 @@ static int detect_alsa(pa_core *c, int just_one) {
}
#endif
#ifdef HAVE_OSS
#ifdef HAVE_OSS_OUTPUT
static int detect_oss(pa_core *c, int just_one) {
FILE *f;
int n = 0, b = 0;
@ -240,7 +240,7 @@ int pa__init(pa_module*m) {
#ifdef HAVE_ALSA
if ((n = detect_alsa(m->core, just_one)) <= 0)
#endif
#ifdef HAVE_OSS
#ifdef HAVE_OSS_OUTPUT
if ((n = detect_oss(m->core, just_one)) <= 0)
#endif
#ifdef HAVE_SOLARIS

View file

@ -0,0 +1,84 @@
/***
This file is part of PulseAudio.
Copyright 2009 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
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 <pulse/xmalloc.h>
#include <pulsecore/module.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
#include <pulsecore/core-util.h>
#include "module-hal-detect-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Compatibility module");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!");
static const char* const valid_modargs[] = {
"api",
"tsched",
"subdevices",
NULL,
};
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
pa_bool_t tsched = TRUE;
pa_module *n;
char *t;
pa_assert(m);
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments");
goto fail;
}
if (pa_modargs_get_value_boolean(ma, "tsched", &tsched) < 0) {
pa_log("tsched= expects boolean arguments");
goto fail;
}
pa_log_warn("We will now load module-udev-detect. Please make sure to remove module-hal-detect from your configuration.");
t = pa_sprintf_malloc("tsched=%s", pa_yes_no(tsched));
n = pa_module_load(m->core, "module-udev-detect", t);
pa_xfree(t);
if (n)
pa_module_unload_request(m, TRUE);
pa_modargs_free(ma);
return n ? 0 : -1;
fail:
if (ma)
pa_modargs_free(ma);
return -1;
}

View file

@ -55,14 +55,16 @@ PA_MODULE_AUTHOR("Shahms King");
PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
#if defined(HAVE_ALSA) && defined(HAVE_OSS)
#if defined(HAVE_ALSA) && defined(HAVE_OSS_OUTPUT)
PA_MODULE_USAGE("api=<alsa or oss> "
"tsched=<enable system timer based scheduling mode?>");
"tsched=<enable system timer based scheduling mode?>"
"subdevices=<init all subdevices>");
#elif defined(HAVE_ALSA)
PA_MODULE_USAGE("api=<alsa> "
"tsched=<enable system timer based scheduling mode?>");
#elif defined(HAVE_OSS)
PA_MODULE_USAGE("api=<oss>");
#elif defined(HAVE_OSS_OUTPUT)
PA_MODULE_USAGE("api=<oss>"
"subdevices=<init all subdevices>");
#endif
PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-hal-detect!");
@ -82,6 +84,9 @@ struct userdata {
#ifdef HAVE_ALSA
pa_bool_t use_tsched;
#endif
#ifdef HAVE_OSS_OUTPUT
pa_bool_t init_subdevs;
#endif
};
#define CAPABILITY_ALSA "alsa"
@ -91,6 +96,9 @@ static const char* const valid_modargs[] = {
"api",
#ifdef HAVE_ALSA
"tsched",
#endif
#ifdef HAVE_OSS_OUTPUT
"subdevices",
#endif
NULL
};
@ -262,9 +270,9 @@ fail:
#endif
#ifdef HAVE_OSS
#ifdef HAVE_OSS_OUTPUT
static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi) {
static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi, pa_bool_t init_subdevices) {
char *class = NULL, *dev = NULL, *e;
int device;
pa_bool_t r = FALSE;
@ -294,7 +302,7 @@ static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi)
/* We only care for the main device */
device = libhal_device_get_property_int(context, udi, "oss.device", &error);
if (dbus_error_is_set(&error) || device != 0)
if (dbus_error_is_set(&error) || (device != 0 && init_subdevices == FALSE))
goto finish;
r = TRUE;
@ -324,7 +332,7 @@ static int hal_device_load_oss(struct userdata *u, const char *udi, struct devic
pa_assert(d);
/* We only care for OSS PCM devices */
if (!hal_oss_device_is_pcm(u->context, udi))
if (!hal_oss_device_is_pcm(u->context, udi, u->init_subdevs))
goto fail;
/* We store only one entry per card, hence we look for the originating device */
@ -394,7 +402,7 @@ static struct device* hal_device_add(struct userdata *u, const char *udi) {
if (pa_streq(u->capability, CAPABILITY_ALSA))
r = hal_device_load_alsa(u, udi, d);
#endif
#ifdef HAVE_OSS
#ifdef HAVE_OSS_OUTPUT
if (pa_streq(u->capability, CAPABILITY_OSS))
r = hal_device_load_oss(u, udi, d);
#endif
@ -753,7 +761,7 @@ int pa__init(pa_module*m) {
api = pa_modargs_get_value(ma, "api", "oss");
#endif
#ifdef HAVE_OSS
#ifdef HAVE_OSS_OUTPUT
if (pa_streq(api, "oss"))
u->capability = CAPABILITY_OSS;
#endif
@ -763,6 +771,13 @@ int pa__init(pa_module*m) {
goto fail;
}
#ifdef HAVE_OSS_OUTPUT
if (pa_modargs_get_value_boolean(ma, "subdevices", &u->init_subdevs) < 0) {
pa_log("Failed to parse subdevices= argument.");
goto fail;
}
#endif
if (!(u->connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
goto fail;

View file

@ -127,6 +127,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
if (s == def)
continue;
if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)))
continue;
if (role_match(s->proplist, role)) {
new_data->sink = s;
new_data->save_sink = FALSE;
@ -173,6 +176,9 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
if (s == def)
continue;
if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
continue;
if (role_match(s->proplist, role)) {
new_data->source = s;
new_data->save_source = FALSE;
@ -201,6 +207,17 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct
if (si->save_sink)
continue;
/* Skip this if it is already in the process of being moved
* anyway */
if (!si->sink)
continue;
/* It might happen that a stream and a sink are set up at the
same time, in which case we want to make sure we don't
interfere with that */
if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
continue;
if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
continue;
@ -237,6 +254,17 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source,
if (so->direct_on_input)
continue;
/* Skip this if it is already in the process of being moved
* anyway */
if (!so->source)
continue;
/* It might happen that a stream and a source are set up at the
same time, in which case we want to make sure we don't
interfere with that */
if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
continue;
if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
continue;
@ -275,26 +303,30 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
uint32_t jdx;
pa_sink *d;
if (!si->sink)
continue;
if (!(role = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_ROLE)))
continue;
/* Would the default sink fit? If so, let's use it */
if (def != sink && role_match(def->proplist, role)) {
pa_sink_input_move_to(si, def, FALSE);
if (def != sink && role_match(def->proplist, role))
if (pa_sink_input_move_to(si, def, FALSE) >= 0)
continue;
}
/* Try to find some other fitting sink */
PA_IDXSET_FOREACH(d, c->sinks, jdx) {
if (d == def || d == sink)
continue;
if (role_match(d->proplist, role)) {
pa_sink_input_move_to(si, d, FALSE);
if (!PA_SINK_IS_LINKED(pa_sink_get_state(d)))
continue;
if (role_match(d->proplist, role))
if (pa_sink_input_move_to(si, d, FALSE) >= 0)
break;
}
}
}
return PA_HOOK_OK;
}
@ -325,6 +357,9 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
if (so->direct_on_input)
continue;
if (!so->source)
continue;
if (!(role = pa_proplist_gets(so->proplist, PA_PROP_MEDIA_ROLE)))
continue;
@ -339,6 +374,9 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
if (d == def || d == source)
continue;
if (!PA_SOURCE_IS_LINKED(pa_source_get_state(d)))
continue;
if (role_match(d->proplist, role) && !source->monitor_of == !d->monitor_of) {
pa_source_output_move_to(so, d, FALSE);
break;

View file

@ -64,10 +64,9 @@ PA_MODULE_USAGE(
#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
struct userdata {
pa_core *core;
pa_module *module;
pa_sink *sink, *master;
pa_sink *sink;
pa_sink_input *sink_input;
const LADSPA_Descriptor *descriptor;
@ -105,19 +104,26 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
switch (code) {
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t usec = 0;
case PA_SINK_MESSAGE_GET_LATENCY:
/* Get the latency of the master sink */
if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
usec = 0;
/* Add the latency internal to our sink input on top */
usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
*((pa_usec_t*) data) = usec;
/* The sink is _put() before the sink input is, so let's
* make sure we don't access it in that time. Also, the
* sink input is first shut down, the sink second. */
if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
!PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) {
*((pa_usec_t*) data) = 0;
return 0;
}
*((pa_usec_t*) data) =
/* Get the latency of the master sink */
pa_sink_get_latency_within_thread(u->sink_input->sink) +
/* Add the latency internal to our sink input on top */
pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
return 0;
}
return pa_sink_process_msg(o, code, data, offset, chunk);
@ -130,12 +136,11 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
if (PA_SINK_IS_LINKED(state) &&
u->sink_input &&
PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
if (!PA_SINK_IS_LINKED(state) ||
!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
return 0;
pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
return 0;
}
@ -146,6 +151,10 @@ static void sink_request_rewind(pa_sink *s) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
!PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
return;
/* Just hand this one over to the master sink */
pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE, FALSE);
}
@ -157,6 +166,10 @@ static void sink_update_requested_latency(pa_sink *s) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
!PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
return;
/* Just hand this one over to the master sink */
pa_sink_input_set_requested_latency_within_thread(
u->sink_input,
@ -175,8 +188,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
pa_assert(chunk);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return -1;
/* Hmm, process any rewind request that might be queued up */
pa_sink_process_rewind(u->sink, 0);
while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) {
pa_memchunk nchunk;
@ -225,9 +238,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return;
if (u->sink->thread_info.rewind_nbytes > 0) {
size_t max_rewrite;
@ -263,9 +273,6 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
return;
pa_memblockq_set_maxrewind(u->memblockq, nbytes);
pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
}
@ -277,9 +284,6 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
return;
pa_sink_set_max_request_within_thread(u->sink, nbytes);
}
@ -290,12 +294,19 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
return;
pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
}
/* Called from I/O thread context */
static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
}
/* Called from I/O thread context */
static void sink_input_detach_cb(pa_sink_input *i) {
struct userdata *u;
@ -303,11 +314,8 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
return;
pa_sink_detach_within_thread(u->sink);
pa_sink_set_asyncmsgq(u->sink, NULL);
pa_sink_set_rtpoll(u->sink, NULL);
}
@ -318,14 +326,13 @@ static void sink_input_attach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
return;
pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll);
pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
pa_sink_set_max_request_within_thread(u->sink, pa_sink_input_get_max_request(i));
pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq);
pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
pa_sink_attach_within_thread(u->sink);
pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
}
/* Called from main context */
@ -335,13 +342,17 @@ static void sink_input_kill_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_unlink(u->sink);
/* The order here matters! We first kill the sink input, followed
* by the sink. That means the sink callbacks must be protected
* against an unconnected sink input! */
pa_sink_input_unlink(u->sink_input);
pa_sink_unlink(u->sink);
pa_sink_input_unref(u->sink_input);
u->sink_input = NULL;
pa_sink_unref(u->sink);
u->sink = NULL;
pa_sink_input_unref(u->sink_input);
u->sink_input = NULL;
pa_module_unload_request(u->module, TRUE);
}
@ -372,6 +383,17 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
return u->sink != dest;
}
/* Called from main context */
static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
}
int pa__init(pa_module*m) {
struct userdata *u;
pa_sample_spec ss;
@ -392,7 +414,7 @@ int pa__init(pa_module*m) {
pa_assert(m);
pa_assert(sizeof(LADSPA_Data) == sizeof(float));
pa_assert_cc(sizeof(LADSPA_Data) == sizeof(float));
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
@ -425,12 +447,8 @@ int pa__init(pa_module*m) {
cdata = pa_modargs_get_value(ma, "control", NULL);
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
m->userdata = u;
u->master = master;
u->sink = NULL;
u->sink_input = NULL;
u->memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&ss), 1, 1, 0, NULL);
if (!(e = getenv("LADSPA_PATH")))
@ -694,11 +712,10 @@ int pa__init(pa_module*m) {
sink_data.module = m;
if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
sink_data.name = pa_sprintf_malloc("%s.ladspa", master->name);
sink_data.namereg_fail = FALSE;
pa_sink_new_data_set_sample_spec(&sink_data, &ss);
pa_sink_new_data_set_channel_map(&sink_data, &map);
z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION);
pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", label, z ? z : master->name);
pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "LADSPA Plugin %s on %s", d->Name, z ? z : master->name);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
pa_proplist_sets(sink_data.proplist, "device.ladspa.module", plugin);
@ -714,7 +731,7 @@ int pa__init(pa_module*m) {
goto fail;
}
u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY);
u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY));
pa_sink_new_data_done(&sink_data);
if (!u->sink) {
@ -729,19 +746,18 @@ int pa__init(pa_module*m) {
u->sink->userdata = u;
pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
pa_sink_set_rtpoll(u->sink, master->rtpoll);
/* Create sink input */
pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__;
sink_input_data.module = m;
sink_input_data.sink = u->master;
sink_input_data.sink = master;
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE);
pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, 0);
pa_sink_input_new_data_done(&sink_input_data);
if (!u->sink_input)
@ -752,11 +768,13 @@ int pa__init(pa_module*m) {
u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
u->sink_input->update_max_request = sink_input_update_max_request_cb;
u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb;
u->sink_input->kill = sink_input_kill_cb;
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->moving = sink_input_moving_cb;
u->sink_input->userdata = u;
pa_sink_put(u->sink);
@ -797,15 +815,20 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata))
return;
if (u->sink) {
pa_sink_unlink(u->sink);
pa_sink_unref(u->sink);
}
/* See comments in sink_input_kill_cb() above regarding
* destruction order! */
if (u->sink_input) {
if (u->sink_input)
pa_sink_input_unlink(u->sink_input);
if (u->sink)
pa_sink_unlink(u->sink);
if (u->sink_input)
pa_sink_input_unref(u->sink_input);
}
if (u->sink)
pa_sink_unref(u->sink);
for (c = 0; c < u->channels; c++)
if (u->handle[c]) {

View file

@ -122,7 +122,7 @@ static int process_render(struct userdata *u) {
pa_assert(u);
if (u->memchunk.length <= 0)
pa_sink_render(u->sink, PIPE_BUF, &u->memchunk);
pa_sink_render(u->sink, pa_pipe_buf(u->fd), &u->memchunk);
pa_assert(u->memchunk.length > 0);
@ -299,8 +299,8 @@ int pa__init(pa_module*m) {
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
pa_sink_set_rtpoll(u->sink, u->rtpoll);
pa_sink_set_max_request(u->sink, PIPE_BUF);
pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(PIPE_BUF, &u->sink->sample_spec));
pa_sink_set_max_request(u->sink, pa_pipe_buf(u->fd));
pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec(pa_pipe_buf(u->fd), &u->sink->sample_spec));
u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);

View file

@ -142,7 +142,7 @@ static void thread_func(void *userdata) {
void *p;
if (!u->memchunk.memblock) {
u->memchunk.memblock = pa_memblock_new(u->core->mempool, PIPE_BUF);
u->memchunk.memblock = pa_memblock_new(u->core->mempool, pa_pipe_buf(u->fd));
u->memchunk.index = u->memchunk.length = 0;
}

View file

@ -1,7 +1,7 @@
/***
This file is part of PulseAudio.
Copyright 2004-2008 Lennart Poettering
Copyright 2004-2009 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
@ -48,16 +48,15 @@ PA_MODULE_USAGE(
"master=<name of sink to remap> "
"master_channel_map=<channel map> "
"format=<sample format> "
"channels=<number of channels> "
"rate=<sample rate> "
"channels=<number of channels> "
"channel_map=<channel map> "
"remix=<remix channels?>");
struct userdata {
pa_core *core;
pa_module *module;
pa_sink *sink, *master;
pa_sink *sink;
pa_sink_input *sink_input;
};
@ -80,19 +79,24 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
switch (code) {
case PA_SINK_MESSAGE_GET_LATENCY: {
pa_usec_t usec = 0;
case PA_SINK_MESSAGE_GET_LATENCY:
/* Get the latency of the master sink */
if (PA_MSGOBJECT(u->master)->process_msg(PA_MSGOBJECT(u->master), PA_SINK_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
usec = 0;
/* Add the latency internal to our sink input on top */
usec += pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->master->sample_spec);
*((pa_usec_t*) data) = usec;
/* The sink is _put() before the sink input is, so let's
* make sure we don't access it yet */
if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
!PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) {
*((pa_usec_t*) data) = 0;
return 0;
}
*((pa_usec_t*) data) =
/* Get the latency of the master sink */
pa_sink_get_latency_within_thread(u->sink_input->sink) +
/* Add the latency internal to our sink input on top */
pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec);
return 0;
}
return pa_sink_process_msg(o, code, data, offset, chunk);
@ -105,12 +109,11 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
if (PA_SINK_IS_LINKED(state) &&
u->sink_input &&
PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
if (!PA_SINK_IS_LINKED(state) ||
!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input)))
return 0;
pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED);
return 0;
}
@ -121,6 +124,10 @@ static void sink_request_rewind(pa_sink *s) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
!PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
return;
pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE, FALSE);
}
@ -131,6 +138,10 @@ static void sink_update_requested_latency(pa_sink *s) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) ||
!PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state))
return;
/* Just hand this one over to the master sink */
pa_sink_input_set_requested_latency_within_thread(
u->sink_input,
@ -145,8 +156,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
pa_assert(chunk);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return -1;
/* Hmm, process any rewind request that might be queued up */
pa_sink_process_rewind(u->sink, 0);
pa_sink_render(u->sink, nbytes, chunk);
return 0;
@ -160,9 +171,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_OPENED(u->sink->thread_info.state))
return;
if (u->sink->thread_info.rewind_nbytes > 0) {
amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes);
u->sink->thread_info.rewind_nbytes = 0;
@ -178,9 +186,6 @@ static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
return;
pa_sink_set_max_rewind_within_thread(u->sink, nbytes);
}
@ -191,9 +196,6 @@ static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
return;
pa_sink_set_max_request_within_thread(u->sink, nbytes);
}
@ -204,12 +206,19 @@ static void sink_input_update_sink_latency_range_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
return;
pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
}
/* Called from I/O thread context */
static void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
}
/* Called from I/O thread context */
static void sink_input_detach_cb(pa_sink_input *i) {
struct userdata *u;
@ -217,11 +226,8 @@ static void sink_input_detach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
return;
pa_sink_detach_within_thread(u->sink);
pa_sink_set_asyncmsgq(u->sink, NULL);
pa_sink_set_rtpoll(u->sink, NULL);
}
@ -232,14 +238,13 @@ static void sink_input_attach_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
if (!u->sink || !PA_SINK_IS_LINKED(u->sink->thread_info.state))
return;
pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll);
pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency);
pa_sink_set_max_request_within_thread(u->sink, pa_sink_input_get_max_request(i));
pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i));
pa_sink_set_asyncmsgq(u->sink, i->sink->asyncmsgq);
pa_sink_set_rtpoll(u->sink, i->sink->rtpoll);
pa_sink_attach_within_thread(u->sink);
pa_sink_set_latency_range_within_thread(u->sink, u->master->thread_info.min_latency, u->master->thread_info.max_latency);
}
/* Called from main context */
@ -249,13 +254,17 @@ static void sink_input_kill_cb(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_unlink(u->sink);
/* The order here matters! We first kill the sink input, followed
* by the sink. That means the sink callbacks must be protected
* against an unconnected sink input! */
pa_sink_input_unlink(u->sink_input);
pa_sink_unlink(u->sink);
pa_sink_input_unref(u->sink_input);
u->sink_input = NULL;
pa_sink_unref(u->sink);
u->sink = NULL;
pa_sink_input_unref(u->sink_input);
u->sink_input = NULL;
pa_module_unload_request(u->module, TRUE);
}
@ -286,6 +295,17 @@ static pa_bool_t sink_input_may_move_to_cb(pa_sink_input *i, pa_sink *dest) {
return u->sink != dest;
}
/* Called from main context */
static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
struct userdata *u;
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
}
int pa__init(pa_module*m) {
struct userdata *u;
pa_sample_spec ss;
@ -336,12 +356,8 @@ int pa__init(pa_module*m) {
}
u = pa_xnew0(struct userdata, 1);
u->core = m->core;
u->module = m;
m->userdata = u;
u->master = master;
u->sink = NULL;
u->sink_input = NULL;
/* Create sink */
pa_sink_new_data_init(&sink_data);
@ -362,7 +378,7 @@ int pa__init(pa_module*m) {
goto fail;
}
u->sink = pa_sink_new(m->core, &sink_data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY);
u->sink = pa_sink_new(m->core, &sink_data, master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY));
pa_sink_new_data_done(&sink_data);
if (!u->sink) {
@ -377,19 +393,18 @@ int pa__init(pa_module*m) {
u->sink->userdata = u;
pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq);
pa_sink_set_rtpoll(u->sink, master->rtpoll);
/* Create sink input */
pa_sink_input_new_data_init(&sink_input_data);
sink_input_data.driver = __FILE__;
sink_input_data.module = m;
sink_input_data.sink = u->master;
sink_input_data.sink = master;
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
pa_sink_input_new_data_set_channel_map(&sink_input_data, &stream_map);
pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, PA_SINK_INPUT_DONT_MOVE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX));
pa_sink_input_new(&u->sink_input, m->core, &sink_input_data, (remix ? 0 : PA_SINK_INPUT_NO_REMIX));
pa_sink_input_new_data_done(&sink_input_data);
if (!u->sink_input)
@ -400,11 +415,13 @@ int pa__init(pa_module*m) {
u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
u->sink_input->update_max_request = sink_input_update_max_request_cb;
u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb;
u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb;
u->sink_input->attach = sink_input_attach_cb;
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->moving = sink_input_moving_cb;
u->sink_input->userdata = u;
pa_sink_put(u->sink);
@ -440,15 +457,20 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata))
return;
if (u->sink) {
pa_sink_unlink(u->sink);
pa_sink_unref(u->sink);
}
/* See comments in sink_input_kill_cb() above regarding
* destruction order! */
if (u->sink_input) {
if (u->sink_input)
pa_sink_input_unlink(u->sink_input);
if (u->sink)
pa_sink_unlink(u->sink);
if (u->sink_input)
pa_sink_input_unref(u->sink_input);
}
if (u->sink)
pa_sink_unref(u->sink);
pa_xfree(u);
}

View file

@ -45,13 +45,46 @@ static const char* const valid_modargs[] = {
};
struct userdata {
pa_hook_slot *sink_slot, *source_slot;
pa_hook_slot
*sink_unlink_slot,
*source_unlink_slot,
*sink_input_move_fail_slot,
*source_output_move_fail_slot;
};
static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
static pa_sink* find_evacuation_sink(pa_core *c, pa_sink_input *i, pa_sink *skip) {
pa_sink *target, *def;
uint32_t idx;
pa_assert(c);
pa_assert(i);
def = pa_namereg_get_default_sink(c);
if (def && def != skip && pa_sink_input_may_move_to(i, def))
return def;
PA_IDXSET_FOREACH(target, c->sinks, idx) {
if (target == def)
continue;
if (target == skip)
continue;
if (!PA_SINK_IS_LINKED(pa_sink_get_state(target)))
continue;
if (pa_sink_input_may_move_to(i, target))
return target;
}
pa_log_debug("No evacuation sink found.");
return NULL;
}
static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, void* userdata) {
pa_sink_input *i;
uint32_t idx;
pa_sink *target;
pa_assert(c);
pa_assert(sink);
@ -65,21 +98,12 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
return PA_HOOK_OK;
}
if (!(target = pa_namereg_get_default_sink(c)) || target == sink) {
PA_IDXSET_FOREACH(target, c->sinks, idx)
if (target != sink)
break;
if (!target) {
pa_log_debug("No evacuation sink found.");
return PA_HOOK_OK;
}
}
pa_assert(target != sink);
PA_IDXSET_FOREACH(i, sink->inputs, idx) {
pa_sink *target;
if (!(target = find_evacuation_sink(c, i, sink)))
continue;
if (pa_sink_input_move_to(i, target, FALSE) < 0)
pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
@ -91,9 +115,66 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
return PA_HOOK_OK;
}
static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void* userdata) {
static pa_hook_result_t sink_input_move_fail_hook_callback(pa_core *c, pa_sink_input *i, void *userdata) {
pa_sink *target;
pa_assert(c);
pa_assert(i);
/* There's no point in doing anything if the core is shut down anyway */
if (c->state == PA_CORE_SHUTDOWN)
return PA_HOOK_OK;
if (!(target = find_evacuation_sink(c, i, NULL)))
return PA_HOOK_OK;
if (pa_sink_input_finish_move(i, target, FALSE) < 0) {
pa_log_info("Failed to move sink input %u \"%s\" to %s.", i->index,
pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
return PA_HOOK_OK;
} else {
pa_log_info("Sucessfully moved sink input %u \"%s\" to %s.", i->index,
pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
return PA_HOOK_STOP;
}
}
static pa_source* find_evacuation_source(pa_core *c, pa_source_output *o, pa_source *skip) {
pa_source *target, *def;
uint32_t idx;
pa_assert(c);
pa_assert(o);
def = pa_namereg_get_default_source(c);
if (def && def != skip && pa_source_output_may_move_to(o, def))
return def;
PA_IDXSET_FOREACH(target, c->sources, idx) {
if (target == def)
continue;
if (target == skip)
continue;
if (!target->monitor_of != !skip->monitor_of)
continue;
if (!PA_SOURCE_IS_LINKED(pa_source_get_state(target)))
continue;
if (pa_source_output_may_move_to(o, target))
return target;
}
pa_log_debug("No evacuation source found.");
return NULL;
}
static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, void* userdata) {
pa_source_output *o;
pa_source *target;
uint32_t idx;
pa_assert(c);
@ -108,21 +189,12 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
return PA_HOOK_OK;
}
if (!(target = pa_namereg_get_default_source(c)) || target == source) {
PA_IDXSET_FOREACH(target, c->sources, idx)
if (target != source && !target->monitor_of == !source->monitor_of)
break;
if (!target) {
pa_log_info("No evacuation source found.");
return PA_HOOK_OK;
}
}
pa_assert(target != source);
PA_IDXSET_FOREACH(o, source->outputs, idx) {
pa_source *target;
if (!(target = find_evacuation_source(c, o, source)))
continue;
if (pa_source_output_move_to(o, target, FALSE) < 0)
pa_log_info("Failed to move source output %u \"%s\" to %s.", o->index,
pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME)), target->name);
@ -134,6 +206,31 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
return PA_HOOK_OK;
}
static pa_hook_result_t source_output_move_fail_hook_callback(pa_core *c, pa_source_output *i, void *userdata) {
pa_source *target;
pa_assert(c);
pa_assert(i);
/* There's no point in doing anything if the core is shut down anyway */
if (c->state == PA_CORE_SHUTDOWN)
return PA_HOOK_OK;
if (!(target = find_evacuation_source(c, i, NULL)))
return PA_HOOK_OK;
if (pa_source_output_finish_move(i, target, FALSE) < 0) {
pa_log_info("Failed to move source input %u \"%s\" to %s.", i->index,
pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
return PA_HOOK_OK;
} else {
pa_log_info("Sucessfully moved source input %u \"%s\" to %s.", i->index,
pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
return PA_HOOK_STOP;
}
}
int pa__init(pa_module*m) {
pa_modargs *ma;
struct userdata *u;
@ -148,8 +245,11 @@ int pa__init(pa_module*m) {
m->userdata = u = pa_xnew(struct userdata, 1);
/* A little bit later than module-stream-restore, module-intended-roles... */
u->sink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_hook_callback, u);
u->source_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) source_hook_callback, u);
u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_unlink_hook_callback, u);
u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE+20, (pa_hook_cb_t) source_unlink_hook_callback, u);
u->sink_input_move_fail_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], PA_HOOK_LATE+20, (pa_hook_cb_t) sink_input_move_fail_hook_callback, u);
u->source_output_move_fail_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], PA_HOOK_LATE+20, (pa_hook_cb_t) source_output_move_fail_hook_callback, u);
pa_modargs_free(ma);
return 0;
@ -163,10 +263,15 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata))
return;
if (u->sink_slot)
pa_hook_slot_free(u->sink_slot);
if (u->source_slot)
pa_hook_slot_free(u->source_slot);
if (u->sink_unlink_slot)
pa_hook_slot_free(u->sink_unlink_slot);
if (u->source_unlink_slot)
pa_hook_slot_free(u->source_unlink_slot);
if (u->sink_input_move_fail_slot)
pa_hook_slot_free(u->sink_input_move_fail_slot);
if (u->source_output_move_fail_slot)
pa_hook_slot_free(u->source_output_move_fail_slot);
pa_xfree(u);
}

View file

@ -114,15 +114,16 @@ struct userdata {
#endif
};
#define ENTRY_VERSION 2
#define ENTRY_VERSION 3
struct entry {
uint8_t version;
pa_bool_t muted_valid:1, volume_valid:1, device_valid:1;
pa_bool_t muted_valid:1, volume_valid:1, device_valid:1, card_valid:1;
pa_bool_t muted:1;
pa_channel_map channel_map;
pa_cvolume volume;
char device[PA_NAME_MAX];
char card[PA_NAME_MAX];
} PA_GCC_PACKED;
enum {
@ -1083,11 +1084,21 @@ static struct entry *read_entry(struct userdata *u, const char *name) {
goto fail;
}
if (!memchr(e->card, 0, sizeof(e->card))) {
pa_log_warn("Database contains entry for stream %s with missing NUL byte in card name", name);
goto fail;
}
if (e->device_valid && !pa_namereg_is_valid_name(e->device)) {
pa_log_warn("Invalid device name stored in database for stream %s", name);
goto fail;
}
if (e->card_valid && !pa_namereg_is_valid_name(e->card)) {
pa_log_warn("Invalid card name stored in database for stream %s", name);
goto fail;
}
if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
pa_log_warn("Invalid channel map stored in database for stream %s", name);
goto fail;
@ -1139,6 +1150,10 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
(a->device_valid && strncmp(a->device, b->device, sizeof(a->device))))
return FALSE;
if (a->card_valid != b->card_valid ||
(a->card_valid && strncmp(a->card, b->card, sizeof(a->card))))
return FALSE;
if (a->muted_valid != b->muted_valid ||
(a->muted_valid && (a->muted != b->muted)))
return FALSE;
@ -1217,6 +1232,10 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
entry.device_valid = TRUE;
device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device));
if (sink_input->sink->card) {
pa_strlcpy(entry.card, sink_input->sink->card->name, sizeof(entry.card));
entry.card_valid = TRUE;
}
}
} else {
@ -1240,6 +1259,11 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
entry.device_valid = source_output->save_source;
device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device));
if (source_output->source->card) {
pa_strlcpy(entry.card, source_output->source->card->name, sizeof(entry.card));
entry.card_valid = TRUE;
}
}
}
@ -1298,19 +1322,28 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
if (!(name = get_name(new_data->proplist, "sink-input")))
return PA_HOOK_OK;
if ((e = read_entry(u, name))) {
if (new_data->sink)
pa_log_debug("Not restoring device for stream %s, because already set.", name);
else if ((e = read_entry(u, name))) {
pa_sink *s = NULL;
if (e->device_valid) {
pa_sink *s;
if (e->device_valid)
s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK);
if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) {
if (!new_data->sink) {
if (!s && e->card_valid) {
pa_card *card;
if ((card = pa_namereg_get(c, e->card, PA_NAMEREG_CARD)))
s = pa_idxset_first(card->sinks, NULL);
}
/* It might happen that a stream and a sink are set up at the
same time, in which case we want to make sure we don't
interfere with that */
if (s && PA_SINK_IS_LINKED(pa_sink_get_state(s))) {
pa_log_info("Restoring device for stream %s.", name);
new_data->sink = s;
new_data->save_sink = TRUE;
} else
pa_log_debug("Not restoring device for stream %s, because already set.", name);
}
}
pa_xfree(e);
@ -1385,18 +1418,28 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
if (!(name = get_name(new_data->proplist, "source-output")))
return PA_HOOK_OK;
if ((e = read_entry(u, name))) {
pa_source *s;
if (new_data->source)
pa_log_debug("Not restoring device for stream %s, because already set", name);
else if ((e = read_entry(u, name))) {
pa_source *s = NULL;
if (e->device_valid) {
if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE))) {
if (!new_data->source) {
if (e->device_valid)
s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE);
if (!s && e->card_valid) {
pa_card *card;
if ((card = pa_namereg_get(c, e->card, PA_NAMEREG_CARD)))
s = pa_idxset_first(card->sources, NULL);
}
/* It might happen that a stream and a sink are set up at the
same time, in which case we want to make sure we don't
interfere with that */
if (s && PA_SOURCE_IS_LINKED(pa_source_get_state(s))) {
pa_log_info("Restoring device for stream %s.", name);
new_data->source = s;
new_data->save_source = TRUE;
} else
pa_log_debug("Not restoring device for stream %s, because already set", name);
}
}
pa_xfree(e);
@ -1426,6 +1469,17 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct
if (si->save_sink)
continue;
/* Skip this if it is already in the process of being moved
* anyway */
if (!si->sink)
continue;
/* It might happen that a stream and a sink are set up at the
same time, in which case we want to make sure we don't
interfere with that */
if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
continue;
if (!(name = get_name(si->proplist, "sink-input")))
continue;
@ -1464,6 +1518,16 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source,
if (so->direct_on_input)
continue;
/* Skip this if it is already in the process of being moved anyway */
if (!so->source)
continue;
/* It might happen that a stream and a sink are set up at the
same time, in which case we want to make sure we don't
interfere with that */
if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
continue;
if (!(name = get_name(so->proplist, "source-input")))
continue;
@ -1497,6 +1561,9 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
char *name;
struct entry *e;
if (!si->sink)
continue;
if (!(name = get_name(si->proplist, "sink-input")))
continue;
@ -1505,7 +1572,9 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
if (e->device_valid) {
pa_sink *d;
if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SINK)) && d != sink)
if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SINK)) &&
d != sink &&
PA_SINK_IS_LINKED(pa_sink_get_state(d)))
pa_sink_input_move_to(si, d, TRUE);
}
@ -1535,6 +1604,12 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
char *name;
struct entry *e;
if (so->direct_on_input)
continue;
if (!so->source)
continue;
if (!(name = get_name(so->proplist, "source-output")))
continue;
@ -1543,7 +1618,9 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
if (e->device_valid) {
pa_source *d;
if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE)) && d != source)
if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE)) &&
d != source &&
PA_SOURCE_IS_LINKED(pa_source_get_state(d)))
pa_source_output_move_to(so, d, TRUE);
}
@ -1567,7 +1644,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
pa_assert(name);
pa_assert(e);
for (si = pa_idxset_first(u->core->sink_inputs, &idx); si; si = pa_idxset_next(u->core->sink_inputs, &idx)) {
PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
char *n;
pa_sink *s;
@ -1585,12 +1662,13 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
v = e->volume;
pa_log_info("Restoring volume for sink input %s.", name);
pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map), FALSE, FALSE);
pa_cvolume_remap(&v, &e->channel_map, &si->channel_map);
pa_sink_input_set_volume(si, &v, TRUE, FALSE);
}
if (u->restore_muted && e->muted_valid) {
pa_log_info("Restoring mute state for sink input %s.", name);
pa_sink_input_set_mute(si, e->muted, FALSE);
pa_sink_input_set_mute(si, e->muted, TRUE);
}
if (u->restore_device &&
@ -1598,11 +1676,11 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
(s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) {
pa_log_info("Restoring device for stream %s.", name);
pa_sink_input_move_to(si, s, FALSE);
pa_sink_input_move_to(si, s, TRUE);
}
}
for (so = pa_idxset_first(u->core->source_outputs, &idx); so; so = pa_idxset_next(u->core->source_outputs, &idx)) {
PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
char *n;
pa_source *s;
@ -1620,7 +1698,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
(s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
pa_log_info("Restoring device for stream %s.", name);
pa_source_output_move_to(so, s, FALSE);
pa_source_output_move_to(so, s, TRUE);
}
}
}

View file

@ -55,6 +55,7 @@
#include <pulsecore/core-error.h>
#include <pulsecore/proplist-util.h>
#include <pulsecore/auth-cookie.h>
#include <pulsecore/mcalign.h>
#ifdef TUNNEL_SINK
#include "module-tunnel-sink-symdef.h"
@ -194,6 +195,7 @@ struct userdata {
#else
char *source_name;
pa_source *source;
pa_mcalign *mcalign;
#endif
pa_auth_cookie *auth_cookie;
@ -614,14 +616,23 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
return 0;
}
case SOURCE_MESSAGE_POST:
case SOURCE_MESSAGE_POST: {
pa_memchunk c;
pa_mcalign_push(u->mcalign, chunk);
while (pa_mcalign_pop(u->mcalign, &c) >= 0) {
if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
pa_source_post(u->source, chunk);
pa_source_post(u->source, &c);
u->counter += (int64_t) chunk->length;
pa_memblock_unref(c.memblock);
u->counter += (int64_t) c.length;
}
return 0;
}
case SOURCE_MESSAGE_REMOTE_SUSPEND:
@ -1154,10 +1165,10 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag
pa_cvolume_equal(&volume, &u->sink->virtual_volume))
return;
pa_sink_volume_changed(u->sink, &volume, FALSE);
pa_sink_volume_changed(u->sink, &volume);
if (u->version >= 11)
pa_sink_mute_changed(u->sink, mute, FALSE);
pa_sink_mute_changed(u->sink, mute);
return;
@ -1937,6 +1948,8 @@ int pa__init(pa_module*m) {
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
pa_source_set_rtpoll(u->source, u->rtpoll);
u->mcalign = pa_mcalign_new(pa_frame_size(&u->source->sample_spec));
#endif
pa_xfree(dn);
@ -2030,6 +2043,11 @@ void pa__done(pa_module*m) {
if (u->time_event)
u->core->mainloop->time_free(u->time_event);
#ifndef TUNNEL_SINK
if (u->mcalign)
pa_mcalign_free(u->mcalign);
#endif
#ifdef TUNNEL_SINK
pa_xfree(u->sink_name);
#else

View file

@ -65,6 +65,8 @@ static const char* const valid_modargs[] = {
NULL
};
static int setup_inotify(struct userdata *u);
static void device_free(struct device *d) {
pa_assert(d);
@ -117,6 +119,9 @@ static void card_changed(struct userdata *u, struct udev_device *dev) {
pa_assert(u);
pa_assert(dev);
/* Maybe /dev/snd is now available? */
setup_inotify(u);
path = udev_device_get_devpath(dev);
if ((d = pa_hashmap_get(u->devices, path))) {
@ -262,7 +267,7 @@ static void inotify_cb(
} buf;
struct userdata *u = userdata;
static int type = 0;
pa_bool_t verify = FALSE;
pa_bool_t verify = FALSE, deleted = FALSE;
for (;;) {
ssize_t r;
@ -279,6 +284,9 @@ static void inotify_cb(
if ((buf.e.mask & IN_CLOSE_WRITE) && pa_startswith(buf.e.name, "pcmC"))
verify = TRUE;
if ((buf.e.mask & (IN_DELETE_SELF|IN_MOVE_SELF)))
deleted = TRUE;
}
if (verify) {
@ -291,11 +299,14 @@ static void inotify_cb(
verify_access(u, d);
}
if (!deleted)
return;
fail:
if (u->inotify_io) {
a->io_free(u->inotify_io);
u->inotify_io = NULL;
}
if (u->inotify_fd >= 0) {
pa_close(u->inotify_fd);
@ -307,17 +318,38 @@ static int setup_inotify(struct userdata *u) {
char *dev_snd;
int r;
if (u->inotify_fd >= 0)
return 0;
if ((u->inotify_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
pa_log("inotify_init1() failed: %s", pa_cstrerror(errno));
return -1;
}
dev_snd = pa_sprintf_malloc("%s/snd", udev_get_dev_path(u->udev));
r = inotify_add_watch(u->inotify_fd, dev_snd, IN_CLOSE_WRITE);
r = inotify_add_watch(u->inotify_fd, dev_snd, IN_CLOSE_WRITE|IN_DELETE_SELF|IN_MOVE_SELF);
pa_xfree(dev_snd);
if (r < 0) {
pa_log("inotify_add_watch() failed: %s", pa_cstrerror(errno));
int saved_errno = errno;
pa_close(u->inotify_fd);
u->inotify_fd = -1;
if (saved_errno == ENOENT) {
pa_log_debug("/dev/snd/ is apparently not existing yet, retrying to create inotify watch later.");
return 0;
}
if (saved_errno == ENOSPC) {
pa_log("You apparently ran out of inotify watches, probably because Tracker/Beagle took them all away. "
"I wished people would do their homework first and fix inotify before using it for watching whole "
"directory trees which is something the current inotify is certainly not useful for. "
"Please make sure to drop the Tracker/Beagle guys a line complaining about their broken use of inotify.");
return 0;
}
pa_log("inotify_add_watch() failed: %s", pa_cstrerror(saved_errno));
return -1;
}

View file

@ -48,6 +48,7 @@ static const char* const valid_modargs[] = {
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
pa_bool_t restore_device = TRUE, restore_volume = TRUE;
pa_module *n;
char *t;
pa_assert(m);
@ -66,13 +67,15 @@ int pa__init(pa_module*m) {
pa_log_warn("We will now load module-stream-restore. Please make sure to remove module-volume-restore from your configuration.");
t = pa_sprintf_malloc("restore_volume=%s restore_device=%s", pa_yes_no(restore_volume), pa_yes_no(restore_device));
pa_module_load(m->core, "module-stream-restore", t);
n = pa_module_load(m->core, "module-stream-restore", t);
pa_xfree(t);
if (n)
pa_module_unload_request(m, TRUE);
pa_modargs_free(ma);
return 0;
return n ? 0 : -1;
fail:
if (ma)

View file

@ -1,3 +1,5 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
/***
Copyright 2009 Lennart Poettering
@ -76,9 +78,17 @@ static DBusHandlerResult filter_handler(
goto invalid;
if (strcmp(name, m->service_name) == 0) {
m->busy = !!(new && *new);
/* If we ourselves own the device, then don't consider this 'busy' */
if (m->busy) {
const char *un;
if ((un = dbus_bus_get_unique_name(c)))
if (strcmp(new, un) == 0)
m->busy = FALSE;
}
if (m->change_cb) {
m->ref++;
m->change_cb(m);

View file

@ -1,3 +1,5 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
#ifndef fooreservemonitorhfoo
#define fooreservemonitorhfoo
@ -28,6 +30,10 @@
#include <dbus/dbus.h>
#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct rm_monitor rm_monitor;
/* Prototype for a function that is called whenever the reservation
@ -59,4 +65,8 @@ void rm_set_userdata(rm_monitor *m, void *userdata);
* userdata was set. */
void* rm_get_userdata(rm_monitor *m);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -336,5 +336,9 @@ pa_bool_t pa_reserve_monitor_wrapper_busy(pa_reserve_monitor_wrapper *w) {
pa_assert(PA_REFCNT_VALUE(w) >= 1);
#ifdef HAVE_DBUS
return rm_busy(w->monitor) > 0;
#else
return FALSE;
#endif
}

View file

@ -1,3 +1,5 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
/***
Copyright 2009 Lennart Poettering

View file

@ -1,3 +1,5 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
#ifndef fooreservehfoo
#define fooreservehfoo
@ -28,6 +30,10 @@
#include <dbus/dbus.h>
#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct rd_device rd_device;
/* Prototype for a function that is called whenever someone else wants
@ -66,4 +72,8 @@ void rd_set_userdata(rd_device *d, void *userdata);
* userdata was set. */
void* rd_get_userdata(rd_device *d);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -361,7 +361,7 @@ static void sink_input_attach(pa_sink_input *i) {
pa_assert_se(s = i->userdata);
pa_assert(!s->rtpoll_item);
s->rtpoll_item = pa_rtpoll_item_new(i->sink->rtpoll, PA_RTPOLL_LATE, 1);
s->rtpoll_item = pa_rtpoll_item_new(i->sink->thread_info.rtpoll, PA_RTPOLL_LATE, 1);
p = pa_rtpoll_item_get_pollfd(s->rtpoll_item, NULL);
p->fd = s->rtp_context.fd;

View file

@ -103,6 +103,7 @@ int pa_client_conf_load(pa_client_conf *c, const char *filename) {
{ "autospawn", pa_config_parse_bool, &c->autospawn, NULL },
{ "cookie-file", pa_config_parse_string, &c->cookie_file, NULL },
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
{ "enable-shm", pa_config_parse_not_bool, &c->disable_shm, NULL },
{ "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL },
{ NULL, NULL, NULL, NULL },
};

View file

@ -30,5 +30,5 @@
; cookie-file =
; disable-shm = no
; enable-shm = yes
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB

View file

@ -668,11 +668,24 @@ static pa_strlist *prepend_per_user(pa_strlist *l) {
static int context_autospawn(pa_context *c) {
pid_t pid;
int status, r;
pa_log_debug("Trying to autospawn...");
struct sigaction sa;
pa_context_ref(c);
if (sigaction(SIGCHLD, NULL, &sa) < 0) {
pa_log_debug("sigaction() failed: %s", pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
goto fail;
}
if ((sa.sa_flags & SA_NOCLDWAIT) || sa.sa_handler == SIG_IGN) {
pa_log_debug("Process disabled waitpid(), cannot autospawn.");
pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
goto fail;
}
pa_log_debug("Trying to autospawn...");
if (c->spawn_api.prefork)
c->spawn_api.prefork();
@ -688,23 +701,23 @@ static int context_autospawn(pa_context *c) {
/* Child */
const char *state = NULL;
#define MAX_ARGS 64
const char * argv[MAX_ARGS+1];
int n;
const char * argv[32];
unsigned n = 0;
if (c->spawn_api.atfork)
c->spawn_api.atfork();
/* We leave most of the cleaning up of the process environment
* to the executable. We only clean up the file descriptors to
* make sure the executable can actually be loaded
* correctly. */
pa_close_all(-1);
/* Setup argv */
n = 0;
argv[n++] = c->conf->daemon_binary;
argv[n++] = "--start";
while (n < MAX_ARGS) {
while (n < PA_ELEMENTSOF(argv)-1) {
char *a;
if (!(a = pa_split_spaces(c->conf->extra_arguments, &state)))
@ -714,10 +727,10 @@ static int context_autospawn(pa_context *c) {
}
argv[n++] = NULL;
pa_assert(n <= PA_ELEMENTSOF(argv));
execv(argv[0], (char * const *) argv);
_exit(1);
#undef MAX_ARGS
}
/* Parent */
@ -730,9 +743,16 @@ static int context_autospawn(pa_context *c) {
} while (r < 0 && errno == EINTR);
if (r < 0) {
if (errno != ESRCH) {
pa_log(_("waitpid(): %s"), pa_cstrerror(errno));
pa_context_fail(c, PA_ERR_INTERNAL);
goto fail;
}
/* hmm, something already reaped our child, so we assume
* startup worked, even if we cannot know */
} else if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
pa_context_fail(c, PA_ERR_CONNECTIONREFUSED);
goto fail;
@ -761,22 +781,33 @@ static void track_pulseaudio_on_dbus(pa_context *c, DBusBusType type, pa_dbus_wr
pa_assert(conn);
dbus_error_init(&error);
if (!(*conn = pa_dbus_wrap_connection_new(c->mainloop, c->use_rtclock, type, &error)) || dbus_error_is_set(&error)) {
pa_log_warn("Unable to contact DBUS: %s: %s", error.name, error.message);
goto finish;
goto fail;
}
if (!dbus_connection_add_filter(pa_dbus_wrap_connection_get(*conn), filter_cb, c, NULL)) {
pa_log_warn("Failed to add filter function");
goto finish;
goto fail;
}
if (pa_dbus_add_matches(
pa_dbus_wrap_connection_get(*conn), &error,
"type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',arg0='org.pulseaudio.Server',arg1=''", NULL) < 0)
pa_log_warn("Unable to track org.pulseaudio.Server: %s: %s", error.name, error.message);
"type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',arg0='org.pulseaudio.Server',arg1=''", NULL) < 0) {
pa_log_warn("Unable to track org.pulseaudio.Server: %s: %s", error.name, error.message);
goto fail;
}
return;
fail:
if (*conn) {
pa_dbus_wrap_connection_free(*conn);
*conn = NULL;
}
finish:
dbus_error_free(&error);
}
#endif
@ -861,7 +892,7 @@ static void on_connection(pa_socket_client *client, pa_iochannel*io, void *userd
c->client = NULL;
if (!io) {
/* Try the item in the list */
/* Try the next item in the list */
if (saved_errno == ECONNREFUSED ||
saved_errno == ETIMEDOUT ||
saved_errno == EHOSTUNREACH) {
@ -897,7 +928,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
/* FIXME: We probably should check if this is actually the NameOwnerChanged we were looking for */
is_session = c->session_bus && bus == pa_dbus_wrap_connection_get(c->session_bus);
pa_log_debug("Rock!! PulseAudio is back on %s bus", is_session ? "session" : "system");
pa_log_debug("Rock!! PulseAudio might be back on %s bus", is_session ? "session" : "system");
if (is_session)
/* The user instance via PF_LOCAL */
@ -937,7 +968,7 @@ int pa_context_connect(
pa_context_ref(c);
c->no_fail = flags & PA_CONTEXT_NOFAIL;
c->no_fail = !!(flags & PA_CONTEXT_NOFAIL);
c->server_specified = !!server;
pa_assert(!c->server_list);
@ -954,10 +985,7 @@ int pa_context_connect(
/* Follow the X display */
if ((d = getenv("DISPLAY"))) {
char *e;
d = pa_xstrdup(d);
if ((e = strchr(d, ':')))
*e = 0;
d = pa_xstrndup(d, strcspn(d, ":"));
if (*d)
c->server_list = pa_strlist_prepend(c->server_list, d);

View file

@ -151,6 +151,11 @@ struct pa_stream {
uint32_t device_index;
char *device_name;
/* playback */
pa_memblock *write_memblock;
void *write_data;
/* recording */
pa_memchunk peek_memchunk;
void *peek_data;
pa_memblockq *record_memblockq;

View file

@ -201,6 +201,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
goto finish;
}
if (o->context->version >= 16) {
if (i.n_ports > 0) {
i.ports = pa_xnew(pa_sink_port_info*, i.n_ports+1);
i.ports[0] = pa_xnew(pa_sink_port_info, i.n_ports);
@ -211,8 +212,8 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) {
pa_context_fail(o->context, PA_ERR_PROTOCOL);
pa_xfree(i.ports);
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
pa_proplist_free(i.proplist);
goto finish;
}
@ -238,6 +239,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
break;
}
}
}
i.mute = (int) mute;
i.flags = (pa_sink_flags_t) flags;
@ -248,6 +250,10 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
cb(o->context, &i, 0, o->userdata);
}
if (i.ports) {
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
}
pa_proplist_free(i.proplist);
}
}
@ -428,6 +434,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
goto finish;
}
if (o->context->version >= 16) {
if (i.n_ports > 0) {
i.ports = pa_xnew(pa_source_port_info*, i.n_ports+1);
i.ports[0] = pa_xnew(pa_source_port_info, i.n_ports);
@ -465,6 +472,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
break;
}
}
}
i.mute = (int) mute;
i.flags = (pa_source_flags_t) flags;
@ -475,6 +483,10 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
cb(o->context, &i, 0, o->userdata);
}
if (i.ports) {
pa_xfree(i.ports[0]);
pa_xfree(i.ports);
}
pa_proplist_free(i.proplist);
}
}

View file

@ -765,23 +765,22 @@ static pa_time_event* find_next_time_event(pa_mainloop *m) {
static int calc_next_timeout(pa_mainloop *m) {
pa_time_event *t;
pa_usec_t usec;
pa_usec_t clock_now;
if (!m->n_enabled_time_events)
return -1;
t = find_next_time_event(m);
pa_assert(t);
pa_assert_se(t = find_next_time_event(m));
if (t->time == 0)
if (t->time <= 0)
return 0;
usec = t->time - pa_rtclock_now();
clock_now = pa_rtclock_now();
if (usec <= 0)
if (t->time <= clock_now)
return 0;
return (int) (usec / 1000); /* in milliseconds */
return (int) ((t->time - clock_now) / 1000); /* in milliseconds */
}
static int dispatch_timeout(pa_mainloop *m) {

View file

@ -40,7 +40,11 @@ pa_operation *pa_operation_ref(pa_operation *o);
/** Decrease the reference count by one */
void pa_operation_unref(pa_operation *o);
/** Cancel the operation. Beware! This will not necessarily cancel the execution of the operation on the server side. */
/** Cancel the operation. Beware! This will not necessarily cancel the
* execution of the operation on the server side. However it will make
* sure that the callback associated with this operation will not be
* called anymore, effectively disabling the operation from the client
* side's view. */
void pa_operation_cancel(pa_operation *o);
/** Return the current status of the operation */

View file

@ -70,8 +70,8 @@ struct pa_simple {
#define CHECK_DEAD_GOTO(p, rerror, label) \
do { \
if (!(p)->context || pa_context_get_state((p)->context) != PA_CONTEXT_READY || \
!(p)->stream || pa_stream_get_state((p)->stream) != PA_STREAM_READY) { \
if (!(p)->context || !PA_CONTEXT_IS_GOOD(pa_context_get_state((p)->context)) || \
!(p)->stream || !PA_STREAM_IS_GOOD(pa_stream_get_state((p)->stream))) { \
if (((p)->context && pa_context_get_state((p)->context) == PA_CONTEXT_FAILED) || \
((p)->stream && pa_stream_get_state((p)->stream) == PA_STREAM_FAILED)) { \
if (rerror) \
@ -157,12 +157,8 @@ pa_simple* pa_simple_new(
CHECK_VALIDITY_RETURN_ANY(rerror, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID, NULL);
CHECK_VALIDITY_RETURN_ANY(rerror, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID, NULL)
p = pa_xnew(pa_simple, 1);
p->context = NULL;
p->stream = NULL;
p = pa_xnew0(pa_simple, 1);
p->direction = dir;
p->read_data = NULL;
p->read_index = p->read_length = 0;
if (!(p->mainloop = pa_threaded_mainloop_new()))
goto fail;
@ -182,14 +178,23 @@ pa_simple* pa_simple_new(
if (pa_threaded_mainloop_start(p->mainloop) < 0)
goto unlock_and_fail;
/* Wait until the context is ready */
pa_threaded_mainloop_wait(p->mainloop);
for (;;) {
pa_context_state_t state;
if (pa_context_get_state(p->context) != PA_CONTEXT_READY) {
state = pa_context_get_state(p->context);
if (state == PA_CONTEXT_READY)
break;
if (!PA_CONTEXT_IS_GOOD(state)) {
error = pa_context_errno(p->context);
goto unlock_and_fail;
}
/* Wait until the context is ready */
pa_threaded_mainloop_wait(p->mainloop);
}
if (!(p->stream = pa_stream_new(p->context, stream_name, ss, map))) {
error = pa_context_errno(p->context);
goto unlock_and_fail;
@ -216,15 +221,23 @@ pa_simple* pa_simple_new(
goto unlock_and_fail;
}
/* Wait until the stream is ready */
pa_threaded_mainloop_wait(p->mainloop);
for (;;) {
pa_stream_state_t state;
/* Wait until the stream is ready */
if (pa_stream_get_state(p->stream) != PA_STREAM_READY) {
state = pa_stream_get_state(p->stream);
if (state == PA_STREAM_READY)
break;
if (!PA_STREAM_IS_GOOD(state)) {
error = pa_context_errno(p->context);
goto unlock_and_fail;
}
/* Wait until the stream is ready */
pa_threaded_mainloop_wait(p->mainloop);
}
pa_threaded_mainloop_unlock(p->mainloop);
return p;
@ -248,8 +261,10 @@ void pa_simple_free(pa_simple *s) {
if (s->stream)
pa_stream_unref(s->stream);
if (s->context)
if (s->context) {
pa_context_disconnect(s->context);
pa_context_unref(s->context);
}
if (s->mainloop)
pa_threaded_mainloop_free(s->mainloop);
@ -261,7 +276,8 @@ int pa_simple_write(pa_simple *p, const void*data, size_t length, int *rerror) {
pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_PLAYBACK, PA_ERR_BADSTATE, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, data, PA_ERR_INVALID, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, length > 0, PA_ERR_INVALID, -1);
pa_threaded_mainloop_lock(p->mainloop);
@ -300,7 +316,8 @@ int pa_simple_read(pa_simple *p, void*data, size_t length, int *rerror) {
pa_assert(p);
CHECK_VALIDITY_RETURN_ANY(rerror, p->direction == PA_STREAM_RECORD, PA_ERR_BADSTATE, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, data && length, PA_ERR_INVALID, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, data, PA_ERR_INVALID, -1);
CHECK_VALIDITY_RETURN_ANY(rerror, length > 0, PA_ERR_INVALID, -1);
pa_threaded_mainloop_lock(p->mainloop);
@ -375,7 +392,7 @@ int pa_simple_drain(pa_simple *p, int *rerror) {
CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
p->operation_success = 0;
while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
pa_threaded_mainloop_wait(p->mainloop);
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
}
@ -411,7 +428,7 @@ int pa_simple_flush(pa_simple *p, int *rerror) {
CHECK_SUCCESS_GOTO(p, rerror, o, unlock_and_fail);
p->operation_success = 0;
while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
while (pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
pa_threaded_mainloop_wait(p->mainloop);
CHECK_DEAD_GOTO(p, rerror, unlock_and_fail);
}

View file

@ -144,12 +144,13 @@ pa_stream *pa_stream_new_with_proplist(
s->suspended = FALSE;
s->corked = FALSE;
s->write_memblock = NULL;
s->write_data = NULL;
pa_memchunk_reset(&s->peek_memchunk);
s->peek_data = NULL;
s->record_memblockq = NULL;
memset(&s->timing_info, 0, sizeof(s->timing_info));
s->timing_info_valid = FALSE;
@ -221,6 +222,11 @@ static void stream_free(pa_stream *s) {
stream_unlink(s);
if (s->write_memblock) {
pa_memblock_release(s->write_memblock);
pa_memblock_unref(s->write_data);
}
if (s->peek_memchunk.memblock) {
if (s->peek_data)
pa_memblock_release(s->peek_memchunk.memblock);
@ -861,7 +867,7 @@ static void automatic_buffer_attr(pa_stream *s, pa_buffer_attr *attr, const pa_s
void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s = userdata;
uint32_t requested_bytes;
uint32_t requested_bytes = 0;
pa_assert(pd);
pa_assert(s);
@ -1166,7 +1172,7 @@ int pa_stream_connect_playback(
const char *dev,
const pa_buffer_attr *attr,
pa_stream_flags_t flags,
pa_cvolume *volume,
const pa_cvolume *volume,
pa_stream *sync_stream) {
pa_assert(s);
@ -1187,20 +1193,71 @@ int pa_stream_connect_record(
return create_stream(PA_STREAM_RECORD, s, dev, attr, flags, NULL, NULL);
}
int pa_stream_begin_write(
pa_stream *s,
void **data,
size_t *nbytes) {
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, data, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, nbytes && *nbytes != 0, PA_ERR_INVALID);
if (*nbytes != (size_t) -1) {
size_t m, fs;
m = pa_mempool_block_size_max(s->context->mempool);
fs = pa_frame_size(&s->sample_spec);
m = (m / fs) * fs;
if (*nbytes > m)
*nbytes = m;
}
if (!s->write_memblock) {
s->write_memblock = pa_memblock_new(s->context->mempool, *nbytes);
s->write_data = pa_memblock_acquire(s->write_memblock);
}
*data = s->write_data;
*nbytes = pa_memblock_get_length(s->write_memblock);
return 0;
}
int pa_stream_cancel_write(
pa_stream *s) {
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED);
PA_CHECK_VALIDITY(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, s->write_memblock, PA_ERR_BADSTATE);
pa_assert(s->write_data);
pa_memblock_release(s->write_memblock);
pa_memblock_unref(s->write_memblock);
s->write_memblock = NULL;
s->write_data = NULL;
return 0;
}
int pa_stream_write(
pa_stream *s,
const void *data,
size_t length,
void (*free_cb)(void *p),
pa_free_cb_t free_cb,
int64_t offset,
pa_seek_mode_t seek) {
pa_memchunk chunk;
pa_seek_mode_t t_seek;
int64_t t_offset;
size_t t_length;
const void *t_data;
pa_assert(s);
pa_assert(PA_REFCNT_VALUE(s) >= 1);
pa_assert(data);
@ -1210,16 +1267,40 @@ int pa_stream_write(
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || s->direction == PA_STREAM_UPLOAD, PA_ERR_BADSTATE);
PA_CHECK_VALIDITY(s->context, seek <= PA_SEEK_RELATIVE_END, PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, s->direction == PA_STREAM_PLAYBACK || (seek == PA_SEEK_RELATIVE && offset == 0), PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context,
!s->write_memblock ||
((data >= s->write_data) &&
((const char*) data + length <= (const char*) s->write_data + pa_memblock_get_length(s->write_memblock))),
PA_ERR_INVALID);
PA_CHECK_VALIDITY(s->context, !free_cb || !s->write_memblock, PA_ERR_INVALID);
if (length <= 0)
return 0;
if (s->write_memblock) {
pa_memchunk chunk;
t_seek = seek;
t_offset = offset;
t_length = length;
t_data = data;
/* pa_stream_write_begin() was called before */
pa_memblock_release(s->write_memblock);
chunk.memblock = s->write_memblock;
chunk.index = (const char *) data - (const char *) s->write_data;
chunk.length = length;
s->write_memblock = NULL;
s->write_data = NULL;
pa_pstream_send_memblock(s->context->pstream, s->channel, offset, seek, &chunk);
pa_memblock_unref(chunk.memblock);
} else {
pa_seek_mode_t t_seek = seek;
int64_t t_offset = offset;
size_t t_length = length;
const void *t_data = data;
/* pa_stream_write_begin() was not called before */
while (t_length > 0) {
pa_memchunk chunk;
chunk.index = 0;
@ -1250,6 +1331,7 @@ int pa_stream_write(
if (free_cb && pa_pstream_get_shm(s->context->pstream))
free_cb((void*) data);
}
/* This is obviously wrong since we ignore the seeking index . But
* that's OK, the server side applies the same error */

View file

@ -405,7 +405,7 @@ int pa_stream_connect_playback(
const char *dev /**< Name of the sink to connect to, or NULL for default */ ,
const pa_buffer_attr *attr /**< Buffering attributes, or NULL for default */,
pa_stream_flags_t flags /**< Additional flags, or 0 for default */,
pa_cvolume *volume /**< Initial volume, or NULL for default */,
const pa_cvolume *volume /**< Initial volume, or NULL for default */,
pa_stream *sync_stream /**< Synchronize this stream with the specified one, or NULL for a standalone stream*/);
/** Connect the stream to a source */
@ -418,15 +418,71 @@ int pa_stream_connect_record(
/** Disconnect a stream from a source/sink */
int pa_stream_disconnect(pa_stream *s);
/** Write some data to the server (for playback sinks), if free_cb is
* non-NULL this routine is called when all data has been written out
* and an internal reference to the specified data is kept, the data
* is not copied. If NULL, the data is copied into an internal
* buffer. The client my freely seek around in the output buffer. For
/** Prepare writing data to the server (for playback streams). This
* function may be used to optimize the number of memory copies when
* doing playback ("zero-copy"). It is recommended to call this
* function before each call to pa_stream_write(). Pass in the address
* to a pointer and an address of the number of bytes you want to
* write. On return the two values will contain a pointer where you
* can place the data to write and the maximum number of bytes you can
* write. On return *nbytes can be smaller or have the same value as
* you passed in. You need to be able to handle both cases. Accessing
* memory beyond the returned *nbytes value is invalid. Acessing the
* memory returned after the following pa_stream_write() or
* pa_stream_cancel_write() is invalid. On invocation only *nbytes
* needs to be initialized, on return both *data and *nbytes will be
* valid. If you place (size_t) -1 in *nbytes on invocation the memory
* size will be chosen automatically (which is recommended to
* do). After placing your data in the memory area returned call
* pa_stream_write() with data set to an address within this memory
* area and an nbytes value that is smaller or equal to what was
* returned by this function to actually execute the write. An
* invocation of pa_stream_write() should follow "quickly" on
* pa_stream_begin_write(). It is not recommended letting an unbounded
* amount of time pass after calling pa_stream_begin_write() and
* before calling pa_stream_write(). If you want to cancel a
* previously called pa_stream_begin_write() without calling
* pa_stream_write() use pa_stream_cancel_write(). Calling
* pa_stream_begin_write() twice without calling pa_stream_write() or
* pa_stream_cancel_write() in between will return exactly the same
* pointer/nbytes values.\since 0.9.16 */
int pa_stream_begin_write(
pa_stream *p,
void **data,
size_t *nbytes);
/** Reverses the effect of pa_stream_begin_write() dropping all data
* that has already been placed in the memory area returned by
* pa_stream_begin_write(). Only valid to call if
* pa_stream_begin_write() was called before and neither
* pa_stream_cancel_write() nor pa_stream_write() have been called
* yet. Accessing the memory previously returned by
* pa_stream_begin_write() after this call is invalid. Any further
* explicit freeing of the memory area is not necessary. \since
* 0.9.16 */
int pa_stream_cancel_write(
pa_stream *p);
/** Write some data to the server (for playback streams), if free_cb
* is non-NULL this routine is called when all data has been written
* out and an internal reference to the specified data is kept, the
* data is not copied. If NULL, the data is copied into an internal
* buffer. The client may freely seek around in the output buffer. For
* most applications passing 0 and PA_SEEK_RELATIVE as arguments for
* offset and seek should be useful. Afte ther write call succeeded
* the write index will be a the position after where this chunk of
* data has been written to. */
* data has been written to.
*
* As an optimization for avoiding needless memory copies you may call
* pa_stream_begin_write() before this call and then place your audio
* data directly in the memory area returned by that call. Then, pass
* a pointer to that memory area to pa_stream_write(). After the
* invocation of pa_stream_write() the memory area may no longer be
* accessed. Any further explicit freeing of the memory area is not
* necessary. It is OK to write the memory area returned by
* pa_stream_begin_write() only partially with this call, skipping
* bytes both at the end and at the beginning of the reserved memory
* area.*/
int pa_stream_write(
pa_stream *p /**< The stream to use */,
const void *data /**< The data to write */,
@ -435,11 +491,12 @@ int pa_stream_write(
int64_t offset, /**< Offset for seeking, must be 0 for upload streams */
pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
/** Read the next fragment from the buffer (for recording).
* data will point to the actual data and length will contain the size
* of the data in bytes (which can be less than a complete framgnet).
* Use pa_stream_drop() to actually remove the data from the
* buffer. If no data is available will return a NULL pointer */
/** Read the next fragment from the buffer (for recording streams).
* data will point to the actual data and nbytes will contain the size
* of the data in bytes (which can be less or more than a complete
* fragment). Use pa_stream_drop() to actually remove the data from
* the buffer. If no data is available this will return a NULL
* pointer */
int pa_stream_peek(
pa_stream *p /**< The stream to use */,
const void **data /**< Pointer to pointer that will point to data */,
@ -455,7 +512,9 @@ size_t pa_stream_writable_size(pa_stream *p);
/** Return the number of bytes that may be read using pa_stream_peek()*/
size_t pa_stream_readable_size(pa_stream *p);
/** Drain a playback stream. Use this for notification when the buffer is empty */
/** Drain a playback stream. Use this for notification when the buffer
* is empty. Please note that only one drain operation per stream may
* be issued at a time. */
pa_operation* pa_stream_drain(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
/** Request a timing info structure update for a stream. Use

View file

@ -51,7 +51,7 @@
struct pa_threaded_mainloop {
pa_mainloop *real_mainloop;
int n_waiting;
int n_waiting, n_waiting_for_accept;
pa_thread* thread;
pa_mutex* mutex;
@ -190,8 +190,12 @@ void pa_threaded_mainloop_signal(pa_threaded_mainloop *m, int wait_for_accept) {
pa_cond_signal(m->cond, 1);
if (wait_for_accept && m->n_waiting > 0)
if (wait_for_accept) {
m->n_waiting_for_accept ++;
while (m->n_waiting_for_accept > 0)
pa_cond_wait(m->accept_cond, m->mutex);
}
}
void pa_threaded_mainloop_wait(pa_threaded_mainloop *m) {
@ -214,6 +218,9 @@ void pa_threaded_mainloop_accept(pa_threaded_mainloop *m) {
/* Make sure that this function is not called from the helper thread */
pa_assert(!m->thread || !pa_thread_is_running(m->thread) || !in_worker(m));
pa_assert(m->n_waiting_for_accept > 0);
m->n_waiting_for_accept --;
pa_cond_signal(m->accept_cond, 0);
}

View file

@ -137,15 +137,19 @@ PA_C_DECL_BEGIN
* The main function, my_drain_stream_func(), will wait for the callback to
* be called using pa_threaded_mainloop_wait().
*
* If your application is multi-threaded, then this waiting must be done
* inside a while loop. The reason for this is that multiple threads might be
* using pa_threaded_mainloop_wait() at the same time. Each thread must
* therefore verify that it was its callback that was invoked.
* If your application is multi-threaded, then this waiting must be
* done inside a while loop. The reason for this is that multiple
* threads might be using pa_threaded_mainloop_wait() at the same
* time. Each thread must therefore verify that it was its callback
* that was invoked. Also the underlying OS synchronization primitives
* are usually not free of spurious wake-ups, so a
* pa_threaded_mainloop_wait() must be called within a loop even if
* you have only one thread waiting.
*
* The callback, my_drain_callback(), indicates to the main function that it
* has been called using pa_threaded_mainloop_signal().
*
* As you can see, both pa_threaded_mainloop_wait() may only be called with
* As you can see, pa_threaded_mainloop_wait() may only be called with
* the lock held. The same thing is true for pa_threaded_mainloop_signal(),
* but as the lock is held before the callback is invoked, you do not have to
* deal with that.
@ -274,7 +278,9 @@ void pa_threaded_mainloop_unlock(pa_threaded_mainloop *m);
* inside the event loop thread. Prior to this call the event loop
* object needs to be locked using pa_threaded_mainloop_lock(). While
* waiting the lock will be released, immediately before returning it
* will be acquired again. */
* will be acquired again. This function may spuriously wake up even
* without _signal() being called. You need to make sure to handle
* that! */
void pa_threaded_mainloop_wait(pa_threaded_mainloop *m);
/** Signal all threads waiting for a signalling event in

View file

@ -205,9 +205,12 @@ pa_volume_t pa_sw_volume_from_linear(double v) {
*
* http://www.robotplanet.dk/audio/audio_gui_design/
* http://lists.linuxaudio.org/pipermail/linux-audio-dev/2009-May/thread.html#23151
*
* We make sure that the conversion to linear and back yields the
* same volume value! That's why we need the lround() below!
*/
return (pa_volume_t) (cbrt(v) * PA_VOLUME_NORM);
return (pa_volume_t) lround(cbrt(v) * PA_VOLUME_NORM);
}
double pa_sw_volume_to_linear(pa_volume_t v) {

View file

@ -26,14 +26,16 @@
#include <unistd.h>
#include <errno.h>
#include <pulse/xmalloc.h>
#include <pulsecore/atomic.h>
#include <pulsecore/macro.h>
#include <pulsecore/log.h>
#include <pulsecore/thread.h>
#include <pulsecore/semaphore.h>
#include <pulsecore/macro.h>
#include <pulsecore/core-util.h>
#include <pulsecore/flist.h>
#include <pulse/xmalloc.h>
#include "asyncmsgq.h"
@ -76,7 +78,7 @@ static void asyncmsgq_free(pa_asyncmsgq *a) {
struct asyncmsgq_item *i;
pa_assert(a);
while ((i = pa_asyncq_pop(a->asyncq, 0))) {
while ((i = pa_asyncq_pop(a->asyncq, FALSE))) {
pa_assert(!i->semaphore);
@ -172,11 +174,11 @@ int pa_asyncmsgq_send(pa_asyncmsgq *a, pa_msgobject *object, int code, const voi
return i.ret;
}
int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, pa_bool_t wait) {
int pa_asyncmsgq_get(pa_asyncmsgq *a, pa_msgobject **object, int *code, void **userdata, int64_t *offset, pa_memchunk *chunk, pa_bool_t wait_op) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
pa_assert(!a->current);
if (!(a->current = pa_asyncq_pop(a->asyncq, wait))) {
if (!(a->current = pa_asyncq_pop(a->asyncq, wait_op))) {
/* pa_log("failure"); */
return -1;
}
@ -246,7 +248,7 @@ int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code) {
pa_memchunk chunk;
int ret;
if (pa_asyncmsgq_get(a, &o, &c, &data, &offset, &chunk, 1) < 0)
if (pa_asyncmsgq_get(a, &o, &c, &data, &offset, &chunk, TRUE) < 0)
return -1;
ret = pa_asyncmsgq_dispatch(o, c, data, offset, &chunk);
@ -269,7 +271,7 @@ int pa_asyncmsgq_process_one(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, 0) < 0)
if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, FALSE) < 0)
return 0;
pa_asyncmsgq_ref(a);
@ -323,3 +325,35 @@ int pa_asyncmsgq_dispatch(pa_msgobject *object, int code, void *userdata, int64_
return 0;
}
void pa_asyncmsgq_flush(pa_asyncmsgq *a, pa_bool_t run) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
for (;;) {
pa_msgobject *object;
int code;
void *data;
int64_t offset;
pa_memchunk chunk;
int ret;
if (pa_asyncmsgq_get(a, &object, &code, &data, &offset, &chunk, FALSE) < 0)
return;
if (!run) {
pa_asyncmsgq_done(a, -1);
continue;
}
pa_asyncmsgq_ref(a);
ret = pa_asyncmsgq_dispatch(object, code, data, offset, &chunk);
pa_asyncmsgq_done(a, ret);
pa_asyncmsgq_unref(a);
}
}
pa_bool_t pa_asyncmsgq_dispatching(pa_asyncmsgq *a) {
pa_assert(PA_REFCNT_VALUE(a) > 0);
return !!a->current;
}

View file

@ -66,6 +66,8 @@ void pa_asyncmsgq_done(pa_asyncmsgq *q, int ret);
int pa_asyncmsgq_wait_for(pa_asyncmsgq *a, int code);
int pa_asyncmsgq_process_one(pa_asyncmsgq *a);
void pa_asyncmsgq_flush(pa_asyncmsgq *a, pa_bool_t run);
/* For the reading side */
int pa_asyncmsgq_read_fd(pa_asyncmsgq *q);
int pa_asyncmsgq_read_before_poll(pa_asyncmsgq *a);
@ -76,4 +78,6 @@ int pa_asyncmsgq_write_fd(pa_asyncmsgq *q);
void pa_asyncmsgq_write_before_poll(pa_asyncmsgq *a);
void pa_asyncmsgq_write_after_poll(pa_asyncmsgq *a);
pa_bool_t pa_asyncmsgq_dispatching(pa_asyncmsgq *a);
#endif

View file

@ -39,6 +39,7 @@ struct pa_aupdate {
pa_atomic_t read_lock;
pa_mutex *write_lock;
pa_semaphore *semaphore;
pa_bool_t swapped;
};
pa_aupdate *pa_aupdate_new(void) {
@ -101,6 +102,8 @@ unsigned pa_aupdate_write_begin(pa_aupdate *a) {
n = (unsigned) pa_atomic_load(&a->read_lock);
a->swapped = FALSE;
return !WHICH(n);
}
@ -119,11 +122,16 @@ unsigned pa_aupdate_write_swap(pa_aupdate *a) {
break;
}
a->swapped = TRUE;
return WHICH(n);
}
void pa_aupdate_write_end(pa_aupdate *a) {
pa_assert(a);
if (!a->swapped)
pa_aupdate_write_swap(a);
pa_mutex_unlock(a->write_lock);
}

View file

@ -93,6 +93,10 @@ unsigned pa_aupdate_write_swap(pa_aupdate *a);
* pa_update_write_end(a)
* }
*
* In some cases keeping both structures up-to-date might not be
* necessary, since they are fully rebuilt on each iteration
* anyway. In that case you may leave the _write_swap() call out, it
* will then be done implicitly in the _write_end() invocation.
*/
#endif

View file

@ -36,6 +36,7 @@
#include <sys/stat.h>
#include <pulse/util.h>
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>
#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
@ -147,47 +148,46 @@ int pa_authkey_load(const char *path, void *data, size_t length) {
/* If the specified file path starts with / return it, otherwise
* return path prepended with home directory */
static const char *normalize_path(const char *fn, char *s, size_t l) {
static char *normalize_path(const char *fn) {
pa_assert(fn);
pa_assert(s);
pa_assert(l > 0);
#ifndef OS_IS_WIN32
if (fn[0] != '/') {
#else
if (strlen(fn) < 3 || !isalpha(fn[0]) || fn[1] != ':' || fn[2] != '\\') {
#endif
char homedir[PATH_MAX];
char *homedir, *s;
if (!pa_get_home_dir(homedir, sizeof(homedir)))
if (!(homedir = pa_get_home_dir_malloc()))
return NULL;
#ifndef OS_IS_WIN32
pa_snprintf(s, l, "%s/%s", homedir, fn);
#else
pa_snprintf(s, l, "%s\\%s", homedir, fn);
#endif
s = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", homedir, fn);
pa_xfree(homedir);
return s;
}
return fn;
return pa_xstrdup(fn);
}
/* Load a cookie from a file in the home directory. If the specified
* path starts with /, use it as absolute path instead. */
int pa_authkey_load_auto(const char *fn, void *data, size_t length) {
char path[PATH_MAX];
const char *p;
char *p;
int ret;
pa_assert(fn);
pa_assert(data);
pa_assert(length > 0);
if (!(p = normalize_path(fn, path, sizeof(path))))
if (!(p = normalize_path(fn)))
return -2;
return pa_authkey_load(p, data, length);
ret = pa_authkey_load(p, data, length);
pa_xfree(p);
return ret;
}
/* Store the specified cookie in the specified cookie file */
@ -195,14 +195,13 @@ int pa_authkey_save(const char *fn, const void *data, size_t length) {
int fd = -1;
int unlock = 0, ret = -1;
ssize_t r;
char path[PATH_MAX];
const char *p;
char *p;
pa_assert(fn);
pa_assert(data);
pa_assert(length > 0);
if (!(p = normalize_path(fn, path, sizeof(path))))
if (!(p = normalize_path(fn)))
return -2;
if ((fd = open(p, O_RDWR|O_CREAT|O_NOCTTY, S_IRUSR|S_IWUSR)) < 0) {
@ -232,5 +231,7 @@ finish:
}
}
pa_xfree(p);
return ret;
}

View file

@ -296,7 +296,7 @@ char *pa_sink_list_to_string(pa_core *c) {
pa_strbuf_printf(
s,
"\tfixed latency: %0.2f ms\n",
(double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC);
(double) pa_sink_get_fixed_latency(sink) / PA_USEC_PER_MSEC);
if (sink->card)
pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name);
@ -415,7 +415,7 @@ char *pa_source_list_to_string(pa_core *c) {
pa_strbuf_printf(
s,
"\tfixed latency: %0.2f ms\n",
(double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC);
(double) pa_source_get_fixed_latency(source) / PA_USEC_PER_MSEC);
if (source->monitor_of)
pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
@ -482,7 +482,7 @@ char *pa_source_output_list_to_string(pa_core *c) {
s,
" index: %u\n"
"\tdriver: <%s>\n"
"\tflags: %s%s%s%s%s%s%s%s%s%s\n"
"\tflags: %s%s%s%s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsource: %u <%s>\n"
"\tcurrent latency: %0.2f ms\n"
@ -501,7 +501,8 @@ char *pa_source_output_list_to_string(pa_core *c) {
o->flags & PA_SOURCE_OUTPUT_FIX_RATE ? "FIX_RATE " : "",
o->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
o->flags & PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND ? "DONT_INHIBIT_AUTO_SUSPEND " : "",
o->flags & PA_SOURCE_OUTPUT_FAIL_ON_SUSPEND ? "FAIL_ON_SUSPEND " : "",
o->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND ? "NO_CREATE_ON_SUSPEND " : "",
o->flags & PA_SOURCE_OUTPUT_KILL_ON_SUSPEND ? "KILL_ON_SUSPEND " : "",
state_table[pa_source_output_get_state(o)],
o->source->index, o->source->name,
(double) pa_source_output_get_latency(o, NULL) / PA_USEC_PER_MSEC,
@ -564,7 +565,7 @@ char *pa_sink_input_list_to_string(pa_core *c) {
s,
" index: %u\n"
"\tdriver: <%s>\n"
"\tflags: %s%s%s%s%s%s%s%s%s%s\n"
"\tflags: %s%s%s%s%s%s%s%s%s%s%s\n"
"\tstate: %s\n"
"\tsink: %u <%s>\n"
"\tvolume: %s\n"
@ -587,7 +588,8 @@ char *pa_sink_input_list_to_string(pa_core *c) {
i->flags & PA_SINK_INPUT_FIX_RATE ? "FIX_RATE " : "",
i->flags & PA_SINK_INPUT_FIX_CHANNELS ? "FIX_CHANNELS " : "",
i->flags & PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND ? "DONT_INHIBIT_AUTO_SUSPEND " : "",
i->flags & PA_SINK_INPUT_FAIL_ON_SUSPEND ? "FAIL_ON_SUSPEND " : "",
i->flags & PA_SINK_INPUT_NO_CREATE_ON_SUSPEND ? "NO_CREATE_SUSPEND " : "",
i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND ? "KILL_ON_SUSPEND " : "",
state_table[pa_sink_input_get_state(i)],
i->sink->index, i->sink->name,
pa_cvolume_snprint(cv, sizeof(cv), &v),

View file

@ -278,6 +278,30 @@ int pa_config_parse_bool(const char *filename, unsigned line, const char *sectio
return 0;
}
int pa_config_parse_not_bool(
const char *filename, unsigned line,
const char *section,
const char *lvalue, const char *rvalue,
void *data, void *userdata) {
int k;
pa_bool_t *b = data;
pa_assert(filename);
pa_assert(lvalue);
pa_assert(rvalue);
pa_assert(data);
if ((k = pa_parse_boolean(rvalue)) < 0) {
pa_log("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
return -1;
}
*b = !k;
return 0;
}
int pa_config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata) {
char **s = data;

View file

@ -47,6 +47,7 @@ int pa_config_parse_int(const char *filename, unsigned line, const char *section
int pa_config_parse_unsigned(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_size(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_not_bool(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
int pa_config_parse_string(const char *filename, unsigned line, const char *section, const char *lvalue, const char *rvalue, void *data, void *userdata);
#endif

View file

@ -494,13 +494,14 @@ int pa_scache_add_directory_lazy(pa_core *c, const char *pathname) {
struct dirent *e;
while ((e = readdir(dir))) {
char p[PATH_MAX];
char *p;
if (e->d_name[0] == '.')
continue;
pa_snprintf(p, sizeof(p), "%s/%s", pathname, e->d_name);
p = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", pathname, e->d_name);
add_file(c, p);
pa_xfree(p);
}
closedir(dir);

View file

@ -101,6 +101,10 @@
#include "rtkit.h"
#endif
#ifdef __linux__
#include <sys/personality.h>
#endif
#include <pulse/xmalloc.h>
#include <pulse/util.h>
#include <pulse/utf8.h>
@ -552,12 +556,20 @@ char *pa_vsprintf_malloc(const char *format, va_list ap) {
/* Similar to OpenBSD's strlcpy() function */
char *pa_strlcpy(char *b, const char *s, size_t l) {
size_t k;
pa_assert(b);
pa_assert(s);
pa_assert(l > 0);
strncpy(b, s, l);
b[l-1] = 0;
k = strlen(s);
if (k > l-1)
k = l-1;
memcpy(b, s, k);
b[k] = 0;
return b;
}
@ -1328,26 +1340,32 @@ int pa_unlock_lockfile(const char *fn, int fd) {
}
static char *get_pulse_home(void) {
char h[PATH_MAX];
char *h;
struct stat st;
char *ret = NULL;
if (!pa_get_home_dir(h, sizeof(h))) {
if (!(h = pa_get_home_dir_malloc())) {
pa_log_error("Failed to get home directory.");
return NULL;
}
if (stat(h, &st) < 0) {
pa_log_error("Failed to stat home directory %s: %s", h, pa_cstrerror(errno));
return NULL;
goto finish;
}
if (st.st_uid != getuid()) {
pa_log_error("Home directory %s not ours.", h);
errno = EACCES;
return NULL;
goto finish;
}
return pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
ret = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse", h);
finish:
pa_xfree(h);
return ret;
}
char *pa_get_state_dir(void) {
@ -1372,6 +1390,50 @@ char *pa_get_state_dir(void) {
return d;
}
char *pa_get_home_dir_malloc(void) {
char *homedir;
size_t allocated = 128;
for (;;) {
homedir = pa_xmalloc(allocated);
if (!pa_get_home_dir(homedir, allocated)) {
pa_xfree(homedir);
return NULL;
}
if (strlen(homedir) < allocated - 1)
break;
pa_xfree(homedir);
allocated *= 2;
}
return homedir;
}
char *pa_get_binary_name_malloc(void) {
char *t;
size_t allocated = 128;
for (;;) {
t = pa_xmalloc(allocated);
if (!pa_get_binary_name(t, allocated)) {
pa_xfree(t);
return NULL;
}
if (strlen(t) < allocated - 1)
break;
pa_xfree(t);
allocated *= 2;
}
return t;
}
static char* make_random_dir(mode_t m) {
static const char table[] =
"abcdefghijklmnopqrstuvwxyz"
@ -1481,7 +1543,7 @@ char *pa_get_runtime_dir(void) {
goto fail;
}
k = pa_sprintf_malloc("%s" PA_PATH_SEP "%s:runtime", d, mid);
k = pa_sprintf_malloc("%s" PA_PATH_SEP "%s-runtime", d, mid);
pa_xfree(d);
pa_xfree(mid);
@ -1626,14 +1688,15 @@ FILE *pa_open_config_file(const char *global, const char *local, const char *env
if (local) {
const char *e;
char *lfn;
char h[PATH_MAX];
char *h;
FILE *f;
if ((e = getenv("PULSE_CONFIG_PATH")))
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
else if (pa_get_home_dir(h, sizeof(h)))
else if ((h = pa_get_home_dir_malloc())) {
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
else
pa_xfree(h);
} else
return NULL;
#ifdef OS_IS_WIN32
@ -1713,13 +1776,14 @@ char *pa_find_config_file(const char *global, const char *local, const char *env
if (local) {
const char *e;
char *lfn;
char h[PATH_MAX];
char *h;
if ((e = getenv("PULSE_CONFIG_PATH")))
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", e, local);
else if (pa_get_home_dir(h, sizeof(h)))
else if ((h = pa_get_home_dir_malloc())) {
fn = lfn = pa_sprintf_malloc("%s" PA_PATH_SEP ".pulse" PA_PATH_SEP "%s", h, local);
else
pa_xfree(h);
} else
return NULL;
#ifdef OS_IS_WIN32
@ -1904,7 +1968,7 @@ static char *get_path(const char *fn, pa_bool_t prependmid, pa_bool_t rt) {
return NULL;
}
r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s:%s", rtp, mid, fn);
r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s-%s", rtp, mid, fn);
pa_xfree(mid);
} else
r = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", rtp, fn);
@ -2801,3 +2865,28 @@ char* pa_maybe_prefix_path(const char *path, const char *prefix) {
return pa_sprintf_malloc("%s" PA_PATH_SEP "%s", prefix, path);
}
size_t pa_pipe_buf(int fd) {
#ifdef _PC_PIPE_BUF
long n;
if ((n = fpathconf(fd, _PC_PIPE_BUF)) >= 0)
return (size_t) n;
#endif
#ifdef PIPE_BUF
return PIPE_BUF;
#else
return 4096;
#endif
}
void pa_reset_personality(void) {
#ifdef __linux__
if (personality(PER_LINUX) < 0)
pa_log_warn("Uh, personality() failed: %s", pa_cstrerror(errno));
#endif
}

View file

@ -126,6 +126,8 @@ char* pa_find_config_file(const char *global, const char *local, const char *env
char *pa_get_runtime_dir(void);
char *pa_get_state_dir(void);
char *pa_get_home_dir_malloc(void);
char *pa_get_binary_name_malloc(void);
char *pa_runtime_path(const char *fn);
char *pa_state_path(const char *fn, pa_bool_t prepend_machine_id);
@ -243,4 +245,9 @@ char **pa_split_spaces_strv(const char *s);
char* pa_maybe_prefix_path(const char *path, const char *prefix);
/* Returns size of the specified pipe or 4096 on failure */
size_t pa_pipe_buf(int fd);
void pa_reset_personality(void);
#endif

Some files were not shown because too many files have changed in this diff Show more