summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFelipe Sateler <fsateler@debian.org>2016-05-13 20:52:16 -0300
committerFelipe Sateler <fsateler@debian.org>2016-05-13 20:52:16 -0300
commite8f565a24d9a69ae696f0600f2ea992ddbd9f7c6 (patch)
tree16aaf7ab080b2f314fe3a239774e8f3768bfcfe8 /src
parentae999803298bb86b891d5ecefb5672e199a8ff2b (diff)
Imported Upstream version 8.99.1
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am22
-rw-r--r--src/Makefile.in574
-rw-r--r--src/daemon/cmdline.c13
-rw-r--r--src/daemon/daemon-conf.c2
-rw-r--r--src/daemon/daemon-conf.h1
-rwxr-xr-xsrc/daemon/default.pa.in31
-rw-r--r--src/daemon/main.c4
-rw-r--r--src/modules/alsa/alsa-mixer.c436
-rw-r--r--src/modules/alsa/mixer/paths/analog-input-linein.conf4
-rw-r--r--src/modules/alsa/mixer/paths/analog-input-mic.conf4
-rw-r--r--src/modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf17
-rw-r--r--src/modules/alsa/module-alsa-card.c32
-rw-r--r--src/modules/echo-cancel/adrian.c18
-rw-r--r--src/modules/echo-cancel/echo-cancel.h14
-rw-r--r--src/modules/echo-cancel/module-echo-cancel.c95
-rw-r--r--src/modules/echo-cancel/null.c4
-rw-r--r--src/modules/echo-cancel/speex.c58
-rw-r--r--src/modules/echo-cancel/webrtc.cc437
-rw-r--r--src/modules/macosx/module-coreaudio-device.c68
-rw-r--r--src/modules/module-card-restore.c72
-rw-r--r--src/modules/module-detect.c3
-rw-r--r--src/modules/module-device-manager.c32
-rw-r--r--src/modules/module-filter-apply.c86
-rw-r--r--src/modules/module-loopback.c28
-rw-r--r--src/modules/module-rescue-streams.c2
-rw-r--r--src/modules/module-role-cork.c256
-rw-r--r--src/modules/module-role-ducking.c267
-rw-r--r--src/modules/module-solaris.c12
-rw-r--r--src/modules/module-stream-restore.c6
-rw-r--r--src/modules/module-switch-on-port-available.c240
-rw-r--r--src/modules/module-tunnel.c2
-rw-r--r--src/modules/rtp/module-rtp-recv.c18
-rw-r--r--src/modules/stream-interaction.c550
-rw-r--r--src/modules/stream-interaction.h21
-rw-r--r--src/pulse/client-conf.c1
-rw-r--r--src/pulse/client-conf.h2
-rw-r--r--src/pulse/context.c103
-rw-r--r--src/pulse/glib-mainloop.c2
-rw-r--r--src/pulse/internal.h3
-rw-r--r--src/pulse/sample.h2
-rw-r--r--src/pulse/stream.h4
-rw-r--r--src/pulse/version.h6
-rw-r--r--src/pulsecore/card.c60
-rw-r--r--src/pulsecore/card.h14
-rw-r--r--src/pulsecore/core-util.c4
-rw-r--r--src/pulsecore/core.c24
-rw-r--r--src/pulsecore/core.h15
-rw-r--r--src/pulsecore/creds.h5
-rw-r--r--src/pulsecore/filter/lfe-filter.c5
-rw-r--r--src/pulsecore/iochannel.c1
-rw-r--r--src/pulsecore/log.c11
-rw-r--r--src/pulsecore/macro.h6
-rw-r--r--src/pulsecore/mem.h60
-rw-r--r--src/pulsecore/memblock.c277
-rw-r--r--src/pulsecore/memblock.h22
-rw-r--r--src/pulsecore/memblockq.c5
-rw-r--r--src/pulsecore/memchunk.c5
-rw-r--r--src/pulsecore/memfd-wrappers.h68
-rw-r--r--src/pulsecore/native-common.c78
-rw-r--r--src/pulsecore/native-common.h11
-rw-r--r--src/pulsecore/pdispatch.c30
-rw-r--r--src/pulsecore/pdispatch.h5
-rw-r--r--src/pulsecore/protocol-native.c118
-rw-r--r--src/pulsecore/pstream-util.c102
-rw-r--r--src/pulsecore/pstream-util.h4
-rw-r--r--src/pulsecore/pstream.c200
-rw-r--r--src/pulsecore/pstream.h8
-rw-r--r--src/pulsecore/shm.c293
-rw-r--r--src/pulsecore/shm.h23
-rw-r--r--src/pulsecore/sink-input.c145
-rw-r--r--src/pulsecore/sink-input.h4
-rw-r--r--src/pulsecore/sink.c7
-rw-r--r--src/pulsecore/sink.h6
-rw-r--r--src/pulsecore/source-output.c168
-rw-r--r--src/pulsecore/source-output.h4
-rw-r--r--src/pulsecore/source.c5
-rw-r--r--src/pulsecore/source.h6
-rw-r--r--src/tests/connect-stress.c15
-rw-r--r--src/tests/cpu-mix-test.c6
-rw-r--r--src/tests/cpu-remap-test.c4
-rw-r--r--src/tests/cpu-sconv-test.c4
-rw-r--r--src/tests/cpu-volume-test.c2
-rw-r--r--src/tests/cpulimit-test.c4
-rw-r--r--src/tests/extended-test.c4
-rw-r--r--src/tests/get-binary-name-test.c2
-rw-r--r--src/tests/interpol-test.c2
-rw-r--r--src/tests/lfe-filter-test.c4
-rw-r--r--src/tests/mcalign-test.c4
-rw-r--r--src/tests/memblock-test.c21
-rw-r--r--src/tests/memblockq-test.c4
-rw-r--r--src/tests/mix-test.c4
-rw-r--r--src/tests/mult-s16-test.c2
-rw-r--r--src/tests/remix-test.c4
-rw-r--r--src/tests/resampler-test.c46
-rw-r--r--src/tests/srbchannel-test.c6
-rw-r--r--src/tests/sync-playback.c4
-rw-r--r--src/utils/padsp.c6
97 files changed, 3782 insertions, 1717 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index b0ca2bc..b600dfb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -48,9 +48,10 @@ AM_CPPFLAGS = \
-DPA_SRCDIR=\"$(abs_srcdir)\" \
-DPA_BUILDDIR=\"$(abs_builddir)\" \
-DPULSE_LOCALEDIR=\"$(localedir)\"
-AM_CFLAGS = \
+AM_CFLAGS = -std=gnu11 \
+ $(PTHREAD_CFLAGS)
+AM_CXXFLAGS = -std=c++11 \
$(PTHREAD_CFLAGS)
-AM_CXXFLAGS = $(AM_CFLAGS)
SERVER_CFLAGS = -D__INCLUDED_FROM_PULSE_AUDIO
AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS)
@@ -313,12 +314,13 @@ endif
if HAVE_GLIB20
TESTS_default += \
mainloop-test-glib
-endif
if HAVE_GTK30
+# gtk-test depends on both glib and gtk
TESTS_norun += \
gtk-test
endif
+endif
if HAVE_ALSA
TESTS_norun += \
@@ -682,7 +684,7 @@ libpulsecommon_@PA_MAJORMINOR@_la_SOURCES = \
pulsecore/memblock.c pulsecore/memblock.h \
pulsecore/memblockq.c pulsecore/memblockq.h \
pulsecore/memchunk.c pulsecore/memchunk.h \
- pulsecore/native-common.h \
+ pulsecore/native-common.c pulsecore/native-common.h \
pulsecore/once.c pulsecore/once.h \
pulsecore/packet.c pulsecore/packet.h \
pulsecore/parseaddr.c pulsecore/parseaddr.h \
@@ -699,6 +701,7 @@ libpulsecommon_@PA_MAJORMINOR@_la_SOURCES = \
pulsecore/refcnt.h \
pulsecore/srbchannel.c pulsecore/srbchannel.h \
pulsecore/sample-util.c pulsecore/sample-util.h \
+ pulsecore/mem.h \
pulsecore/shm.c pulsecore/shm.h \
pulsecore/bitset.c pulsecore/bitset.h \
pulsecore/socket-client.c pulsecore/socket-client.h \
@@ -727,6 +730,11 @@ libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBJSON_CFLAGS) $(LIBS
libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
libpulsecommon_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBJSON_LIBS) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBSNDFILE_LIBS)
+if HAVE_MEMFD
+libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += \
+ pulsecore/memfd-wrappers.h
+endif
+
if HAVE_X11
libpulsecommon_@PA_MAJORMINOR@_la_SOURCES += \
pulse/client-conf-x11.c pulse/client-conf-x11.h \
@@ -1939,7 +1947,8 @@ module_position_event_sounds_la_LIBADD = $(MODULE_LIBADD)
module_position_event_sounds_la_CFLAGS = $(AM_CFLAGS)
# Ducking effect based on stream roles
-module_role_ducking_la_SOURCES = modules/module-role-ducking.c
+module_role_ducking_la_SOURCES = modules/module-role-ducking.c \
+ modules/stream-interaction.c modules/stream-interaction.h
module_role_ducking_la_LDFLAGS = $(MODULE_LDFLAGS)
module_role_ducking_la_LIBADD = $(MODULE_LIBADD)
module_role_ducking_la_CFLAGS = $(AM_CFLAGS)
@@ -1952,7 +1961,8 @@ module_augment_properties_la_LIBADD = $(MODULE_LIBADD)
module_augment_properties_la_CFLAGS = $(AM_CFLAGS) -DDESKTOPFILEDIR=\"/usr/share/applications\"
# Cork certain streams while others are active (e.g. cork music when phone streams appear)
-module_role_cork_la_SOURCES = modules/module-role-cork.c
+module_role_cork_la_SOURCES = modules/module-role-cork.c \
+ modules/stream-interaction.c modules/stream-interaction.h
module_role_cork_la_LDFLAGS = $(MODULE_LDFLAGS)
module_role_cork_la_LIBADD = $(MODULE_LIBADD)
module_role_cork_la_CFLAGS = $(AM_CFLAGS)
diff --git a/src/Makefile.in b/src/Makefile.in
index 158cabb..25c8668 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -171,8 +171,10 @@ bin_PROGRAMS = pulseaudio$(EXEEXT) pacat$(EXEEXT) pactl$(EXEEXT) \
@HAVE_GLIB20_TRUE@am__append_21 = \
@HAVE_GLIB20_TRUE@ mainloop-test-glib
-@HAVE_GTK30_TRUE@am__append_22 = \
-@HAVE_GTK30_TRUE@ gtk-test
+
+# gtk-test depends on both glib and gtk
+@HAVE_GLIB20_TRUE@@HAVE_GTK30_TRUE@am__append_22 = \
+@HAVE_GLIB20_TRUE@@HAVE_GTK30_TRUE@ gtk-test
@HAVE_ALSA_TRUE@am__append_23 = \
@HAVE_ALSA_TRUE@ alsa-time-test
@@ -193,214 +195,217 @@ bin_PROGRAMS = pulseaudio$(EXEEXT) pacat$(EXEEXT) pactl$(EXEEXT) \
@OS_IS_WIN32_TRUE@ pulsecore/winerrno.h
@OS_IS_WIN32_FALSE@am__append_26 = pulsecore/poll-posix.c pulsecore/poll.h
-@HAVE_X11_TRUE@am__append_27 = \
+@HAVE_MEMFD_TRUE@am__append_27 = \
+@HAVE_MEMFD_TRUE@ pulsecore/memfd-wrappers.h
+
+@HAVE_X11_TRUE@am__append_28 = \
@HAVE_X11_TRUE@ pulse/client-conf-x11.c pulse/client-conf-x11.h \
@HAVE_X11_TRUE@ pulsecore/x11prop.c pulsecore/x11prop.h
-@HAVE_X11_TRUE@am__append_28 = $(X11_CFLAGS)
-@HAVE_X11_TRUE@am__append_29 = $(X11_LIBS)
-@HAVE_SYSTEMD_DAEMON_TRUE@am__append_30 = $(SYSTEMD_FLAGS) $(SYSTEMDDAEMON_FLAGS)
-@HAVE_SYSTEMD_DAEMON_TRUE@am__append_31 = $(SYSTEMD_LIBS) $(SYSTEMDDAEMON_LIBS)
-@HAVE_SYSTEMD_JOURNAL_TRUE@am__append_32 = $(SYSTEMD_FLAGS) $(SYSTEMDJOURNAL_FLAGS)
-@HAVE_SYSTEMD_JOURNAL_TRUE@am__append_33 = $(SYSTEMD_LIBS) $(SYSTEMDJOURNAL_LIBS)
-@OS_IS_WIN32_TRUE@am__append_34 = \
+@HAVE_X11_TRUE@am__append_29 = $(X11_CFLAGS)
+@HAVE_X11_TRUE@am__append_30 = $(X11_LIBS)
+@HAVE_SYSTEMD_DAEMON_TRUE@am__append_31 = $(SYSTEMD_FLAGS) $(SYSTEMDDAEMON_FLAGS)
+@HAVE_SYSTEMD_DAEMON_TRUE@am__append_32 = $(SYSTEMD_LIBS) $(SYSTEMDDAEMON_LIBS)
+@HAVE_SYSTEMD_JOURNAL_TRUE@am__append_33 = $(SYSTEMD_FLAGS) $(SYSTEMDJOURNAL_FLAGS)
+@HAVE_SYSTEMD_JOURNAL_TRUE@am__append_34 = $(SYSTEMD_LIBS) $(SYSTEMDJOURNAL_LIBS)
+@OS_IS_WIN32_TRUE@am__append_35 = \
@OS_IS_WIN32_TRUE@ pulsecore/mutex-win32.c pulsecore/mutex.h \
@OS_IS_WIN32_TRUE@ pulsecore/thread-win32.c pulsecore/thread.h \
@OS_IS_WIN32_TRUE@ pulsecore/semaphore-win32.c pulsecore/semaphore.h
-@OS_IS_DARWIN_TRUE@@OS_IS_WIN32_FALSE@am__append_35 = \
+@OS_IS_DARWIN_TRUE@@OS_IS_WIN32_FALSE@am__append_36 = \
@OS_IS_DARWIN_TRUE@@OS_IS_WIN32_FALSE@ pulsecore/mutex-posix.c pulsecore/mutex.h \
@OS_IS_DARWIN_TRUE@@OS_IS_WIN32_FALSE@ pulsecore/thread-posix.c pulsecore/thread.h \
@OS_IS_DARWIN_TRUE@@OS_IS_WIN32_FALSE@ pulsecore/semaphore-osx.c pulsecore/semaphore.h
-@OS_IS_DARWIN_TRUE@@OS_IS_WIN32_FALSE@am__append_36 = "-I/Developer/Headers/FlatCarbon/"
+@OS_IS_DARWIN_TRUE@@OS_IS_WIN32_FALSE@am__append_37 = "-I/Developer/Headers/FlatCarbon/"
#libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS += "-framework CoreServices"
-@OS_IS_DARWIN_FALSE@@OS_IS_WIN32_FALSE@am__append_37 = \
+@OS_IS_DARWIN_FALSE@@OS_IS_WIN32_FALSE@am__append_38 = \
@OS_IS_DARWIN_FALSE@@OS_IS_WIN32_FALSE@ pulsecore/mutex-posix.c pulsecore/mutex.h \
@OS_IS_DARWIN_FALSE@@OS_IS_WIN32_FALSE@ pulsecore/thread-posix.c pulsecore/thread.h \
@OS_IS_DARWIN_FALSE@@OS_IS_WIN32_FALSE@ pulsecore/semaphore-posix.c pulsecore/semaphore.h
-@HAVE_LIBASYNCNS_TRUE@am__append_38 = $(LIBASYNCNS_CFLAGS)
-@HAVE_LIBASYNCNS_TRUE@am__append_39 = $(LIBASYNCNS_LIBS)
-@OS_IS_WIN32_TRUE@am__append_40 = pulsecore/dllmain.c
-@HAVE_DBUS_TRUE@am__append_41 = \
+@HAVE_LIBASYNCNS_TRUE@am__append_39 = $(LIBASYNCNS_CFLAGS)
+@HAVE_LIBASYNCNS_TRUE@am__append_40 = $(LIBASYNCNS_LIBS)
+@OS_IS_WIN32_TRUE@am__append_41 = pulsecore/dllmain.c
+@HAVE_DBUS_TRUE@am__append_42 = \
@HAVE_DBUS_TRUE@ pulsecore/dbus-util.c pulsecore/dbus-util.h \
@HAVE_DBUS_TRUE@ pulsecore/rtkit.c pulsecore/rtkit.h
-@HAVE_DBUS_TRUE@am__append_42 = $(DBUS_CFLAGS)
-@HAVE_DBUS_TRUE@am__append_43 = $(DBUS_LIBS)
-@HAVE_GLIB20_TRUE@am__append_44 = \
+@HAVE_DBUS_TRUE@am__append_43 = $(DBUS_CFLAGS)
+@HAVE_DBUS_TRUE@am__append_44 = $(DBUS_LIBS)
+@HAVE_GLIB20_TRUE@am__append_45 = \
@HAVE_GLIB20_TRUE@ pulse/glib-mainloop.h
-@HAVE_GLIB20_TRUE@am__append_45 = \
+@HAVE_GLIB20_TRUE@am__append_46 = \
@HAVE_GLIB20_TRUE@ libpulse-mainloop-glib.la
-@HAVE_DBUS_TRUE@am__append_46 = $(DBUS_CFLAGS)
-@HAVE_DBUS_TRUE@am__append_47 = $(DBUS_LIBS)
-@HAVE_OSS_WRAPPER_TRUE@am__append_48 = padsp
+@HAVE_DBUS_TRUE@am__append_47 = $(DBUS_CFLAGS)
+@HAVE_DBUS_TRUE@am__append_48 = $(DBUS_LIBS)
@HAVE_OSS_WRAPPER_TRUE@am__append_49 = padsp
-@HAVE_NEON_TRUE@am__append_50 = libpulsecore_sconv_neon.la libpulsecore_mix_neon.la libpulsecore_remap_neon.la
+@HAVE_OSS_WRAPPER_TRUE@am__append_50 = padsp
@HAVE_NEON_TRUE@am__append_51 = libpulsecore_sconv_neon.la libpulsecore_mix_neon.la libpulsecore_remap_neon.la
-@HAVE_ORC_TRUE@am__append_52 = pulsecore/svolume_orc.c
-@HAVE_ORC_TRUE@am__append_53 = $(ORC_CFLAGS)
-@HAVE_ORC_TRUE@am__append_54 = $(ORC_LIBS)
-@HAVE_X11_TRUE@am__append_55 = pulsecore/x11wrap.c pulsecore/x11wrap.h
-@HAVE_X11_TRUE@am__append_56 = $(X11_CFLAGS)
-@HAVE_X11_TRUE@am__append_57 = $(X11_LIBS)
-@HAVE_DBUS_TRUE@am__append_58 = \
+@HAVE_NEON_TRUE@am__append_52 = libpulsecore_sconv_neon.la libpulsecore_mix_neon.la libpulsecore_remap_neon.la
+@HAVE_ORC_TRUE@am__append_53 = pulsecore/svolume_orc.c
+@HAVE_ORC_TRUE@am__append_54 = $(ORC_CFLAGS)
+@HAVE_ORC_TRUE@am__append_55 = $(ORC_LIBS)
+@HAVE_X11_TRUE@am__append_56 = pulsecore/x11wrap.c pulsecore/x11wrap.h
+@HAVE_X11_TRUE@am__append_57 = $(X11_CFLAGS)
+@HAVE_X11_TRUE@am__append_58 = $(X11_LIBS)
+@HAVE_DBUS_TRUE@am__append_59 = \
@HAVE_DBUS_TRUE@ pulsecore/dbus-shared.c pulsecore/dbus-shared.h \
@HAVE_DBUS_TRUE@ pulsecore/protocol-dbus.c pulsecore/protocol-dbus.h
-@HAVE_DBUS_TRUE@am__append_59 = $(DBUS_CFLAGS)
-@HAVE_DBUS_TRUE@am__append_60 = $(DBUS_LIBS)
-@HAVE_GDBM_TRUE@am__append_61 = pulsecore/database-gdbm.c
-@HAVE_GDBM_TRUE@am__append_62 = $(GDBM_CFLAGS)
-@HAVE_GDBM_TRUE@am__append_63 = $(GDBM_LIBS)
-@HAVE_TDB_TRUE@am__append_64 = pulsecore/database-tdb.c
-@HAVE_TDB_TRUE@am__append_65 = $(TDB_CFLAGS)
-@HAVE_TDB_TRUE@am__append_66 = $(TDB_LIBS)
-@HAVE_SIMPLEDB_TRUE@am__append_67 = pulsecore/database-simple.c
-@HAVE_SPEEX_TRUE@am__append_68 = pulsecore/resampler/speex.c
-@HAVE_SPEEX_TRUE@am__append_69 = $(LIBSPEEX_CFLAGS)
-@HAVE_SPEEX_TRUE@am__append_70 = $(LIBSPEEX_LIBS)
-@HAVE_SOXR_TRUE@am__append_71 = pulsecore/resampler/soxr.c
-@HAVE_SOXR_TRUE@am__append_72 = $(LIBSOXR_CFLAGS)
-@HAVE_SOXR_TRUE@am__append_73 = $(LIBSOXR_LIBS)
-@HAVE_LIBSAMPLERATE_TRUE@am__append_74 = pulsecore/resampler/libsamplerate.c
-@HAVE_LIBSAMPLERATE_TRUE@am__append_75 = $(LIBSAMPLERATE_CFLAGS)
-@HAVE_LIBSAMPLERATE_TRUE@am__append_76 = $(LIBSAMPLERATE_LIBS)
-@HAVE_WEBRTC_TRUE@am__append_77 = libwebrtc-util.la
-@HAVE_ESOUND_TRUE@am__append_78 = \
+@HAVE_DBUS_TRUE@am__append_60 = $(DBUS_CFLAGS)
+@HAVE_DBUS_TRUE@am__append_61 = $(DBUS_LIBS)
+@HAVE_GDBM_TRUE@am__append_62 = pulsecore/database-gdbm.c
+@HAVE_GDBM_TRUE@am__append_63 = $(GDBM_CFLAGS)
+@HAVE_GDBM_TRUE@am__append_64 = $(GDBM_LIBS)
+@HAVE_TDB_TRUE@am__append_65 = pulsecore/database-tdb.c
+@HAVE_TDB_TRUE@am__append_66 = $(TDB_CFLAGS)
+@HAVE_TDB_TRUE@am__append_67 = $(TDB_LIBS)
+@HAVE_SIMPLEDB_TRUE@am__append_68 = pulsecore/database-simple.c
+@HAVE_SPEEX_TRUE@am__append_69 = pulsecore/resampler/speex.c
+@HAVE_SPEEX_TRUE@am__append_70 = $(LIBSPEEX_CFLAGS)
+@HAVE_SPEEX_TRUE@am__append_71 = $(LIBSPEEX_LIBS)
+@HAVE_SOXR_TRUE@am__append_72 = pulsecore/resampler/soxr.c
+@HAVE_SOXR_TRUE@am__append_73 = $(LIBSOXR_CFLAGS)
+@HAVE_SOXR_TRUE@am__append_74 = $(LIBSOXR_LIBS)
+@HAVE_LIBSAMPLERATE_TRUE@am__append_75 = pulsecore/resampler/libsamplerate.c
+@HAVE_LIBSAMPLERATE_TRUE@am__append_76 = $(LIBSAMPLERATE_CFLAGS)
+@HAVE_LIBSAMPLERATE_TRUE@am__append_77 = $(LIBSAMPLERATE_LIBS)
+@HAVE_WEBRTC_TRUE@am__append_78 = libwebrtc-util.la
+@HAVE_ESOUND_TRUE@am__append_79 = \
@HAVE_ESOUND_TRUE@ libprotocol-esound.la
# We need to emulate sendmsg/recvmsg to support this on Win32
-@OS_IS_WIN32_FALSE@am__append_79 = \
+@OS_IS_WIN32_FALSE@am__append_80 = \
@OS_IS_WIN32_FALSE@ librtp.la
-@HAVE_AVAHI_TRUE@am__append_80 = \
+@HAVE_AVAHI_TRUE@am__append_81 = \
@HAVE_AVAHI_TRUE@ libavahi-wrap.la
-@HAVE_DBUS_TRUE@am__append_81 = $(DBUS_CFLAGS)
-@HAVE_DBUS_TRUE@am__append_82 = $(DBUS_LIBS)
+@HAVE_DBUS_TRUE@am__append_82 = $(DBUS_CFLAGS)
+@HAVE_DBUS_TRUE@am__append_83 = $(DBUS_LIBS)
###################################
# Plug-in libraries #
###################################
# Serveral module (e.g. libalsa-util.la)
-@HAVE_DBUS_TRUE@am__append_83 = \
+@HAVE_DBUS_TRUE@am__append_84 = \
@HAVE_DBUS_TRUE@ module-console-kit.la
-@HAVE_ESOUND_TRUE@am__append_84 = \
+@HAVE_ESOUND_TRUE@am__append_85 = \
@HAVE_ESOUND_TRUE@ module-esound-protocol-tcp.la \
@HAVE_ESOUND_TRUE@ module-esound-sink.la
# See comment at librtp.la above
-@OS_IS_WIN32_FALSE@am__append_85 = \
+@OS_IS_WIN32_FALSE@am__append_86 = \
@OS_IS_WIN32_FALSE@ module-rtp-send.la \
@OS_IS_WIN32_FALSE@ module-rtp-recv.la
-@HAVE_AF_UNIX_TRUE@am__append_86 = \
+@HAVE_AF_UNIX_TRUE@am__append_87 = \
@HAVE_AF_UNIX_TRUE@ module-cli-protocol-unix.la \
@HAVE_AF_UNIX_TRUE@ module-simple-protocol-unix.la \
@HAVE_AF_UNIX_TRUE@ module-http-protocol-unix.la \
@HAVE_AF_UNIX_TRUE@ module-native-protocol-unix.la
-@HAVE_AF_UNIX_TRUE@@HAVE_ESOUND_TRUE@am__append_87 = \
+@HAVE_AF_UNIX_TRUE@@HAVE_ESOUND_TRUE@am__append_88 = \
@HAVE_AF_UNIX_TRUE@@HAVE_ESOUND_TRUE@ module-esound-protocol-unix.la
-@HAVE_MKFIFO_TRUE@am__append_88 = \
+@HAVE_MKFIFO_TRUE@am__append_89 = \
@HAVE_MKFIFO_TRUE@ module-pipe-sink.la \
@HAVE_MKFIFO_TRUE@ module-pipe-source.la
-@HAVE_ESOUND_TRUE@@OS_IS_WIN32_FALSE@am__append_89 = \
+@HAVE_ESOUND_TRUE@@OS_IS_WIN32_FALSE@am__append_90 = \
@HAVE_ESOUND_TRUE@@OS_IS_WIN32_FALSE@ module-esound-compat-spawnfd.la \
@HAVE_ESOUND_TRUE@@OS_IS_WIN32_FALSE@ module-esound-compat-spawnpid.la
-@HAVE_REGEX_TRUE@am__append_90 = \
+@HAVE_REGEX_TRUE@am__append_91 = \
@HAVE_REGEX_TRUE@ module-match.la
-@HAVE_X11_TRUE@am__append_91 = \
+@HAVE_X11_TRUE@am__append_92 = \
@HAVE_X11_TRUE@ module-x11-bell.la \
@HAVE_X11_TRUE@ module-x11-publish.la \
@HAVE_X11_TRUE@ module-x11-xsmp.la \
@HAVE_X11_TRUE@ module-x11-cork-request.la
-@HAVE_OSS_OUTPUT_TRUE@am__append_92 = \
+@HAVE_OSS_OUTPUT_TRUE@am__append_93 = \
@HAVE_OSS_OUTPUT_TRUE@ liboss-util.la \
@HAVE_OSS_OUTPUT_TRUE@ module-oss.la
-@HAVE_COREAUDIO_TRUE@am__append_93 = \
+@HAVE_COREAUDIO_TRUE@am__append_94 = \
@HAVE_COREAUDIO_TRUE@ module-coreaudio-detect.la \
@HAVE_COREAUDIO_TRUE@ module-coreaudio-device.la
pulselibexec_PROGRAMS = $(am__EXEEXT_15)
-@HAVE_ALSA_TRUE@am__append_94 = \
+@HAVE_ALSA_TRUE@am__append_95 = \
@HAVE_ALSA_TRUE@ libalsa-util.la \
@HAVE_ALSA_TRUE@ module-alsa-sink.la \
@HAVE_ALSA_TRUE@ module-alsa-source.la \
@HAVE_ALSA_TRUE@ module-alsa-card.la
-@HAVE_SOLARIS_TRUE@am__append_95 = \
+@HAVE_SOLARIS_TRUE@am__append_96 = \
@HAVE_SOLARIS_TRUE@ module-solaris.la
-@HAVE_AVAHI_TRUE@am__append_96 = \
+@HAVE_AVAHI_TRUE@am__append_97 = \
@HAVE_AVAHI_TRUE@ module-zeroconf-publish.la \
@HAVE_AVAHI_TRUE@ module-zeroconf-discover.la
-@HAVE_BONJOUR_TRUE@am__append_97 = \
+@HAVE_BONJOUR_TRUE@am__append_98 = \
@HAVE_BONJOUR_TRUE@ module-bonjour-publish.la
-@HAVE_LIRC_TRUE@am__append_98 = \
+@HAVE_LIRC_TRUE@am__append_99 = \
@HAVE_LIRC_TRUE@ module-lirc.la
-@HAVE_XEN_TRUE@am__append_99 = \
+@HAVE_XEN_TRUE@am__append_100 = \
@HAVE_XEN_TRUE@ module-xenpv-sink.la
-@HAVE_EVDEV_TRUE@am__append_100 = \
+@HAVE_EVDEV_TRUE@am__append_101 = \
@HAVE_EVDEV_TRUE@ module-mmkbd-evdev.la
-@HAVE_JACK_TRUE@am__append_101 = \
+@HAVE_JACK_TRUE@am__append_102 = \
@HAVE_JACK_TRUE@ module-jack-sink.la \
@HAVE_JACK_TRUE@ module-jack-source.la
-@HAVE_DBUS_TRUE@@HAVE_JACK_TRUE@am__append_102 = \
+@HAVE_DBUS_TRUE@@HAVE_JACK_TRUE@am__append_103 = \
@HAVE_DBUS_TRUE@@HAVE_JACK_TRUE@ module-jackdbus-detect.la
-@HAVE_GCONF_TRUE@am__append_103 = \
+@HAVE_GCONF_TRUE@am__append_104 = \
@HAVE_GCONF_TRUE@ module-gconf.la
-@HAVE_GCONF_TRUE@am__append_104 = \
+@HAVE_GCONF_TRUE@am__append_105 = \
@HAVE_GCONF_TRUE@ gconf-helper
-@HAVE_WAVEOUT_TRUE@am__append_105 = \
+@HAVE_WAVEOUT_TRUE@am__append_106 = \
@HAVE_WAVEOUT_TRUE@ module-waveout.la
-@HAVE_HAL_COMPAT_TRUE@am__append_106 = \
+@HAVE_HAL_COMPAT_TRUE@am__append_107 = \
@HAVE_HAL_COMPAT_TRUE@ module-hal-detect.la
-@HAVE_UDEV_TRUE@am__append_107 = \
+@HAVE_UDEV_TRUE@am__append_108 = \
@HAVE_UDEV_TRUE@ module-udev-detect.la
-@HAVE_SYSTEMD_LOGIN_TRUE@am__append_108 = \
+@HAVE_SYSTEMD_LOGIN_TRUE@am__append_109 = \
@HAVE_SYSTEMD_LOGIN_TRUE@ module-systemd-login.la
-@HAVE_DBUS_TRUE@am__append_109 = \
+@HAVE_DBUS_TRUE@am__append_110 = \
@HAVE_DBUS_TRUE@ module-rygel-media-server.la \
@HAVE_DBUS_TRUE@ module-dbus-protocol.la
-@HAVE_BLUEZ_TRUE@am__append_110 = \
+@HAVE_BLUEZ_TRUE@am__append_111 = \
@HAVE_BLUEZ_TRUE@ module-bluetooth-discover.la \
@HAVE_BLUEZ_TRUE@ module-bluetooth-policy.la
-@HAVE_BLUEZ_4_TRUE@am__append_111 = \
+@HAVE_BLUEZ_4_TRUE@am__append_112 = \
@HAVE_BLUEZ_4_TRUE@ libbluez4-util.la \
@HAVE_BLUEZ_4_TRUE@ module-bluez4-discover.la \
@HAVE_BLUEZ_4_TRUE@ module-bluez4-device.la
-@HAVE_BLUEZ_5_TRUE@am__append_112 = \
+@HAVE_BLUEZ_5_TRUE@am__append_113 = \
@HAVE_BLUEZ_5_TRUE@ libbluez5-util.la \
@HAVE_BLUEZ_5_TRUE@ module-bluez5-discover.la \
@HAVE_BLUEZ_5_TRUE@ module-bluez5-device.la
@@ -408,53 +413,53 @@ pulselibexec_PROGRAMS = $(am__EXEEXT_15)
# RAOP depends on RTP, and we don't support RTP on Windows, see comment at
# librtp.la above.
-@HAVE_OPENSSL_TRUE@@OS_IS_WIN32_FALSE@am__append_113 = \
+@HAVE_OPENSSL_TRUE@@OS_IS_WIN32_FALSE@am__append_114 = \
@HAVE_OPENSSL_TRUE@@OS_IS_WIN32_FALSE@ libraop.la \
@HAVE_OPENSSL_TRUE@@OS_IS_WIN32_FALSE@ module-raop-sink.la
-@HAVE_AVAHI_TRUE@@HAVE_OPENSSL_TRUE@@OS_IS_WIN32_FALSE@am__append_114 = \
+@HAVE_AVAHI_TRUE@@HAVE_OPENSSL_TRUE@@OS_IS_WIN32_FALSE@am__append_115 = \
@HAVE_AVAHI_TRUE@@HAVE_OPENSSL_TRUE@@OS_IS_WIN32_FALSE@ module-raop-discover.la
-@HAVE_DBUS_TRUE@@HAVE_FFTW_TRUE@am__append_115 = \
+@HAVE_DBUS_TRUE@@HAVE_FFTW_TRUE@am__append_116 = \
@HAVE_DBUS_TRUE@@HAVE_FFTW_TRUE@ module-equalizer-sink.la
-@HAVE_DBUS_TRUE@@HAVE_FFTW_TRUE@am__append_116 = utils/qpaeq
-@HAVE_ESOUND_TRUE@am__append_117 = \
+@HAVE_DBUS_TRUE@@HAVE_FFTW_TRUE@am__append_117 = utils/qpaeq
+@HAVE_ESOUND_TRUE@am__append_118 = \
@HAVE_ESOUND_TRUE@ module-esound-protocol-tcp-symdef.h \
@HAVE_ESOUND_TRUE@ module-esound-protocol-unix-symdef.h \
@HAVE_ESOUND_TRUE@ module-esound-compat-spawnfd-symdef.h \
@HAVE_ESOUND_TRUE@ module-esound-compat-spawnpid-symdef.h \
@HAVE_ESOUND_TRUE@ module-esound-sink-symdef.h
-@HAVE_DBUS_TRUE@am__append_118 = $(DBUS_CFLAGS)
-@HAVE_DBUS_TRUE@am__append_119 = $(DBUS_LIBS)
-@HAVE_UDEV_TRUE@am__append_120 = modules/udev-util.h modules/udev-util.c
-@HAVE_UDEV_TRUE@am__append_121 = $(UDEV_LIBS)
-@HAVE_UDEV_TRUE@am__append_122 = $(UDEV_CFLAGS)
-@HAVE_DBUS_TRUE@am__append_123 = modules/reserve.h modules/reserve.c modules/reserve-monitor.h modules/reserve-monitor.c
-@HAVE_DBUS_TRUE@am__append_124 = $(DBUS_LIBS)
-@HAVE_DBUS_TRUE@am__append_125 = $(DBUS_CFLAGS)
-@HAVE_DBUS_TRUE@am__append_126 = $(DBUS_LIBS)
-@HAVE_DBUS_TRUE@am__append_127 = $(DBUS_CFLAGS)
-@HAVE_DBUS_TRUE@am__append_128 = $(DBUS_LIBS)
-@HAVE_DBUS_TRUE@am__append_129 = $(DBUS_CFLAGS)
-@HAVE_ADRIAN_EC_TRUE@am__append_130 = \
+@HAVE_DBUS_TRUE@am__append_119 = $(DBUS_CFLAGS)
+@HAVE_DBUS_TRUE@am__append_120 = $(DBUS_LIBS)
+@HAVE_UDEV_TRUE@am__append_121 = modules/udev-util.h modules/udev-util.c
+@HAVE_UDEV_TRUE@am__append_122 = $(UDEV_LIBS)
+@HAVE_UDEV_TRUE@am__append_123 = $(UDEV_CFLAGS)
+@HAVE_DBUS_TRUE@am__append_124 = modules/reserve.h modules/reserve.c modules/reserve-monitor.h modules/reserve-monitor.c
+@HAVE_DBUS_TRUE@am__append_125 = $(DBUS_LIBS)
+@HAVE_DBUS_TRUE@am__append_126 = $(DBUS_CFLAGS)
+@HAVE_DBUS_TRUE@am__append_127 = $(DBUS_LIBS)
+@HAVE_DBUS_TRUE@am__append_128 = $(DBUS_CFLAGS)
+@HAVE_DBUS_TRUE@am__append_129 = $(DBUS_LIBS)
+@HAVE_DBUS_TRUE@am__append_130 = $(DBUS_CFLAGS)
+@HAVE_ADRIAN_EC_TRUE@am__append_131 = \
@HAVE_ADRIAN_EC_TRUE@ modules/echo-cancel/adrian-aec.c modules/echo-cancel/adrian-aec.h \
@HAVE_ADRIAN_EC_TRUE@ modules/echo-cancel/adrian.c modules/echo-cancel/adrian.h
-@HAVE_ADRIAN_EC_TRUE@am__append_131 = -DHAVE_ADRIAN_EC=1
-@HAVE_ADRIAN_EC_TRUE@am__append_132 = modules/echo-cancel/adrian-aec
-@HAVE_ADRIAN_EC_TRUE@@HAVE_ORC_TRUE@am__append_133 = $(ORC_LIBS)
-@HAVE_ADRIAN_EC_TRUE@@HAVE_ORC_TRUE@am__append_134 = $(ORC_CFLAGS) -I$(top_builddir)/src/modules/echo-cancel
-@HAVE_SPEEX_TRUE@am__append_135 = modules/echo-cancel/speex.c
-@HAVE_SPEEX_TRUE@am__append_136 = $(LIBSPEEX_CFLAGS)
-@HAVE_SPEEX_TRUE@am__append_137 = $(LIBSPEEX_LIBS)
-@HAVE_WEBRTC_TRUE@am__append_138 = -DHAVE_WEBRTC=1
-@HAVE_WEBRTC_TRUE@am__append_139 = libwebrtc-util.la
-@HAVE_BLUEZ_5_OFONO_HEADSET_TRUE@am__append_140 = \
+@HAVE_ADRIAN_EC_TRUE@am__append_132 = -DHAVE_ADRIAN_EC=1
+@HAVE_ADRIAN_EC_TRUE@am__append_133 = modules/echo-cancel/adrian-aec
+@HAVE_ADRIAN_EC_TRUE@@HAVE_ORC_TRUE@am__append_134 = $(ORC_LIBS)
+@HAVE_ADRIAN_EC_TRUE@@HAVE_ORC_TRUE@am__append_135 = $(ORC_CFLAGS) -I$(top_builddir)/src/modules/echo-cancel
+@HAVE_SPEEX_TRUE@am__append_136 = modules/echo-cancel/speex.c
+@HAVE_SPEEX_TRUE@am__append_137 = $(LIBSPEEX_CFLAGS)
+@HAVE_SPEEX_TRUE@am__append_138 = $(LIBSPEEX_LIBS)
+@HAVE_WEBRTC_TRUE@am__append_139 = -DHAVE_WEBRTC=1
+@HAVE_WEBRTC_TRUE@am__append_140 = libwebrtc-util.la
+@HAVE_BLUEZ_5_OFONO_HEADSET_TRUE@am__append_141 = \
@HAVE_BLUEZ_5_OFONO_HEADSET_TRUE@ modules/bluetooth/backend-ofono.c
-@HAVE_BLUEZ_5_NATIVE_HEADSET_TRUE@am__append_141 = \
+@HAVE_BLUEZ_5_NATIVE_HEADSET_TRUE@am__append_142 = \
@HAVE_BLUEZ_5_NATIVE_HEADSET_TRUE@ modules/bluetooth/backend-native.c
subdir = src
@@ -462,6 +467,8 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/acx_libwrap.m4 \
$(top_srcdir)/m4/ax_check_define.m4 \
$(top_srcdir)/m4/ax_check_flag.m4 \
+ $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
$(top_srcdir)/m4/ax_define_dir.m4 \
$(top_srcdir)/m4/ax_pthread.m4 $(top_srcdir)/m4/ax_tls.m4 \
$(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
@@ -762,25 +769,26 @@ am__libpulsecommon_@PA_MAJORMINOR@_la_SOURCES_DIST = \
pulsecore/mcalign.c pulsecore/mcalign.h pulsecore/memblock.c \
pulsecore/memblock.h pulsecore/memblockq.c \
pulsecore/memblockq.h pulsecore/memchunk.c \
- pulsecore/memchunk.h pulsecore/native-common.h \
- pulsecore/once.c pulsecore/once.h pulsecore/packet.c \
- pulsecore/packet.h pulsecore/parseaddr.c pulsecore/parseaddr.h \
- pulsecore/pdispatch.c pulsecore/pdispatch.h pulsecore/pid.c \
- pulsecore/pid.h pulsecore/pipe.c pulsecore/pipe.h \
- pulsecore/memtrap.c pulsecore/memtrap.h pulsecore/aupdate.c \
- pulsecore/aupdate.h pulsecore/proplist-util.c \
- pulsecore/proplist-util.h pulsecore/pstream-util.c \
- pulsecore/pstream-util.h pulsecore/pstream.c \
- pulsecore/pstream.h pulsecore/queue.c pulsecore/queue.h \
- pulsecore/random.c pulsecore/random.h pulsecore/refcnt.h \
- pulsecore/srbchannel.c pulsecore/srbchannel.h \
- pulsecore/sample-util.c pulsecore/sample-util.h \
- pulsecore/shm.c pulsecore/shm.h pulsecore/bitset.c \
- pulsecore/bitset.h pulsecore/socket-client.c \
- pulsecore/socket-client.h pulsecore/socket-server.c \
- pulsecore/socket-server.h pulsecore/socket-util.c \
- pulsecore/socket-util.h pulsecore/strbuf.c pulsecore/strbuf.h \
- pulsecore/strlist.c pulsecore/strlist.h pulsecore/svolume_c.c \
+ pulsecore/memchunk.h pulsecore/native-common.c \
+ pulsecore/native-common.h pulsecore/once.c pulsecore/once.h \
+ pulsecore/packet.c pulsecore/packet.h pulsecore/parseaddr.c \
+ pulsecore/parseaddr.h pulsecore/pdispatch.c \
+ pulsecore/pdispatch.h pulsecore/pid.c pulsecore/pid.h \
+ pulsecore/pipe.c pulsecore/pipe.h pulsecore/memtrap.c \
+ pulsecore/memtrap.h pulsecore/aupdate.c pulsecore/aupdate.h \
+ pulsecore/proplist-util.c pulsecore/proplist-util.h \
+ pulsecore/pstream-util.c pulsecore/pstream-util.h \
+ pulsecore/pstream.c pulsecore/pstream.h pulsecore/queue.c \
+ pulsecore/queue.h pulsecore/random.c pulsecore/random.h \
+ pulsecore/refcnt.h pulsecore/srbchannel.c \
+ pulsecore/srbchannel.h pulsecore/sample-util.c \
+ pulsecore/sample-util.h pulsecore/mem.h pulsecore/shm.c \
+ pulsecore/shm.h pulsecore/bitset.c pulsecore/bitset.h \
+ pulsecore/socket-client.c pulsecore/socket-client.h \
+ pulsecore/socket-server.c pulsecore/socket-server.h \
+ pulsecore/socket-util.c pulsecore/socket-util.h \
+ pulsecore/strbuf.c pulsecore/strbuf.h pulsecore/strlist.c \
+ pulsecore/strlist.h pulsecore/svolume_c.c \
pulsecore/svolume_arm.c pulsecore/svolume_mmx.c \
pulsecore/svolume_sse.c pulsecore/tagstruct.c \
pulsecore/tagstruct.h pulsecore/time-smoother.c \
@@ -789,9 +797,10 @@ am__libpulsecommon_@PA_MAJORMINOR@_la_SOURCES_DIST = \
pulsecore/usergroup.h pulsecore/sndfile-util.c \
pulsecore/sndfile-util.h pulsecore/socket.h \
pulsecore/poll-win32.c pulsecore/poll.h pulsecore/winerrno.h \
- pulsecore/poll-posix.c pulse/client-conf-x11.c \
- pulse/client-conf-x11.h pulsecore/x11prop.c \
- pulsecore/x11prop.h pulsecore/mutex-win32.c pulsecore/mutex.h \
+ pulsecore/poll-posix.c pulsecore/memfd-wrappers.h \
+ pulse/client-conf-x11.c pulse/client-conf-x11.h \
+ pulsecore/x11prop.c pulsecore/x11prop.h \
+ pulsecore/mutex-win32.c pulsecore/mutex.h \
pulsecore/thread-win32.c pulsecore/thread.h \
pulsecore/semaphore-win32.c pulsecore/semaphore.h \
pulsecore/mutex-posix.c pulsecore/thread-posix.c \
@@ -800,19 +809,20 @@ am__libpulsecommon_@PA_MAJORMINOR@_la_SOURCES_DIST = \
pulsecore/dbus-util.h pulsecore/rtkit.c pulsecore/rtkit.h
@OS_IS_WIN32_TRUE@am__objects_5 = pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-poll-win32.lo
@OS_IS_WIN32_FALSE@am__objects_6 = pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-poll-posix.lo
-@HAVE_X11_TRUE@am__objects_7 = pulse/libpulsecommon_@PA_MAJORMINOR@_la-client-conf-x11.lo \
+am__objects_7 =
+@HAVE_X11_TRUE@am__objects_8 = pulse/libpulsecommon_@PA_MAJORMINOR@_la-client-conf-x11.lo \
@HAVE_X11_TRUE@ pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-x11prop.lo
-@OS_IS_WIN32_TRUE@am__objects_8 = pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-mutex-win32.lo \
+@OS_IS_WIN32_TRUE@am__objects_9 = pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-mutex-win32.lo \
@OS_IS_WIN32_TRUE@ pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-thread-win32.lo \
@OS_IS_WIN32_TRUE@ pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-semaphore-win32.lo
-@OS_IS_DARWIN_TRUE@@OS_IS_WIN32_FALSE@am__objects_9 = pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-mutex-posix.lo \
+@OS_IS_DARWIN_TRUE@@OS_IS_WIN32_FALSE@am__objects_10 = pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-mutex-posix.lo \
@OS_IS_DARWIN_TRUE@@OS_IS_WIN32_FALSE@ pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-thread-posix.lo \
@OS_IS_DARWIN_TRUE@@OS_IS_WIN32_FALSE@ pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-semaphore-osx.lo
-@OS_IS_DARWIN_FALSE@@OS_IS_WIN32_FALSE@am__objects_10 = pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-mutex-posix.lo \
+@OS_IS_DARWIN_FALSE@@OS_IS_WIN32_FALSE@am__objects_11 = pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-mutex-posix.lo \
@OS_IS_DARWIN_FALSE@@OS_IS_WIN32_FALSE@ pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-thread-posix.lo \
@OS_IS_DARWIN_FALSE@@OS_IS_WIN32_FALSE@ pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-semaphore-posix.lo
-@OS_IS_WIN32_TRUE@am__objects_11 = pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-dllmain.lo
-@HAVE_DBUS_TRUE@am__objects_12 = pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-dbus-util.lo \
+@OS_IS_WIN32_TRUE@am__objects_12 = pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-dllmain.lo
+@HAVE_DBUS_TRUE@am__objects_13 = pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-dbus-util.lo \
@HAVE_DBUS_TRUE@ pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-rtkit.lo
am_libpulsecommon_@PA_MAJORMINOR@_la_OBJECTS = \
pulse/libpulsecommon_@PA_MAJORMINOR@_la-client-conf.lo \
@@ -851,6 +861,7 @@ am_libpulsecommon_@PA_MAJORMINOR@_la_OBJECTS = \
pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-memblock.lo \
pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-memblockq.lo \
pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-memchunk.lo \
+ pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-native-common.lo \
pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-once.lo \
pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-packet.lo \
pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-parseaddr.lo \
@@ -884,7 +895,7 @@ am_libpulsecommon_@PA_MAJORMINOR@_la_OBJECTS = \
pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-sndfile-util.lo \
$(am__objects_5) $(am__objects_6) $(am__objects_7) \
$(am__objects_8) $(am__objects_9) $(am__objects_10) \
- $(am__objects_11) $(am__objects_12)
+ $(am__objects_11) $(am__objects_12) $(am__objects_13)
libpulsecommon_@PA_MAJORMINOR@_la_OBJECTS = \
$(am_libpulsecommon_@PA_MAJORMINOR@_la_OBJECTS)
libpulsecommon_@PA_MAJORMINOR@_la_LINK = $(LIBTOOL) $(AM_V_lt) \
@@ -901,7 +912,7 @@ libpulsecore_@PA_MAJORMINOR@_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
libpulsecommon-@PA_MAJORMINOR@.la libpulse.la \
- libpulsecore-foreign.la $(am__append_51) $(am__DEPENDENCIES_7) \
+ libpulsecore-foreign.la $(am__append_52) $(am__DEPENDENCIES_7) \
$(am__DEPENDENCIES_5) $(am__DEPENDENCIES_8) \
$(am__DEPENDENCIES_9) $(am__DEPENDENCIES_10) \
$(am__DEPENDENCIES_11) $(am__DEPENDENCIES_12)
@@ -955,16 +966,16 @@ am__libpulsecore_@PA_MAJORMINOR@_la_SOURCES_DIST = \
pulsecore/database-tdb.c pulsecore/database-simple.c \
pulsecore/resampler/speex.c pulsecore/resampler/soxr.c \
pulsecore/resampler/libsamplerate.c
-@HAVE_ORC_TRUE@am__objects_13 = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-svolume_orc.lo
-@HAVE_X11_TRUE@am__objects_14 = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-x11wrap.lo
-@HAVE_DBUS_TRUE@am__objects_15 = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-dbus-shared.lo \
+@HAVE_ORC_TRUE@am__objects_14 = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-svolume_orc.lo
+@HAVE_X11_TRUE@am__objects_15 = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-x11wrap.lo
+@HAVE_DBUS_TRUE@am__objects_16 = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-dbus-shared.lo \
@HAVE_DBUS_TRUE@ pulsecore/libpulsecore_@PA_MAJORMINOR@_la-protocol-dbus.lo
-@HAVE_GDBM_TRUE@am__objects_16 = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-database-gdbm.lo
-@HAVE_TDB_TRUE@am__objects_17 = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-database-tdb.lo
-@HAVE_SIMPLEDB_TRUE@am__objects_18 = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-database-simple.lo
-@HAVE_SPEEX_TRUE@am__objects_19 = pulsecore/resampler/libpulsecore_@PA_MAJORMINOR@_la-speex.lo
-@HAVE_SOXR_TRUE@am__objects_20 = pulsecore/resampler/libpulsecore_@PA_MAJORMINOR@_la-soxr.lo
-@HAVE_LIBSAMPLERATE_TRUE@am__objects_21 = pulsecore/resampler/libpulsecore_@PA_MAJORMINOR@_la-libsamplerate.lo
+@HAVE_GDBM_TRUE@am__objects_17 = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-database-gdbm.lo
+@HAVE_TDB_TRUE@am__objects_18 = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-database-tdb.lo
+@HAVE_SIMPLEDB_TRUE@am__objects_19 = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-database-simple.lo
+@HAVE_SPEEX_TRUE@am__objects_20 = pulsecore/resampler/libpulsecore_@PA_MAJORMINOR@_la-speex.lo
+@HAVE_SOXR_TRUE@am__objects_21 = pulsecore/resampler/libpulsecore_@PA_MAJORMINOR@_la-soxr.lo
+@HAVE_LIBSAMPLERATE_TRUE@am__objects_22 = pulsecore/resampler/libpulsecore_@PA_MAJORMINOR@_la-libsamplerate.lo
am_libpulsecore_@PA_MAJORMINOR@_la_OBJECTS = pulsecore/filter/libpulsecore_@PA_MAJORMINOR@_la-lfe-filter.lo \
pulsecore/filter/libpulsecore_@PA_MAJORMINOR@_la-biquad.lo \
pulsecore/filter/libpulsecore_@PA_MAJORMINOR@_la-crossover.lo \
@@ -1017,9 +1028,9 @@ am_libpulsecore_@PA_MAJORMINOR@_la_OBJECTS = pulsecore/filter/libpulsecore_@PA_M
pulsecore/libpulsecore_@PA_MAJORMINOR@_la-source.lo \
pulsecore/libpulsecore_@PA_MAJORMINOR@_la-start-child.lo \
pulsecore/libpulsecore_@PA_MAJORMINOR@_la-thread-mq.lo \
- $(am__objects_13) $(am__objects_14) $(am__objects_15) \
- $(am__objects_16) $(am__objects_17) $(am__objects_18) \
- $(am__objects_19) $(am__objects_20) $(am__objects_21)
+ $(am__objects_14) $(am__objects_15) $(am__objects_16) \
+ $(am__objects_17) $(am__objects_18) $(am__objects_19) \
+ $(am__objects_20) $(am__objects_21) $(am__objects_22)
@HAVE_ORC_TRUE@nodist_libpulsecore_@PA_MAJORMINOR@_la_OBJECTS = pulsecore/libpulsecore_@PA_MAJORMINOR@_la-svolume-orc-gen.lo
libpulsecore_@PA_MAJORMINOR@_la_OBJECTS = \
$(am_libpulsecore_@PA_MAJORMINOR@_la_OBJECTS) \
@@ -1377,19 +1388,19 @@ module_device_restore_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
@HAVE_ADRIAN_EC_TRUE@@HAVE_ORC_TRUE@ $(am__DEPENDENCIES_1)
module_echo_cancel_la_DEPENDENCIES = $(am__DEPENDENCIES_3) \
$(am__DEPENDENCIES_13) $(am__DEPENDENCIES_10) \
- $(am__append_139)
+ $(am__append_140)
am__module_echo_cancel_la_SOURCES_DIST = \
modules/echo-cancel/module-echo-cancel.c \
modules/echo-cancel/null.c modules/echo-cancel/echo-cancel.h \
modules/echo-cancel/adrian-aec.c \
modules/echo-cancel/adrian-aec.h modules/echo-cancel/adrian.c \
modules/echo-cancel/adrian.h modules/echo-cancel/speex.c
-@HAVE_ADRIAN_EC_TRUE@am__objects_22 = modules/echo-cancel/module_echo_cancel_la-adrian-aec.lo \
+@HAVE_ADRIAN_EC_TRUE@am__objects_23 = modules/echo-cancel/module_echo_cancel_la-adrian-aec.lo \
@HAVE_ADRIAN_EC_TRUE@ modules/echo-cancel/module_echo_cancel_la-adrian.lo
-@HAVE_SPEEX_TRUE@am__objects_23 = modules/echo-cancel/module_echo_cancel_la-speex.lo
+@HAVE_SPEEX_TRUE@am__objects_24 = modules/echo-cancel/module_echo_cancel_la-speex.lo
am_module_echo_cancel_la_OBJECTS = modules/echo-cancel/module_echo_cancel_la-module-echo-cancel.lo \
modules/echo-cancel/module_echo_cancel_la-null.lo \
- $(am__objects_22) $(am__objects_23)
+ $(am__objects_23) $(am__objects_24)
@HAVE_ADRIAN_EC_TRUE@@HAVE_ORC_TRUE@nodist_module_echo_cancel_la_OBJECTS = modules/echo-cancel/module_echo_cancel_la-adrian-aec-orc-gen.lo
module_echo_cancel_la_OBJECTS = $(am_module_echo_cancel_la_OBJECTS) \
$(nodist_module_echo_cancel_la_OBJECTS)
@@ -1742,7 +1753,8 @@ module_rescue_streams_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
$(module_rescue_streams_la_LDFLAGS) $(LDFLAGS) -o $@
module_role_cork_la_DEPENDENCIES = $(am__DEPENDENCIES_3)
am_module_role_cork_la_OBJECTS = \
- modules/module_role_cork_la-module-role-cork.lo
+ modules/module_role_cork_la-module-role-cork.lo \
+ modules/module_role_cork_la-stream-interaction.lo
module_role_cork_la_OBJECTS = $(am_module_role_cork_la_OBJECTS)
module_role_cork_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
@@ -1750,7 +1762,8 @@ module_role_cork_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
$(module_role_cork_la_LDFLAGS) $(LDFLAGS) -o $@
module_role_ducking_la_DEPENDENCIES = $(am__DEPENDENCIES_3)
am_module_role_ducking_la_OBJECTS = \
- modules/module_role_ducking_la-module-role-ducking.lo
+ modules/module_role_ducking_la-module-role-ducking.lo \
+ modules/module_role_ducking_la-stream-interaction.lo
module_role_ducking_la_OBJECTS = $(am_module_role_ducking_la_OBJECTS)
module_role_ducking_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
@@ -2073,7 +2086,7 @@ am__EXEEXT_9 = mainloop-test$(EXEEXT) strlist-test$(EXEEXT) \
$(am__EXEEXT_6) $(am__EXEEXT_7) $(am__EXEEXT_8)
@HAVE_SIGXCPU_TRUE@am__EXEEXT_10 = cpulimit-test$(EXEEXT) \
@HAVE_SIGXCPU_TRUE@ cpulimit-test2$(EXEEXT)
-@HAVE_GTK30_TRUE@am__EXEEXT_11 = gtk-test$(EXEEXT)
+@HAVE_GLIB20_TRUE@@HAVE_GTK30_TRUE@am__EXEEXT_11 = gtk-test$(EXEEXT)
@HAVE_ALSA_TRUE@am__EXEEXT_12 = alsa-time-test$(EXEEXT)
am__EXEEXT_13 = ipacl-test$(EXEEXT) mcalign-test$(EXEEXT) \
pacat-simple$(EXEEXT) parec-simple$(EXEEXT) \
@@ -2211,19 +2224,19 @@ am__echo_cancel_test_SOURCES_DIST = \
modules/echo-cancel/adrian-aec.c \
modules/echo-cancel/adrian-aec.h modules/echo-cancel/adrian.c \
modules/echo-cancel/adrian.h modules/echo-cancel/speex.c
-@HAVE_ADRIAN_EC_TRUE@am__objects_24 = modules/echo-cancel/echo_cancel_test-adrian-aec.$(OBJEXT) \
+@HAVE_ADRIAN_EC_TRUE@am__objects_25 = modules/echo-cancel/echo_cancel_test-adrian-aec.$(OBJEXT) \
@HAVE_ADRIAN_EC_TRUE@ modules/echo-cancel/echo_cancel_test-adrian.$(OBJEXT)
-@HAVE_SPEEX_TRUE@am__objects_25 = modules/echo-cancel/echo_cancel_test-speex.$(OBJEXT)
-am__objects_26 = modules/echo-cancel/echo_cancel_test-module-echo-cancel.$(OBJEXT) \
+@HAVE_SPEEX_TRUE@am__objects_26 = modules/echo-cancel/echo_cancel_test-speex.$(OBJEXT)
+am__objects_27 = modules/echo-cancel/echo_cancel_test-module-echo-cancel.$(OBJEXT) \
modules/echo-cancel/echo_cancel_test-null.$(OBJEXT) \
- $(am__objects_24) $(am__objects_25)
-am_echo_cancel_test_OBJECTS = $(am__objects_26)
-@HAVE_ADRIAN_EC_TRUE@@HAVE_ORC_TRUE@am__objects_27 = modules/echo-cancel/echo_cancel_test-adrian-aec-orc-gen.$(OBJEXT)
-nodist_echo_cancel_test_OBJECTS = $(am__objects_27)
+ $(am__objects_25) $(am__objects_26)
+am_echo_cancel_test_OBJECTS = $(am__objects_27)
+@HAVE_ADRIAN_EC_TRUE@@HAVE_ORC_TRUE@am__objects_28 = modules/echo-cancel/echo_cancel_test-adrian-aec-orc-gen.$(OBJEXT)
+nodist_echo_cancel_test_OBJECTS = $(am__objects_28)
echo_cancel_test_OBJECTS = $(am_echo_cancel_test_OBJECTS) \
$(nodist_echo_cancel_test_OBJECTS)
am__DEPENDENCIES_14 = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_13) \
- $(am__DEPENDENCIES_10) $(am__append_139)
+ $(am__DEPENDENCIES_10) $(am__append_140)
echo_cancel_test_DEPENDENCIES = $(am__DEPENDENCIES_14)
echo_cancel_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
@@ -2342,8 +2355,8 @@ mainloop_test_DEPENDENCIES = $(am__DEPENDENCIES_2) libpulse.la \
mainloop_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(mainloop_test_CFLAGS) \
$(CFLAGS) $(mainloop_test_LDFLAGS) $(LDFLAGS) -o $@
-am__objects_28 = tests/mainloop_test_glib-mainloop-test.$(OBJEXT)
-am_mainloop_test_glib_OBJECTS = $(am__objects_28)
+am__objects_29 = tests/mainloop_test_glib-mainloop-test.$(OBJEXT)
+am_mainloop_test_glib_OBJECTS = $(am__objects_29)
mainloop_test_glib_OBJECTS = $(am_mainloop_test_glib_OBJECTS)
am__DEPENDENCIES_15 = $(am__DEPENDENCIES_2) libpulse.la \
libpulsecommon-@PA_MAJORMINOR@.la
@@ -2469,7 +2482,7 @@ am__pulseaudio_SOURCES_DIST = daemon/caps.c daemon/caps.h \
daemon/dumpmodules.c daemon/dumpmodules.h \
daemon/ltdl-bind-now.c daemon/ltdl-bind-now.h daemon/main.c \
daemon/server-lookup.c daemon/server-lookup.h
-@HAVE_DBUS_TRUE@am__objects_29 = \
+@HAVE_DBUS_TRUE@am__objects_30 = \
@HAVE_DBUS_TRUE@ daemon/pulseaudio-server-lookup.$(OBJEXT)
am_pulseaudio_OBJECTS = daemon/pulseaudio-caps.$(OBJEXT) \
daemon/pulseaudio-cmdline.$(OBJEXT) \
@@ -2477,7 +2490,7 @@ am_pulseaudio_OBJECTS = daemon/pulseaudio-caps.$(OBJEXT) \
daemon/pulseaudio-daemon-conf.$(OBJEXT) \
daemon/pulseaudio-dumpmodules.$(OBJEXT) \
daemon/pulseaudio-ltdl-bind-now.$(OBJEXT) \
- daemon/pulseaudio-main.$(OBJEXT) $(am__objects_29)
+ daemon/pulseaudio-main.$(OBJEXT) $(am__objects_30)
pulseaudio_OBJECTS = $(am_pulseaudio_OBJECTS)
pulseaudio_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(pulseaudio_CFLAGS) \
@@ -3282,8 +3295,10 @@ HAVE_BLUEZ_5 = @HAVE_BLUEZ_5@
HAVE_BLUEZ_5_NATIVE_HEADSET = @HAVE_BLUEZ_5_NATIVE_HEADSET@
HAVE_BLUEZ_5_OFONO_HEADSET = @HAVE_BLUEZ_5_OFONO_HEADSET@
HAVE_COREAUDIO = @HAVE_COREAUDIO@
+HAVE_CXX11 = @HAVE_CXX11@
HAVE_DBUS = @HAVE_DBUS@
HAVE_GLIB20 = @HAVE_GLIB20@
+HAVE_MEMFD = @HAVE_MEMFD@
HAVE_MKFIFO = @HAVE_MKFIFO@
HAVE_NEON = @HAVE_NEON@
HAVE_OSS_OUTPUT = @HAVE_OSS_OUTPUT@
@@ -3519,8 +3534,8 @@ AM_CPPFLAGS = \
-DPA_BUILDDIR=\"$(abs_builddir)\" \
-DPULSE_LOCALEDIR=\"$(localedir)\"
-AM_CFLAGS = $(PTHREAD_CFLAGS) $(am__append_1)
-AM_CXXFLAGS = $(AM_CFLAGS) $(am__append_2)
+AM_CFLAGS = -std=gnu11 $(PTHREAD_CFLAGS) $(am__append_1)
+AM_CXXFLAGS = -std=c++11 $(PTHREAD_CFLAGS) $(am__append_2)
SERVER_CFLAGS = -D__INCLUDED_FROM_PULSE_AUDIO
AM_LIBADD = $(PTHREAD_LIBS) $(INTLLIBS)
AM_LDADD = $(PTHREAD_LIBS) $(INTLLIBS)
@@ -3568,7 +3583,7 @@ BUILT_SOURCES = $(am__append_6) $(SYMDEF_FILES) builddirs
###################################
# Some minor stuff #
###################################
-CLEANFILES = $(am__append_7) $(am__append_49) \
+CLEANFILES = $(am__append_7) $(am__append_50) \
daemon/pulseaudio.desktop
@HAVE_ORC_TRUE@ORC_BUILT_SOURCE = $(addsuffix -orc-gen.c,$(ORC_SOURCE))
@HAVE_ORC_TRUE@ORC_BUILT_HEADER = $(addsuffix -orc-gen.h,$(ORC_SOURCE))
@@ -3578,7 +3593,7 @@ CLEANFILES = $(am__append_7) $(am__append_49) \
@HAVE_ORC_TRUE@cp_v_gen = $(cp_v_gen_$(V))
@HAVE_ORC_TRUE@cp_v_gen_ = $(cp_v_gen_$(AM_DEFAULT_VERBOSITY))
@HAVE_ORC_TRUE@cp_v_gen_0 = @echo " CP $@";
-ORC_SOURCE = pulsecore/svolume $(am__append_132)
+ORC_SOURCE = pulsecore/svolume $(am__append_133)
pulseaudio_SOURCES = daemon/caps.c daemon/caps.h daemon/cmdline.c \
daemon/cmdline.h daemon/cpulimit.c daemon/cpulimit.h \
daemon/daemon-conf.c daemon/daemon-conf.h daemon/dumpmodules.c \
@@ -3605,8 +3620,8 @@ pulseaudio_DEPENDENCIES = libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJ
###################################
# Utility programs #
###################################
-bin_SCRIPTS = esdcompat $(am__append_16) $(am__append_48) \
- $(am__append_116)
+bin_SCRIPTS = esdcompat $(am__append_16) $(am__append_49) \
+ $(am__append_117)
pacat_SOURCES = utils/pacat.c
pacat_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la $(LIBSNDFILE_LIBS)
pacat_CFLAGS = $(AM_CFLAGS) $(LIBSNDFILE_CFLAGS)
@@ -3637,7 +3652,7 @@ pax11publish_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
###################################
# We split the foreign code off to not be annoyed by warnings we don't care about
-noinst_LTLIBRARIES = liblo-test-util.la $(am__append_50) \
+noinst_LTLIBRARIES = liblo-test-util.la $(am__append_51) \
libpulsecore-foreign.la
TESTS_default = mainloop-test strlist-test close-test memblockq-test \
channelmap-test thread-mainloop-test utf8-test format-test \
@@ -3921,25 +3936,26 @@ libpulsecommon_@PA_MAJORMINOR@_la_SOURCES = pulse/client-conf.c \
pulsecore/mcalign.c pulsecore/mcalign.h pulsecore/memblock.c \
pulsecore/memblock.h pulsecore/memblockq.c \
pulsecore/memblockq.h pulsecore/memchunk.c \
- pulsecore/memchunk.h pulsecore/native-common.h \
- pulsecore/once.c pulsecore/once.h pulsecore/packet.c \
- pulsecore/packet.h pulsecore/parseaddr.c pulsecore/parseaddr.h \
- pulsecore/pdispatch.c pulsecore/pdispatch.h pulsecore/pid.c \
- pulsecore/pid.h pulsecore/pipe.c pulsecore/pipe.h \
- pulsecore/memtrap.c pulsecore/memtrap.h pulsecore/aupdate.c \
- pulsecore/aupdate.h pulsecore/proplist-util.c \
- pulsecore/proplist-util.h pulsecore/pstream-util.c \
- pulsecore/pstream-util.h pulsecore/pstream.c \
- pulsecore/pstream.h pulsecore/queue.c pulsecore/queue.h \
- pulsecore/random.c pulsecore/random.h pulsecore/refcnt.h \
- pulsecore/srbchannel.c pulsecore/srbchannel.h \
- pulsecore/sample-util.c pulsecore/sample-util.h \
- pulsecore/shm.c pulsecore/shm.h pulsecore/bitset.c \
- pulsecore/bitset.h pulsecore/socket-client.c \
- pulsecore/socket-client.h pulsecore/socket-server.c \
- pulsecore/socket-server.h pulsecore/socket-util.c \
- pulsecore/socket-util.h pulsecore/strbuf.c pulsecore/strbuf.h \
- pulsecore/strlist.c pulsecore/strlist.h pulsecore/svolume_c.c \
+ pulsecore/memchunk.h pulsecore/native-common.c \
+ pulsecore/native-common.h pulsecore/once.c pulsecore/once.h \
+ pulsecore/packet.c pulsecore/packet.h pulsecore/parseaddr.c \
+ pulsecore/parseaddr.h pulsecore/pdispatch.c \
+ pulsecore/pdispatch.h pulsecore/pid.c pulsecore/pid.h \
+ pulsecore/pipe.c pulsecore/pipe.h pulsecore/memtrap.c \
+ pulsecore/memtrap.h pulsecore/aupdate.c pulsecore/aupdate.h \
+ pulsecore/proplist-util.c pulsecore/proplist-util.h \
+ pulsecore/pstream-util.c pulsecore/pstream-util.h \
+ pulsecore/pstream.c pulsecore/pstream.h pulsecore/queue.c \
+ pulsecore/queue.h pulsecore/random.c pulsecore/random.h \
+ pulsecore/refcnt.h pulsecore/srbchannel.c \
+ pulsecore/srbchannel.h pulsecore/sample-util.c \
+ pulsecore/sample-util.h pulsecore/mem.h pulsecore/shm.c \
+ pulsecore/shm.h pulsecore/bitset.c pulsecore/bitset.h \
+ pulsecore/socket-client.c pulsecore/socket-client.h \
+ pulsecore/socket-server.c pulsecore/socket-server.h \
+ pulsecore/socket-util.c pulsecore/socket-util.h \
+ pulsecore/strbuf.c pulsecore/strbuf.h pulsecore/strlist.c \
+ pulsecore/strlist.h pulsecore/svolume_c.c \
pulsecore/svolume_arm.c pulsecore/svolume_mmx.c \
pulsecore/svolume_sse.c pulsecore/tagstruct.c \
pulsecore/tagstruct.h pulsecore/time-smoother.c \
@@ -3947,22 +3963,22 @@ libpulsecommon_@PA_MAJORMINOR@_la_SOURCES = pulse/client-conf.c \
pulsecore/tokenizer.h pulsecore/usergroup.c \
pulsecore/usergroup.h pulsecore/sndfile-util.c \
pulsecore/sndfile-util.h pulsecore/socket.h $(am__append_25) \
- $(am__append_26) $(am__append_27) $(am__append_34) \
- $(am__append_35) $(am__append_37) $(am__append_40) \
- $(am__append_41)
+ $(am__append_26) $(am__append_27) $(am__append_28) \
+ $(am__append_35) $(am__append_36) $(am__append_38) \
+ $(am__append_41) $(am__append_42)
# proplist-util.h uses these header files, but not the library itself!
libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) \
- $(LIBJSON_CFLAGS) $(LIBSNDFILE_CFLAGS) $(am__append_28) \
- $(am__append_30) $(am__append_32) $(GLIB20_CFLAGS) \
- $(GTK30_CFLAGS) $(am__append_36) $(am__append_38) \
- $(am__append_42)
+ $(LIBJSON_CFLAGS) $(LIBSNDFILE_CFLAGS) $(am__append_29) \
+ $(am__append_31) $(am__append_33) $(GLIB20_CFLAGS) \
+ $(GTK30_CFLAGS) $(am__append_37) $(am__append_39) \
+ $(am__append_43)
libpulsecommon_@PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) \
- $(AM_LIBLDFLAGS) -avoid-version $(am__append_29) \
- $(am__append_31) $(am__append_33)
+ $(AM_LIBLDFLAGS) -avoid-version $(am__append_30) \
+ $(am__append_32) $(am__append_34)
libpulsecommon_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) \
$(LIBJSON_LIBS) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) \
- $(LIBSNDFILE_LIBS) $(am__append_39) $(am__append_43)
+ $(LIBSNDFILE_LIBS) $(am__append_40) $(am__append_44)
###################################
# Client library #
@@ -3977,8 +3993,8 @@ pulseinclude_HEADERS = pulse/cdecl.h pulse/channelmap.h \
pulse/sample.h pulse/scache.h pulse/simple.h pulse/stream.h \
pulse/subscribe.h pulse/thread-mainloop.h pulse/timeval.h \
pulse/utf8.h pulse/util.h pulse/version.h pulse/volume.h \
- pulse/xmalloc.h $(am__append_44)
-lib_LTLIBRARIES = libpulse.la libpulse-simple.la $(am__append_45)
+ pulse/xmalloc.h $(am__append_45)
+lib_LTLIBRARIES = libpulse.la libpulse-simple.la $(am__append_46)
# Public interface
libpulse_la_SOURCES = \
@@ -4013,10 +4029,10 @@ libpulse_la_SOURCES = \
pulse/volume.c pulse/volume.h \
pulse/xmalloc.c pulse/xmalloc.h
-libpulse_la_CFLAGS = $(AM_CFLAGS) $(LIBJSON_CFLAGS) $(am__append_46)
+libpulse_la_CFLAGS = $(AM_CFLAGS) $(LIBJSON_CFLAGS) $(am__append_47)
libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) \
$(LIBJSON_LIBS) libpulsecommon-@PA_MAJORMINOR@.la \
- $(am__append_47)
+ $(am__append_48)
libpulse_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO)
libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h
libpulse_simple_la_CFLAGS = $(AM_CFLAGS)
@@ -4088,23 +4104,23 @@ libpulsecore_@PA_MAJORMINOR@_la_SOURCES = \
pulsecore/source-output.c pulsecore/source-output.h \
pulsecore/source.c pulsecore/source.h pulsecore/start-child.c \
pulsecore/start-child.h pulsecore/thread-mq.c \
- pulsecore/thread-mq.h pulsecore/database.h $(am__append_52) \
- $(am__append_55) $(am__append_58) $(am__append_61) \
- $(am__append_64) $(am__append_67) $(am__append_68) \
- $(am__append_71) $(am__append_74)
-libpulsecore_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) \
- $(LIBSNDFILE_CFLAGS) $(WINSOCK_CFLAGS) $(am__append_53) \
+ pulsecore/thread-mq.h pulsecore/database.h $(am__append_53) \
$(am__append_56) $(am__append_59) $(am__append_62) \
- $(am__append_65) $(am__append_69) $(am__append_72) \
- $(am__append_75)
+ $(am__append_65) $(am__append_68) $(am__append_69) \
+ $(am__append_72) $(am__append_75)
+libpulsecore_@PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) \
+ $(LIBSNDFILE_CFLAGS) $(WINSOCK_CFLAGS) $(am__append_54) \
+ $(am__append_57) $(am__append_60) $(am__append_63) \
+ $(am__append_66) $(am__append_70) $(am__append_73) \
+ $(am__append_76)
libpulsecore_@PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) \
- $(AM_LIBLDFLAGS) -avoid-version $(am__append_57)
+ $(AM_LIBLDFLAGS) -avoid-version $(am__append_58)
libpulsecore_@PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) \
$(LIBSNDFILE_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) \
libpulsecommon-@PA_MAJORMINOR@.la libpulse.la \
- libpulsecore-foreign.la $(am__append_51) $(am__append_54) \
- $(am__append_60) $(am__append_63) $(am__append_66) \
- $(am__append_70) $(am__append_73) $(am__append_76)
+ libpulsecore-foreign.la $(am__append_52) $(am__append_55) \
+ $(am__append_61) $(am__append_64) $(am__append_67) \
+ $(am__append_71) $(am__append_74) $(am__append_77)
@HAVE_NEON_TRUE@libpulsecore_sconv_neon_la_SOURCES = pulsecore/sconv_neon.c
@HAVE_NEON_TRUE@libpulsecore_sconv_neon_la_CFLAGS = $(AM_CFLAGS) $(NEON_CFLAGS)
@HAVE_NEON_TRUE@libpulsecore_mix_neon_la_SOURCES = pulsecore/mix_neon.c
@@ -4127,8 +4143,8 @@ libpulsecore_foreign_la_CFLAGS = $(AM_CFLAGS) $(FOREIGN_CFLAGS)
### the head, and not the other way!
modlibexec_LTLIBRARIES = libcli.la libprotocol-cli.la \
libprotocol-simple.la libprotocol-http.la \
- libprotocol-native.la $(am__append_77) $(am__append_78) \
- $(am__append_79) $(am__append_80) $(am__append_83) \
+ libprotocol-native.la $(am__append_78) $(am__append_79) \
+ $(am__append_80) $(am__append_81) $(am__append_84) \
module-cli.la module-cli-protocol-tcp.la \
module-simple-protocol-tcp.la module-null-sink.la \
module-null-source.la module-sine-source.la module-detect.la \
@@ -4149,17 +4165,17 @@ modlibexec_LTLIBRARIES = libcli.la libprotocol-cli.la \
module-virtual-source.la module-virtual-surround-sink.la \
module-switch-on-connect.la module-switch-on-port-available.la \
module-filter-apply.la module-filter-heuristics.la \
- module-role-ducking.la $(am__append_84) $(am__append_85) \
- $(am__append_86) $(am__append_87) $(am__append_88) \
- $(am__append_89) $(am__append_90) $(am__append_91) \
- $(am__append_92) $(am__append_93) $(am__append_94) \
- $(am__append_95) $(am__append_96) $(am__append_97) \
- $(am__append_98) $(am__append_99) $(am__append_100) \
- $(am__append_101) $(am__append_102) $(am__append_103) \
- $(am__append_105) $(am__append_106) $(am__append_107) \
- $(am__append_108) $(am__append_109) $(am__append_110) \
- $(am__append_111) $(am__append_112) $(am__append_113) \
- $(am__append_114) $(am__append_115)
+ module-role-ducking.la $(am__append_85) $(am__append_86) \
+ $(am__append_87) $(am__append_88) $(am__append_89) \
+ $(am__append_90) $(am__append_91) $(am__append_92) \
+ $(am__append_93) $(am__append_94) $(am__append_95) \
+ $(am__append_96) $(am__append_97) $(am__append_98) \
+ $(am__append_99) $(am__append_100) $(am__append_101) \
+ $(am__append_102) $(am__append_103) $(am__append_104) \
+ $(am__append_106) $(am__append_107) $(am__append_108) \
+ $(am__append_109) $(am__append_110) $(am__append_111) \
+ $(am__append_112) $(am__append_113) $(am__append_114) \
+ $(am__append_115) $(am__append_116)
libprotocol_simple_la_SOURCES = pulsecore/protocol-simple.c pulsecore/protocol-simple.h
libprotocol_simple_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
libprotocol_simple_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
@@ -4174,11 +4190,11 @@ libprotocol_http_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
libprotocol_http_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
libprotocol_native_la_SOURCES = pulsecore/protocol-native.c pulsecore/protocol-native.h pulsecore/native-common.h
libprotocol_native_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) \
- $(am__append_81)
+ $(am__append_82)
libprotocol_native_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
libprotocol_native_la_LIBADD = $(AM_LIBADD) \
libpulsecore-@PA_MAJORMINOR@.la \
- libpulsecommon-@PA_MAJORMINOR@.la libpulse.la $(am__append_82)
+ libpulsecommon-@PA_MAJORMINOR@.la libpulse.la $(am__append_83)
@HAVE_ESOUND_TRUE@libprotocol_esound_la_SOURCES = pulsecore/protocol-esound.c pulsecore/protocol-esound.h pulsecore/esound.h
@HAVE_ESOUND_TRUE@libprotocol_esound_la_LDFLAGS = $(AM_LDFLAGS) $(AM_LIBLDFLAGS) -avoid-version
@HAVE_ESOUND_TRUE@libprotocol_esound_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la
@@ -4313,7 +4329,7 @@ SYMDEF_FILES = module-cli-symdef.h module-cli-protocol-tcp-symdef.h \
module-switch-on-connect-symdef.h \
module-switch-on-port-available-symdef.h \
module-filter-apply-symdef.h module-filter-heuristics-symdef.h \
- $(am__append_117)
+ $(am__append_118)
# Simple protocol
module_simple_protocol_tcp_la_SOURCES = modules/module-protocol-stub.c
@@ -4448,10 +4464,10 @@ module_remap_source_la_LDFLAGS = $(MODULE_LDFLAGS)
module_remap_source_la_LIBADD = $(MODULE_LIBADD)
module_ladspa_sink_la_SOURCES = modules/module-ladspa-sink.c modules/ladspa.h
module_ladspa_sink_la_CFLAGS = -DLADSPA_PATH=\"$(libdir)/ladspa:/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/local/lib64/ladspa:/usr/lib64/ladspa\" \
- $(AM_CFLAGS) $(SERVER_CFLAGS) $(am__append_118)
+ $(AM_CFLAGS) $(SERVER_CFLAGS) $(am__append_119)
module_ladspa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
module_ladspa_sink_la_LIBADD = $(MODULE_LIBADD) $(LIBLTDL) \
- $(am__append_119)
+ $(am__append_120)
module_equalizer_sink_la_SOURCES = modules/module-equalizer-sink.c
module_equalizer_sink_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) $(DBUS_CFLAGS) $(FFTW_CFLAGS)
module_equalizer_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
@@ -4535,12 +4551,12 @@ libalsa_util_la_SOURCES = modules/alsa/alsa-util.c \
modules/alsa/alsa-mixer.h modules/alsa/alsa-sink.c \
modules/alsa/alsa-sink.h modules/alsa/alsa-source.c \
modules/alsa/alsa-source.h modules/reserve-wrap.c \
- modules/reserve-wrap.h $(am__append_120) $(am__append_123)
+ modules/reserve-wrap.h $(am__append_121) $(am__append_124)
libalsa_util_la_LDFLAGS = -avoid-version
libalsa_util_la_LIBADD = $(MODULE_LIBADD) $(ASOUNDLIB_LIBS) \
- $(am__append_121) $(am__append_124)
+ $(am__append_122) $(am__append_125)
libalsa_util_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) \
- $(ASOUNDLIB_CFLAGS) $(am__append_122) $(am__append_125)
+ $(ASOUNDLIB_CFLAGS) $(am__append_123) $(am__append_126)
module_alsa_sink_la_SOURCES = modules/alsa/module-alsa-sink.c
module_alsa_sink_la_LDFLAGS = $(MODULE_LDFLAGS)
module_alsa_sink_la_LIBADD = $(MODULE_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la
@@ -4619,7 +4635,9 @@ module_position_event_sounds_la_LIBADD = $(MODULE_LIBADD)
module_position_event_sounds_la_CFLAGS = $(AM_CFLAGS)
# Ducking effect based on stream roles
-module_role_ducking_la_SOURCES = modules/module-role-ducking.c
+module_role_ducking_la_SOURCES = modules/module-role-ducking.c \
+ modules/stream-interaction.c modules/stream-interaction.h
+
module_role_ducking_la_LDFLAGS = $(MODULE_LDFLAGS)
module_role_ducking_la_LIBADD = $(MODULE_LIBADD)
module_role_ducking_la_CFLAGS = $(AM_CFLAGS)
@@ -4632,7 +4650,9 @@ module_augment_properties_la_LIBADD = $(MODULE_LIBADD)
module_augment_properties_la_CFLAGS = $(AM_CFLAGS) -DDESKTOPFILEDIR=\"/usr/share/applications\"
# Cork certain streams while others are active (e.g. cork music when phone streams appear)
-module_role_cork_la_SOURCES = modules/module-role-cork.c
+module_role_cork_la_SOURCES = modules/module-role-cork.c \
+ modules/stream-interaction.c modules/stream-interaction.h
+
module_role_cork_la_LDFLAGS = $(MODULE_LDFLAGS)
module_role_cork_la_LIBADD = $(MODULE_LIBADD)
module_role_cork_la_CFLAGS = $(AM_CFLAGS)
@@ -4647,15 +4667,15 @@ module_device_manager_la_CFLAGS = $(AM_CFLAGS)
module_device_restore_la_SOURCES = modules/module-device-restore.c
module_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
module_device_restore_la_LIBADD = $(MODULE_LIBADD) \
- libprotocol-native.la $(am__append_126)
-module_device_restore_la_CFLAGS = $(AM_CFLAGS) $(am__append_127)
+ libprotocol-native.la $(am__append_127)
+module_device_restore_la_CFLAGS = $(AM_CFLAGS) $(am__append_128)
# Stream volume/muted/device restore module
module_stream_restore_la_SOURCES = modules/module-stream-restore.c
module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
module_stream_restore_la_LIBADD = $(MODULE_LIBADD) \
- libprotocol-native.la $(am__append_128)
-module_stream_restore_la_CFLAGS = $(AM_CFLAGS) $(am__append_129)
+ libprotocol-native.la $(am__append_129)
+module_stream_restore_la_CFLAGS = $(AM_CFLAGS) $(am__append_130)
# Card profile restore module
module_card_restore_la_SOURCES = modules/module-card-restore.c
@@ -4697,13 +4717,13 @@ module_suspend_on_idle_la_CFLAGS = $(AM_CFLAGS)
module_echo_cancel_la_SOURCES = \
modules/echo-cancel/module-echo-cancel.c \
modules/echo-cancel/null.c modules/echo-cancel/echo-cancel.h \
- $(am__append_130) $(am__append_135)
+ $(am__append_131) $(am__append_136)
module_echo_cancel_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_echo_cancel_la_LIBADD = $(MODULE_LIBADD) $(am__append_133) \
- $(am__append_137) $(am__append_139)
+module_echo_cancel_la_LIBADD = $(MODULE_LIBADD) $(am__append_134) \
+ $(am__append_138) $(am__append_140)
module_echo_cancel_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) \
- $(am__append_131) $(am__append_134) $(am__append_136) \
- $(am__append_138)
+ $(am__append_132) $(am__append_135) $(am__append_137) \
+ $(am__append_139)
@HAVE_ADRIAN_EC_TRUE@@HAVE_ORC_TRUE@nodist_module_echo_cancel_la_SOURCES = \
@HAVE_ADRIAN_EC_TRUE@@HAVE_ORC_TRUE@ modules/echo-cancel/adrian-aec-orc-gen.c \
@HAVE_ADRIAN_EC_TRUE@@HAVE_ORC_TRUE@ modules/echo-cancel/adrian-aec-orc-gen.h
@@ -4799,8 +4819,8 @@ module_bluez4_device_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) $(SBC_CFLAGS)
# Bluetooth BlueZ 5 sink / source
libbluez5_util_la_SOURCES = modules/bluetooth/bluez5-util.c \
modules/bluetooth/bluez5-util.h \
- modules/bluetooth/a2dp-codecs.h $(am__append_140) \
- $(am__append_141)
+ modules/bluetooth/a2dp-codecs.h $(am__append_141) \
+ $(am__append_142)
libbluez5_util_la_LDFLAGS = -avoid-version
libbluez5_util_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
libbluez5_util_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
@@ -5348,6 +5368,8 @@ pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-memblockq.lo: \
pulsecore/$(am__dirstamp) pulsecore/$(DEPDIR)/$(am__dirstamp)
pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-memchunk.lo: \
pulsecore/$(am__dirstamp) pulsecore/$(DEPDIR)/$(am__dirstamp)
+pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-native-common.lo: \
+ pulsecore/$(am__dirstamp) pulsecore/$(DEPDIR)/$(am__dirstamp)
pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-once.lo: \
pulsecore/$(am__dirstamp) pulsecore/$(DEPDIR)/$(am__dirstamp)
pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-packet.lo: \
@@ -6074,11 +6096,15 @@ module-rescue-streams.la: $(module_rescue_streams_la_OBJECTS) $(module_rescue_st
$(AM_V_CCLD)$(module_rescue_streams_la_LINK) -rpath $(modlibexecdir) $(module_rescue_streams_la_OBJECTS) $(module_rescue_streams_la_LIBADD) $(LIBS)
modules/module_role_cork_la-module-role-cork.lo: \
modules/$(am__dirstamp) modules/$(DEPDIR)/$(am__dirstamp)
+modules/module_role_cork_la-stream-interaction.lo: \
+ modules/$(am__dirstamp) modules/$(DEPDIR)/$(am__dirstamp)
module-role-cork.la: $(module_role_cork_la_OBJECTS) $(module_role_cork_la_DEPENDENCIES) $(EXTRA_module_role_cork_la_DEPENDENCIES)
$(AM_V_CCLD)$(module_role_cork_la_LINK) -rpath $(modlibexecdir) $(module_role_cork_la_OBJECTS) $(module_role_cork_la_LIBADD) $(LIBS)
modules/module_role_ducking_la-module-role-ducking.lo: \
modules/$(am__dirstamp) modules/$(DEPDIR)/$(am__dirstamp)
+modules/module_role_ducking_la-stream-interaction.lo: \
+ modules/$(am__dirstamp) modules/$(DEPDIR)/$(am__dirstamp)
module-role-ducking.la: $(module_role_ducking_la_OBJECTS) $(module_role_ducking_la_DEPENDENCIES) $(EXTRA_module_role_ducking_la_DEPENDENCIES)
$(AM_V_CCLD)$(module_role_ducking_la_LINK) -rpath $(modlibexecdir) $(module_role_ducking_la_OBJECTS) $(module_role_ducking_la_LIBADD) $(LIBS)
@@ -6915,7 +6941,9 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/module_position_event_sounds_la-module-position-event-sounds.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/module_rescue_streams_la-module-rescue-streams.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/module_role_cork_la-module-role-cork.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/module_role_cork_la-stream-interaction.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/module_role_ducking_la-module-role-ducking.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/module_role_ducking_la-stream-interaction.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/module_rygel_media_server_la-module-rygel-media-server.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/module_simple_protocol_tcp_la-module-protocol-stub.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@modules/$(DEPDIR)/module_simple_protocol_unix_la-module-protocol-stub.Plo@am__quote@
@@ -7072,6 +7100,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-memtrap.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-mutex-posix.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-mutex-win32.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-native-common.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-once.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-packet.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-parseaddr.Plo@am__quote@
@@ -7812,6 +7841,13 @@ pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-memchunk.lo: pulsecore/memchunk.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-memchunk.lo `test -f 'pulsecore/memchunk.c' || echo '$(srcdir)/'`pulsecore/memchunk.c
+pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-native-common.lo: pulsecore/native-common.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-native-common.lo -MD -MP -MF pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-native-common.Tpo -c -o pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-native-common.lo `test -f 'pulsecore/native-common.c' || echo '$(srcdir)/'`pulsecore/native-common.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-native-common.Tpo pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-native-common.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pulsecore/native-common.c' object='pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-native-common.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -c -o pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-native-common.lo `test -f 'pulsecore/native-common.c' || echo '$(srcdir)/'`pulsecore/native-common.c
+
pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-once.lo: pulsecore/once.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libpulsecommon_@PA_MAJORMINOR@_la_CFLAGS) $(CFLAGS) -MT pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-once.lo -MD -MP -MF pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-once.Tpo -c -o pulsecore/libpulsecommon_@PA_MAJORMINOR@_la-once.lo `test -f 'pulsecore/once.c' || echo '$(srcdir)/'`pulsecore/once.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-once.Tpo pulsecore/$(DEPDIR)/libpulsecommon_@PA_MAJORMINOR@_la-once.Plo
@@ -9023,6 +9059,13 @@ modules/module_role_cork_la-module-role-cork.lo: modules/module-role-cork.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(module_role_cork_la_CFLAGS) $(CFLAGS) -c -o modules/module_role_cork_la-module-role-cork.lo `test -f 'modules/module-role-cork.c' || echo '$(srcdir)/'`modules/module-role-cork.c
+modules/module_role_cork_la-stream-interaction.lo: modules/stream-interaction.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(module_role_cork_la_CFLAGS) $(CFLAGS) -MT modules/module_role_cork_la-stream-interaction.lo -MD -MP -MF modules/$(DEPDIR)/module_role_cork_la-stream-interaction.Tpo -c -o modules/module_role_cork_la-stream-interaction.lo `test -f 'modules/stream-interaction.c' || echo '$(srcdir)/'`modules/stream-interaction.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) modules/$(DEPDIR)/module_role_cork_la-stream-interaction.Tpo modules/$(DEPDIR)/module_role_cork_la-stream-interaction.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='modules/stream-interaction.c' object='modules/module_role_cork_la-stream-interaction.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(module_role_cork_la_CFLAGS) $(CFLAGS) -c -o modules/module_role_cork_la-stream-interaction.lo `test -f 'modules/stream-interaction.c' || echo '$(srcdir)/'`modules/stream-interaction.c
+
modules/module_role_ducking_la-module-role-ducking.lo: modules/module-role-ducking.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(module_role_ducking_la_CFLAGS) $(CFLAGS) -MT modules/module_role_ducking_la-module-role-ducking.lo -MD -MP -MF modules/$(DEPDIR)/module_role_ducking_la-module-role-ducking.Tpo -c -o modules/module_role_ducking_la-module-role-ducking.lo `test -f 'modules/module-role-ducking.c' || echo '$(srcdir)/'`modules/module-role-ducking.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) modules/$(DEPDIR)/module_role_ducking_la-module-role-ducking.Tpo modules/$(DEPDIR)/module_role_ducking_la-module-role-ducking.Plo
@@ -9030,6 +9073,13 @@ modules/module_role_ducking_la-module-role-ducking.lo: modules/module-role-ducki
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(module_role_ducking_la_CFLAGS) $(CFLAGS) -c -o modules/module_role_ducking_la-module-role-ducking.lo `test -f 'modules/module-role-ducking.c' || echo '$(srcdir)/'`modules/module-role-ducking.c
+modules/module_role_ducking_la-stream-interaction.lo: modules/stream-interaction.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(module_role_ducking_la_CFLAGS) $(CFLAGS) -MT modules/module_role_ducking_la-stream-interaction.lo -MD -MP -MF modules/$(DEPDIR)/module_role_ducking_la-stream-interaction.Tpo -c -o modules/module_role_ducking_la-stream-interaction.lo `test -f 'modules/stream-interaction.c' || echo '$(srcdir)/'`modules/stream-interaction.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) modules/$(DEPDIR)/module_role_ducking_la-stream-interaction.Tpo modules/$(DEPDIR)/module_role_ducking_la-stream-interaction.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='modules/stream-interaction.c' object='modules/module_role_ducking_la-stream-interaction.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(module_role_ducking_la_CFLAGS) $(CFLAGS) -c -o modules/module_role_ducking_la-stream-interaction.lo `test -f 'modules/stream-interaction.c' || echo '$(srcdir)/'`modules/stream-interaction.c
+
modules/rtp/module_rtp_recv_la-module-rtp-recv.lo: modules/rtp/module-rtp-recv.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(module_rtp_recv_la_CFLAGS) $(CFLAGS) -MT modules/rtp/module_rtp_recv_la-module-rtp-recv.lo -MD -MP -MF modules/rtp/$(DEPDIR)/module_rtp_recv_la-module-rtp-recv.Tpo -c -o modules/rtp/module_rtp_recv_la-module-rtp-recv.lo `test -f 'modules/rtp/module-rtp-recv.c' || echo '$(srcdir)/'`modules/rtp/module-rtp-recv.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) modules/rtp/$(DEPDIR)/module_rtp_recv_la-module-rtp-recv.Tpo modules/rtp/$(DEPDIR)/module_rtp_recv_la-module-rtp-recv.Plo
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
index 117147d..0454b6d 100644
--- a/src/daemon/cmdline.c
+++ b/src/daemon/cmdline.c
@@ -63,6 +63,7 @@ enum {
ARG_CHECK,
ARG_NO_CPU_LIMIT,
ARG_DISABLE_SHM,
+ ARG_ENABLE_MEMFD,
ARG_DUMP_RESAMPLE_METHODS,
ARG_SYSTEM,
ARG_CLEANUP_SHM,
@@ -100,6 +101,7 @@ static const struct option long_options[] = {
{"system", 2, 0, ARG_SYSTEM},
{"no-cpu-limit", 2, 0, ARG_NO_CPU_LIMIT},
{"disable-shm", 2, 0, ARG_DISABLE_SHM},
+ {"enable-memfd", 2, 0, ARG_ENABLE_MEMFD},
{"dump-resample-methods", 2, 0, ARG_DUMP_RESAMPLE_METHODS},
{"cleanup-shm", 2, 0, ARG_CLEANUP_SHM},
{NULL, 0, 0, 0}
@@ -152,7 +154,8 @@ void pa_cmdline_help(const char *argv0) {
" --use-pid-file[=BOOL] Create a PID file\n"
" --no-cpu-limit[=BOOL] Do not install CPU load limiter on\n"
" platforms that support it.\n"
- " --disable-shm[=BOOL] Disable shared memory support.\n\n"
+ " --disable-shm[=BOOL] Disable shared memory support.\n"
+ " --enable-memfd[=BOOL] Enable memfd shared memory support.\n\n"
"STARTUP SCRIPT:\n"
" -L, --load=\"MODULE ARGUMENTS\" Load the specified plugin module with\n"
@@ -389,6 +392,14 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d
conf->disable_shm = !!b;
break;
+ case ARG_ENABLE_MEMFD:
+ if ((b = optarg ? pa_parse_boolean(optarg) : 1) < 0) {
+ pa_log(_("--enable-memfd expects boolean argument"));
+ goto fail;
+ }
+ conf->disable_memfd = !b;
+ break;
+
default:
goto fail;
}
diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c
index 288aed2..965a5c8 100644
--- a/src/daemon/daemon-conf.c
+++ b/src/daemon/daemon-conf.c
@@ -92,6 +92,7 @@ static const pa_daemon_conf default_conf = {
#endif
.no_cpu_limit = true,
.disable_shm = false,
+ .disable_memfd = true,
.lock_memory = false,
.deferred_volume = true,
.default_n_fragments = 4,
@@ -526,6 +527,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) {
{ "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 },
+ { "enable-memfd", pa_config_parse_not_bool, &c->disable_memfd, NULL },
{ "flat-volumes", pa_config_parse_bool, &c->flat_volumes, NULL },
{ "lock-memory", pa_config_parse_bool, &c->lock_memory, NULL },
{ "enable-deferred-volume", pa_config_parse_bool, &c->deferred_volume, NULL },
diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h
index 458784c..82b619f 100644
--- a/src/daemon/daemon-conf.h
+++ b/src/daemon/daemon-conf.h
@@ -66,6 +66,7 @@ typedef struct pa_daemon_conf {
system_instance,
no_cpu_limit,
disable_shm,
+ disable_memfd,
disable_remixing,
disable_lfe_remixing,
load_default_script_file,
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
index 783e326..7a68653 100755
--- a/src/daemon/default.pa.in
+++ b/src/daemon/default.pa.in
@@ -19,19 +19,6 @@
# (i.e. not in system mode)
changequote(`[', `]')dnl Set up m4 quoting
-.nofail
-
-### Load something into the sample cache
-ifelse(@OS_IS_WIN32@, 1, [dnl
-load-sample x11-bell %WINDIR%\Media\ding.wav
-load-sample-dir-lazy %WINDIR%\Media\*.wav
-], [dnl
-#load-sample-lazy x11-bell /usr/share/sounds/freedesktop/stereo/bell.oga
-#load-sample-lazy pulse-hotplug /usr/share/sounds/freedesktop/stereo/device-added.oga
-#load-sample-lazy pulse-coldplug /usr/share/sounds/freedesktop/stereo/device-added.oga
-#load-sample-lazy pulse-access /usr/share/sounds/freedesktop/stereo/message.oga
-])dnl
-
.fail
### Automatically restore the volume of streams and devices
@@ -174,24 +161,6 @@ load-module module-filter-heuristics
load-module module-filter-apply
])dnl
-ifelse(@HAVE_X11@, 1, [dnl
-# X11 modules should not be started from default.pa so that one daemon
-# can be shared by multiple sessions.
-
-### Load X11 bell module
-#load-module module-x11-bell sample=x11-bell
-
-### Register ourselves in the X11 session manager
-#load-module module-x11-xsmp
-
-### Publish connection data in the X11 root window
-#.ifexists module-x11-publish@PA_SOEXT@
-#.nofail
-#load-module module-x11-publish
-#.fail
-#.endif
-])dnl
-
### Make some devices default
#set-default-sink output
#set-default-source input
diff --git a/src/daemon/main.c b/src/daemon/main.c
index c2f47b6..ae1185d 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -1017,7 +1017,9 @@ int main(int argc, char *argv[]) {
pa_assert_se(mainloop = pa_mainloop_new());
- if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm, conf->shm_size))) {
+ if (!(c = pa_core_new(pa_mainloop_get_api(mainloop), !conf->disable_shm,
+ !conf->disable_shm && !conf->disable_memfd && pa_memfd_is_locally_supported(),
+ conf->shm_size))) {
pa_log(_("pa_core_new() failed."));
goto finish;
}
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 1fe2a02..8079147 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -1518,287 +1518,263 @@ static int check_required(pa_alsa_element *e, snd_mixer_elem_t *me) {
return 0;
}
-static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
- snd_mixer_selem_id_t *sid;
- snd_mixer_elem_t *me;
+static int element_ask_vol_dB(snd_mixer_elem_t *me, pa_alsa_direction_t dir, long value, long *dBvalue) {
+ if (dir == PA_ALSA_DIRECTION_OUTPUT)
+ return snd_mixer_selem_ask_playback_vol_dB(me, value, dBvalue);
+ else
+ return snd_mixer_selem_ask_capture_vol_dB(me, value, dBvalue);
+}
- pa_assert(m);
- pa_assert(e);
- pa_assert(e->path);
+static bool element_probe_volume(pa_alsa_element *e, snd_mixer_elem_t *me) {
- SELEM_INIT(sid, e->alsa_name);
+ long min_dB = 0, max_dB = 0;
+ int r;
+ bool is_mono;
+ pa_channel_position_t p;
- if (!(me = snd_mixer_find_selem(m, sid))) {
+ if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
+ if (!snd_mixer_selem_has_playback_volume(me)) {
+ if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
+ e->direction = PA_ALSA_DIRECTION_INPUT;
+ else
+ return false;
+ }
+ } else {
+ if (!snd_mixer_selem_has_capture_volume(me)) {
+ if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
+ e->direction = PA_ALSA_DIRECTION_OUTPUT;
+ else
+ return false;
+ }
+ }
- if (e->required != PA_ALSA_REQUIRED_IGNORE)
- return -1;
+ e->direction_try_other = false;
- e->switch_use = PA_ALSA_SWITCH_IGNORE;
- e->volume_use = PA_ALSA_VOLUME_IGNORE;
- e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
+ if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+ r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
+ else
+ r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
- return 0;
+ if (r < 0) {
+ pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
+ return false;
}
- if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
- if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
-
- if (!snd_mixer_selem_has_playback_switch(me)) {
- if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
- e->direction = PA_ALSA_DIRECTION_INPUT;
- else
- e->switch_use = PA_ALSA_SWITCH_IGNORE;
- }
+ if (e->min_volume >= e->max_volume) {
+ pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.",
+ e->min_volume, e->max_volume);
+ return false;
+ }
+ if (e->volume_use == PA_ALSA_VOLUME_CONSTANT && (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
+ pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
+ e->constant_volume, e->alsa_name, e->min_volume, e->max_volume);
+ return false;
+ }
- } else {
- if (!snd_mixer_selem_has_capture_switch(me)) {
- if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
- e->direction = PA_ALSA_DIRECTION_OUTPUT;
- else
- e->switch_use = PA_ALSA_SWITCH_IGNORE;
- }
- }
+ if (e->db_fix && ((e->min_volume > e->db_fix->min_step) || (e->max_volume < e->db_fix->max_step))) {
+ pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
+ "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name,
+ e->db_fix->min_step, e->db_fix->max_step, e->min_volume, e->max_volume);
- if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
- e->direction_try_other = false;
+ decibel_fix_free(e->db_fix);
+ e->db_fix = NULL;
}
- if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
-
- if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
-
- if (!snd_mixer_selem_has_playback_volume(me)) {
- if (e->direction_try_other && snd_mixer_selem_has_capture_volume(me))
- e->direction = PA_ALSA_DIRECTION_INPUT;
- else
- e->volume_use = PA_ALSA_VOLUME_IGNORE;
- }
+ if (e->db_fix) {
+ e->has_dB = true;
+ e->min_volume = e->db_fix->min_step;
+ e->max_volume = e->db_fix->max_step;
+ min_dB = e->db_fix->db_values[0];
+ max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step];
+ } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+ e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
+ else
+ e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
- } else {
+ /* Check that the kernel driver returns consistent limits with
+ * both _get_*_dB_range() and _ask_*_vol_dB(). */
+ if (e->has_dB && !e->db_fix) {
+ long min_dB_checked = 0;
+ long max_dB_checked = 0;
- if (!snd_mixer_selem_has_capture_volume(me)) {
- if (e->direction_try_other && snd_mixer_selem_has_playback_volume(me))
- e->direction = PA_ALSA_DIRECTION_OUTPUT;
- else
- e->volume_use = PA_ALSA_VOLUME_IGNORE;
- }
+ if (element_ask_vol_dB(me, e->direction, e->min_volume, &min_dB_checked) < 0) {
+ pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->min_volume);
+ return false;
}
- if (e->volume_use != PA_ALSA_VOLUME_IGNORE) {
- long min_dB = 0, max_dB = 0;
- int r;
-
- e->direction_try_other = false;
+ if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB_checked) < 0) {
+ pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->max_volume);
+ return false;
+ }
- if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
- r = snd_mixer_selem_get_playback_volume_range(me, &e->min_volume, &e->max_volume);
- else
- r = snd_mixer_selem_get_capture_volume_range(me, &e->min_volume, &e->max_volume);
+ if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
+ pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
+ "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
+ "%0.2f dB at level %li.", e->alsa_name, min_dB / 100.0, max_dB / 100.0,
+ min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
+ return false;
+ }
+ }
- if (r < 0) {
- pa_log_warn("Failed to get volume range of %s: %s", e->alsa_name, pa_alsa_strerror(r));
- return -1;
- }
+ if (e->has_dB) {
+ e->min_dB = ((double) min_dB) / 100.0;
+ e->max_dB = ((double) max_dB) / 100.0;
- if (e->min_volume >= e->max_volume) {
- pa_log_warn("Your kernel driver is broken: it reports a volume range from %li to %li which makes no sense.", e->min_volume, e->max_volume);
- e->volume_use = PA_ALSA_VOLUME_IGNORE;
-
- } else if (e->volume_use == PA_ALSA_VOLUME_CONSTANT &&
- (e->min_volume > e->constant_volume || e->max_volume < e->constant_volume)) {
- pa_log_warn("Constant volume %li configured for element %s, but the available range is from %li to %li.",
- e->constant_volume, e->alsa_name, e->min_volume, e->max_volume);
- e->volume_use = PA_ALSA_VOLUME_IGNORE;
+ if (min_dB >= max_dB) {
+ pa_assert(!e->db_fix);
+ pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.",
+ e->min_dB, e->max_dB);
+ e->has_dB = false;
+ }
+ }
- } else {
- bool is_mono;
- pa_channel_position_t p;
-
- if (e->db_fix &&
- ((e->min_volume > e->db_fix->min_step) ||
- (e->max_volume < e->db_fix->max_step))) {
- pa_log_warn("The step range of the decibel fix for element %s (%li-%li) doesn't fit to the "
- "real hardware range (%li-%li). Disabling the decibel fix.", e->alsa_name,
- e->db_fix->min_step, e->db_fix->max_step,
- e->min_volume, e->max_volume);
-
- decibel_fix_free(e->db_fix);
- e->db_fix = NULL;
- }
+ if (e->volume_limit >= 0) {
+ if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
+ pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
+ "%li-%li. The volume limit is ignored.",
+ e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
+ else {
+ e->max_volume = e->volume_limit;
+ if (e->has_dB) {
if (e->db_fix) {
- e->has_dB = true;
- e->min_volume = e->db_fix->min_step;
- e->max_volume = e->db_fix->max_step;
- min_dB = e->db_fix->db_values[0];
- max_dB = e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step];
- } else if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
- e->has_dB = snd_mixer_selem_get_playback_dB_range(me, &min_dB, &max_dB) >= 0;
- else
- e->has_dB = snd_mixer_selem_get_capture_dB_range(me, &min_dB, &max_dB) >= 0;
-
- /* Check that the kernel driver returns consistent limits with
- * both _get_*_dB_range() and _ask_*_vol_dB(). */
- if (e->has_dB && !e->db_fix) {
- long min_dB_checked = 0;
- long max_dB_checked = 0;
-
- if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
- r = snd_mixer_selem_ask_playback_vol_dB(me, e->min_volume, &min_dB_checked);
- else
- r = snd_mixer_selem_ask_capture_vol_dB(me, e->min_volume, &min_dB_checked);
-
- if (r < 0) {
- pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->min_volume);
- return -1;
- }
-
- if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
- r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB_checked);
- else
- r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB_checked);
-
- if (r < 0) {
- pa_log_warn("Failed to query the dB value for %s at volume level %li", e->alsa_name, e->max_volume);
- return -1;
- }
+ e->db_fix->max_step = e->max_volume;
+ e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
+ } else if (element_ask_vol_dB(me, e->direction, e->max_volume, &max_dB) < 0) {
+ pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
+ e->has_dB = false;
+ } else
+ e->max_dB = ((double) max_dB) / 100.0;
+ }
+ }
+ }
- if (min_dB != min_dB_checked || max_dB != max_dB_checked) {
- pa_log_warn("Your kernel driver is broken: the reported dB range for %s (from %0.2f dB to %0.2f dB) "
- "doesn't match the dB values at minimum and maximum volume levels: %0.2f dB at level %li, "
- "%0.2f dB at level %li.",
- e->alsa_name,
- min_dB / 100.0, max_dB / 100.0,
- min_dB_checked / 100.0, e->min_volume, max_dB_checked / 100.0, e->max_volume);
- return -1;
- }
- }
+ if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+ is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
+ else
+ is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
- if (e->has_dB) {
- e->min_dB = ((double) min_dB) / 100.0;
- e->max_dB = ((double) max_dB) / 100.0;
+ if (is_mono) {
+ e->n_channels = 1;
- if (min_dB >= max_dB) {
- pa_assert(!e->db_fix);
- pa_log_warn("Your kernel driver is broken: it reports a volume range from %0.2f dB to %0.2f dB which makes no sense.", e->min_dB, e->max_dB);
- e->has_dB = false;
- }
- }
+ if (!e->override_map) {
+ for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
+ if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
+ continue;
+ e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
+ }
+ e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
+ }
+ e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
+ return true;
+ }
- if (e->volume_limit >= 0) {
- if (e->volume_limit <= e->min_volume || e->volume_limit > e->max_volume)
- pa_log_warn("Volume limit for element %s of path %s is invalid: %li isn't within the valid range "
- "%li-%li. The volume limit is ignored.",
- e->alsa_name, e->path->name, e->volume_limit, e->min_volume + 1, e->max_volume);
+ e->n_channels = 0;
+ for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
+ if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
+ continue;
- else {
- e->max_volume = e->volume_limit;
+ if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+ e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
+ else
+ e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
+ }
- if (e->has_dB) {
- if (e->db_fix) {
- e->db_fix->max_step = e->max_volume;
- e->max_dB = ((double) e->db_fix->db_values[e->db_fix->max_step - e->db_fix->min_step]) / 100.0;
+ if (e->n_channels <= 0) {
+ pa_log_warn("Volume element %s with no channels?", e->alsa_name);
+ return false;
+ } else if (e->n_channels > 2) {
+ /* FIXME: In some places code like this is used:
+ *
+ * e->masks[alsa_channel_ids[p]][e->n_channels-1]
+ *
+ * The definition of e->masks is
+ *
+ * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
+ *
+ * Since the array size is fixed at 2, we obviously
+ * don't support elements with more than two
+ * channels... */
+ pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e->alsa_name, e->n_channels);
+ return false;
+ }
- } else {
- if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
- r = snd_mixer_selem_ask_playback_vol_dB(me, e->max_volume, &max_dB);
- else
- r = snd_mixer_selem_ask_capture_vol_dB(me, e->max_volume, &max_dB);
-
- if (r < 0) {
- pa_log_warn("Failed to get dB value of %s: %s", e->alsa_name, pa_alsa_strerror(r));
- e->has_dB = false;
- } else
- e->max_dB = ((double) max_dB) / 100.0;
- }
- }
- }
- }
+ if (!e->override_map) {
+ for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
+ bool has_channel;
- if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
- is_mono = snd_mixer_selem_is_playback_mono(me) > 0;
- else
- is_mono = snd_mixer_selem_is_capture_mono(me) > 0;
+ if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
+ continue;
- if (is_mono) {
- e->n_channels = 1;
+ if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
+ has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
+ else
+ has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
- if (!e->override_map) {
- for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
- if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
- continue;
+ e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
+ }
+ }
- e->masks[alsa_channel_ids[p]][e->n_channels-1] = 0;
- }
+ e->merged_mask = 0;
+ for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
+ if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
+ continue;
- e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1] = PA_CHANNEL_POSITION_MASK_ALL;
- }
+ e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
+ }
+ return true;
+}
- e->merged_mask = e->masks[SND_MIXER_SCHN_MONO][e->n_channels-1];
- } else {
- e->n_channels = 0;
- for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
+static int element_probe(pa_alsa_element *e, snd_mixer_t *m) {
+ snd_mixer_selem_id_t *sid;
+ snd_mixer_elem_t *me;
- if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
- continue;
+ pa_assert(m);
+ pa_assert(e);
+ pa_assert(e->path);
- if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
- e->n_channels += snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
- else
- e->n_channels += snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
- }
+ SELEM_INIT(sid, e->alsa_name);
- if (e->n_channels <= 0) {
- pa_log_warn("Volume element %s with no channels?", e->alsa_name);
- e->volume_use = PA_ALSA_VOLUME_IGNORE;
- }
+ if (!(me = snd_mixer_find_selem(m, sid))) {
- else if (e->n_channels > 2) {
- /* FIXME: In some places code like this is used:
- *
- * e->masks[alsa_channel_ids[p]][e->n_channels-1]
- *
- * The definition of e->masks is
- *
- * pa_channel_position_mask_t masks[SND_MIXER_SCHN_LAST + 1][2];
- *
- * Since the array size is fixed at 2, we obviously
- * don't support elements with more than two
- * channels... */
- pa_log_warn("Volume element %s has %u channels. That's too much! I can't handle that!", e->alsa_name, e->n_channels);
- e->volume_use = PA_ALSA_VOLUME_IGNORE;
- }
+ if (e->required != PA_ALSA_REQUIRED_IGNORE)
+ return -1;
- if (!e->override_map) {
- for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
- bool has_channel;
+ e->switch_use = PA_ALSA_SWITCH_IGNORE;
+ e->volume_use = PA_ALSA_VOLUME_IGNORE;
+ e->enumeration_use = PA_ALSA_ENUMERATION_IGNORE;
- if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
- continue;
+ return 0;
+ }
- if (e->direction == PA_ALSA_DIRECTION_OUTPUT)
- has_channel = snd_mixer_selem_has_playback_channel(me, alsa_channel_ids[p]) > 0;
- else
- has_channel = snd_mixer_selem_has_capture_channel(me, alsa_channel_ids[p]) > 0;
+ if (e->switch_use != PA_ALSA_SWITCH_IGNORE) {
+ if (e->direction == PA_ALSA_DIRECTION_OUTPUT) {
- e->masks[alsa_channel_ids[p]][e->n_channels-1] = has_channel ? PA_CHANNEL_POSITION_MASK(p) : 0;
- }
- }
+ if (!snd_mixer_selem_has_playback_switch(me)) {
+ if (e->direction_try_other && snd_mixer_selem_has_capture_switch(me))
+ e->direction = PA_ALSA_DIRECTION_INPUT;
+ else
+ e->switch_use = PA_ALSA_SWITCH_IGNORE;
+ }
- e->merged_mask = 0;
- for (p = PA_CHANNEL_POSITION_FRONT_LEFT; p < PA_CHANNEL_POSITION_MAX; p++) {
- if (alsa_channel_ids[p] == SND_MIXER_SCHN_UNKNOWN)
- continue;
+ } else {
- e->merged_mask |= e->masks[alsa_channel_ids[p]][e->n_channels-1];
- }
- }
+ if (!snd_mixer_selem_has_capture_switch(me)) {
+ if (e->direction_try_other && snd_mixer_selem_has_playback_switch(me))
+ e->direction = PA_ALSA_DIRECTION_OUTPUT;
+ else
+ e->switch_use = PA_ALSA_SWITCH_IGNORE;
}
}
+ if (e->switch_use != PA_ALSA_SWITCH_IGNORE)
+ e->direction_try_other = false;
}
+ if (!element_probe_volume(e, me))
+ e->volume_use = PA_ALSA_VOLUME_IGNORE;
+
if (e->switch_use == PA_ALSA_SWITCH_SELECT) {
pa_alsa_option *o;
diff --git a/src/modules/alsa/mixer/paths/analog-input-linein.conf b/src/modules/alsa/mixer/paths/analog-input-linein.conf
index 2c94a0b..8163ffb 100644
--- a/src/modules/alsa/mixer/paths/analog-input-linein.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-linein.conf
@@ -69,6 +69,10 @@ enumeration = select
name = analog-input-linein
required-any = any
+[Option PCM Capture Source:Line In]
+name = analog-input-linein
+required-any = any
+
[Element Mic]
switch = off
volume = off
diff --git a/src/modules/alsa/mixer/paths/analog-input-mic.conf b/src/modules/alsa/mixer/paths/analog-input-mic.conf
index 4d64f92..123439b 100644
--- a/src/modules/alsa/mixer/paths/analog-input-mic.conf
+++ b/src/modules/alsa/mixer/paths/analog-input-mic.conf
@@ -76,6 +76,10 @@ enumeration = select
name = analog-input-microphone
required-any = any
+[Option PCM Capture Source:Mic-In/Mic Array]
+name = analog-input-microphone
+required-any = any
+
;;; Some AC'97s have "Mic Select" and "Mic Boost (+20dB)"
[Element Mic Select]
diff --git a/src/modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf b/src/modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf
index e534def..d5922da 100644
--- a/src/modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf
+++ b/src/modules/alsa/mixer/profile-sets/sb-omni-surround-5.1.conf
@@ -20,7 +20,8 @@
; This config supports both cases.
; Also by default there are some non-existing (physically) inputs
; and outputs that are not present here.
-; And finally in 2.1 mode LFE is not working, so it is removed also.
+; And finally officially supported modes are stereo and 5.1 + stereo S/PDIF,
+; so only these modes included.
;
; See default.conf for an explanation on the directives used here.
@@ -42,20 +43,6 @@ paths-input = analog-input-mic analog-input-linein
channel-map = left,right
direction = input
-[Mapping analog-surround-41]
-device-strings = surround41:%f
-channel-map = front-left,front-right,rear-left,rear-right,lfe
-paths-output = analog-output
-priority = 7
-direction = output
-
-[Mapping analog-surround-50]
-device-strings = surround50:%f
-channel-map = front-left,front-right,rear-left,rear-right,front-center
-paths-output = analog-output
-priority = 8
-direction = output
-
[Mapping analog-surround-51]
device-strings = surround51:%f
channel-map = front-left,front-right,rear-left,rear-right,front-center,lfe
diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c
index 286cfc9..e5cc4ae 100644
--- a/src/modules/alsa/module-alsa-card.c
+++ b/src/modules/alsa/module-alsa-card.c
@@ -369,6 +369,21 @@ static int report_jack_state(snd_mixer_elem_t *melem, unsigned int mask) {
pa_assert(u);
+ /* Changing the jack state may cause a port change, and a port change will
+ * make the sink or source change the mixer settings. If there are multiple
+ * users having pulseaudio running, the mixer changes done by inactive
+ * users may mess up the volume settings for the active users, because when
+ * the inactive users change the mixer settings, those changes are picked
+ * up by the active user's pulseaudio instance and the changes are
+ * interpreted as if the active user changed the settings manually e.g.
+ * with alsamixer. Even single-user systems suffer from this, because gdm
+ * runs its own pulseaudio instance.
+ *
+ * We rerun this function when being unsuspended to catch up on jack state
+ * changes */
+ if (u->card->suspend_cause & PA_SUSPEND_SESSION)
+ return 0;
+
if (mask == SND_CTL_EVENT_MASK_REMOVE)
return 0;
@@ -576,6 +591,20 @@ static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *de
pa_xfree(t);
}
+static pa_hook_result_t card_suspend_changed(pa_core *c, pa_card *card, struct userdata *u) {
+ void *state;
+ pa_alsa_jack *jack;
+
+ if (card->suspend_cause == 0) {
+ /* We were unsuspended, update jack state in case it changed while we were suspended */
+ PA_HASHMAP_FOREACH(jack, u->jacks, state) {
+ report_jack_state(jack->melem, 0);
+ }
+ }
+
+ return PA_HOOK_OK;
+}
+
static pa_hook_result_t sink_input_put_hook_callback(pa_core *c, pa_sink_input *sink_input, struct userdata *u) {
const char *role;
pa_sink *sink = sink_input->sink;
@@ -782,6 +811,9 @@ int pa__init(pa_module *m) {
u->card->userdata = u;
u->card->set_profile = card_set_profile;
+ pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_SUSPEND_CHANGED], PA_HOOK_NORMAL,
+ (pa_hook_cb_t) card_suspend_changed, u);
+
init_jacks(u);
init_profile(u);
init_eld_ctls(u);
diff --git a/src/modules/echo-cancel/adrian.c b/src/modules/echo-cancel/adrian.c
index 60a2b66..3c47fae 100644
--- a/src/modules/echo-cancel/adrian.c
+++ b/src/modules/echo-cancel/adrian.c
@@ -78,16 +78,16 @@ bool pa_adrian_ec_init(pa_core *c, pa_echo_canceller *ec,
rate = out_ss->rate;
*nframes = (rate * frame_size_ms) / 1000;
- ec->params.priv.adrian.blocksize = (*nframes) * pa_frame_size(out_ss);
+ ec->params.adrian.blocksize = (*nframes) * pa_frame_size(out_ss);
- pa_log_debug ("Using nframes %d, blocksize %u, channels %d, rate %d", *nframes, ec->params.priv.adrian.blocksize, out_ss->channels, out_ss->rate);
+ pa_log_debug ("Using nframes %d, blocksize %u, channels %d, rate %d", *nframes, ec->params.adrian.blocksize, out_ss->channels, out_ss->rate);
/* For now we only support SSE */
if (c->cpu_info.cpu_type == PA_CPU_X86 && (c->cpu_info.flags.x86 & PA_CPU_X86_SSE))
have_vector = 1;
- ec->params.priv.adrian.aec = AEC_init(rate, have_vector);
- if (!ec->params.priv.adrian.aec)
+ ec->params.adrian.aec = AEC_init(rate, have_vector);
+ if (!ec->params.adrian.aec)
goto fail;
pa_modargs_free(ma);
@@ -102,17 +102,17 @@ fail:
void pa_adrian_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) {
unsigned int i;
- for (i = 0; i < ec->params.priv.adrian.blocksize; i += 2) {
+ for (i = 0; i < ec->params.adrian.blocksize; i += 2) {
/* We know it's S16NE mono data */
int r = *(int16_t *)(rec + i);
int p = *(int16_t *)(play + i);
- *(int16_t *)(out + i) = (int16_t) AEC_doAEC(ec->params.priv.adrian.aec, r, p);
+ *(int16_t *)(out + i) = (int16_t) AEC_doAEC(ec->params.adrian.aec, r, p);
}
}
void pa_adrian_ec_done(pa_echo_canceller *ec) {
- if (ec->params.priv.adrian.aec) {
- AEC_done(ec->params.priv.adrian.aec);
- ec->params.priv.adrian.aec = NULL;
+ if (ec->params.adrian.aec) {
+ AEC_done(ec->params.adrian.aec);
+ ec->params.adrian.aec = NULL;
}
}
diff --git a/src/modules/echo-cancel/echo-cancel.h b/src/modules/echo-cancel/echo-cancel.h
index 29d1574..ee67949 100644
--- a/src/modules/echo-cancel/echo-cancel.h
+++ b/src/modules/echo-cancel/echo-cancel.h
@@ -64,13 +64,17 @@ struct pa_echo_canceller_params {
/* This is a void* so that we don't have to convert this whole file
* to C++ linkage. apm is a pointer to an AudioProcessing object */
void *apm;
- uint32_t blocksize;
- pa_sample_spec sample_spec;
+ unsigned int blocksize; /* in frames */
+ pa_sample_spec rec_ss, play_ss, out_ss;
+ float *rec_buffer[PA_CHANNELS_MAX], *play_buffer[PA_CHANNELS_MAX]; /* for deinterleaved buffers */
+ void *trace_callback;
bool agc;
+ bool first;
+ unsigned int agc_start_volume;
} webrtc;
#endif
/* each canceller-specific structure goes here */
- } priv;
+ };
/* Set this if canceller can do drift compensation. Also see set_drift()
* below */
@@ -128,8 +132,8 @@ struct pa_echo_canceller {
};
/* Functions to be used by the canceller analog gain control routines */
-void pa_echo_canceller_get_capture_volume(pa_echo_canceller *ec, pa_cvolume *v);
-void pa_echo_canceller_set_capture_volume(pa_echo_canceller *ec, pa_cvolume *v);
+pa_volume_t pa_echo_canceller_get_capture_volume(pa_echo_canceller *ec);
+void pa_echo_canceller_set_capture_volume(pa_echo_canceller *ec, pa_volume_t volume);
/* Computes EC block size in frames (rounded down to nearest power-of-2) based
* on sample rate and milliseconds. */
diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
index 18fe5dc..c873648 100644
--- a/src/modules/echo-cancel/module-echo-cancel.c
+++ b/src/modules/echo-cancel/module-echo-cancel.c
@@ -75,6 +75,7 @@ PA_MODULE_USAGE(
"save_aec=<save AEC data in /tmp> "
"autoloaded=<set if this module is being loaded automatically> "
"use_volume_sharing=<yes or no> "
+ "use_master_format=<yes or no> "
));
/* NOTE: Make sure the enum and ec_table are maintained in the correct order */
@@ -140,6 +141,7 @@ static const pa_echo_canceller ec_table[] = {
#define DEFAULT_ADJUST_TOLERANCE (5*PA_USEC_PER_MSEC)
#define DEFAULT_SAVE_AEC false
#define DEFAULT_AUTOLOADED false
+#define DEFAULT_USE_MASTER_FORMAT false
#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
@@ -204,7 +206,6 @@ struct userdata {
pa_core *core;
pa_module *module;
- bool autoloaded;
bool dead;
bool save_aec;
@@ -275,6 +276,7 @@ static const char* const valid_modargs[] = {
"save_aec",
"autoloaded",
"use_volume_sharing",
+ "use_master_format",
NULL
};
@@ -1433,12 +1435,6 @@ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
pa_assert_ctl_context();
pa_assert_se(u = o->userdata);
- if (u->autoloaded) {
- /* We were autoloaded, and don't support moving. Let's unload ourselves. */
- pa_log_debug("Can't move autoloaded streams, unloading");
- pa_module_unload_request(u->module, true);
- }
-
if (dest) {
pa_source_set_asyncmsgq(u->source, dest->asyncmsgq);
pa_source_update_flags(u->source, PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY, dest->flags);
@@ -1470,12 +1466,6 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
pa_sink_input_assert_ref(i);
pa_assert_se(u = i->userdata);
- if (u->autoloaded) {
- /* We were autoloaded, and don't support moving. Let's unload ourselves. */
- pa_log_debug("Can't move autoloaded streams, unloading");
- pa_module_unload_request(u->module, true);
- }
-
if (dest) {
pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq);
pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags);
@@ -1532,12 +1522,16 @@ static int canceller_process_msg_cb(pa_msgobject *o, int code, void *userdata, i
switch (code) {
case ECHO_CANCELLER_MESSAGE_SET_VOLUME: {
- pa_cvolume *v = (pa_cvolume *) userdata;
-
- if (u->use_volume_sharing)
- pa_source_set_volume(u->source, v, true, false);
- else
- pa_source_output_set_volume(u->source_output, v, false, true);
+ pa_volume_t v = PA_PTR_TO_UINT(userdata);
+ pa_cvolume vol;
+
+ if (u->use_volume_sharing) {
+ pa_cvolume_set(&vol, u->source->sample_spec.channels, v);
+ pa_source_set_volume(u->source, &vol, true, false);
+ } else {
+ pa_cvolume_set(&vol, u->source_output->sample_spec.channels, v);
+ pa_source_output_set_volume(u->source_output, &vol, false, true);
+ }
break;
}
@@ -1551,22 +1545,20 @@ static int canceller_process_msg_cb(pa_msgobject *o, int code, void *userdata, i
}
/* Called by the canceller, so source I/O thread context. */
-void pa_echo_canceller_get_capture_volume(pa_echo_canceller *ec, pa_cvolume *v) {
+pa_volume_t pa_echo_canceller_get_capture_volume(pa_echo_canceller *ec) {
#ifndef ECHO_CANCEL_TEST
- *v = ec->msg->userdata->thread_info.current_volume;
+ return pa_cvolume_avg(&ec->msg->userdata->thread_info.current_volume);
#else
- pa_cvolume_set(v, 1, PA_VOLUME_NORM);
+ return PA_VOLUME_NORM;
#endif
}
/* Called by the canceller, so source I/O thread context. */
-void pa_echo_canceller_set_capture_volume(pa_echo_canceller *ec, pa_cvolume *v) {
+void pa_echo_canceller_set_capture_volume(pa_echo_canceller *ec, pa_volume_t v) {
#ifndef ECHO_CANCEL_TEST
- if (!pa_cvolume_equal(&ec->msg->userdata->thread_info.current_volume, v)) {
- pa_cvolume *vol = pa_xnewdup(pa_cvolume, v, 1);
-
- pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(ec->msg), ECHO_CANCELLER_MESSAGE_SET_VOLUME, vol, 0, NULL,
- pa_xfree);
+ if (pa_cvolume_avg(&ec->msg->userdata->thread_info.current_volume) != v) {
+ pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(ec->msg), ECHO_CANCELLER_MESSAGE_SET_VOLUME, PA_UINT_TO_PTR(v),
+ 0, NULL, NULL);
}
#endif
}
@@ -1652,6 +1644,7 @@ int pa__init(pa_module*m) {
pa_modargs *ma;
pa_source *source_master=NULL;
pa_sink *sink_master=NULL;
+ bool autoloaded;
pa_source_output_new_data source_output_data;
pa_sink_input_new_data sink_input_data;
pa_source_new_data source_data;
@@ -1659,6 +1652,7 @@ int pa__init(pa_module*m) {
pa_memchunk silence;
uint32_t temp;
uint32_t nframes = 0;
+ bool use_master_format;
pa_assert(m);
@@ -1684,13 +1678,30 @@ int pa__init(pa_module*m) {
goto fail;
}
- source_ss = source_master->sample_spec;
- source_ss.rate = DEFAULT_RATE;
- source_ss.channels = DEFAULT_CHANNELS;
- pa_channel_map_init_auto(&source_map, source_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+ /* Set to true if we just want to inherit sample spec and channel map from the sink and source master */
+ use_master_format = DEFAULT_USE_MASTER_FORMAT;
+ if (pa_modargs_get_value_boolean(ma, "use_master_format", &use_master_format) < 0) {
+ pa_log("use_master_format= expects a boolean argument");
+ goto fail;
+ }
+ source_ss = source_master->sample_spec;
sink_ss = sink_master->sample_spec;
- sink_map = sink_master->channel_map;
+
+ if (use_master_format) {
+ source_map = source_master->channel_map;
+ sink_map = sink_master->channel_map;
+ } else {
+ source_ss = source_master->sample_spec;
+ source_ss.rate = DEFAULT_RATE;
+ source_ss.channels = DEFAULT_CHANNELS;
+ pa_channel_map_init_auto(&source_map, source_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+ sink_ss = sink_master->sample_spec;
+ sink_ss.rate = DEFAULT_RATE;
+ sink_ss.channels = DEFAULT_CHANNELS;
+ pa_channel_map_init_auto(&sink_map, sink_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+ }
u = pa_xnew0(struct userdata, 1);
if (!u) {
@@ -1736,8 +1747,8 @@ int pa__init(pa_module*m) {
goto fail;
}
- u->autoloaded = DEFAULT_AUTOLOADED;
- if (pa_modargs_get_value_boolean(ma, "autoloaded", &u->autoloaded) < 0) {
+ autoloaded = DEFAULT_AUTOLOADED;
+ if (pa_modargs_get_value_boolean(ma, "autoloaded", &autoloaded) < 0) {
pa_log("Failed to parse autoloaded value");
goto fail;
}
@@ -1782,7 +1793,7 @@ int pa__init(pa_module*m) {
pa_source_new_data_set_channel_map(&source_data, &source_map);
pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, source_master->name);
pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
- if (!u->autoloaded)
+ if (!autoloaded)
pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
if (pa_modargs_get_proplist(ma, "source_properties", source_data.proplist, PA_UPDATE_REPLACE) < 0) {
@@ -1832,7 +1843,7 @@ int pa__init(pa_module*m) {
pa_sink_new_data_set_channel_map(&sink_data, &sink_map);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, sink_master->name);
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
- if (!u->autoloaded)
+ if (!autoloaded)
pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) {
@@ -1884,6 +1895,9 @@ int pa__init(pa_module*m) {
pa_source_output_new_data_set_sample_spec(&source_output_data, &source_output_ss);
pa_source_output_new_data_set_channel_map(&source_output_data, &source_output_map);
+ if (autoloaded)
+ source_output_data.flags |= PA_SOURCE_OUTPUT_DONT_MOVE;
+
pa_source_output_new(&u->source_output, m->core, &source_output_data);
pa_source_output_new_data_done(&source_output_data);
@@ -1919,6 +1933,9 @@ int pa__init(pa_module*m) {
pa_sink_input_new_data_set_channel_map(&sink_input_data, &sink_map);
sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE;
+ if (autoloaded)
+ sink_input_data.flags |= PA_SINK_INPUT_DONT_MOVE;
+
pa_sink_input_new(&u->sink_input, m->core, &sink_input_data);
pa_sink_input_new_data_done(&sink_input_data);
@@ -2137,12 +2154,12 @@ int main(int argc, char* argv[]) {
goto fail;
}
- source_ss.format = PA_SAMPLE_S16LE;
+ source_ss.format = PA_SAMPLE_FLOAT32LE;
source_ss.rate = DEFAULT_RATE;
source_ss.channels = DEFAULT_CHANNELS;
pa_channel_map_init_auto(&source_map, source_ss.channels, PA_CHANNEL_MAP_DEFAULT);
- sink_ss.format = PA_SAMPLE_S16LE;
+ sink_ss.format = PA_SAMPLE_FLOAT32LE;
sink_ss.rate = DEFAULT_RATE;
sink_ss.channels = DEFAULT_CHANNELS;
pa_channel_map_init_auto(&sink_map, sink_ss.channels, PA_CHANNEL_MAP_DEFAULT);
diff --git a/src/modules/echo-cancel/null.c b/src/modules/echo-cancel/null.c
index 673b14f..c8ecf27 100644
--- a/src/modules/echo-cancel/null.c
+++ b/src/modules/echo-cancel/null.c
@@ -34,7 +34,7 @@ bool pa_null_ec_init(pa_core *c, pa_echo_canceller *ec,
char strss_sink[PA_SAMPLE_SPEC_SNPRINT_MAX];
*nframes = 256;
- ec->params.priv.null.out_ss = *out_ss;
+ ec->params.null.out_ss = *out_ss;
*rec_ss = *out_ss;
*rec_map = *out_map;
@@ -49,7 +49,7 @@ bool pa_null_ec_init(pa_core *c, pa_echo_canceller *ec,
void pa_null_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) {
/* The null implementation simply copies the recorded buffer to the output
buffer and ignores the play buffer. */
- memcpy(out, rec, 256 * pa_frame_size(&ec->params.priv.null.out_ss));
+ memcpy(out, rec, 256 * pa_frame_size(&ec->params.null.out_ss));
}
void pa_null_ec_done(pa_echo_canceller *ec) {
diff --git a/src/modules/echo-cancel/speex.c b/src/modules/echo-cancel/speex.c
index 11e53b3..a3ae646 100644
--- a/src/modules/echo-cancel/speex.c
+++ b/src/modules/echo-cancel/speex.c
@@ -47,9 +47,9 @@ static const char* const valid_modargs[] = {
NULL
};
-static void pa_speex_ec_fixate_spec(pa_sample_spec *rec_ss, pa_channel_map *rec_map,
- pa_sample_spec *play_ss, pa_channel_map *play_map,
- pa_sample_spec *out_ss, pa_channel_map *out_map) {
+static void speex_ec_fixate_spec(pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+ pa_sample_spec *play_ss, pa_channel_map *play_map,
+ pa_sample_spec *out_ss, pa_channel_map *out_map) {
out_ss->format = PA_SAMPLE_S16NE;
*play_ss = *out_ss;
@@ -111,26 +111,26 @@ static bool pa_speex_ec_preprocessor_init(pa_echo_canceller *ec, pa_sample_spec
goto fail;
}
- ec->params.priv.speex.pp_state = speex_preprocess_state_init(nframes, out_ss->rate);
+ ec->params.speex.pp_state = speex_preprocess_state_init(nframes, out_ss->rate);
tmp = agc;
- speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_AGC, &tmp);
+ speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_AGC, &tmp);
tmp = denoise;
- speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_DENOISE, &tmp);
+ speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_DENOISE, &tmp);
if (echo_suppress) {
if (echo_suppress_attenuation)
- speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS,
+ speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS,
&echo_suppress_attenuation);
if (echo_suppress_attenuation_active) {
- speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE,
+ speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE,
&echo_suppress_attenuation_active);
}
- speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_STATE,
- ec->params.priv.speex.state);
+ speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_STATE,
+ ec->params.speex.state);
}
pa_log_info("Loaded speex preprocessor with params: agc=%s, denoise=%s, echo_suppress=%s", pa_yes_no(agc),
@@ -170,18 +170,18 @@ bool pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec,
goto fail;
}
- pa_speex_ec_fixate_spec(rec_ss, rec_map, play_ss, play_map, out_ss, out_map);
+ speex_ec_fixate_spec(rec_ss, rec_map, play_ss, play_map, out_ss, out_map);
rate = out_ss->rate;
*nframes = pa_echo_canceller_blocksize_power2(rate, frame_size_ms);
pa_log_debug ("Using nframes %d, channels %d, rate %d", *nframes, out_ss->channels, out_ss->rate);
- ec->params.priv.speex.state = speex_echo_state_init_mc(*nframes, (rate * filter_size_ms) / 1000, out_ss->channels, out_ss->channels);
+ ec->params.speex.state = speex_echo_state_init_mc(*nframes, (rate * filter_size_ms) / 1000, out_ss->channels, out_ss->channels);
- if (!ec->params.priv.speex.state)
+ if (!ec->params.speex.state)
goto fail;
- speex_echo_ctl(ec->params.priv.speex.state, SPEEX_ECHO_SET_SAMPLING_RATE, &rate);
+ speex_echo_ctl(ec->params.speex.state, SPEEX_ECHO_SET_SAMPLING_RATE, &rate);
if (!pa_speex_ec_preprocessor_init(ec, out_ss, *nframes, ma))
goto fail;
@@ -192,34 +192,34 @@ bool pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec,
fail:
if (ma)
pa_modargs_free(ma);
- if (ec->params.priv.speex.pp_state) {
- speex_preprocess_state_destroy(ec->params.priv.speex.pp_state);
- ec->params.priv.speex.pp_state = NULL;
+ if (ec->params.speex.pp_state) {
+ speex_preprocess_state_destroy(ec->params.speex.pp_state);
+ ec->params.speex.pp_state = NULL;
}
- if (ec->params.priv.speex.state) {
- speex_echo_state_destroy(ec->params.priv.speex.state);
- ec->params.priv.speex.state = NULL;
+ if (ec->params.speex.state) {
+ speex_echo_state_destroy(ec->params.speex.state);
+ ec->params.speex.state = NULL;
}
return false;
}
void pa_speex_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) {
- speex_echo_cancellation(ec->params.priv.speex.state, (const spx_int16_t *) rec, (const spx_int16_t *) play,
+ speex_echo_cancellation(ec->params.speex.state, (const spx_int16_t *) rec, (const spx_int16_t *) play,
(spx_int16_t *) out);
/* preprecessor is run after AEC. This is not a mistake! */
- if (ec->params.priv.speex.pp_state)
- speex_preprocess_run(ec->params.priv.speex.pp_state, (spx_int16_t *) out);
+ if (ec->params.speex.pp_state)
+ speex_preprocess_run(ec->params.speex.pp_state, (spx_int16_t *) out);
}
void pa_speex_ec_done(pa_echo_canceller *ec) {
- if (ec->params.priv.speex.pp_state) {
- speex_preprocess_state_destroy(ec->params.priv.speex.pp_state);
- ec->params.priv.speex.pp_state = NULL;
+ if (ec->params.speex.pp_state) {
+ speex_preprocess_state_destroy(ec->params.speex.pp_state);
+ ec->params.speex.pp_state = NULL;
}
- if (ec->params.priv.speex.state) {
- speex_echo_state_destroy(ec->params.priv.speex.state);
- ec->params.priv.speex.state = NULL;
+ if (ec->params.speex.state) {
+ speex_echo_state_destroy(ec->params.speex.state);
+ ec->params.speex.state = NULL;
}
}
diff --git a/src/modules/echo-cancel/webrtc.cc b/src/modules/echo-cancel/webrtc.cc
index 511c7ee..db6873d 100644
--- a/src/modules/echo-cancel/webrtc.cc
+++ b/src/modules/echo-cancel/webrtc.cc
@@ -2,8 +2,9 @@
This file is part of PulseAudio.
Copyright 2011 Collabora Ltd.
+ 2015 Aldebaran SoftBank Group
- Contributor: Arun Raghavan <arun.raghavan@collabora.co.uk>
+ Contributor: Arun Raghavan <mail@arunraghavan.net>
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@@ -33,8 +34,9 @@ PA_C_DECL_BEGIN
#include "echo-cancel.h"
PA_C_DECL_END
-#include <audio_processing.h>
-#include <module_common_types.h>
+#include <webrtc/modules/audio_processing/include/audio_processing.h>
+#include <webrtc/modules/interface/module_common_types.h>
+#include <webrtc/system_wrappers/include/trace.h>
#define BLOCK_SIZE_US 10000
@@ -46,6 +48,15 @@ PA_C_DECL_END
#define DEFAULT_ROUTING_MODE "speakerphone"
#define DEFAULT_COMFORT_NOISE true
#define DEFAULT_DRIFT_COMPENSATION false
+#define DEFAULT_VAD true
+#define DEFAULT_EXTENDED_FILTER false
+#define DEFAULT_INTELLIGIBILITY_ENHANCER false
+#define DEFAULT_EXPERIMENTAL_AGC false
+#define DEFAULT_AGC_START_VOLUME 85
+#define DEFAULT_BEAMFORMING false
+#define DEFAULT_TRACE false
+
+#define WEBRTC_AGC_MAX_VOLUME 255
static const char* const valid_modargs[] = {
"high_pass_filter",
@@ -56,6 +67,15 @@ static const char* const valid_modargs[] = {
"routing_mode",
"comfort_noise",
"drift_compensation",
+ "voice_detection",
+ "extended_filter",
+ "intelligibility_enhancer",
+ "experimental_agc",
+ "agc_start_volume",
+ "beamforming",
+ "mic_geometry", /* documented in parse_mic_geometry() */
+ "target_direction", /* documented in parse_mic_geometry() */
+ "trace",
NULL
};
@@ -74,15 +94,155 @@ static int routing_mode_from_string(const char *rmode) {
return -1;
}
+class PaWebrtcTraceCallback : public webrtc::TraceCallback {
+ void Print(webrtc::TraceLevel level, const char *message, int length)
+ {
+ if (level & webrtc::kTraceError || level & webrtc::kTraceCritical)
+ pa_log(message);
+ else if (level & webrtc::kTraceWarning)
+ pa_log_warn(message);
+ else if (level & webrtc::kTraceInfo)
+ pa_log_info(message);
+ else
+ pa_log_debug(message);
+ }
+};
+
+static int webrtc_volume_from_pa(pa_volume_t v)
+{
+ return (v * WEBRTC_AGC_MAX_VOLUME) / PA_VOLUME_NORM;
+}
+
+static pa_volume_t webrtc_volume_to_pa(int v)
+{
+ return (v * PA_VOLUME_NORM) / WEBRTC_AGC_MAX_VOLUME;
+}
+
+static void webrtc_ec_fixate_spec(pa_sample_spec *rec_ss, pa_channel_map *rec_map,
+ pa_sample_spec *play_ss, pa_channel_map *play_map,
+ pa_sample_spec *out_ss, pa_channel_map *out_map,
+ bool beamforming)
+{
+ rec_ss->format = PA_SAMPLE_FLOAT32NE;
+ play_ss->format = PA_SAMPLE_FLOAT32NE;
+
+ /* AudioProcessing expects one of the following rates */
+ if (rec_ss->rate >= 48000)
+ rec_ss->rate = 48000;
+ else if (rec_ss->rate >= 32000)
+ rec_ss->rate = 32000;
+ else if (rec_ss->rate >= 16000)
+ rec_ss->rate = 16000;
+ else
+ rec_ss->rate = 8000;
+
+ *out_ss = *rec_ss;
+ *out_map = *rec_map;
+
+ if (beamforming) {
+ /* The beamformer gives us a single channel */
+ out_ss->channels = 1;
+ pa_channel_map_init_mono(out_map);
+ }
+
+ /* Playback stream rate needs to be the same as capture */
+ play_ss->rate = rec_ss->rate;
+}
+
+static bool parse_point(const char **point, float (&f)[3]) {
+ int ret, length;
+
+ ret = sscanf(*point, "%g,%g,%g%n", &f[0], &f[1], &f[2], &length);
+ if (ret != 3)
+ return false;
+
+ /* Consume the bytes we've read so far */
+ *point += length;
+
+ return true;
+}
+
+static bool parse_mic_geometry(const char **mic_geometry, std::vector<webrtc::Point>& geometry) {
+ /* The microphone geometry is expressed as cartesian point form:
+ * x1,y1,z1,x2,y2,z2,...
+ *
+ * Where x1,y1,z1 is the position of the first microphone with regards to
+ * the array's "center", x2,y2,z2 the position of the second, and so on.
+ *
+ * 'x' is the horizontal coordinate, with positive values being to the
+ * right from the mic array's perspective.
+ *
+ * 'y' is the depth coordinate, with positive values being in front of the
+ * array.
+ *
+ * 'z' is the vertical coordinate, with positive values being above the
+ * array.
+ *
+ * All distances are in meters.
+ */
+
+ /* The target direction is expected to be in spherical point form:
+ * a,e,r
+ *
+ * Where 'a is the azimuth of the first mic channel, 'e' its elevation,
+ * and 'r' the radius.
+ *
+ * 0 radians azimuth is to the right of the array, and positive angles
+ * move in a counter-clockwise direction.
+ *
+ * 0 radians elevation is horizontal w.r.t. the array, and positive
+ * angles go upwards.
+ *
+ * radius is distance from the array center in meters.
+ */
+
+ int i;
+ float f[3];
+
+ for (i = 0; i < geometry.size(); i++) {
+ if (!parse_point(mic_geometry, f)) {
+ pa_log("Failed to parse channel %d in mic_geometry", i);
+ return false;
+ }
+
+ /* Except for the last point, we should have a trailing comma */
+ if (i != geometry.size() - 1) {
+ if (**mic_geometry != ',') {
+ pa_log("Failed to parse channel %d in mic_geometry", i);
+ return false;
+ }
+
+ (*mic_geometry)++;
+ }
+
+ pa_log_debug("Got mic #%d position: (%g, %g, %g)", i, f[0], f[1], f[2]);
+
+ geometry[i].c[0] = f[0];
+ geometry[i].c[1] = f[1];
+ geometry[i].c[2] = f[2];
+ }
+
+ if (**mic_geometry != '\0') {
+ pa_log("Failed to parse mic_geometry value: more parameters than expected");
+ return false;
+ }
+
+ return true;
+}
+
bool pa_webrtc_ec_init(pa_core *c, pa_echo_canceller *ec,
pa_sample_spec *rec_ss, pa_channel_map *rec_map,
pa_sample_spec *play_ss, pa_channel_map *play_map,
pa_sample_spec *out_ss, pa_channel_map *out_map,
uint32_t *nframes, const char *args) {
webrtc::AudioProcessing *apm = NULL;
- bool hpf, ns, agc, dgc, mobile, cn;
- int rm = -1;
+ webrtc::ProcessingConfig pconfig;
+ webrtc::Config config;
+ bool hpf, ns, agc, dgc, mobile, cn, vad, ext_filter, intelligibility, experimental_agc, beamforming;
+ int rm = -1, i;
+ uint32_t agc_start_volume;
pa_modargs *ma;
+ bool trace = false;
if (!(ma = pa_modargs_new(args, valid_modargs))) {
pa_log("Failed to parse submodule arguments.");
@@ -153,32 +313,134 @@ bool pa_webrtc_ec_init(pa_core *c, pa_echo_canceller *ec,
}
}
- apm = webrtc::AudioProcessing::Create(0);
+ vad = DEFAULT_VAD;
+ if (pa_modargs_get_value_boolean(ma, "voice_detection", &vad) < 0) {
+ pa_log("Failed to parse voice_detection value");
+ goto fail;
+ }
+
+ ext_filter = DEFAULT_EXTENDED_FILTER;
+ if (pa_modargs_get_value_boolean(ma, "extended_filter", &ext_filter) < 0) {
+ pa_log("Failed to parse extended_filter value");
+ goto fail;
+ }
+
+ intelligibility = DEFAULT_INTELLIGIBILITY_ENHANCER;
+ if (pa_modargs_get_value_boolean(ma, "intelligibility_enhancer", &intelligibility) < 0) {
+ pa_log("Failed to parse intelligibility_enhancer value");
+ goto fail;
+ }
+
+ experimental_agc = DEFAULT_EXPERIMENTAL_AGC;
+ if (pa_modargs_get_value_boolean(ma, "experimental_agc", &experimental_agc) < 0) {
+ pa_log("Failed to parse experimental_agc value");
+ goto fail;
+ }
+
+ agc_start_volume = DEFAULT_AGC_START_VOLUME;
+ if (pa_modargs_get_value_u32(ma, "agc_start_volume", &agc_start_volume) < 0) {
+ pa_log("Failed to parse agc_start_volume value");
+ goto fail;
+ }
+ if (agc_start_volume > WEBRTC_AGC_MAX_VOLUME) {
+ pa_log("AGC start volume must not exceed %u", WEBRTC_AGC_MAX_VOLUME);
+ goto fail;
+ }
+ ec->params.webrtc.agc_start_volume = agc_start_volume;
+
+ beamforming = DEFAULT_BEAMFORMING;
+ if (pa_modargs_get_value_boolean(ma, "beamforming", &beamforming) < 0) {
+ pa_log("Failed to parse beamforming value");
+ goto fail;
+ }
+
+ if (ext_filter)
+ config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(true));
+ if (intelligibility)
+ pa_log_warn("The intelligibility enhancer is not currently supported");
+ if (experimental_agc)
+ config.Set<webrtc::ExperimentalAgc>(new webrtc::ExperimentalAgc(true, ec->params.webrtc.agc_start_volume));
+
+ trace = DEFAULT_TRACE;
+ if (pa_modargs_get_value_boolean(ma, "trace", &trace) < 0) {
+ pa_log("Failed to parse trace value");
+ goto fail;
+ }
+
+ if (trace) {
+ webrtc::Trace::CreateTrace();
+ webrtc::Trace::set_level_filter(webrtc::kTraceAll);
+ ec->params.webrtc.trace_callback = new PaWebrtcTraceCallback();
+ webrtc::Trace::SetTraceCallback((PaWebrtcTraceCallback *) ec->params.webrtc.trace_callback);
+ }
+
+ webrtc_ec_fixate_spec(rec_ss, rec_map, play_ss, play_map, out_ss, out_map, beamforming);
+
+ /* We do this after fixate because we need the capture channel count */
+ if (beamforming) {
+ std::vector<webrtc::Point> geometry(rec_ss->channels);
+ webrtc::SphericalPointf direction(0.0f, 0.0f, 0.0f);
+ const char *mic_geometry, *target_direction;
+
+ if (!(mic_geometry = pa_modargs_get_value(ma, "mic_geometry", NULL))) {
+ pa_log("mic_geometry must be set if beamforming is enabled");
+ goto fail;
+ }
+
+ if (!parse_mic_geometry(&mic_geometry, geometry)) {
+ pa_log("Failed to parse mic_geometry value");
+ goto fail;
+ }
+
+ if ((target_direction = pa_modargs_get_value(ma, "target_direction", NULL))) {
+ float f[3];
+
+ if (!parse_point(&target_direction, f)) {
+ pa_log("Failed to parse target_direction value");
+ goto fail;
+ }
+
+ if (*target_direction != '\0') {
+ pa_log("Failed to parse target_direction value: more parameters than expected");
+ goto fail;
+ }
+
+#define IS_ZERO(f) ((f) < 0.000001 && (f) > -0.000001)
+
+ if (!IS_ZERO(f[1]) || !IS_ZERO(f[2])) {
+ pa_log("The beamformer currently only supports targeting along the azimuth");
+ goto fail;
+ }
+
+ direction.s[0] = f[0];
+ direction.s[1] = f[1];
+ direction.s[2] = f[2];
+ }
- out_ss->format = PA_SAMPLE_S16NE;
- *play_ss = *out_ss;
- /* FIXME: the implementation actually allows a different number of
- * source/sink channels. Do we want to support that? */
- *play_map = *out_map;
- *rec_ss = *out_ss;
- *rec_map = *out_map;
+ if (!target_direction)
+ config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry));
+ else
+ config.Set<webrtc::Beamforming>(new webrtc::Beamforming(true, geometry, direction));
+ }
- apm->set_sample_rate_hz(out_ss->rate);
+ apm = webrtc::AudioProcessing::Create(config);
- apm->set_num_channels(out_ss->channels, out_ss->channels);
- apm->set_num_reverse_channels(play_ss->channels);
+ pconfig = {
+ webrtc::StreamConfig(rec_ss->rate, rec_ss->channels, false), /* input stream */
+ webrtc::StreamConfig(out_ss->rate, out_ss->channels, false), /* output stream */
+ webrtc::StreamConfig(play_ss->rate, play_ss->channels, false), /* reverse input stream */
+ webrtc::StreamConfig(play_ss->rate, play_ss->channels, false), /* reverse output stream */
+ };
+ if (apm->Initialize(pconfig) != webrtc::AudioProcessing::kNoError) {
+ pa_log("Error initialising audio processing module");
+ goto fail;
+ }
if (hpf)
apm->high_pass_filter()->Enable(true);
if (!mobile) {
- if (ec->params.drift_compensation) {
- apm->echo_cancellation()->set_device_sample_rate_hz(out_ss->rate);
- apm->echo_cancellation()->enable_drift_compensation(true);
- } else {
- apm->echo_cancellation()->enable_drift_compensation(false);
- }
-
+ apm->echo_cancellation()->enable_drift_compensation(ec->params.drift_compensation);
apm->echo_cancellation()->Enable(true);
} else {
apm->echo_control_mobile()->set_routing_mode(static_cast<webrtc::EchoControlMobile::RoutingMode>(rm));
@@ -195,28 +457,38 @@ bool pa_webrtc_ec_init(pa_core *c, pa_echo_canceller *ec,
if (mobile && rm <= webrtc::EchoControlMobile::kEarpiece) {
/* Maybe this should be a knob, but we've got a lot of knobs already */
apm->gain_control()->set_mode(webrtc::GainControl::kFixedDigital);
- ec->params.priv.webrtc.agc = false;
+ ec->params.webrtc.agc = false;
} else if (dgc) {
apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital);
- ec->params.priv.webrtc.agc = false;
+ ec->params.webrtc.agc = false;
} else {
apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveAnalog);
- if (apm->gain_control()->set_analog_level_limits(0, PA_VOLUME_NORM-1) != apm->kNoError) {
+ if (apm->gain_control()->set_analog_level_limits(0, WEBRTC_AGC_MAX_VOLUME) !=
+ webrtc::AudioProcessing::kNoError) {
pa_log("Failed to initialise AGC");
goto fail;
}
- ec->params.priv.webrtc.agc = true;
+ ec->params.webrtc.agc = true;
}
apm->gain_control()->Enable(true);
}
- apm->voice_detection()->Enable(true);
+ if (vad)
+ apm->voice_detection()->Enable(true);
+
+ ec->params.webrtc.apm = apm;
+ ec->params.webrtc.rec_ss = *rec_ss;
+ ec->params.webrtc.play_ss = *play_ss;
+ ec->params.webrtc.out_ss = *out_ss;
+ ec->params.webrtc.blocksize = (uint64_t) out_ss->rate * BLOCK_SIZE_US / PA_USEC_PER_SEC;
+ *nframes = ec->params.webrtc.blocksize;
+ ec->params.webrtc.first = true;
- ec->params.priv.webrtc.apm = apm;
- ec->params.priv.webrtc.sample_spec = *out_ss;
- ec->params.priv.webrtc.blocksize = (uint64_t)pa_bytes_per_second(out_ss) * BLOCK_SIZE_US / PA_USEC_PER_SEC;
- *nframes = ec->params.priv.webrtc.blocksize / pa_frame_size(out_ss);
+ for (i = 0; i < rec_ss->channels; i++)
+ ec->params.webrtc.rec_buffer[i] = pa_xnew(float, *nframes);
+ for (i = 0; i < play_ss->channels; i++)
+ ec->params.webrtc.play_buffer[i] = pa_xnew(float, *nframes);
pa_modargs_free(ma);
return true;
@@ -224,58 +496,77 @@ bool pa_webrtc_ec_init(pa_core *c, pa_echo_canceller *ec,
fail:
if (ma)
pa_modargs_free(ma);
- if (apm)
- webrtc::AudioProcessing::Destroy(apm);
+ if (ec->params.webrtc.trace_callback) {
+ webrtc::Trace::ReturnTrace();
+ delete ((PaWebrtcTraceCallback *) ec->params.webrtc.trace_callback);
+ } if (apm)
+ delete apm;
return false;
}
void pa_webrtc_ec_play(pa_echo_canceller *ec, const uint8_t *play) {
- webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.priv.webrtc.apm;
- webrtc::AudioFrame play_frame;
- const pa_sample_spec *ss = &ec->params.priv.webrtc.sample_spec;
+ webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.webrtc.apm;
+ const pa_sample_spec *ss = &ec->params.webrtc.play_ss;
+ int n = ec->params.webrtc.blocksize;
+ float **buf = ec->params.webrtc.play_buffer;
+ webrtc::StreamConfig config(ss->rate, ss->channels, false);
+
+ pa_deinterleave(play, (void **) buf, ss->channels, pa_sample_size(ss), n);
- play_frame._audioChannel = ss->channels;
- play_frame._frequencyInHz = ss->rate;
- play_frame._payloadDataLengthInSamples = ec->params.priv.webrtc.blocksize / pa_frame_size(ss);
- memcpy(play_frame._payloadData, play, ec->params.priv.webrtc.blocksize);
+ pa_assert_se(apm->ProcessReverseStream(buf, config, config, buf) == webrtc::AudioProcessing::kNoError);
- apm->AnalyzeReverseStream(&play_frame);
+ /* FIXME: If ProcessReverseStream() makes any changes to the audio, such as
+ * applying intelligibility enhancement, those changes don't have any
+ * effect. This function is called at the source side, but the processing
+ * would have to be done in the sink to be able to feed the processed audio
+ * to speakers. */
}
void pa_webrtc_ec_record(pa_echo_canceller *ec, const uint8_t *rec, uint8_t *out) {
- webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.priv.webrtc.apm;
- webrtc::AudioFrame out_frame;
- const pa_sample_spec *ss = &ec->params.priv.webrtc.sample_spec;
- pa_cvolume v;
-
- out_frame._audioChannel = ss->channels;
- out_frame._frequencyInHz = ss->rate;
- out_frame._payloadDataLengthInSamples = ec->params.priv.webrtc.blocksize / pa_frame_size(ss);
- memcpy(out_frame._payloadData, rec, ec->params.priv.webrtc.blocksize);
-
- if (ec->params.priv.webrtc.agc) {
- pa_cvolume_init(&v);
- pa_echo_canceller_get_capture_volume(ec, &v);
- apm->gain_control()->set_stream_analog_level(pa_cvolume_avg(&v));
+ webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.webrtc.apm;
+ const pa_sample_spec *rec_ss = &ec->params.webrtc.rec_ss;
+ const pa_sample_spec *out_ss = &ec->params.webrtc.out_ss;
+ float **buf = ec->params.webrtc.rec_buffer;
+ int n = ec->params.webrtc.blocksize;
+ int old_volume, new_volume;
+ webrtc::StreamConfig rec_config(rec_ss->rate, rec_ss->channels, false);
+ webrtc::StreamConfig out_config(out_ss->rate, out_ss->channels, false);
+
+ pa_deinterleave(rec, (void **) buf, rec_ss->channels, pa_sample_size(rec_ss), n);
+
+ if (ec->params.webrtc.agc) {
+ pa_volume_t v = pa_echo_canceller_get_capture_volume(ec);
+ old_volume = webrtc_volume_from_pa(v);
+ apm->gain_control()->set_stream_analog_level(old_volume);
}
apm->set_stream_delay_ms(0);
- apm->ProcessStream(&out_frame);
+ pa_assert_se(apm->ProcessStream(buf, rec_config, out_config, buf) == webrtc::AudioProcessing::kNoError);
+
+ if (ec->params.webrtc.agc) {
+ if (PA_UNLIKELY(ec->params.webrtc.first)) {
+ /* We start at a sane default volume (taken from the Chromium
+ * condition on the experimental AGC in audio_processing.h). This is
+ * needed to make sure that there's enough energy in the capture
+ * signal for the AGC to work */
+ ec->params.webrtc.first = false;
+ new_volume = ec->params.webrtc.agc_start_volume;
+ } else {
+ new_volume = apm->gain_control()->stream_analog_level();
+ }
- if (ec->params.priv.webrtc.agc) {
- pa_cvolume_set(&v, ss->channels, apm->gain_control()->stream_analog_level());
- pa_echo_canceller_set_capture_volume(ec, &v);
+ if (old_volume != new_volume)
+ pa_echo_canceller_set_capture_volume(ec, webrtc_volume_to_pa(new_volume));
}
- memcpy(out, out_frame._payloadData, ec->params.priv.webrtc.blocksize);
+ pa_interleave((const void **) buf, out_ss->channels, out, pa_sample_size(out_ss), n);
}
void pa_webrtc_ec_set_drift(pa_echo_canceller *ec, float drift) {
- webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.priv.webrtc.apm;
- const pa_sample_spec *ss = &ec->params.priv.webrtc.sample_spec;
+ webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.webrtc.apm;
- apm->echo_cancellation()->set_stream_drift_samples(drift * ec->params.priv.webrtc.blocksize / pa_frame_size(ss));
+ apm->echo_cancellation()->set_stream_drift_samples(drift * ec->params.webrtc.blocksize);
}
void pa_webrtc_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) {
@@ -284,8 +575,20 @@ void pa_webrtc_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *
}
void pa_webrtc_ec_done(pa_echo_canceller *ec) {
- if (ec->params.priv.webrtc.apm) {
- webrtc::AudioProcessing::Destroy((webrtc::AudioProcessing*)ec->params.priv.webrtc.apm);
- ec->params.priv.webrtc.apm = NULL;
+ int i;
+
+ if (ec->params.webrtc.trace_callback) {
+ webrtc::Trace::ReturnTrace();
+ delete ((PaWebrtcTraceCallback *) ec->params.webrtc.trace_callback);
}
+
+ if (ec->params.webrtc.apm) {
+ delete (webrtc::AudioProcessing*)ec->params.webrtc.apm;
+ ec->params.webrtc.apm = NULL;
+ }
+
+ for (i = 0; i < ec->params.webrtc.rec_ss.channels; i++)
+ pa_xfree(ec->params.webrtc.rec_buffer[i]);
+ for (i = 0; i < ec->params.webrtc.play_ss.channels; i++)
+ pa_xfree(ec->params.webrtc.play_buffer[i]);
}
diff --git a/src/modules/macosx/module-coreaudio-device.c b/src/modules/macosx/module-coreaudio-device.c
index 0c92d42..6c9e55d 100644
--- a/src/modules/macosx/module-coreaudio-device.c
+++ b/src/modules/macosx/module-coreaudio-device.c
@@ -376,23 +376,25 @@ static int ca_sink_set_state(pa_sink *s, pa_sink_state_t state) {
}
/* Caveat: The caller is responsible to get rid of the CFString(Ref). */
-static bool CFString_to_cstr_n(CFStringRef cfstr, char *buf, long n) {
- bool ret;
-
- pa_assert (buf);
+static char * CFString_to_cstr(CFStringRef cfstr) {
+ char *ret = NULL;
ret = false;
if (cfstr != NULL) {
const char *tmp = CFStringGetCStringPtr(cfstr, kCFStringEncodingUTF8);
+ CFIndex n = CFStringGetLength(cfstr) + 1 /* for the terminating NULL */;
+
+ ret = pa_xmalloc(n);
if (tmp == NULL) {
- if (CFStringGetCString(cfstr, buf, n, kCFStringEncodingUTF8))
- ret = true;
+ if (!CFStringGetCString(cfstr, ret, n, kCFStringEncodingUTF8)) {
+ pa_xfree(ret);
+ ret = NULL;
+ }
} else {
- strncpy(buf, tmp, n);
- buf[n - 1] = 0;
- ret = true;
+ strncpy(ret, tmp, n - 1);
+ ret[n - 1] = '\0';
}
}
@@ -408,11 +410,16 @@ static int ca_device_create_sink(pa_module *m, AudioBuffer *buf, int channel_idx
coreaudio_sink *ca_sink;
pa_sink *sink;
unsigned int i;
- char tmp[255];
+ char *tmp;
pa_strbuf *strbuf;
AudioObjectPropertyAddress property_address;
CFStringRef tmp_cfstr = NULL;
+ if (buf->mNumberChannels > PA_CHANNELS_MAX) {
+ pa_log("Skipping device with more channels than we support (%u)", (unsigned int) buf->mNumberChannels);
+ return -1;
+ }
+
ca_sink = pa_xnew0(coreaudio_sink, 1);
ca_sink->map.channels = buf->mNumberChannels;
ca_sink->ss.channels = buf->mNumberChannels;
@@ -425,23 +432,24 @@ static int ca_device_create_sink(pa_module *m, AudioBuffer *buf, int channel_idx
property_address.mSelector = kAudioObjectPropertyElementName;
property_address.mScope = kAudioDevicePropertyScopeOutput;
property_address.mElement = channel_idx + i + 1;
- size = sizeof(tmp);
+ size = sizeof(tmp_cfstr);
err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &tmp_cfstr);
if (err == 0) {
- err = !(CFString_to_cstr_n(tmp_cfstr, tmp, sizeof(tmp)));
+ tmp = CFString_to_cstr(tmp_cfstr);
- if (tmp_cfstr) {
+ if (tmp_cfstr)
CFRelease(tmp_cfstr);
- }
}
- if (err || !strlen(tmp))
- snprintf(tmp, sizeof(tmp), "Channel %d", (int) property_address.mElement);
-
if (i > 0)
pa_strbuf_puts(strbuf, ", ");
- pa_strbuf_puts(strbuf, tmp);
+ if (err || !tmp || !strlen(tmp))
+ pa_strbuf_printf(strbuf, "Channel %d", (int) property_address.mElement);
+ else
+ pa_strbuf_puts(strbuf, tmp);
+
+ pa_xfree(tmp);
}
ca_sink->name = pa_strbuf_to_string_free(strbuf);
@@ -535,11 +543,16 @@ static int ca_device_create_source(pa_module *m, AudioBuffer *buf, int channel_i
coreaudio_source *ca_source;
pa_source *source;
unsigned int i;
- char tmp[255];
+ char *tmp;
pa_strbuf *strbuf;
AudioObjectPropertyAddress property_address;
CFStringRef tmp_cfstr = NULL;
+ if (buf->mNumberChannels > PA_CHANNELS_MAX) {
+ pa_log("Skipping device with more channels than we support (%u)", (unsigned int) buf->mNumberChannels);
+ return -1;
+ }
+
ca_source = pa_xnew0(coreaudio_source, 1);
ca_source->map.channels = buf->mNumberChannels;
ca_source->ss.channels = buf->mNumberChannels;
@@ -552,23 +565,24 @@ static int ca_device_create_source(pa_module *m, AudioBuffer *buf, int channel_i
property_address.mSelector = kAudioObjectPropertyElementName;
property_address.mScope = kAudioDevicePropertyScopeInput;
property_address.mElement = channel_idx + i + 1;
- size = sizeof(tmp);
+ size = sizeof(tmp_cfstr);
err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &tmp_cfstr);
if (err == 0) {
- err = !(CFString_to_cstr_n(tmp_cfstr, tmp, sizeof(tmp)));
+ tmp = CFString_to_cstr(tmp_cfstr);
- if (tmp_cfstr) {
+ if (tmp_cfstr)
CFRelease(tmp_cfstr);
- }
}
- if (err || !strlen(tmp))
- snprintf(tmp, sizeof(tmp), "Channel %d", (int) property_address.mElement);
-
if (i > 0)
pa_strbuf_puts(strbuf, ", ");
- pa_strbuf_puts(strbuf, tmp);
+ if (err || !tmp || !strlen(tmp))
+ pa_strbuf_printf(strbuf, "Channel %d", (int) property_address.mElement);
+ else
+ pa_strbuf_puts(strbuf, tmp);
+
+ pa_xfree(tmp);
}
ca_source->name = pa_strbuf_to_string_free(strbuf);
diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
index f906843..7545aa5 100644
--- a/src/modules/module-card-restore.c
+++ b/src/modules/module-card-restore.c
@@ -64,7 +64,7 @@ struct userdata {
pa_database *database;
};
-#define ENTRY_VERSION 3
+#define ENTRY_VERSION 4
struct port_info {
char *name;
@@ -75,6 +75,8 @@ struct port_info {
struct entry {
char *profile;
pa_hashmap *ports; /* Port name -> struct port_info */
+ char *preferred_input_port;
+ char *preferred_output_port;
};
static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
@@ -131,6 +133,8 @@ static struct port_info *port_info_new(pa_device_port *port) {
static void entry_free(struct entry* e) {
pa_assert(e);
+ pa_xfree(e->preferred_output_port);
+ pa_xfree(e->preferred_input_port);
pa_xfree(e->profile);
pa_hashmap_free(e->ports);
@@ -176,6 +180,12 @@ static bool entrys_equal(struct entry *a, struct entry *b) {
return false;
}
+ if (!pa_safe_streq(a->preferred_input_port, b->preferred_input_port))
+ return false;
+
+ if (!pa_safe_streq(a->preferred_output_port, b->preferred_output_port))
+ return false;
+
return true;
}
@@ -201,6 +211,9 @@ static bool entry_write(struct userdata *u, const char *name, const struct entry
pa_tagstruct_puts(t, p_info->profile);
}
+ pa_tagstruct_puts(t, e->preferred_input_port);
+ pa_tagstruct_puts(t, e->preferred_output_port);
+
key.data = (char *) name;
key.size = strlen(name);
@@ -314,6 +327,18 @@ static struct entry* entry_read(struct userdata *u, const char *name) {
}
}
+ if (version >= 4) {
+ const char *preferred_input_port;
+ const char *preferred_output_port;
+
+ if (pa_tagstruct_gets(t, &preferred_input_port) < 0
+ || pa_tagstruct_gets(t, &preferred_output_port) < 0)
+ goto fail;
+
+ e->preferred_input_port = pa_xstrdup(preferred_input_port);
+ e->preferred_output_port = pa_xstrdup(preferred_output_port);
+ }
+
if (!pa_tagstruct_eof(t))
goto fail;
@@ -547,6 +572,46 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new
if (!p->preferred_profile && p_info->profile)
pa_device_port_set_preferred_profile(p, p_info->profile);
}
+
+ if (e->preferred_input_port) {
+ p = pa_hashmap_get(new_data->ports, e->preferred_input_port);
+ if (p)
+ pa_card_new_data_set_preferred_port(new_data, PA_DIRECTION_INPUT, p);
+ }
+
+ if (e->preferred_output_port) {
+ p = pa_hashmap_get(new_data->ports, e->preferred_output_port);
+ if (p)
+ pa_card_new_data_set_preferred_port(new_data, PA_DIRECTION_OUTPUT, p);
+ }
+
+ entry_free(e);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_preferred_port_changed_callback(pa_core *core, pa_card_preferred_port_changed_hook_data *data,
+ struct userdata *u) {
+ struct entry *e;
+ pa_card *card;
+
+ card = data->card;
+
+ e = entry_read(u, card->name);
+ if (!e)
+ e = entry_from_card(card);
+
+ if (data->direction == PA_DIRECTION_INPUT) {
+ pa_xfree(e->preferred_input_port);
+ e->preferred_input_port = pa_xstrdup(card->preferred_input_port ? card->preferred_input_port->name : NULL);
+ } else {
+ pa_xfree(e->preferred_output_port);
+ e->preferred_output_port = pa_xstrdup(card->preferred_output_port ? card->preferred_output_port->name : NULL);
+ }
+
+ if (entry_write(u, card->name, e))
+ trigger_save(u);
+
entry_free(e);
return PA_HOOK_OK;
@@ -570,6 +635,7 @@ int pa__init(pa_module*m) {
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) card_new_hook_callback, u);
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) card_put_hook_callback, u);
+ pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_preferred_port_changed_callback, u);
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_changed_callback, u);
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_ADDED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_added_callback, u);
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED],
@@ -608,8 +674,10 @@ void pa__done(pa_module*m) {
if (!(u = m->userdata))
return;
- if (u->save_time_event)
+ if (u->save_time_event) {
u->core->mainloop->time_free(u->save_time_event);
+ pa_database_sync(u->database);
+ }
if (u->database)
pa_database_close(u->database);
diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
index a9a2de0..d6c6b76 100644
--- a/src/modules/module-detect.c
+++ b/src/modules/module-detect.c
@@ -46,7 +46,10 @@ PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(true);
PA_MODULE_USAGE("just-one=<boolean>");
+
+#ifdef __linux__
PA_MODULE_DEPRECATED("Please use module-udev-detect instead of module-detect!");
+#endif
static const char* const valid_modargs[] = {
"just-one",
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index f125bdd..2a0a67f 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -292,8 +292,10 @@ static struct entry* entry_read(struct userdata *u, const char *name) {
pa_zero(data);
- if (!pa_database_get(u->database, &key, &data))
- goto fail;
+ if (!pa_database_get(u->database, &key, &data)) {
+ pa_log_debug("Database contains no data for key: %s", name);
+ return NULL;
+ }
t = pa_tagstruct_new_fixed(data.data, data.size);
e = entry_new();
@@ -647,8 +649,10 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha
}
static void route_sink_input(struct userdata *u, pa_sink_input *si) {
+ const char *auto_filtered_prop;
const char *role;
uint32_t role_index, device_index;
+ bool auto_filtered = false;
pa_sink *sink;
pa_assert(u);
@@ -661,6 +665,10 @@ static void route_sink_input(struct userdata *u, pa_sink_input *si) {
if (!si->sink)
return;
+ auto_filtered_prop = pa_proplist_gets(si->proplist, "module-device-manager.auto_filtered");
+ if (auto_filtered_prop)
+ auto_filtered = (pa_parse_boolean(auto_filtered_prop) == 1);
+
/* 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 */
@@ -682,6 +690,13 @@ static void route_sink_input(struct userdata *u, pa_sink_input *si) {
if (!(sink = pa_idxset_get_by_index(u->core->sinks, device_index)))
return;
+ if (auto_filtered) {
+ /* For streams for which a filter has been loaded by another module, we
+ * do not try to execute moves within the same filter hierarchy */
+ if (pa_sink_get_master(si->sink) == pa_sink_get_master(sink))
+ return;
+ }
+
if (si->sink != sink)
pa_sink_input_move_to(si, sink, false);
}
@@ -705,8 +720,10 @@ static pa_hook_result_t route_sink_inputs(struct userdata *u, pa_sink *ignore_si
}
static void route_source_output(struct userdata *u, pa_source_output *so) {
+ const char *auto_filtered_prop;
const char *role;
uint32_t role_index, device_index;
+ bool auto_filtered = false;
pa_source *source;
pa_assert(u);
@@ -722,6 +739,10 @@ static void route_source_output(struct userdata *u, pa_source_output *so) {
if (!so->source)
return;
+ auto_filtered_prop = pa_proplist_gets(so->proplist, "module-device-manager.auto_filtered");
+ if (auto_filtered_prop)
+ auto_filtered = (pa_parse_boolean(auto_filtered_prop) == 1);
+
/* 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 */
@@ -743,6 +764,13 @@ static void route_source_output(struct userdata *u, pa_source_output *so) {
if (!(source = pa_idxset_get_by_index(u->core->sources, device_index)))
return;
+ if (auto_filtered) {
+ /* For streams for which a filter has been loaded by another module, we
+ * do not try to execute moves within the same filter hierarchy */
+ if (pa_source_get_master(so->source) == pa_source_get_master(source))
+ return;
+ }
+
if (so->source != source)
pa_source_output_move_to(so, source, false);
}
diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c
index c3f83ce..364d68b 100644
--- a/src/modules/module-filter-apply.c
+++ b/src/modules/module-filter-apply.c
@@ -38,6 +38,7 @@
#include "module-filter-apply-symdef.h"
#define PA_PROP_FILTER_APPLY_MOVING "filter.apply.moving"
+#define PA_PROP_MDM_AUTO_FILTERED "module-device-manager.auto_filtered"
PA_MODULE_AUTHOR("Colin Guthrie");
PA_MODULE_DESCRIPTION("Load filter sinks automatically when needed");
@@ -65,6 +66,10 @@ struct filter {
struct userdata {
pa_core *core;
pa_hashmap *filters;
+ /* Keep track of streams we're managing PA_PROP_MDM_AUTO_FILTERED on, we're
+ * only maintaining membership, so key and value are just the
+ * pa_sink_input/pa_source_output. */
+ pa_hashmap *mdm_ignored_inputs, *mdm_ignored_outputs;
bool autoclean;
pa_time_event *housekeeping_time_event;
};
@@ -124,7 +129,7 @@ static const char* should_filter(pa_object *o, bool is_sink_input) {
else
pl = PA_SOURCE_OUTPUT(o)->proplist;
- /* If the stream doesn't what any filter, then let it be. */
+ /* If the stream doesn't want any filter, then let it be. */
if ((apply = pa_proplist_gets(pl, PA_PROP_FILTER_APPLY)) && !pa_streq(apply, "")) {
const char* suppress = pa_proplist_gets(pl, PA_PROP_FILTER_SUPPRESS);
@@ -270,14 +275,20 @@ static void trigger_housekeeping(struct userdata *u) {
u->housekeeping_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + HOUSEKEEPING_INTERVAL, housekeeping_time_callback, u);
}
-static int do_move(pa_object *obj, pa_object *parent, bool restore, bool is_input) {
- if (is_input)
- return pa_sink_input_move_to(PA_SINK_INPUT(obj), PA_SINK(parent), restore);
- else
- return pa_source_output_move_to(PA_SOURCE_OUTPUT(obj), PA_SOURCE(parent), restore);
+static int do_move(struct userdata *u, pa_object *obj, pa_object *parent, bool is_input) {
+ /* Keep track of objects that we've marked for module-device-manager to ignore */
+ pa_hashmap_put(is_input ? u->mdm_ignored_inputs : u->mdm_ignored_outputs, obj, obj);
+
+ if (is_input) {
+ pa_sink_input_set_property(PA_SINK_INPUT(obj), PA_PROP_MDM_AUTO_FILTERED, "1");
+ return pa_sink_input_move_to(PA_SINK_INPUT(obj), PA_SINK(parent), false);
+ } else {
+ pa_source_output_set_property(PA_SOURCE_OUTPUT(obj), PA_PROP_MDM_AUTO_FILTERED, "1");
+ return pa_source_output_move_to(PA_SOURCE_OUTPUT(obj), PA_SOURCE(parent), false);
+ }
}
-static void move_object_for_filter(pa_object *o, struct filter* filter, bool restore, bool is_sink_input) {
+static void move_object_for_filter(struct userdata *u, pa_object *o, struct filter* filter, bool restore, bool is_sink_input) {
pa_object *parent;
pa_proplist *pl;
const char *name;
@@ -301,7 +312,7 @@ static void move_object_for_filter(pa_object *o, struct filter* filter, bool res
pa_proplist_sets(pl, PA_PROP_FILTER_APPLY_MOVING, "1");
- if (do_move(o, parent, false, is_sink_input) < 0)
+ if (do_move(u, o, parent, is_sink_input) < 0)
pa_log_info("Failed to move %s for \"%s\" to <%s>.", is_sink_input ? "sink-input" : "source-output",
pa_strnull(pa_proplist_gets(pl, PA_PROP_APPLICATION_NAME)), name);
else
@@ -315,7 +326,7 @@ static void move_objects_for_filter(struct userdata *u, pa_object *o, struct fil
bool is_sink_input) {
if (!should_group_filter(filter))
- move_object_for_filter(o, filter, restore, is_sink_input);
+ move_object_for_filter(u, o, filter, restore, is_sink_input);
else {
pa_source_output *so;
pa_sink_input *si;
@@ -328,7 +339,7 @@ static void move_objects_for_filter(struct userdata *u, pa_object *o, struct fil
g = get_group(PA_OBJECT(so), false);
if (pa_streq(g, group))
- move_object_for_filter(PA_OBJECT(so), filter, restore, false);
+ move_object_for_filter(u, PA_OBJECT(so), filter, restore, false);
pa_xfree(g);
}
@@ -337,7 +348,7 @@ static void move_objects_for_filter(struct userdata *u, pa_object *o, struct fil
g = get_group(PA_OBJECT(si), true);
if (pa_streq(g, group))
- move_object_for_filter(PA_OBJECT(si), filter, restore, true);
+ move_object_for_filter(u, PA_OBJECT(si), filter, restore, true);
pa_xfree(g);
}
@@ -405,6 +416,8 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i
pa_sink *sink = NULL;
pa_source *source = NULL;
pa_module *module = NULL;
+ char *module_name = NULL;
+ struct filter *fltr = NULL, *filter = NULL;
if (is_sink_input) {
sink = PA_SINK_INPUT(o)->sink;
@@ -420,31 +433,27 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i
/* If there is no sink/source yet, we can't do much */
if ((is_sink_input && !sink) || (!is_sink_input && !source))
- return PA_HOOK_OK;
+ goto done;
/* If the stream doesn't what any filter, then let it be. */
if ((want = should_filter(o, is_sink_input))) {
- char *module_name;
- struct filter *fltr, *filter;
-
/* We need to ensure the SI is playing on a sink of this type
* attached to the sink it's "officially" playing on */
if (!module)
- return PA_HOOK_OK;
+ goto done;
module_name = pa_sprintf_malloc("module-%s", want);
if (pa_streq(module->name, module_name)) {
pa_log_debug("Stream appears to be playing on an appropriate sink already. Ignoring.");
- pa_xfree(module_name);
- return PA_HOOK_OK;
+ goto done;
}
fltr = filter_new(want, sink, source);
if (should_group_filter(fltr) && !find_paired_master(u, fltr, o, is_sink_input)) {
pa_log_debug("Want group filtering but don't have enough streams.");
- return PA_HOOK_OK;
+ goto done;
}
if (!(filter = pa_hashmap_get(u->filters, fltr))) {
@@ -467,14 +476,10 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i
pa_xfree(args);
}
- pa_xfree(fltr);
-
if (!filter) {
pa_log("Unable to load %s", module_name);
- pa_xfree(module_name);
- return PA_HOOK_OK;
+ goto done;
}
- pa_xfree(module_name);
/* We can move the stream now as we know the destination. If this
* isn't true, we will do it later when the sink appears. */
@@ -484,7 +489,6 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i
}
} else {
void *state;
- struct filter *filter = NULL;
/* We do not want to filter... but are we already filtered?
* This can happen if an input's proplist changes */
@@ -500,6 +504,10 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, bool is_sink_i
if (done_something)
trigger_housekeeping(u);
+done:
+ pa_xfree(module_name);
+ pa_xfree(fltr);
+
return PA_HOOK_OK;
}
@@ -517,6 +525,9 @@ static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *
if (pa_proplist_gets(i->proplist, PA_PROP_FILTER_APPLY_MOVING))
return PA_HOOK_OK;
+ /* If we're managing m-d-m.auto_filtered on this, remove and re-add if we're continuing to manage it */
+ pa_hashmap_remove(u->mdm_ignored_inputs, i);
+
return process(u, PA_OBJECT(i), true);
}
@@ -536,6 +547,8 @@ static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, st
if (pa_hashmap_size(u->filters) > 0)
trigger_housekeeping(u);
+ pa_hashmap_remove(u->mdm_ignored_inputs, i);
+
return PA_HOOK_OK;
}
@@ -589,6 +602,9 @@ static pa_hook_result_t source_output_move_finish_cb(pa_core *core, pa_source_ou
if (pa_proplist_gets(o->proplist, PA_PROP_FILTER_APPLY_MOVING))
return PA_HOOK_OK;
+ /* If we're managing m-d-m.auto_filtered on this, remove and re-add if we're continuing to manage it */
+ pa_hashmap_remove(u->mdm_ignored_outputs, o);
+
return process(u, PA_OBJECT(o), false);
}
@@ -608,6 +624,8 @@ static pa_hook_result_t source_output_unlink_cb(pa_core *core, pa_source_output
if (pa_hashmap_size(u->filters) > 0)
trigger_housekeeping(u);
+ pa_hashmap_remove(u->mdm_ignored_outputs, o);
+
return PA_HOOK_OK;
}
@@ -647,6 +665,16 @@ static pa_hook_result_t source_unlink_cb(pa_core *core, pa_source *source, struc
return PA_HOOK_OK;
}
+static void unset_mdm_ignore_input(pa_sink_input *i)
+{
+ pa_sink_input_set_property(i, PA_PROP_MDM_AUTO_FILTERED, NULL);
+}
+
+static void unset_mdm_ignore_output(pa_source_output *o)
+{
+ pa_source_output_set_property(o, PA_PROP_MDM_AUTO_FILTERED, NULL);
+}
+
int pa__init(pa_module *m) {
pa_modargs *ma = NULL;
struct userdata *u;
@@ -669,6 +697,8 @@ int pa__init(pa_module *m) {
}
u->filters = pa_hashmap_new(filter_hash, filter_compare);
+ u->mdm_ignored_inputs = pa_hashmap_new_full(NULL, NULL, (pa_free_cb_t) unset_mdm_ignore_input, NULL);
+ u->mdm_ignored_outputs = pa_hashmap_new_full(NULL, NULL, (pa_free_cb_t) unset_mdm_ignore_output, NULL);
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_cb, u);
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u);
@@ -716,5 +746,11 @@ void pa__done(pa_module *m) {
pa_hashmap_free(u->filters);
}
+ if (u->mdm_ignored_inputs)
+ pa_hashmap_free(u->mdm_ignored_inputs);
+
+ if (u->mdm_ignored_outputs)
+ pa_hashmap_free(u->mdm_ignored_outputs);
+
pa_xfree(u);
}
diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 37bf7b1..f4d0761 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -398,9 +398,9 @@ static bool source_output_may_move_to_cb(pa_source_output *o, pa_source *dest) {
/* Called from main thread */
static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
- pa_proplist *p;
- const char *n;
struct userdata *u;
+ char *input_description;
+ const char *n;
if (!dest)
return;
@@ -409,14 +409,13 @@ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
pa_assert_ctl_context();
pa_assert_se(u = o->userdata);
- p = pa_proplist_new();
- pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback of %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ input_description = pa_sprintf_malloc("Loopback of %s",
+ pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ pa_sink_input_set_property(u->sink_input, PA_PROP_MEDIA_NAME, input_description);
+ pa_xfree(input_description);
if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
- pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);
-
- pa_sink_input_update_proplist(u->sink_input, PA_UPDATE_REPLACE, p);
- pa_proplist_free(p);
+ pa_sink_input_set_property(u->sink_input, PA_PROP_DEVICE_ICON_NAME, n);
if (pa_source_get_state(dest) == PA_SOURCE_SUSPENDED)
pa_sink_input_cork(u->sink_input, true);
@@ -671,7 +670,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
/* Called from main thread */
static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
struct userdata *u;
- pa_proplist *p;
+ char *output_description;
const char *n;
if (!dest)
@@ -681,14 +680,13 @@ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
pa_assert_ctl_context();
pa_assert_se(u = i->userdata);
- p = pa_proplist_new();
- pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ output_description = pa_sprintf_malloc("Loopback to %s",
+ pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION)));
+ pa_source_output_set_property(u->source_output, PA_PROP_MEDIA_NAME, output_description);
+ pa_xfree(output_description);
if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME)))
- pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n);
-
- pa_source_output_update_proplist(u->source_output, PA_UPDATE_REPLACE, p);
- pa_proplist_free(p);
+ pa_source_output_set_property(u->source_output, PA_PROP_MEDIA_ICON_NAME, n);
if (pa_sink_get_state(dest) == PA_SINK_SUSPENDED)
pa_source_output_cork(u->source_output, true);
diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
index 901dabd..60ac1c4 100644
--- a/src/modules/module-rescue-streams.c
+++ b/src/modules/module-rescue-streams.c
@@ -309,7 +309,7 @@ static pa_hook_result_t source_output_move_fail_hook_callback(pa_core *c, pa_sou
return PA_HOOK_OK;
} else {
- pa_log_info("Successfully moved output input %u \"%s\" to %s.", i->index,
+ pa_log_info("Successfully moved source output %u \"%s\" to %s.", i->index,
pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME)), target->name);
return PA_HOOK_STOP;
}
diff --git a/src/modules/module-role-cork.c b/src/modules/module-role-cork.c
index 7c0f59f..d71cc38 100644
--- a/src/modules/module-role-cork.c
+++ b/src/modules/module-role-cork.c
@@ -21,15 +21,9 @@
#include <config.h>
#endif
-#include <pulse/xmalloc.h>
-
#include <pulsecore/macro.h>
-#include <pulsecore/hashmap.h>
-#include <pulsecore/hook-list.h>
#include <pulsecore/core.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/sink-input.h>
-#include <pulsecore/modargs.h>
+#include <stream-interaction.h>
#include "module-role-cork-symdef.h"
@@ -49,260 +43,16 @@ static const char* const valid_modargs[] = {
NULL
};
-struct userdata {
- pa_core *core;
- pa_hashmap *cork_state;
- pa_idxset *trigger_roles;
- pa_idxset *cork_roles;
- bool global:1;
- pa_hook_slot
- *sink_input_put_slot,
- *sink_input_unlink_slot,
- *sink_input_move_start_slot,
- *sink_input_move_finish_slot;
-};
-
-static bool shall_cork(struct userdata *u, pa_sink *s, pa_sink_input *ignore) {
- pa_sink_input *j;
- uint32_t idx, role_idx;
- const char *trigger_role;
-
- pa_assert(u);
- pa_sink_assert_ref(s);
-
- for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- const char *role;
-
- if (j == ignore)
- continue;
-
- if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
- continue;
-
- PA_IDXSET_FOREACH(trigger_role, u->trigger_roles, role_idx) {
- if (pa_streq(role, trigger_role)) {
- pa_log_debug("Found a '%s' stream that will trigger the auto-cork.", trigger_role);
- return true;
- }
- }
- }
-
- return false;
-}
-
-static inline void apply_cork_to_sink(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool cork) {
- pa_sink_input *j;
- uint32_t idx, role_idx;
- const char *cork_role;
- bool trigger = false;
-
- pa_assert(u);
- pa_sink_assert_ref(s);
-
- for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- bool corked, corked_here;
- const char *role;
-
- if (j == ignore)
- continue;
-
- if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
- continue;
-
- PA_IDXSET_FOREACH(cork_role, u->cork_roles, role_idx) {
- if ((trigger = pa_streq(role, cork_role)))
- break;
- }
- if (!trigger)
- continue;
-
- corked = (pa_sink_input_get_state(j) == PA_SINK_INPUT_CORKED);
- corked_here = !!pa_hashmap_get(u->cork_state, j);
-
- if (cork && !corked && !j->muted) {
- pa_log_debug("Found a '%s' stream that should be corked/muted.", cork_role);
- if (!corked_here)
- pa_hashmap_put(u->cork_state, j, PA_INT_TO_PTR(1));
- pa_sink_input_set_mute(j, true, false);
- pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_CORK, NULL);
- } else if (!cork) {
- pa_hashmap_remove(u->cork_state, j);
-
- if (corked_here && (corked || j->muted)) {
- pa_log_debug("Found a '%s' stream that should be uncorked/unmuted.", cork_role);
- if (j->muted)
- pa_sink_input_set_mute(j, false, false);
- if (corked)
- pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_UNCORK, NULL);
- }
- }
- }
-}
-
-static void apply_cork(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool cork) {
- pa_assert(u);
-
- if (u->global) {
- uint32_t idx;
- PA_IDXSET_FOREACH(s, u->core->sinks, idx)
- apply_cork_to_sink(u, s, ignore, cork);
- } else
- apply_cork_to_sink(u, s, ignore, cork);
-}
-
-static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, bool create) {
- bool cork = false;
- const char *role;
-
- pa_assert(u);
- pa_sink_input_assert_ref(i);
-
- if (!create)
- pa_hashmap_remove(u->cork_state, i);
-
- if (!(role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE)))
- return PA_HOOK_OK;
-
- if (!i->sink)
- return PA_HOOK_OK;
-
- cork = shall_cork(u, i->sink, create ? NULL : i);
- apply_cork(u, i->sink, create ? NULL : i, cork);
-
- return PA_HOOK_OK;
-}
-
-static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
- pa_core_assert_ref(core);
- pa_sink_input_assert_ref(i);
-
- return process(u, i, true);
-}
-
-static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
- pa_sink_input_assert_ref(i);
-
- return process(u, i, false);
-}
-
-static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
- pa_core_assert_ref(core);
- pa_sink_input_assert_ref(i);
-
- return process(u, i, false);
-}
-
-static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
- pa_core_assert_ref(core);
- pa_sink_input_assert_ref(i);
-
- return process(u, i, true);
-}
-
int pa__init(pa_module *m) {
- pa_modargs *ma = NULL;
- struct userdata *u;
- const char *roles;
- bool global = false;
pa_assert(m);
- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("Failed to parse module arguments");
- goto fail;
- }
-
- m->userdata = u = pa_xnew(struct userdata, 1);
-
- u->core = m->core;
- u->cork_state = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
-
- u->trigger_roles = pa_idxset_new(NULL, NULL);
- roles = pa_modargs_get_value(ma, "trigger_roles", NULL);
- if (roles) {
- const char *split_state = NULL;
- char *n = NULL;
- while ((n = pa_split(roles, ",", &split_state))) {
- if (n[0] != '\0')
- pa_idxset_put(u->trigger_roles, n, NULL);
- else
- pa_xfree(n);
- }
- }
- if (pa_idxset_isempty(u->trigger_roles)) {
- pa_log_debug("Using role 'phone' as trigger role.");
- pa_idxset_put(u->trigger_roles, pa_xstrdup("phone"), NULL);
- }
-
- u->cork_roles = pa_idxset_new(NULL, NULL);
- roles = pa_modargs_get_value(ma, "cork_roles", NULL);
- if (roles) {
- const char *split_state = NULL;
- char *n = NULL;
- while ((n = pa_split(roles, ",", &split_state))) {
- if (n[0] != '\0')
- pa_idxset_put(u->cork_roles, n, NULL);
- else
- pa_xfree(n);
- }
- }
- if (pa_idxset_isempty(u->cork_roles)) {
- pa_log_debug("Using roles 'music' and 'video' as cork roles.");
- pa_idxset_put(u->cork_roles, pa_xstrdup("music"), NULL);
- pa_idxset_put(u->cork_roles, pa_xstrdup("video"), NULL);
- }
-
- if (pa_modargs_get_value_boolean(ma, "global", &global) < 0) {
- pa_log("Invalid boolean parameter: global");
- goto fail;
- }
- u->global = global;
-
- u->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_cb, u);
- u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u);
- u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_start_cb, u);
- u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u);
-
- pa_modargs_free(ma);
-
- return 0;
-
-fail:
- pa__done(m);
-
- if (ma)
- pa_modargs_free(ma);
-
- return -1;
-
+ return pa_stream_interaction_init(m, valid_modargs);
}
void pa__done(pa_module *m) {
- struct userdata* u;
pa_assert(m);
- if (!(u = m->userdata))
- return;
-
- if (u->trigger_roles)
- pa_idxset_free(u->trigger_roles, pa_xfree);
-
- if (u->cork_roles)
- pa_idxset_free(u->cork_roles, pa_xfree);
-
- if (u->sink_input_put_slot)
- pa_hook_slot_free(u->sink_input_put_slot);
- if (u->sink_input_unlink_slot)
- pa_hook_slot_free(u->sink_input_unlink_slot);
- if (u->sink_input_move_start_slot)
- pa_hook_slot_free(u->sink_input_move_start_slot);
- if (u->sink_input_move_finish_slot)
- pa_hook_slot_free(u->sink_input_move_finish_slot);
-
- if (u->cork_state)
- pa_hashmap_free(u->cork_state);
-
- pa_xfree(u);
-
+ pa_stream_interaction_done(m);
}
diff --git a/src/modules/module-role-ducking.c b/src/modules/module-role-ducking.c
index 4f9be10..add2d36 100644
--- a/src/modules/module-role-ducking.c
+++ b/src/modules/module-role-ducking.c
@@ -21,16 +21,9 @@
#include <config.h>
#endif
-#include <pulse/volume.h>
-#include <pulse/xmalloc.h>
-
#include <pulsecore/macro.h>
-#include <pulsecore/hashmap.h>
-#include <pulsecore/hook-list.h>
#include <pulsecore/core.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/sink-input.h>
-#include <pulsecore/modargs.h>
+#include <stream-interaction.h>
#include "module-role-ducking-symdef.h"
@@ -39,10 +32,10 @@ PA_MODULE_DESCRIPTION("Apply a ducking effect based on streams roles");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(true);
PA_MODULE_USAGE(
- "trigger_roles=<Comma separated list of roles which will trigger a ducking> "
- "ducking_roles=<Comma separated list of roles which will be ducked> "
+ "trigger_roles=<Comma(and slash) separated list of roles which will trigger a ducking. Slash can divide the roles into groups>"
+ "ducking_roles=<Comma(and slash) separated list of roles which will be ducked. Slash can divide the roles into groups>"
"global=<Should we operate globally or only inside the same device?>"
- "volume=<Volume for the attenuated streams. Default: -20dB"
+ "volume=<Volume for the attenuated streams. Default: -20dB. If trigger_roles and ducking_roles are separated by slash, use slash for dividing volume group>"
);
static const char* const valid_modargs[] = {
@@ -53,264 +46,16 @@ static const char* const valid_modargs[] = {
NULL
};
-struct userdata {
- pa_core *core;
- const char *name;
- pa_idxset *trigger_roles;
- pa_idxset *ducking_roles;
- pa_idxset *ducked_inputs;
- bool global;
- pa_volume_t volume;
- pa_hook_slot
- *sink_input_put_slot,
- *sink_input_unlink_slot,
- *sink_input_move_start_slot,
- *sink_input_move_finish_slot;
-};
-
-static bool sink_has_trigger_streams(struct userdata *u, pa_sink *s, pa_sink_input *ignore) {
- pa_sink_input *j;
- uint32_t idx, role_idx;
- const char *trigger_role;
-
- pa_assert(u);
- pa_sink_assert_ref(s);
-
- PA_IDXSET_FOREACH(j, s->inputs, idx) {
- const char *role;
-
- if (j == ignore)
- continue;
-
- if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
- continue;
-
- PA_IDXSET_FOREACH(trigger_role, u->trigger_roles, role_idx) {
- if (pa_streq(role, trigger_role)) {
- pa_log_debug("Found a '%s' stream that will trigger the ducking.", trigger_role);
- return true;
- }
- }
- }
-
- return false;
-}
-
-static void apply_ducking_to_sink(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool duck) {
- pa_sink_input *j;
- uint32_t idx, role_idx;
- const char *ducking_role;
- bool trigger = false;
-
- pa_assert(u);
- pa_sink_assert_ref(s);
-
- PA_IDXSET_FOREACH(j, s->inputs, idx) {
- const char *role;
- pa_sink_input *i;
-
- if (j == ignore)
- continue;
-
- if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
- continue;
-
- PA_IDXSET_FOREACH(ducking_role, u->ducking_roles, role_idx) {
- if ((trigger = pa_streq(role, ducking_role)))
- break;
- }
- if (!trigger)
- continue;
-
- i = pa_idxset_get_by_data(u->ducked_inputs, j, NULL);
- if (duck && !i) {
- pa_cvolume vol;
- vol.channels = 1;
- vol.values[0] = u->volume;
-
- pa_log_debug("Found a '%s' stream that should be ducked.", ducking_role);
- pa_sink_input_add_volume_factor(j, u->name, &vol);
- pa_idxset_put(u->ducked_inputs, j, NULL);
- } else if (!duck && i) { /* This stream should not longer be ducked */
- pa_log_debug("Found a '%s' stream that should be unducked", ducking_role);
- pa_idxset_remove_by_data(u->ducked_inputs, j, NULL);
- pa_sink_input_remove_volume_factor(j, u->name);
- }
- }
-}
-
-static void apply_ducking(struct userdata *u, pa_sink *s, pa_sink_input *ignore, bool duck) {
- pa_assert(u);
-
- if (u->global) {
- uint32_t idx;
- PA_IDXSET_FOREACH(s, u->core->sinks, idx)
- apply_ducking_to_sink(u, s, ignore, duck);
- } else
- apply_ducking_to_sink(u, s, ignore, duck);
-}
-
-static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, bool duck) {
- bool should_duck = false;
- const char *role;
-
- pa_assert(u);
- pa_sink_input_assert_ref(i);
-
- if (!(role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE)))
- return PA_HOOK_OK;
-
- if (!i->sink)
- return PA_HOOK_OK;
-
- should_duck = sink_has_trigger_streams(u, i->sink, duck ? NULL : i);
- apply_ducking(u, i->sink, duck ? NULL : i, should_duck);
-
- return PA_HOOK_OK;
-}
-
-static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
- pa_core_assert_ref(core);
- pa_sink_input_assert_ref(i);
-
- return process(u, i, true);
-}
-
-static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
- pa_sink_input_assert_ref(i);
-
- pa_idxset_remove_by_data(u->ducked_inputs, i, NULL);
- return process(u, i, false);
-}
-
-static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
- pa_core_assert_ref(core);
- pa_sink_input_assert_ref(i);
-
- return process(u, i, false);
-}
-
-static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
- pa_core_assert_ref(core);
- pa_sink_input_assert_ref(i);
-
- return process(u, i, true);
-}
-
int pa__init(pa_module *m) {
- pa_modargs *ma = NULL;
- struct userdata *u;
- const char *roles;
pa_assert(m);
- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("Failed to parse module arguments");
- goto fail;
- }
-
- m->userdata = u = pa_xnew0(struct userdata, 1);
-
- u->core = m->core;
- u->name = m->name;
-
- u->ducked_inputs = pa_idxset_new(NULL, NULL);
-
- u->trigger_roles = pa_idxset_new(NULL, NULL);
- roles = pa_modargs_get_value(ma, "trigger_roles", NULL);
- if (roles) {
- const char *split_state = NULL;
- char *n = NULL;
- while ((n = pa_split(roles, ",", &split_state))) {
- if (n[0] != '\0')
- pa_idxset_put(u->trigger_roles, n, NULL);
- else
- pa_xfree(n);
- }
- }
- if (pa_idxset_isempty(u->trigger_roles)) {
- pa_log_debug("Using role 'phone' as trigger role.");
- pa_idxset_put(u->trigger_roles, pa_xstrdup("phone"), NULL);
- }
-
- u->ducking_roles = pa_idxset_new(NULL, NULL);
- roles = pa_modargs_get_value(ma, "ducking_roles", NULL);
- if (roles) {
- const char *split_state = NULL;
- char *n = NULL;
- while ((n = pa_split(roles, ",", &split_state))) {
- if (n[0] != '\0')
- pa_idxset_put(u->ducking_roles, n, NULL);
- else
- pa_xfree(n);
- }
- }
- if (pa_idxset_isempty(u->ducking_roles)) {
- pa_log_debug("Using roles 'music' and 'video' as ducking roles.");
- pa_idxset_put(u->ducking_roles, pa_xstrdup("music"), NULL);
- pa_idxset_put(u->ducking_roles, pa_xstrdup("video"), NULL);
- }
-
- u->global = false;
- if (pa_modargs_get_value_boolean(ma, "global", &u->global) < 0) {
- pa_log("Failed to parse a boolean parameter: global");
- goto fail;
- }
-
- u->volume = pa_sw_volume_from_dB(-20);
- if (pa_modargs_get_value_volume(ma, "volume", &u->volume) < 0) {
- pa_log("Failed to parse a volume parameter: volume");
- goto fail;
- }
-
- u->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_cb, u);
- u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u);
- u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_start_cb, u);
- u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u);
-
- pa_modargs_free(ma);
-
- return 0;
-
-fail:
- pa__done(m);
-
- if (ma)
- pa_modargs_free(ma);
-
- return -1;
+ return pa_stream_interaction_init(m, valid_modargs);
}
void pa__done(pa_module *m) {
- struct userdata* u;
- pa_sink_input *i;
pa_assert(m);
- if (!(u = m->userdata))
- return;
-
- if (u->trigger_roles)
- pa_idxset_free(u->trigger_roles, pa_xfree);
-
- if (u->ducking_roles)
- pa_idxset_free(u->ducking_roles, pa_xfree);
-
- if (u->ducked_inputs) {
- while ((i = pa_idxset_steal_first(u->ducked_inputs, NULL)))
- pa_sink_input_remove_volume_factor(i, u->name);
-
- pa_idxset_free(u->ducked_inputs, NULL);
- }
-
- if (u->sink_input_put_slot)
- pa_hook_slot_free(u->sink_input_put_slot);
- if (u->sink_input_unlink_slot)
- pa_hook_slot_free(u->sink_input_unlink_slot);
- if (u->sink_input_move_start_slot)
- pa_hook_slot_free(u->sink_input_move_start_slot);
- if (u->sink_input_move_finish_slot)
- pa_hook_slot_free(u->sink_input_move_finish_slot);
-
- pa_xfree(u);
+ pa_stream_interaction_done(m);
}
diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
index c79918a..2fa0bff 100644
--- a/src/modules/module-solaris.c
+++ b/src/modules/module-solaris.c
@@ -412,10 +412,12 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
pa_smoother_resume(u->smoother, pa_rtclock_now(), true);
if (!u->source || u->source_suspended) {
+ bool mute;
if (unsuspend(u) < 0)
return -1;
u->sink->get_volume(u->sink);
- u->sink->get_mute(u->sink);
+ if (u->sink->get_mute(u->sink, &mute) >= 0)
+ pa_sink_set_mute(u->sink, mute, false);
}
u->sink_suspended = false;
}
@@ -1033,8 +1035,12 @@ int pa__init(pa_module *m) {
if (sink_new_data.muted_is_set)
u->sink->set_mute(u->sink);
- else
- u->sink->get_mute(u->sink);
+ else {
+ bool mute;
+
+ if (u->sink->get_mute(u->sink, &mute) >= 0)
+ pa_sink_set_mute(u->sink, mute, false);
+ }
pa_sink_put(u->sink);
}
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index ac51ff3..d1c2597 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -645,7 +645,7 @@ static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userd
e->channel_map = map;
e->volume_valid = !!map.channels;
- device_updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device);
+ device_updated = (e->device_valid != !!device[0]) || !pa_safe_streq(e->device, device);
pa_xfree(e->device);
e->device = pa_xstrdup(device);
e->device_valid = !!device[0];
@@ -757,7 +757,7 @@ static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBus
pa_assert_se(e = entry_read(de->userdata, de->entry_name));
- updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device);
+ updated = (e->device_valid != !!device[0]) || !pa_safe_streq(e->device, device);
if (updated) {
pa_xfree(e->device);
@@ -2341,7 +2341,7 @@ static void clean_up_db(struct userdata *u) {
PA_LLIST_FOREACH_SAFE(item, next, to_be_converted) {
pa_log_debug("Upgrading a legacy entry to the current format: %s", item->entry_name);
- pa_assert_se(entry_write(u, item->entry_name, item->entry, true) >= 0);
+ pa_assert_se(entry_write(u, item->entry_name, item->entry, true));
trigger_save(u);
PA_LLIST_REMOVE(struct clean_up_item, to_be_converted, item);
diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c
index 5dd9786..b9a0f3b 100644
--- a/src/modules/module-switch-on-port-available.c
+++ b/src/modules/module-switch-on-port-available.c
@@ -29,45 +29,97 @@
#include "module-switch-on-port-available-symdef.h"
-static bool profile_good_for_output(pa_card_profile *profile) {
+struct card_info {
+ struct userdata *userdata;
+ pa_card *card;
+
+ /* We need to cache the active profile, because we want to compare the old
+ * and new profiles in the PROFILE_CHANGED hook. Without this we'd only
+ * have access to the new profile. */
+ pa_card_profile *active_profile;
+};
+
+struct userdata {
+ pa_hashmap *card_infos; /* pa_card -> struct card_info */
+};
+
+static void card_info_new(struct userdata *u, pa_card *card) {
+ struct card_info *info;
+
+ info = pa_xnew0(struct card_info, 1);
+ info->userdata = u;
+ info->card = card;
+ info->active_profile = card->active_profile;
+
+ pa_hashmap_put(u->card_infos, card, info);
+}
+
+static void card_info_free(struct card_info *info) {
+ pa_hashmap_remove(info->userdata->card_infos, info->card);
+ pa_xfree(info);
+}
+
+static bool profile_good_for_output(pa_card_profile *profile, pa_device_port *port) {
+ pa_card *card;
pa_sink *sink;
uint32_t idx;
pa_assert(profile);
- if (!pa_safe_streq(profile->card->active_profile->input_name, profile->input_name))
+ card = profile->card;
+
+ if (!pa_safe_streq(card->active_profile->input_name, profile->input_name))
return false;
- if (profile->card->active_profile->n_sources != profile->n_sources)
+ if (card->active_profile->n_sources != profile->n_sources)
return false;
- if (profile->card->active_profile->max_source_channels != profile->max_source_channels)
+ if (card->active_profile->max_source_channels != profile->max_source_channels)
return false;
- /* Try not to switch to HDMI sinks from analog when HDMI is becoming available */
- PA_IDXSET_FOREACH(sink, profile->card->sinks, idx) {
+ if (port == card->preferred_output_port)
+ return true;
+
+ PA_IDXSET_FOREACH(sink, card->sinks, idx) {
if (!sink->active_port)
continue;
- if (sink->active_port->available != PA_AVAILABLE_NO)
+ if ((sink->active_port->available != PA_AVAILABLE_NO) && (sink->active_port->priority >= port->priority))
return false;
}
return true;
}
-static bool profile_good_for_input(pa_card_profile *profile) {
+static bool profile_good_for_input(pa_card_profile *profile, pa_device_port *port) {
+ pa_card *card;
+ pa_source *source;
+ uint32_t idx;
+
pa_assert(profile);
- if (!pa_safe_streq(profile->card->active_profile->output_name, profile->output_name))
+ card = profile->card;
+
+ if (!pa_safe_streq(card->active_profile->output_name, profile->output_name))
return false;
- if (profile->card->active_profile->n_sinks != profile->n_sinks)
+ if (card->active_profile->n_sinks != profile->n_sinks)
return false;
- if (profile->card->active_profile->max_sink_channels != profile->max_sink_channels)
+ if (card->active_profile->max_sink_channels != profile->max_sink_channels)
return false;
+ if (port == card->preferred_input_port)
+ return true;
+
+ PA_IDXSET_FOREACH(source, card->sources, idx) {
+ if (!source->active_port)
+ continue;
+
+ if ((source->active_port->available != PA_AVAILABLE_NO) && (source->active_port->priority >= port->priority))
+ return false;
+ }
+
return true;
}
@@ -88,12 +140,12 @@ static int try_to_switch_profile(pa_device_port *port) {
switch (port->direction) {
case PA_DIRECTION_OUTPUT:
name = profile->output_name;
- good = profile_good_for_output(profile);
+ good = profile_good_for_output(profile, port);
break;
case PA_DIRECTION_INPUT:
name = profile->input_name;
- good = profile_good_for_input(profile);
+ good = profile_good_for_input(profile, port);
break;
}
@@ -182,7 +234,7 @@ static bool switch_to_port(pa_device_port *port) {
pa_log_debug("Trying to switch to port %s", port->name);
if (!pp.is_preferred_profile_active) {
if (try_to_switch_profile(port) < 0) {
- if (pp.is_possible_profile_active)
+ if (!pp.is_possible_profile_active)
return false;
}
else
@@ -307,9 +359,142 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
return PA_HOOK_OK;
}
+static pa_hook_result_t card_put_hook_callback(pa_core *core, pa_card *card, struct userdata *u) {
+ card_info_new(u, card);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_unlink_hook_callback(pa_core *core, pa_card *card, struct userdata *u) {
+ card_info_free(pa_hashmap_get(u->card_infos, card));
+
+ return PA_HOOK_OK;
+}
+
+static void update_preferred_input_port(pa_card *card, pa_card_profile *old_profile, pa_card_profile *new_profile) {
+ pa_source *source;
+
+ /* If the profile change didn't affect input, it doesn't indicate change in
+ * the user's input port preference. */
+ if (pa_safe_streq(old_profile->input_name, new_profile->input_name))
+ return;
+
+ /* If there are more than one source, we don't know which of those the user
+ * prefers. If there are no sources, then the user doesn't seem to care
+ * about input at all. */
+ if (pa_idxset_size(card->sources) != 1) {
+ pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, NULL);
+ return;
+ }
+
+ /* If the profile change modified the set of sinks, then it's unclear
+ * whether the user wanted to activate some specific input port, or was the
+ * input change only a side effect of activating some output. If the new
+ * profile contains no sinks, though, then we know the user only cares
+ * about input. */
+ if (pa_idxset_size(card->sinks) > 0 && !pa_safe_streq(old_profile->output_name, new_profile->output_name)) {
+ pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, NULL);
+ return;
+ }
+
+ source = pa_idxset_first(card->sources, NULL);
+
+ /* We know the user wanted to activate this source. The user might not have
+ * wanted to activate the port that was selected by default, but if that's
+ * the case, the user will change the port manually, and we'll update the
+ * port preference at that time. If no port change occurs, we can assume
+ * that the user likes the port that is now active. */
+ pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, source->active_port);
+}
+
+static void update_preferred_output_port(pa_card *card, pa_card_profile *old_profile, pa_card_profile *new_profile) {
+ pa_sink *sink;
+
+ /* If the profile change didn't affect output, it doesn't indicate change in
+ * the user's output port preference. */
+ if (pa_safe_streq(old_profile->output_name, new_profile->output_name))
+ return;
+
+ /* If there are more than one sink, we don't know which of those the user
+ * prefers. If there are no sinks, then the user doesn't seem to care about
+ * output at all. */
+ if (pa_idxset_size(card->sinks) != 1) {
+ pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, NULL);
+ return;
+ }
+
+ /* If the profile change modified the set of sources, then it's unclear
+ * whether the user wanted to activate some specific output port, or was
+ * the output change only a side effect of activating some input. If the
+ * new profile contains no sources, though, then we know the user only
+ * cares about output. */
+ if (pa_idxset_size(card->sources) > 0 && !pa_safe_streq(old_profile->input_name, new_profile->input_name)) {
+ pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, NULL);
+ return;
+ }
+
+ sink = pa_idxset_first(card->sinks, NULL);
+
+ /* We know the user wanted to activate this sink. The user might not have
+ * wanted to activate the port that was selected by default, but if that's
+ * the case, the user will change the port manually, and we'll update the
+ * port preference at that time. If no port change occurs, we can assume
+ * that the user likes the port that is now active. */
+ pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, sink->active_port);
+}
+
+static pa_hook_result_t card_profile_changed_callback(pa_core *core, pa_card *card, struct userdata *u) {
+ struct card_info *info;
+ pa_card_profile *old_profile;
+ pa_card_profile *new_profile;
+
+ info = pa_hashmap_get(u->card_infos, card);
+ old_profile = info->active_profile;
+ new_profile = card->active_profile;
+ info->active_profile = new_profile;
+
+ /* This profile change wasn't initiated by the user, so it doesn't signal
+ * a change in the user's port preferences. */
+ if (!card->save_profile)
+ return PA_HOOK_OK;
+
+ update_preferred_input_port(card, old_profile, new_profile);
+ update_preferred_output_port(card, old_profile, new_profile);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_port_changed_callback(pa_core *core, pa_source *source, void *userdata) {
+ if (!source->save_port)
+ return PA_HOOK_OK;
+
+ pa_card_set_preferred_port(source->card, PA_DIRECTION_INPUT, source->active_port);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_port_changed_callback(pa_core *core, pa_sink *sink, void *userdata) {
+ if (!sink->save_port)
+ return PA_HOOK_OK;
+
+ pa_card_set_preferred_port(sink->card, PA_DIRECTION_OUTPUT, sink->active_port);
+
+ return PA_HOOK_OK;
+}
+
int pa__init(pa_module*m) {
+ struct userdata *u;
+ pa_card *card;
+ uint32_t idx;
+
pa_assert(m);
+ u = m->userdata = pa_xnew0(struct userdata, 1);
+ u->card_infos = pa_hashmap_new(NULL, NULL);
+
+ PA_IDXSET_FOREACH(card, m->core->cards, idx)
+ card_info_new(u, card);
+
/* Make sure we are after module-device-restore, so we can overwrite that suggestion if necessary */
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_NEW],
PA_HOOK_NORMAL, (pa_hook_cb_t) sink_new_hook_callback, NULL);
@@ -317,8 +502,35 @@ int pa__init(pa_module*m) {
PA_HOOK_NORMAL, (pa_hook_cb_t) source_new_hook_callback, NULL);
pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED],
PA_HOOK_LATE, (pa_hook_cb_t) port_available_hook_callback, NULL);
+ pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PUT],
+ PA_HOOK_NORMAL, (pa_hook_cb_t) card_put_hook_callback, u);
+ pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_UNLINK],
+ PA_HOOK_NORMAL, (pa_hook_cb_t) card_unlink_hook_callback, u);
+ pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED],
+ PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_changed_callback, u);
+ pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED],
+ PA_HOOK_NORMAL, (pa_hook_cb_t) source_port_changed_callback, NULL);
+ pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED],
+ PA_HOOK_NORMAL, (pa_hook_cb_t) sink_port_changed_callback, NULL);
handle_all_unavailable(m->core);
return 0;
}
+
+void pa__done(pa_module *module) {
+ struct userdata *u;
+ struct card_info *info;
+
+ pa_assert(module);
+
+ if (!(u = module->userdata))
+ return;
+
+ while ((info = pa_hashmap_last(u->card_infos)))
+ card_info_free(info);
+
+ pa_hashmap_free(u->card_infos);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index 53c4402..5c8b84a 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -1778,7 +1778,7 @@ static void pstream_die_callback(pa_pstream *p, void *userdata) {
}
/* Called from main context */
-static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_cmsg_ancil_data *ancil_data, void *userdata) {
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
struct userdata *u = userdata;
pa_assert(p);
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index dc42f7c..5977500 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -106,6 +106,7 @@ struct session {
pa_usec_t intended_latency;
pa_usec_t sink_latency;
+ unsigned int base_rate;
pa_usec_t last_rate_update;
pa_usec_t last_latency;
double estimated_rate;
@@ -284,7 +285,6 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
if (s->last_rate_update + RATE_UPDATE_INTERVAL < pa_timeval_load(&now)) {
pa_usec_t wi, ri, render_delay, sink_delay = 0, latency;
- uint32_t base_rate = s->sink_input->sink->sample_spec.rate;
uint32_t current_rate = s->sink_input->sample_spec.rate;
uint32_t new_rate;
double estimated_rate, alpha = 0.02;
@@ -351,12 +351,12 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
new_rate = (uint32_t) ((double) (RATE_UPDATE_INTERVAL + latency/4 - s->intended_latency/4) / (double) RATE_UPDATE_INTERVAL * s->avg_estimated_rate);
s->last_latency = latency;
- if (new_rate < (uint32_t) (base_rate*0.8) || new_rate > (uint32_t) (base_rate*1.25)) {
- pa_log_warn("Sample rates too different, not adjusting (%u vs. %u).", base_rate, new_rate);
- new_rate = base_rate;
+ if (new_rate < (uint32_t) (s->base_rate*0.8) || new_rate > (uint32_t) (s->base_rate*1.25)) {
+ pa_log_warn("Sample rates too different, not adjusting (%u vs. %u).", s->base_rate, new_rate);
+ new_rate = s->base_rate;
} else {
- if (base_rate < new_rate + 20 && new_rate < base_rate + 20)
- new_rate = base_rate;
+ if (s->base_rate < new_rate + 20 && new_rate < s->base_rate + 20)
+ new_rate = s->base_rate;
/* Do the adjustment in small steps; 2‰ can be considered inaudible */
if (new_rate < (uint32_t) (current_rate*0.998) || new_rate > (uint32_t) (current_rate*1.002)) {
pa_log_info("New rate of %u Hz not within 2‰ of %u Hz, forcing smaller adjustment", new_rate, current_rate);
@@ -521,8 +521,6 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
s->intended_latency = u->latency;
s->last_rate_update = pa_timeval_load(&now);
s->last_latency = u->latency;
- s->estimated_rate = (double) sink->sample_spec.rate;
- s->avg_estimated_rate = (double) sink->sample_spec.rate;
pa_atomic_store(&s->timestamp, (int) now.tv_sec);
if ((fd = mcast_socket((const struct sockaddr*) &sdp_info->sa, sdp_info->salen)) < 0)
@@ -554,6 +552,10 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
goto fail;
}
+ s->base_rate = (double) s->sink_input->sample_spec.rate;
+ s->estimated_rate = (double) s->sink_input->sample_spec.rate;
+ s->avg_estimated_rate = (double) s->sink_input->sample_spec.rate;
+
s->sink_input->userdata = s;
s->sink_input->parent.process_msg = sink_input_process_msg;
diff --git a/src/modules/stream-interaction.c b/src/modules/stream-interaction.c
new file mode 100644
index 0000000..7a476c3
--- /dev/null
+++ b/src/modules/stream-interaction.c
@@ -0,0 +1,550 @@
+/***
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/hook-list.h>
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/modargs.h>
+
+#include "stream-interaction.h"
+
+struct group {
+ char *name;
+ pa_idxset *trigger_roles;
+ pa_idxset *interaction_roles;
+ pa_hashmap *interaction_state;
+ pa_volume_t volume;
+};
+
+struct userdata {
+ pa_core *core;
+ uint32_t n_groups;
+ struct group **groups;
+ bool global:1;
+ bool duck:1;
+ pa_hook_slot
+ *sink_input_put_slot,
+ *sink_input_unlink_slot,
+ *sink_input_move_start_slot,
+ *sink_input_move_finish_slot,
+ *sink_input_state_changed_slot,
+ *sink_input_mute_changed_slot,
+ *sink_input_proplist_changed_slot;
+};
+
+static const char *get_trigger_role(struct userdata *u, pa_sink_input *i, struct group *g) {
+ const char *role, *trigger_role;
+ uint32_t role_idx;
+
+ if (!(role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE)))
+ role = "no_role";
+
+ if (g == NULL) {
+ /* get it from all groups */
+ uint32_t j;
+ for (j = 0; j < u->n_groups; j++) {
+ PA_IDXSET_FOREACH(trigger_role, u->groups[j]->trigger_roles, role_idx) {
+ if (pa_streq(role, trigger_role))
+ return trigger_role;
+ }
+ }
+ } else {
+ PA_IDXSET_FOREACH(trigger_role, g->trigger_roles, role_idx) {
+ if (pa_streq(role, trigger_role))
+ return trigger_role;
+ }
+ }
+
+ return NULL;
+}
+
+static const char *find_trigger_stream(struct userdata *u, pa_sink *s, pa_sink_input *ignore, struct group *g) {
+ pa_sink_input *j;
+ uint32_t idx;
+ const char *trigger_role;
+
+ pa_assert(u);
+ pa_sink_assert_ref(s);
+
+ for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
+
+ if (j == ignore)
+ continue;
+
+ trigger_role = get_trigger_role(u, j, g);
+ if (trigger_role && !j->muted && pa_sink_input_get_state(j) != PA_SINK_INPUT_CORKED)
+ return trigger_role;
+ }
+
+ return NULL;
+}
+
+static const char *find_global_trigger_stream(struct userdata *u, pa_sink *s, pa_sink_input *ignore, struct group *g) {
+ const char *trigger_role = NULL;
+
+ pa_assert(u);
+
+ if (u->global) {
+ uint32_t idx;
+ PA_IDXSET_FOREACH(s, u->core->sinks, idx)
+ if ((trigger_role = find_trigger_stream(u, s, ignore, g)))
+ break;
+ } else
+ trigger_role = find_trigger_stream(u, s, ignore, g);
+
+ return trigger_role;
+}
+
+static void cork_or_duck(struct userdata *u, pa_sink_input *i, const char *interaction_role, const char *trigger_role, bool interaction_applied, struct group *g) {
+
+ if (u->duck && !interaction_applied) {
+ pa_cvolume vol;
+ vol.channels = 1;
+ vol.values[0] = g->volume;
+
+ pa_log_debug("Found a '%s' stream of '%s' that ducks a '%s' stream.", trigger_role, g->name, interaction_role);
+ pa_sink_input_add_volume_factor(i, g->name, &vol);
+
+ } else if (!u->duck) {
+ pa_log_debug("Found a '%s' stream that corks/mutes a '%s' stream.", trigger_role, interaction_role);
+ pa_sink_input_set_mute(i, true, false);
+ pa_sink_input_send_event(i, PA_STREAM_EVENT_REQUEST_CORK, NULL);
+ }
+}
+
+static void uncork_or_unduck(struct userdata *u, pa_sink_input *i, const char *interaction_role, bool corked, struct group *g) {
+
+ if (u->duck) {
+ pa_log_debug("In '%s', found a '%s' stream that should be unducked", g->name, interaction_role);
+ pa_sink_input_remove_volume_factor(i, g->name);
+ }
+ else if (corked || i->muted) {
+ pa_log_debug("Found a '%s' stream that should be uncorked/unmuted.", interaction_role);
+ if (i->muted)
+ pa_sink_input_set_mute(i, false, false);
+ if (corked)
+ pa_sink_input_send_event(i, PA_STREAM_EVENT_REQUEST_UNCORK, NULL);
+ }
+}
+
+static inline void apply_interaction_to_sink(struct userdata *u, pa_sink *s, const char *new_trigger, pa_sink_input *ignore, bool new_stream, struct group *g) {
+ pa_sink_input *j;
+ uint32_t idx, role_idx;
+ const char *interaction_role;
+ bool trigger = false;
+
+ pa_assert(u);
+ pa_sink_assert_ref(s);
+
+ for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
+ bool corked, interaction_applied;
+ const char *role;
+
+ if (j == ignore)
+ continue;
+
+ if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
+ role = "no_role";
+
+ PA_IDXSET_FOREACH(interaction_role, g->interaction_roles, role_idx) {
+ if ((trigger = pa_streq(role, interaction_role)))
+ break;
+ if ((trigger = (pa_streq(interaction_role, "any_role") && !get_trigger_role(u, j, g))))
+ break;
+ }
+ if (!trigger)
+ continue;
+
+ /* Some applications start their streams corked, so the stream is uncorked by */
+ /* the application only after sink_input_put() was called. If a new stream turns */
+ /* up, act as if it was not corked. In the case of module-role-cork this will */
+ /* only mute the stream because corking is reverted later by the application */
+ corked = (pa_sink_input_get_state(j) == PA_SINK_INPUT_CORKED);
+ if (new_stream && corked)
+ corked = false;
+ interaction_applied = !!pa_hashmap_get(g->interaction_state, j);
+
+ if (new_trigger && ((!corked && !j->muted) || u->duck)) {
+ if (!interaction_applied)
+ pa_hashmap_put(g->interaction_state, j, PA_INT_TO_PTR(1));
+
+ cork_or_duck(u, j, role, new_trigger, interaction_applied, g);
+
+ } else if (!new_trigger && interaction_applied) {
+ pa_hashmap_remove(g->interaction_state, j);
+
+ uncork_or_unduck(u, j, role, corked, g);
+ }
+ }
+}
+
+static void apply_interaction(struct userdata *u, pa_sink *s, const char *trigger_role, pa_sink_input *ignore, bool new_stream, struct group *g) {
+ pa_assert(u);
+
+ if (u->global) {
+ uint32_t idx;
+ PA_IDXSET_FOREACH(s, u->core->sinks, idx)
+ apply_interaction_to_sink(u, s, trigger_role, ignore, new_stream, g);
+ } else
+ apply_interaction_to_sink(u, s, trigger_role, ignore, new_stream, g);
+}
+
+static void remove_interactions(struct userdata *u, struct group *g) {
+ uint32_t idx, idx_input;
+ pa_sink *s;
+ pa_sink_input *j;
+ bool corked;
+ const char *role;
+
+ PA_IDXSET_FOREACH(s, u->core->sinks, idx) {
+
+ for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx_input)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx_input))) {
+
+ if(!!pa_hashmap_get(g->interaction_state, j)) {
+ corked = (pa_sink_input_get_state(j) == PA_SINK_INPUT_CORKED);
+ if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
+ role = "no_role";
+ uncork_or_unduck(u, j, role, corked, g);
+ }
+ }
+ }
+}
+
+static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, bool create, bool new_stream) {
+ const char *trigger_role;
+ uint32_t j;
+
+ pa_assert(u);
+ pa_sink_input_assert_ref(i);
+
+ if (!create)
+ for (j = 0; j < u->n_groups; j++)
+ pa_hashmap_remove(u->groups[j]->interaction_state, i);
+
+ if (!i->sink)
+ return PA_HOOK_OK;
+
+ for (j = 0; j < u->n_groups; j++) {
+ trigger_role = find_global_trigger_stream(u, i->sink, create ? NULL : i, u->groups[j]);
+ apply_interaction(u, i->sink, trigger_role, create ? NULL : i, new_stream, u->groups[j]);
+ }
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+ pa_core_assert_ref(core);
+ pa_sink_input_assert_ref(i);
+
+ return process(u, i, true, true);
+}
+
+static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+ pa_sink_input_assert_ref(i);
+
+ return process(u, i, false, false);
+}
+
+static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+ pa_core_assert_ref(core);
+ pa_sink_input_assert_ref(i);
+
+ return process(u, i, false, false);
+}
+
+static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+ pa_core_assert_ref(core);
+ pa_sink_input_assert_ref(i);
+
+ return process(u, i, true, false);
+}
+
+static pa_hook_result_t sink_input_state_changed_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+ pa_core_assert_ref(core);
+ pa_sink_input_assert_ref(i);
+
+ if (PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(i)) && get_trigger_role(u, i, NULL))
+ return process(u, i, true, false);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_mute_changed_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+ pa_core_assert_ref(core);
+ pa_sink_input_assert_ref(i);
+
+ if (PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(i)) && get_trigger_role(u, i, NULL))
+ return process(u, i, true, false);
+
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_proplist_changed_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+ pa_core_assert_ref(core);
+ pa_sink_input_assert_ref(i);
+
+ if (PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(i)))
+ return process(u, i, true, false);
+
+ return PA_HOOK_OK;
+}
+
+int pa_stream_interaction_init(pa_module *m, const char* const v_modargs[]) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ const char *roles;
+ bool global = false;
+ uint32_t i = 0;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, v_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+
+ u->core = m->core;
+
+ u->duck = false;
+ if (pa_streq(m->name, "module-role-ducking"))
+ u->duck = true;
+
+ u->n_groups = 1;
+
+ if (u->duck) {
+ const char *volumes;
+ uint32_t group_count_tr = 0;
+ uint32_t group_count_du = 0;
+ uint32_t group_count_vol = 0;
+
+ roles = pa_modargs_get_value(ma, "trigger_roles", NULL);
+ if (roles) {
+ const char *split_state = NULL;
+ char *n = NULL;
+ while ((n = pa_split(roles, "/", &split_state))) {
+ group_count_tr++;
+ pa_xfree(n);
+ }
+ }
+ roles = pa_modargs_get_value(ma, "ducking_roles", NULL);
+ if (roles) {
+ const char *split_state = NULL;
+ char *n = NULL;
+ while ((n = pa_split(roles, "/", &split_state))) {
+ group_count_du++;
+ pa_xfree(n);
+ }
+ }
+ volumes = pa_modargs_get_value(ma, "volume", NULL);
+ if (volumes) {
+ const char *split_state = NULL;
+ char *n = NULL;
+ while ((n = pa_split(volumes, "/", &split_state))) {
+ group_count_vol++;
+ pa_xfree(n);
+ }
+ }
+
+ if ((group_count_tr > 1 || group_count_du > 1 || group_count_vol > 1) &&
+ ((group_count_tr != group_count_du) || (group_count_tr != group_count_vol))) {
+ pa_log("Invalid number of groups");
+ goto fail;
+ }
+
+ if (group_count_tr > 0)
+ u->n_groups = group_count_tr;
+ }
+
+ u->groups = pa_xnew0(struct group*, u->n_groups);
+ for (i = 0; i < u->n_groups; i++) {
+ u->groups[i] = pa_xnew0(struct group, 1);
+ u->groups[i]->trigger_roles = pa_idxset_new(NULL, NULL);
+ u->groups[i]->interaction_roles = pa_idxset_new(NULL, NULL);
+ u->groups[i]->interaction_state = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+ if (u->duck)
+ u->groups[i]->name = pa_sprintf_malloc("ducking_group_%u", i);
+ }
+
+ roles = pa_modargs_get_value(ma, "trigger_roles", NULL);
+ if (roles) {
+ const char *group_split_state = NULL;
+ char *roles_in_group = NULL;
+ i = 0;
+ while ((roles_in_group = pa_split(roles, "/", &group_split_state))) {
+ if (roles_in_group[0] != '\0') {
+ const char *split_state = NULL;
+ char *n = NULL;
+ while ((n = pa_split(roles_in_group, ",", &split_state))) {
+ if (n[0] != '\0')
+ pa_idxset_put(u->groups[i]->trigger_roles, n, NULL);
+ else {
+ pa_log("empty trigger role");
+ pa_xfree(n);
+ goto fail;
+ }
+ }
+ i++;
+ } else {
+ pa_log("empty trigger roles");
+ pa_xfree(roles_in_group);
+ goto fail;
+ }
+ }
+ }
+ if (pa_idxset_isempty(u->groups[0]->trigger_roles)) {
+ pa_log_debug("Using role 'phone' as trigger role.");
+ pa_idxset_put(u->groups[0]->trigger_roles, pa_xstrdup("phone"), NULL);
+ }
+
+ roles = pa_modargs_get_value(ma, u->duck ? "ducking_roles" : "cork_roles", NULL);
+ if (roles) {
+ const char *group_split_state = NULL;
+ char *roles_in_group = NULL;
+ i = 0;
+ while ((roles_in_group = pa_split(roles, "/", &group_split_state))) {
+ if (roles_in_group[0] != '\0') {
+ const char *split_state = NULL;
+ char *n = NULL;
+ while ((n = pa_split(roles_in_group, ",", &split_state))) {
+ if (n[0] != '\0')
+ pa_idxset_put(u->groups[i]->interaction_roles, n, NULL);
+ else {
+ pa_log("empty ducking role");
+ pa_xfree(n);
+ goto fail;
+ }
+ }
+ i++;
+ } else {
+ pa_log("empty ducking roles");
+ pa_xfree(roles_in_group);
+ goto fail;
+ }
+ }
+ }
+ if (pa_idxset_isempty(u->groups[0]->interaction_roles)) {
+ pa_log_debug("Using roles 'music' and 'video' as %s roles.", u->duck ? "ducking" : "cork");
+ pa_idxset_put(u->groups[0]->interaction_roles, pa_xstrdup("music"), NULL);
+ pa_idxset_put(u->groups[0]->interaction_roles, pa_xstrdup("video"), NULL);
+ }
+
+ if (u->duck) {
+ const char *volumes;
+ u->groups[0]->volume = pa_sw_volume_from_dB(-20);
+ if ((volumes = pa_modargs_get_value(ma, "volume", NULL))) {
+ const char *group_split_state = NULL;
+ char *n = NULL;
+ i = 0;
+ while ((n = pa_split(volumes, "/", &group_split_state))) {
+ if (n[0] != '\0') {
+ if (pa_parse_volume(n, &(u->groups[i++]->volume)) < 0) {
+ pa_log("Failed to parse volume");
+ pa_xfree(n);
+ goto fail;
+ }
+ } else {
+ pa_log("empty volume");
+ pa_xfree(n);
+ goto fail;
+ }
+ pa_xfree(n);
+ }
+ }
+ }
+
+ if (pa_modargs_get_value_boolean(ma, "global", &global) < 0) {
+ pa_log("Invalid boolean parameter: global");
+ goto fail;
+ }
+ u->global = global;
+
+ u->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_cb, u);
+ u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u);
+ u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_start_cb, u);
+ u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u);
+ u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_state_changed_cb, u);
+ u->sink_input_mute_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_mute_changed_cb, u);
+ u->sink_input_proplist_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_proplist_changed_cb, u);
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+ pa_stream_interaction_done(m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+
+}
+
+void pa_stream_interaction_done(pa_module *m) {
+ struct userdata* u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->groups) {
+ uint32_t j;
+ for (j = 0; j < u->n_groups; j++) {
+ remove_interactions(u, u->groups[j]);
+ pa_idxset_free(u->groups[j]->trigger_roles, pa_xfree);
+ pa_idxset_free(u->groups[j]->interaction_roles, pa_xfree);
+ pa_hashmap_free(u->groups[j]->interaction_state);
+ if (u->duck)
+ pa_xfree(u->groups[j]->name);
+ pa_xfree(u->groups[j]);
+ }
+ pa_xfree(u->groups);
+ }
+
+ if (u->sink_input_put_slot)
+ pa_hook_slot_free(u->sink_input_put_slot);
+ if (u->sink_input_unlink_slot)
+ pa_hook_slot_free(u->sink_input_unlink_slot);
+ if (u->sink_input_move_start_slot)
+ pa_hook_slot_free(u->sink_input_move_start_slot);
+ if (u->sink_input_move_finish_slot)
+ pa_hook_slot_free(u->sink_input_move_finish_slot);
+ if (u->sink_input_state_changed_slot)
+ pa_hook_slot_free(u->sink_input_state_changed_slot);
+ if (u->sink_input_mute_changed_slot)
+ pa_hook_slot_free(u->sink_input_mute_changed_slot);
+ if (u->sink_input_proplist_changed_slot)
+ pa_hook_slot_free(u->sink_input_proplist_changed_slot);
+
+ pa_xfree(u);
+
+}
diff --git a/src/modules/stream-interaction.h b/src/modules/stream-interaction.h
new file mode 100644
index 0000000..d5bc626
--- /dev/null
+++ b/src/modules/stream-interaction.h
@@ -0,0 +1,21 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2015 Georg Chini
+
+ 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, see <http://www.gnu.org/licenses/>.
+***/
+
+int pa_stream_interaction_init(pa_module *m, const char* const v_modargs[]);
+void pa_stream_interaction_done(pa_module *m);
diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c
index c23aa6b..a3c9486 100644
--- a/src/pulse/client-conf.c
+++ b/src/pulse/client-conf.c
@@ -141,6 +141,7 @@ void pa_client_conf_load(pa_client_conf *c, bool load_from_x11, bool load_from_e
{ "cookie-file", pa_config_parse_string, &c->cookie_file_from_client_conf, NULL },
{ "disable-shm", pa_config_parse_bool, &c->disable_shm, NULL },
{ "enable-shm", pa_config_parse_not_bool, &c->disable_shm, NULL },
+ { "enable-memfd", pa_config_parse_not_bool, &c->disable_memfd, NULL },
{ "shm-size-bytes", pa_config_parse_size, &c->shm_size, NULL },
{ "auto-connect-localhost", pa_config_parse_bool, &c->auto_connect_localhost, NULL },
{ "auto-connect-display", pa_config_parse_bool, &c->auto_connect_display, NULL },
diff --git a/src/pulse/client-conf.h b/src/pulse/client-conf.h
index eac705a..7691ec7 100644
--- a/src/pulse/client-conf.h
+++ b/src/pulse/client-conf.h
@@ -37,7 +37,7 @@ typedef struct pa_client_conf {
bool cookie_from_x11_valid;
char *cookie_file_from_application;
char *cookie_file_from_client_conf;
- bool autospawn, disable_shm, auto_connect_localhost, auto_connect_display;
+ bool autospawn, disable_shm, disable_memfd, auto_connect_localhost, auto_connect_display;
size_t shm_size;
} pa_client_conf;
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 4f084e8..69be5f4 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -69,6 +69,7 @@
void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void pa_command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void pa_command_disable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void pa_command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_REQUEST] = pa_command_request,
@@ -90,6 +91,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = pa_command_stream_buffer_attr,
[PA_COMMAND_ENABLE_SRBCHANNEL] = pa_command_enable_srbchannel,
[PA_COMMAND_DISABLE_SRBCHANNEL] = pa_command_disable_srbchannel,
+ [PA_COMMAND_REGISTER_MEMFD_SHMID] = pa_command_register_memfd_shmid,
};
static void context_free(pa_context *c);
@@ -125,6 +127,7 @@ static void reset_callbacks(pa_context *c) {
pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
pa_context *c;
+ pa_mem_type_t type;
pa_assert(mainloop);
@@ -170,10 +173,18 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
c->srb_template.readfd = -1;
c->srb_template.writefd = -1;
- if (!(c->mempool = pa_mempool_new(!c->conf->disable_shm, c->conf->shm_size))) {
+ c->memfd_on_local = (!c->conf->disable_memfd && pa_memfd_is_locally_supported());
- if (!c->conf->disable_shm)
- c->mempool = pa_mempool_new(false, c->conf->shm_size);
+ type = (c->conf->disable_shm) ? PA_MEM_TYPE_PRIVATE :
+ ((!c->memfd_on_local) ?
+ PA_MEM_TYPE_SHARED_POSIX : PA_MEM_TYPE_SHARED_MEMFD);
+
+ if (!(c->mempool = pa_mempool_new(type, c->conf->shm_size, true))) {
+
+ if (!c->conf->disable_shm) {
+ pa_log_warn("Failed to allocate shared memory pool. Falling back to a normal private one.");
+ c->mempool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, c->conf->shm_size, true);
+ }
if (!c->mempool) {
context_free(c);
@@ -249,7 +260,7 @@ static void context_free(pa_context *c) {
pa_hashmap_free(c->playback_streams);
if (c->mempool)
- pa_mempool_free(c->mempool);
+ pa_mempool_unref(c->mempool);
if (c->conf)
pa_client_conf_free(c->conf);
@@ -326,7 +337,7 @@ static void pstream_die_callback(pa_pstream *p, void *userdata) {
pa_context_fail(c, PA_ERR_CONNECTIONTERMINATED);
}
-static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_cmsg_ancil_data *ancil_data, void *userdata) {
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
pa_context *c = userdata;
pa_assert(p);
@@ -476,6 +487,7 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
case PA_CONTEXT_AUTHORIZING: {
pa_tagstruct *reply;
bool shm_on_remote = false;
+ bool memfd_on_remote = false;
if (pa_tagstruct_getu32(t, &c->version) < 0 ||
!pa_tagstruct_eof(t)) {
@@ -494,7 +506,15 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
not. */
if (c->version >= 13) {
shm_on_remote = !!(c->version & 0x80000000U);
- c->version &= 0x7FFFFFFFU;
+
+ /* Starting with protocol version 31, the second MSB of the version
+ * tag reflects whether memfd is supported on the other PA end. */
+ if (c->version >= 31)
+ memfd_on_remote = !!(c->version & 0x40000000U);
+
+ /* Reserve the two most-significant _bytes_ of the version tag
+ * for flags. */
+ c->version &= 0x0000FFFFU;
}
pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
@@ -520,6 +540,26 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
pa_log_debug("Negotiated SHM: %s", pa_yes_no(c->do_shm));
pa_pstream_enable_shm(c->pstream, c->do_shm);
+ c->shm_type = PA_MEM_TYPE_PRIVATE;
+ if (c->do_shm) {
+ if (c->version >= 31 && memfd_on_remote && c->memfd_on_local) {
+ const char *reason;
+
+ pa_pstream_enable_memfd(c->pstream);
+ if (pa_mempool_is_memfd_backed(c->mempool))
+ if (pa_pstream_register_memfd_mempool(c->pstream, c->mempool, &reason))
+ pa_log("Failed to regester memfd mempool. Reason: %s", reason);
+
+ /* Even if memfd pool registration fails, the negotiated SHM type
+ * shall remain memfd as both endpoints claim to support it. */
+ c->shm_type = PA_MEM_TYPE_SHARED_MEMFD;
+ } else
+ c->shm_type = PA_MEM_TYPE_SHARED_POSIX;
+ }
+
+ pa_log_debug("Memfd possible: %s", pa_yes_no(c->memfd_on_local));
+ pa_log_debug("Negotiated SHM type: %s", pa_mem_type_to_string(c->shm_type));
+
reply = pa_tagstruct_command(c, PA_COMMAND_SET_CLIENT_NAME, &tag);
if (c->version >= 13) {
@@ -587,8 +627,10 @@ static void setup_context(pa_context *c, pa_iochannel *io) {
pa_log_debug("SHM possible: %s", pa_yes_no(c->do_shm));
/* Starting with protocol version 13 we use the MSB of the version
- * tag for informing the other side if we could do SHM or not */
- pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION | (c->do_shm ? 0x80000000U : 0));
+ * tag for informing the other side if we could do SHM or not.
+ * Starting from version 31, second MSB is used to flag memfd support. */
+ pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION | (c->do_shm ? 0x80000000U : 0) |
+ (c->memfd_on_local ? 0x40000000 : 0));
pa_tagstruct_put_arbitrary(t, cookie, sizeof(cookie));
#ifdef HAVE_CREDS
@@ -1428,8 +1470,7 @@ static void pa_command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uin
pa_context *c = userdata;
#ifdef HAVE_CREDS
- const int *fds;
- int nfd;
+ pa_cmsg_ancil_data *ancil = NULL;
pa_assert(pd);
pa_assert(command == PA_COMMAND_ENABLE_SRBCHANNEL);
@@ -1437,26 +1478,34 @@ static void pa_command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uin
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
+ ancil = pa_pdispatch_take_ancil_data(pd);
+ if (!ancil)
+ goto fail;
+
/* Currently only one srb channel is supported, might change in future versions */
- if (c->srb_template.readfd != -1) {
- pa_context_fail(c, PA_ERR_PROTOCOL);
- return;
- }
+ if (c->srb_template.readfd != -1)
+ goto fail;
- fds = pa_pdispatch_fds(pd, &nfd);
- if (nfd != 2 || !fds || fds[0] == -1 || fds[1] == -1) {
- pa_context_fail(c, PA_ERR_PROTOCOL);
- return;
- }
+ if (ancil->nfd != 2 || ancil->fds[0] == -1 || ancil->fds[1] == -1)
+ goto fail;
pa_context_ref(c);
- c->srb_template.readfd = fds[0];
- c->srb_template.writefd = fds[1];
+ c->srb_template.readfd = ancil->fds[0];
+ c->srb_template.writefd = ancil->fds[1];
c->srb_setup_tag = tag;
pa_context_unref(c);
+ ancil->close_fds_on_cleanup = false;
+ return;
+
+fail:
+ if (ancil)
+ pa_cmsg_ancil_data_close_fds(ancil);
+
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ return;
#else
pa_assert(c);
pa_context_fail(c, PA_ERR_PROTOCOL);
@@ -1489,6 +1538,18 @@ static void pa_command_disable_srbchannel(pa_pdispatch *pd, uint32_t command, ui
pa_pstream_send_tagstruct(c->pstream, t2);
}
+static void pa_command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_REGISTER_MEMFD_SHMID);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ if (pa_common_command_register_memfd_shmid(c->pstream, pd, c->version, command, t))
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+}
void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_context *c = userdata;
diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c
index 268894a..1ce3cd3 100644
--- a/src/pulse/glib-mainloop.c
+++ b/src/pulse/glib-mainloop.c
@@ -595,7 +595,7 @@ static const pa_mainloop_api vtable = {
.io_new = glib_io_new,
.io_enable = glib_io_enable,
.io_free = glib_io_free,
- .io_set_destroy= glib_io_set_destroy,
+ .io_set_destroy = glib_io_set_destroy,
.time_new = glib_time_new,
.time_restart = glib_time_restart,
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index eefd181..9bbb903 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -88,6 +88,7 @@ struct pa_context {
bool is_local:1;
bool do_shm:1;
+ bool memfd_on_local:1;
bool server_specified:1;
bool no_fail:1;
bool do_autospawn:1;
@@ -95,6 +96,8 @@ struct pa_context {
bool filter_added:1;
pa_spawn_api spawn_api;
+ pa_mem_type_t shm_type;
+
pa_strlist *server_list;
char *server;
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index 7cf50d6..4299eec 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -128,7 +128,7 @@ PA_C_DECL_BEGIN
#define PA_CHANNELS_MAX 32U
/** Maximum allowed sample rate */
-#define PA_RATE_MAX (48000U*4U)
+#define PA_RATE_MAX (48000U*8U)
/** Sample format */
typedef enum pa_sample_format {
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index ab233d5..5dfdee1 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -547,9 +547,9 @@ int pa_stream_cancel_write(
int pa_stream_write(
pa_stream *p /**< The stream to use */,
const void *data /**< The data to write */,
- size_t nbytes /**< The length of the data to write in bytes */,
+ size_t nbytes /**< The length of the data to write in bytes, must be in multiples of the stream's sample spec frame size */,
pa_free_cb_t free_cb /**< A cleanup routine for the data or NULL to request an internal copy */,
- int64_t offset, /**< Offset for seeking, must be 0 for upload streams */
+ int64_t offset /**< Offset for seeking, must be 0 for upload streams, must be in multiples of the stream's sample spec frame size */,
pa_seek_mode_t seek /**< Seek mode, must be PA_SEEK_RELATIVE for upload streams */);
/** Function does exactly the same as pa_stream_write() with the difference
diff --git a/src/pulse/version.h b/src/pulse/version.h
index c59c550..f717c5e 100644
--- a/src/pulse/version.h
+++ b/src/pulse/version.h
@@ -33,7 +33,7 @@ PA_C_DECL_BEGIN
/** Return the version of the header files. Keep in mind that this is
a macro and not a function, so it is impossible to get the pointer of
it. */
-#define pa_get_headers_version() ("8.0.0")
+#define pa_get_headers_version() ("8.99.0")
/** Return the version of the library the current application is
* linked to. */
@@ -47,13 +47,13 @@ const char* pa_get_library_version(void);
/** The current protocol version. Version 8 relates to Polypaudio
* 0.8/PulseAudio 0.9. */
-#define PA_PROTOCOL_VERSION 30
+#define PA_PROTOCOL_VERSION 31
/** The major version of PA. \since 0.9.15 */
#define PA_MAJOR 8
/** The minor version of PA. \since 0.9.15 */
-#define PA_MINOR 0
+#define PA_MINOR 99
/** The micro version of PA (will always be 0 from v1.0 onwards). \since 0.9.15 */
#define PA_MICRO 0
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index b6cbbf7..410746b 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -103,6 +103,15 @@ void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile) {
data->active_profile = pa_xstrdup(profile);
}
+void pa_card_new_data_set_preferred_port(pa_card_new_data *data, pa_direction_t direction, pa_device_port *port) {
+ pa_assert(data);
+
+ if (direction == PA_DIRECTION_INPUT)
+ data->preferred_input_port = port;
+ else
+ data->preferred_output_port = port;
+}
+
void pa_card_new_data_done(pa_card_new_data *data) {
pa_assert(data);
@@ -169,6 +178,9 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
PA_HASHMAP_FOREACH(port, c->ports, state)
port->card = c;
+ c->preferred_input_port = data->preferred_input_port;
+ c->preferred_output_port = data->preferred_output_port;
+
if (data->active_profile)
if ((c->active_profile = pa_hashmap_get(c->profiles, data->active_profile)))
c->save_profile = data->save_profile;
@@ -309,15 +321,63 @@ int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save) {
return 0;
}
+void pa_card_set_preferred_port(pa_card *c, pa_direction_t direction, pa_device_port *port) {
+ pa_device_port *old_port;
+ const char *old_port_str;
+ const char *new_port_str;
+ pa_card_preferred_port_changed_hook_data data;
+
+ pa_assert(c);
+
+ if (direction == PA_DIRECTION_INPUT) {
+ old_port = c->preferred_input_port;
+ old_port_str = c->preferred_input_port ? c->preferred_input_port->name : "(unset)";
+ } else {
+ old_port = c->preferred_output_port;
+ old_port_str = c->preferred_output_port ? c->preferred_output_port->name : "(unset)";
+ }
+
+ if (port == old_port)
+ return;
+
+ new_port_str = port ? port->name : "(unset)";
+
+ if (direction == PA_DIRECTION_INPUT) {
+ c->preferred_input_port = port;
+ pa_log_debug("%s: preferred_input_port: %s -> %s", c->name, old_port_str, new_port_str);
+ } else {
+ c->preferred_output_port = port;
+ pa_log_debug("%s: preferred_output_port: %s -> %s", c->name, old_port_str, new_port_str);
+ }
+
+ data.card = c;
+ data.direction = direction;
+ pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED], &data);
+}
+
int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause) {
pa_sink *sink;
pa_source *source;
+ pa_suspend_cause_t suspend_cause;
uint32_t idx;
int ret = 0;
pa_assert(c);
pa_assert(cause != 0);
+ suspend_cause = c->suspend_cause;
+
+ if (suspend)
+ suspend_cause |= cause;
+ else
+ suspend_cause &= ~cause;
+
+ if (c->suspend_cause != suspend_cause) {
+ pa_log_debug("Card suspend causes/state changed");
+ c->suspend_cause = suspend_cause;
+ pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_SUSPEND_CHANGED], c);
+ }
+
PA_IDXSET_FOREACH(sink, c->sinks, idx) {
int r;
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
index 30bfc0e..d4970e3 100644
--- a/src/pulsecore/card.h
+++ b/src/pulsecore/card.h
@@ -79,9 +79,13 @@ struct pa_card {
pa_card_profile *active_profile;
pa_hashmap *ports;
+ pa_device_port *preferred_input_port;
+ pa_device_port *preferred_output_port;
bool save_profile:1;
+ pa_suspend_cause_t suspend_cause;
+
void *userdata;
int (*set_profile)(pa_card *c, pa_card_profile *profile);
@@ -98,12 +102,19 @@ typedef struct pa_card_new_data {
char *active_profile;
pa_hashmap *ports;
+ pa_device_port *preferred_input_port;
+ pa_device_port *preferred_output_port;
bool namereg_fail:1;
bool save_profile:1;
} pa_card_new_data;
+typedef struct {
+ pa_card *card;
+ pa_direction_t direction;
+} pa_card_preferred_port_changed_hook_data;
+
pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra);
void pa_card_profile_free(pa_card_profile *c);
@@ -113,6 +124,7 @@ void pa_card_profile_set_available(pa_card_profile *c, pa_available_t available)
pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data);
void pa_card_new_data_set_name(pa_card_new_data *data, const char *name);
void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile);
+void pa_card_new_data_set_preferred_port(pa_card_new_data *data, pa_direction_t direction, pa_device_port *port);
void pa_card_new_data_done(pa_card_new_data *data);
pa_card *pa_card_new(pa_core *c, pa_card_new_data *data);
@@ -122,6 +134,8 @@ void pa_card_add_profile(pa_card *c, pa_card_profile *profile);
int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save);
+void pa_card_set_preferred_port(pa_card *c, pa_direction_t direction, pa_device_port *port);
+
int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause);
#endif
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index 19c89a9..b6eb85a 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2535,8 +2535,10 @@ char *pa_getcwd(void) {
if (getcwd(p, l))
return p;
- if (errno != ERANGE)
+ if (errno != ERANGE) {
+ pa_xfree(p);
return NULL;
+ }
pa_xfree(p);
l *= 2;
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 43fd30e..6d102f5 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -61,22 +61,25 @@ static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t o
static void core_free(pa_object *o);
-pa_core* pa_core_new(pa_mainloop_api *m, bool shared, size_t shm_size) {
+pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t shm_size) {
pa_core* c;
pa_mempool *pool;
+ pa_mem_type_t type;
int j;
pa_assert(m);
if (shared) {
- if (!(pool = pa_mempool_new(shared, shm_size))) {
- pa_log_warn("Failed to allocate shared memory pool. Falling back to a normal memory pool.");
+ type = (enable_memfd) ? PA_MEM_TYPE_SHARED_MEMFD : PA_MEM_TYPE_SHARED_POSIX;
+ if (!(pool = pa_mempool_new(type, shm_size, false))) {
+ pa_log_warn("Failed to allocate %s memory pool. Falling back to a normal memory pool.",
+ pa_mem_type_to_string(type));
shared = false;
}
}
if (!shared) {
- if (!(pool = pa_mempool_new(shared, shm_size))) {
+ if (!(pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, shm_size, false))) {
pa_log("pa_mempool_new() failed.");
return NULL;
}
@@ -123,13 +126,9 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, size_t shm_size) {
c->subscription_event_last = NULL;
c->mempool = pool;
+ c->shm_size = shm_size;
pa_silence_cache_init(&c->silence_cache);
- if (shared && !(c->rw_mempool = pa_mempool_new(shared, shm_size)))
- pa_log_warn("Failed to allocate shared writable memory pool.");
- if (c->rw_mempool)
- pa_mempool_set_is_remote_writable(c->rw_mempool, true);
-
c->exit_event = NULL;
c->scache_auto_unload_event = NULL;
@@ -216,9 +215,7 @@ static void core_free(pa_object *o) {
pa_assert(!c->default_sink);
pa_silence_cache_done(&c->silence_cache);
- if (c->rw_mempool)
- pa_mempool_free(c->rw_mempool);
- pa_mempool_free(c->mempool);
+ pa_mempool_unref(c->mempool);
for (j = 0; j < PA_CORE_HOOK_MAX; j++)
pa_hook_done(&c->hooks[j]);
@@ -283,9 +280,6 @@ void pa_core_maybe_vacuum(pa_core *c) {
}
pa_mempool_vacuum(c->mempool);
-
- if (c->rw_mempool)
- pa_mempool_vacuum(c->rw_mempool);
}
pa_time_event* pa_core_rttime_new(pa_core *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) {
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index aefc1eb..00d7f2f 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -120,9 +120,11 @@ typedef enum pa_core_hook {
PA_CORE_HOOK_CARD_NEW,
PA_CORE_HOOK_CARD_PUT,
PA_CORE_HOOK_CARD_UNLINK,
+ PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED,
PA_CORE_HOOK_CARD_PROFILE_CHANGED,
PA_CORE_HOOK_CARD_PROFILE_ADDED,
PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED,
+ PA_CORE_HOOK_CARD_SUSPEND_CHANGED,
PA_CORE_HOOK_PORT_AVAILABLE_CHANGED,
PA_CORE_HOOK_PORT_LATENCY_OFFSET_CHANGED,
PA_CORE_HOOK_DEFAULT_SINK_CHANGED,
@@ -177,10 +179,13 @@ struct pa_core {
PA_LLIST_HEAD(pa_subscription_event, subscription_event_queue);
pa_subscription_event *subscription_event_last;
- /* The mempool is used for data we write to, it's readonly for the client.
- The rw_mempool is used for data writable by both server and client (and
- can be NULL in some cases). */
- pa_mempool *mempool, *rw_mempool;
+ /* The mempool is used for data we write to, it's readonly for the client. */
+ pa_mempool *mempool;
+
+ /* Shared memory size, as specified either by daemon configuration
+ * or PA daemon defaults (~ 64 MiB). */
+ size_t shm_size;
+
pa_silence_cache silence_cache;
pa_time_event *exit_event;
@@ -215,7 +220,7 @@ enum {
PA_CORE_MESSAGE_MAX
};
-pa_core* pa_core_new(pa_mainloop_api *m, bool shared, size_t shm_size);
+pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t shm_size);
/* Check whether no one is connected to this core */
void pa_core_check_idle(pa_core *c);
diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h
index 64d8387..9fdbb4f 100644
--- a/src/pulsecore/creds.h
+++ b/src/pulsecore/creds.h
@@ -49,9 +49,14 @@ struct pa_cmsg_ancil_data {
pa_creds creds;
bool creds_valid;
int nfd;
+
+ /* Don't close these fds by your own. Check pa_cmsg_ancil_data_close_fds() */
int fds[MAX_ANCIL_DATA_FDS];
+ bool close_fds_on_cleanup;
};
+void pa_cmsg_ancil_data_close_fds(struct pa_cmsg_ancil_data *ancil);
+
#else
#undef HAVE_CREDS
#endif
diff --git a/src/pulsecore/filter/lfe-filter.c b/src/pulsecore/filter/lfe-filter.c
index 5f5ace2..c0b1eb0 100644
--- a/src/pulsecore/filter/lfe-filter.c
+++ b/src/pulsecore/filter/lfe-filter.c
@@ -110,6 +110,7 @@ static void process_block(pa_lfe_filter_t *f, pa_memchunk *buf, bool store_resul
}
pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *f, pa_memchunk *buf) {
+ pa_mempool *pool;
struct saved_state *s, *s2;
void *data;
@@ -129,10 +130,12 @@ pa_memchunk * pa_lfe_filter_process(pa_lfe_filter_t *f, pa_memchunk *buf) {
/* TODO: This actually memcpys the entire chunk into a new allocation, because we need to retain the original
in case of rewinding. Investigate whether this can be avoided. */
data = pa_memblock_acquire_chunk(buf);
- s->chunk.memblock = pa_memblock_new_malloced(pa_memblock_get_pool(buf->memblock), pa_xmemdup(data, buf->length), buf->length);
+ pool = pa_memblock_get_pool(buf->memblock);
+ s->chunk.memblock = pa_memblock_new_malloced(pool, pa_xmemdup(data, buf->length), buf->length);
s->chunk.length = buf->length;
s->chunk.index = 0;
pa_memblock_release(buf->memblock);
+ pa_mempool_unref(pool), pool = NULL;
s->index = f->index;
memcpy(s->lr4, f->lr4, sizeof(struct lr4) * f->cm.channels);
diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c
index d1bb109..e62750b 100644
--- a/src/pulsecore/iochannel.c
+++ b/src/pulsecore/iochannel.c
@@ -450,6 +450,7 @@ ssize_t pa_iochannel_read_with_ancil_data(pa_iochannel*io, void*data, size_t l,
}
memcpy(ancil_data->fds, CMSG_DATA(cmh), nfd * sizeof(int));
ancil_data->nfd = nfd;
+ ancil_data->close_fds_on_cleanup = true;
}
}
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index 3c876f6..b5db818 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -39,6 +39,17 @@
#endif
#ifdef HAVE_SYSTEMD_JOURNAL
+
+/* sd_journal_send() implicitly add fields for the source file,
+ * function name and code line from where it's invoked. As the
+ * correct code location fields CODE_FILE, CODE_LINE and
+ * CODE_FUNC are already handled by this module, we do not want
+ * the automatic values supplied by the systemd journal API.
+ *
+ * Without suppressing these, both the actual log event source
+ * and the call to sd_journal_send() will be logged. */
+#define SD_JOURNAL_SUPPRESS_LOCATION
+
#include <systemd/sd-journal.h>
#endif
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index 1716f81..1e15f4f 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -298,14 +298,16 @@ static inline size_t PA_PAGE_ALIGN(size_t l) {
#define PA_INT_TYPE_SIGNED(type) (!!((type) 0 > (type) -1))
+#define PA_INT_TYPE_HALF(type) ((type) 1 << (sizeof(type)*8 - 2))
+
#define PA_INT_TYPE_MAX(type) \
((type) (PA_INT_TYPE_SIGNED(type) \
- ? ~(~(type) 0 << (8*sizeof(type)-1)) \
+ ? (PA_INT_TYPE_HALF(type) - 1 + PA_INT_TYPE_HALF(type)) \
: (type) -1))
#define PA_INT_TYPE_MIN(type) \
((type) (PA_INT_TYPE_SIGNED(type) \
- ? (~(type) 0 << (8*sizeof(type)-1)) \
+ ? (-1 - PA_INT_TYPE_MAX(type)) \
: (type) 0))
/* We include this at the very last place */
diff --git a/src/pulsecore/mem.h b/src/pulsecore/mem.h
new file mode 100644
index 0000000..cba1410
--- /dev/null
+++ b/src/pulsecore/mem.h
@@ -0,0 +1,60 @@
+#ifndef foopulsememhfoo
+#define foopulsememhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2016 Ahmed S. Darwish <darwish.07@gmail.com>
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.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, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdbool.h>
+
+#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
+
+typedef enum pa_mem_type {
+ PA_MEM_TYPE_SHARED_POSIX, /* Data is shared and created using POSIX shm_open() */
+ PA_MEM_TYPE_SHARED_MEMFD, /* Data is shared and created using Linux memfd_create() */
+ PA_MEM_TYPE_PRIVATE, /* Data is private and created using classic memory allocation
+ (posix_memalign(), malloc() or anonymous mmap()) */
+} pa_mem_type_t;
+
+static inline const char *pa_mem_type_to_string(pa_mem_type_t type) {
+ switch (type) {
+ case PA_MEM_TYPE_SHARED_POSIX:
+ return "shared posix-shm";
+ case PA_MEM_TYPE_SHARED_MEMFD:
+ return "shared memfd";
+ case PA_MEM_TYPE_PRIVATE:
+ return "private";
+ }
+
+ pa_assert_not_reached();
+}
+
+static inline bool pa_mem_type_is_shared(pa_mem_type_t t) {
+ return (t == PA_MEM_TYPE_SHARED_POSIX) || (t == PA_MEM_TYPE_SHARED_MEMFD);
+}
+
+static inline bool pa_memfd_is_locally_supported() {
+#if defined(HAVE_CREDS) && defined(HAVE_MEMFD)
+ return true;
+#else
+ return false;
+#endif
+}
+
+#endif
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index 9b6810d..17520ed 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -100,6 +100,28 @@ struct pa_memimport_segment {
bool writable;
};
+/*
+ * If true, this segment's lifetime will not be limited by the
+ * number of active blocks (seg->n_blocks) using its shared memory.
+ * Rather, it will exist for the full lifetime of the memimport it
+ * is attached to.
+ *
+ * This is done to support memfd blocks transport.
+ *
+ * To transfer memfd-backed blocks without passing their fd every
+ * time, thus minimizing overhead and avoiding fd leaks, a command
+ * is sent with the memfd fd as ancil data very early on.
+ *
+ * This command has an ID that identifies the memfd region. Further
+ * block references are then exclusively done using this ID. On the
+ * receiving end, such logic is enabled by the memimport's segment
+ * hash and 'permanent' segments below.
+ */
+static bool segment_is_permanent(pa_memimport_segment *seg) {
+ pa_assert(seg);
+ return seg->memory.type == PA_MEM_TYPE_SHARED_MEMFD;
+}
+
/* A collection of multiple segments */
struct pa_memimport {
pa_mutex *mutex;
@@ -142,10 +164,30 @@ struct pa_memexport {
};
struct pa_mempool {
+ /* Reference count the mempool
+ *
+ * Any block allocation from the pool itself, or even just imported from
+ * another process through SHM and attached to it (PA_MEMBLOCK_IMPORTED),
+ * shall increase the refcount.
+ *
+ * This is done for per-client mempools: global references to blocks in
+ * the pool, or just to attached ones, can still be lingering around when
+ * the client connection dies and all per-client objects are to be freed.
+ * That is, current PulseAudio design does not guarantee that the client
+ * mempool blocks are referenced only by client-specific objects.
+ *
+ * For further details, please check:
+ * https://lists.freedesktop.org/archives/pulseaudio-discuss/2016-February/025587.html
+ */
+ PA_REFCNT_DECLARE;
+
pa_semaphore *semaphore;
pa_mutex *mutex;
pa_shm memory;
+
+ bool global;
+
size_t block_size;
unsigned n_blocks;
bool is_remote_writable;
@@ -237,6 +279,7 @@ static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length);
PA_REFCNT_INIT(b);
b->pool = p;
+ pa_mempool_ref(b->pool);
b->type = PA_MEMBLOCK_APPENDED;
b->read_only = b->is_silence = false;
pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
@@ -367,6 +410,7 @@ pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
PA_REFCNT_INIT(b);
b->pool = p;
+ pa_mempool_ref(b->pool);
b->read_only = b->is_silence = false;
b->length = length;
pa_atomic_store(&b->n_acquired, 0);
@@ -390,6 +434,7 @@ pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, bool r
PA_REFCNT_INIT(b);
b->pool = p;
+ pa_mempool_ref(b->pool);
b->type = PA_MEMBLOCK_FIXED;
b->read_only = read_only;
b->is_silence = false;
@@ -423,6 +468,7 @@ pa_memblock *pa_memblock_new_user(
PA_REFCNT_INIT(b);
b->pool = p;
+ pa_mempool_ref(b->pool);
b->type = PA_MEMBLOCK_USER;
b->read_only = read_only;
b->is_silence = false;
@@ -518,10 +564,13 @@ size_t pa_memblock_get_length(pa_memblock *b) {
return b->length;
}
+/* Note! Always unref the returned pool after use */
pa_mempool* pa_memblock_get_pool(pa_memblock *b) {
pa_assert(b);
pa_assert(PA_REFCNT_VALUE(b) > 0);
+ pa_assert(b->pool);
+ pa_mempool_ref(b->pool);
return b->pool;
}
@@ -535,10 +584,13 @@ pa_memblock* pa_memblock_ref(pa_memblock*b) {
}
static void memblock_free(pa_memblock *b) {
- pa_assert(b);
+ pa_mempool *pool;
+ pa_assert(b);
+ pa_assert(b->pool);
pa_assert(pa_atomic_load(&b->n_acquired) == 0);
+ pool = b->pool;
stat_remove(b);
switch (b->type) {
@@ -620,6 +672,8 @@ static void memblock_free(pa_memblock *b) {
default:
pa_assert_not_reached();
}
+
+ pa_mempool_unref(pool);
}
/* No lock necessary */
@@ -744,11 +798,39 @@ static void memblock_replace_import(pa_memblock *b) {
pa_mutex_unlock(import->mutex);
}
-pa_mempool* pa_mempool_new(bool shared, size_t size) {
+/*@per_client: This is a security measure. By default this should
+ * be set to true where the created mempool is never shared with more
+ * than one client in the system. Set this to false if a global
+ * mempool, shared with all existing and future clients, is required.
+ *
+ * NOTE-1: Do not create any further global mempools! They allow data
+ * leaks between clients and thus conflict with the xdg-app containers
+ * model. They also complicate the handling of memfd-based pools.
+ *
+ * NOTE-2: Almost all mempools are now created on a per client basis.
+ * The only exception is the pa_core's mempool which is still shared
+ * between all clients of the system.
+ *
+ * Beside security issues, special marking for global mempools is
+ * required for memfd communication. To avoid fd leaks, memfd pools
+ * are registered with the connection pstream to create an ID<->memfd
+ * mapping on both PA endpoints. Such memory regions are then always
+ * referenced by their IDs and never by their fds and thus their fds
+ * can be quickly closed later.
+ *
+ * Unfortunately this scheme cannot work with global pools since the
+ * ID registration mechanism needs to happen for each newly connected
+ * client, and thus the need for a more special handling. That is,
+ * for the pool's fd to be always open :-(
+ *
+ * TODO-1: Transform the global core mempool to a per-client one
+ * TODO-2: Remove global mempools support */
+pa_mempool *pa_mempool_new(pa_mem_type_t type, size_t size, bool per_client) {
pa_mempool *p;
char t1[PA_BYTES_SNPRINT_MAX], t2[PA_BYTES_SNPRINT_MAX];
p = pa_xnew0(pa_mempool, 1);
+ PA_REFCNT_INIT(p);
p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE);
if (p->block_size < PA_PAGE_SIZE)
@@ -763,18 +845,20 @@ pa_mempool* pa_mempool_new(bool shared, size_t size) {
p->n_blocks = 2;
}
- if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {
+ if (pa_shm_create_rw(&p->memory, type, p->n_blocks * p->block_size, 0700) < 0) {
pa_xfree(p);
return NULL;
}
pa_log_debug("Using %s memory pool with %u slots of size %s each, total size is %s, maximum usable slot size is %lu",
- p->memory.shared ? "shared" : "private",
+ pa_mem_type_to_string(type),
p->n_blocks,
pa_bytes_snprint(t1, sizeof(t1), (unsigned) p->block_size),
pa_bytes_snprint(t2, sizeof(t2), (unsigned) (p->n_blocks * p->block_size)),
(unsigned long) pa_mempool_block_size_max(p));
+ p->global = !per_client;
+
pa_atomic_store(&p->n_init, 0);
PA_LLIST_HEAD_INIT(pa_memimport, p->imports);
@@ -788,7 +872,7 @@ pa_mempool* pa_mempool_new(bool shared, size_t size) {
return p;
}
-void pa_mempool_free(pa_mempool *p) {
+static void mempool_free(pa_mempool *p) {
pa_assert(p);
pa_mutex_lock(p->mutex);
@@ -893,10 +977,24 @@ void pa_mempool_vacuum(pa_mempool *p) {
}
/* No lock necessary */
+bool pa_mempool_is_shared(pa_mempool *p) {
+ pa_assert(p);
+
+ return pa_mem_type_is_shared(p->memory.type);
+}
+
+/* No lock necessary */
+bool pa_mempool_is_memfd_backed(const pa_mempool *p) {
+ pa_assert(p);
+
+ return (p->memory.type == PA_MEM_TYPE_SHARED_MEMFD);
+}
+
+/* No lock necessary */
int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
pa_assert(p);
- if (!p->memory.shared)
+ if (!pa_mempool_is_shared(p))
return -1;
*id = p->memory.id;
@@ -904,11 +1002,84 @@ int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
return 0;
}
-/* No lock necessary */
-bool pa_mempool_is_shared(pa_mempool *p) {
+pa_mempool* pa_mempool_ref(pa_mempool *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+ PA_REFCNT_INC(p);
+ return p;
+}
+
+void pa_mempool_unref(pa_mempool *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+ if (PA_REFCNT_DEC(p) <= 0)
+ mempool_free(p);
+}
+
+/* No lock necessary
+ * Check pa_mempool_new() for per-client vs. global mempools */
+bool pa_mempool_is_global(pa_mempool *p) {
+ pa_assert(p);
+
+ return p->global;
+}
+
+/* No lock necessary
+ * Check pa_mempool_new() for per-client vs. global mempools */
+bool pa_mempool_is_per_client(pa_mempool *p) {
+ return !pa_mempool_is_global(p);
+}
+
+/* Self-locked
+ *
+ * This is only for per-client mempools!
+ *
+ * After this method's return, the caller owns the file descriptor
+ * and is responsible for closing it in the appropriate time. This
+ * should only be called once during during a mempool's lifetime.
+ *
+ * Check pa_shm->fd and pa_mempool_new() for further context. */
+int pa_mempool_take_memfd_fd(pa_mempool *p) {
+ int memfd_fd;
+
pa_assert(p);
+ pa_assert(pa_mempool_is_shared(p));
+ pa_assert(pa_mempool_is_memfd_backed(p));
+ pa_assert(pa_mempool_is_per_client(p));
+
+ pa_mutex_lock(p->mutex);
+
+ memfd_fd = p->memory.fd;
+ p->memory.fd = -1;
- return p->memory.shared;
+ pa_mutex_unlock(p->mutex);
+
+ pa_assert(memfd_fd != -1);
+ return memfd_fd;
+}
+
+/* No lock necessary
+ *
+ * This is only for global mempools!
+ *
+ * Global mempools have their memfd descriptor always open. DO NOT
+ * close the returned descriptor by your own.
+ *
+ * Check pa_mempool_new() for further context. */
+int pa_mempool_get_memfd_fd(pa_mempool *p) {
+ int memfd_fd;
+
+ pa_assert(p);
+ pa_assert(pa_mempool_is_shared(p));
+ pa_assert(pa_mempool_is_memfd_backed(p));
+ pa_assert(pa_mempool_is_global(p));
+
+ memfd_fd = p->memory.fd;
+ pa_assert(memfd_fd != -1);
+
+ return memfd_fd;
}
/* For receiving blocks from other nodes */
@@ -921,6 +1092,7 @@ pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void
i = pa_xnew(pa_memimport, 1);
i->mutex = pa_mutex_new(true, true);
i->pool = p;
+ pa_mempool_ref(i->pool);
i->segments = pa_hashmap_new(NULL, NULL);
i->blocks = pa_hashmap_new(NULL, NULL);
i->release_cb = cb;
@@ -935,16 +1107,19 @@ pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void
static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i);
-/* Should be called locked */
-static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id, bool writable) {
+/* Should be called locked
+ * Caller owns passed @memfd_fd and must close it down when appropriate. */
+static pa_memimport_segment* segment_attach(pa_memimport *i, pa_mem_type_t type, uint32_t shm_id,
+ int memfd_fd, bool writable) {
pa_memimport_segment* seg;
+ pa_assert(pa_mem_type_is_shared(type));
if (pa_hashmap_size(i->segments) >= PA_MEMIMPORT_SEGMENTS_MAX)
return NULL;
seg = pa_xnew0(pa_memimport_segment, 1);
- if (pa_shm_attach(&seg->memory, shm_id, writable) < 0) {
+ if (pa_shm_attach(&seg->memory, type, shm_id, memfd_fd, writable) < 0) {
pa_xfree(seg);
return NULL;
}
@@ -960,6 +1135,7 @@ static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id, bo
/* Should be called locked */
static void segment_detach(pa_memimport_segment *seg) {
pa_assert(seg);
+ pa_assert(seg->n_blocks == (segment_is_permanent(seg) ? 1u : 0u));
pa_hashmap_remove(seg->import->segments, PA_UINT32_TO_PTR(seg->memory.id));
pa_shm_free(&seg->memory);
@@ -974,6 +1150,8 @@ static void segment_detach(pa_memimport_segment *seg) {
void pa_memimport_free(pa_memimport *i) {
pa_memexport *e;
pa_memblock *b;
+ pa_memimport_segment *seg;
+ void *state = NULL;
pa_assert(i);
@@ -982,6 +1160,15 @@ void pa_memimport_free(pa_memimport *i) {
while ((b = pa_hashmap_first(i->blocks)))
memblock_replace_import(b);
+ /* Permanent segments exist for the lifetime of the memimport. Now
+ * that we're freeing the memimport itself, clear them all up.
+ *
+ * Careful! segment_detach() internally removes itself from the
+ * memimport's hash; the same hash we're now using for iteration. */
+ PA_HASHMAP_FOREACH(seg, i->segments, state) {
+ if (segment_is_permanent(seg))
+ segment_detach(seg);
+ }
pa_assert(pa_hashmap_size(i->segments) == 0);
pa_mutex_unlock(i->mutex);
@@ -996,6 +1183,7 @@ void pa_memimport_free(pa_memimport *i) {
pa_mutex_unlock(i->pool->mutex);
+ pa_mempool_unref(i->pool);
pa_hashmap_free(i->blocks);
pa_hashmap_free(i->segments);
@@ -1004,13 +1192,47 @@ void pa_memimport_free(pa_memimport *i) {
pa_xfree(i);
}
+/* Create a new memimport's memfd segment entry, with passed SHM ID
+ * as key and the newly-created segment (with its mmap()-ed memfd
+ * memory region) as its value.
+ *
+ * Note! check comments at 'pa_shm->fd', 'segment_is_permanent()',
+ * and 'pa_pstream_register_memfd_mempool()' for further details.
+ *
+ * Caller owns passed @memfd_fd and must close it down when appropriate. */
+int pa_memimport_attach_memfd(pa_memimport *i, uint32_t shm_id, int memfd_fd, bool writable) {
+ pa_memimport_segment *seg;
+ int ret = -1;
+
+ pa_assert(i);
+ pa_assert(memfd_fd != -1);
+
+ pa_mutex_lock(i->mutex);
+
+ if (!(seg = segment_attach(i, PA_MEM_TYPE_SHARED_MEMFD, shm_id, memfd_fd, writable)))
+ goto finish;
+
+ /* n_blocks acts as a segment reference count. To avoid the segment
+ * being deleted when receiving silent memchunks, etc., mark our
+ * permanent presence by incrementing that refcount. */
+ seg->n_blocks++;
+
+ pa_assert(segment_is_permanent(seg));
+ ret = 0;
+
+finish:
+ pa_mutex_unlock(i->mutex);
+ return ret;
+}
+
/* Self-locked */
-pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_id,
+pa_memblock* pa_memimport_get(pa_memimport *i, pa_mem_type_t type, uint32_t block_id, uint32_t shm_id,
size_t offset, size_t size, bool writable) {
pa_memblock *b = NULL;
pa_memimport_segment *seg;
pa_assert(i);
+ pa_assert(pa_mem_type_is_shared(type));
pa_mutex_lock(i->mutex);
@@ -1022,12 +1244,20 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX)
goto finish;
- if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id))))
- if (!(seg = segment_attach(i, shm_id, writable)))
+ if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id)))) {
+ if (type == PA_MEM_TYPE_SHARED_MEMFD) {
+ pa_log("Bailing out! No cached memimport segment for memfd ID %u", shm_id);
+ pa_log("Did the other PA endpoint forget registering its memfd pool?");
goto finish;
+ }
+
+ pa_assert(type == PA_MEM_TYPE_SHARED_POSIX);
+ if (!(seg = segment_attach(i, type, shm_id, -1, writable)))
+ goto finish;
+ }
- if (writable != seg->writable) {
- pa_log("Cannot open segment - writable status changed!");
+ if (writable && !seg->writable) {
+ pa_log("Cannot import cached segment in write mode - previously mapped as read-only");
goto finish;
}
@@ -1039,6 +1269,7 @@ pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_i
PA_REFCNT_INIT(b);
b->pool = i->pool;
+ pa_mempool_ref(b->pool);
b->type = PA_MEMBLOCK_IMPORTED;
b->read_only = !writable;
b->is_silence = false;
@@ -1090,12 +1321,13 @@ pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void
pa_assert(p);
pa_assert(cb);
- if (!p->memory.shared)
+ if (!pa_mempool_is_shared(p))
return NULL;
e = pa_xnew(pa_memexport, 1);
e->mutex = pa_mutex_new(true, true);
e->pool = p;
+ pa_mempool_ref(e->pool);
PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots);
PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots);
e->n_init = 0;
@@ -1123,6 +1355,7 @@ void pa_memexport_free(pa_memexport *e) {
PA_LLIST_REMOVE(pa_memexport, e->pool->exports, e);
pa_mutex_unlock(e->pool->mutex);
+ pa_mempool_unref(e->pool);
pa_mutex_free(e->mutex);
pa_xfree(e);
}
@@ -1217,13 +1450,15 @@ static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) {
}
/* Self-locked */
-int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32_t *shm_id, size_t *offset, size_t * size) {
- pa_shm *memory;
+int pa_memexport_put(pa_memexport *e, pa_memblock *b, pa_mem_type_t *type, uint32_t *block_id,
+ uint32_t *shm_id, size_t *offset, size_t * size) {
+ pa_shm *memory;
struct memexport_slot *slot;
void *data;
pa_assert(e);
pa_assert(b);
+ pa_assert(type);
pa_assert(block_id);
pa_assert(shm_id);
pa_assert(offset);
@@ -1261,12 +1496,14 @@ int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32
} else {
pa_assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL);
pa_assert(b->pool);
+ pa_assert(pa_mempool_is_shared(b->pool));
memory = &b->pool->memory;
}
pa_assert(data >= memory->ptr);
pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size);
+ *type = memory->type;
*shm_id = memory->id;
*offset = (size_t) ((uint8_t*) data - (uint8_t*) memory->ptr);
*size = b->length;
diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h
index 4faef75..57ae4b2 100644
--- a/src/pulsecore/memblock.h
+++ b/src/pulsecore/memblock.h
@@ -30,6 +30,7 @@ typedef struct pa_memblock pa_memblock;
#include <pulse/xmalloc.h>
#include <pulsecore/atomic.h>
#include <pulsecore/memchunk.h>
+#include <pulsecore/mem.h>
/* A pa_memblock is a reference counted memory block. PulseAudio
* passes references to pa_memblocks around instead of copying
@@ -116,32 +117,43 @@ void *pa_memblock_acquire_chunk(const pa_memchunk *c);
void pa_memblock_release(pa_memblock *b);
size_t pa_memblock_get_length(pa_memblock *b);
+
+/* Note! Always unref the returned pool after use */
pa_mempool * pa_memblock_get_pool(pa_memblock *b);
pa_memblock *pa_memblock_will_need(pa_memblock *b);
/* The memory block manager */
-pa_mempool* pa_mempool_new(bool shared, size_t size);
-void pa_mempool_free(pa_mempool *p);
+pa_mempool *pa_mempool_new(pa_mem_type_t type, size_t size, bool per_client);
+void pa_mempool_unref(pa_mempool *p);
+pa_mempool* pa_mempool_ref(pa_mempool *p);
const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p);
void pa_mempool_vacuum(pa_mempool *p);
int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id);
bool pa_mempool_is_shared(pa_mempool *p);
+bool pa_mempool_is_memfd_backed(const pa_mempool *p);
+bool pa_mempool_is_global(pa_mempool *p);
+bool pa_mempool_is_per_client(pa_mempool *p);
bool pa_mempool_is_remote_writable(pa_mempool *p);
void pa_mempool_set_is_remote_writable(pa_mempool *p, bool writable);
size_t pa_mempool_block_size_max(pa_mempool *p);
+int pa_mempool_take_memfd_fd(pa_mempool *p);
+int pa_mempool_get_memfd_fd(pa_mempool *p);
+
/* For receiving blocks from other nodes */
pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata);
void pa_memimport_free(pa_memimport *i);
-pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_id,
- size_t offset, size_t size, bool writable);
+int pa_memimport_attach_memfd(pa_memimport *i, uint32_t shm_id, int memfd_fd, bool writable);
+pa_memblock* pa_memimport_get(pa_memimport *i, pa_mem_type_t type, uint32_t block_id,
+ uint32_t shm_id, size_t offset, size_t size, bool writable);
int pa_memimport_process_revoke(pa_memimport *i, uint32_t block_id);
/* For sending blocks to other nodes */
pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void *userdata);
void pa_memexport_free(pa_memexport *e);
-int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32_t *shm_id, size_t *offset, size_t *size);
+int pa_memexport_put(pa_memexport *e, pa_memblock *b, pa_mem_type_t *type, uint32_t *block_id,
+ uint32_t *shm_id, size_t *offset, size_t * size);
int pa_memexport_process_release(pa_memexport *e, uint32_t id);
#endif
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index d314d4e..d283ed2 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -530,6 +530,7 @@ int pa_memblockq_peek(pa_memblockq* bq, pa_memchunk *chunk) {
}
int pa_memblockq_peek_fixed_size(pa_memblockq *bq, size_t block_size, pa_memchunk *chunk) {
+ pa_mempool *pool;
pa_memchunk tchunk, rchunk;
int64_t ri;
struct list_item *item;
@@ -548,9 +549,11 @@ int pa_memblockq_peek_fixed_size(pa_memblockq *bq, size_t block_size, pa_memchun
return 0;
}
- rchunk.memblock = pa_memblock_new(pa_memblock_get_pool(tchunk.memblock), block_size);
+ pool = pa_memblock_get_pool(tchunk.memblock);
+ rchunk.memblock = pa_memblock_new(pool, block_size);
rchunk.index = 0;
rchunk.length = tchunk.length;
+ pa_mempool_unref(pool), pool = NULL;
pa_memchunk_memcpy(&rchunk, &tchunk);
pa_memblock_unref(tchunk.memblock);
diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c
index eb5917c..8822134 100644
--- a/src/pulsecore/memchunk.c
+++ b/src/pulsecore/memchunk.c
@@ -32,6 +32,7 @@
#include "memchunk.h"
pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
+ pa_mempool *pool;
pa_memblock *n;
size_t l;
void *tdata, *sdata;
@@ -46,7 +47,9 @@ pa_memchunk* pa_memchunk_make_writable(pa_memchunk *c, size_t min) {
l = PA_MAX(c->length, min);
- n = pa_memblock_new(pa_memblock_get_pool(c->memblock), l);
+ pool = pa_memblock_get_pool(c->memblock);
+ n = pa_memblock_new(pool, l);
+ pa_mempool_unref(pool), pool = NULL;
sdata = pa_memblock_acquire(c->memblock);
tdata = pa_memblock_acquire(n);
diff --git a/src/pulsecore/memfd-wrappers.h b/src/pulsecore/memfd-wrappers.h
new file mode 100644
index 0000000..3bed9b2
--- /dev/null
+++ b/src/pulsecore/memfd-wrappers.h
@@ -0,0 +1,68 @@
+#ifndef foopulsememfdwrappershfoo
+#define foopulsememfdwrappershfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2016 Ahmed S. Darwish <darwish.07@gmail.com>
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_MEMFD
+
+#include <sys/syscall.h>
+#include <fcntl.h>
+
+/*
+ * No glibc wrappers exist for memfd_create(2), so provide our own.
+ *
+ * Also define memfd fcntl sealing macros. While they are already
+ * defined in the kernel header file <linux/fcntl.h>, that file as
+ * a whole conflicts with the original glibc header <fnctl.h>.
+ */
+
+static inline int memfd_create(const char *name, unsigned int flags) {
+ return syscall(SYS_memfd_create, name, flags);
+}
+
+/* memfd_create(2) flags */
+
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC 0x0001U
+#endif
+
+#ifndef MFD_ALLOW_SEALING
+#define MFD_ALLOW_SEALING 0x0002U
+#endif
+
+/* fcntl() seals-related flags */
+
+#ifndef F_LINUX_SPECIFIC_BASE
+#define F_LINUX_SPECIFIC_BASE 1024
+#endif
+
+#ifndef F_ADD_SEALS
+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
+#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
+
+#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
+#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
+#define F_SEAL_GROW 0x0004 /* prevent file from growing */
+#define F_SEAL_WRITE 0x0008 /* prevent writes */
+#endif
+
+#endif /* HAVE_MEMFD */
+
+#endif
diff --git a/src/pulsecore/native-common.c b/src/pulsecore/native-common.c
new file mode 100644
index 0000000..282a4ed
--- /dev/null
+++ b/src/pulsecore/native-common.c
@@ -0,0 +1,78 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2016 Ahmed S. Darwish <darwish.07@gmail.com>
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.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, see <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/creds.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/tagstruct.h>
+
+#include "native-common.h"
+
+/*
+ * Command handlers shared between client and server
+ */
+
+/* Check pa_pstream_register_memfd_mempool() for further details */
+int pa_common_command_register_memfd_shmid(pa_pstream *p, pa_pdispatch *pd, uint32_t version,
+ uint32_t command, pa_tagstruct *t) {
+#if defined(HAVE_CREDS) && defined(HAVE_MEMFD)
+ pa_cmsg_ancil_data *ancil = NULL;
+ unsigned shm_id;
+ int ret = -1;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_REGISTER_MEMFD_SHMID);
+ pa_assert(t);
+
+ ancil = pa_pdispatch_take_ancil_data(pd);
+ if (!ancil)
+ goto finish;
+
+ /* Upon fd leaks and reaching our open fd limit, recvmsg(2)
+ * just strips all passed fds from the ancillary data */
+ if (ancil->nfd == 0) {
+ pa_log("Expected 1 memfd fd to be received over pipe; got 0");
+ pa_log("Did we reach our open file descriptors limit?");
+ goto finish;
+ }
+
+ if (ancil->nfd != 1 || ancil->fds[0] == -1)
+ goto finish;
+
+ if (version < 31 || pa_tagstruct_getu32(t, &shm_id) < 0 || !pa_tagstruct_eof(t))
+ goto finish;
+
+ pa_pstream_attach_memfd_shmid(p, shm_id, ancil->fds[0]);
+
+ ret = 0;
+finish:
+ if (ancil)
+ pa_cmsg_ancil_data_close_fds(ancil);
+
+ return ret;
+#else
+ return -1;
+#endif
+}
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index dc62895..70338b9 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -24,6 +24,10 @@
#include <pulse/cdecl.h>
#include <pulse/def.h>
+#include <pulsecore/pdispatch.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/tagstruct.h>
+
PA_C_DECL_BEGIN
enum {
@@ -179,6 +183,10 @@ enum {
PA_COMMAND_ENABLE_SRBCHANNEL,
PA_COMMAND_DISABLE_SRBCHANNEL,
+ /* Supported since protocol v31 (9.0)
+ * BOTH DIRECTIONS */
+ PA_COMMAND_REGISTER_MEMFD_SHMID,
+
PA_COMMAND_MAX
};
@@ -193,6 +201,9 @@ enum {
#define PA_NATIVE_DEFAULT_UNIX_SOCKET "native"
+int pa_common_command_register_memfd_shmid(pa_pstream *p, pa_pdispatch *pd, uint32_t version,
+ uint32_t command, pa_tagstruct *t);
+
PA_C_DECL_END
#endif
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index f136875..ab632a5 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -195,6 +195,10 @@ static const char *command_names[PA_COMMAND_MAX] = {
/* BOTH DIRECTIONS */
[PA_COMMAND_ENABLE_SRBCHANNEL] = "ENABLE_SRBCHANNEL",
[PA_COMMAND_DISABLE_SRBCHANNEL] = "DISABLE_SRBCHANNEL",
+
+ /* Supported since protocol v31 (9.0) */
+ /* BOTH DIRECTIONS */
+ [PA_COMMAND_REGISTER_MEMFD_SHMID] = "REGISTER_MEMFD_SHMID",
};
#endif
@@ -219,7 +223,7 @@ struct pa_pdispatch {
PA_LLIST_HEAD(struct reply_info, replies);
pa_pdispatch_drain_cb_t drain_callback;
void *drain_userdata;
- const pa_cmsg_ancil_data *ancil_data;
+ pa_cmsg_ancil_data *ancil_data;
bool use_rtclock;
};
@@ -289,7 +293,7 @@ static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command,
pa_pdispatch_unref(pd);
}
-int pa_pdispatch_run(pa_pdispatch *pd, pa_packet *packet, const pa_cmsg_ancil_data *ancil_data, void *userdata) {
+int pa_pdispatch_run(pa_pdispatch *pd, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
uint32_t tag, command;
pa_tagstruct *ts = NULL;
int ret = -1;
@@ -448,18 +452,24 @@ const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
return NULL;
}
-const int * pa_pdispatch_fds(pa_pdispatch *pd, int *nfd) {
+/* Should be called only once during the dispatcher lifetime
+ *
+ * If the returned ancillary data contains any fds, caller maintains sole
+ * responsibility of closing them down using pa_cmsg_ancil_data_close_fds() */
+pa_cmsg_ancil_data *pa_pdispatch_take_ancil_data(pa_pdispatch *pd) {
+ pa_cmsg_ancil_data *ancil;
+
pa_assert(pd);
pa_assert(PA_REFCNT_VALUE(pd) >= 1);
- pa_assert(nfd);
- if (pd->ancil_data) {
- *nfd = pd->ancil_data->nfd;
- return pd->ancil_data->fds;
- }
+ ancil = pd->ancil_data;
- *nfd = 0;
- return NULL;
+ /* iochannel guarantees us that nfd will always be capped */
+ if (ancil)
+ pa_assert(ancil->nfd <= MAX_ANCIL_DATA_FDS);
+
+ pd->ancil_data = NULL;
+ return ancil;
}
#endif
diff --git a/src/pulsecore/pdispatch.h b/src/pulsecore/pdispatch.h
index 9cb3419..af76981 100644
--- a/src/pulsecore/pdispatch.h
+++ b/src/pulsecore/pdispatch.h
@@ -39,7 +39,7 @@ pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *m, bool use_rtclock, const pa_pd
void pa_pdispatch_unref(pa_pdispatch *pd);
pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd);
-int pa_pdispatch_run(pa_pdispatch *pd, pa_packet *p, const pa_cmsg_ancil_data *ancil_data, void *userdata);
+int pa_pdispatch_run(pa_pdispatch *pd, pa_packet *p, pa_cmsg_ancil_data *ancil_data, void *userdata);
void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t callback, void *userdata, pa_free_cb_t free_cb);
@@ -51,7 +51,6 @@ void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, pa_pdispatch_drain_cb_t c
void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata);
const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd);
-
-const int * pa_pdispatch_fds(pa_pdispatch *pd, int *nfd);
+pa_cmsg_ancil_data *pa_pdispatch_take_ancil_data(pa_pdispatch *pd);
#endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 145db04..5619b9c 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -48,6 +48,7 @@
#include <pulsecore/core-scache.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
+#include <pulsecore/mem.h>
#include <pulsecore/strlist.h>
#include <pulsecore/shared.h>
#include <pulsecore/sample-util.h>
@@ -55,6 +56,7 @@
#include <pulsecore/core-util.h>
#include <pulsecore/ipacl.h>
#include <pulsecore/thread-mq.h>
+#include <pulsecore/mem.h>
#include "protocol-native.h"
@@ -173,6 +175,12 @@ struct pa_native_connection {
bool is_local:1;
uint32_t version;
pa_client *client;
+ /* R/W mempool, one per client connection, for srbchannel transport.
+ * Both server and client can write to this shm area.
+ *
+ * Note: This will be NULL if our connection with the client does
+ * not support srbchannels */
+ pa_mempool *rw_mempool;
pa_pstream *pstream;
pa_pdispatch *pdispatch;
pa_idxset *record_streams, *output_streams;
@@ -294,6 +302,7 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_
static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = NULL,
@@ -399,6 +408,8 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ENABLE_SRBCHANNEL] = command_enable_srbchannel,
+ [PA_COMMAND_REGISTER_MEMFD_SHMID] = command_register_memfd_shmid,
+
[PA_COMMAND_EXTENSION] = command_extension
};
@@ -1371,6 +1382,9 @@ static void native_connection_free(pa_object *o) {
pa_pdispatch_unref(c->pdispatch);
pa_pstream_unref(c->pstream);
+ if (c->rw_mempool)
+ pa_mempool_unref(c->rw_mempool);
+
pa_client_free(c->client);
pa_xfree(c);
@@ -2584,13 +2598,18 @@ static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */
}
-static void setup_srbchannel(pa_native_connection *c) {
+static void setup_srbchannel(pa_native_connection *c, pa_mem_type_t shm_type) {
pa_srbchannel_template srbt;
pa_srbchannel *srb;
pa_memchunk mc;
pa_tagstruct *t;
int fdlist[2];
+#ifndef HAVE_CREDS
+ pa_log_debug("Disabling srbchannel, reason: No fd passing support");
+ return;
+#endif
+
if (!c->options->srbchannel) {
pa_log_debug("Disabling srbchannel, reason: Must be enabled by module parameter");
return;
@@ -2606,15 +2625,31 @@ static void setup_srbchannel(pa_native_connection *c) {
return;
}
- if (!c->protocol->core->rw_mempool) {
- pa_log_debug("Disabling srbchannel, reason: No rw memory pool");
+ if (c->rw_mempool) {
+ pa_log_debug("Ignoring srbchannel setup, reason: received COMMAND_AUTH "
+ "more than once");
return;
}
- srb = pa_srbchannel_new(c->protocol->core->mainloop, c->protocol->core->rw_mempool);
+ if (!(c->rw_mempool = pa_mempool_new(shm_type, c->protocol->core->shm_size, true))) {
+ pa_log_warn("Disabling srbchannel, reason: Failed to allocate shared "
+ "writable memory pool.");
+ return;
+ }
+
+ if (shm_type == PA_MEM_TYPE_SHARED_MEMFD) {
+ const char *reason;
+ if (pa_pstream_register_memfd_mempool(c->pstream, c->rw_mempool, &reason)) {
+ pa_log_warn("Disabling srbchannel, reason: Failed to register memfd mempool: %s", reason);
+ goto fail;
+ }
+ }
+ pa_mempool_set_is_remote_writable(c->rw_mempool, true);
+
+ srb = pa_srbchannel_new(c->protocol->core->mainloop, c->rw_mempool);
if (!srb) {
pa_log_debug("Failed to create srbchannel");
- return;
+ goto fail;
}
pa_log_debug("Enabling srbchannel...");
pa_srbchannel_export(srb, &srbt);
@@ -2625,7 +2660,7 @@ static void setup_srbchannel(pa_native_connection *c) {
pa_tagstruct_putu32(t, (size_t) srb); /* tag */
fdlist[0] = srbt.readfd;
fdlist[1] = srbt.writefd;
- pa_pstream_send_tagstruct_with_fds(c->pstream, t, 2, fdlist);
+ pa_pstream_send_tagstruct_with_fds(c->pstream, t, 2, fdlist, false);
/* Send ringbuffer memblock to client */
mc.memblock = srbt.memblock;
@@ -2634,6 +2669,13 @@ static void setup_srbchannel(pa_native_connection *c) {
pa_pstream_send_memblock(c->pstream, 0, 0, 0, &mc);
c->srbpending = srb;
+ return;
+
+fail:
+ if (c->rw_mempool) {
+ pa_mempool_unref(c->rw_mempool);
+ c->rw_mempool = NULL;
+ }
}
static void command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -2652,7 +2694,9 @@ static void command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32
static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
const void*cookie;
+ bool memfd_on_remote = false, do_memfd = false;
pa_tagstruct *reply;
+ pa_mem_type_t shm_type;
bool shm_on_remote = false, do_shm;
pa_native_connection_assert_ref(c);
@@ -2676,7 +2720,15 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
not. */
if (c->version >= 13) {
shm_on_remote = !!(c->version & 0x80000000U);
- c->version &= 0x7FFFFFFFU;
+
+ /* Starting with protocol version 31, the second MSB of the version
+ * tag reflects whether memfd is supported on the other PA end. */
+ if (c->version >= 31)
+ memfd_on_remote = !!(c->version & 0x40000000U);
+
+ /* Reserve the two most-significant _bytes_ of the version tag
+ * for flags. */
+ c->version &= 0x0000FFFFU;
}
pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
@@ -2737,7 +2789,7 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
}
}
- /* Enable shared memory support if possible */
+ /* Enable shared memory and memfd support if possible */
do_shm =
pa_mempool_is_shared(c->protocol->core->mempool) &&
c->is_local;
@@ -2763,8 +2815,24 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm));
pa_pstream_enable_shm(c->pstream, do_shm);
+ do_memfd =
+ do_shm && pa_mempool_is_memfd_backed(c->protocol->core->mempool);
+
+ shm_type = PA_MEM_TYPE_PRIVATE;
+ if (do_shm) {
+ if (c->version >= 31 && memfd_on_remote && do_memfd) {
+ pa_pstream_enable_memfd(c->pstream);
+ shm_type = PA_MEM_TYPE_SHARED_MEMFD;
+ } else
+ shm_type = PA_MEM_TYPE_SHARED_POSIX;
+
+ pa_log_debug("Memfd possible: %s", pa_yes_no(pa_memfd_is_locally_supported()));
+ pa_log_debug("Negotiated SHM type: %s", pa_mem_type_to_string(shm_type));
+ }
+
reply = reply_new(tag);
- pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION | (do_shm ? 0x80000000 : 0));
+ pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION | (do_shm ? 0x80000000 : 0) |
+ (do_memfd ? 0x40000000 : 0));
#ifdef HAVE_CREDS
{
@@ -2781,7 +2849,29 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
pa_pstream_send_tagstruct(c->pstream, reply);
#endif
- setup_srbchannel(c);
+ /* The client enables memfd transport on its pstream only after
+ * inspecting our version flags to see if we support memfds too.
+ *
+ * Thus register any pools after sending the server's version
+ * flags and _never_ before it. */
+ if (shm_type == PA_MEM_TYPE_SHARED_MEMFD) {
+ const char *reason;
+
+ if (pa_pstream_register_memfd_mempool(c->pstream, c->protocol->core->mempool, &reason))
+ pa_log("Failed to register memfd mempool. Reason: %s", reason);
+ }
+
+ setup_srbchannel(c, shm_type);
+}
+
+static void command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+
+ pa_native_connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_common_command_register_memfd_shmid(c->pstream, pd, c->version, command, t))
+ protocol_error(c);
}
static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -4447,7 +4537,7 @@ static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
- pa_sink_input_set_name(s->sink_input, name);
+ pa_sink_input_set_property(s->sink_input, PA_PROP_MEDIA_NAME, name);
} else {
record_stream *s;
@@ -4456,7 +4546,7 @@ static void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t
s = pa_idxset_get_by_index(c->record_streams, idx);
CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
- pa_source_output_set_name(s->source_output, name);
+ pa_source_output_set_property(s->source_output, PA_PROP_MEDIA_NAME, name);
}
pa_pstream_send_simple_ack(c->pstream, tag);
@@ -4889,7 +4979,7 @@ static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command,
/*** pstream callbacks ***/
-static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_cmsg_ancil_data *ancil_data, void *userdata) {
+static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
pa_assert(p);
@@ -5125,6 +5215,8 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
c->client->send_event = client_send_event_cb;
c->client->userdata = c;
+ c->rw_mempool = NULL;
+
c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
pa_pstream_set_receive_packet_callback(c->pstream, pstream_packet_callback, c);
pa_pstream_set_receive_memblock_callback(c->pstream, pstream_memblock_callback, c);
diff --git a/src/pulsecore/pstream-util.c b/src/pulsecore/pstream-util.c
index e874503..d0d6c66 100644
--- a/src/pulsecore/pstream-util.c
+++ b/src/pulsecore/pstream-util.c
@@ -21,13 +21,16 @@
#include <config.h>
#endif
-#include <pulsecore/native-common.h>
+#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
+#include <pulsecore/native-common.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/refcnt.h>
#include <pulse/xmalloc.h>
#include "pstream-util.h"
-static void pa_pstream_send_tagstruct_with_ancil_data(pa_pstream *p, pa_tagstruct *t, const pa_cmsg_ancil_data *ancil_data) {
+static void pa_pstream_send_tagstruct_with_ancil_data(pa_pstream *p, pa_tagstruct *t, pa_cmsg_ancil_data *ancil_data) {
size_t length;
const uint8_t *data;
pa_packet *packet;
@@ -58,12 +61,21 @@ void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const
pa_pstream_send_tagstruct_with_ancil_data(p, t, NULL);
}
-void pa_pstream_send_tagstruct_with_fds(pa_pstream *p, pa_tagstruct *t, int nfd, const int *fds) {
+/* @close_fds: If set then the pstreams code, after invoking a sendmsg(),
+ * will close all passed fds.
+ *
+ * Such fds cannot be closed here as this might lead to freeing them
+ * before they're actually passed to the other end. The internally-used
+ * pa_pstream_send_packet() does not do any actual writes and just
+ * defers write events over the pstream. */
+void pa_pstream_send_tagstruct_with_fds(pa_pstream *p, pa_tagstruct *t, int nfd, const int *fds,
+ bool close_fds) {
if (nfd > 0) {
pa_cmsg_ancil_data a;
a.nfd = nfd;
a.creds_valid = false;
+ a.close_fds_on_cleanup = close_fds;
pa_assert(nfd <= MAX_ANCIL_DATA_FDS);
memcpy(a.fds, fds, sizeof(int) * nfd);
pa_pstream_send_tagstruct_with_ancil_data(p, t, &a);
@@ -78,7 +90,8 @@ void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const
pa_pstream_send_tagstruct_with_ancil_data(p, t, NULL);
}
-void pa_pstream_send_tagstruct_with_fds(pa_pstream *p, pa_tagstruct *t, int nfd, const int *fds) {
+void PA_GCC_NORETURN pa_pstream_send_tagstruct_with_fds(pa_pstream *p, pa_tagstruct *t, int nfd, const int *fds,
+ bool close_fds) {
pa_assert_not_reached();
}
@@ -102,3 +115,84 @@ void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag) {
pa_tagstruct_putu32(t, tag);
pa_pstream_send_tagstruct(p, t);
}
+
+/* Before sending blocks from a memfd-backed pool over the pipe, we
+ * must call this method first.
+ *
+ * This is needed to transfer memfd blocks without passing their fd
+ * every time, thus minimizing overhead and avoiding fd leaks.
+ *
+ * On registration a packet is sent with the memfd fd as ancil data;
+ * such packet has an ID that uniquely identifies the pool's memfd
+ * region. Upon arrival the other end creates a permanent mapping
+ * between that ID and the passed memfd memory area.
+ *
+ * By doing so, we won't need to reference the pool's memfd fd any
+ * further - just its ID. Both endpoints can then close their fds. */
+int pa_pstream_register_memfd_mempool(pa_pstream *p, pa_mempool *pool, const char **fail_reason) {
+#if defined(HAVE_CREDS) && defined(HAVE_MEMFD)
+ unsigned shm_id;
+ int memfd_fd, ret = -1;
+ pa_tagstruct *t;
+ bool per_client_mempool;
+
+ pa_assert(p);
+ pa_assert(fail_reason);
+
+ *fail_reason = NULL;
+ per_client_mempool = pa_mempool_is_per_client(pool);
+
+ pa_pstream_ref(p);
+
+ if (!pa_mempool_is_shared(pool)) {
+ *fail_reason = "mempool is not shared";
+ goto finish;
+ }
+
+ if (!pa_mempool_is_memfd_backed(pool)) {
+ *fail_reason = "mempool is not memfd-backed";
+ goto finish;
+ }
+
+ if (pa_mempool_get_shm_id(pool, &shm_id)) {
+ *fail_reason = "could not extract pool SHM ID";
+ goto finish;
+ }
+
+ if (!pa_pstream_get_memfd(p)) {
+ *fail_reason = "pipe does not support memfd transport";
+ goto finish;
+ }
+
+ memfd_fd = (per_client_mempool) ? pa_mempool_take_memfd_fd(pool) :
+ pa_mempool_get_memfd_fd(pool);
+
+ /* Note! For per-client mempools we've taken ownership of the memfd
+ * fd, and we're thus the sole code path responsible for closing it.
+ * In case of any failure, it MUST be closed. */
+
+ if (pa_pstream_attach_memfd_shmid(p, shm_id, memfd_fd)) {
+ *fail_reason = "could not attach memfd SHM ID to pipe";
+
+ if (per_client_mempool)
+ pa_assert_se(pa_close(memfd_fd) == 0);
+ goto finish;
+ }
+
+ t = pa_tagstruct_new();
+ pa_tagstruct_putu32(t, PA_COMMAND_REGISTER_MEMFD_SHMID);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, shm_id);
+ pa_pstream_send_tagstruct_with_fds(p, t, 1, &memfd_fd, per_client_mempool);
+
+ ret = 0;
+finish:
+ pa_pstream_unref(p);
+ return ret;
+
+#else
+ pa_assert(fail_reason);
+ *fail_reason = "memfd support not compiled in";
+ return -1;
+#endif
+}
diff --git a/src/pulsecore/pstream-util.h b/src/pulsecore/pstream-util.h
index 1366314..1191d48 100644
--- a/src/pulsecore/pstream-util.h
+++ b/src/pulsecore/pstream-util.h
@@ -27,11 +27,13 @@
/* The tagstruct is freed!*/
void pa_pstream_send_tagstruct_with_creds(pa_pstream *p, pa_tagstruct *t, const pa_creds *creds);
-void pa_pstream_send_tagstruct_with_fds(pa_pstream *p, pa_tagstruct *t, int nfd, const int *fds);
+void pa_pstream_send_tagstruct_with_fds(pa_pstream *p, pa_tagstruct *t, int nfd, const int *fds, bool close_fds);
#define pa_pstream_send_tagstruct(p, t) pa_pstream_send_tagstruct_with_creds((p), (t), NULL)
void pa_pstream_send_error(pa_pstream *p, uint32_t tag, uint32_t error);
void pa_pstream_send_simple_ack(pa_pstream *p, uint32_t tag);
+int pa_pstream_register_memfd_mempool(pa_pstream *p, pa_mempool *pool, const char **fail_reason);
+
#endif
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
index 98a8382..1ea3c5b 100644
--- a/src/pulsecore/pstream.c
+++ b/src/pulsecore/pstream.c
@@ -32,6 +32,7 @@
#include <pulse/xmalloc.h>
+#include <pulsecore/idxset.h>
#include <pulsecore/socket.h>
#include <pulsecore/queue.h>
#include <pulsecore/log.h>
@@ -44,6 +45,7 @@
/* We piggyback information if audio data blocks are stored in SHM on the seek mode */
#define PA_FLAG_SHMDATA 0x80000000LU
+#define PA_FLAG_SHMDATA_MEMFD_BLOCK 0x20000000LU
#define PA_FLAG_SHMRELEASE 0x40000000LU
#define PA_FLAG_SHMREVOKE 0xC0000000LU
#define PA_FLAG_SHMMASK 0xFF000000LU
@@ -143,7 +145,17 @@ struct pa_pstream {
struct pstream_read readio, readsrb;
- bool use_shm;
+ /* @use_shm: beside copying the full audio data to the other
+ * PA end, this pipe supports just sending references of the
+ * same audio data blocks if they reside in a SHM pool.
+ *
+ * @use_memfd: pipe supports sending SHM memfd block references
+ *
+ * @registered_memfd_ids: registered memfd pools SHM IDs. Check
+ * pa_pstream_register_memfd_mempool() for more information. */
+ bool use_shm, use_memfd;
+ pa_idxset *registered_memfd_ids;
+
pa_memimport *import;
pa_memexport *export;
@@ -168,11 +180,33 @@ struct pa_pstream {
pa_mempool *mempool;
#ifdef HAVE_CREDS
- pa_cmsg_ancil_data read_ancil_data, write_ancil_data;
+ pa_cmsg_ancil_data read_ancil_data, *write_ancil_data;
bool send_ancil_data_now;
#endif
};
+#ifdef HAVE_CREDS
+/* Don't close the ancillary fds by your own! Always call this method;
+ * it guarantees necessary cleanups after fds close.. This method is
+ * also multiple-invocations safe. */
+void pa_cmsg_ancil_data_close_fds(struct pa_cmsg_ancil_data *ancil) {
+ if (ancil && ancil->close_fds_on_cleanup) {
+ int i;
+
+ pa_assert(ancil->nfd <= MAX_ANCIL_DATA_FDS);
+
+ for (i = 0; i < ancil->nfd; i++)
+ if (ancil->fds[i] != -1) {
+ pa_assert_se(pa_close(ancil->fds[i]) == 0);
+ ancil->fds[i] = -1;
+ }
+
+ ancil->nfd = 0;
+ ancil->close_fds_on_cleanup = false;
+ }
+}
+#endif
+
static int do_write(pa_pstream *p);
static int do_read(pa_pstream *p, struct pstream_read *re);
@@ -287,6 +321,35 @@ pa_pstream *pa_pstream_new(pa_mainloop_api *m, pa_iochannel *io, pa_mempool *poo
return p;
}
+/* Attach memfd<->SHM_ID mapping to given pstream and its memimport.
+ * Check pa_pstream_register_memfd_mempool() for further info.
+ *
+ * Caller owns the passed @memfd_fd and must close it down when appropriate. */
+int pa_pstream_attach_memfd_shmid(pa_pstream *p, unsigned shm_id, int memfd_fd) {
+ int err = -1;
+
+ pa_assert(memfd_fd != -1);
+
+ if (!p->use_memfd) {
+ pa_log_warn("Received memfd ID registration request over a pipe "
+ "that does not support memfds");
+ return err;
+ }
+
+ if (pa_idxset_get_by_data(p->registered_memfd_ids, PA_UINT32_TO_PTR(shm_id), NULL)) {
+ pa_log_warn("previously registered memfd SHM ID = %u", shm_id);
+ return err;
+ }
+
+ if (pa_memimport_attach_memfd(p->import, shm_id, memfd_fd, true)) {
+ pa_log("Failed to create permanent mapping for memfd region with ID = %u", shm_id);
+ return err;
+ }
+
+ pa_assert_se(pa_idxset_put(p->registered_memfd_ids, PA_UINT32_TO_PTR(shm_id), NULL) == 0);
+ return 0;
+}
+
static void item_free(void *item) {
struct item_info *i = item;
pa_assert(i);
@@ -299,6 +362,15 @@ static void item_free(void *item) {
pa_packet_unref(i->packet);
}
+#ifdef HAVE_CREDS
+ /* On error recovery paths, there might be lingering items
+ * on the pstream send queue and they are usually freed with
+ * a call to 'pa_queue_free(p->send_queue, item_free)'. Make
+ * sure we do not leak any fds in that case! */
+ if (i->with_ancil_data)
+ pa_cmsg_ancil_data_close_fds(&i->ancil_data);
+#endif
+
if (pa_flist_push(PA_STATIC_FLIST_GET(items), i) < 0)
pa_xfree(i);
}
@@ -328,18 +400,25 @@ static void pstream_free(pa_pstream *p) {
if (p->readio.packet)
pa_packet_unref(p->readio.packet);
+ if (p->registered_memfd_ids)
+ pa_idxset_free(p->registered_memfd_ids, NULL);
+
pa_xfree(p);
}
-void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_cmsg_ancil_data *ancil_data) {
+void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data) {
struct item_info *i;
pa_assert(p);
pa_assert(PA_REFCNT_VALUE(p) > 0);
pa_assert(packet);
- if (p->dead)
+ if (p->dead) {
+#ifdef HAVE_CREDS
+ pa_cmsg_ancil_data_close_fds(ancil_data);
+#endif
return;
+ }
if (!(i = pa_flist_pop(PA_STATIC_FLIST_GET(items))))
i = pa_xnew(struct item_info, 1);
@@ -536,6 +615,7 @@ static void prepare_next_write_item(pa_pstream *p) {
flags = (uint32_t) (p->write.current->seek_mode & PA_FLAG_SEEKMASK);
if (p->use_shm) {
+ pa_mem_type_t type;
uint32_t block_id, shm_id;
size_t offset, length;
uint32_t *shm_info = (uint32_t *) &p->write.minibuf[PA_PSTREAM_DESCRIPTOR_SIZE];
@@ -550,29 +630,50 @@ static void prepare_next_write_item(pa_pstream *p) {
if (pa_memexport_put(current_export,
p->write.current->chunk.memblock,
+ &type,
&block_id,
&shm_id,
&offset,
&length) >= 0) {
- flags |= PA_FLAG_SHMDATA;
- if (pa_mempool_is_remote_writable(current_pool))
- flags |= PA_FLAG_SHMWRITABLE;
- send_payload = false;
+ if (type == PA_MEM_TYPE_SHARED_POSIX)
+ send_payload = false;
+
+ if (type == PA_MEM_TYPE_SHARED_MEMFD && p->use_memfd) {
+ if (pa_idxset_get_by_data(p->registered_memfd_ids, PA_UINT32_TO_PTR(shm_id), NULL)) {
+ flags |= PA_FLAG_SHMDATA_MEMFD_BLOCK;
+ send_payload = false;
+ } else {
+ if (pa_log_ratelimit(PA_LOG_ERROR)) {
+ pa_log("Cannot send block reference with non-registered memfd ID = %u", shm_id);
+ pa_log("Fallig back to copying full block data over socket");
+ }
+ }
+ }
+
+ if (send_payload) {
+ pa_assert_se(pa_memexport_process_release(current_export, block_id) == 0);
+ } else {
+ flags |= PA_FLAG_SHMDATA;
+ if (pa_mempool_is_remote_writable(current_pool))
+ flags |= PA_FLAG_SHMWRITABLE;
- shm_info[PA_PSTREAM_SHM_BLOCKID] = htonl(block_id);
- shm_info[PA_PSTREAM_SHM_SHMID] = htonl(shm_id);
- shm_info[PA_PSTREAM_SHM_INDEX] = htonl((uint32_t) (offset + p->write.current->chunk.index));
- shm_info[PA_PSTREAM_SHM_LENGTH] = htonl((uint32_t) p->write.current->chunk.length);
+ shm_info[PA_PSTREAM_SHM_BLOCKID] = htonl(block_id);
+ shm_info[PA_PSTREAM_SHM_SHMID] = htonl(shm_id);
+ shm_info[PA_PSTREAM_SHM_INDEX] = htonl((uint32_t) (offset + p->write.current->chunk.index));
+ shm_info[PA_PSTREAM_SHM_LENGTH] = htonl((uint32_t) p->write.current->chunk.length);
- p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(shm_size);
- p->write.minibuf_validsize = PA_PSTREAM_DESCRIPTOR_SIZE + shm_size;
+ p->write.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(shm_size);
+ p->write.minibuf_validsize = PA_PSTREAM_DESCRIPTOR_SIZE + shm_size;
+ }
}
/* else */
+/* FIXME: Avoid memexport slot leaks. Call pa_memexport_process_release() */
/* pa_log_warn("Failed to export memory block."); */
if (current_export != p->export)
pa_memexport_free(current_export);
+ pa_mempool_unref(current_pool);
}
if (send_payload) {
@@ -586,7 +687,7 @@ static void prepare_next_write_item(pa_pstream *p) {
#ifdef HAVE_CREDS
if ((p->send_ancil_data_now = p->write.current->with_ancil_data))
- p->write_ancil_data = p->write.current->ancil_data;
+ p->write_ancil_data = &p->write.current->ancil_data;
#endif
}
@@ -646,14 +747,16 @@ static int do_write(pa_pstream *p) {
#ifdef HAVE_CREDS
if (p->send_ancil_data_now) {
- if (p->write_ancil_data.creds_valid) {
- pa_assert(p->write_ancil_data.nfd == 0);
- if ((r = pa_iochannel_write_with_creds(p->io, d, l, &p->write_ancil_data.creds)) < 0)
+ if (p->write_ancil_data->creds_valid) {
+ pa_assert(p->write_ancil_data->nfd == 0);
+ if ((r = pa_iochannel_write_with_creds(p->io, d, l, &p->write_ancil_data->creds)) < 0)
goto fail;
}
else
- if ((r = pa_iochannel_write_with_fds(p->io, d, l, p->write_ancil_data.nfd, p->write_ancil_data.fds)) < 0)
+ if ((r = pa_iochannel_write_with_fds(p->io, d, l, p->write_ancil_data->nfd, p->write_ancil_data->fds)) < 0)
goto fail;
+
+ pa_cmsg_ancil_data_close_fds(p->write_ancil_data);
p->send_ancil_data_now = false;
} else
#endif
@@ -684,6 +787,10 @@ static int do_write(pa_pstream *p) {
return (size_t) r == l ? 1 : 0;
fail:
+#ifdef HAVE_CREDS
+ if (p->send_ancil_data_now)
+ pa_cmsg_ancil_data_close_fds(p->write_ancil_data);
+#endif
if (release_memblock)
pa_memblock_release(release_memblock);
@@ -764,6 +871,7 @@ static int do_read(pa_pstream *p, struct pstream_read *re) {
pa_assert(b.nfd <= MAX_ANCIL_DATA_FDS);
p->read_ancil_data.nfd = b.nfd;
memcpy(p->read_ancil_data.fds, b.fds, sizeof(int) * b.nfd);
+ p->read_ancil_data.close_fds_on_cleanup = b.close_fds_on_cleanup;
}
}
#else
@@ -840,7 +948,7 @@ static int do_read(pa_pstream *p, struct pstream_read *re) {
return -1;
}
- if ((flags & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA) {
+ if (((flags & PA_FLAG_SHMMASK) & PA_FLAG_SHMDATA) != 0) {
if (length != sizeof(re->shm_info)) {
pa_log_warn("Received SHM memblock frame with invalid frame length.");
@@ -883,18 +991,28 @@ static int do_read(pa_pstream *p, struct pstream_read *re) {
pa_packet_unref(re->packet);
} else {
- pa_memblock *b;
+ pa_memblock *b = NULL;
uint32_t flags = ntohl(re->descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]);
- pa_assert((flags & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA);
+ uint32_t shm_id = ntohl(re->shm_info[PA_PSTREAM_SHM_SHMID]);
+ pa_mem_type_t type = (flags & PA_FLAG_SHMDATA_MEMFD_BLOCK) ?
+ PA_MEM_TYPE_SHARED_MEMFD : PA_MEM_TYPE_SHARED_POSIX;
+ pa_assert(((flags & PA_FLAG_SHMMASK) & PA_FLAG_SHMDATA) != 0);
pa_assert(p->import);
- if (!(b = pa_memimport_get(p->import,
- ntohl(re->shm_info[PA_PSTREAM_SHM_BLOCKID]),
- ntohl(re->shm_info[PA_PSTREAM_SHM_SHMID]),
- ntohl(re->shm_info[PA_PSTREAM_SHM_INDEX]),
- ntohl(re->shm_info[PA_PSTREAM_SHM_LENGTH]),
- !!(flags & PA_FLAG_SHMWRITABLE)))) {
+ if (type == PA_MEM_TYPE_SHARED_MEMFD && p->use_memfd &&
+ !pa_idxset_get_by_data(p->registered_memfd_ids, PA_UINT32_TO_PTR(shm_id), NULL)) {
+
+ if (pa_log_ratelimit(PA_LOG_ERROR))
+ pa_log("Ignoring received block reference with non-registered memfd ID = %u", shm_id);
+
+ } else if (!(b = pa_memimport_get(p->import,
+ type,
+ ntohl(re->shm_info[PA_PSTREAM_SHM_BLOCKID]),
+ shm_id,
+ ntohl(re->shm_info[PA_PSTREAM_SHM_INDEX]),
+ ntohl(re->shm_info[PA_PSTREAM_SHM_LENGTH]),
+ !!(flags & PA_FLAG_SHMWRITABLE)))) {
if (pa_log_ratelimit(PA_LOG_DEBUG))
pa_log_debug("Failed to import memory block.");
@@ -937,6 +1055,13 @@ frame_done:
re->data = NULL;
#ifdef HAVE_CREDS
+ /* FIXME: Close received ancillary data fds if the pstream's
+ * receive_packet_callback did not do so.
+ *
+ * Malicious clients can attach fds to unknown commands, or attach them
+ * to commands that does not expect fds. By doing so, server will reach
+ * its open fd limit and future clients' SHM transfers will always fail.
+ */
p->read_ancil_data.creds_valid = false;
p->read_ancil_data.nfd = 0;
#endif
@@ -1085,6 +1210,18 @@ void pa_pstream_enable_shm(pa_pstream *p, bool enable) {
}
}
+void pa_pstream_enable_memfd(pa_pstream *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+ pa_assert(p->use_shm);
+
+ p->use_memfd = true;
+
+ if (!p->registered_memfd_ids) {
+ p->registered_memfd_ids = pa_idxset_new(NULL, NULL);
+ }
+}
+
bool pa_pstream_get_shm(pa_pstream *p) {
pa_assert(p);
pa_assert(PA_REFCNT_VALUE(p) > 0);
@@ -1092,6 +1229,13 @@ bool pa_pstream_get_shm(pa_pstream *p) {
return p->use_shm;
}
+bool pa_pstream_get_memfd(pa_pstream *p) {
+ pa_assert(p);
+ pa_assert(PA_REFCNT_VALUE(p) > 0);
+
+ return p->use_memfd;
+}
+
void pa_pstream_set_srbchannel(pa_pstream *p, pa_srbchannel *srb) {
pa_assert(p);
pa_assert(PA_REFCNT_VALUE(p) > 0 || srb == NULL);
diff --git a/src/pulsecore/pstream.h b/src/pulsecore/pstream.h
index f4e1462..2bff270 100644
--- a/src/pulsecore/pstream.h
+++ b/src/pulsecore/pstream.h
@@ -36,7 +36,7 @@
typedef struct pa_pstream pa_pstream;
-typedef void (*pa_pstream_packet_cb_t)(pa_pstream *p, pa_packet *packet, const pa_cmsg_ancil_data *ancil_data, void *userdata);
+typedef void (*pa_pstream_packet_cb_t)(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata);
typedef void (*pa_pstream_memblock_cb_t)(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata);
typedef void (*pa_pstream_notify_cb_t)(pa_pstream *p, void *userdata);
typedef void (*pa_pstream_block_id_cb_t)(pa_pstream *p, uint32_t block_id, void *userdata);
@@ -48,7 +48,9 @@ void pa_pstream_unref(pa_pstream*p);
void pa_pstream_unlink(pa_pstream *p);
-void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, const pa_cmsg_ancil_data *ancil_data);
+int pa_pstream_attach_memfd_shmid(pa_pstream *p, unsigned shm_id, int memfd_fd);
+
+void pa_pstream_send_packet(pa_pstream*p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data);
void pa_pstream_send_memblock(pa_pstream*p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk);
void pa_pstream_send_release(pa_pstream *p, uint32_t block_id);
void pa_pstream_send_revoke(pa_pstream *p, uint32_t block_id);
@@ -63,7 +65,9 @@ void pa_pstream_set_revoke_callback(pa_pstream *p, pa_pstream_block_id_cb_t cb,
bool pa_pstream_is_pending(pa_pstream *p);
void pa_pstream_enable_shm(pa_pstream *p, bool enable);
+void pa_pstream_enable_memfd(pa_pstream *p);
bool pa_pstream_get_shm(pa_pstream *p);
+bool pa_pstream_get_memfd(pa_pstream *p);
/* Enables shared ringbuffer channel. Note that the srbchannel is now owned by the pstream.
Setting srb to NULL will free any existing srbchannel. */
diff --git a/src/pulsecore/shm.c b/src/pulsecore/shm.c
index d613168..bcf7182 100644
--- a/src/pulsecore/shm.c
+++ b/src/pulsecore/shm.c
@@ -45,12 +45,14 @@
#include <pulse/xmalloc.h>
#include <pulse/gccmacro.h>
+#include <pulsecore/memfd-wrappers.h>
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include <pulsecore/random.h>
#include <pulsecore/core-util.h>
#include <pulsecore/macro.h>
#include <pulsecore/atomic.h>
+#include <pulsecore/mem.h>
#include "shm.h"
@@ -91,7 +93,12 @@ struct shm_marker {
uint64_t _reserved4;
} PA_GCC_PACKED;
-#define SHM_MARKER_SIZE PA_ALIGN(sizeof(struct shm_marker))
+static inline size_t shm_marker_size(pa_shm *m) {
+ if (m->type == PA_MEM_TYPE_SHARED_POSIX)
+ return PA_ALIGN(sizeof(struct shm_marker));
+
+ return 0;
+}
#ifdef HAVE_SHM_OPEN
static char *segment_name(char *fn, size_t l, unsigned id) {
@@ -100,106 +107,153 @@ static char *segment_name(char *fn, size_t l, unsigned id) {
}
#endif
-int pa_shm_create_rw(pa_shm *m, size_t size, bool shared, mode_t mode) {
-#ifdef HAVE_SHM_OPEN
- char fn[32];
- int fd = -1;
-#endif
-
+static int privatemem_create(pa_shm *m, size_t size) {
pa_assert(m);
pa_assert(size > 0);
- pa_assert(size <= MAX_SHM_SIZE);
- pa_assert(!(mode & ~0777));
- pa_assert(mode >= 0600);
-
- /* Each time we create a new SHM area, let's first drop all stale
- * ones */
- pa_shm_cleanup();
- /* Round up to make it page aligned */
- size = PA_PAGE_ALIGN(size);
-
- if (!shared) {
- m->id = 0;
- m->size = size;
+ m->type = PA_MEM_TYPE_PRIVATE;
+ m->id = 0;
+ m->size = size;
+ m->do_unlink = false;
+ m->fd = -1;
#ifdef MAP_ANONYMOUS
- if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) {
- pa_log("mmap() failed: %s", pa_cstrerror(errno));
- goto fail;
- }
+ if ((m->ptr = mmap(NULL, m->size, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, (off_t) 0)) == MAP_FAILED) {
+ pa_log("mmap() failed: %s", pa_cstrerror(errno));
+ return -1;
+ }
#elif defined(HAVE_POSIX_MEMALIGN)
- {
- int r;
+ {
+ int r;
- if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {
- pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
- goto fail;
- }
+ if ((r = posix_memalign(&m->ptr, PA_PAGE_SIZE, size)) < 0) {
+ pa_log("posix_memalign() failed: %s", pa_cstrerror(r));
+ return r;
}
+ }
#else
- m->ptr = pa_xmalloc(m->size);
+ m->ptr = pa_xmalloc(m->size);
#endif
- m->do_unlink = false;
+ return 0;
+}
- } else {
-#ifdef HAVE_SHM_OPEN
- struct shm_marker *marker;
+static int sharedmem_create(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode) {
+#if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD)
+ char fn[32];
+ int fd = -1;
+ struct shm_marker *marker;
+ bool do_unlink = false;
- pa_random(&m->id, sizeof(m->id));
+ /* Each time we create a new SHM area, let's first drop all stale
+ * ones */
+ pa_shm_cleanup();
+
+ pa_random(&m->id, sizeof(m->id));
+
+ switch (type) {
+#ifdef HAVE_SHM_OPEN
+ case PA_MEM_TYPE_SHARED_POSIX:
segment_name(fn, sizeof(fn), m->id);
+ fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode);
+ do_unlink = true;
+ break;
+#endif
+#ifdef HAVE_MEMFD
+ case PA_MEM_TYPE_SHARED_MEMFD:
+ fd = memfd_create("pulseaudio", MFD_ALLOW_SEALING);
+ break;
+#endif
+ default:
+ goto fail;
+ }
- if ((fd = shm_open(fn, O_RDWR|O_CREAT|O_EXCL, mode)) < 0) {
- pa_log("shm_open() failed: %s", pa_cstrerror(errno));
- goto fail;
- }
+ if (fd < 0) {
+ pa_log("%s open() failed: %s", pa_mem_type_to_string(type), pa_cstrerror(errno));
+ goto fail;
+ }
- m->size = size + SHM_MARKER_SIZE;
+ m->type = type;
+ m->size = size + shm_marker_size(m);
+ m->do_unlink = do_unlink;
- if (ftruncate(fd, (off_t) m->size) < 0) {
- pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
- goto fail;
- }
+ if (ftruncate(fd, (off_t) m->size) < 0) {
+ pa_log("ftruncate() failed: %s", pa_cstrerror(errno));
+ goto fail;
+ }
#ifndef MAP_NORESERVE
#define MAP_NORESERVE 0
#endif
- if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (off_t) 0)) == MAP_FAILED) {
- pa_log("mmap() failed: %s", pa_cstrerror(errno));
- goto fail;
- }
+ if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (off_t) 0)) == MAP_FAILED) {
+ pa_log("mmap() failed: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+ if (type == PA_MEM_TYPE_SHARED_POSIX) {
/* We store our PID at the end of the shm block, so that we
* can check for dead shm segments later */
- marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - SHM_MARKER_SIZE);
+ marker = (struct shm_marker*) ((uint8_t*) m->ptr + m->size - shm_marker_size(m));
pa_atomic_store(&marker->pid, (int) getpid());
pa_atomic_store(&marker->marker, SHM_MARKER);
+ }
+ /* For memfds, we keep the fd open until we pass it
+ * to the other PA endpoint over unix domain socket. */
+ if (type == PA_MEM_TYPE_SHARED_MEMFD)
+ m->fd = fd;
+ else {
pa_assert_se(pa_close(fd) == 0);
- m->do_unlink = true;
-#else
- goto fail;
-#endif
+ m->fd = -1;
}
- m->shared = shared;
-
return 0;
fail:
-
-#ifdef HAVE_SHM_OPEN
if (fd >= 0) {
- shm_unlink(fn);
+#ifdef HAVE_SHM_OPEN
+ if (type == PA_MEM_TYPE_SHARED_POSIX)
+ shm_unlink(fn);
+#endif
pa_close(fd);
}
-#endif
+#endif /* defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) */
return -1;
}
+int pa_shm_create_rw(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode) {
+ pa_assert(m);
+ pa_assert(size > 0);
+ pa_assert(size <= MAX_SHM_SIZE);
+ pa_assert(!(mode & ~0777));
+ pa_assert(mode >= 0600);
+
+ /* Round up to make it page aligned */
+ size = PA_PAGE_ALIGN(size);
+
+ if (type == PA_MEM_TYPE_PRIVATE)
+ return privatemem_create(m, size);
+
+ return sharedmem_create(m, type, size, mode);
+}
+
+static void privatemem_free(pa_shm *m) {
+ pa_assert(m);
+ pa_assert(m->ptr);
+ pa_assert(m->size > 0);
+
+#ifdef MAP_ANONYMOUS
+ if (munmap(m->ptr, m->size) < 0)
+ pa_log("munmap() failed: %s", pa_cstrerror(errno));
+#elif defined(HAVE_POSIX_MEMALIGN)
+ free(m->ptr);
+#else
+ pa_xfree(m->ptr);
+#endif
+}
+
void pa_shm_free(pa_shm *m) {
pa_assert(m);
pa_assert(m->ptr);
@@ -209,34 +263,35 @@ void pa_shm_free(pa_shm *m) {
pa_assert(m->ptr != MAP_FAILED);
#endif
- if (!m->shared) {
-#ifdef MAP_ANONYMOUS
- if (munmap(m->ptr, m->size) < 0)
- pa_log("munmap() failed: %s", pa_cstrerror(errno));
-#elif defined(HAVE_POSIX_MEMALIGN)
- free(m->ptr);
-#else
- pa_xfree(m->ptr);
-#endif
- } else {
-#ifdef HAVE_SHM_OPEN
- if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0)
- pa_log("munmap() failed: %s", pa_cstrerror(errno));
+ if (m->type == PA_MEM_TYPE_PRIVATE) {
+ privatemem_free(m);
+ goto finish;
+ }
- if (m->do_unlink) {
- char fn[32];
+#if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD)
+ if (munmap(m->ptr, PA_PAGE_ALIGN(m->size)) < 0)
+ pa_log("munmap() failed: %s", pa_cstrerror(errno));
- segment_name(fn, sizeof(fn), m->id);
+#ifdef HAVE_SHM_OPEN
+ if (m->type == PA_MEM_TYPE_SHARED_POSIX && m->do_unlink) {
+ char fn[32];
+
+ segment_name(fn, sizeof(fn), m->id);
+ if (shm_unlink(fn) < 0)
+ pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
+ }
+#endif
+#ifdef HAVE_MEMFD
+ if (m->type == PA_MEM_TYPE_SHARED_MEMFD && m->fd != -1)
+ pa_assert_se(pa_close(m->fd) == 0);
+#endif
- if (shm_unlink(fn) < 0)
- pa_log(" shm_unlink(%s) failed: %s", fn, pa_cstrerror(errno));
- }
#else
- /* We shouldn't be here without shm support */
- pa_assert_not_reached();
+ /* We shouldn't be here without shm or memfd support */
+ pa_assert_not_reached();
#endif
- }
+finish:
pa_zero(*m);
}
@@ -286,9 +341,8 @@ void pa_shm_punch(pa_shm *m, size_t offset, size_t size) {
#endif
}
-#ifdef HAVE_SHM_OPEN
-
-static int shm_attach(pa_shm *m, unsigned id, bool writable, bool for_cleanup) {
+static int shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable, bool for_cleanup) {
+#if defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD)
char fn[32];
int fd = -1;
int prot;
@@ -296,11 +350,25 @@ static int shm_attach(pa_shm *m, unsigned id, bool writable, bool for_cleanup) {
pa_assert(m);
- segment_name(fn, sizeof(fn), m->id = id);
-
- if ((fd = shm_open(fn, writable ? O_RDWR : O_RDONLY, 0)) < 0) {
- if ((errno != EACCES && errno != ENOENT) || !for_cleanup)
- pa_log("shm_open() failed: %s", pa_cstrerror(errno));
+ switch (type) {
+#ifdef HAVE_SHM_OPEN
+ case PA_MEM_TYPE_SHARED_POSIX:
+ pa_assert(memfd_fd == -1);
+ segment_name(fn, sizeof(fn), id);
+ if ((fd = shm_open(fn, writable ? O_RDWR : O_RDONLY, 0)) < 0) {
+ if ((errno != EACCES && errno != ENOENT) || !for_cleanup)
+ pa_log("shm_open() failed: %s", pa_cstrerror(errno));
+ goto fail;
+ }
+ break;
+#endif
+#ifdef HAVE_MEMFD
+ case PA_MEM_TYPE_SHARED_MEMFD:
+ pa_assert(memfd_fd != -1);
+ fd = memfd_fd;
+ break;
+#endif
+ default:
goto fail;
}
@@ -310,45 +378,48 @@ static int shm_attach(pa_shm *m, unsigned id, bool writable, bool for_cleanup) {
}
if (st.st_size <= 0 ||
- st.st_size > (off_t) (MAX_SHM_SIZE+SHM_MARKER_SIZE) ||
+ st.st_size > (off_t) MAX_SHM_SIZE + (off_t) shm_marker_size(m) ||
PA_ALIGN((size_t) st.st_size) != (size_t) st.st_size) {
pa_log("Invalid shared memory segment size");
goto fail;
}
- m->size = (size_t) st.st_size;
-
prot = writable ? PROT_READ | PROT_WRITE : PROT_READ;
- if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(m->size), prot, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
+ if ((m->ptr = mmap(NULL, PA_PAGE_ALIGN(st.st_size), prot, MAP_SHARED, fd, (off_t) 0)) == MAP_FAILED) {
pa_log("mmap() failed: %s", pa_cstrerror(errno));
goto fail;
}
- m->do_unlink = false;
- m->shared = true;
+ /* In case of attaching to memfd areas, _the caller_ maintains
+ * ownership of the passed fd and has the sole responsibility
+ * of closing it down.. For other types, we're the code path
+ * which created the fd in the first place and we're thus the
+ * ones responsible for closing it down */
+ if (type != PA_MEM_TYPE_SHARED_MEMFD)
+ pa_assert_se(pa_close(fd) == 0);
- pa_assert_se(pa_close(fd) == 0);
+ m->type = type;
+ m->id = id;
+ m->size = (size_t) st.st_size;
+ m->do_unlink = false;
+ m->fd = -1;
return 0;
fail:
- if (fd >= 0)
+ /* In case of memfds, caller maintains fd ownership */
+ if (fd >= 0 && type != PA_MEM_TYPE_SHARED_MEMFD)
pa_close(fd);
- return -1;
-}
-
-int pa_shm_attach(pa_shm *m, unsigned id, bool writable) {
- return shm_attach(m, id, writable, false);
-}
+#endif /* defined(HAVE_SHM_OPEN) || defined(HAVE_MEMFD) */
-#else /* HAVE_SHM_OPEN */
-
-int pa_shm_attach(pa_shm *m, unsigned id, bool writable) {
return -1;
}
-#endif /* HAVE_SHM_OPEN */
+/* Caller owns passed @memfd_fd and must close it down when appropriate. */
+int pa_shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable) {
+ return shm_attach(m, type, id, memfd_fd, writable, false);
+}
int pa_shm_cleanup(void) {
@@ -379,15 +450,15 @@ int pa_shm_cleanup(void) {
if (pa_atou(de->d_name + SHM_ID_LEN, &id) < 0)
continue;
- if (shm_attach(&seg, id, false, true) < 0)
+ if (shm_attach(&seg, PA_MEM_TYPE_SHARED_POSIX, id, -1, false, true) < 0)
continue;
- if (seg.size < SHM_MARKER_SIZE) {
+ if (seg.size < shm_marker_size(&seg)) {
pa_shm_free(&seg);
continue;
}
- m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - SHM_MARKER_SIZE);
+ m = (struct shm_marker*) ((uint8_t*) seg.ptr + seg.size - shm_marker_size(&seg));
if (pa_atomic_load(&m->marker) != SHM_MARKER) {
pa_shm_free(&seg);
diff --git a/src/pulsecore/shm.h b/src/pulsecore/shm.h
index d438961..67a2114 100644
--- a/src/pulsecore/shm.h
+++ b/src/pulsecore/shm.h
@@ -23,17 +23,34 @@
#include <sys/types.h>
#include <pulsecore/macro.h>
+#include <pulsecore/mem.h>
typedef struct pa_shm {
+ pa_mem_type_t type;
unsigned id;
void *ptr;
size_t size;
+
+ /* Only for type = PA_MEM_TYPE_SHARED_POSIX */
bool do_unlink:1;
- bool shared:1;
+
+ /* Only for type = PA_MEM_TYPE_SHARED_MEMFD
+ *
+ * To avoid fd leaks, we keep this fd open only until we pass it
+ * to the other PA endpoint over unix domain socket.
+ *
+ * When we don't have ownership for the memfd fd in question (e.g.
+ * pa_shm_attach()), or the file descriptor has now been closed,
+ * this is set to -1.
+ *
+ * For the special case of a global mempool, we keep this fd
+ * always open. Check comments on top of pa_mempool_new() for
+ * rationale. */
+ int fd;
} pa_shm;
-int pa_shm_create_rw(pa_shm *m, size_t size, bool shared, mode_t mode);
-int pa_shm_attach(pa_shm *m, unsigned id, bool writable);
+int pa_shm_create_rw(pa_shm *m, pa_mem_type_t type, size_t size, mode_t mode);
+int pa_shm_attach(pa_shm *m, pa_mem_type_t type, unsigned id, int memfd_fd, bool writable);
void pa_shm_punch(pa_shm *m, size_t offset, size_t size);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 8ec63b5..361b445 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1426,17 +1426,124 @@ void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) {
pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MUTE_CHANGED], i);
}
+void pa_sink_input_set_property(pa_sink_input *i, const char *key, const char *value) {
+ char *old_value = NULL;
+ const char *new_value;
+
+ pa_assert(i);
+ pa_assert(key);
+
+ if (pa_proplist_contains(i->proplist, key)) {
+ old_value = pa_xstrdup(pa_proplist_gets(i->proplist, key));
+ if (old_value) {
+ if (pa_streq(value, old_value))
+ goto finish;
+ } else
+ old_value = pa_xstrdup("(data)");
+ } else {
+ if (!value)
+ goto finish;
+
+ old_value = pa_xstrdup("(unset)");
+ }
+
+ if (value) {
+ pa_proplist_sets(i->proplist, key, value);
+ new_value = value;
+ } else {
+ pa_proplist_unset(i->proplist, key);
+ new_value = "(unset)";
+ }
+
+ if (PA_SINK_INPUT_IS_LINKED(i->state)) {
+ pa_log_debug("Sink input %u: proplist[%s]: %s -> %s", i->index, key, old_value, new_value);
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ }
+
+finish:
+ pa_xfree(old_value);
+}
+
+void pa_sink_input_set_property_arbitrary(pa_sink_input *i, const char *key, const uint8_t *value, size_t nbytes) {
+ const uint8_t *old_value;
+ size_t old_nbytes;
+ const char *old_value_str;
+ const char *new_value_str;
+
+ pa_assert(i);
+ pa_assert(key);
+
+ if (pa_proplist_get(i->proplist, key, (const void **) &old_value, &old_nbytes) >= 0) {
+ if (value && nbytes == old_nbytes && !memcmp(value, old_value, nbytes))
+ return;
+
+ old_value_str = "(data)";
+
+ } else {
+ if (!value)
+ return;
+
+ old_value_str = "(unset)";
+ }
+
+ if (value) {
+ pa_proplist_set(i->proplist, key, value, nbytes);
+ new_value_str = "(data)";
+ } else {
+ pa_proplist_unset(i->proplist, key);
+ new_value_str = "(unset)";
+ }
+
+ if (PA_SINK_INPUT_IS_LINKED(i->state)) {
+ pa_log_debug("Sink input %u: proplist[%s]: %s -> %s", i->index, key, old_value_str, new_value_str);
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
+ pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT | PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ }
+}
+
/* Called from main thread */
void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p) {
+ void *state;
+ const char *key;
+ const uint8_t *value;
+ size_t nbytes;
+
pa_sink_input_assert_ref(i);
+ pa_assert(p);
pa_assert_ctl_context();
- if (p)
- pa_proplist_update(i->proplist, mode, p);
+ switch (mode) {
+ case PA_UPDATE_SET: {
+ /* Delete everything that is not in p. */
+ for (state = NULL; (key = pa_proplist_iterate(i->proplist, &state));) {
+ if (!pa_proplist_contains(p, key))
+ pa_sink_input_set_property(i, key, NULL);
+ }
- if (PA_SINK_INPUT_IS_LINKED(i->state)) {
- pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
+ /* Fall through. */
+ }
+
+ case PA_UPDATE_REPLACE: {
+ for (state = NULL; (key = pa_proplist_iterate(p, &state));) {
+ pa_proplist_get(p, key, (const void **) &value, &nbytes);
+ pa_sink_input_set_property_arbitrary(i, key, value, nbytes);
+ }
+
+ break;
+ }
+
+ case PA_UPDATE_MERGE: {
+ for (state = NULL; (key = pa_proplist_iterate(p, &state));) {
+ if (pa_proplist_contains(i->proplist, key))
+ continue;
+
+ pa_proplist_get(p, key, (const void **) &value, &nbytes);
+ pa_sink_input_set_property_arbitrary(i, key, value, nbytes);
+ }
+
+ break;
+ }
}
}
@@ -1468,31 +1575,6 @@ int pa_sink_input_set_rate(pa_sink_input *i, uint32_t rate) {
}
/* Called from main context */
-void pa_sink_input_set_name(pa_sink_input *i, const char *name) {
- const char *old;
- pa_sink_input_assert_ref(i);
- pa_assert_ctl_context();
-
- if (!name && !pa_proplist_contains(i->proplist, PA_PROP_MEDIA_NAME))
- return;
-
- old = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME);
-
- if (old && name && pa_streq(old, name))
- return;
-
- if (name)
- pa_proplist_sets(i->proplist, PA_PROP_MEDIA_NAME, name);
- else
- pa_proplist_unset(i->proplist, PA_PROP_MEDIA_NAME);
-
- if (PA_SINK_INPUT_IS_LINKED(i->state)) {
- pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED], i);
- pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- }
-}
-
-/* Called from main context */
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i) {
pa_sink_input_assert_ref(i);
pa_assert_ctl_context();
@@ -1538,6 +1620,9 @@ bool pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
if (dest == i->sink)
return true;
+ if (dest->unlink_requested)
+ return false;
+
if (!pa_sink_input_may_move(i))
return false;
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 86deab2..8bbee4e 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -335,8 +335,6 @@ int pa_sink_input_new(
void pa_sink_input_put(pa_sink_input *i);
void pa_sink_input_unlink(pa_sink_input* i);
-void pa_sink_input_set_name(pa_sink_input *i, const char *name);
-
pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec);
/* Request that the specified number of bytes already written out to
@@ -373,6 +371,8 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, bool
void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save);
+void pa_sink_input_set_property(pa_sink_input *i, const char *key, const char *value);
+void pa_sink_input_set_property_arbitrary(pa_sink_input *i, const char *key, const uint8_t *value, size_t nbytes);
void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_proplist *p);
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 0b44fc7..3f1ef72 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -676,9 +676,10 @@ void pa_sink_unlink(pa_sink* s) {
* reversing pa_sink_put(). It also undoes the registrations
* already done in pa_sink_new()! */
- /* All operations here shall be idempotent, i.e. pa_sink_unlink()
- * may be called multiple times on the same sink without bad
- * effects. */
+ if (s->unlink_requested)
+ return;
+
+ s->unlink_requested = true;
linked = PA_SINK_IS_LINKED(s->state);
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 5df109e..b64a666 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -63,6 +63,12 @@ struct pa_sink {
pa_core *core;
pa_sink_state_t state;
+
+ /* Set in the beginning of pa_sink_unlink() before setting the sink state
+ * to UNLINKED. The purpose is to prevent moving streams to a sink that is
+ * about to be removed. */
+ bool unlink_requested;
+
pa_sink_flags_t flags;
pa_suspend_cause_t suspend_cause;
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index c73c548..d74a60e 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -792,14 +792,14 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
pa_volume_memchunk(&qchunk, &o->source->sample_spec, &o->thread_info.soft_volume);
}
- if (!o->thread_info.resampler) {
- if (nvfs) {
- pa_memchunk_make_writable(&qchunk, 0);
- pa_volume_memchunk(&qchunk, &o->thread_info.sample_spec, &o->volume_factor_source);
- }
+ if (nvfs) {
+ pa_memchunk_make_writable(&qchunk, 0);
+ pa_volume_memchunk(&qchunk, &o->source->sample_spec, &o->volume_factor_source);
+ }
+ if (!o->thread_info.resampler)
o->push(o, &qchunk);
- } else {
+ else {
pa_memchunk rchunk;
if (mbs == 0)
@@ -810,14 +810,8 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
pa_resampler_run(o->thread_info.resampler, &qchunk, &rchunk);
- if (rchunk.length > 0) {
- if (nvfs) {
- pa_memchunk_make_writable(&rchunk, 0);
- pa_volume_memchunk(&rchunk, &o->thread_info.sample_spec, &o->volume_factor_source);
- }
-
+ if (rchunk.length > 0)
o->push(o, &rchunk);
- }
if (rchunk.memblock)
pa_memblock_unref(rchunk.memblock);
@@ -1084,17 +1078,124 @@ void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) {
pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MUTE_CHANGED], o);
}
+void pa_source_output_set_property(pa_source_output *o, const char *key, const char *value) {
+ char *old_value = NULL;
+ const char *new_value;
+
+ pa_assert(o);
+ pa_assert(key);
+
+ if (pa_proplist_contains(o->proplist, key)) {
+ old_value = pa_xstrdup(pa_proplist_gets(o->proplist, key));
+ if (old_value) {
+ if (pa_streq(value, old_value))
+ goto finish;
+ } else
+ old_value = pa_xstrdup("(data)");
+ } else {
+ if (!value)
+ goto finish;
+
+ old_value = pa_xstrdup("(unset)");
+ }
+
+ if (value) {
+ pa_proplist_sets(o->proplist, key, value);
+ new_value = value;
+ } else {
+ pa_proplist_unset(o->proplist, key);
+ new_value = "(unset)";
+ }
+
+ if (PA_SINK_INPUT_IS_LINKED(o->state)) {
+ pa_log_debug("Source output %u: proplist[%s]: %s -> %s", o->index, key, old_value, new_value);
+ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
+ pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT | PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+ }
+
+finish:
+ pa_xfree(old_value);
+}
+
+void pa_source_output_set_property_arbitrary(pa_source_output *o, const char *key, const uint8_t *value, size_t nbytes) {
+ const uint8_t *old_value;
+ size_t old_nbytes;
+ const char *old_value_str;
+ const char *new_value_str;
+
+ pa_assert(o);
+ pa_assert(key);
+
+ if (pa_proplist_get(o->proplist, key, (const void **) &old_value, &old_nbytes) >= 0) {
+ if (value && nbytes == old_nbytes && !memcmp(value, old_value, nbytes))
+ return;
+
+ old_value_str = "(data)";
+
+ } else {
+ if (!value)
+ return;
+
+ old_value_str = "(unset)";
+ }
+
+ if (value) {
+ pa_proplist_set(o->proplist, key, value, nbytes);
+ new_value_str = "(data)";
+ } else {
+ pa_proplist_unset(o->proplist, key);
+ new_value_str = "(unset)";
+ }
+
+ if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
+ pa_log_debug("Source output %u: proplist[%s]: %s -> %s", o->index, key, old_value_str, new_value_str);
+ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
+ pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT | PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+ }
+}
+
/* Called from main thread */
void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
+ void *state;
+ const char *key;
+ const uint8_t *value;
+ size_t nbytes;
+
pa_source_output_assert_ref(o);
+ pa_assert(p);
pa_assert_ctl_context();
- if (p)
- pa_proplist_update(o->proplist, mode, p);
+ switch (mode) {
+ case PA_UPDATE_SET: {
+ /* Delete everything that is not in p. */
+ for (state = NULL; (key = pa_proplist_iterate(o->proplist, &state));) {
+ if (!pa_proplist_contains(p, key))
+ pa_source_output_set_property(o, key, NULL);
+ }
+
+ /* Fall through. */
+ }
- if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
- pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+ case PA_UPDATE_REPLACE: {
+ for (state = NULL; (key = pa_proplist_iterate(p, &state));) {
+ pa_proplist_get(p, key, (const void **) &value, &nbytes);
+ pa_source_output_set_property_arbitrary(o, key, value, nbytes);
+ }
+
+ break;
+ }
+
+ case PA_UPDATE_MERGE: {
+ for (state = NULL; (key = pa_proplist_iterate(p, &state));) {
+ if (pa_proplist_contains(o->proplist, key))
+ continue;
+
+ pa_proplist_get(p, key, (const void **) &value, &nbytes);
+ pa_source_output_set_property_arbitrary(o, key, value, nbytes);
+ }
+
+ break;
+ }
}
}
@@ -1126,31 +1227,6 @@ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
}
/* Called from main context */
-void pa_source_output_set_name(pa_source_output *o, const char *name) {
- const char *old;
- pa_assert_ctl_context();
- pa_source_output_assert_ref(o);
-
- if (!name && !pa_proplist_contains(o->proplist, PA_PROP_MEDIA_NAME))
- return;
-
- old = pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME);
-
- if (old && name && pa_streq(old, name))
- return;
-
- if (name)
- pa_proplist_sets(o->proplist, PA_PROP_MEDIA_NAME, name);
- else
- pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME);
-
- if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) {
- pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o);
- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
- }
-}
-
-/* Called from main context */
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o) {
pa_source_output_assert_ref(o);
pa_assert_ctl_context();
@@ -1193,6 +1269,9 @@ bool pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
if (dest == o->source)
return true;
+ if (dest->unlink_requested)
+ return false;
+
if (!pa_source_output_may_move(o))
return false;
@@ -1250,6 +1329,9 @@ int pa_source_output_start_move(pa_source_output *o) {
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
pa_source_update_status(o->source);
+
+ pa_cvolume_remap(&o->volume_factor_source, &o->source->channel_map, &o->channel_map);
+
o->source = NULL;
pa_source_output_unref(o);
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 26be484..7ac44bd 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -291,8 +291,6 @@ int pa_source_output_new(
void pa_source_output_put(pa_source_output *o);
void pa_source_output_unlink(pa_source_output*o);
-void pa_source_output_set_name(pa_source_output *o, const char *name);
-
pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec);
void pa_source_output_cork(pa_source_output *o, bool b);
@@ -316,6 +314,8 @@ pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume,
void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save);
+void pa_source_output_set_property(pa_source_output *o, const char *key, const char *value);
+void pa_source_output_set_property_arbitrary(pa_source_output *o, const char *key, const uint8_t *value, size_t nbytes);
void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p);
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index f4b96ab..98374ae 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -618,6 +618,11 @@ void pa_source_unlink(pa_source *s) {
/* See pa_sink_unlink() for a couple of comments how this function
* works. */
+ if (s->unlink_requested)
+ return;
+
+ s->unlink_requested = true;
+
linked = PA_SOURCE_IS_LINKED(s->state);
if (linked)
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 19fb41b..91e8674 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -64,6 +64,12 @@ struct pa_source {
pa_core *core;
pa_source_state_t state;
+
+ /* Set in the beginning of pa_source_unlink() before setting the source
+ * state to UNLINKED. The purpose is to prevent moving streams to a source
+ * that is about to be removed. */
+ bool unlink_requested;
+
pa_source_flags_t flags;
pa_suspend_cause_t suspend_cause;
diff --git a/src/tests/connect-stress.c b/src/tests/connect-stress.c
index 7c755e9..a243df9 100644
--- a/src/tests/connect-stress.c
+++ b/src/tests/connect-stress.c
@@ -63,7 +63,8 @@ static const pa_sample_spec sample_spec = {
static void context_state_callback(pa_context *c, void *userdata);
-static void connect(const char *name, int *try) {
+/* Note: don't conflict with connect(2) declaration */
+static void _connect(const char *name, int *try) {
int ret;
pa_mainloop_api *api;
@@ -80,14 +81,14 @@ static void connect(const char *name, int *try) {
/* Connect the context */
if (pa_context_connect(context, NULL, 0, NULL) < 0) {
fprintf(stderr, "pa_context_connect() failed.\n");
- fail();
+ ck_abort();
}
ret = pa_threaded_mainloop_start(mainloop);
fail_unless(ret == 0);
}
-static void disconnect(void) {
+static void _disconnect(void) {
int i;
fail_unless(mainloop != NULL);
@@ -144,7 +145,7 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
default:
case PA_STREAM_FAILED:
fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
- fail();
+ ck_abort();
}
}
@@ -190,7 +191,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
case PA_CONTEXT_FAILED:
default:
fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
- fail();
+ ck_abort();
}
}
@@ -201,9 +202,9 @@ START_TEST (connect_stress_test) {
streams[i] = NULL;
for (i = 0; i < NTESTS; i++) {
- connect(bname, &i);
+ _connect(bname, &i);
usleep(rand() % 500000);
- disconnect();
+ _disconnect();
usleep(rand() % 500000);
}
diff --git a/src/tests/cpu-mix-test.c b/src/tests/cpu-mix-test.c
index f3bc0cc..a3189b7 100644
--- a/src/tests/cpu-mix-test.c
+++ b/src/tests/cpu-mix-test.c
@@ -76,7 +76,7 @@ static void run_mix_test(
samples_ref = out_ref + (8 - align);
nsamples = channels * (SAMPLES - (8 - align));
- fail_unless((pool = pa_mempool_new(false, 0)) != NULL, NULL);
+ fail_unless((pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true)) != NULL, NULL);
pa_random(samples0, nsamples * sizeof(int16_t));
c0.memblock = pa_memblock_new_fixed(pool, samples0, nsamples * sizeof(int16_t), false);
@@ -118,7 +118,7 @@ static void run_mix_test(
i,
samples[i], samples_ref[i],
samples0[i], samples1[i]);
- fail();
+ ck_abort();
}
}
}
@@ -142,7 +142,7 @@ static void run_mix_test(
pa_memblock_unref(c0.memblock);
pa_memblock_unref(c1.memblock);
- pa_mempool_free(pool);
+ pa_mempool_unref(pool);
}
START_TEST (mix_special_test) {
diff --git a/src/tests/cpu-remap-test.c b/src/tests/cpu-remap-test.c
index cfe8647..7af5e29 100644
--- a/src/tests/cpu-remap-test.c
+++ b/src/tests/cpu-remap-test.c
@@ -70,7 +70,7 @@ static void run_remap_test_float(
pa_log_debug("Correctness test failed: align=%d", align);
pa_log_debug("%d: %.24f != %.24f\n", i,
out[i], out_ref[i]);
- fail();
+ ck_abort();
}
}
}
@@ -123,7 +123,7 @@ static void run_remap_test_s16(
if (abs(out[i] - out_ref[i]) > 3) {
pa_log_debug("Correctness test failed: align=%d", align);
pa_log_debug("%d: %d != %d\n", i, out[i], out_ref[i]);
- fail();
+ ck_abort();
}
}
}
diff --git a/src/tests/cpu-sconv-test.c b/src/tests/cpu-sconv-test.c
index 2eb51de..3f189d1 100644
--- a/src/tests/cpu-sconv-test.c
+++ b/src/tests/cpu-sconv-test.c
@@ -65,7 +65,7 @@ static void run_conv_test_float_to_s16(
if (abs(samples[i] - samples_ref[i]) > 1) {
pa_log_debug("Correctness test failed: align=%d", align);
pa_log_debug("%d: %04hx != %04hx (%.24f)\n", i, samples[i], samples_ref[i], floats[i]);
- fail();
+ ck_abort();
}
}
}
@@ -115,7 +115,7 @@ static void run_conv_test_s16_to_float(
if (fabsf(floats[i] - floats_ref[i]) > 0.0001f) {
pa_log_debug("Correctness test failed: align=%d", align);
pa_log_debug("%d: %.24f != %.24f (%d)\n", i, floats[i], floats_ref[i], samples[i]);
- fail();
+ ck_abort();
}
}
}
diff --git a/src/tests/cpu-volume-test.c b/src/tests/cpu-volume-test.c
index 01ac2fc..15f0658 100644
--- a/src/tests/cpu-volume-test.c
+++ b/src/tests/cpu-volume-test.c
@@ -78,7 +78,7 @@ static void run_volume_test(
pa_log_debug("Correctness test failed: align=%d, channels=%d", align, channels);
pa_log_debug("%d: %04hx != %04hx (%04hx * %08x)\n", i, samples[i], samples_ref[i],
samples_orig[i], volumes[i % channels]);
- fail();
+ ck_abort();
}
}
}
diff --git a/src/tests/cpulimit-test.c b/src/tests/cpulimit-test.c
index 3a8124f..e01a5b8 100644
--- a/src/tests/cpulimit-test.c
+++ b/src/tests/cpulimit-test.c
@@ -48,7 +48,7 @@ static void func(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata
if ((now - start) >= 30) {
m->quit(m, 1);
fprintf(stderr, "Test failed\n");
- fail();
+ ck_abort();
} else
raise(SIGUSR1);
}
@@ -78,7 +78,7 @@ START_TEST (cpulimit_test) {
if ((now - start) >= 30) {
fprintf(stderr, "Test failed\n");
- fail();
+ ck_abort();
break;
}
}
diff --git a/src/tests/extended-test.c b/src/tests/extended-test.c
index ee766b8..0d08fac 100644
--- a/src/tests/extended-test.c
+++ b/src/tests/extended-test.c
@@ -100,7 +100,7 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
default:
case PA_STREAM_FAILED:
fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
- fail();
+ ck_abort();
}
}
@@ -151,7 +151,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
case PA_CONTEXT_FAILED:
default:
fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
- fail();
+ ck_abort();
}
}
diff --git a/src/tests/get-binary-name-test.c b/src/tests/get-binary-name-test.c
index 4c855bb..cd53bde 100644
--- a/src/tests/get-binary-name-test.c
+++ b/src/tests/get-binary-name-test.c
@@ -39,7 +39,7 @@ START_TEST (getbinaryname_test) {
if (!pa_get_binary_name(exename, allocated)) {
pa_log_error("failed to read binary name");
pa_xfree(exename);
- fail();
+ ck_abort();
}
if (strlen(exename) < allocated - 1) {
diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c
index 7a7bffc..b4cf18c 100644
--- a/src/tests/interpol-test.c
+++ b/src/tests/interpol-test.c
@@ -144,7 +144,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
case PA_CONTEXT_FAILED:
default:
pa_log_error("Context error: %s", pa_strerror(pa_context_errno(c)));
- fail();
+ ck_abort();
}
}
diff --git a/src/tests/lfe-filter-test.c b/src/tests/lfe-filter-test.c
index 389a2b9..ba63895 100644
--- a/src/tests/lfe-filter-test.c
+++ b/src/tests/lfe-filter-test.c
@@ -136,7 +136,7 @@ START_TEST (lfe_filter_test) {
a.format = PA_SAMPLE_S16NE;
lft.ss = &a;
- pa_assert_se(lft.pool = pa_mempool_new(false, 0));
+ pa_assert_se(lft.pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true));
/* We prepare pseudo-random input audio samples for lfe-filter rewind testing*/
ori_sample_ptr = pa_xmalloc(pa_frame_size(lft.ss) * TOTAL_SAMPLES);
@@ -163,7 +163,7 @@ START_TEST (lfe_filter_test) {
pa_lfe_filter_free(lft.lf);
- pa_mempool_free(lft.pool);
+ pa_mempool_unref(lft.pool);
if (!ret)
pa_log_debug("lfe-filter-test: tests for both rewind to block boundary and rewind to middle position of a block passed!");
diff --git a/src/tests/mcalign-test.c b/src/tests/mcalign-test.c
index 0d27dfd..3127ccf 100644
--- a/src/tests/mcalign-test.c
+++ b/src/tests/mcalign-test.c
@@ -36,7 +36,7 @@ int main(int argc, char *argv[]) {
pa_mcalign *a;
pa_memchunk c;
- p = pa_mempool_new(false, 0);
+ p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true);
a = pa_mcalign_new(11);
@@ -100,7 +100,7 @@ int main(int argc, char *argv[]) {
if (c.memblock)
pa_memblock_unref(c.memblock);
- pa_mempool_free(p);
+ pa_mempool_unref(p);
return 0;
}
diff --git a/src/tests/memblock-test.c b/src/tests/memblock-test.c
index 2b51108..d48349f 100644
--- a/src/tests/memblock-test.c
+++ b/src/tests/memblock-test.c
@@ -74,17 +74,18 @@ START_TEST (memblock_test) {
pa_memblock *mb_a, *mb_b, *mb_c;
int r, i;
pa_memblock* blocks[5];
+ pa_mem_type_t mem_type;
uint32_t id, shm_id;
size_t offset, size;
char *x;
const char txt[] = "This is a test!";
- pool_a = pa_mempool_new(true, 0);
+ pool_a = pa_mempool_new(PA_MEM_TYPE_SHARED_POSIX, 0, true);
fail_unless(pool_a != NULL);
- pool_b = pa_mempool_new(true, 0);
+ pool_b = pa_mempool_new(PA_MEM_TYPE_SHARED_POSIX, 0, true);
fail_unless(pool_b != NULL);
- pool_c = pa_mempool_new(true, 0);
+ pool_c = pa_mempool_new(PA_MEM_TYPE_SHARED_POSIX, 0, true);
fail_unless(pool_c != NULL);
pa_mempool_get_shm_id(pool_a, &id_a);
@@ -122,22 +123,22 @@ START_TEST (memblock_test) {
import_c = pa_memimport_new(pool_c, release_cb, (void*) "C");
fail_unless(import_b != NULL);
- r = pa_memexport_put(export_a, mb_a, &id, &shm_id, &offset, &size);
+ r = pa_memexport_put(export_a, mb_a, &mem_type, &id, &shm_id, &offset, &size);
fail_unless(r >= 0);
fail_unless(shm_id == id_a);
pa_log("A: Memory block exported as %u", id);
- mb_b = pa_memimport_get(import_b, id, shm_id, offset, size, false);
+ mb_b = pa_memimport_get(import_b, PA_MEM_TYPE_SHARED_POSIX, id, shm_id, offset, size, false);
fail_unless(mb_b != NULL);
- r = pa_memexport_put(export_b, mb_b, &id, &shm_id, &offset, &size);
+ r = pa_memexport_put(export_b, mb_b, &mem_type, &id, &shm_id, &offset, &size);
fail_unless(r >= 0);
fail_unless(shm_id == id_a || shm_id == id_b);
pa_memblock_unref(mb_b);
pa_log("B: Memory block exported as %u", id);
- mb_c = pa_memimport_get(import_c, id, shm_id, offset, size, false);
+ mb_c = pa_memimport_get(import_c, PA_MEM_TYPE_SHARED_POSIX, id, shm_id, offset, size, false);
fail_unless(mb_c != NULL);
x = pa_memblock_acquire(mb_c);
pa_log_debug("1 data=%s", x);
@@ -169,9 +170,9 @@ START_TEST (memblock_test) {
pa_log("vacuuming done...");
- pa_mempool_free(pool_a);
- pa_mempool_free(pool_b);
- pa_mempool_free(pool_c);
+ pa_mempool_unref(pool_a);
+ pa_mempool_unref(pool_b);
+ pa_mempool_unref(pool_c);
}
END_TEST
diff --git a/src/tests/memblockq-test.c b/src/tests/memblockq-test.c
index eea6cfa..2404ee2 100644
--- a/src/tests/memblockq-test.c
+++ b/src/tests/memblockq-test.c
@@ -108,7 +108,7 @@ START_TEST (memblockq_test) {
pa_log_set_level(PA_LOG_DEBUG);
- p = pa_mempool_new(false, 0);
+ p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true);
silence.memblock = pa_memblock_new_fixed(p, (char*) "__", 2, 1);
fail_unless(silence.memblock != NULL);
@@ -208,7 +208,7 @@ START_TEST (memblockq_test) {
pa_memblock_unref(chunk3.memblock);
pa_memblock_unref(chunk4.memblock);
- pa_mempool_free(p);
+ pa_mempool_unref(p);
}
END_TEST
diff --git a/src/tests/mix-test.c b/src/tests/mix-test.c
index c8af600..972f5d7 100644
--- a/src/tests/mix-test.c
+++ b/src/tests/mix-test.c
@@ -286,7 +286,7 @@ START_TEST (mix_test) {
if (!getenv("MAKE_CHECK"))
pa_log_set_level(PA_LOG_DEBUG);
- fail_unless((pool = pa_mempool_new(false, 0)) != NULL, NULL);
+ fail_unless((pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true)) != NULL, NULL);
a.channels = 1;
a.rate = 44100;
@@ -338,7 +338,7 @@ START_TEST (mix_test) {
pa_memblock_unref(k.memblock);
}
- pa_mempool_free(pool);
+ pa_mempool_unref(pool);
}
END_TEST
diff --git a/src/tests/mult-s16-test.c b/src/tests/mult-s16-test.c
index 7340d85..91740c2 100644
--- a/src/tests/mult-s16-test.c
+++ b/src/tests/mult-s16-test.c
@@ -65,7 +65,7 @@ START_TEST (mult_s16_test) {
if (a != b) {
pa_log_debug("%d: %d != %d", i, a, b);
- fail();
+ ck_abort();
}
}
diff --git a/src/tests/remix-test.c b/src/tests/remix-test.c
index 6feb8dc..e21c109 100644
--- a/src/tests/remix-test.c
+++ b/src/tests/remix-test.c
@@ -51,7 +51,7 @@ int main(int argc, char *argv[]) {
pa_log_set_level(PA_LOG_DEBUG);
- pa_assert_se(pool = pa_mempool_new(false, 0));
+ pa_assert_se(pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true));
for (i = 0; maps[i].channels > 0; i++)
for (j = 0; maps[j].channels > 0; j++) {
@@ -75,7 +75,7 @@ int main(int argc, char *argv[]) {
pa_resampler_free(r);
}
- pa_mempool_free(pool);
+ pa_mempool_unref(pool);
return 0;
}
diff --git a/src/tests/resampler-test.c b/src/tests/resampler-test.c
index 9832a31..40000d5 100644
--- a/src/tests/resampler-test.c
+++ b/src/tests/resampler-test.c
@@ -252,26 +252,26 @@ static pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss) {
}
static void help(const char *argv0) {
- printf(_("%s [options]\n\n"
- "-h, --help Show this help\n"
- "-v, --verbose Print debug messages\n"
- " --from-rate=SAMPLERATE From sample rate in Hz (defaults to 44100)\n"
- " --from-format=SAMPLEFORMAT From sample type (defaults to s16le)\n"
- " --from-channels=CHANNELS From number of channels (defaults to 1)\n"
- " --to-rate=SAMPLERATE To sample rate in Hz (defaults to 44100)\n"
- " --to-format=SAMPLEFORMAT To sample type (defaults to s16le)\n"
- " --to-channels=CHANNELS To number of channels (defaults to 1)\n"
- " --resample-method=METHOD Resample method (defaults to auto)\n"
- " --seconds=SECONDS From stream duration (defaults to 60)\n"
- "\n"
- "If the formats are not specified, the test performs all formats combinations,\n"
- "back and forth.\n"
- "\n"
- "Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, alaw,\n"
- "s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
- "\n"
- "See --dump-resample-methods for possible values of resample methods.\n"),
- argv0);
+ printf("%s [options]\n\n"
+ "-h, --help Show this help\n"
+ "-v, --verbose Print debug messages\n"
+ " --from-rate=SAMPLERATE From sample rate in Hz (defaults to 44100)\n"
+ " --from-format=SAMPLEFORMAT From sample type (defaults to s16le)\n"
+ " --from-channels=CHANNELS From number of channels (defaults to 1)\n"
+ " --to-rate=SAMPLERATE To sample rate in Hz (defaults to 44100)\n"
+ " --to-format=SAMPLEFORMAT To sample type (defaults to s16le)\n"
+ " --to-channels=CHANNELS To number of channels (defaults to 1)\n"
+ " --resample-method=METHOD Resample method (defaults to auto)\n"
+ " --seconds=SECONDS From stream duration (defaults to 60)\n"
+ "\n"
+ "If the formats are not specified, the test performs all formats combinations,\n"
+ "back and forth.\n"
+ "\n"
+ "Sample type must be one of s16le, s16be, u8, float32le, float32be, ulaw, alaw,\n"
+ "s24le, s24be, s24-32le, s24-32be, s32le, s32be (defaults to s16ne)\n"
+ "\n"
+ "See --dump-resample-methods for possible values of resample methods.\n",
+ argv0);
}
enum {
@@ -350,7 +350,7 @@ int main(int argc, char *argv[]) {
break;
case ARG_VERSION:
- printf(_("%s %s\n"), argv[0], PACKAGE_VERSION);
+ printf("%s %s\n", argv[0], PACKAGE_VERSION);
ret = 0;
goto quit;
@@ -404,7 +404,7 @@ int main(int argc, char *argv[]) {
}
ret = 0;
- pa_assert_se(pool = pa_mempool_new(false, 0));
+ pa_assert_se(pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true));
if (!all_formats) {
@@ -473,7 +473,7 @@ int main(int argc, char *argv[]) {
quit:
if (pool)
- pa_mempool_free(pool);
+ pa_mempool_unref(pool);
return ret;
}
diff --git a/src/tests/srbchannel-test.c b/src/tests/srbchannel-test.c
index cd4d397..0e7b0ce 100644
--- a/src/tests/srbchannel-test.c
+++ b/src/tests/srbchannel-test.c
@@ -34,7 +34,7 @@ static unsigned packets_received;
static unsigned packets_checksum;
static size_t packets_length;
-static void packet_received(pa_pstream *p, pa_packet *packet, const pa_cmsg_ancil_data *ancil_data, void *userdata) {
+static void packet_received(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
const uint8_t *pdata;
size_t plen;
unsigned i;
@@ -85,7 +85,7 @@ START_TEST (srbchannel_test) {
int pipefd[4];
pa_mainloop *ml = pa_mainloop_new();
- pa_mempool *mp = pa_mempool_new(true, 0);
+ pa_mempool *mp = pa_mempool_new(PA_MEM_TYPE_SHARED_POSIX, 0, true);
pa_iochannel *io1, *io2;
pa_pstream *p1, *p2;
pa_srbchannel *sr1, *sr2;
@@ -116,7 +116,7 @@ START_TEST (srbchannel_test) {
pa_pstream_unref(p1);
pa_pstream_unref(p2);
- pa_mempool_free(mp);
+ pa_mempool_unref(mp);
pa_mainloop_free(ml);
}
END_TEST
diff --git a/src/tests/sync-playback.c b/src/tests/sync-playback.c
index f06b459..9ef038c 100644
--- a/src/tests/sync-playback.c
+++ b/src/tests/sync-playback.c
@@ -106,7 +106,7 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
default:
case PA_STREAM_FAILED:
fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
- fail();
+ ck_abort();
}
}
@@ -148,7 +148,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
case PA_CONTEXT_FAILED:
default:
fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
- fail();
+ ck_abort();
}
}
diff --git a/src/utils/padsp.c b/src/utils/padsp.c
index 5e336bb..943479b 100644
--- a/src/utils/padsp.c
+++ b/src/utils/padsp.c
@@ -2278,6 +2278,7 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
break;
}
+#if HAVE_DECL_SOUND_PCM_READ_RATE
case SOUND_PCM_READ_RATE:
debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_RATE\n");
@@ -2285,7 +2286,9 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
*(int*) argp = i->sample_spec.rate;
pa_threaded_mainloop_unlock(i->mainloop);
break;
+#endif
+#if HAVE_DECL_SOUND_PCM_READ_CHANNELS
case SOUND_PCM_READ_CHANNELS:
debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_CHANNELS\n");
@@ -2293,7 +2296,9 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
*(int*) argp = i->sample_spec.channels;
pa_threaded_mainloop_unlock(i->mainloop);
break;
+#endif
+#if HAVE_DECL_SOUND_PCM_READ_BITS
case SOUND_PCM_READ_BITS:
debug(DEBUG_LEVEL_NORMAL, __FILE__": SOUND_PCM_READ_BITS\n");
@@ -2301,6 +2306,7 @@ static int dsp_ioctl(fd_info *i, unsigned long request, void*argp, int *_errno)
*(int*) argp = pa_sample_size(&i->sample_spec)*8;
pa_threaded_mainloop_unlock(i->mainloop);
break;
+#endif
case SNDCTL_DSP_GETOPTR: {
count_info *info;