[pulseaudio-commits] 20 commits - configure.ac PROTOCOL src/daemon src/Makefile.am src/map-file src/modules src/pulse src/pulsecore src/tests src/utils

Colin Guthrie colin at kemper.freedesktop.org
Wed Jun 22 15:39:12 PDT 2011


 PROTOCOL                                          |   30 
 configure.ac                                      |   14 
 src/Makefile.am                                   |    9 
 src/daemon/caps.c                                 |    3 
 src/daemon/cmdline.c                              |    2 
 src/daemon/cpulimit.c                             |    3 
 src/daemon/dumpmodules.c                          |    1 
 src/daemon/main.c                                 |    6 
 src/map-file                                      |    7 
 src/modules/alsa/alsa-mixer.c                     |   90 +
 src/modules/alsa/alsa-mixer.h                     |    6 
 src/modules/alsa/alsa-sink.c                      |   14 
 src/modules/alsa/alsa-sink.h                      |    1 
 src/modules/alsa/alsa-source.c                    |  210 +++-
 src/modules/alsa/alsa-util.c                      |    2 
 src/modules/alsa/alsa-util.h                      |    4 
 src/modules/alsa/module-alsa-sink.c               |    1 
 src/modules/alsa/module-alsa-source.c             |   24 
 src/modules/bluetooth/bluetooth-util.c            |    2 
 src/modules/bluetooth/bluetooth-util.h            |    2 
 src/modules/bluetooth/module-bluetooth-device.c   |    5 
 src/modules/bluetooth/module-bluetooth-discover.c |    2 
 src/modules/dbus/iface-card-profile.h             |    1 
 src/modules/dbus/iface-device.c                   |    2 
 src/modules/echo-cancel/adrian-aec.h              |    4 
 src/modules/echo-cancel/adrian.c                  |    4 
 src/modules/echo-cancel/module-echo-cancel.c      |   25 
 src/modules/gconf/module-gconf.c                  |    2 
 src/modules/jack/module-jack-sink.c               |    4 
 src/modules/jack/module-jack-source.c             |    4 
 src/modules/jack/module-jackdbus-detect.c         |    2 
 src/modules/macosx/module-bonjour-publish.c       |    2 
 src/modules/macosx/module-coreaudio-device.c      |    2 
 src/modules/module-always-sink.c                  |    3 
 src/modules/module-augment-properties.c           |    3 
 src/modules/module-card-restore.c                 |  174 ++-
 src/modules/module-cli.c                          |    1 
 src/modules/module-combine-sink.c                 |    2 
 src/modules/module-combine.c                      |    1 
 src/modules/module-console-kit.c                  |    7 
 src/modules/module-cork-music-on-phone.c          |    2 
 src/modules/module-default-device-restore.c       |    2 
 src/modules/module-detect.c                       |    2 
 src/modules/module-device-manager.c               |  347 ++++---
 src/modules/module-device-restore.c               |  551 ++++++++++-
 src/modules/module-equalizer-sink.c               |    4 
 src/modules/module-esound-compat-spawnfd.c        |    1 
 src/modules/module-esound-compat-spawnpid.c       |    2 
 src/modules/module-esound-sink.c                  |    4 
 src/modules/module-filter-apply.c                 |    1 
 src/modules/module-filter-heuristics.c            |    3 
 src/modules/module-hal-detect.c                   |    4 
 src/modules/module-intended-roles.c               |   11 
 src/modules/module-ladspa-sink.c                  |    5 
 src/modules/module-loopback.c                     |    3 
 src/modules/module-null-sink.c                    |    6 
 src/modules/module-null-source.c                  |    6 
 src/modules/module-pipe-sink.c                    |    2 
 src/modules/module-pipe-source.c                  |    2 
 src/modules/module-position-event-sounds.c        |    3 
 src/modules/module-protocol-stub.c                |    2 
 src/modules/module-remap-sink.c                   |    3 
 src/modules/module-rygel-media-server.c           |    3 
 src/modules/module-sine-source.c                  |    6 
 src/modules/module-sine.c                         |    2 
 src/modules/module-solaris.c                      |    5 
 src/modules/module-stream-restore.c               |  484 ++++++----
 src/modules/module-suspend-on-idle.c              |    1 
 src/modules/module-tunnel.c                       |    2 
 src/modules/module-virtual-sink.c                 |    4 
 src/modules/module-virtual-source.c               |   82 -
 src/modules/module-waveout.c                      |    3 
 src/modules/module-zeroconf-discover.c            |    5 
 src/modules/module-zeroconf-publish.c             |    3 
 src/modules/oss/module-oss.c                      |   12 
 src/modules/oss/oss-util.c                        |    1 
 src/modules/raop/module-raop-discover.c           |    5 
 src/modules/raop/module-raop-sink.c               |    7 
 src/modules/raop/raop_client.c                    |    6 
 src/modules/raop/raop_client.h                    |    2 
 src/modules/reserve-monitor.h                     |    1 
 src/modules/rtp/module-rtp-recv.c                 |    4 
 src/modules/rtp/module-rtp-send.c                 |    4 
 src/modules/rtp/rtp.c                             |    1 
 src/modules/rtp/rtsp_client.c                     |    3 
 src/modules/rtp/rtsp_client.h                     |    2 
 src/modules/rtp/sap.c                             |    1 
 src/modules/udev-util.h                           |    2 
 src/modules/x11/module-x11-bell.c                 |    4 
 src/modules/x11/module-x11-cork-request.c         |    2 
 src/modules/x11/module-x11-publish.c              |    5 
 src/modules/x11/module-x11-xsmp.c                 |    5 
 src/pulse/client-conf.c                           |    1 
 src/pulse/context.c                               |   18 
 src/pulse/def.h                                   |   29 
 src/pulse/error.c                                 |    6 
 src/pulse/error.h                                 |    1 
 src/pulse/ext-device-manager.c                    |    1 
 src/pulse/ext-device-manager.h                    |    1 
 src/pulse/ext-device-restore.c                    |  346 +++++++
 src/pulse/ext-device-restore.h                    |  106 ++
 src/pulse/ext-stream-restore.c                    |    1 
 src/pulse/ext-stream-restore.h                    |    1 
 src/pulse/format.h                                |    1 
 src/pulse/glib-mainloop.c                         |    1 
 src/pulse/i18n.h                                  |    1 
 src/pulse/internal.h                              |    6 
 src/pulse/introspect.c                            |  216 +++-
 src/pulse/introspect.h                            |   18 
 src/pulse/mainloop-api.c                          |    1 
 src/pulse/mainloop-api.h                          |    1 
 src/pulse/mainloop-signal.c                       |    2 
 src/pulse/mainloop.c                              |    2 
 src/pulse/proplist.c                              |    1 
 src/pulse/rtclock.h                               |    3 
 src/pulse/sample.c                                |    1 
 src/pulse/sample.h                                |    1 
 src/pulse/scache.c                                |    1 
 src/pulse/simple.c                                |    1 
 src/pulse/stream.c                                |   64 -
 src/pulse/subscribe.c                             |    2 
 src/pulse/thread-mainloop.c                       |    1 
 src/pulse/util.c                                  |    2 
 src/pulse/util.h                                  |    1 
 src/pulse/volume.c                                |    1 
 src/pulsecore/arpa-inet.c                         |    1 
 src/pulsecore/asyncmsgq.c                         |    4 
 src/pulsecore/auth-cookie.c                       |    1 
 src/pulsecore/authkey.c                           |    2 
 src/pulsecore/avahi-wrap.c                        |    1 
 src/pulsecore/cli-command.c                       |    3 
 src/pulsecore/cli-text.c                          |    3 
 src/pulsecore/cli.c                               |    7 
 src/pulsecore/core-error.c                        |    2 
 src/pulsecore/core-error.h                        |    1 
 src/pulsecore/core-scache.c                       |    2 
 src/pulsecore/core-subscribe.c                    |    1 
 src/pulsecore/core.c                              |    4 
 src/pulsecore/core.h                              |    2 
 src/pulsecore/cpu-arm.c                           |    1 
 src/pulsecore/creds.h                             |    4 
 src/pulsecore/database-simple.c                   |    2 
 src/pulsecore/dbus-shared.c                       |    5 
 src/pulsecore/dbus-util.h                         |    4 
 src/pulsecore/dllmain.c                           |    1 
 src/pulsecore/fdsem.c                             |    1 
 src/pulsecore/fdsem.h                             |    1 
 src/pulsecore/flist.c                             |    1 
 src/pulsecore/hashmap.c                           |    2 
 src/pulsecore/hook-list.c                         |    2 
 src/pulsecore/hook-list.h                         |    3 
 src/pulsecore/idxset.c                            |    1 
 src/pulsecore/iochannel.c                         |    1 
 src/pulsecore/ioline.c                            |    1 
 src/pulsecore/ioline.h                            |    3 
 src/pulsecore/lock-autospawn.c                    |    2 
 src/pulsecore/log.c                               |    2 
 src/pulsecore/ltdl-helper.c                       |    1 
 src/pulsecore/macro.h                             |    2 
 src/pulsecore/memblock.c                          |    3 
 src/pulsecore/memblock.h                          |    2 
 src/pulsecore/memchunk.c                          |    1 
 src/pulsecore/modargs.c                           |    4 
 src/pulsecore/modargs.h                           |    2 
 src/pulsecore/modinfo.c                           |    1 
 src/pulsecore/module.c                            |    3 
 src/pulsecore/msgobject.h                         |    2 
 src/pulsecore/mutex-posix.c                       |    2 
 src/pulsecore/native-common.h                     |    4 
 src/pulsecore/object.c                            |    2 
 src/pulsecore/object.h                            |    1 
 src/pulsecore/once.h                              |    1 
 src/pulsecore/parseaddr.c                         |    1 
 src/pulsecore/pdispatch.c                         |    7 
 src/pulsecore/pid.c                               |    1 
 src/pulsecore/play-memblockq.c                    |    3 
 src/pulsecore/play-memchunk.c                     |    5 
 src/pulsecore/prioq.h                             |    2 
 src/pulsecore/protocol-esound.c                   |    8 
 src/pulsecore/protocol-http.c                     |    3 
 src/pulsecore/protocol-native.c                   |  221 +++-
 src/pulsecore/protocol-simple.c                   |    4 
 src/pulsecore/pstream.c                           |    3 
 src/pulsecore/random.c                            |    1 
 src/pulsecore/remap_mmx.c                         |    2 
 src/pulsecore/remap_sse.c                         |    2 
 src/pulsecore/rtpoll.c                            |    1 
 src/pulsecore/sample-util.c                       |    1 
 src/pulsecore/sample-util.h                       |    2 
 src/pulsecore/sconv-s16le.c                       |    2 
 src/pulsecore/sconv.c                             |    1 
 src/pulsecore/sconv.h                             |    1 
 src/pulsecore/sconv_sse.c                         |    1 
 src/pulsecore/shared.c                            |    1 
 src/pulsecore/shmasyncq.h                         |    1 
 src/pulsecore/sink-input.c                        |   14 
 src/pulsecore/sink-input.h                        |    3 
 src/pulsecore/sink.c                              |   32 
 src/pulsecore/sink.h                              |    3 
 src/pulsecore/sndfile-util.c                      |    1 
 src/pulsecore/socket-util.c                       |    4 
 src/pulsecore/sound-file-stream.c                 |    2 
 src/pulsecore/sound-file.c                        |    1 
 src/pulsecore/source-output.c                     |  679 +++++++++++++-
 src/pulsecore/source-output.h                     |   84 +
 src/pulsecore/source.c                            | 1053 ++++++++++++++++++++--
 src/pulsecore/source.h                            |   78 +
 src/pulsecore/svolume_arm.c                       |    3 
 src/pulsecore/svolume_c.c                         |    1 
 src/pulsecore/svolume_mmx.c                       |    3 
 src/pulsecore/svolume_orc.c                       |    1 
 src/pulsecore/svolume_sse.c                       |    3 
 src/pulsecore/tagstruct.h                         |    1 
 src/pulsecore/thread-mq.c                         |    7 
 src/pulsecore/thread-posix.c                      |    2 
 src/pulsecore/thread-win32.c                      |    1 
 src/pulsecore/thread.h                            |    2 
 src/pulsecore/time-smoother.c                     |    1 
 src/pulsecore/tokenizer.c                         |    1 
 src/tests/asyncmsgq-test.c                        |    3 
 src/tests/asyncq-test.c                           |    2 
 src/tests/channelmap-test.c                       |    1 
 src/tests/cpulimit-test.c                         |    1 
 src/tests/extended-test.c                         |    2 
 src/tests/get-binary-name-test.c                  |    1 
 src/tests/interpol-test.c                         |    1 
 src/tests/mainloop-test.c                         |    1 
 src/tests/mcalign-test.c                          |    2 
 src/tests/mix-test.c                              |    1 
 src/tests/once-test.c                             |    2 
 src/tests/pacat-simple.c                          |    1 
 src/tests/parec-simple.c                          |    1 
 src/tests/queue-test.c                            |    3 
 src/tests/remix-test.c                            |    3 
 src/tests/resampler-test.c                        |    1 
 src/tests/rtstutter.c                             |    1 
 src/tests/strlist-test.c                          |    1 
 src/tests/sync-playback.c                         |    2 
 src/tests/thread-mainloop-test.c                  |    1 
 src/tests/voltest.c                               |    2 
 src/utils/pabrowse.c                              |    1 
 src/utils/pacmd.c                                 |    1 
 src/utils/pactl.c                                 |   73 +
 src/utils/pasuspender.c                           |    1 
 src/utils/pax11publish.c                          |    1 
 245 files changed, 4316 insertions(+), 1303 deletions(-)

New commits:
commit c8f0a649cf058019f1046c5fc7f6484c864fc954
Author: Bryan Gleeson <bryangleeson at yahoo.com>
Date:   Fri Jun 10 13:02:20 2011 -0700

    raop: Change socket buffer size handling to avoid playback underruns
    
    When a TCP socket is created the size of the send buffer (SO_SNDBUF) used is
    determined by the OS, using the net.ipv4.tcp_wmem sysctl parameter. Previously
    a call to setsockopt set the buffer size to a value that was too small, and
    that in some cases could result in underruns and choppy playback. This
    setsockopt call has now been removed so that the value determined by the OS
    is used unchanged.
    
    Note that the value used for the send buffer size is the 2nd value in
    net.ipv4.tcp_wmem, e.g. if this is set to "4096 65536 8388608" the send buffer
    size is set to 65536.

diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index 4d2ad6e..cba7af9 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -219,7 +219,6 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
     c->fd = pa_iochannel_get_send_fd(io);
 
     pa_iochannel_set_noclose(io, TRUE);
-    pa_iochannel_socket_set_sndbuf(io, 1024);
     pa_iochannel_free(io);
 
     pa_make_tcp_socket_low_delay(c->fd);

commit dd9265ac78b56c378bc7ca47920be9d9d93392b7
Author: Maarten Bosmans <mkbosmans at gmail.com>
Date:   Mon Jun 13 15:04:33 2011 +0200

    Remove unnecessary #includes

diff --git a/src/daemon/caps.c b/src/daemon/caps.c
index 76b62e0..74ccb1c 100644
--- a/src/daemon/caps.c
+++ b/src/daemon/caps.c
@@ -26,15 +26,12 @@
 
 #include <unistd.h>
 #include <errno.h>
-#include <string.h>
 #include <sys/types.h>
 
 #include <pulse/i18n.h>
 
 #include <pulsecore/macro.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-util.h>
 
 #ifdef HAVE_SYS_CAPABILITY_H
 #include <sys/capability.h>
diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c
index 4854aff..ec37d46 100644
--- a/src/daemon/cmdline.c
+++ b/src/daemon/cmdline.c
@@ -23,11 +23,9 @@
 #include <config.h>
 #endif
 
-#include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <getopt.h>
-#include <sys/stat.h>
 
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
diff --git a/src/daemon/cpulimit.c b/src/daemon/cpulimit.c
index 9e22d7e..3a97297 100644
--- a/src/daemon/cpulimit.c
+++ b/src/daemon/cpulimit.c
@@ -23,11 +23,9 @@
 #include <config.h>
 #endif
 
-#include <pulse/error.h>
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
 
-#include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/core-error.h>
 #include <pulsecore/log.h>
@@ -40,7 +38,6 @@
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
-#include <sys/time.h>
 #include <unistd.h>
 #include <signal.h>
 
diff --git a/src/daemon/dumpmodules.c b/src/daemon/dumpmodules.c
index d0504dc..68ab5bb 100644
--- a/src/daemon/dumpmodules.c
+++ b/src/daemon/dumpmodules.c
@@ -25,7 +25,6 @@
 #endif
 
 #include <string.h>
-#include <getopt.h>
 #include <stdio.h>
 #include <ltdl.h>
 
diff --git a/src/daemon/main.c b/src/daemon/main.c
index 5316656..90ed5ce 100644
--- a/src/daemon/main.c
+++ b/src/daemon/main.c
@@ -33,7 +33,6 @@
 #include <stddef.h>
 #include <ltdl.h>
 #include <limits.h>
-#include <fcntl.h>
 #include <unistd.h>
 #include <locale.h>
 #include <sys/types.h>
@@ -74,7 +73,6 @@
 #include <pulsecore/core-error.h>
 #include <pulsecore/core-rtclock.h>
 #include <pulsecore/core.h>
-#include <pulsecore/memblock.h>
 #include <pulsecore/module.h>
 #include <pulsecore/cli-command.h>
 #include <pulsecore/log.h>
@@ -82,12 +80,8 @@
 #include <pulsecore/sioman.h>
 #include <pulsecore/cli-text.h>
 #include <pulsecore/pid.h>
-#include <pulsecore/namereg.h>
 #include <pulsecore/random.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/mutex.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/once.h>
 #include <pulsecore/shm.h>
 #include <pulsecore/memtrap.h>
 #include <pulsecore/strlist.h>
diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index dada122..348f037 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -25,27 +25,25 @@
 #endif
 
 #include <sys/types.h>
-#include <limits.h>
 #include <asoundlib.h>
+#include <math.h>
 
 #ifdef HAVE_VALGRIND_MEMCHECK_H
 #include <valgrind/memcheck.h>
 #endif
 
+#include <pulse/mainloop-api.h>
 #include <pulse/sample.h>
-#include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
 #include <pulse/util.h>
+#include <pulse/volume.h>
+#include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
 #include <pulse/utf8.h>
 
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/core-util.h>
-#include <pulsecore/atomic.h>
-#include <pulsecore/core-error.h>
-#include <pulsecore/once.h>
-#include <pulsecore/thread.h>
 #include <pulsecore/conf-parser.h>
 #include <pulsecore/strbuf.h>
 
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index e1cf1f8..d92d3e9 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -26,16 +26,12 @@
 #include <asoundlib.h>
 
 #include <pulse/sample.h>
-#include <pulse/volume.h>
 #include <pulse/mainloop-api.h>
 #include <pulse/channelmap.h>
-#include <pulse/proplist.h>
 #include <pulse/volume.h>
 
 #include <pulsecore/llist.h>
 #include <pulsecore/rtpoll.h>
-#include <pulsecore/core.h>
-#include <pulsecore/log.h>
 
 typedef struct pa_alsa_fdlist pa_alsa_fdlist;
 typedef struct pa_alsa_mixer_pdata pa_alsa_mixer_pdata;
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index a042c2d..97492ab 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -35,7 +35,7 @@
 #include <pulse/i18n.h>
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
-#include <pulse/util.h>
+#include <pulse/volume.h>
 #include <pulse/xmalloc.h>
 
 #include <pulsecore/core.h>
@@ -49,7 +49,6 @@
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/thread.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/time-smoother.h>
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index fb96ed0..f847b1e 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -31,10 +31,9 @@
 #include <pulse/i18n.h>
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
-#include <pulse/util.h>
+#include <pulse/volume.h>
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/core.h>
 #include <pulsecore/module.h>
 #include <pulsecore/memchunk.h>
@@ -46,7 +45,6 @@
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/thread.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/time-smoother.h>
diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c
index 6435db0..883c26f 100644
--- a/src/modules/alsa/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -25,7 +25,6 @@
 #endif
 
 #include <sys/types.h>
-#include <limits.h>
 #include <asoundlib.h>
 
 #include <pulse/sample.h>
@@ -40,7 +39,6 @@
 #include <pulsecore/core-util.h>
 #include <pulsecore/atomic.h>
 #include <pulsecore/core-error.h>
-#include <pulsecore/once.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/conf-parser.h>
 #include <pulsecore/core-rtclock.h>
diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h
index 9e29fd4..ee5e781 100644
--- a/src/modules/alsa/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -26,13 +26,9 @@
 #include <asoundlib.h>
 
 #include <pulse/sample.h>
-#include <pulse/volume.h>
-#include <pulse/mainloop-api.h>
 #include <pulse/channelmap.h>
 #include <pulse/proplist.h>
-#include <pulse/volume.h>
 
-#include <pulsecore/llist.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/core.h>
 #include <pulsecore/log.h>
diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c
index 465c8b9..6e64ab3 100644
--- a/src/modules/alsa/module-alsa-sink.c
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -24,7 +24,6 @@
 #include <config.h>
 #endif
 
-#include <pulsecore/core.h>
 #include <pulsecore/module.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/modargs.h>
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index 478a2e8..5ecd1e3 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -32,26 +32,10 @@
 #include <valgrind/memcheck.h>
 #endif
 
-#include <pulse/xmalloc.h>
-#include <pulse/util.h>
-#include <pulse/timeval.h>
-
-#include <pulsecore/core-error.h>
-#include <pulsecore/core-rtclock.h>
-#include <pulsecore/core.h>
 #include <pulsecore/module.h>
-#include <pulsecore/memchunk.h>
-#include <pulsecore/sink.h>
 #include <pulsecore/modargs.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/sample-util.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/core-error.h>
-#include <pulsecore/thread-mq.h>
-#include <pulsecore/rtpoll.h>
-#include <pulsecore/time-smoother.h>
 
 #include "alsa-util.h"
 #include "alsa-source.h"
diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c
index 597a39d..b24fe7a 100644
--- a/src/modules/bluetooth/bluetooth-util.c
+++ b/src/modules/bluetooth/bluetooth-util.c
@@ -23,6 +23,8 @@
 #include <config.h>
 #endif
 
+#include <pulse/xmalloc.h>
+
 #include <pulsecore/core-util.h>
 #include <pulsecore/shared.h>
 #include <pulsecore/dbus-shared.h>
diff --git a/src/modules/bluetooth/bluetooth-util.h b/src/modules/bluetooth/bluetooth-util.h
index 2054f6e..248ca47 100644
--- a/src/modules/bluetooth/bluetooth-util.h
+++ b/src/modules/bluetooth/bluetooth-util.h
@@ -25,9 +25,7 @@
 #include <dbus/dbus.h>
 
 #include <pulsecore/llist.h>
-#include <pulsecore/strlist.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/core-util.h>
 
 #define PA_BLUETOOTH_ERROR_NOT_SUPPORTED "org.bluez.Error.NotSupported"
 
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 0639afd..288ad2f 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -48,7 +48,6 @@
 #include <pulsecore/time-smoother.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/dbus-shared.h>
-#include <pulsecore/llist.h>
 
 #include "module-bluetooth-device-symdef.h"
 #include "ipc.h"
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index fc68fcd..7b27f6b 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -25,14 +25,12 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include <pulse/xmalloc.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/llist.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/dbus-shared.h>
 
diff --git a/src/modules/dbus/iface-card-profile.h b/src/modules/dbus/iface-card-profile.h
index a09767f..8ffb4b9 100644
--- a/src/modules/dbus/iface-card-profile.h
+++ b/src/modules/dbus/iface-card-profile.h
@@ -28,7 +28,6 @@
  * documentation.
  */
 
-#include <pulsecore/core-scache.h>
 #include <pulsecore/protocol-dbus.h>
 
 #include "iface-card.h"
diff --git a/src/modules/echo-cancel/adrian-aec.h b/src/modules/echo-cancel/adrian-aec.h
index 0efe53e..d024b3c 100644
--- a/src/modules/echo-cancel/adrian-aec.h
+++ b/src/modules/echo-cancel/adrian-aec.h
@@ -17,9 +17,11 @@
 #include <config.h>
 #endif
 
-#include <pulsecore/macro.h>
+#include <pulse/gccmacro.h>
 #include <pulse/xmalloc.h>
 
+#include <pulsecore/macro.h>
+
 #define WIDEB 2
 
 // use double if your CPU does software-emulation of float
diff --git a/src/modules/echo-cancel/adrian.c b/src/modules/echo-cancel/adrian.c
index 08df2ed..ab3858a 100644
--- a/src/modules/echo-cancel/adrian.c
+++ b/src/modules/echo-cancel/adrian.c
@@ -29,8 +29,10 @@
 #include <config.h>
 #endif
 
+#include <pulse/xmalloc.h>
+
 #include <pulsecore/modargs.h>
-#include <pulsecore/endianmacros.h>
+
 #include "echo-cancel.h"
 
 /* should be between 10-20 ms */
diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
index e5ee5b1..b84bf1d 100644
--- a/src/modules/echo-cancel/module-echo-cancel.c
+++ b/src/modules/echo-cancel/module-echo-cancel.c
@@ -31,7 +31,6 @@
 #endif
 
 #include <stdio.h>
-#include <math.h>
 
 #include "echo-cancel.h"
 
@@ -42,17 +41,13 @@
 
 #include <pulsecore/atomic.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/sample-util.h>
 #include <pulsecore/ltdl-helper.h>
diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c
index 5f31d68..3bad911 100644
--- a/src/modules/gconf/module-gconf.c
+++ b/src/modules/gconf/module-gconf.c
@@ -30,12 +30,10 @@
 #include <signal.h>
 #include <sys/types.h>
 #include <sys/wait.h>
-#include <fcntl.h>
 
 #include <pulse/xmalloc.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core.h>
-#include <pulsecore/llist.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
 #include <pulse/mainloop-api.h>
diff --git a/src/modules/jack/module-jack-sink.c b/src/modules/jack/module-jack-sink.c
index dfb5be1..35b0385 100644
--- a/src/modules/jack/module-jack-sink.c
+++ b/src/modules/jack/module-jack-sink.c
@@ -24,19 +24,15 @@
 #endif
 
 #include <stdlib.h>
-#include <sys/stat.h>
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <limits.h>
 
 #include <jack/jack.h>
 
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
diff --git a/src/modules/jack/module-jack-source.c b/src/modules/jack/module-jack-source.c
index 8453bd9..13109f3 100644
--- a/src/modules/jack/module-jack-source.c
+++ b/src/modules/jack/module-jack-source.c
@@ -24,19 +24,15 @@
 #endif
 
 #include <stdlib.h>
-#include <sys/stat.h>
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <limits.h>
 
 #include <jack/jack.h>
 
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/source.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
diff --git a/src/modules/jack/module-jackdbus-detect.c b/src/modules/jack/module-jackdbus-detect.c
index c3dd7bb..864f96b 100644
--- a/src/modules/jack/module-jackdbus-detect.c
+++ b/src/modules/jack/module-jackdbus-detect.c
@@ -27,6 +27,8 @@
 #include <config.h>
 #endif
 
+#include <pulse/xmalloc.h>
+
 #include <pulsecore/log.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/core-util.h>
diff --git a/src/modules/macosx/module-bonjour-publish.c b/src/modules/macosx/module-bonjour-publish.c
index 095a1d2..667b6b7 100644
--- a/src/modules/macosx/module-bonjour-publish.c
+++ b/src/modules/macosx/module-bonjour-publish.c
@@ -41,10 +41,8 @@
 #include <pulsecore/native-common.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-subscribe.h>
 #include <pulsecore/dynarray.h>
 #include <pulsecore/modargs.h>
-#include <pulsecore/endianmacros.h>
 #include <pulsecore/protocol-native.h>
 
 #include "module-bonjour-publish-symdef.h"
diff --git a/src/modules/macosx/module-coreaudio-device.c b/src/modules/macosx/module-coreaudio-device.c
index 393ce7f..d276281 100644
--- a/src/modules/macosx/module-coreaudio-device.c
+++ b/src/modules/macosx/module-coreaudio-device.c
@@ -29,9 +29,7 @@
 #endif
 
 #include <pulse/xmalloc.h>
-#include <pulse/util.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/source.h>
 #include <pulsecore/module.h>
diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c
index 3d7de9c..4c871da 100644
--- a/src/modules/module-always-sink.c
+++ b/src/modules/module-always-sink.c
@@ -27,10 +27,9 @@
 #include <pulse/i18n.h>
 
 #include <pulsecore/core.h>
-#include <pulsecore/sink-input.h>
+#include <pulsecore/sink.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/namereg.h>
 #include <pulsecore/core-util.h>
 
 #include "module-always-sink-symdef.h"
diff --git a/src/modules/module-augment-properties.c b/src/modules/module-augment-properties.c
index c3f5c08..bfc637a 100644
--- a/src/modules/module-augment-properties.c
+++ b/src/modules/module-augment-properties.c
@@ -28,10 +28,7 @@
 #include <time.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/volume.h>
-#include <pulse/channelmap.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
index f1c1930..fc5df5f 100644
--- a/src/modules/module-card-restore.c
+++ b/src/modules/module-card-restore.c
@@ -29,12 +29,10 @@
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <ctype.h>
 
+#include <pulse/gccmacro.h>
 #include <pulse/xmalloc.h>
-#include <pulse/volume.h>
 #include <pulse/timeval.h>
-#include <pulse/util.h>
 #include <pulse/rtclock.h>
 
 #include <pulsecore/core-error.h>
diff --git a/src/modules/module-cli.c b/src/modules/module-cli.c
index 90ce3b6..7788a75 100644
--- a/src/modules/module-cli.c
+++ b/src/modules/module-cli.c
@@ -36,7 +36,6 @@
 #include <pulsecore/modargs.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/core-util.h>
-#include <pulsecore/core-error.h>
 
 #include "module-cli-symdef.h"
 
diff --git a/src/modules/module-combine-sink.c b/src/modules/module-combine-sink.c
index 72ee83c..5d29af4 100644
--- a/src/modules/module-combine-sink.c
+++ b/src/modules/module-combine-sink.c
@@ -41,11 +41,9 @@
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/namereg.h>
-#include <pulsecore/mutex.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/time-smoother.h>
 #include <pulsecore/strlist.h>
 
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index 5812884..251df49 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -28,7 +28,6 @@
 
 #include <pulsecore/module.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-util.h>
 
 #include "module-combine-symdef.h"
 
diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c
index d52cc24..4c5857c 100644
--- a/src/modules/module-console-kit.c
+++ b/src/modules/module-console-kit.c
@@ -25,24 +25,17 @@
 
 #include <stdio.h>
 #include <unistd.h>
-#include <string.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <stdlib.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/timeval.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
 #include <pulsecore/log.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/idxset.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/namereg.h>
-#include <pulsecore/core-scache.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/dbus-shared.h>
 
diff --git a/src/modules/module-cork-music-on-phone.c b/src/modules/module-cork-music-on-phone.c
index 5e6aa64..4c9c178 100644
--- a/src/modules/module-cork-music-on-phone.c
+++ b/src/modules/module-cork-music-on-phone.c
@@ -23,6 +23,8 @@
 #include <config.h>
 #endif
 
+#include <pulse/xmalloc.h>
+
 #include <pulsecore/macro.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/hook-list.h>
diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c
index 0c4aea3..f28bddb 100644
--- a/src/modules/module-default-device-restore.c
+++ b/src/modules/module-default-device-restore.c
@@ -28,7 +28,7 @@
 
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
-#include <pulse/util.h>
+#include <pulse/xmalloc.h>
 
 #include <pulsecore/core-util.h>
 #include <pulsecore/module.h>
diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
index 2a90eb6..bb4994c 100644
--- a/src/modules/module-detect.c
+++ b/src/modules/module-detect.c
@@ -34,8 +34,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 
-#include <pulse/xmalloc.h>
-
 #include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
 #include <pulsecore/modargs.h>
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index ea92cd0..67baef3 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -30,12 +30,10 @@
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <ctype.h>
 
+#include <pulse/gccmacro.h>
 #include <pulse/xmalloc.h>
-#include <pulse/volume.h>
 #include <pulse/timeval.h>
-#include <pulse/util.h>
 #include <pulse/rtclock.h>
 
 #include <pulsecore/core-error.h>
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 495a911..7d94ffa 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -30,12 +30,11 @@
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <ctype.h>
 
+#include <pulse/gccmacro.h>
 #include <pulse/xmalloc.h>
 #include <pulse/volume.h>
 #include <pulse/timeval.h>
-#include <pulse/util.h>
 #include <pulse/rtclock.h>
 #include <pulse/format.h>
 #include <pulse/internal.h>
@@ -46,8 +45,8 @@
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
 #include <pulsecore/core-subscribe.h>
-#include <pulsecore/sink-input.h>
-#include <pulsecore/source-output.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/source.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/protocol-native.h>
 #include <pulsecore/pstream.h>
diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index e20ee4a..e7d8790 100644
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -36,7 +36,6 @@
 #include <math.h>
 #include <string.h>
 #include <stdint.h>
-#include <time.h>
 
 //#undef __SSE2__
 #ifdef __SSE2__
@@ -52,15 +51,12 @@
 
 #include <pulsecore/core-rtclock.h>
 #include <pulsecore/aupdate.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/sample-util.h>
 #include <pulsecore/shared.h>
diff --git a/src/modules/module-esound-compat-spawnfd.c b/src/modules/module-esound-compat-spawnfd.c
index 56cda4d..617d5a1 100644
--- a/src/modules/module-esound-compat-spawnfd.c
+++ b/src/modules/module-esound-compat-spawnfd.c
@@ -24,7 +24,6 @@
 #endif
 
 #include <unistd.h>
-#include <string.h>
 #include <errno.h>
 
 #include <pulsecore/core-error.h>
diff --git a/src/modules/module-esound-compat-spawnpid.c b/src/modules/module-esound-compat-spawnpid.c
index 5925f59..94ebdaa 100644
--- a/src/modules/module-esound-compat-spawnpid.c
+++ b/src/modules/module-esound-compat-spawnpid.c
@@ -25,13 +25,11 @@
 #endif
 
 #include <unistd.h>
-#include <string.h>
 #include <errno.h>
 #include <signal.h>
 
 #include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
-#include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
 
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index 5a1391d..d79054f 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -24,13 +24,10 @@
 #endif
 
 #include <stdlib.h>
-#include <sys/stat.h>
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <limits.h>
 
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
@@ -57,7 +54,6 @@
 #include <pulsecore/iochannel.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
-#include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c
index e9c9f65..c742373 100644
--- a/src/modules/module-filter-apply.c
+++ b/src/modules/module-filter-apply.c
@@ -26,6 +26,7 @@
 #include <pulse/timeval.h>
 #include <pulse/rtclock.h>
 #include <pulse/i18n.h>
+#include <pulse/xmalloc.h>
 
 #include <pulsecore/macro.h>
 #include <pulsecore/hashmap.h>
diff --git a/src/modules/module-filter-heuristics.c b/src/modules/module-filter-heuristics.c
index 1285c46..222787f 100644
--- a/src/modules/module-filter-heuristics.c
+++ b/src/modules/module-filter-heuristics.c
@@ -23,8 +23,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>
diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
index 941ac3a..62f0f20 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -31,19 +31,15 @@
 #include <errno.h>
 #include <stdlib.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/timeval.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
 #include <pulsecore/log.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/idxset.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/namereg.h>
-#include <pulsecore/core-scache.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/dbus-shared.h>
 
diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c
index 2f9bba4..9ba893b 100644
--- a/src/modules/module-intended-roles.c
+++ b/src/modules/module-intended-roles.c
@@ -24,16 +24,11 @@
 #endif
 
 #include <pulse/xmalloc.h>
-#include <pulse/volume.h>
-#include <pulse/timeval.h>
-#include <pulse/util.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-subscribe.h>
 #include <pulsecore/sink-input.h>
 #include <pulsecore/source-output.h>
 #include <pulsecore/namereg.h>
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index 6489f3f..9cce269 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -26,18 +26,17 @@
 #include <config.h>
 #endif
 
+#include <math.h>
+
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/sample-util.h>
 #include <pulsecore/ltdl-helper.h>
diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 936133f..cf88267 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -25,7 +25,6 @@
 #endif
 
 #include <stdio.h>
-#include <math.h>
 
 #include <pulse/xmalloc.h>
 
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
index eeb6f6c..1db639f 100644
--- a/src/modules/module-null-sink.c
+++ b/src/modules/module-null-sink.c
@@ -24,13 +24,9 @@
 #endif
 
 #include <stdlib.h>
-#include <sys/stat.h>
 #include <stdio.h>
 #include <errno.h>
-#include <string.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <limits.h>
 
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
@@ -40,9 +36,7 @@
 #include <pulsecore/macro.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
-#include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
 #include <pulsecore/thread.h>
diff --git a/src/modules/module-null-source.c b/src/modules/module-null-source.c
index 358ffc6..b2981c3 100644
--- a/src/modules/module-null-source.c
+++ b/src/modules/module-null-source.c
@@ -25,20 +25,14 @@
 #endif
 
 #include <stdlib.h>
-#include <sys/stat.h>
 #include <stdio.h>
 #include <errno.h>
-#include <string.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <limits.h>
 
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/core-error.h>
-#include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index 6623aef..91e01f9 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -27,10 +27,8 @@
 #include <sys/stat.h>
 #include <stdio.h>
 #include <errno.h>
-#include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <limits.h>
 #include <sys/ioctl.h>
 
 #ifdef HAVE_SYS_FILIO_H
diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
index c50536e..a941f08 100644
--- a/src/modules/module-pipe-source.c
+++ b/src/modules/module-pipe-source.c
@@ -27,10 +27,8 @@
 #include <sys/stat.h>
 #include <stdio.h>
 #include <errno.h>
-#include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <limits.h>
 #include <sys/ioctl.h>
 
 #ifdef HAVE_SYS_FILIO_H
diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
index 3cba0f3..091453a 100644
--- a/src/modules/module-position-event-sounds.c
+++ b/src/modules/module-position-event-sounds.c
@@ -24,18 +24,15 @@
 #endif
 
 #include <unistd.h>
-#include <string.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <ctype.h>
 
 #include <pulse/xmalloc.h>
 #include <pulse/volume.h>
 #include <pulse/channelmap.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c
index 29cb419..e1bf397 100644
--- a/src/modules/module-protocol-stub.c
+++ b/src/modules/module-protocol-stub.c
@@ -24,11 +24,9 @@
 #include <config.h>
 #endif
 
-#include <string.h>
 #include <errno.h>
 #include <stdio.h>
 #include <unistd.h>
-#include <limits.h>
 
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index 79627f7..2822a7f 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -25,15 +25,12 @@
 
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 
 #include "module-remap-sink-symdef.h"
diff --git a/src/modules/module-rygel-media-server.c b/src/modules/module-rygel-media-server.c
index f34142c..2293074 100644
--- a/src/modules/module-rygel-media-server.c
+++ b/src/modules/module-rygel-media-server.c
@@ -28,8 +28,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <pulse/gccmacro.h>
 #include <pulse/xmalloc.h>
-#include <pulse/util.h>
 #include <pulse/i18n.h>
 #include <pulse/utf8.h>
 
@@ -39,7 +39,6 @@
 #include <pulsecore/log.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/dbus-shared.h>
-#include <pulsecore/endianmacros.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/mime-type.h>
 #include <pulsecore/strbuf.h>
diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c
index 955834f..20a6868 100644
--- a/src/modules/module-sine-source.c
+++ b/src/modules/module-sine-source.c
@@ -24,22 +24,16 @@
 #endif
 
 #include <stdlib.h>
-#include <sys/stat.h>
 #include <stdio.h>
 #include <errno.h>
-#include <string.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <limits.h>
 
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/source.h>
 #include <pulsecore/module.h>
-#include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
index cee01f1..c6d7303 100644
--- a/src/modules/module-sine.c
+++ b/src/modules/module-sine.c
@@ -24,7 +24,6 @@
 #endif
 
 #include <stdio.h>
-#include <math.h>
 
 #include <pulse/xmalloc.h>
 
@@ -33,7 +32,6 @@
 #include <pulsecore/modargs.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-util.h>
 
 #include "module-sine-symdef.h"
 
diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c
index ee06b3b..0e4e401 100644
--- a/src/modules/module-solaris.c
+++ b/src/modules/module-solaris.c
@@ -28,12 +28,9 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
-#include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <limits.h>
 #include <sys/ioctl.h>
-#include <sys/stat.h>
 #include <sys/types.h>
 
 #include <signal.h>
@@ -41,14 +38,12 @@
 #include <sys/conf.h>
 #include <sys/audio.h>
 
-#include <pulse/error.h>
 #include <pulse/mainloop-signal.h>
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
 #include <pulse/util.h>
 #include <pulse/rtclock.h>
 
-#include <pulsecore/iochannel.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/source.h>
 #include <pulsecore/module.h>
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 310517e..19c09bb 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -30,12 +30,11 @@
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <ctype.h>
 
+#include <pulse/gccmacro.h>
 #include <pulse/xmalloc.h>
 #include <pulse/volume.h>
 #include <pulse/timeval.h>
-#include <pulse/util.h>
 #include <pulse/rtclock.h>
 
 #include <pulsecore/core-error.h>
diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index 0069270..e724262 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -33,7 +33,6 @@
 #include <pulsecore/source-output.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/namereg.h>
 
 #include "module-suspend-on-idle-symdef.h"
 
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index 1b2d3a1..4b1ae7d 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -42,12 +42,10 @@
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
 #include <pulsecore/core-subscribe.h>
-#include <pulsecore/sink-input.h>
 #include <pulsecore/pdispatch.h>
 #include <pulsecore/pstream.h>
 #include <pulsecore/pstream-util.h>
 #include <pulsecore/socket-client.h>
-#include <pulsecore/socket-util.h>
 #include <pulsecore/time-smoother.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/thread-mq.h>
diff --git a/src/modules/module-virtual-sink.c b/src/modules/module-virtual-sink.c
index fe26930..a6be244 100644
--- a/src/modules/module-virtual-sink.c
+++ b/src/modules/module-virtual-sink.c
@@ -27,18 +27,16 @@
 #include <config.h>
 #endif
 
+#include <pulse/gccmacro.h>
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
 
-#include <pulsecore/core-error.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/sample-util.h>
 #include <pulsecore/ltdl-helper.h>
diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c
index 680e71a..e15f4b9 100644
--- a/src/modules/module-virtual-source.c
+++ b/src/modules/module-virtual-source.c
@@ -25,23 +25,17 @@
 #endif
 
 #include <stdio.h>
-#include <math.h>
 
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
 
 #include <pulsecore/macro.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
-#include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
-#include <pulsecore/thread.h>
-#include <pulsecore/thread-mq.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/sample-util.h>
 #include <pulsecore/ltdl-helper.h>
diff --git a/src/modules/module-waveout.c b/src/modules/module-waveout.c
index 9f119c5..cb02723 100644
--- a/src/modules/module-waveout.c
+++ b/src/modules/module-waveout.c
@@ -27,11 +27,8 @@
 #include <windows.h>
 #include <mmsystem.h>
 
-#include <pulse/mainloop-api.h>
-
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
-#include <pulse/rtclock.h>
 
 #include <pulsecore/sink.h>
 #include <pulsecore/source.h>
diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c
index 1fdc1f4..cd076aa 100644
--- a/src/modules/module-zeroconf-discover.c
+++ b/src/modules/module-zeroconf-discover.c
@@ -36,14 +36,9 @@
 #include <avahi-common/malloc.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/util.h>
 
-#include <pulsecore/sink.h>
-#include <pulsecore/source.h>
-#include <pulsecore/native-common.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-subscribe.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/namereg.h>
diff --git a/src/modules/module-zeroconf-publish.c b/src/modules/module-zeroconf-publish.c
index d72d264..0c20cf6 100644
--- a/src/modules/module-zeroconf-publish.c
+++ b/src/modules/module-zeroconf-publish.c
@@ -25,7 +25,6 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <unistd.h>
 
 #include <avahi-client/client.h>
@@ -43,11 +42,9 @@
 #include <pulsecore/native-common.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-subscribe.h>
 #include <pulsecore/dynarray.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/avahi-wrap.h>
-#include <pulsecore/endianmacros.h>
 #include <pulsecore/protocol-native.h>
 
 #include "module-zeroconf-publish-symdef.h"
diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index dea5628..2a99d11 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -45,14 +45,10 @@
 #include <sys/soundcard.h>
 #include <sys/ioctl.h>
 #include <stdlib.h>
-#include <sys/stat.h>
 #include <stdio.h>
 #include <errno.h>
-#include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <limits.h>
-#include <signal.h>
 
 #include <pulse/xmalloc.h>
 #include <pulse/util.h>
diff --git a/src/modules/oss/oss-util.c b/src/modules/oss/oss-util.c
index 966a6ca..04899af 100644
--- a/src/modules/oss/oss-util.c
+++ b/src/modules/oss/oss-util.c
@@ -31,7 +31,6 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 #include <fcntl.h>
 
 #include <pulse/xmalloc.h>
diff --git a/src/modules/raop/module-raop-discover.c b/src/modules/raop/module-raop-discover.c
index adba8e4..da1e82b 100644
--- a/src/modules/raop/module-raop-discover.c
+++ b/src/modules/raop/module-raop-discover.c
@@ -37,14 +37,9 @@
 #include <avahi-common/malloc.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/util.h>
 
-#include <pulsecore/sink.h>
-#include <pulsecore/source.h>
-#include <pulsecore/native-common.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-subscribe.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/namereg.h>
diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c
index 89d8cb8..3f362b2 100644
--- a/src/modules/raop/module-raop-sink.c
+++ b/src/modules/raop/module-raop-sink.c
@@ -25,13 +25,10 @@
 #endif
 
 #include <stdlib.h>
-#include <sys/stat.h>
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
-#include <fcntl.h>
 #include <unistd.h>
-#include <limits.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
@@ -46,19 +43,15 @@
 #include <pulse/xmalloc.h>
 
 #include <pulsecore/core-error.h>
-#include <pulsecore/iochannel.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
-#include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
 #include <pulsecore/socket-client.h>
-#include <pulsecore/authkey.h>
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/time-smoother.h>
-#include <pulsecore/socket-util.h>
 #include <pulsecore/poll.h>
 
 #include "module-raop-sink-symdef.h"
diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c
index 05c7b16..4d2ad6e 100644
--- a/src/modules/raop/raop_client.c
+++ b/src/modules/raop/raop_client.c
@@ -23,7 +23,6 @@
 #include <config.h>
 #endif
 
-#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
@@ -45,12 +44,12 @@
 
 #include <pulsecore/core-error.h>
 #include <pulsecore/core-util.h>
+#include <pulsecore/iochannel.h>
 #include <pulsecore/socket-util.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/strbuf.h>
+#include <pulsecore/memchunk.h>
 #include <pulsecore/random.h>
-#include <pulsecore/poll.h>
 
 #include "raop_client.h"
 #include "rtsp_client.h"
diff --git a/src/modules/raop/raop_client.h b/src/modules/raop/raop_client.h
index 5ad3e3f..ce81f39 100644
--- a/src/modules/raop/raop_client.h
+++ b/src/modules/raop/raop_client.h
@@ -22,8 +22,6 @@
   USA.
 ***/
 
-#include <pulse/mainloop-api.h>
-#include <pulsecore/iochannel.h>
 #include <pulsecore/core.h>
 
 typedef struct pa_raop_client pa_raop_client;
diff --git a/src/modules/reserve-monitor.h b/src/modules/reserve-monitor.h
index 421a52e..3408680 100644
--- a/src/modules/reserve-monitor.h
+++ b/src/modules/reserve-monitor.h
@@ -28,7 +28,6 @@
 ***/
 
 #include <dbus/dbus.h>
-#include <inttypes.h>
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index fb3bccb..7025c15 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -30,6 +30,7 @@
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
+#include <math.h>
 
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
@@ -48,9 +49,8 @@
 #include <pulsecore/namereg.h>
 #include <pulsecore/sample-util.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/atomic.h>
-#include <pulsecore/atomic.h>
 #include <pulsecore/socket-util.h>
+#include <pulsecore/atomic.h>
 #include <pulsecore/once.h>
 #include <pulsecore/poll.h>
 #include <pulsecore/arpa-inet.h>
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index e0fed99..7131629 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -27,7 +27,6 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <errno.h>
-#include <string.h>
 #include <unistd.h>
 
 #include <pulse/rtclock.h>
@@ -37,7 +36,6 @@
 
 #include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
-#include <pulsecore/llist.h>
 #include <pulsecore/source.h>
 #include <pulsecore/source-output.h>
 #include <pulsecore/memblockq.h>
diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c
index 22e491b..05c736a 100644
--- a/src/modules/rtp/rtp.c
+++ b/src/modules/rtp/rtp.c
@@ -23,7 +23,6 @@
 #include <config.h>
 #endif
 
-#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
diff --git a/src/modules/rtp/rtsp_client.c b/src/modules/rtp/rtsp_client.c
index 8a5a1d7..ecf85b8 100644
--- a/src/modules/rtp/rtsp_client.c
+++ b/src/modules/rtp/rtsp_client.c
@@ -23,7 +23,6 @@
 #include <config.h>
 #endif
 
-#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
@@ -39,12 +38,10 @@
 
 #include <pulsecore/core-error.h>
 #include <pulsecore/core-util.h>
-#include <pulsecore/socket-util.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/strbuf.h>
 #include <pulsecore/ioline.h>
-#include <pulsecore/poll.h>
 #include <pulsecore/arpa-inet.h>
 
 #include "rtsp_client.h"
diff --git a/src/modules/rtp/rtsp_client.h b/src/modules/rtp/rtsp_client.h
index b229f26..a56b932 100644
--- a/src/modules/rtp/rtsp_client.h
+++ b/src/modules/rtp/rtsp_client.h
@@ -27,8 +27,6 @@
 #include <sys/types.h>
 #include <netdb.h>
 
-#include <pulsecore/memblockq.h>
-#include <pulsecore/memchunk.h>
 #include <pulsecore/socket-client.h>
 #include <pulse/mainloop-api.h>
 
diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c
index 87c8b8f..4d8bf66 100644
--- a/src/modules/rtp/sap.c
+++ b/src/modules/rtp/sap.c
@@ -23,7 +23,6 @@
 #include <config.h>
 #endif
 
-#include <time.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/socket.h>
diff --git a/src/modules/udev-util.h b/src/modules/udev-util.h
index 8523bc4..a978178 100644
--- a/src/modules/udev-util.h
+++ b/src/modules/udev-util.h
@@ -23,7 +23,7 @@
 ***/
 
 
-#include <pulsecore/core.h>
+#include <pulse/proplist.h>
 
 int pa_udev_get_info(int card_idx, pa_proplist *p);
 char* pa_udev_get_property(int card_idx, const char *name);
diff --git a/src/modules/x11/module-x11-bell.c b/src/modules/x11/module-x11-bell.c
index ac303c3..37ab2e7 100644
--- a/src/modules/x11/module-x11-bell.c
+++ b/src/modules/x11/module-x11-bell.c
@@ -25,18 +25,14 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include <X11/Xlib.h>
 #include <X11/XKBlib.h>
 
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/iochannel.h>
-#include <pulsecore/sink.h>
 #include <pulsecore/core-scache.h>
 #include <pulsecore/modargs.h>
-#include <pulsecore/namereg.h>
 #include <pulsecore/log.h>
 #include <pulsecore/x11wrap.h>
 
diff --git a/src/modules/x11/module-x11-cork-request.c b/src/modules/x11/module-x11-cork-request.c
index c1380c2..0e67db0 100644
--- a/src/modules/x11/module-x11-cork-request.c
+++ b/src/modules/x11/module-x11-cork-request.c
@@ -25,7 +25,6 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 #include <unistd.h>
 
 #include <X11/Xlib.h>
@@ -33,7 +32,6 @@
 #include <X11/XF86keysym.h>
 #include <X11/keysym.h>
 
-#include <pulse/util.h>
 #include <pulse/xmalloc.h>
 
 #include <pulsecore/module.h>
diff --git a/src/modules/x11/module-x11-publish.c b/src/modules/x11/module-x11-publish.c
index 6544e07..716fe0b 100644
--- a/src/modules/x11/module-x11-publish.c
+++ b/src/modules/x11/module-x11-publish.c
@@ -30,14 +30,10 @@
 
 #include <xcb/xcb.h>
 
-#include <pulse/util.h>
 #include <pulse/xmalloc.h>
 
 #include <pulsecore/module.h>
-#include <pulsecore/sink.h>
-#include <pulsecore/core-scache.h>
 #include <pulsecore/modargs.h>
-#include <pulsecore/namereg.h>
 #include <pulsecore/log.h>
 #include <pulsecore/x11wrap.h>
 #include <pulsecore/core-util.h>
@@ -45,7 +41,6 @@
 #include <pulsecore/auth-cookie.h>
 #include <pulsecore/x11prop.h>
 #include <pulsecore/strlist.h>
-#include <pulsecore/shared.h>
 #include <pulsecore/protocol-native.h>
 
 #include "module-x11-publish-symdef.h"
diff --git a/src/modules/x11/module-x11-xsmp.c b/src/modules/x11/module-x11-xsmp.c
index 47a4e93..6a6116f 100644
--- a/src/modules/x11/module-x11-xsmp.c
+++ b/src/modules/x11/module-x11-xsmp.c
@@ -33,13 +33,8 @@
 #include <pulse/xmalloc.h>
 #include <pulse/util.h>
 
-#include <pulsecore/iochannel.h>
-#include <pulsecore/sink.h>
-#include <pulsecore/core-scache.h>
 #include <pulsecore/modargs.h>
-#include <pulsecore/namereg.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-util.h>
 #include <pulsecore/x11wrap.h>
 
 #include "module-x11-xsmp-symdef.h"
diff --git a/src/pulse/client-conf.c b/src/pulse/client-conf.c
index 18fafe3..e899af1 100644
--- a/src/pulse/client-conf.c
+++ b/src/pulse/client-conf.c
@@ -27,7 +27,6 @@
 #include <stdlib.h>
 #include <unistd.h>
 #include <errno.h>
-#include <string.h>
 
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 5dd780b..e8f3032 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -32,23 +32,17 @@
 #include <sys/stat.h>
 #include <errno.h>
 #include <signal.h>
-#include <limits.h>
-#include <locale.h>
 
 #ifdef HAVE_SYS_WAIT_H
 #include <sys/wait.h>
 #endif
 
-#ifdef HAVE_SYS_UN_H
-#include <sys/un.h>
-#endif
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
 
 #include <pulse/version.h>
 #include <pulse/xmalloc.h>
-#include <pulse/utf8.h>
 #include <pulse/util.h>
 #include <pulse/i18n.h>
 #include <pulse/mainloop.h>
@@ -70,7 +64,6 @@
 #include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
 #include <pulsecore/socket.h>
-#include <pulsecore/socket-util.h>
 #include <pulsecore/creds.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/proplist-util.h>
diff --git a/src/pulse/error.c b/src/pulse/error.c
index e827699..19a759c 100644
--- a/src/pulse/error.c
+++ b/src/pulse/error.c
@@ -27,14 +27,10 @@
 #include <errno.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 
-#include <pulse/xmalloc.h>
+#include <pulse/def.h>
 #include <pulse/i18n.h>
 
-#include <pulsecore/core-util.h>
-#include <pulsecore/native-common.h>
-
 #include "error.h"
 
 const char*pa_strerror(int error) {
diff --git a/src/pulse/error.h b/src/pulse/error.h
index ea53560..788db84 100644
--- a/src/pulse/error.h
+++ b/src/pulse/error.h
@@ -23,7 +23,6 @@
   USA.
 ***/
 
-#include <inttypes.h>
 #include <pulse/cdecl.h>
 #include <pulse/version.h>
 
diff --git a/src/pulse/ext-device-manager.c b/src/pulse/ext-device-manager.c
index 7b78c24..f2ea63a 100644
--- a/src/pulse/ext-device-manager.c
+++ b/src/pulse/ext-device-manager.c
@@ -25,7 +25,6 @@
 #endif
 
 #include <pulse/context.h>
-#include <pulse/gccmacro.h>
 #include <pulse/xmalloc.h>
 #include <pulse/fork-detect.h>
 #include <pulse/operation.h>
diff --git a/src/pulse/ext-device-manager.h b/src/pulse/ext-device-manager.h
index df0ab92..1e41ebd 100644
--- a/src/pulse/ext-device-manager.h
+++ b/src/pulse/ext-device-manager.h
@@ -23,6 +23,7 @@
   USA.
 ***/
 
+#include <pulse/cdecl.h>
 #include <pulse/context.h>
 #include <pulse/version.h>
 
diff --git a/src/pulse/ext-stream-restore.c b/src/pulse/ext-stream-restore.c
index 7bc1a61..1f72c1c 100644
--- a/src/pulse/ext-stream-restore.c
+++ b/src/pulse/ext-stream-restore.c
@@ -24,7 +24,6 @@
 #endif
 
 #include <pulse/context.h>
-#include <pulse/gccmacro.h>
 #include <pulse/fork-detect.h>
 #include <pulse/operation.h>
 
diff --git a/src/pulse/ext-stream-restore.h b/src/pulse/ext-stream-restore.h
index 54516f6..acb16a8 100644
--- a/src/pulse/ext-stream-restore.h
+++ b/src/pulse/ext-stream-restore.h
@@ -22,6 +22,7 @@
   USA.
 ***/
 
+#include <pulse/cdecl.h>
 #include <pulse/context.h>
 #include <pulse/version.h>
 #include <pulse/volume.h>
diff --git a/src/pulse/format.h b/src/pulse/format.h
index 06e1fe6..821149c 100644
--- a/src/pulse/format.h
+++ b/src/pulse/format.h
@@ -25,6 +25,7 @@
 ***/
 
 #include <pulse/cdecl.h>
+#include <pulse/gccmacro.h>
 #include <pulse/proplist.h>
 #include <pulse/sample.h>
 #include <pulse/channelmap.h>
diff --git a/src/pulse/glib-mainloop.c b/src/pulse/glib-mainloop.c
index 2e5f2f9..35c9c6a 100644
--- a/src/pulse/glib-mainloop.c
+++ b/src/pulse/glib-mainloop.c
@@ -26,7 +26,6 @@
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
 
-#include <pulsecore/idxset.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
 #include <pulsecore/llist.h>
diff --git a/src/pulse/i18n.h b/src/pulse/i18n.h
index f91c0bf..fac3c7b 100644
--- a/src/pulse/i18n.h
+++ b/src/pulse/i18n.h
@@ -23,7 +23,6 @@
 ***/
 
 #include <pulse/cdecl.h>
-#include <pulse/gccmacro.h>
 #include <pulse/version.h>
 
 PA_C_DECL_BEGIN
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 014eadf..27a298c 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -24,10 +24,7 @@
 #include <config.h>
 #endif
 
-#include <string.h>
-
 #include <pulse/context.h>
-#include <pulse/gccmacro.h>
 #include <pulse/xmalloc.h>
 #include <pulse/fork-detect.h>
 
diff --git a/src/pulse/mainloop-api.c b/src/pulse/mainloop-api.c
index 4b862f9..c1f7604 100644
--- a/src/pulse/mainloop-api.c
+++ b/src/pulse/mainloop-api.c
@@ -26,7 +26,6 @@
 #include <stdlib.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/gccmacro.h>
 #include <pulse/i18n.h>
 
 #include <pulsecore/macro.h>
diff --git a/src/pulse/mainloop-api.h b/src/pulse/mainloop-api.h
index 212ff3c..eb2e842 100644
--- a/src/pulse/mainloop-api.h
+++ b/src/pulse/mainloop-api.h
@@ -26,7 +26,6 @@
 #include <sys/time.h>
 
 #include <pulse/cdecl.h>
-#include <pulse/sample.h>
 #include <pulse/version.h>
 
 /** \file
diff --git a/src/pulse/mainloop-signal.c b/src/pulse/mainloop-signal.c
index 70c0122..393d1f7 100644
--- a/src/pulse/mainloop-signal.c
+++ b/src/pulse/mainloop-signal.c
@@ -30,14 +30,12 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
-#include <fcntl.h>
 
 #ifdef HAVE_WINDOWS_H
 #include <windows.h>
 #endif
 
 #include <pulse/xmalloc.h>
-#include <pulse/gccmacro.h>
 #include <pulse/i18n.h>
 
 #include <pulsecore/core-error.h>
diff --git a/src/pulse/mainloop.c b/src/pulse/mainloop.c
index f02d31a..3ef387b 100644
--- a/src/pulse/mainloop.c
+++ b/src/pulse/mainloop.c
@@ -25,10 +25,8 @@
 #endif
 
 #include <stdio.h>
-#include <signal.h>
 #include <unistd.h>
 #include <stdlib.h>
-#include <string.h>
 #include <fcntl.h>
 #include <errno.h>
 
diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c
index faa98b7..75445e0 100644
--- a/src/pulse/proplist.c
+++ b/src/pulse/proplist.c
@@ -28,7 +28,6 @@
 
 #include <pulse/xmalloc.h>
 #include <pulse/utf8.h>
-#include <pulse/i18n.h>
 
 #include <pulsecore/hashmap.h>
 #include <pulsecore/strbuf.h>
diff --git a/src/pulse/rtclock.h b/src/pulse/rtclock.h
index 6459d92..cba20e3 100644
--- a/src/pulse/rtclock.h
+++ b/src/pulse/rtclock.h
@@ -23,8 +23,7 @@
 ***/
 
 #include <pulse/cdecl.h>
-#include <pulse/def.h>
-#include <pulse/gccmacro.h>
+#include <pulse/sample.h>
 
 /** \file
  *  Monotonic clock utilities. */
diff --git a/src/pulse/sample.c b/src/pulse/sample.c
index 50d5521..8365e93 100644
--- a/src/pulse/sample.c
+++ b/src/pulse/sample.c
@@ -25,7 +25,6 @@
 #endif
 
 #include <stdio.h>
-#include <math.h>
 #include <string.h>
 
 #include <pulse/timeval.h>
diff --git a/src/pulse/sample.h b/src/pulse/sample.h
index c9e6fc4..8d9ef1d 100644
--- a/src/pulse/sample.h
+++ b/src/pulse/sample.h
@@ -26,7 +26,6 @@
 #include <inttypes.h>
 #include <sys/types.h>
 #include <sys/param.h>
-#include <math.h>
 
 #include <pulse/gccmacro.h>
 #include <pulse/cdecl.h>
diff --git a/src/pulse/scache.c b/src/pulse/scache.c
index 6ed80a6..3fad82a 100644
--- a/src/pulse/scache.c
+++ b/src/pulse/scache.c
@@ -25,7 +25,6 @@
 
 #include <stdlib.h>
 #include <stdio.h>
-#include <string.h>
 
 #include <pulse/utf8.h>
 #include <pulse/fork-detect.h>
diff --git a/src/pulse/simple.c b/src/pulse/simple.c
index 9ed7a65..3524296 100644
--- a/src/pulse/simple.c
+++ b/src/pulse/simple.c
@@ -32,7 +32,6 @@
 #include <pulse/thread-mainloop.h>
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/native-common.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 
diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c
index 52d0af3..a6ad238 100644
--- a/src/pulse/subscribe.c
+++ b/src/pulse/subscribe.c
@@ -25,8 +25,6 @@
 
 #include <stdio.h>
 
-#include <pulse/gccmacro.h>
-
 #include <pulsecore/macro.h>
 #include <pulsecore/pstream-util.h>
 
diff --git a/src/pulse/thread-mainloop.c b/src/pulse/thread-mainloop.c
index ef4ef56..b07ad78 100644
--- a/src/pulse/thread-mainloop.c
+++ b/src/pulse/thread-mainloop.c
@@ -36,7 +36,6 @@
 #include <pulse/i18n.h>
 
 #include <pulsecore/log.h>
-#include <pulsecore/hashmap.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/mutex.h>
 #include <pulsecore/macro.h>
diff --git a/src/pulse/util.c b/src/pulse/util.c
index 48ccf29..ba1d2a0 100644
--- a/src/pulse/util.c
+++ b/src/pulse/util.c
@@ -58,8 +58,6 @@
 #include <pulse/timeval.h>
 
 #include <pulsecore/socket.h>
-#include <pulsecore/core-error.h>
-#include <pulsecore/log.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/usergroup.h>
diff --git a/src/pulse/util.h b/src/pulse/util.h
index ad85653..e490d56 100644
--- a/src/pulse/util.h
+++ b/src/pulse/util.h
@@ -26,7 +26,6 @@
 #include <stddef.h>
 
 #include <pulse/cdecl.h>
-#include <pulse/gccmacro.h>
 #include <pulse/version.h>
 
 /** \file
diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 134c007..82e5757 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -25,6 +25,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <math.h>
 
 #include <pulse/i18n.h>
 
diff --git a/src/pulsecore/arpa-inet.c b/src/pulsecore/arpa-inet.c
index 3d6f316..c927ca0 100644
--- a/src/pulsecore/arpa-inet.c
+++ b/src/pulsecore/arpa-inet.c
@@ -27,7 +27,6 @@
 
 #include <errno.h>
 
-#include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/socket.h>
 
diff --git a/src/pulsecore/asyncmsgq.c b/src/pulsecore/asyncmsgq.c
index c211d9b..408416c 100644
--- a/src/pulsecore/asyncmsgq.c
+++ b/src/pulsecore/asyncmsgq.c
@@ -28,13 +28,11 @@
 
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/atomic.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/log.h>
-#include <pulsecore/thread.h>
 #include <pulsecore/semaphore.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/core-util.h>
+#include <pulsecore/mutex.h>
 #include <pulsecore/flist.h>
 
 #include "asyncmsgq.h"
diff --git a/src/pulsecore/auth-cookie.c b/src/pulsecore/auth-cookie.c
index 2f45eca..0897502 100644
--- a/src/pulsecore/auth-cookie.c
+++ b/src/pulsecore/auth-cookie.c
@@ -26,7 +26,6 @@
 #include <sys/types.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/util.h>
 
 #include <pulsecore/refcnt.h>
 #include <pulsecore/macro.h>
diff --git a/src/pulsecore/authkey.c b/src/pulsecore/authkey.c
index a590a65..398e5b1 100644
--- a/src/pulsecore/authkey.c
+++ b/src/pulsecore/authkey.c
@@ -29,9 +29,7 @@
 #include <string.h>
 #include <errno.h>
 #include <stdio.h>
-#include <inttypes.h>
 #include <stdlib.h>
-#include <limits.h>
 #include <sys/stat.h>
 
 #include <pulse/util.h>
diff --git a/src/pulsecore/avahi-wrap.c b/src/pulsecore/avahi-wrap.c
index f1f08bc..997d81f 100644
--- a/src/pulsecore/avahi-wrap.c
+++ b/src/pulsecore/avahi-wrap.c
@@ -26,7 +26,6 @@
 #include <pulse/timeval.h>
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 
 #include "avahi-wrap.h"
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 1db19ce..e5c1806 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -48,7 +48,6 @@
 #include <pulsecore/namereg.h>
 #include <pulsecore/cli-text.h>
 #include <pulsecore/core-scache.h>
-#include <pulsecore/sample-util.h>
 #include <pulsecore/sound-file.h>
 #include <pulsecore/play-memchunk.h>
 #include <pulsecore/sound-file-stream.h>
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index e6018da..5498744 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -23,8 +23,6 @@
 #include <config.h>
 #endif
 
-#include <string.h>
-
 #include <pulse/volume.h>
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
@@ -36,7 +34,6 @@
 #include <pulsecore/sink-input.h>
 #include <pulsecore/source-output.h>
 #include <pulsecore/strbuf.h>
-#include <pulsecore/sample-util.h>
 #include <pulsecore/core-scache.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/core-util.h>
diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
index 54514e7..53aa651 100644
--- a/src/pulsecore/cli.c
+++ b/src/pulsecore/cli.c
@@ -24,21 +24,16 @@
 #endif
 
 #include <stdio.h>
-#include <string.h>
 #include <stdlib.h>
 
 #include <pulse/xmalloc.h>
 
+#include <pulsecore/core-util.h>
 #include <pulsecore/ioline.h>
 #include <pulsecore/module.h>
-#include <pulsecore/sink.h>
-#include <pulsecore/source.h>
 #include <pulsecore/client.h>
-#include <pulsecore/sink-input.h>
-#include <pulsecore/source-output.h>
 #include <pulsecore/tokenizer.h>
 #include <pulsecore/strbuf.h>
-#include <pulsecore/namereg.h>
 #include <pulsecore/cli-text.h>
 #include <pulsecore/cli-command.h>
 #include <pulsecore/log.h>
diff --git a/src/pulsecore/core-error.c b/src/pulsecore/core-error.c
index c440806..4d930a0 100644
--- a/src/pulsecore/core-error.c
+++ b/src/pulsecore/core-error.c
@@ -32,8 +32,6 @@
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/core-util.h>
-#include <pulsecore/native-common.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/log.h>
diff --git a/src/pulsecore/core-error.h b/src/pulsecore/core-error.h
index e7bc4fc..e295091 100644
--- a/src/pulsecore/core-error.h
+++ b/src/pulsecore/core-error.h
@@ -23,7 +23,6 @@
   USA.
 ***/
 
-#include <inttypes.h>
 #include <pulse/cdecl.h>
 
 /** \file
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
index 1aed907..4f2a44d 100644
--- a/src/pulsecore/core-scache.c
+++ b/src/pulsecore/core-scache.c
@@ -25,7 +25,6 @@
 #endif
 
 #include <stdlib.h>
-#include <string.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <dirent.h>
@@ -51,7 +50,6 @@
 #include <pulse/rtclock.h>
 
 #include <pulsecore/sink-input.h>
-#include <pulsecore/sample-util.h>
 #include <pulsecore/play-memchunk.h>
 #include <pulsecore/core-subscribe.h>
 #include <pulsecore/namereg.h>
diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c
index 54fb7ec..e13e6ea 100644
--- a/src/pulsecore/core-subscribe.c
+++ b/src/pulsecore/core-subscribe.c
@@ -27,7 +27,6 @@
 
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/queue.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
index 626ae65..2a68fdf 100644
--- a/src/pulsecore/core.c
+++ b/src/pulsecore/core.c
@@ -33,14 +33,10 @@
 #include <pulse/xmalloc.h>
 
 #include <pulsecore/module.h>
-#include <pulsecore/sink.h>
-#include <pulsecore/source.h>
-#include <pulsecore/namereg.h>
 #include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/core-scache.h>
 #include <pulsecore/core-subscribe.h>
-#include <pulsecore/shared.h>
 #include <pulsecore/random.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 6b25fba..61c8690 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -43,7 +43,6 @@ typedef enum pa_suspend_cause {
 #include <pulsecore/hashmap.h>
 #include <pulsecore/memblock.h>
 #include <pulsecore/resampler.h>
-#include <pulsecore/queue.h>
 #include <pulsecore/llist.h>
 #include <pulsecore/hook-list.h>
 #include <pulsecore/asyncmsgq.h>
@@ -51,7 +50,6 @@ typedef enum pa_suspend_cause {
 #include <pulsecore/sink.h>
 #include <pulsecore/source.h>
 #include <pulsecore/core-subscribe.h>
-#include <pulsecore/sink-input.h>
 #include <pulsecore/msgobject.h>
 
 typedef enum pa_server_type {
diff --git a/src/pulsecore/cpu-arm.c b/src/pulsecore/cpu-arm.c
index 0287043..078fb04 100644
--- a/src/pulsecore/cpu-arm.c
+++ b/src/pulsecore/cpu-arm.c
@@ -26,7 +26,6 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-#include <sys/stat.h>
 #include <fcntl.h>
 
 #include <pulse/xmalloc.h>
diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h
index 9e6fb12..aa1d560 100644
--- a/src/pulsecore/creds.h
+++ b/src/pulsecore/creds.h
@@ -30,10 +30,6 @@
 
 #include <pulsecore/socket.h>
 
-#ifdef HAVE_SYS_UN_H
-#include <sys/un.h>
-#endif
-
 typedef struct pa_creds pa_creds;
 
 #if defined(SCM_CREDENTIALS)
diff --git a/src/pulsecore/database-simple.c b/src/pulsecore/database-simple.c
index 237d0bd..3538127 100644
--- a/src/pulsecore/database-simple.c
+++ b/src/pulsecore/database-simple.c
@@ -25,9 +25,7 @@
 #endif
 
 #include <errno.h>
-#include <sys/stat.h>
 #include <sys/types.h>
-#include <fcntl.h>
 #include <unistd.h>
 #include <stdio.h>
 
diff --git a/src/pulsecore/dbus-shared.c b/src/pulsecore/dbus-shared.c
index 20ef9b1..3477407 100644
--- a/src/pulsecore/dbus-shared.c
+++ b/src/pulsecore/dbus-shared.c
@@ -24,11 +24,8 @@
 #include <config.h>
 #endif
 
-#include <stdarg.h>
-
 #include <pulse/xmalloc.h>
-#include <pulse/timeval.h>
-#include <pulsecore/log.h>
+
 #include <pulsecore/shared.h>
 
 #include "dbus-shared.h"
diff --git a/src/pulsecore/dbus-util.h b/src/pulsecore/dbus-util.h
index f35e66c..347f9f3 100644
--- a/src/pulsecore/dbus-util.h
+++ b/src/pulsecore/dbus-util.h
@@ -24,10 +24,12 @@
 
 #include <dbus/dbus.h>
 
-#include <pulsecore/llist.h>
+#include <pulse/gccmacro.h>
 #include <pulse/mainloop-api.h>
 #include <pulse/proplist.h>
 
+#include <pulsecore/llist.h>
+
 /* A wrap connection is not shared or refcounted, it is available in client side */
 typedef struct pa_dbus_wrap_connection pa_dbus_wrap_connection;
 
diff --git a/src/pulsecore/dllmain.c b/src/pulsecore/dllmain.c
index cb88c92..3ee8967 100644
--- a/src/pulsecore/dllmain.c
+++ b/src/pulsecore/dllmain.c
@@ -27,7 +27,6 @@
 
 #include <stdlib.h>
 #include <stdio.h>
-#include <string.h>
 
 #include <windows.h>
 #include <winsock2.h>
diff --git a/src/pulsecore/fdsem.c b/src/pulsecore/fdsem.c
index 6238045..14fcbd6 100644
--- a/src/pulsecore/fdsem.c
+++ b/src/pulsecore/fdsem.c
@@ -32,7 +32,6 @@
 
 #include <pulsecore/atomic.h>
 #include <pulsecore/log.h>
-#include <pulsecore/thread.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/core-error.h>
diff --git a/src/pulsecore/fdsem.h b/src/pulsecore/fdsem.h
index 48a77c4..90e8599 100644
--- a/src/pulsecore/fdsem.h
+++ b/src/pulsecore/fdsem.h
@@ -23,7 +23,6 @@
 ***/
 
 #include <sys/types.h>
-#include <pulse/def.h>
 
 /* A simple, asynchronous semaphore which uses fds for sleeping. In
  * the best case all functions are lock-free unless sleeping is
diff --git a/src/pulsecore/flist.c b/src/pulsecore/flist.c
index e342a57..add3aa2 100644
--- a/src/pulsecore/flist.c
+++ b/src/pulsecore/flist.c
@@ -30,7 +30,6 @@
 
 #include <pulsecore/atomic.h>
 #include <pulsecore/log.h>
-#include <pulsecore/thread.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c
index 1fac97e..e368512 100644
--- a/src/pulsecore/hashmap.c
+++ b/src/pulsecore/hashmap.c
@@ -24,11 +24,9 @@
 #endif
 
 #include <stdlib.h>
-#include <string.h>
 
 #include <pulse/xmalloc.h>
 #include <pulsecore/idxset.h>
-#include <pulsecore/log.h>
 #include <pulsecore/flist.h>
 #include <pulsecore/macro.h>
 
diff --git a/src/pulsecore/hook-list.c b/src/pulsecore/hook-list.c
index d9b9917..00981be 100644
--- a/src/pulsecore/hook-list.c
+++ b/src/pulsecore/hook-list.c
@@ -23,6 +23,8 @@
 #include <config.h>
 #endif
 
+#include <pulse/xmalloc.h>
+
 #include <pulsecore/macro.h>
 
 #include "hook-list.h"
diff --git a/src/pulsecore/hook-list.h b/src/pulsecore/hook-list.h
index 86ce9d2..e4b70d3 100644
--- a/src/pulsecore/hook-list.h
+++ b/src/pulsecore/hook-list.h
@@ -22,9 +22,6 @@
   USA.
 ***/
 
-#include <pulse/xmalloc.h>
-#include <pulse/gccmacro.h>
-
 #include <pulsecore/llist.h>
 
 typedef struct pa_hook_slot pa_hook_slot;
diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c
index 408011f..2b6af90 100644
--- a/src/pulsecore/idxset.c
+++ b/src/pulsecore/idxset.c
@@ -29,7 +29,6 @@
 #include <string.h>
 
 #include <pulse/xmalloc.h>
-#include <pulsecore/log.h>
 #include <pulsecore/flist.h>
 #include <pulsecore/macro.h>
 
diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c
index f89b067..4b0fb3a 100644
--- a/src/pulsecore/iochannel.c
+++ b/src/pulsecore/iochannel.c
@@ -25,7 +25,6 @@
 #endif
 
 #include <stdlib.h>
-#include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
 
diff --git a/src/pulsecore/ioline.c b/src/pulsecore/ioline.c
index 963f7d2..a18188d 100644
--- a/src/pulsecore/ioline.c
+++ b/src/pulsecore/ioline.c
@@ -32,6 +32,7 @@
 
 #include <pulsecore/socket.h>
 #include <pulsecore/core-error.h>
+#include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/refcnt.h>
diff --git a/src/pulsecore/ioline.h b/src/pulsecore/ioline.h
index d973a3c..12f6dea 100644
--- a/src/pulsecore/ioline.h
+++ b/src/pulsecore/ioline.h
@@ -22,8 +22,9 @@
   USA.
 ***/
 
+#include <pulse/gccmacro.h>
+
 #include <pulsecore/iochannel.h>
-#include <pulsecore/core-util.h>
 
 /* An ioline wraps an iochannel for line based communication. A
  * callback function is called whenever a new line has been recieved
diff --git a/src/pulsecore/lock-autospawn.c b/src/pulsecore/lock-autospawn.c
index a9c942f..40aa5e9 100644
--- a/src/pulsecore/lock-autospawn.c
+++ b/src/pulsecore/lock-autospawn.c
@@ -23,7 +23,6 @@
 #include <config.h>
 #endif
 
-#include <fcntl.h>
 #include <errno.h>
 #include <string.h>
 #include <signal.h>
@@ -32,6 +31,7 @@
 #include <pthread.h>
 #endif
 
+#include <pulse/gccmacro.h>
 #include <pulse/i18n.h>
 #include <pulse/xmalloc.h>
 
diff --git a/src/pulsecore/log.c b/src/pulsecore/log.c
index b12cbf0..1303814 100644
--- a/src/pulsecore/log.c
+++ b/src/pulsecore/log.c
@@ -38,6 +38,7 @@
 #include <syslog.h>
 #endif
 
+#include <pulse/gccmacro.h>
 #include <pulse/rtclock.h>
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
@@ -46,7 +47,6 @@
 
 #include <pulsecore/macro.h>
 #include <pulsecore/core-util.h>
-#include <pulsecore/core-rtclock.h>
 #include <pulsecore/once.h>
 #include <pulsecore/ratelimit.h>
 
diff --git a/src/pulsecore/ltdl-helper.c b/src/pulsecore/ltdl-helper.c
index 1a0e555..bdb5a5d 100644
--- a/src/pulsecore/ltdl-helper.c
+++ b/src/pulsecore/ltdl-helper.c
@@ -28,7 +28,6 @@
 #include <ctype.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/util.h>
 
 #include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
diff --git a/src/pulsecore/macro.h b/src/pulsecore/macro.h
index 9a5a267..1207a10 100644
--- a/src/pulsecore/macro.h
+++ b/src/pulsecore/macro.h
@@ -31,8 +31,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <pulse/gccmacro.h>
-
 #ifndef PACKAGE
 #error "Please include config.h before including this file!"
 #endif
diff --git a/src/pulsecore/memblock.c b/src/pulsecore/memblock.c
index bc80457..e34a739 100644
--- a/src/pulsecore/memblock.c
+++ b/src/pulsecore/memblock.c
@@ -42,7 +42,10 @@
 #include <pulsecore/log.h>
 #include <pulsecore/hashmap.h>
 #include <pulsecore/semaphore.h>
+#include <pulsecore/mutex.h>
 #include <pulsecore/macro.h>
+#include <pulsecore/refcnt.h>
+#include <pulsecore/llist.h>
 #include <pulsecore/flist.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/memtrap.h>
diff --git a/src/pulsecore/memblock.h b/src/pulsecore/memblock.h
index b1eab2a..ca108cc 100644
--- a/src/pulsecore/memblock.h
+++ b/src/pulsecore/memblock.h
@@ -27,8 +27,6 @@
 #include <inttypes.h>
 
 #include <pulse/def.h>
-#include <pulsecore/llist.h>
-#include <pulsecore/refcnt.h>
 #include <pulsecore/atomic.h>
 
 /* A pa_memblock is a reference counted memory block. PulseAudio
diff --git a/src/pulsecore/memchunk.c b/src/pulsecore/memchunk.c
index 34317fe..cc242e4 100644
--- a/src/pulsecore/memchunk.c
+++ b/src/pulsecore/memchunk.c
@@ -28,7 +28,6 @@
 #include <string.h>
 #include <errno.h>
 
-#include <pulse/xmalloc.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/core-util.h>
 
diff --git a/src/pulsecore/modargs.c b/src/pulsecore/modargs.c
index 3106775..45ffba2 100644
--- a/src/pulsecore/modargs.c
+++ b/src/pulsecore/modargs.c
@@ -31,10 +31,6 @@
 
 #include <pulsecore/hashmap.h>
 #include <pulsecore/idxset.h>
-#include <pulsecore/sample-util.h>
-#include <pulsecore/namereg.h>
-#include <pulsecore/sink.h>
-#include <pulsecore/source.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
 
diff --git a/src/pulsecore/modargs.h b/src/pulsecore/modargs.h
index 1ed66e9..9a81e8d 100644
--- a/src/pulsecore/modargs.h
+++ b/src/pulsecore/modargs.h
@@ -25,7 +25,7 @@
 #include <inttypes.h>
 #include <pulse/sample.h>
 #include <pulse/channelmap.h>
-#include <pulsecore/core.h>
+#include <pulse/proplist.h>
 #include <pulsecore/macro.h>
 
 typedef struct pa_modargs pa_modargs;
diff --git a/src/pulsecore/modinfo.c b/src/pulsecore/modinfo.c
index b5ee9f5..860b806 100644
--- a/src/pulsecore/modinfo.c
+++ b/src/pulsecore/modinfo.c
@@ -27,7 +27,6 @@
 
 #include <pulse/xmalloc.h>
 
-#include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/ltdl-helper.h>
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index 1b1e112..8b3ff8f 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -24,14 +24,11 @@
 #include <config.h>
 #endif
 
-#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <ctype.h>
 
-#include <pulse/timeval.h>
 #include <pulse/xmalloc.h>
 #include <pulse/proplist.h>
 
diff --git a/src/pulsecore/msgobject.h b/src/pulsecore/msgobject.h
index ee0ec1e..9b5712b 100644
--- a/src/pulsecore/msgobject.h
+++ b/src/pulsecore/msgobject.h
@@ -25,8 +25,6 @@
 
 #include <sys/types.h>
 
-#include <pulse/xmalloc.h>
-#include <pulsecore/refcnt.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/object.h>
 #include <pulsecore/memchunk.h>
diff --git a/src/pulsecore/mutex-posix.c b/src/pulsecore/mutex-posix.c
index 0ff4bee..634087d 100644
--- a/src/pulsecore/mutex-posix.c
+++ b/src/pulsecore/mutex-posix.c
@@ -28,8 +28,6 @@
 
 #include <pulse/xmalloc.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/log.h>
-#include <pulsecore/core-error.h>
 
 #include "mutex.h"
 
diff --git a/src/pulsecore/object.c b/src/pulsecore/object.c
index 099d50d..0dc8198 100644
--- a/src/pulsecore/object.c
+++ b/src/pulsecore/object.c
@@ -24,8 +24,6 @@
 #include <config.h>
 #endif
 
-#include <pulsecore/core-util.h>
-
 #include "object.h"
 
 const char pa_object_type_id[] = "pa_object";
diff --git a/src/pulsecore/object.h b/src/pulsecore/object.h
index 4c120cd..1ddce3b 100644
--- a/src/pulsecore/object.h
+++ b/src/pulsecore/object.h
@@ -23,7 +23,6 @@
   USA.
 ***/
 
-#include <string.h>
 #include <sys/types.h>
 
 #include <pulse/xmalloc.h>
diff --git a/src/pulsecore/once.h b/src/pulsecore/once.h
index 50ac18c..edc8188 100644
--- a/src/pulsecore/once.h
+++ b/src/pulsecore/once.h
@@ -22,7 +22,6 @@
   USA.
 ***/
 
-#include <pulsecore/mutex.h>
 #include <pulsecore/atomic.h>
 
 typedef struct pa_once {
diff --git a/src/pulsecore/parseaddr.c b/src/pulsecore/parseaddr.c
index 4b6cf9d..7ec068a 100644
--- a/src/pulsecore/parseaddr.c
+++ b/src/pulsecore/parseaddr.c
@@ -27,7 +27,6 @@
 #include <stdlib.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/util.h>
 
 #include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
diff --git a/src/pulsecore/pid.c b/src/pulsecore/pid.c
index cb50077..50968de 100644
--- a/src/pulsecore/pid.c
+++ b/src/pulsecore/pid.c
@@ -40,7 +40,6 @@
 #endif
 
 #include <pulse/xmalloc.h>
-#include <pulse/util.h>
 
 #include <pulsecore/core-error.h>
 #include <pulsecore/core-util.h>
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index 9455340..b093261 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -25,14 +25,11 @@
 
 #include <stdlib.h>
 #include <stdio.h>
-#include <string.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/gccmacro.h>
 
 #include <pulsecore/sink-input.h>
 #include <pulsecore/thread-mq.h>
-#include <pulsecore/sample-util.h>
 
 #include "play-memblockq.h"
 
diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c
index 6fb8902..ee25958 100644
--- a/src/pulsecore/play-memchunk.c
+++ b/src/pulsecore/play-memchunk.c
@@ -25,13 +25,8 @@
 
 #include <stdlib.h>
 #include <stdio.h>
-#include <string.h>
-
-#include <pulse/xmalloc.h>
-#include <pulse/gccmacro.h>
 
 #include <pulsecore/sink-input.h>
-#include <pulsecore/thread-mq.h>
 #include <pulsecore/play-memblockq.h>
 
 #include "play-memchunk.h"
diff --git a/src/pulsecore/prioq.h b/src/pulsecore/prioq.h
index fd3550b..4df5e04 100644
--- a/src/pulsecore/prioq.h
+++ b/src/pulsecore/prioq.h
@@ -22,8 +22,6 @@
   USA.
 ***/
 
-#include <inttypes.h>
-
 #include <pulsecore/macro.h>
 #include <pulsecore/idxset.h>
 
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 484821c..95355bf 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -28,7 +28,6 @@
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <limits.h>
 
 #include <pulse/rtclock.h>
 #include <pulse/sample.h>
@@ -46,7 +45,6 @@
 #include <pulsecore/source.h>
 #include <pulsecore/core-scache.h>
 #include <pulsecore/sample-util.h>
-#include <pulsecore/authkey.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/log.h>
 #include <pulsecore/core-util.h>
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index 1de0434..d085e61 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -32,6 +32,7 @@
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
 
+#include <pulsecore/core-util.h>
 #include <pulsecore/ioline.h>
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/macro.h>
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 6c7c897..6b98175 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -46,7 +46,6 @@
 #include <pulsecore/tagstruct.h>
 #include <pulsecore/pdispatch.h>
 #include <pulsecore/pstream-util.h>
-#include <pulsecore/authkey.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/core-scache.h>
 #include <pulsecore/core-subscribe.h>
@@ -54,7 +53,6 @@
 #include <pulsecore/strlist.h>
 #include <pulsecore/shared.h>
 #include <pulsecore/sample-util.h>
-#include <pulsecore/llist.h>
 #include <pulsecore/creds.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/ipacl.h>
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index 41a3cc5..6def27f 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -24,10 +24,8 @@
 #endif
 
 #include <stdlib.h>
-#include <limits.h>
 #include <stdio.h>
 #include <errno.h>
-#include <string.h>
 
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
index 369e22c..3e59fc4 100644
--- a/src/pulsecore/pstream.c
+++ b/src/pulsecore/pstream.c
@@ -28,9 +28,6 @@
 #include <stdlib.h>
 #include <unistd.h>
 
-#ifdef HAVE_SYS_UN_H
-#include <sys/un.h>
-#endif
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #endif
diff --git a/src/pulsecore/random.c b/src/pulsecore/random.c
index bdbc143..1bd69c2 100644
--- a/src/pulsecore/random.c
+++ b/src/pulsecore/random.c
@@ -27,7 +27,6 @@
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
-#include <string.h>
 #include <stdlib.h>
 #include <time.h>
 
diff --git a/src/pulsecore/remap_mmx.c b/src/pulsecore/remap_mmx.c
index 37d72da..9a69ada 100644
--- a/src/pulsecore/remap_mmx.c
+++ b/src/pulsecore/remap_mmx.c
@@ -24,8 +24,6 @@
 #include <config.h>
 #endif
 
-#include <string.h>
-
 #include <pulse/sample.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
diff --git a/src/pulsecore/remap_sse.c b/src/pulsecore/remap_sse.c
index e1cb161..8344a27 100644
--- a/src/pulsecore/remap_sse.c
+++ b/src/pulsecore/remap_sse.c
@@ -24,8 +24,6 @@
 #include <config.h>
 #endif
 
-#include <string.h>
-
 #include <pulse/sample.h>
 #include <pulsecore/log.h>
 #include <pulsecore/macro.h>
diff --git a/src/pulsecore/rtpoll.c b/src/pulsecore/rtpoll.c
index a5e990f..d90c996 100644
--- a/src/pulsecore/rtpoll.c
+++ b/src/pulsecore/rtpoll.c
@@ -26,7 +26,6 @@
 
 #include <sys/types.h>
 #include <stdio.h>
-#include <signal.h>
 #include <string.h>
 #include <errno.h>
 
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index 62b7c46..8a13495 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -29,6 +29,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <errno.h>
+#include <math.h>
 
 #include <pulse/timeval.h>
 
diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h
index d0235d6..cf79d43 100644
--- a/src/pulsecore/sample-util.h
+++ b/src/pulsecore/sample-util.h
@@ -23,9 +23,11 @@
   USA.
 ***/
 
+#include <pulse/gccmacro.h>
 #include <pulse/sample.h>
 #include <pulse/volume.h>
 #include <pulse/channelmap.h>
+
 #include <pulsecore/memblock.h>
 #include <pulsecore/memchunk.h>
 
diff --git a/src/pulsecore/sconv-s16le.c b/src/pulsecore/sconv-s16le.c
index 43587f3..138e418 100644
--- a/src/pulsecore/sconv-s16le.c
+++ b/src/pulsecore/sconv-s16le.c
@@ -27,10 +27,10 @@
 
 #include <inttypes.h>
 #include <stdio.h>
+#include <math.h>
 
 #include <pulsecore/sconv.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/log.h>
 #include <pulsecore/endianmacros.h>
 
 #include "sconv-s16le.h"
diff --git a/src/pulsecore/sconv.c b/src/pulsecore/sconv.c
index 988d4b3..f0f154f 100644
--- a/src/pulsecore/sconv.c
+++ b/src/pulsecore/sconv.c
@@ -26,6 +26,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <math.h>
 
 #include <pulsecore/g711.h>
 #include <pulsecore/macro.h>
diff --git a/src/pulsecore/sconv.h b/src/pulsecore/sconv.h
index cd93755..204887f 100644
--- a/src/pulsecore/sconv.h
+++ b/src/pulsecore/sconv.h
@@ -23,6 +23,7 @@
   USA.
 ***/
 
+#include <pulse/gccmacro.h>
 #include <pulse/sample.h>
 
 typedef void (*pa_convert_func_t)(unsigned n, const void *a, void *b);
diff --git a/src/pulsecore/sconv_sse.c b/src/pulsecore/sconv_sse.c
index 26daa22..ac99bf6 100644
--- a/src/pulsecore/sconv_sse.c
+++ b/src/pulsecore/sconv_sse.c
@@ -27,7 +27,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <pulsecore/g711.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/endianmacros.h>
 
diff --git a/src/pulsecore/shared.c b/src/pulsecore/shared.c
index edd7b7f..368a6c3 100644
--- a/src/pulsecore/shared.c
+++ b/src/pulsecore/shared.c
@@ -24,7 +24,6 @@
 #endif
 
 #include <pulse/xmalloc.h>
-#include <pulsecore/log.h>
 #include <pulsecore/macro.h>
 
 #include "shared.h"
diff --git a/src/pulsecore/shmasyncq.h b/src/pulsecore/shmasyncq.h
index 9845c39..4212055 100644
--- a/src/pulsecore/shmasyncq.h
+++ b/src/pulsecore/shmasyncq.h
@@ -24,7 +24,6 @@
 
 #include <sys/types.h>
 
-#include <pulse/def.h>
 #include <pulsecore/macro.h>
 
 /* Similar to pa_asyncq, but stores data in a shared memory segment */
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 2e718c4..d386b00 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -26,7 +26,6 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index cbff5ca..85c22ec 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -36,9 +36,10 @@ typedef struct pa_sink_volume_change pa_sink_volume_change;
 
 #include <pulsecore/core.h>
 #include <pulsecore/idxset.h>
+#include <pulsecore/memchunk.h>
 #include <pulsecore/source.h>
 #include <pulsecore/module.h>
-#include <pulsecore/refcnt.h>
+#include <pulsecore/asyncmsgq.h>
 #include <pulsecore/msgobject.h>
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/card.h>
diff --git a/src/pulsecore/sndfile-util.c b/src/pulsecore/sndfile-util.c
index 292eb6e..85d7a1c 100644
--- a/src/pulsecore/sndfile-util.c
+++ b/src/pulsecore/sndfile-util.c
@@ -29,7 +29,6 @@
 #include <pulse/utf8.h>
 
 #include <pulsecore/macro.h>
-#include <pulsecore/core-util.h>
 
 #include "sndfile-util.h"
 
diff --git a/src/pulsecore/socket-util.c b/src/pulsecore/socket-util.c
index 3023395..46623c4 100644
--- a/src/pulsecore/socket-util.c
+++ b/src/pulsecore/socket-util.c
@@ -25,14 +25,12 @@
 #include <config.h>
 #endif
 
-#include <stdarg.h>
 #include <stdlib.h>
 #include <signal.h>
 #include <errno.h>
 #include <string.h>
 #include <stdio.h>
 #include <sys/types.h>
-#include <fcntl.h>
 #include <unistd.h>
 #include <sys/stat.h>
 
@@ -55,8 +53,6 @@
 #include <netdb.h>
 #endif
 
-#include <pulse/xmalloc.h>
-
 #include <pulsecore/core-error.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/log.h>
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index d33eca5..96b5fb8 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -25,7 +25,6 @@
 
 #include <stdlib.h>
 #include <stdio.h>
-#include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -40,7 +39,6 @@
 #include <pulsecore/log.h>
 #include <pulsecore/thread-mq.h>
 #include <pulsecore/core-util.h>
-#include <pulsecore/sample-util.h>
 #include <pulsecore/sndfile-util.h>
 
 #include "sound-file-stream.h"
diff --git a/src/pulsecore/sound-file.c b/src/pulsecore/sound-file.c
index d8c10b1..3db0981 100644
--- a/src/pulsecore/sound-file.c
+++ b/src/pulsecore/sound-file.c
@@ -23,7 +23,6 @@
 #include <config.h>
 #endif
 
-#include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 78a5203..7024802 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -26,7 +26,6 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
 
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 902cc03..b68dfd5 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -28,23 +28,23 @@ typedef struct pa_source_volume_change pa_source_volume_change;
 
 #include <inttypes.h>
 
+#include <pulse/def.h>
 #include <pulse/sample.h>
 #include <pulse/channelmap.h>
 #include <pulse/volume.h>
 
 #include <pulsecore/core.h>
 #include <pulsecore/idxset.h>
-#include <pulsecore/memblock.h>
 #include <pulsecore/memchunk.h>
 #include <pulsecore/sink.h>
 #include <pulsecore/module.h>
 #include <pulsecore/asyncmsgq.h>
 #include <pulsecore/msgobject.h>
 #include <pulsecore/rtpoll.h>
-#include <pulsecore/source-output.h>
 #include <pulsecore/card.h>
 #include <pulsecore/queue.h>
 #include <pulsecore/thread-mq.h>
+#include <pulsecore/source-output.h>
 
 #define PA_MAX_OUTPUTS_PER_SOURCE 32
 
diff --git a/src/pulsecore/svolume_arm.c b/src/pulsecore/svolume_arm.c
index 098f10e..b779733 100644
--- a/src/pulsecore/svolume_arm.c
+++ b/src/pulsecore/svolume_arm.c
@@ -24,11 +24,8 @@
 #include <config.h>
 #endif
 
-#include <pulse/timeval.h>
 #include <pulsecore/random.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/g711.h>
-#include <pulsecore/core-util.h>
 #include <pulsecore/endianmacros.h>
 
 #include "cpu-arm.h"
diff --git a/src/pulsecore/svolume_c.c b/src/pulsecore/svolume_c.c
index e55d0d7..6c0fbf8 100644
--- a/src/pulsecore/svolume_c.c
+++ b/src/pulsecore/svolume_c.c
@@ -27,7 +27,6 @@
 
 #include <pulsecore/macro.h>
 #include <pulsecore/g711.h>
-#include <pulsecore/core-util.h>
 #include <pulsecore/endianmacros.h>
 
 #include "sample-util.h"
diff --git a/src/pulsecore/svolume_mmx.c b/src/pulsecore/svolume_mmx.c
index 28bbfdd..6401081 100644
--- a/src/pulsecore/svolume_mmx.c
+++ b/src/pulsecore/svolume_mmx.c
@@ -24,13 +24,10 @@
 #include <config.h>
 #endif
 
-#include <pulse/timeval.h>
 #include <pulse/rtclock.h>
 
 #include <pulsecore/random.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/g711.h>
-#include <pulsecore/core-util.h>
 #include <pulsecore/endianmacros.h>
 
 #include "cpu-x86.h"
diff --git a/src/pulsecore/svolume_orc.c b/src/pulsecore/svolume_orc.c
index 270b291..8e1e46c 100644
--- a/src/pulsecore/svolume_orc.c
+++ b/src/pulsecore/svolume_orc.c
@@ -26,7 +26,6 @@
 #endif
 
 #include "cpu-orc.h"
-#include <pulse/xmalloc.h>
 #include <pulse/rtclock.h>
 #include <pulsecore/sample-util.h>
 #include <pulsecore/random.h>
diff --git a/src/pulsecore/svolume_sse.c b/src/pulsecore/svolume_sse.c
index dcd71c4..f713761 100644
--- a/src/pulsecore/svolume_sse.c
+++ b/src/pulsecore/svolume_sse.c
@@ -24,13 +24,10 @@
 #include <config.h>
 #endif
 
-#include <pulse/timeval.h>
 #include <pulse/rtclock.h>
 
 #include <pulsecore/random.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/g711.h>
-#include <pulsecore/core-util.h>
 #include <pulsecore/endianmacros.h>
 
 #include "cpu-x86.h"
diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h
index 0091eeb..5f729bc 100644
--- a/src/pulsecore/tagstruct.h
+++ b/src/pulsecore/tagstruct.h
@@ -31,7 +31,6 @@
 #include <pulse/channelmap.h>
 #include <pulse/volume.h>
 #include <pulse/proplist.h>
-#include <pulse/gccmacro.h>
 
 #include <pulsecore/macro.h>
 
diff --git a/src/pulsecore/thread-mq.c b/src/pulsecore/thread-mq.c
index 73997a7..51fb765 100644
--- a/src/pulsecore/thread-mq.c
+++ b/src/pulsecore/thread-mq.c
@@ -26,16 +26,9 @@
 #include <unistd.h>
 #include <errno.h>
 
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/atomic.h>
-#include <pulsecore/once.h>
-#include <pulsecore/log.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/semaphore.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/flist.h>
 
 #include "thread-mq.h"
 
diff --git a/src/pulsecore/thread-posix.c b/src/pulsecore/thread-posix.c
index 58bcb72..3f4ae5c 100644
--- a/src/pulsecore/thread-posix.c
+++ b/src/pulsecore/thread-posix.c
@@ -33,8 +33,6 @@
 #endif
 
 #include <pulse/xmalloc.h>
-#include <pulsecore/mutex.h>
-#include <pulsecore/once.h>
 #include <pulsecore/atomic.h>
 #include <pulsecore/macro.h>
 
diff --git a/src/pulsecore/thread-win32.c b/src/pulsecore/thread-win32.c
index 7d458b9..e0839af 100644
--- a/src/pulsecore/thread-win32.c
+++ b/src/pulsecore/thread-win32.c
@@ -28,7 +28,6 @@
 #include <windows.h>
 
 #include <pulse/xmalloc.h>
-#include <pulsecore/log.h>
 #include <pulsecore/once.h>
 
 #include "thread.h"
diff --git a/src/pulsecore/thread.h b/src/pulsecore/thread.h
index 79b61ae..9cabb89 100644
--- a/src/pulsecore/thread.h
+++ b/src/pulsecore/thread.h
@@ -24,6 +24,8 @@
 ***/
 
 #include <pulse/def.h>
+#include <pulse/gccmacro.h>
+
 #include <pulsecore/once.h>
 #include <pulsecore/core-util.h>
 
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
index a8dd333..be054a5 100644
--- a/src/pulsecore/time-smoother.c
+++ b/src/pulsecore/time-smoother.c
@@ -24,6 +24,7 @@
 #endif
 
 #include <stdio.h>
+#include <math.h>
 
 #include <pulse/sample.h>
 #include <pulse/xmalloc.h>
diff --git a/src/pulsecore/tokenizer.c b/src/pulsecore/tokenizer.c
index 1eb466d..a0ade69 100644
--- a/src/pulsecore/tokenizer.c
+++ b/src/pulsecore/tokenizer.c
@@ -27,7 +27,6 @@
 #include <stdlib.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/gccmacro.h>
 
 #include <pulsecore/dynarray.h>
 #include <pulsecore/macro.h>
diff --git a/src/tests/asyncmsgq-test.c b/src/tests/asyncmsgq-test.c
index 94bfcea..96e5a0d 100644
--- a/src/tests/asyncmsgq-test.c
+++ b/src/tests/asyncmsgq-test.c
@@ -25,12 +25,9 @@
 #include <stdlib.h>
 #include <unistd.h>
 
-#include <pulse/util.h>
-#include <pulse/xmalloc.h>
 #include <pulsecore/asyncmsgq.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
 
 enum {
diff --git a/src/tests/asyncq-test.c b/src/tests/asyncq-test.c
index eae8cce..46bac9f 100644
--- a/src/tests/asyncq-test.c
+++ b/src/tests/asyncq-test.c
@@ -26,11 +26,9 @@
 #include <unistd.h>
 
 #include <pulse/util.h>
-#include <pulse/xmalloc.h>
 #include <pulsecore/asyncq.h>
 #include <pulsecore/thread.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
 
 static void producer(void *_q) {
diff --git a/src/tests/channelmap-test.c b/src/tests/channelmap-test.c
index 6cf58fb..70d6a6a 100644
--- a/src/tests/channelmap-test.c
+++ b/src/tests/channelmap-test.c
@@ -6,7 +6,6 @@
 #include <assert.h>
 
 #include <pulse/channelmap.h>
-#include <pulse/gccmacro.h>
 
 int main(int argc, char *argv[]) {
     char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
diff --git a/src/tests/cpulimit-test.c b/src/tests/cpulimit-test.c
index 5b11bc4..8bd0341 100644
--- a/src/tests/cpulimit-test.c
+++ b/src/tests/cpulimit-test.c
@@ -28,7 +28,6 @@
 #include <signal.h>
 
 #include <pulse/mainloop.h>
-#include <pulse/gccmacro.h>
 
 #ifdef TEST2
 #include <pulse/mainloop-signal.h>
diff --git a/src/tests/extended-test.c b/src/tests/extended-test.c
index ab8ab2c..631fdc8 100644
--- a/src/tests/extended-test.c
+++ b/src/tests/extended-test.c
@@ -22,13 +22,11 @@
 #endif
 
 #include <signal.h>
-#include <string.h>
 #include <errno.h>
 #include <unistd.h>
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <getopt.h>
 #include <math.h>
 
 #include <pulse/pulseaudio.h>
diff --git a/src/tests/get-binary-name-test.c b/src/tests/get-binary-name-test.c
index e49f2ef..4afe81b 100644
--- a/src/tests/get-binary-name-test.c
+++ b/src/tests/get-binary-name-test.c
@@ -21,7 +21,6 @@
 #include <config.h>
 #endif
 
-#include <limits.h>
 #include <stdio.h>
 #include <string.h>
 
diff --git a/src/tests/interpol-test.c b/src/tests/interpol-test.c
index ca5c50b..ffe4ab3 100644
--- a/src/tests/interpol-test.c
+++ b/src/tests/interpol-test.c
@@ -28,7 +28,6 @@
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <math.h>
 
 #include <pulse/pulseaudio.h>
 #include <pulse/mainloop.h>
diff --git a/src/tests/mainloop-test.c b/src/tests/mainloop-test.c
index cd54bcf..ab23de4 100644
--- a/src/tests/mainloop-test.c
+++ b/src/tests/mainloop-test.c
@@ -28,7 +28,6 @@
 
 #include <pulse/rtclock.h>
 #include <pulse/timeval.h>
-#include <pulse/gccmacro.h>
 
 #include <pulsecore/core-util.h>
 #include <pulsecore/core-rtclock.h>
diff --git a/src/tests/mcalign-test.c b/src/tests/mcalign-test.c
index 92e3e14..75a71dd 100644
--- a/src/tests/mcalign-test.c
+++ b/src/tests/mcalign-test.c
@@ -29,8 +29,6 @@
 #include <stdlib.h>
 #include <time.h>
 
-#include <pulse/gccmacro.h>
-
 #include <pulsecore/core-util.h>
 #include <pulsecore/mcalign.h>
 
diff --git a/src/tests/mix-test.c b/src/tests/mix-test.c
index 55844e7..7c05b8e 100644
--- a/src/tests/mix-test.c
+++ b/src/tests/mix-test.c
@@ -26,7 +26,6 @@
 #include <pulse/sample.h>
 #include <pulse/volume.h>
 
-#include <pulsecore/resampler.h>
 #include <pulsecore/macro.h>
 #include <pulsecore/endianmacros.h>
 #include <pulsecore/memblock.h>
diff --git a/src/tests/once-test.c b/src/tests/once-test.c
index 86a1247..1379168 100644
--- a/src/tests/once-test.c
+++ b/src/tests/once-test.c
@@ -24,11 +24,9 @@
 #include <pthread.h>
 
 #include <pulsecore/thread.h>
-#include <pulsecore/mutex.h>
 #include <pulsecore/once.h>
 #include <pulsecore/log.h>
 #include <pulsecore/core-util.h>
-#include <pulsecore/core-error.h>
 #include <pulsecore/atomic.h>
 #include <pulse/xmalloc.h>
 
diff --git a/src/tests/pacat-simple.c b/src/tests/pacat-simple.c
index d4224e1..7d119c4 100644
--- a/src/tests/pacat-simple.c
+++ b/src/tests/pacat-simple.c
@@ -29,7 +29,6 @@
 
 #include <pulse/simple.h>
 #include <pulse/error.h>
-#include <pulse/gccmacro.h>
 
 #define BUFSIZE 1024
 
diff --git a/src/tests/parec-simple.c b/src/tests/parec-simple.c
index 9f19ff4..dfa43f0 100644
--- a/src/tests/parec-simple.c
+++ b/src/tests/parec-simple.c
@@ -28,7 +28,6 @@
 
 #include <pulse/simple.h>
 #include <pulse/error.h>
-#include <pulse/gccmacro.h>
 
 #define BUFSIZE 1024
 
diff --git a/src/tests/queue-test.c b/src/tests/queue-test.c
index 7ee2693..b21775e 100644
--- a/src/tests/queue-test.c
+++ b/src/tests/queue-test.c
@@ -25,11 +25,8 @@
 #include <stdlib.h>
 #include <unistd.h>
 
-#include <pulse/util.h>
-#include <pulse/xmalloc.h>
 #include <pulsecore/queue.h>
 #include <pulsecore/log.h>
-#include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
 
 int main(int argc, char *argv[]) {
diff --git a/src/tests/remix-test.c b/src/tests/remix-test.c
index 4990bf9..19f5582 100644
--- a/src/tests/remix-test.c
+++ b/src/tests/remix-test.c
@@ -24,13 +24,10 @@
 #include <stdio.h>
 
 #include <pulse/sample.h>
-#include <pulse/volume.h>
 
 #include <pulsecore/resampler.h>
 #include <pulsecore/macro.h>
-#include <pulsecore/endianmacros.h>
 #include <pulsecore/memblock.h>
-#include <pulsecore/sample-util.h>
 
 int main(int argc, char *argv[]) {
 
diff --git a/src/tests/resampler-test.c b/src/tests/resampler-test.c
index 097465a..78461da 100644
--- a/src/tests/resampler-test.c
+++ b/src/tests/resampler-test.c
@@ -37,7 +37,6 @@
 #include <pulsecore/endianmacros.h>
 #include <pulsecore/memblock.h>
 #include <pulsecore/sample-util.h>
-#include <pulsecore/core-rtclock.h>
 #include <pulsecore/core-util.h>
 
 static void dump_block(const pa_sample_spec *ss, const pa_memchunk *chunk) {
diff --git a/src/tests/rtstutter.c b/src/tests/rtstutter.c
index 402a8c0..739683d 100644
--- a/src/tests/rtstutter.c
+++ b/src/tests/rtstutter.c
@@ -27,7 +27,6 @@
 #include <unistd.h>
 #include <time.h>
 #include <inttypes.h>
-#include <string.h>
 
 #ifdef HAVE_PTHREAD
 #include <pthread.h>
diff --git a/src/tests/strlist-test.c b/src/tests/strlist-test.c
index 10f370c..86f4f07 100644
--- a/src/tests/strlist-test.c
+++ b/src/tests/strlist-test.c
@@ -1,7 +1,6 @@
 #include <stdio.h>
 
 #include <pulse/xmalloc.h>
-#include <pulse/gccmacro.h>
 
 #include <pulsecore/strlist.h>
 
diff --git a/src/tests/sync-playback.c b/src/tests/sync-playback.c
index bb64a91..8eaa25f 100644
--- a/src/tests/sync-playback.c
+++ b/src/tests/sync-playback.c
@@ -22,13 +22,11 @@
 #endif
 
 #include <signal.h>
-#include <string.h>
 #include <errno.h>
 #include <unistd.h>
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <getopt.h>
 #include <math.h>
 
 #include <pulse/pulseaudio.h>
diff --git a/src/tests/thread-mainloop-test.c b/src/tests/thread-mainloop-test.c
index 4696fb0..7227597 100644
--- a/src/tests/thread-mainloop-test.c
+++ b/src/tests/thread-mainloop-test.c
@@ -29,7 +29,6 @@
 #include <pulse/timeval.h>
 #include <pulse/util.h>
 #include <pulse/thread-mainloop.h>
-#include <pulse/gccmacro.h>
 
 #include <pulsecore/macro.h>
 #include <pulsecore/core-rtclock.h>
diff --git a/src/tests/voltest.c b/src/tests/voltest.c
index 551f7ec..ece86f8 100644
--- a/src/tests/voltest.c
+++ b/src/tests/voltest.c
@@ -22,9 +22,9 @@
 #endif
 
 #include <stdio.h>
+#include <math.h>
 
 #include <pulse/volume.h>
-#include <pulse/gccmacro.h>
 
 #include <pulsecore/macro.h>
 
diff --git a/src/utils/pabrowse.c b/src/utils/pabrowse.c
index a349e41..a00fb4d 100644
--- a/src/utils/pabrowse.c
+++ b/src/utils/pabrowse.c
@@ -29,7 +29,6 @@
 
 #include <pulse/browser.h>
 #include <pulse/pulseaudio.h>
-#include <pulse/rtclock.h>
 
 #include <pulsecore/core-util.h>
 
diff --git a/src/utils/pacmd.c b/src/utils/pacmd.c
index 4d07c4c..f077980 100644
--- a/src/utils/pacmd.c
+++ b/src/utils/pacmd.c
@@ -32,7 +32,6 @@
 #include <sys/un.h>
 #include <locale.h>
 
-#include <pulse/error.h>
 #include <pulse/util.h>
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index a9d4c82..e6f9c17 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -30,7 +30,6 @@
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <limits.h>
 #include <getopt.h>
 #include <locale.h>
 
diff --git a/src/utils/pasuspender.c b/src/utils/pasuspender.c
index 534b77b..e1ee251 100644
--- a/src/utils/pasuspender.c
+++ b/src/utils/pasuspender.c
@@ -33,7 +33,6 @@
 #include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <limits.h>
 #include <getopt.h>
 #include <locale.h>
 
diff --git a/src/utils/pax11publish.c b/src/utils/pax11publish.c
index 6600363..41361a1 100644
--- a/src/utils/pax11publish.c
+++ b/src/utils/pax11publish.c
@@ -25,7 +25,6 @@
 
 #include <stdio.h>
 #include <getopt.h>
-#include <string.h>
 #include <assert.h>
 #include <locale.h>
 

commit 9ffa9382dd8627aa12f1cffd67c96a6dacb4420f
Author: Colin Guthrie <colin at mageia.org>
Date:   Wed Jun 8 00:47:19 2011 +0100

    database: Support legacy format database entries.
    
    This adds code to specifically support legacy entries.
    I kept this code in a separate commit so that it can be (relatively)
    easily removed at some point in the future.

diff --git a/configure.ac b/configure.ac
index f95cbee..fbdb5d1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1138,6 +1138,13 @@ if test "x$enable_legacy_runtime_dir" != "xno" ; then
         AC_DEFINE(ENABLE_LEGACY_RUNTIME_DIR, [1], [Legacy runtime dir])
 fi
 
+AC_ARG_ENABLE([legacy-database-entry-format],
+        AS_HELP_STRING([--disable-legacy-database-entry-format], [Try to load legacy (< 1.0) database files (card, device and volume restore).]))
+if test "x$enable_legacy_database_entry_format" != "xno" ; then
+        AC_DEFINE(ENABLE_LEGACY_DATABASE_ENTRY_FORMAT, [1], [Legacy database entry format])
+fi
+AC_DEFINE([WIBBLE], 1, [Just a test.])
+
 AC_ARG_ENABLE([static-bins],
     AS_HELP_STRING([--enable-static-bins],[Statically link executables.]))
 
@@ -1229,6 +1236,8 @@ AS_IF([test "x$HAVE_TDB" = "x1"], ENABLE_TDB=yes, ENABLE_TDB=no)
 AS_IF([test "x$HAVE_GDBM" = "x1"], ENABLE_GDBM=yes, ENABLE_GDBM=no)
 AS_IF([test "x$HAVE_SIMPLEDB" = "x1"], ENABLE_SIMPLEDB=yes, ENABLE_SIMPLEDB=no)
 AS_IF([test "x$USE_PER_USER_ESOUND_SOCKET" = "x1"], ENABLE_PER_USER_ESOUND_SOCKET=yes, ENABLE_PER_USER_ESOUND_SOCKET=no)
+AS_IF([test "x$enable_legacy_runtime_dir" != "xno"], ENABLE_LEGACY_RUNTIME_DIR=yes, ENABLE_LEGACY_RUNTIME_DIR=no)
+AS_IF([test "x$enable_legacy_database_entry_format" != "xno"], ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=yes, ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=no)
 
 echo "
  ---{ $PACKAGE_NAME $VERSION }---
@@ -1279,6 +1288,9 @@ echo "
     Enable per-user EsounD socket: ${ENABLE_PER_USER_ESOUND_SOCKET}
     Force preopen:                 ${FORCE_PREOPEN}
     Preopened modules:             ${PREOPEN_MODS}
+
+    Legacy Runtime Dir Support:    ${ENABLE_LEGACY_RUNTIME_DIR}
+    Legacy Database Entry Support: ${ENABLE_LEGACY_DATABASE_ENTRY_FORMAT}
 "
 
 if test "${ENABLE_DBUS}" = "no" && test "x$os_is_win32" != "x1" ; then
diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
index 83acbd7..f1c1930 100644
--- a/src/modules/module-card-restore.c
+++ b/src/modules/module-card-restore.c
@@ -70,7 +70,7 @@ struct userdata {
     pa_database *database;
 };
 
-#define ENTRY_VERSION 2
+#define ENTRY_VERSION 1
 
 struct entry {
     uint8_t version;
@@ -92,6 +92,13 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
     pa_log_info("Synced.");
 }
 
+static void trigger_save(struct userdata *u) {
+    if (u->save_time_event)
+        return;
+
+    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
+}
+
 static struct entry* entry_new(void) {
     struct entry *r = pa_xnew0(struct entry, 1);
     r->version = ENTRY_VERSION;
@@ -105,6 +112,68 @@ static void entry_free(struct entry* e) {
     pa_xfree(e);
 }
 
+static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    pa_bool_t r;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu8(t, e->version);
+    pa_tagstruct_puts(t, e->profile);
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
+
+    pa_tagstruct_free(t);
+
+    return r;
+}
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+
+#define LEGACY_ENTRY_VERSION 1
+static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
+    struct legacy_entry {
+        uint8_t version;
+        char profile[PA_NAME_MAX];
+    } PA_GCC_PACKED ;
+    struct legacy_entry *le;
+    struct entry *e;
+
+    pa_assert(u);
+    pa_assert(data);
+
+    if (data->size != sizeof(struct legacy_entry)) {
+        pa_log_debug("Size does not match.");
+        return NULL;
+    }
+
+    le = (struct legacy_entry*)data->data;
+
+    if (le->version != LEGACY_ENTRY_VERSION) {
+        pa_log_debug("Version mismatch.");
+        return NULL;
+    }
+
+    if (!memchr(le->profile, 0, sizeof(le->profile))) {
+        pa_log_warn("Profile has missing NUL byte.");
+        return NULL;
+    }
+
+    e = entry_new();
+    e->profile = pa_xstrdup(le->profile);
+    return e;
+}
+#endif
+
 static struct entry* entry_read(struct userdata *u, const char *name) {
     pa_datum key, data;
     struct entry *e = NULL;
@@ -150,40 +219,21 @@ fail:
         entry_free(e);
     if (t)
         pa_tagstruct_free(t);
-    pa_datum_free(&data);
-    return NULL;
-}
-
-static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
-    pa_tagstruct *t;
-    pa_datum key, data;
-    pa_bool_t r;
-
-    pa_assert(u);
-    pa_assert(name);
-    pa_assert(e);
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu8(t, e->version);
-    pa_tagstruct_puts(t, e->profile);
-
-    key.data = (char *) name;
-    key.size = strlen(name);
-
-    data.data = (void*)pa_tagstruct_data(t, &data.size);
-
-    r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
-
-    pa_tagstruct_free(t);
 
-    return r;
-}
-
-static void trigger_save(struct userdata *u) {
-    if (u->save_time_event)
-        return;
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+    pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
+    if ((e = legacy_entry_read(u, &data))) {
+        pa_log_debug("Success. Saving new format for key: %s", name);
+        if (entry_write(u, name, e))
+            trigger_save(u);
+        pa_datum_free(&data);
+        return e;
+    } else
+        pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
+#endif
 
-    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
+    pa_datum_free(&data);
+    return NULL;
 }
 
 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 6bac7c0..ea92cd0 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -130,7 +130,7 @@ struct userdata {
     role_indexes_t preferred_sources;
 };
 
-#define ENTRY_VERSION 2
+#define ENTRY_VERSION 1
 
 struct entry {
     uint8_t version;
@@ -152,6 +152,44 @@ enum {
 };
 
 
+/* Forward declarations */
+#ifdef DUMP_DATABASE
+static void dump_database(struct userdata *);
+#endif
+static void notify_subscribers(struct userdata *);
+
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
+    struct userdata *u = userdata;
+
+    pa_assert(a);
+    pa_assert(e);
+    pa_assert(u);
+
+    pa_assert(e == u->save_time_event);
+    u->core->mainloop->time_free(u->save_time_event);
+    u->save_time_event = NULL;
+
+    pa_database_sync(u->database);
+    pa_log_info("Synced.");
+
+#ifdef DUMP_DATABASE
+    dump_database(u);
+#endif
+}
+
+static void trigger_save(struct userdata *u) {
+
+    pa_assert(u);
+
+    notify_subscribers(u);
+
+    if (u->save_time_event)
+        return;
+
+    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
+}
+
 static struct entry* entry_new(void) {
     struct entry *r = pa_xnew0(struct entry, 1);
     r->version = ENTRY_VERSION;
@@ -165,6 +203,81 @@ static void entry_free(struct entry* e) {
     pa_xfree(e->icon);
 }
 
+static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    pa_bool_t r;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu8(t, e->version);
+    pa_tagstruct_puts(t, e->description);
+    pa_tagstruct_put_boolean(t, e->user_set_description);
+    pa_tagstruct_puts(t, e->icon);
+    for (uint8_t i=0; i<ROLE_MAX; ++i)
+        pa_tagstruct_putu32(t, e->priority[i]);
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
+
+    pa_tagstruct_free(t);
+
+    return r;
+}
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+
+#define LEGACY_ENTRY_VERSION 1
+static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
+    struct legacy_entry {
+        uint8_t version;
+        char description[PA_NAME_MAX];
+        pa_bool_t user_set_description;
+        char icon[PA_NAME_MAX];
+        role_indexes_t priority;
+    } PA_GCC_PACKED;
+    struct legacy_entry *le;
+    struct entry *e;
+
+    pa_assert(u);
+    pa_assert(data);
+
+    if (data->size != sizeof(struct legacy_entry)) {
+        pa_log_debug("Size does not match.");
+        return NULL;
+    }
+
+    le = (struct legacy_entry*)data->data;
+
+    if (le->version != LEGACY_ENTRY_VERSION) {
+        pa_log_debug("Version mismatch.");
+        return NULL;
+    }
+
+    if (!memchr(le->description, 0, sizeof(le->description))) {
+        pa_log_warn("Description has missing NUL byte.");
+        return NULL;
+    }
+
+    if (!memchr(le->icon, 0, sizeof(le->icon))) {
+        pa_log_warn("Icon has missing NUL byte.");
+        return NULL;
+    }
+
+    e = entry_new();
+    e->description = pa_xstrdup(le->description);
+    e->icon = pa_xstrdup(le->icon);
+    return e;
+}
+#endif
+
 static struct entry* entry_read(struct userdata *u, const char *name) {
     pa_datum key, data;
     struct entry *e = NULL;
@@ -217,37 +330,21 @@ fail:
         entry_free(e);
     if (t)
         pa_tagstruct_free(t);
-    pa_datum_free(&data);
-    return NULL;
-}
-
-static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
-    pa_tagstruct *t;
-    pa_datum key, data;
-    pa_bool_t r;
-
-    pa_assert(u);
-    pa_assert(name);
-    pa_assert(e);
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu8(t, e->version);
-    pa_tagstruct_puts(t, e->description);
-    pa_tagstruct_put_boolean(t, e->user_set_description);
-    pa_tagstruct_puts(t, e->icon);
-    for (uint8_t i=0; i<ROLE_MAX; ++i)
-        pa_tagstruct_putu32(t, e->priority[i]);
-
-    key.data = (char *) name;
-    key.size = strlen(name);
-
-    data.data = (void*)pa_tagstruct_data(t, &data.size);
-
-    r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
 
-    pa_tagstruct_free(t);
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+    pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
+    if ((e = legacy_entry_read(u, &data))) {
+        pa_log_debug("Success. Saving new format for key: %s", name);
+        if (entry_write(u, name, e))
+            trigger_save(u);
+        pa_datum_free(&data);
+        return e;
+    } else
+        pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
+#endif
 
-    return r;
+    pa_datum_free(&data);
+    return NULL;
 }
 
 #ifdef DUMP_DATABASE
@@ -332,25 +429,6 @@ static void dump_database(struct userdata *u) {
 }
 #endif
 
-static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
-    struct userdata *u = userdata;
-
-    pa_assert(a);
-    pa_assert(e);
-    pa_assert(u);
-
-    pa_assert(e == u->save_time_event);
-    u->core->mainloop->time_free(u->save_time_event);
-    u->save_time_event = NULL;
-
-    pa_database_sync(u->database);
-    pa_log_info("Synced.");
-
-#ifdef DUMP_DATABASE
-    dump_database(u);
-#endif
-}
-
 static void notify_subscribers(struct userdata *u) {
 
     pa_native_connection *c;
@@ -372,18 +450,6 @@ static void notify_subscribers(struct userdata *u) {
     }
 }
 
-static void trigger_save(struct userdata *u) {
-
-    pa_assert(u);
-
-    notify_subscribers(u);
-
-    if (u->save_time_event)
-        return;
-
-    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
-}
-
 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
 
     pa_assert(a);
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 35e455d..495a911 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -107,7 +107,7 @@ enum {
 };
 
 
-#define ENTRY_VERSION 3
+#define ENTRY_VERSION 1
 
 struct entry {
     uint8_t version;
@@ -134,10 +134,22 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
     pa_log_info("Synced.");
 }
 
-static struct entry* entry_new(void) {
+static void trigger_save(struct userdata *u) {
+    if (u->save_time_event)
+        return;
+
+    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
+}
+
+static struct entry* entry_new(pa_bool_t add_pcm_format) {
     struct entry *r = pa_xnew0(struct entry, 1);
     r->version = ENTRY_VERSION;
     r->formats = pa_idxset_new(NULL, NULL);
+    if (add_pcm_format) {
+        pa_format_info *f = pa_format_info_new();
+        f->encoding = PA_ENCODING_PCM;
+        pa_idxset_put(r->formats, f, NULL);
+    }
     return r;
 }
 
@@ -149,6 +161,105 @@ static void entry_free(struct entry* e) {
     pa_xfree(e);
 }
 
+static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    pa_bool_t r;
+    uint32_t i;
+    pa_format_info *f;
+    uint8_t n_formats;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    n_formats = pa_idxset_size(e->formats);
+    pa_assert(n_formats > 0);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu8(t, e->version);
+    pa_tagstruct_put_boolean(t, e->volume_valid);
+    pa_tagstruct_put_channel_map(t, &e->channel_map);
+    pa_tagstruct_put_cvolume(t, &e->volume);
+    pa_tagstruct_put_boolean(t, e->muted_valid);
+    pa_tagstruct_put_boolean(t, e->muted);
+    pa_tagstruct_put_boolean(t, e->port_valid);
+    pa_tagstruct_puts(t, e->port);
+    pa_tagstruct_putu8(t, n_formats);
+
+    PA_IDXSET_FOREACH(f, e->formats, i) {
+        pa_tagstruct_put_format_info(t, f);
+    }
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
+
+    pa_tagstruct_free(t);
+
+    return r;
+}
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+
+#define LEGACY_ENTRY_VERSION 2
+static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
+    struct legacy_entry {
+        uint8_t version;
+        pa_bool_t muted_valid:1, volume_valid:1, port_valid:1;
+        pa_bool_t muted:1;
+        pa_channel_map channel_map;
+        pa_cvolume volume;
+        char port[PA_NAME_MAX];
+    } PA_GCC_PACKED;
+    struct legacy_entry *le;
+    struct entry *e;
+
+    pa_assert(u);
+    pa_assert(data);
+
+    if (data->size != sizeof(struct legacy_entry)) {
+        pa_log_debug("Size does not match.");
+        return NULL;
+    }
+
+    le = (struct legacy_entry*)data->data;
+
+    if (le->version != LEGACY_ENTRY_VERSION) {
+        pa_log_debug("Version mismatch.");
+        return NULL;
+    }
+
+    if (!memchr(le->port, 0, sizeof(le->port))) {
+        pa_log_warn("Port has missing NUL byte.");
+        return NULL;
+    }
+
+    if (le->volume_valid && !pa_channel_map_valid(&le->channel_map)) {
+        pa_log_warn("Invalid channel map.");
+        return NULL;
+    }
+
+    if (le->volume_valid && (!pa_cvolume_valid(&le->volume) || !pa_cvolume_compatible_with_channel_map(&le->volume, &le->channel_map))) {
+        pa_log_warn("Volume and channel map don't match.");
+        return NULL;
+    }
+
+    e = entry_new(TRUE);
+    e->muted_valid = le->muted_valid;
+    e->volume_valid = le->volume_valid;
+    e->port_valid = le->port_valid;
+    e->muted = le->muted;
+    e->channel_map = le->channel_map;
+    e->volume = le->volume;
+    e->port = pa_xstrdup(le->port);
+    return e;
+}
+#endif
+
 static struct entry* entry_read(struct userdata *u, const char *name) {
     pa_datum key, data;
     struct entry *e = NULL;
@@ -168,7 +279,7 @@ static struct entry* entry_read(struct userdata *u, const char *name) {
         goto fail;
 
     t = pa_tagstruct_new(data.data, data.size);
-    e = entry_new();
+    e = entry_new(FALSE);
 
     if (pa_tagstruct_getu8(t, &e->version) < 0 ||
         e->version > ENTRY_VERSION ||
@@ -179,7 +290,7 @@ static struct entry* entry_read(struct userdata *u, const char *name) {
         pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
         pa_tagstruct_get_boolean(t, &e->port_valid) < 0 ||
         pa_tagstruct_gets(t, &port) < 0 ||
-        pa_tagstruct_getu8(t, &n_formats) < 0) {
+        pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1) {
 
         goto fail;
     }
@@ -221,46 +332,21 @@ fail:
         entry_free(e);
     if (t)
         pa_tagstruct_free(t);
-    pa_datum_free(&data);
-    return NULL;
-}
-
-static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
-    pa_tagstruct *t;
-    pa_datum key, data;
-    pa_bool_t r;
-    uint32_t i;
-    pa_format_info *f;
-
-    pa_assert(u);
-    pa_assert(name);
-    pa_assert(e);
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu8(t, e->version);
-    pa_tagstruct_put_boolean(t, e->volume_valid);
-    pa_tagstruct_put_channel_map(t, &e->channel_map);
-    pa_tagstruct_put_cvolume(t, &e->volume);
-    pa_tagstruct_put_boolean(t, e->muted_valid);
-    pa_tagstruct_put_boolean(t, e->muted);
-    pa_tagstruct_put_boolean(t, e->port_valid);
-    pa_tagstruct_puts(t, e->port);
-    pa_tagstruct_putu8(t, pa_idxset_size(e->formats));
-
-    PA_IDXSET_FOREACH(f, e->formats, i) {
-        pa_tagstruct_put_format_info(t, f);
-    }
 
-    key.data = (char *) name;
-    key.size = strlen(name);
-
-    data.data = (void*)pa_tagstruct_data(t, &data.size);
-
-    r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
-
-    pa_tagstruct_free(t);
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+    pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
+    if ((e = legacy_entry_read(u, &data))) {
+        pa_log_debug("Success. Saving new format for key: %s", name);
+        if (entry_write(u, name, e))
+            trigger_save(u);
+        pa_datum_free(&data);
+        return e;
+    } else
+        pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
+#endif
 
-    return r;
+    pa_datum_free(&data);
+    return NULL;
 }
 
 static struct entry* entry_copy(const struct entry *e) {
@@ -269,7 +355,7 @@ static struct entry* entry_copy(const struct entry *e) {
     pa_format_info *f;
 
     pa_assert(e);
-    r = entry_new();
+    r = entry_new(FALSE);
     r->version = e->version;
     r->muted_valid = e->muted_valid;
     r->volume_valid = e->volume_valid;
@@ -285,13 +371,6 @@ static struct entry* entry_copy(const struct entry *e) {
     return r;
 }
 
-static void trigger_save(struct userdata *u) {
-    if (u->save_time_event)
-        return;
-
-    u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
-}
-
 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
     pa_cvolume t;
 
@@ -308,6 +387,11 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
         (a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume)))
         return FALSE;
 
+    if (pa_idxset_size(a->formats) != pa_idxset_size(b->formats))
+        return FALSE;
+
+    /** TODO: Compare a bit better */
+
     return TRUE;
 }
 
@@ -336,7 +420,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         if ((old = entry_read(u, name)))
             entry = entry_copy(old);
         else
-            entry = entry_new();
+            entry = entry_new(TRUE);
 
         if (sink->save_volume) {
             entry->channel_map = sink->channel_map;
@@ -368,7 +452,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         if ((old = entry_read(u, name)))
             entry = entry_copy(old);
         else
-            entry = entry_new();
+            entry = entry_new(TRUE);
 
         if (source->save_volume) {
             entry->channel_map = source->channel_map;
@@ -702,7 +786,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             /* Read or create an entry */
             name = pa_sprintf_malloc("sink:%s", sink->name);
             if (!(e = entry_read(u, name)))
-                e = entry_new();
+                e = entry_new(FALSE);
 
             /* Read all the formats from our tagstruct */
             for (i = 0; i < n_formats; ++i) {
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index e690838..310517e 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -115,7 +115,7 @@ struct userdata {
 #endif
 };
 
-#define ENTRY_VERSION 4
+#define ENTRY_VERSION 1
 
 struct entry {
     uint8_t version;
@@ -1002,6 +1002,86 @@ static void entry_free(struct entry* e) {
     pa_xfree(e);
 }
 
+static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e, pa_bool_t replace) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    pa_bool_t r;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu8(t, e->version);
+    pa_tagstruct_put_boolean(t, e->volume_valid);
+    pa_tagstruct_put_channel_map(t, &e->channel_map);
+    pa_tagstruct_put_cvolume(t, &e->volume);
+    pa_tagstruct_put_boolean(t, e->muted_valid);
+    pa_tagstruct_put_boolean(t, e->muted);
+    pa_tagstruct_put_boolean(t, e->device_valid);
+    pa_tagstruct_puts(t, e->device);
+    pa_tagstruct_put_boolean(t, e->card_valid);
+    pa_tagstruct_puts(t, e->card);
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, replace) == 0);
+
+    pa_tagstruct_free(t);
+
+    return r;
+}
+
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+
+#define LEGACY_ENTRY_VERSION 3
+static struct entry* legacy_entry_read(struct userdata *u, pa_datum *data) {
+    struct legacy_entry {
+        uint8_t version;
+        pa_bool_t muted_valid:1, volume_valid:1, device_valid:1, card_valid:1;
+        pa_bool_t muted:1;
+        pa_channel_map channel_map;
+        pa_cvolume volume;
+        char device[PA_NAME_MAX];
+        char card[PA_NAME_MAX];
+    } PA_GCC_PACKED;
+    struct legacy_entry *le;
+    struct entry *e;
+
+    pa_assert(u);
+    pa_assert(data);
+
+    if (data->size != sizeof(struct legacy_entry)) {
+        pa_log_debug("Size does not match.");
+        return NULL;
+    }
+
+    le = (struct legacy_entry*)data->data;
+
+    if (le->version != LEGACY_ENTRY_VERSION) {
+        pa_log_debug("Version mismatch.");
+        return NULL;
+    }
+
+    if (!memchr(le->device, 0, sizeof(le->device))) {
+        pa_log_warn("Device has missing NUL byte.");
+        return NULL;
+    }
+
+    if (!memchr(le->card, 0, sizeof(le->card))) {
+        pa_log_warn("Card has missing NUL byte.");
+        return NULL;
+    }
+
+    e = entry_new();
+    e->card = pa_xstrdup(le->card);
+    return e;
+}
+#endif
+
 static struct entry *entry_read(struct userdata *u, const char *name) {
     pa_datum key, data;
     struct entry *e = NULL;
@@ -1076,41 +1156,21 @@ fail:
         entry_free(e);
     if (t)
         pa_tagstruct_free(t);
-    pa_datum_free(&data);
-    return NULL;
-}
-
-static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e, pa_bool_t replace) {
-    pa_tagstruct *t;
-    pa_datum key, data;
-    pa_bool_t r;
-
-    pa_assert(u);
-    pa_assert(name);
-    pa_assert(e);
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu8(t, e->version);
-    pa_tagstruct_put_boolean(t, e->volume_valid);
-    pa_tagstruct_put_channel_map(t, &e->channel_map);
-    pa_tagstruct_put_cvolume(t, &e->volume);
-    pa_tagstruct_put_boolean(t, e->muted_valid);
-    pa_tagstruct_put_boolean(t, e->muted);
-    pa_tagstruct_put_boolean(t, e->device_valid);
-    pa_tagstruct_puts(t, e->device);
-    pa_tagstruct_put_boolean(t, e->card_valid);
-    pa_tagstruct_puts(t, e->card);
 
-    key.data = (char *) name;
-    key.size = strlen(name);
-
-    data.data = (void*)pa_tagstruct_data(t, &data.size);
-
-    r = (pa_database_set(u->database, &key, &data, replace) == 0);
-
-    pa_tagstruct_free(t);
+#ifdef ENABLE_LEGACY_DATABASE_ENTRY_FORMAT
+    pa_log_debug("Attempting to load legacy (pre-v1.0) data for key: %s", name);
+    if ((e = legacy_entry_read(u, &data))) {
+        pa_log_debug("Success. Saving new format for key: %s", name);
+        if (entry_write(u, name, e, TRUE))
+            trigger_save(u);
+        pa_datum_free(&data);
+        return e;
+    } else
+        pa_log_debug("Unable to load legacy (pre-v1.0) data for key: %s. Ignoring.", name);
+#endif
 
-    return r;
+    pa_datum_free(&data);
+    return NULL;
 }
 
 static struct entry* entry_copy(const struct entry *e) {

commit 35f99c6e31d90439f2bfbefb8b45cff323b40661
Author: Colin Guthrie <colin at mageia.org>
Date:   Tue Jun 7 12:18:17 2011 +0200

    device-restore: Add a new protocol extension for device-restore.
    
    This simply exposes the formats that a device supports
    via a simple protocol extension that will allow clients
    to setup what a connected receiver supports format wise.

diff --git a/src/Makefile.am b/src/Makefile.am
index aa6b445..01c2ffc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -732,6 +732,7 @@ pulseinclude_HEADERS = \
 		pulse/def.h \
 		pulse/error.h \
 		pulse/ext-device-manager.h \
+		pulse/ext-device-restore.h \
 		pulse/ext-stream-restore.h \
 		pulse/format.h \
 		pulse/gccmacro.h \
@@ -784,6 +785,7 @@ libpulse_la_SOURCES = \
 		pulse/def.h \
 		pulse/error.c pulse/error.h \
 		pulse/ext-device-manager.c pulse/ext-device-manager.h \
+		pulse/ext-device-restore.c pulse/ext-device-restore.h \
 		pulse/ext-stream-restore.c pulse/ext-stream-restore.h \
 		pulse/gccmacro.h \
 		pulse/internal.h \
@@ -1754,9 +1756,14 @@ module_device_manager_la_CFLAGS = $(AM_CFLAGS)
 # Device volume/muted restore module
 module_device_restore_la_SOURCES = modules/module-device-restore.c
 module_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
-module_device_restore_la_LIBADD = $(MODULE_LIBADD)
+module_device_restore_la_LIBADD = $(MODULE_LIBADD) libprotocol-native.la
 module_device_restore_la_CFLAGS = $(AM_CFLAGS)
 
+if HAVE_DBUS
+module_device_restore_la_LIBADD += $(DBUS_LIBS)
+module_device_restore_la_CFLAGS += $(DBUS_CFLAGS)
+endif
+
 # Stream volume/muted/device restore module
 module_stream_restore_la_SOURCES = modules/module-stream-restore.c
 module_stream_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
diff --git a/src/map-file b/src/map-file
index a9d9aac..31ef271 100644
--- a/src/map-file
+++ b/src/map-file
@@ -157,6 +157,11 @@ pa_ext_device_manager_set_device_description;
 pa_ext_device_manager_set_subscribe_cb;
 pa_ext_device_manager_subscribe;
 pa_ext_device_manager_test;
+pa_ext_device_restore_read_sink_formats;
+pa_ext_device_restore_read_sink_formats_all;
+pa_ext_device_restore_save_sink_formats;
+pa_ext_device_restore_subscribe;
+pa_ext_device_restore_test;
 pa_ext_stream_restore_delete;
 pa_ext_stream_restore_read;
 pa_ext_stream_restore_set_subscribe_cb;
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 4ef8a24..35e455d 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -2,6 +2,7 @@
   This file is part of PulseAudio.
 
   Copyright 2006-2008 Lennart Poettering
+  Copyright 2011 Colin Guthrie
 
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
@@ -36,6 +37,8 @@
 #include <pulse/timeval.h>
 #include <pulse/util.h>
 #include <pulse/rtclock.h>
+#include <pulse/format.h>
+#include <pulse/internal.h>
 
 #include <pulsecore/core-error.h>
 #include <pulsecore/module.h>
@@ -46,6 +49,9 @@
 #include <pulsecore/sink-input.h>
 #include <pulsecore/source-output.h>
 #include <pulsecore/namereg.h>
+#include <pulsecore/protocol-native.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/pstream-util.h>
 #include <pulsecore/database.h>
 #include <pulsecore/tagstruct.h>
 
@@ -77,15 +83,30 @@ struct userdata {
         *sink_new_hook_slot,
         *sink_fixate_hook_slot,
         *source_new_hook_slot,
-        *source_fixate_hook_slot;
+        *source_fixate_hook_slot,
+        *connection_unlink_hook_slot;
     pa_time_event *save_time_event;
     pa_database *database;
 
+    pa_native_protocol *protocol;
+    pa_idxset *subscribed;
+
     pa_bool_t restore_volume:1;
     pa_bool_t restore_muted:1;
     pa_bool_t restore_port:1;
 };
 
+/* Protocol extention commands */
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT,
+    SUBCOMMAND_READ_SINK_FORMATS_ALL,
+    SUBCOMMAND_READ_SINK_FORMATS,
+    SUBCOMMAND_SAVE_SINK_FORMATS
+};
+
+
 #define ENTRY_VERSION 3
 
 struct entry {
@@ -95,6 +116,7 @@ struct entry {
     pa_channel_map channel_map;
     pa_cvolume volume;
     char *port;
+    pa_idxset *formats;
 };
 
 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
@@ -115,12 +137,14 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
 static struct entry* entry_new(void) {
     struct entry *r = pa_xnew0(struct entry, 1);
     r->version = ENTRY_VERSION;
+    r->formats = pa_idxset_new(NULL, NULL);
     return r;
 }
 
 static void entry_free(struct entry* e) {
     pa_assert(e);
 
+    pa_idxset_free(e->formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
     pa_xfree(e->port);
     pa_xfree(e);
 }
@@ -130,6 +154,7 @@ static struct entry* entry_read(struct userdata *u, const char *name) {
     struct entry *e = NULL;
     pa_tagstruct *t = NULL;
     const char* port;
+    uint8_t i, n_formats;
 
     pa_assert(u);
     pa_assert(name);
@@ -153,13 +178,23 @@ static struct entry* entry_read(struct userdata *u, const char *name) {
         pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 ||
         pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
         pa_tagstruct_get_boolean(t, &e->port_valid) < 0 ||
-        pa_tagstruct_gets(t, &port) < 0) {
+        pa_tagstruct_gets(t, &port) < 0 ||
+        pa_tagstruct_getu8(t, &n_formats) < 0) {
 
         goto fail;
     }
 
     e->port = pa_xstrdup(port);
 
+    for (i = 0; i < n_formats; ++i) {
+        pa_format_info *f = pa_format_info_new();
+        if (pa_tagstruct_get_format_info(t, f) < 0) {
+            pa_format_info_free(f);
+            goto fail;
+        }
+        pa_idxset_put(e->formats, f, NULL);
+    }
+
     if (!pa_tagstruct_eof(t))
         goto fail;
 
@@ -194,6 +229,8 @@ static pa_bool_t entry_write(struct userdata *u, const char *name, const struct
     pa_tagstruct *t;
     pa_datum key, data;
     pa_bool_t r;
+    uint32_t i;
+    pa_format_info *f;
 
     pa_assert(u);
     pa_assert(name);
@@ -208,6 +245,11 @@ static pa_bool_t entry_write(struct userdata *u, const char *name, const struct
     pa_tagstruct_put_boolean(t, e->muted);
     pa_tagstruct_put_boolean(t, e->port_valid);
     pa_tagstruct_puts(t, e->port);
+    pa_tagstruct_putu8(t, pa_idxset_size(e->formats));
+
+    PA_IDXSET_FOREACH(f, e->formats, i) {
+        pa_tagstruct_put_format_info(t, f);
+    }
 
     key.data = (char *) name;
     key.size = strlen(name);
@@ -223,11 +265,23 @@ static pa_bool_t entry_write(struct userdata *u, const char *name, const struct
 
 static struct entry* entry_copy(const struct entry *e) {
     struct entry* r;
+    uint32_t idx;
+    pa_format_info *f;
 
     pa_assert(e);
     r = entry_new();
-    *r = *e;
+    r->version = e->version;
+    r->muted_valid = e->muted_valid;
+    r->volume_valid = e->volume_valid;
+    r->port_valid = e->port_valid;
+    r->muted = e->muted;
+    r->channel_map = e->channel_map;
+    r->volume = e->volume;
     r->port = pa_xstrdup(e->port);
+
+    PA_IDXSET_FOREACH(f, e->formats, idx) {
+        pa_idxset_put(r->formats, pa_format_info_copy(f), NULL);
+    }
     return r;
 }
 
@@ -511,6 +565,197 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da
     return PA_HOOK_OK;
 }
 
+#define EXT_VERSION 1
+
+static void read_sink_format_reply(struct userdata *u, pa_tagstruct *reply, pa_sink *sink) {
+    struct entry *e;
+    char *name;
+
+    pa_assert(u);
+    pa_assert(reply);
+    pa_assert(sink);
+
+    pa_tagstruct_putu32(reply, sink->index);
+
+    /* Read or create an entry */
+    name = pa_sprintf_malloc("sink:%s", sink->name);
+    if (!(e = entry_read(u, name))) {
+        /* Fake a reply with PCM encoding supported */
+        pa_format_info *f = pa_format_info_new();
+
+        pa_tagstruct_putu8(reply, 1);
+        f->encoding = PA_ENCODING_PCM;
+        pa_tagstruct_put_format_info(reply, f);
+
+        pa_format_info_free(f);
+    } else {
+        uint32_t idx;
+        pa_format_info *f;
+
+        /* Write all the formats from the entry to the reply */
+        pa_tagstruct_putu8(reply, pa_idxset_size(e->formats));
+        PA_IDXSET_FOREACH(f, e->formats, idx) {
+            pa_tagstruct_put_format_info(reply, f);
+        }
+    }
+    pa_xfree(name);
+}
+
+static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
+    struct userdata *u;
+    uint32_t command;
+    pa_tagstruct *reply = NULL;
+
+    pa_assert(p);
+    pa_assert(m);
+    pa_assert(c);
+    pa_assert(t);
+
+    u = m->userdata;
+
+    if (pa_tagstruct_getu32(t, &command) < 0)
+        goto fail;
+
+    reply = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+    pa_tagstruct_putu32(reply, tag);
+
+    switch (command) {
+        case SUBCOMMAND_TEST: {
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            pa_tagstruct_putu32(reply, EXT_VERSION);
+            break;
+        }
+
+        case SUBCOMMAND_SUBSCRIBE: {
+
+            pa_bool_t enabled;
+
+            if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
+                !pa_tagstruct_eof(t))
+                goto fail;
+
+            if (enabled)
+                pa_idxset_put(u->subscribed, c, NULL);
+            else
+                pa_idxset_remove_by_data(u->subscribed, c, NULL);
+
+            break;
+        }
+
+        case SUBCOMMAND_READ_SINK_FORMATS_ALL: {
+            pa_sink *sink;
+            uint32_t idx;
+
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
+                read_sink_format_reply(u, reply, sink);
+            }
+
+            break;
+        }
+        case SUBCOMMAND_READ_SINK_FORMATS: {
+            uint32_t sink_index;
+            pa_sink *sink;
+
+            pa_assert(reply);
+
+            /* Get the sink index and the number of formats from the tagstruct */
+            if (pa_tagstruct_getu32(t, &sink_index) < 0)
+                goto fail;
+
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            /* Now find our sink */
+            if (!(sink = pa_idxset_get_by_index(u->core->sinks, sink_index)))
+                goto fail;
+
+            read_sink_format_reply(u, reply, sink);
+
+            break;
+        }
+
+        case SUBCOMMAND_SAVE_SINK_FORMATS: {
+
+            struct entry *e;
+            uint32_t sink_index;
+            char *name;
+            pa_sink *sink;
+            uint8_t i, n_formats;
+
+            /* Get the sink index and the number of formats from the tagstruct */
+            if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
+                pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1) {
+
+                goto fail;
+            }
+
+            /* Now find our sink */
+            if (!(sink = pa_idxset_get_by_index(u->core->sinks, sink_index)))
+                goto fail;
+
+            /* Read or create an entry */
+            name = pa_sprintf_malloc("sink:%s", sink->name);
+            if (!(e = entry_read(u, name)))
+                e = entry_new();
+
+            /* Read all the formats from our tagstruct */
+            for (i = 0; i < n_formats; ++i) {
+                pa_format_info *f = pa_format_info_new();
+                if (pa_tagstruct_get_format_info(t, f) < 0) {
+                    pa_format_info_free(f);
+                    pa_xfree(name);
+                    goto fail;
+                }
+                pa_idxset_put(e->formats, f, NULL);
+            }
+
+            if (!pa_tagstruct_eof(t)) {
+                entry_free(e);
+                pa_xfree(name);
+                goto fail;
+            }
+
+            if (entry_write(u, name, e))
+                trigger_save(u);
+            else
+                pa_log_warn("Could not save format info for sink %s", sink->name);
+
+            pa_xfree(name);
+            entry_free(e);
+
+            break;
+        }
+
+        default:
+            goto fail;
+    }
+
+    pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
+    return 0;
+
+fail:
+
+    if (reply)
+        pa_tagstruct_free(reply);
+
+    return -1;
+}
+
+static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
+    pa_assert(p);
+    pa_assert(c);
+    pa_assert(u);
+
+    pa_idxset_remove_by_data(u->subscribed, c, NULL);
+    return PA_HOOK_OK;
+}
+
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
@@ -544,6 +789,13 @@ int pa__init(pa_module*m) {
     u->restore_muted = restore_muted;
     u->restore_port = restore_port;
 
+    u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->protocol = pa_native_protocol_get(m->core);
+    pa_native_protocol_install_ext(u->protocol, m, extension_cb);
+
+    u->connection_unlink_hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
+
     u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
 
     if (restore_port) {
@@ -606,11 +858,22 @@ void pa__done(pa_module*m) {
     if (u->source_new_hook_slot)
         pa_hook_slot_free(u->source_new_hook_slot);
 
+    if (u->connection_unlink_hook_slot)
+        pa_hook_slot_free(u->connection_unlink_hook_slot);
+
     if (u->save_time_event)
         u->core->mainloop->time_free(u->save_time_event);
 
     if (u->database)
         pa_database_close(u->database);
 
+    if (u->protocol) {
+        pa_native_protocol_remove_ext(u->protocol, m);
+        pa_native_protocol_unref(u->protocol);
+    }
+
+    if (u->subscribed)
+        pa_idxset_free(u->subscribed, NULL, NULL);
+
     pa_xfree(u);
 }
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 1480af5..5dd780b 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -124,6 +124,9 @@ static void reset_callbacks(pa_context *c) {
     c->ext_device_manager.callback = NULL;
     c->ext_device_manager.userdata = NULL;
 
+    c->ext_device_restore.callback = NULL;
+    c->ext_device_restore.userdata = NULL;
+
     c->ext_stream_restore.callback = NULL;
     c->ext_stream_restore.userdata = NULL;
 }
@@ -1425,10 +1428,12 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
         goto finish;
     }
 
-    if (!strcmp(name, "module-stream-restore"))
-        pa_ext_stream_restore_command(c, tag, t);
-    else if (!strcmp(name, "module-device-manager"))
+    if (pa_streq(name, "module-device-manager"))
         pa_ext_device_manager_command(c, tag, t);
+    else if (pa_streq(name, "module-device-restore"))
+        pa_ext_device_manager_command(c, tag, t);
+    else if (pa_streq(name, "module-stream-restore"))
+        pa_ext_stream_restore_command(c, tag, t);
     else
         pa_log(_("Received message for unknown extension '%s'"), name);
 
diff --git a/src/pulse/ext-device-restore.c b/src/pulse/ext-device-restore.c
new file mode 100644
index 0000000..938d466
--- /dev/null
+++ b/src/pulse/ext-device-restore.c
@@ -0,0 +1,346 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+  Copyright 2011 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pulse/context.h>
+#include <pulse/gccmacro.h>
+#include <pulse/xmalloc.h>
+#include <pulse/fork-detect.h>
+#include <pulse/operation.h>
+#include <pulse/format.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+#include "ext-device-restore.h"
+
+/* Protocol extention commands */
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT,
+    SUBCOMMAND_READ_SINK_FORMATS_ALL,
+    SUBCOMMAND_READ_SINK_FORMATS,
+    SUBCOMMAND_SAVE_SINK_FORMATS
+};
+
+static void ext_device_restore_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    uint32_t version = PA_INVALID_INDEX;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+    } else if (pa_tagstruct_getu32(t, &version) < 0 ||
+               !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_ext_device_restore_test_cb_t cb = (pa_ext_device_restore_test_cb_t) o->callback;
+        cb(o->context, version, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_device_restore_test(
+        pa_context *c,
+        pa_ext_device_restore_test_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_restore_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_restore_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE);
+    pa_tagstruct_put_boolean(t, enable);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+void pa_ext_device_restore_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_device_restore_subscribe_cb_t cb,
+        void *userdata) {
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (pa_detect_fork())
+        return;
+
+    c->ext_device_restore.callback = cb;
+    c->ext_device_restore.userdata = userdata;
+}
+
+static void ext_device_restore_read_device_formats_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+        uint8_t j;
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_ext_device_restore_info i;
+            pa_zero(i);
+
+            if (pa_tagstruct_getu32(t, &i.index) < 0 ||
+                pa_tagstruct_getu8(t, &i.n_formats) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            if (i.n_formats > 0) {
+                i.formats = pa_xnew0(pa_format_info*, i.n_formats);
+
+                for (j = 0; j < i.n_formats; j++) {
+
+                    pa_format_info *f = i.formats[j] = pa_format_info_new();
+                    if (pa_tagstruct_get_format_info(t, f) < 0) {
+                        uint8_t k;
+
+                        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                        for (k = 0; k <= j; k++)
+                            pa_format_info_free(i.formats[k]);
+                        pa_xfree(i.formats);
+                        goto finish;
+                    }
+                }
+            }
+
+            if (o->callback) {
+                pa_ext_device_restore_read_device_formats_cb_t cb = (pa_ext_device_restore_read_device_formats_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+
+            for (j = 0; j < i.n_formats; j++)
+                pa_format_info_free(i.formats[j]);
+            pa_xfree(i.formats);
+        }
+    }
+
+    if (o->callback) {
+        pa_ext_device_restore_read_device_formats_cb_t cb = (pa_ext_device_restore_read_device_formats_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_device_restore_read_sink_formats_all(
+        pa_context *c,
+        pa_ext_device_restore_read_device_formats_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_READ_SINK_FORMATS_ALL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_restore_read_device_formats_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_restore_read_sink_formats(
+        pa_context *c,
+        uint32_t idx,
+        pa_ext_device_restore_read_device_formats_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(idx != PA_INVALID_INDEX);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_READ_SINK_FORMATS);
+    pa_tagstruct_putu32(t, idx);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_device_restore_read_device_formats_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_device_restore_save_sink_formats(
+        pa_context *c,
+        uint32_t idx,
+        uint8_t n_formats,
+        pa_format_info **formats,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint8_t j;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(idx != PA_INVALID_INDEX);
+    pa_assert(n_formats > 0);
+    pa_assert(formats && *formats);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-device-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_SAVE_SINK_FORMATS);
+
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_putu8(t, n_formats);
+    for (j = 0; j < n_formats; j++)
+        pa_tagstruct_put_format_info(t, formats[j]);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+/* Command function defined in internal.h */
+void pa_ext_device_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t) {
+    uint32_t subcommand;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &subcommand) < 0 ||
+        !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (subcommand != SUBCOMMAND_EVENT) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (c->ext_device_restore.callback)
+        c->ext_device_restore.callback(c, c->ext_device_restore.userdata);
+}
diff --git a/src/pulse/ext-device-restore.h b/src/pulse/ext-device-restore.h
new file mode 100644
index 0000000..eb0619c
--- /dev/null
+++ b/src/pulse/ext-device-restore.h
@@ -0,0 +1,106 @@
+#ifndef foopulseextdevicerestorehfoo
+#define foopulseextdevicerestorehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 Lennart Poettering
+  Copyright 2011 Colin Guthrie
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#include <pulse/context.h>
+#include <pulse/version.h>
+
+/** \file
+ *
+ * Routines for controlling module-device-restore
+ */
+
+PA_C_DECL_BEGIN
+
+/** Stores information about one device in the device database that is
+ * maintained by module-device-manager. \since 1.0 */
+typedef struct pa_ext_device_restore_info {
+    uint32_t index;              /**< The device index */
+    uint8_t n_formats;           /**< How many formats do we have? */
+    pa_format_info **formats;    /**< An array of formats (may be NULL if n_formats == 0) */
+} pa_ext_device_restore_info;
+
+/** Callback prototype for pa_ext_device_restore_test(). \since 1.0 */
+typedef void (*pa_ext_device_restore_test_cb_t)(
+        pa_context *c,
+        uint32_t version,
+        void *userdata);
+
+/** Test if this extension module is available in the server. \since 1.0 */
+pa_operation *pa_ext_device_restore_test(
+        pa_context *c,
+        pa_ext_device_restore_test_cb_t cb,
+        void *userdata);
+
+/** Subscribe to changes in the device database. \since 1.0 */
+pa_operation *pa_ext_device_restore_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+/** Callback prototype for pa_ext_device_restore_set_subscribe_cb(). \since 1.0 */
+typedef void (*pa_ext_device_restore_subscribe_cb_t)(
+        pa_context *c,
+        void *userdata);
+
+/** Set the subscription callback that is called when
+ * pa_ext_device_restore_subscribe() was called. \since 1.0 */
+void pa_ext_device_restore_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_device_restore_subscribe_cb_t cb,
+        void *userdata);
+
+/** Callback prototype for pa_ext_device_restore_read_sink_formats(). \since 1.0 */
+typedef void (*pa_ext_device_restore_read_device_formats_cb_t)(
+        pa_context *c,
+        const pa_ext_device_restore_info *info,
+        int eol,
+        void *userdata);
+
+/** Read the formats for all present sinks from the device database. \since 1.0 */
+pa_operation *pa_ext_device_restore_read_sink_formats_all(
+        pa_context *c,
+        pa_ext_device_restore_read_device_formats_cb_t cb,
+        void *userdata);
+
+/** Read an entry from the device database. \since 1.0 */
+pa_operation *pa_ext_device_restore_read_sink_formats(
+        pa_context *c,
+        uint32_t idx,
+        pa_ext_device_restore_read_device_formats_cb_t cb,
+        void *userdata);
+
+/** Read an entry from the device database. \since 1.0 */
+pa_operation *pa_ext_device_restore_save_sink_formats(
+        pa_context *c,
+        uint32_t idx,
+        uint8_t n_formats,
+        pa_format_info **formats,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 40f6804..9237909 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -29,6 +29,7 @@
 #include <pulse/operation.h>
 #include <pulse/subscribe.h>
 #include <pulse/ext-device-manager.h>
+#include <pulse/ext-device-restore.h>
 #include <pulse/ext-stream-restore.h>
 
 #include <pulsecore/socket-client.h>
@@ -107,6 +108,10 @@ struct pa_context {
         void *userdata;
     } ext_device_manager;
     struct {
+        pa_ext_device_restore_subscribe_cb_t callback;
+        void *userdata;
+    } ext_device_restore;
+    struct {
         pa_ext_stream_restore_subscribe_cb_t callback;
         void *userdata;
     } ext_stream_restore;
@@ -294,6 +299,7 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
     PA_FAIL_RETURN_ANY(context, error, NULL)
 
 void pa_ext_device_manager_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
+void pa_ext_device_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
 void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
 
 void pa_format_info_free2(pa_format_info *f, void *userdata);

commit 695d5363803b7051f85bef82b55220aa37b212a5
Author: Colin Guthrie <colin at mageia.org>
Date:   Tue Jun 7 23:21:04 2011 +0100

    database: Convert our use of database files to save in tagstruct format.
    
    This has the advantage of allowing versioned updates in the future,
    thus allowing us to be more user friendly going forward (as opposed
    to just ignoring entries from old versions).
    
    The primary motivation for this, however, is to allow variable length
    storage in each entry which will be needed for upcoming work.
    
    At present this commit will ignore any legacy entries but support
    for reading and subsequently converting legacy entries will be added
    shortly.

diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
index ed0a31d..83acbd7 100644
--- a/src/modules/module-card-restore.c
+++ b/src/modules/module-card-restore.c
@@ -46,6 +46,7 @@
 #include <pulsecore/card.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/database.h>
+#include <pulsecore/tagstruct.h>
 
 #include "module-card-restore-symdef.h"
 
@@ -69,12 +70,12 @@ struct userdata {
     pa_database *database;
 };
 
-#define ENTRY_VERSION 1
+#define ENTRY_VERSION 2
 
 struct entry {
     uint8_t version;
-    char profile[PA_NAME_MAX];
-} PA_GCC_PACKED ;
+    char *profile;
+};
 
 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
     struct userdata *u = userdata;
@@ -91,9 +92,24 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
     pa_log_info("Synced.");
 }
 
-static struct entry* read_entry(struct userdata *u, const char *name) {
+static struct entry* entry_new(void) {
+    struct entry *r = pa_xnew0(struct entry, 1);
+    r->version = ENTRY_VERSION;
+    return r;
+}
+
+static void entry_free(struct entry* e) {
+    pa_assert(e);
+
+    pa_xfree(e->profile);
+    pa_xfree(e);
+}
+
+static struct entry* entry_read(struct userdata *u, const char *name) {
     pa_datum key, data;
-    struct entry *e;
+    struct entry *e = NULL;
+    pa_tagstruct *t = NULL;
+    const char* profile;
 
     pa_assert(u);
     pa_assert(name);
@@ -106,31 +122,63 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
     if (!pa_database_get(u->database, &key, &data))
         goto fail;
 
-    if (data.size != sizeof(struct entry)) {
-        pa_log_debug("Database contains entry for card %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
-        goto fail;
-    }
+    t = pa_tagstruct_new(data.data, data.size);
+    e = entry_new();
 
-    e = (struct entry*) data.data;
+    if (pa_tagstruct_getu8(t, &e->version) < 0 ||
+        e->version > ENTRY_VERSION ||
+        pa_tagstruct_gets(t, &profile) < 0) {
 
-    if (e->version != ENTRY_VERSION) {
-        pa_log_debug("Version of database entry for card %s doesn't match our version. Probably due to upgrade, ignoring.", name);
         goto fail;
     }
 
-    if (!memchr(e->profile, 0, sizeof(e->profile))) {
-        pa_log_warn("Database contains entry for card %s with missing NUL byte in profile name", name);
+    e->profile = pa_xstrdup(profile);
+
+    if (!pa_tagstruct_eof(t))
         goto fail;
-    }
+
+    pa_tagstruct_free(t);
+    pa_datum_free(&data);
 
     return e;
 
 fail:
 
+    pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
+
+    if (e)
+        entry_free(e);
+    if (t)
+        pa_tagstruct_free(t);
     pa_datum_free(&data);
     return NULL;
 }
 
+static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    pa_bool_t r;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu8(t, e->version);
+    pa_tagstruct_puts(t, e->profile);
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
+
+    pa_tagstruct_free(t);
+
+    return r;
+}
+
 static void trigger_save(struct userdata *u) {
     if (u->save_time_event)
         return;
@@ -140,8 +188,7 @@ static void trigger_save(struct userdata *u) {
 
 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     struct userdata *u = userdata;
-    struct entry entry, *old;
-    pa_datum key, data;
+    struct entry *entry, *old;
     pa_card *card;
 
     pa_assert(c);
@@ -151,8 +198,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         t != (PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE))
         return;
 
-    pa_zero(entry);
-    entry.version = ENTRY_VERSION;
+    entry = entry_new();
 
     if (!(card = pa_idxset_get_by_index(c->cards, idx)))
         return;
@@ -160,29 +206,25 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
     if (!card->save_profile)
         return;
 
-    pa_strlcpy(entry.profile, card->active_profile ? card->active_profile->name : "", sizeof(entry.profile));
+    entry->profile = pa_xstrdup(card->active_profile ? card->active_profile->name : "");
 
-    if ((old = read_entry(u, card->name))) {
+    if ((old = entry_read(u, card->name))) {
 
-        if (strncmp(old->profile, entry.profile, sizeof(entry.profile)) == 0) {
-            pa_xfree(old);
+        if (pa_streq(old->profile, entry->profile)) {
+            entry_free(old);
+            entry_free(entry);
             return;
         }
 
-        pa_xfree(old);
+        entry_free(old);
     }
 
-    key.data = card->name;
-    key.size = strlen(card->name);
-
-    data.data = &entry;
-    data.size = sizeof(entry);
-
     pa_log_info("Storing profile for card %s.", card->name);
 
-    pa_database_set(u->database, &key, &data, TRUE);
+    if (entry_write(u, card->name, entry))
+        trigger_save(u);
 
-    trigger_save(u);
+    entry_free(entry);
 }
 
 static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) {
@@ -190,7 +232,7 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new
 
     pa_assert(new_data);
 
-    if ((e = read_entry(u, new_data->name)) && e->profile[0]) {
+    if ((e = entry_read(u, new_data->name)) && e->profile[0]) {
 
         if (!new_data->active_profile) {
             pa_log_info("Restoring profile for card %s.", new_data->name);
@@ -199,7 +241,7 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new
         } else
             pa_log_debug("Not restoring profile for card %s, because already set.", new_data->name);
 
-        pa_xfree(e);
+        entry_free(e);
     }
 
     return PA_HOOK_OK;
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 272fad2..6bac7c0 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -51,6 +51,7 @@
 #include <pulsecore/pstream.h>
 #include <pulsecore/pstream-util.h>
 #include <pulsecore/database.h>
+#include <pulsecore/tagstruct.h>
 
 #include "module-device-manager-symdef.h"
 
@@ -84,6 +85,7 @@ enum {
     ROLE_ANIMATION,
     ROLE_PRODUCTION,
     ROLE_A11Y,
+    ROLE_MAX
 };
 
 typedef uint32_t role_indexes_t[NUM_ROLES];
@@ -128,15 +130,15 @@ struct userdata {
     role_indexes_t preferred_sources;
 };
 
-#define ENTRY_VERSION 1
+#define ENTRY_VERSION 2
 
 struct entry {
     uint8_t version;
-    char description[PA_NAME_MAX];
+    char *description;
     pa_bool_t user_set_description;
-    char icon[PA_NAME_MAX];
+    char *icon;
     role_indexes_t priority;
-} PA_GCC_PACKED;
+};
 
 enum {
     SUBCOMMAND_TEST,
@@ -150,9 +152,24 @@ enum {
 };
 
 
-static struct entry* read_entry(struct userdata *u, const char *name) {
+static struct entry* entry_new(void) {
+    struct entry *r = pa_xnew0(struct entry, 1);
+    r->version = ENTRY_VERSION;
+    return r;
+}
+
+static void entry_free(struct entry* e) {
+    pa_assert(e);
+
+    pa_xfree(e->description);
+    pa_xfree(e->icon);
+}
+
+static struct entry* entry_read(struct userdata *u, const char *name) {
     pa_datum key, data;
-    struct entry *e;
+    struct entry *e = NULL;
+    pa_tagstruct *t = NULL;
+    const char *description, *icon;
 
     pa_assert(u);
     pa_assert(name);
@@ -165,36 +182,74 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
     if (!pa_database_get(u->database, &key, &data))
         goto fail;
 
-    if (data.size != sizeof(struct entry)) {
-        pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
-        goto fail;
-    }
+    t = pa_tagstruct_new(data.data, data.size);
+    e = entry_new();
 
-    e = (struct entry*) data.data;
+    if (pa_tagstruct_getu8(t, &e->version) < 0 ||
+        e->version > ENTRY_VERSION ||
+        pa_tagstruct_gets(t, &description) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->user_set_description) < 0 ||
+        pa_tagstruct_gets(t, &icon) < 0) {
 
-    if (e->version != ENTRY_VERSION) {
-        pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
         goto fail;
     }
 
-    if (!memchr(e->description, 0, sizeof(e->description))) {
-        pa_log_warn("Database contains entry for device %s with missing NUL byte in description", name);
-        goto fail;
+    e->description = pa_xstrdup(description);
+    e->icon = pa_xstrdup(icon);
+
+    for (uint8_t i=0; i<ROLE_MAX; ++i) {
+        if (pa_tagstruct_getu32(t, &e->priority[i]) < 0)
+            goto fail;
     }
 
-    if (!memchr(e->icon, 0, sizeof(e->icon))) {
-        pa_log_warn("Database contains entry for device %s with missing NUL byte in icon", name);
+    if (!pa_tagstruct_eof(t))
         goto fail;
-    }
+
+    pa_tagstruct_free(t);
+    pa_datum_free(&data);
 
     return e;
 
 fail:
+    pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
 
+    if (e)
+        entry_free(e);
+    if (t)
+        pa_tagstruct_free(t);
     pa_datum_free(&data);
     return NULL;
 }
 
+static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    pa_bool_t r;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu8(t, e->version);
+    pa_tagstruct_puts(t, e->description);
+    pa_tagstruct_put_boolean(t, e->user_set_description);
+    pa_tagstruct_puts(t, e->icon);
+    for (uint8_t i=0; i<ROLE_MAX; ++i)
+        pa_tagstruct_putu32(t, e->priority[i]);
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
+
+    pa_tagstruct_free(t);
+
+    return r;
+}
+
 #ifdef DUMP_DATABASE
 static void dump_database_helper(struct userdata *u, uint32_t role_index, const char* human, pa_bool_t sink_mode) {
     pa_assert(u);
@@ -233,14 +288,14 @@ static void dump_database(struct userdata *u) {
 
         name = pa_xstrndup(key.data, key.size);
 
-        if ((e = read_entry(u, name))) {
+        if ((e = entry_read(u, name))) {
             pa_log_debug(" Got entry: %s", name);
             pa_log_debug("  Description: %s", e->description);
             pa_log_debug("  Priorities: None:   %3u, Video: %3u, Music:  %3u, Game: %3u, Event: %3u",
                          e->priority[ROLE_NONE], e->priority[ROLE_VIDEO], e->priority[ROLE_MUSIC], e->priority[ROLE_GAME], e->priority[ROLE_EVENT]);
             pa_log_debug("              Phone:  %3u, Anim:  %3u, Prodtn: %3u, A11y: %3u",
                          e->priority[ROLE_PHONE], e->priority[ROLE_ANIMATION], e->priority[ROLE_PRODUCTION], e->priority[ROLE_A11Y]);
-            pa_xfree(e);
+            entry_free(e);
         }
 
         pa_xfree(name);
@@ -334,9 +389,9 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
     pa_assert(a);
     pa_assert(b);
 
-    if (strncmp(a->description, b->description, sizeof(a->description))
+    if (!pa_streq(a->description, b->description)
         || a->user_set_description != b->user_set_description
-        || strncmp(a->icon, b->icon, sizeof(a->icon)))
+        || !pa_streq(a->icon, b->icon))
         return FALSE;
 
     for (int i=0; i < NUM_ROLES; ++i)
@@ -364,9 +419,11 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct
     pa_assert(name);
     pa_assert(prefix);
 
-    if ((old = read_entry(u, name)))
+    if ((old = entry_read(u, name))) {
         *entry = *old;
-    else {
+        entry->description = pa_xstrdup(old->description);
+        entry->icon = pa_xstrdup(old->icon);
+    } else {
         /* This is a new device, so make sure we write it's priority list correctly */
         role_indexes_t max_priority;
         pa_datum key;
@@ -387,12 +444,12 @@ static inline struct entry *load_or_initialize_entry(struct userdata *u, struct
 
                 name2 = pa_xstrndup(key.data, key.size);
 
-                if ((e = read_entry(u, name2))) {
+                if ((e = entry_read(u, name2))) {
                     for (uint32_t i = 0; i < NUM_ROLES; ++i) {
                         max_priority[i] = PA_MAX(max_priority[i], e->priority[i]);
                     }
 
-                    pa_xfree(e);
+                    entry_free(e);
                 }
 
                 pa_xfree(name2);
@@ -456,7 +513,7 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha
             name = pa_xstrndup(key.data, key.size);
             device_name = get_name(name, prefix);
 
-            if ((e = read_entry(u, name))) {
+            if ((e = entry_read(u, name))) {
                 for (uint32_t i = 0; i < NUM_ROLES; ++i) {
                     if (!highest_priority_available[i] || e->priority[i] < highest_priority_available[i]) {
                         /* We've found a device with a higher priority than that we've currently got,
@@ -497,7 +554,7 @@ static void update_highest_priority_device_indexes(struct userdata *u, const cha
                     }
                 }
 
-                pa_xfree(e);
+                entry_free(e);
             }
 
             pa_xfree(name);
@@ -631,9 +688,8 @@ static pa_hook_result_t route_source_outputs(struct userdata *u, pa_source* igno
 
 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     struct userdata *u = userdata;
-    struct entry entry, *old = NULL;
+    struct entry *entry, *old = NULL;
     char *name = NULL;
-    pa_datum key, data;
 
     pa_assert(c);
     pa_assert(u);
@@ -649,8 +705,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
         return;
 
-    pa_zero(entry);
-    entry.version = ENTRY_VERSION;
+    entry = entry_new();
 
     if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
         pa_sink_input *si;
@@ -684,19 +739,21 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
         name = pa_sprintf_malloc("sink:%s", sink->name);
 
-        old = load_or_initialize_entry(u, &entry, name, "sink:");
+        old = load_or_initialize_entry(u, entry, name, "sink:");
 
-        if (!entry.user_set_description)
-            pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
-        else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) {
+        if (!entry->user_set_description) {
+            pa_xfree(entry->description);
+            entry->description = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
+        } else if (!pa_streq(entry->description, pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
             /* Warning: If two modules fight over the description, this could cause an infinite loop.
                by changing the description here, we retrigger this subscription callback. The only thing stopping us from
                looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
                the description, this will fail... */
-            pa_sink_set_description(sink, entry.description);
+            pa_sink_set_description(sink, entry->description);
         }
 
-        pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
+        pa_xfree(entry->icon);
+        entry->icon = pa_xstrdup(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_ICON_NAME));
 
     } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
         pa_source *source;
@@ -711,48 +768,46 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
         name = pa_sprintf_malloc("source:%s", source->name);
 
-        old = load_or_initialize_entry(u, &entry, name, "source:");
+        old = load_or_initialize_entry(u, entry, name, "source:");
 
-        if (!entry.user_set_description)
-            pa_strlcpy(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description));
-        else if (strncmp(entry.description, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)), sizeof(entry.description)) != 0) {
+        if (!entry->user_set_description) {
+            pa_xfree(entry->description);
+            entry->description = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION));
+        } else if (!pa_streq(entry->description, pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION))) {
             /* Warning: If two modules fight over the description, this could cause an infinite loop.
                by changing the description here, we retrigger this subscription callback. The only thing stopping us from
                looping is the fact that the string comparison will fail on the second iteration. If another module tries to manage
                the description, this will fail... */
-            pa_source_set_description(source, entry.description);
+            pa_source_set_description(source, entry->description);
         }
 
-        pa_strlcpy(entry.icon, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME)), sizeof(entry.icon));
+        pa_xfree(entry->icon);
+        entry->icon = pa_xstrdup(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_ICON_NAME));
     }
 
     pa_assert(name);
 
     if (old) {
 
-        if (entries_equal(old, &entry)) {
-            pa_xfree(old);
+        if (entries_equal(old, entry)) {
+            entry_free(old);
+            entry_free(entry);
             pa_xfree(name);
 
             return;
         }
 
-        pa_xfree(old);
+        entry_free(old);
     }
 
-    key.data = name;
-    key.size = strlen(name);
-
-    data.data = &entry;
-    data.size = sizeof(entry);
-
     pa_log_info("Storing device %s.", name);
 
-    if (pa_database_set(u->database, &key, &data, TRUE) == 0)
+    if (entry_write(u, name, entry))
         trigger_save(u);
     else
         pa_log_warn("Could not save device");;
 
+    entry_free(entry);
     pa_xfree(name);
 }
 
@@ -766,13 +821,13 @@ static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new
 
     name = pa_sprintf_malloc("sink:%s", new_data->name);
 
-    if ((e = read_entry(u, name))) {
+    if ((e = entry_read(u, name))) {
         if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
             pa_log_info("Restoring description for sink %s.", new_data->name);
             pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
         }
 
-        pa_xfree(e);
+        entry_free(e);
     }
 
     pa_xfree(name);
@@ -790,14 +845,14 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
 
     name = pa_sprintf_malloc("source:%s", new_data->name);
 
-    if ((e = read_entry(u, name))) {
+    if ((e = entry_read(u, name))) {
         if (e->user_set_description && strncmp(e->description, pa_proplist_gets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION), sizeof(e->description)) != 0) {
             /* NB, We cannot detect if we are a monitor here... this could mess things up a bit... */
             pa_log_info("Restoring description for source %s.", new_data->name);
             pa_proplist_sets(new_data->proplist, PA_PROP_DEVICE_DESCRIPTION, e->description);
         }
 
-        pa_xfree(e);
+        entry_free(e);
     }
 
     pa_xfree(name);
@@ -1029,7 +1084,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         name = pa_xstrndup(key.data, key.size);
         pa_datum_free(&key);
 
-        if ((e = read_entry(u, name))) {
+        if ((e = entry_read(u, name))) {
             uint32_t idx;
             char *device_name;
             uint32_t found_index = PA_INVALID_INDEX;
@@ -1065,7 +1120,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                 pa_tagstruct_putu32(reply, e->priority[i]);
             }
 
-            pa_xfree(e);
+            entry_free(e);
         }
 
         pa_xfree(name);
@@ -1088,19 +1143,12 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         if (!device || !*device || !description || !*description)
           goto fail;
 
-        if ((e = read_entry(u, device))) {
-            pa_datum key, data;
-
-            pa_strlcpy(e->description, description, sizeof(e->description));
+        if ((e = entry_read(u, device))) {
+            pa_xfree(e->description);
+            e->description = pa_xstrdup(description);
             e->user_set_description = TRUE;
 
-            key.data = (char *) device;
-            key.size = strlen(device);
-
-            data.data = e;
-            data.size = sizeof(*e);
-
-            if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
+            if (entry_write(u, (char *)device, e)) {
                 apply_entry(u, device, e);
 
                 trigger_save(u);
@@ -1108,7 +1156,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             else
                 pa_log_warn("Could not save device");
 
-            pa_xfree(e);
+            entry_free(e);
         }
         else
             pa_log_warn("Could not rename device %s, no entry in database", device);
@@ -1157,7 +1205,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         const char *role;
         struct entry *e;
         uint32_t role_index, n_devices;
-        pa_datum key, data;
+        pa_datum key;
         pa_bool_t done, sink_mode = TRUE;
         struct device_t { uint32_t prio; char *device; };
         struct device_t *device;
@@ -1193,7 +1241,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             }
 
             /* Ensure this is a valid entry */
-            if (!(e = read_entry(u, s))) {
+            if (!(e = entry_read(u, s))) {
                 while ((device = pa_hashmap_steal_first(h))) {
                     pa_xfree(device->device);
                     pa_xfree(device);
@@ -1203,7 +1251,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                 pa_log_error("Client specified an unknown device in it's reorder list.");
                 goto fail;
             }
-            pa_xfree(e);
+            entry_free(e);
 
             if (first) {
                 first = FALSE;
@@ -1255,7 +1303,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
 
                 /* Add the device to our hashmap. If it's alredy in it, free it now and carry on */
                 if (pa_hashmap_put(h, device->device, device) == 0
-                    && (e = read_entry(u, device->device))) {
+                    && (e = entry_read(u, device->device))) {
                     /* We add offset on to the existing priorirty so that when we order, the
                        existing entries are always lower priority than the new ones. */
                     device->prio = (offset + e->priority[role_index]);
@@ -1310,19 +1358,13 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
         idx = 1;
         first = TRUE;
         for (i = 0; i < n_devices; ++i) {
-            if ((e = read_entry(u, devices[i]->device))) {
+            if ((e = entry_read(u, devices[i]->device))) {
                 if (e->priority[role_index] == idx)
                     idx++;
                 else {
                     e->priority[role_index] = idx;
 
-                    key.data = (char *) devices[i]->device;
-                    key.size = strlen(devices[i]->device);
-
-                    data.data = e;
-                    data.size = sizeof(*e);
-
-                    if (pa_database_set(u->database, &key, &data, TRUE) == 0) {
+                    if (entry_write(u, (char *) devices[i]->device, e)) {
                         first = FALSE;
                         idx++;
                     }
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index 4c31a98..4ef8a24 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -47,6 +47,7 @@
 #include <pulsecore/source-output.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/database.h>
+#include <pulsecore/tagstruct.h>
 
 #include "module-device-restore-symdef.h"
 
@@ -85,16 +86,16 @@ struct userdata {
     pa_bool_t restore_port:1;
 };
 
-#define ENTRY_VERSION 2
+#define ENTRY_VERSION 3
 
 struct entry {
     uint8_t version;
-    pa_bool_t muted_valid:1, volume_valid:1, port_valid:1;
-    pa_bool_t muted:1;
+    pa_bool_t muted_valid, volume_valid, port_valid;
+    pa_bool_t muted;
     pa_channel_map channel_map;
     pa_cvolume volume;
-    char port[PA_NAME_MAX];
-} PA_GCC_PACKED;
+    char *port;
+};
 
 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
     struct userdata *u = userdata;
@@ -111,9 +112,24 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
     pa_log_info("Synced.");
 }
 
-static struct entry* read_entry(struct userdata *u, const char *name) {
+static struct entry* entry_new(void) {
+    struct entry *r = pa_xnew0(struct entry, 1);
+    r->version = ENTRY_VERSION;
+    return r;
+}
+
+static void entry_free(struct entry* e) {
+    pa_assert(e);
+
+    pa_xfree(e->port);
+    pa_xfree(e);
+}
+
+static struct entry* entry_read(struct userdata *u, const char *name) {
     pa_datum key, data;
-    struct entry *e;
+    struct entry *e = NULL;
+    pa_tagstruct *t = NULL;
+    const char* port;
 
     pa_assert(u);
     pa_assert(name);
@@ -126,22 +142,26 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
     if (!pa_database_get(u->database, &key, &data))
         goto fail;
 
-    if (data.size != sizeof(struct entry)) {
-        pa_log_debug("Database contains entry for device %s of wrong size %lu != %lu. Probably due to upgrade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
-        goto fail;
-    }
+    t = pa_tagstruct_new(data.data, data.size);
+    e = entry_new();
 
-    e = (struct entry*) data.data;
+    if (pa_tagstruct_getu8(t, &e->version) < 0 ||
+        e->version > ENTRY_VERSION ||
+        pa_tagstruct_get_boolean(t, &e->volume_valid) < 0 ||
+        pa_tagstruct_get_channel_map(t, &e->channel_map) < 0 ||
+        pa_tagstruct_get_cvolume(t, &e->volume) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->port_valid) < 0 ||
+        pa_tagstruct_gets(t, &port) < 0) {
 
-    if (e->version != ENTRY_VERSION) {
-        pa_log_debug("Version of database entry for device %s doesn't match our version. Probably due to upgrade, ignoring.", name);
         goto fail;
     }
 
-    if (!memchr(e->port, 0, sizeof(e->port))) {
-        pa_log_warn("Database contains entry for device %s with missing NUL byte in port name", name);
+    e->port = pa_xstrdup(port);
+
+    if (!pa_tagstruct_eof(t))
         goto fail;
-    }
 
     if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
         pa_log_warn("Invalid channel map stored in database for device %s", name);
@@ -153,14 +173,64 @@ static struct entry* read_entry(struct userdata *u, const char *name) {
         goto fail;
     }
 
+    pa_tagstruct_free(t);
+    pa_datum_free(&data);
+
     return e;
 
 fail:
 
+    pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
+
+    if (e)
+        entry_free(e);
+    if (t)
+        pa_tagstruct_free(t);
     pa_datum_free(&data);
     return NULL;
 }
 
+static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    pa_bool_t r;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu8(t, e->version);
+    pa_tagstruct_put_boolean(t, e->volume_valid);
+    pa_tagstruct_put_channel_map(t, &e->channel_map);
+    pa_tagstruct_put_cvolume(t, &e->volume);
+    pa_tagstruct_put_boolean(t, e->muted_valid);
+    pa_tagstruct_put_boolean(t, e->muted);
+    pa_tagstruct_put_boolean(t, e->port_valid);
+    pa_tagstruct_puts(t, e->port);
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, TRUE) == 0);
+
+    pa_tagstruct_free(t);
+
+    return r;
+}
+
+static struct entry* entry_copy(const struct entry *e) {
+    struct entry* r;
+
+    pa_assert(e);
+    r = entry_new();
+    *r = *e;
+    r->port = pa_xstrdup(e->port);
+    return r;
+}
+
 static void trigger_save(struct userdata *u) {
     if (u->save_time_event)
         return;
@@ -172,7 +242,7 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
     pa_cvolume t;
 
     if (a->port_valid != b->port_valid ||
-        (a->port_valid && strncmp(a->port, b->port, sizeof(a->port))))
+        (a->port_valid && !pa_streq(a->port, b->port)))
         return FALSE;
 
     if (a->muted_valid != b->muted_valid ||
@@ -189,9 +259,8 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
 
 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     struct userdata *u = userdata;
-    struct entry entry, *old;
+    struct entry *entry, *old;
     char *name;
-    pa_datum key, data;
 
     pa_assert(c);
     pa_assert(u);
@@ -202,9 +271,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
         return;
 
-    pa_zero(entry);
-    entry.version = ENTRY_VERSION;
-
     if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
         pa_sink *sink;
 
@@ -213,23 +279,26 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
         name = pa_sprintf_malloc("sink:%s", sink->name);
 
-        if ((old = read_entry(u, name)))
-            entry = *old;
+        if ((old = entry_read(u, name)))
+            entry = entry_copy(old);
+        else
+            entry = entry_new();
 
         if (sink->save_volume) {
-            entry.channel_map = sink->channel_map;
-            entry.volume = *pa_sink_get_volume(sink, FALSE);
-            entry.volume_valid = TRUE;
+            entry->channel_map = sink->channel_map;
+            entry->volume = *pa_sink_get_volume(sink, FALSE);
+            entry->volume_valid = TRUE;
         }
 
         if (sink->save_muted) {
-            entry.muted = pa_sink_get_mute(sink, FALSE);
-            entry.muted_valid = TRUE;
+            entry->muted = pa_sink_get_mute(sink, FALSE);
+            entry->muted_valid = TRUE;
         }
 
         if (sink->save_port) {
-            pa_strlcpy(entry.port, sink->active_port ? sink->active_port->name : "", sizeof(entry.port));
-            entry.port_valid = TRUE;
+            pa_xfree(entry->port);
+            entry->port = pa_xstrdup(sink->active_port ? sink->active_port->name : "");
+            entry->port_valid = TRUE;
         }
 
     } else {
@@ -242,50 +311,50 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
         name = pa_sprintf_malloc("source:%s", source->name);
 
-        if ((old = read_entry(u, name)))
-            entry = *old;
+        if ((old = entry_read(u, name)))
+            entry = entry_copy(old);
+        else
+            entry = entry_new();
 
         if (source->save_volume) {
-            entry.channel_map = source->channel_map;
-            entry.volume = *pa_source_get_volume(source, FALSE);
-            entry.volume_valid = TRUE;
+            entry->channel_map = source->channel_map;
+            entry->volume = *pa_source_get_volume(source, FALSE);
+            entry->volume_valid = TRUE;
         }
 
         if (source->save_muted) {
-            entry.muted = pa_source_get_mute(source, FALSE);
-            entry.muted_valid = TRUE;
+            entry->muted = pa_source_get_mute(source, FALSE);
+            entry->muted_valid = TRUE;
         }
 
         if (source->save_port) {
-            pa_strlcpy(entry.port, source->active_port ? source->active_port->name : "", sizeof(entry.port));
-            entry.port_valid = TRUE;
+            pa_xfree(entry->port);
+            entry->port = pa_xstrdup(source->active_port ? source->active_port->name : "");
+            entry->port_valid = TRUE;
         }
     }
 
+    pa_assert(entry);
+
     if (old) {
 
-        if (entries_equal(old, &entry)) {
-            pa_xfree(old);
+        if (entries_equal(old, entry)) {
+            entry_free(old);
+            entry_free(entry);
             pa_xfree(name);
             return;
         }
 
-        pa_xfree(old);
+        entry_free(old);
     }
 
-    key.data = name;
-    key.size = strlen(name);
-
-    data.data = &entry;
-    data.size = sizeof(entry);
-
     pa_log_info("Storing volume/mute/port for device %s.", name);
 
-    pa_database_set(u->database, &key, &data, TRUE);
+    if (entry_write(u, name, entry))
+        trigger_save(u);
 
+    entry_free(entry);
     pa_xfree(name);
-
-    trigger_save(u);
 }
 
 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
@@ -299,7 +368,7 @@ static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new
 
     name = pa_sprintf_malloc("sink:%s", new_data->name);
 
-    if ((e = read_entry(u, name))) {
+    if ((e = entry_read(u, name))) {
 
         if (e->port_valid) {
             if (!new_data->active_port) {
@@ -310,7 +379,7 @@ static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new
                 pa_log_debug("Not restoring port for sink %s, because already set.", name);
         }
 
-        pa_xfree(e);
+        entry_free(e);
     }
 
     pa_xfree(name);
@@ -329,7 +398,7 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *
 
     name = pa_sprintf_malloc("sink:%s", new_data->name);
 
-    if ((e = read_entry(u, name))) {
+    if ((e = entry_read(u, name))) {
 
         if (u->restore_volume && e->volume_valid) {
 
@@ -357,7 +426,7 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *
                 pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name);
         }
 
-        pa_xfree(e);
+        entry_free(e);
     }
 
     pa_xfree(name);
@@ -376,7 +445,7 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
 
     name = pa_sprintf_malloc("source:%s", new_data->name);
 
-    if ((e = read_entry(u, name))) {
+    if ((e = entry_read(u, name))) {
 
         if (e->port_valid) {
             if (!new_data->active_port) {
@@ -387,7 +456,7 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
                 pa_log_debug("Not restoring port for source %s, because already set.", name);
         }
 
-        pa_xfree(e);
+        entry_free(e);
     }
 
     pa_xfree(name);
@@ -406,7 +475,7 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da
 
     name = pa_sprintf_malloc("source:%s", new_data->name);
 
-    if ((e = read_entry(u, name))) {
+    if ((e = entry_read(u, name))) {
 
         if (u->restore_volume && e->volume_valid) {
 
@@ -434,7 +503,7 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da
                 pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name);
         }
 
-        pa_xfree(e);
+        entry_free(e);
     }
 
     pa_xfree(name);
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 617de5b..e690838 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -51,6 +51,7 @@
 #include <pulsecore/pstream.h>
 #include <pulsecore/pstream-util.h>
 #include <pulsecore/database.h>
+#include <pulsecore/tagstruct.h>
 
 #ifdef HAVE_DBUS
 #include <pulsecore/dbus-util.h>
@@ -114,17 +115,17 @@ struct userdata {
 #endif
 };
 
-#define ENTRY_VERSION 3
+#define ENTRY_VERSION 4
 
 struct entry {
     uint8_t version;
-    pa_bool_t muted_valid:1, volume_valid:1, device_valid:1, card_valid:1;
-    pa_bool_t muted:1;
+    pa_bool_t muted_valid, volume_valid, device_valid, card_valid;
+    pa_bool_t muted;
     pa_channel_map channel_map;
     pa_cvolume volume;
-    char device[PA_NAME_MAX];
-    char card[PA_NAME_MAX];
-} PA_GCC_PACKED;
+    char* device;
+    char* card;
+};
 
 enum {
     SUBCOMMAND_TEST,
@@ -135,8 +136,13 @@ enum {
     SUBCOMMAND_EVENT
 };
 
-static struct entry *read_entry(struct userdata *u, const char *name);
-static void apply_entry(struct userdata *u, const char *name, struct entry *e);
+
+static struct entry* entry_new(void);
+static void entry_free(struct entry *e);
+static struct entry *entry_read(struct userdata *u, const char *name);
+static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e, pa_bool_t replace);
+static struct entry* entry_copy(const struct entry *e);
+static void entry_apply(struct userdata *u, const char *name, struct entry *e);
 static void trigger_save(struct userdata *u);
 
 #ifdef HAVE_DBUS
@@ -590,8 +596,6 @@ static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userd
     pa_cvolume vol;
     dbus_bool_t muted = FALSE;
     dbus_bool_t apply_immediately = FALSE;
-    pa_datum key;
-    pa_datum value;
     struct dbus_entry *dbus_entry = NULL;
     struct entry *e = NULL;
 
@@ -624,7 +628,7 @@ static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userd
         pa_bool_t volume_updated = FALSE;
         pa_bool_t device_updated = FALSE;
 
-        pa_assert_se(e = read_entry(u, name));
+        pa_assert_se(e = entry_read(u, name));
         mute_updated = e->muted != muted;
         e->muted = muted;
         e->muted_valid = TRUE;
@@ -635,7 +639,8 @@ static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userd
         e->volume_valid = !!map.channels;
 
         device_updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device);
-        pa_strlcpy(e->device, device, sizeof(e->device));
+        pa_xfree(e->device);
+        e->device = pa_xstrdup(device);
         e->device_valid = !!device[0];
 
         if (mute_updated)
@@ -649,34 +654,28 @@ static void handle_add_entry(DBusConnection *conn, DBusMessage *msg, void *userd
         dbus_entry = dbus_entry_new(u, name);
         pa_assert_se(pa_hashmap_put(u->dbus_entries, dbus_entry->entry_name, dbus_entry) == 0);
 
-        e = pa_xnew0(struct entry, 1);
-        e->version = ENTRY_VERSION;
+        e = entry_new();
         e->muted_valid = TRUE;
         e->volume_valid = !!map.channels;
         e->device_valid = !!device[0];
         e->muted = muted;
         e->volume = vol;
         e->channel_map = map;
-        pa_strlcpy(e->device, device, sizeof(e->device));
+        e->device = pa_xstrdup(device);
 
         send_new_entry_signal(dbus_entry);
     }
 
-    key.data = (char *) name;
-    key.size = strlen(name);
+    pa_assert_se(entry_write(u, name, e, TRUE));
 
-    value.data = e;
-    value.size = sizeof(struct entry);
-
-    pa_assert_se(pa_database_set(u->database, &key, &value, TRUE) == 0);
     if (apply_immediately)
-        apply_entry(u, name, e);
+        entry_apply(u, name, e);
 
     trigger_save(u);
 
     pa_dbus_send_empty_reply(conn, msg);
 
-    pa_xfree(e);
+    entry_free(e);
 }
 
 static void handle_get_entry_by_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -727,13 +726,13 @@ static void handle_entry_get_device(DBusConnection *conn, DBusMessage *msg, void
     pa_assert(msg);
     pa_assert(de);
 
-    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
 
     device = e->device_valid ? e->device : "";
 
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &device);
 
-    pa_xfree(e);
+    entry_free(e);
 }
 
 static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
@@ -749,31 +748,25 @@ static void handle_entry_set_device(DBusConnection *conn, DBusMessage *msg, DBus
 
     dbus_message_iter_get_basic(iter, &device);
 
-    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
 
     updated = (e->device_valid != !!device[0]) || !pa_streq(e->device, device);
 
     if (updated) {
-        pa_datum key;
-        pa_datum value;
-
-        pa_strlcpy(e->device, device, sizeof(e->device));
+        pa_xfree(e->device);
+        e->device = pa_xstrdup(device);
         e->device_valid = !!device[0];
 
-        key.data = de->entry_name;
-        key.size = strlen(de->entry_name);
-        value.data = e;
-        value.size = sizeof(struct entry);
-        pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0);
+        pa_assert_se(entry_write(de->userdata, de->entry_name, e, TRUE));
 
-        apply_entry(de->userdata, de->entry_name, e);
+        entry_apply(de->userdata, de->entry_name, e);
         send_device_updated_signal(de, e);
         trigger_save(de->userdata);
     }
 
     pa_dbus_send_empty_reply(conn, msg);
 
-    pa_xfree(e);
+    entry_free(e);
 }
 
 static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -786,7 +779,7 @@ static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void
     pa_assert(msg);
     pa_assert(de);
 
-    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
 
     pa_assert_se(reply = dbus_message_new_method_return(msg));
 
@@ -795,7 +788,7 @@ static void handle_entry_get_volume(DBusConnection *conn, DBusMessage *msg, void
 
     pa_assert_se(dbus_connection_send(conn, reply, NULL));
 
-    pa_xfree(e);
+    entry_free(e);
 }
 
 static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
@@ -813,32 +806,25 @@ static void handle_entry_set_volume(DBusConnection *conn, DBusMessage *msg, DBus
     if (get_volume_arg(conn, msg, iter, &map, &vol) < 0)
         return;
 
-    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
 
     updated = (e->volume_valid != !!map.channels) || !pa_cvolume_equal(&e->volume, &vol);
 
     if (updated) {
-        pa_datum key;
-        pa_datum value;
-
         e->volume = vol;
         e->channel_map = map;
         e->volume_valid = !!map.channels;
 
-        key.data = de->entry_name;
-        key.size = strlen(de->entry_name);
-        value.data = e;
-        value.size = sizeof(struct entry);
-        pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0);
+        pa_assert_se(entry_write(de->userdata, de->entry_name, e, TRUE));
 
-        apply_entry(de->userdata, de->entry_name, e);
+        entry_apply(de->userdata, de->entry_name, e);
         send_volume_updated_signal(de, e);
         trigger_save(de->userdata);
     }
 
     pa_dbus_send_empty_reply(conn, msg);
 
-    pa_xfree(e);
+    entry_free(e);
 }
 
 static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -850,13 +836,13 @@ static void handle_entry_get_mute(DBusConnection *conn, DBusMessage *msg, void *
     pa_assert(msg);
     pa_assert(de);
 
-    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
 
     mute = e->muted_valid ? e->muted : FALSE;
 
     pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_BOOLEAN, &mute);
 
-    pa_xfree(e);
+    entry_free(e);
 }
 
 static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMessageIter *iter, void *userdata) {
@@ -872,31 +858,24 @@ static void handle_entry_set_mute(DBusConnection *conn, DBusMessage *msg, DBusMe
 
     dbus_message_iter_get_basic(iter, &mute);
 
-    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
 
     updated = !e->muted_valid || e->muted != mute;
 
     if (updated) {
-        pa_datum key;
-        pa_datum value;
-
         e->muted = mute;
         e->muted_valid = TRUE;
 
-        key.data = de->entry_name;
-        key.size = strlen(de->entry_name);
-        value.data = e;
-        value.size = sizeof(struct entry);
-        pa_assert_se(pa_database_set(de->userdata->database, &key, &value, TRUE) == 0);
+        pa_assert_se(entry_write(de->userdata, de->entry_name, e, TRUE));
 
-        apply_entry(de->userdata, de->entry_name, e);
+        entry_apply(de->userdata, de->entry_name, e);
         send_mute_updated_signal(de, e);
         trigger_save(de->userdata);
     }
 
     pa_dbus_send_empty_reply(conn, msg);
 
-    pa_xfree(e);
+    entry_free(e);
 }
 
 static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -913,7 +892,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u
     pa_assert(msg);
     pa_assert(de);
 
-    pa_assert_se(e = read_entry(de->userdata, de->entry_name));
+    pa_assert_se(e = entry_read(de->userdata, de->entry_name));
 
     device = e->device_valid ? e->device : "";
     mute = e->muted_valid ? e->muted : FALSE;
@@ -942,7 +921,7 @@ static void handle_entry_get_all(DBusConnection *conn, DBusMessage *msg, void *u
 
     dbus_message_unref(reply);
 
-    pa_xfree(e);
+    entry_free(e);
 }
 
 static void handle_entry_remove(DBusConnection *conn, DBusMessage *msg, void *userdata) {
@@ -1009,9 +988,25 @@ static char *get_name(pa_proplist *p, const char *prefix) {
     return t;
 }
 
-static struct entry *read_entry(struct userdata *u, const char *name) {
+static struct entry* entry_new(void) {
+    struct entry *r = pa_xnew0(struct entry, 1);
+    r->version = ENTRY_VERSION;
+    return r;
+}
+
+static void entry_free(struct entry* e) {
+    pa_assert(e);
+
+    pa_xfree(e->device);
+    pa_xfree(e->card);
+    pa_xfree(e);
+}
+
+static struct entry *entry_read(struct userdata *u, const char *name) {
     pa_datum key, data;
-    struct entry *e;
+    struct entry *e = NULL;
+    pa_tagstruct *t = NULL;
+    const char *device, *card;
 
     pa_assert(u);
     pa_assert(name);
@@ -1024,29 +1019,29 @@ static struct entry *read_entry(struct userdata *u, const char *name) {
     if (!pa_database_get(u->database, &key, &data))
         goto fail;
 
-    if (data.size != sizeof(struct entry)) {
-        /* This is probably just a database upgrade, hence let's not
-         * consider this more than a debug message */
-        pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu. Probably due to uprade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
-        goto fail;
-    }
-
-    e = (struct entry*) data.data;
+    t = pa_tagstruct_new(data.data, data.size);
+    e = entry_new();
+
+    if (pa_tagstruct_getu8(t, &e->version) < 0 ||
+        e->version > ENTRY_VERSION ||
+        pa_tagstruct_get_boolean(t, &e->volume_valid) < 0 ||
+        pa_tagstruct_get_channel_map(t, &e->channel_map) < 0 ||
+        pa_tagstruct_get_cvolume(t, &e->volume) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->muted_valid) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->muted) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->device_valid) < 0 ||
+        pa_tagstruct_gets(t, &device) < 0 ||
+        pa_tagstruct_get_boolean(t, &e->card_valid) < 0 ||
+        pa_tagstruct_gets(t, &card) < 0) {
 
-    if (e->version != ENTRY_VERSION) {
-        pa_log_debug("Version of database entry for stream %s doesn't match our version. Probably due to upgrade, ignoring.", name);
         goto fail;
     }
 
-    if (!memchr(e->device, 0, sizeof(e->device))) {
-        pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name);
-        goto fail;
-    }
+    e->device = pa_xstrdup(device);
+    e->card = pa_xstrdup(card);
 
-    if (!memchr(e->card, 0, sizeof(e->card))) {
-        pa_log_warn("Database contains entry for stream %s with missing NUL byte in card name", name);
+    if (!pa_tagstruct_eof(t))
         goto fail;
-    }
 
     if (e->device_valid && !pa_namereg_is_valid_name(e->device)) {
         pa_log_warn("Invalid device name stored in database for stream %s", name);
@@ -1068,14 +1063,67 @@ static struct entry *read_entry(struct userdata *u, const char *name) {
         goto fail;
     }
 
+    pa_tagstruct_free(t);
+    pa_datum_free(&data);
+
     return e;
 
 fail:
 
+    pa_log_debug("Database contains invalid data for key: %s (probably pre-v1.0 data)", name);
+
+    if (e)
+        entry_free(e);
+    if (t)
+        pa_tagstruct_free(t);
     pa_datum_free(&data);
     return NULL;
 }
 
+static pa_bool_t entry_write(struct userdata *u, const char *name, const struct entry *e, pa_bool_t replace) {
+    pa_tagstruct *t;
+    pa_datum key, data;
+    pa_bool_t r;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu8(t, e->version);
+    pa_tagstruct_put_boolean(t, e->volume_valid);
+    pa_tagstruct_put_channel_map(t, &e->channel_map);
+    pa_tagstruct_put_cvolume(t, &e->volume);
+    pa_tagstruct_put_boolean(t, e->muted_valid);
+    pa_tagstruct_put_boolean(t, e->muted);
+    pa_tagstruct_put_boolean(t, e->device_valid);
+    pa_tagstruct_puts(t, e->device);
+    pa_tagstruct_put_boolean(t, e->card_valid);
+    pa_tagstruct_puts(t, e->card);
+
+    key.data = (char *) name;
+    key.size = strlen(name);
+
+    data.data = (void*)pa_tagstruct_data(t, &data.size);
+
+    r = (pa_database_set(u->database, &key, &data, replace) == 0);
+
+    pa_tagstruct_free(t);
+
+    return r;
+}
+
+static struct entry* entry_copy(const struct entry *e) {
+    struct entry* r;
+
+    pa_assert(e);
+    r = entry_new();
+    *r = *e;
+    r->device = pa_xstrdup(e->device);
+    r->card = pa_xstrdup(e->card);
+    return r;
+}
+
 static void trigger_save(struct userdata *u) {
     pa_native_connection *c;
     uint32_t idx;
@@ -1106,11 +1154,11 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
     pa_assert(b);
 
     if (a->device_valid != b->device_valid ||
-        (a->device_valid && strncmp(a->device, b->device, sizeof(a->device))))
+        (a->device_valid && !pa_streq(a->device, b->device)))
         return FALSE;
 
     if (a->card_valid != b->card_valid ||
-        (a->card_valid && strncmp(a->card, b->card, sizeof(a->card))))
+        (a->card_valid && !pa_streq(a->card, b->card)))
         return FALSE;
 
     if (a->muted_valid != b->muted_valid ||
@@ -1127,9 +1175,8 @@ static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
 
 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     struct userdata *u = userdata;
-    struct entry entry, *old = NULL;
+    struct entry *entry, *old = NULL;
     char *name = NULL;
-    pa_datum key, data;
 
     /* These are only used when D-Bus is enabled, but in order to reduce ifdef
      * clutter these are defined here unconditionally. */
@@ -1151,9 +1198,6 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
         return;
 
-    pa_zero(entry);
-    entry.version = ENTRY_VERSION;
-
     if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
         pa_sink_input *sink_input;
 
@@ -1163,39 +1207,42 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         if (!(name = get_name(sink_input->proplist, "sink-input")))
             return;
 
-        if ((old = read_entry(u, name))) {
-            entry = *old;
+        if ((old = entry_read(u, name))) {
+            entry = entry_copy(old);
             created_new_entry = FALSE;
-        }
+        } else
+            entry = entry_new();
 
         if (sink_input->save_volume && pa_sink_input_is_volume_readable(sink_input)) {
             pa_assert(sink_input->volume_writable);
 
-            entry.channel_map = sink_input->channel_map;
-            pa_sink_input_get_volume(sink_input, &entry.volume, FALSE);
-            entry.volume_valid = TRUE;
+            entry->channel_map = sink_input->channel_map;
+            pa_sink_input_get_volume(sink_input, &entry->volume, FALSE);
+            entry->volume_valid = TRUE;
 
             volume_updated = !created_new_entry
                              && (!old->volume_valid
-                                 || !pa_channel_map_equal(&entry.channel_map, &old->channel_map)
-                                 || !pa_cvolume_equal(&entry.volume, &old->volume));
+                                 || !pa_channel_map_equal(&entry->channel_map, &old->channel_map)
+                                 || !pa_cvolume_equal(&entry->volume, &old->volume));
         }
 
         if (sink_input->save_muted) {
-            entry.muted = pa_sink_input_get_mute(sink_input);
-            entry.muted_valid = TRUE;
+            entry->muted = pa_sink_input_get_mute(sink_input);
+            entry->muted_valid = TRUE;
 
-            mute_updated = !created_new_entry && (!old->muted_valid || entry.muted != old->muted);
+            mute_updated = !created_new_entry && (!old->muted_valid || entry->muted != old->muted);
         }
 
         if (sink_input->save_sink) {
-            pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
-            entry.device_valid = TRUE;
+            pa_xfree(entry->device);
+            entry->device = pa_xstrdup(sink_input->sink->name);
+            entry->device_valid = TRUE;
 
-            device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device));
+            device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry->device, old->device));
             if (sink_input->sink->card) {
-                pa_strlcpy(entry.card, sink_input->sink->card->name, sizeof(entry.card));
-                entry.card_valid = TRUE;
+                pa_xfree(entry->card);
+                entry->card = pa_xstrdup(sink_input->sink->card->name);
+                entry->card_valid = TRUE;
             }
         }
 
@@ -1210,44 +1257,45 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         if (!(name = get_name(source_output->proplist, "source-output")))
             return;
 
-        if ((old = read_entry(u, name))) {
-            entry = *old;
+        if ((old = entry_read(u, name))) {
+            entry = entry_copy(old);
             created_new_entry = FALSE;
-        }
+        } else
+            entry = entry_new();
 
         if (source_output->save_source) {
-            pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
-            entry.device_valid = TRUE;
+            pa_xfree(entry->device);
+            entry->device = pa_xstrdup(source_output->source->name);
+            entry->device_valid = TRUE;
 
-            device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device));
+            device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry->device, old->device));
 
             if (source_output->source->card) {
-                pa_strlcpy(entry.card, source_output->source->card->name, sizeof(entry.card));
-                entry.card_valid = TRUE;
+                pa_xfree(entry->card);
+                entry->card = pa_xstrdup(source_output->source->card->name);
+                entry->card_valid = TRUE;
             }
         }
     }
 
+    pa_assert(entry);
+
     if (old) {
 
-        if (entries_equal(old, &entry)) {
-            pa_xfree(old);
+        if (entries_equal(old, entry)) {
+            entry_free(old);
+            entry_free(entry);
             pa_xfree(name);
             return;
         }
 
-        pa_xfree(old);
+        entry_free(old);
     }
 
-    key.data = name;
-    key.size = strlen(name);
-
-    data.data = &entry;
-    data.size = sizeof(entry);
-
     pa_log_info("Storing volume/mute/device for stream %s.", name);
 
-    pa_database_set(u->database, &key, &data, TRUE);
+    if (entry_write(u, name, entry, TRUE))
+        trigger_save(u);
 
 #ifdef HAVE_DBUS
     if (created_new_entry) {
@@ -1258,17 +1306,16 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         pa_assert_se(de = pa_hashmap_get(u->dbus_entries, name));
 
         if (device_updated)
-            send_device_updated_signal(de, &entry);
+            send_device_updated_signal(de, entry);
         if (volume_updated)
-            send_volume_updated_signal(de, &entry);
+            send_volume_updated_signal(de, entry);
         if (mute_updated)
-            send_mute_updated_signal(de, &entry);
+            send_mute_updated_signal(de, entry);
     }
 #endif
 
+    entry_free(entry);
     pa_xfree(name);
-
-    trigger_save(u);
 }
 
 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
@@ -1285,7 +1332,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
 
     if (new_data->sink)
         pa_log_debug("Not restoring device for stream %s, because already set to '%s'.", name, new_data->sink->name);
-    else if ((e = read_entry(u, name))) {
+    else if ((e = entry_read(u, name))) {
         pa_sink *s = NULL;
 
         if (e->device_valid)
@@ -1305,7 +1352,7 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
             if (pa_sink_input_new_data_set_sink(new_data, s, TRUE))
                 pa_log_info("Restoring device for stream %s.", name);
 
-        pa_xfree(e);
+        entry_free(e);
     }
 
     pa_xfree(name);
@@ -1325,7 +1372,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
     if (!(name = get_name(new_data->proplist, "sink-input")))
         return PA_HOOK_OK;
 
-    if ((e = read_entry(u, name))) {
+    if ((e = entry_read(u, name))) {
 
         if (u->restore_volume && e->volume_valid) {
             if (!new_data->volume_writable)
@@ -1356,7 +1403,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
                 pa_log_debug("Not restoring mute state for sink input %s, because already set.", name);
         }
 
-        pa_xfree(e);
+        entry_free(e);
     }
 
     pa_xfree(name);
@@ -1381,7 +1428,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
 
     if (new_data->source)
         pa_log_debug("Not restoring device for stream %s, because already set", name);
-    else if ((e = read_entry(u, name))) {
+    else if ((e = entry_read(u, name))) {
         pa_source *s = NULL;
 
         if (e->device_valid)
@@ -1402,7 +1449,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
             pa_source_output_new_data_set_source(new_data, s, TRUE);
         }
 
-        pa_xfree(e);
+        entry_free(e);
     }
 
     pa_xfree(name);
@@ -1443,11 +1490,11 @@ static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct
         if (!(name = get_name(si->proplist, "sink-input")))
             continue;
 
-        if ((e = read_entry(u, name))) {
+        if ((e = entry_read(u, name))) {
             if (e->device_valid && pa_streq(e->device, sink->name))
                 pa_sink_input_move_to(si, sink, TRUE);
 
-            pa_xfree(e);
+            entry_free(e);
         }
 
         pa_xfree(name);
@@ -1491,11 +1538,11 @@ static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source,
         if (!(name = get_name(so->proplist, "source-output")))
             continue;
 
-        if ((e = read_entry(u, name))) {
+        if ((e = entry_read(u, name))) {
             if (e->device_valid && pa_streq(e->device, source->name))
                 pa_source_output_move_to(so, source, TRUE);
 
-            pa_xfree(e);
+            entry_free(e);
         }
 
         pa_xfree(name);
@@ -1527,7 +1574,7 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
         if (!(name = get_name(si->proplist, "sink-input")))
             continue;
 
-        if ((e = read_entry(u, name))) {
+        if ((e = entry_read(u, name))) {
 
             if (e->device_valid) {
                 pa_sink *d;
@@ -1538,7 +1585,7 @@ static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, str
                     pa_sink_input_move_to(si, d, TRUE);
             }
 
-            pa_xfree(e);
+            entry_free(e);
         }
 
         pa_xfree(name);
@@ -1573,7 +1620,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
         if (!(name = get_name(so->proplist, "source-output")))
             continue;
 
-        if ((e = read_entry(u, name))) {
+        if ((e = entry_read(u, name))) {
 
             if (e->device_valid) {
                 pa_source *d;
@@ -1584,7 +1631,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
                     pa_source_output_move_to(so, d, TRUE);
             }
 
-            pa_xfree(e);
+            entry_free(e);
         }
 
         pa_xfree(name);
@@ -1595,7 +1642,7 @@ static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *sourc
 
 #define EXT_VERSION 1
 
-static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
+static void entry_apply(struct userdata *u, const char *name, struct entry *e) {
     pa_sink_input *si;
     pa_source_output *so;
     uint32_t idx;
@@ -1704,14 +1751,14 @@ PA_GCC_UNUSED static void stream_restore_dump_database(struct userdata *u) {
         name = pa_xstrndup(key.data, key.size);
         pa_datum_free(&key);
 
-        if ((e = read_entry(u, name))) {
+        if ((e = entry_read(u, name))) {
             char t[256];
             pa_log("name=%s", name);
             pa_log("device=%s %s", e->device, pa_yes_no(e->device_valid));
             pa_log("channel_map=%s", pa_channel_map_snprint(t, sizeof(t), &e->channel_map));
             pa_log("volume=%s %s", pa_cvolume_snprint(t, sizeof(t), &e->volume), pa_yes_no(e->volume_valid));
             pa_log("mute=%s %s", pa_yes_no(e->muted), pa_yes_no(e->volume_valid));
-            pa_xfree(e);
+            entry_free(e);
         }
 
         pa_xfree(name);
@@ -1768,7 +1815,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                 name = pa_xstrndup(key.data, key.size);
                 pa_datum_free(&key);
 
-                if ((e = read_entry(u, name))) {
+                if ((e = entry_read(u, name))) {
                     pa_cvolume r;
                     pa_channel_map cm;
 
@@ -1778,7 +1825,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                     pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
                     pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
 
-                    pa_xfree(e);
+                    entry_free(e);
                 }
 
                 pa_xfree(name);
@@ -1818,74 +1865,70 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
             while (!pa_tagstruct_eof(t)) {
                 const char *name, *device;
                 pa_bool_t muted;
-                struct entry entry;
-                pa_datum key, data;
+                struct entry *entry;
 #ifdef HAVE_DBUS
                 struct entry *old;
 #endif
 
-                pa_zero(entry);
-                entry.version = ENTRY_VERSION;
+                entry = entry_new();
 
                 if (pa_tagstruct_gets(t, &name) < 0 ||
-                    pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
-                    pa_tagstruct_get_cvolume(t, &entry.volume) < 0 ||
+                    pa_tagstruct_get_channel_map(t, &entry->channel_map) ||
+                    pa_tagstruct_get_cvolume(t, &entry->volume) < 0 ||
                     pa_tagstruct_gets(t, &device) < 0 ||
                     pa_tagstruct_get_boolean(t, &muted) < 0)
                     goto fail;
 
-                if (!name || !*name)
+                if (!name || !*name) {
+                    entry_free(entry);
                     goto fail;
+                }
 
-                entry.volume_valid = entry.volume.channels > 0;
+                entry->volume_valid = entry->volume.channels > 0;
 
-                if (entry.volume_valid)
-                    if (!pa_cvolume_compatible_with_channel_map(&entry.volume, &entry.channel_map))
+                if (entry->volume_valid)
+                    if (!pa_cvolume_compatible_with_channel_map(&entry->volume, &entry->channel_map)) {
+                        entry_free(entry);
                         goto fail;
+                    }
 
-                entry.muted = muted;
-                entry.muted_valid = TRUE;
+                entry->muted = muted;
+                entry->muted_valid = TRUE;
 
-                if (device)
-                    pa_strlcpy(entry.device, device, sizeof(entry.device));
-                entry.device_valid = !!entry.device[0];
+                entry->device = pa_xstrdup(device);
+                entry->device_valid = device && !!entry->device[0];
 
-                if (entry.device_valid &&
-                    !pa_namereg_is_valid_name(entry.device))
+                if (entry->device_valid && !pa_namereg_is_valid_name(entry->device)) {
+                    entry_free(entry);
                     goto fail;
+                }
 
 #ifdef HAVE_DBUS
-                old = read_entry(u, name);
+                old = entry_read(u, name);
 #endif
 
-                key.data = (char*) name;
-                key.size = strlen(name);
-
-                data.data = &entry;
-                data.size = sizeof(entry);
-
                 pa_log_debug("Client %s changes entry %s.",
                              pa_strnull(pa_proplist_gets(pa_native_connection_get_client(c)->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)),
                              name);
 
-                if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0) {
+                if (entry_write(u, name, entry, mode == PA_UPDATE_REPLACE)) {
 #ifdef HAVE_DBUS
                     struct dbus_entry *de;
 
                     if (old) {
                         pa_assert_se((de = pa_hashmap_get(u->dbus_entries, name)));
 
-                        if ((old->device_valid != entry.device_valid)
-                            || (entry.device_valid && !pa_streq(entry.device, old->device)))
-                            send_device_updated_signal(de, &entry);
+                        if ((old->device_valid != entry->device_valid)
+                            || (entry->device_valid && !pa_streq(entry->device, old->device)))
+                            send_device_updated_signal(de, entry);
 
-                        if ((old->volume_valid != entry.volume_valid)
-                            || (entry.volume_valid && (!pa_cvolume_equal(&entry.volume, &old->volume)
-                                                       || !pa_channel_map_equal(&entry.channel_map, &old->channel_map))))
-                            send_volume_updated_signal(de, &entry);
+                        if ((old->volume_valid != entry->volume_valid)
+                            || (entry->volume_valid && (!pa_cvolume_equal(&entry->volume, &old->volume)
+                                                       || !pa_channel_map_equal(&entry->channel_map, &old->channel_map))))
+                            send_volume_updated_signal(de, entry);
 
-                        if (!old->muted_valid || (entry.muted != old->muted))
-                            send_mute_updated_signal(de, &entry);
+                        if (!old->muted_valid || (entry->muted != old->muted))
+                            send_mute_updated_signal(de, entry);
 
                     } else {
                         de = dbus_entry_new(u, name);
@@ -1895,13 +1938,14 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
 #endif
 
                     if (apply_immediately)
-                        apply_entry(u, name, &entry);
+                        entry_apply(u, name, entry);
                 }
 
 #ifdef HAVE_DBUS
                 if (old)
-                    pa_xfree(old);
+                    entry_free(old);
 #endif
+                entry_free(entry);
             }
 
             trigger_save(u);
@@ -2080,11 +2124,11 @@ int pa__init(pa_module*m) {
         name = pa_xstrndup(key.data, key.size);
         pa_datum_free(&key);
 
-        /* Use read_entry() for checking that the entry is valid. */
-        if ((e = read_entry(u, name))) {
+        /* Use entry_read() for checking that the entry is valid. */
+        if ((e = entry_read(u, name))) {
             de = dbus_entry_new(u, name);
             pa_assert_se(pa_hashmap_put(u->dbus_entries, de->entry_name, de) == 0);
-            pa_xfree(e);
+            entry_free(e);
         }
 
         pa_xfree(name);

commit ec4fa4c668a61de8ed7c9452e73919ba0bb1a870
Author: Colin Guthrie <colin at mageia.org>
Date:   Wed Jun 8 20:18:12 2011 +0100

    esound,streams: Fix some crashes.
    
    After the rework to the add pa_sink_input_new_data_set_sink() (and
    the source equiv) calling with a NULL sink object will hit an assert.
    
    This caused crashes with the esd protocol and there was the potential
    (albeit unlikely) for a crash when creating a sink input without any
    sinks available (module-always-sink mitigates this risk but it's still
    a potential crasher).

diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index edca96a..484821c 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -426,7 +426,8 @@ static int esd_proto_stream_play(connection *c, esd_proto_t request, const void
     sdata.driver = __FILE__;
     sdata.module = c->options->module;
     sdata.client = c->client;
-    pa_sink_input_new_data_set_sink(&sdata, sink, FALSE);
+    if (sink)
+        pa_sink_input_new_data_set_sink(&sdata, sink, FALSE);
     pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
 
     pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata);
@@ -524,7 +525,8 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
     sdata.driver = __FILE__;
     sdata.module = c->options->module;
     sdata.client = c->client;
-    pa_source_output_new_data_set_source(&sdata, source, FALSE);
+    if (source)
+        pa_source_output_new_data_set_source(&sdata, source, FALSE);
     pa_source_output_new_data_set_sample_spec(&sdata, &ss);
 
     pa_source_output_new(&c->source_output, c->protocol->core, &sdata);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 9a43e04..2e718c4 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -273,9 +273,11 @@ int pa_sink_input_new(
 
     pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
 
-    if (!data->sink)
-        pa_sink_input_new_data_set_sink(data, pa_namereg_get(core, NULL, PA_NAMEREG_SINK), FALSE);
-
+    if (!data->sink) {
+        pa_sink *sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK);
+        pa_return_val_if_fail(sink, -PA_ERR_NOENTITY);
+        pa_sink_input_new_data_set_sink(data, sink, FALSE);
+    }
     /* Routing's done, we have a sink. Now let's fix the format and set up the
      * sample spec */
 
@@ -298,7 +300,6 @@ int pa_sink_input_new(
         pa_sink_input_new_data_set_sample_spec(data, &ss);
     }
 
-    pa_return_val_if_fail(data->sink, -PA_ERR_NOENTITY);
     pa_return_val_if_fail(PA_SINK_IS_LINKED(pa_sink_get_state(data->sink)), -PA_ERR_BADSTATE);
     pa_return_val_if_fail(!data->sync_base || (data->sync_base->sink == data->sink && pa_sink_input_get_state(data->sync_base) == PA_SINK_INPUT_CORKED), -PA_ERR_INVALID);
 
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index e6cec60..59b6c2f 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -253,8 +253,11 @@ int pa_source_output_new(
 
     pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
 
-    if (!data->source)
-        pa_source_output_new_data_set_source(data, pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE), FALSE);
+    if (!data->source) {
+        pa_source *source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE);
+        pa_return_val_if_fail(source, -PA_ERR_NOENTITY);
+        pa_source_output_new_data_set_source(data, source, FALSE);
+    }
 
     /* Routing's done, we have a source. Now let's fix the format and set up the
      * sample spec */
@@ -278,7 +281,6 @@ int pa_source_output_new(
         pa_source_output_new_data_set_sample_spec(data, &ss);
     }
 
-    pa_return_val_if_fail(data->source, -PA_ERR_NOENTITY);
     pa_return_val_if_fail(PA_SOURCE_IS_LINKED(pa_source_get_state(data->source)), -PA_ERR_BADSTATE);
     pa_return_val_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of, -PA_ERR_INVALID);
 

commit 32927c50a8fef49f8a24eee82bf4ca64253a3dfc
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Sun May 22 13:11:30 2011 +0530

    pactl: Add a set-source-output-volume command

diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 3bf983e..a9d4c82 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -108,6 +108,7 @@ static enum {
     SET_SINK_VOLUME,
     SET_SOURCE_VOLUME,
     SET_SINK_INPUT_VOLUME,
+    SET_SOURCE_OUTPUT_VOLUME,
     SET_SINK_MUTE,
     SET_SOURCE_MUTE,
     SET_SINK_INPUT_MUTE,
@@ -863,6 +864,25 @@ static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_in
     pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &cv, simple_callback, NULL));
 }
 
+static void get_source_output_volume_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
+    pa_cvolume cv;
+
+    if (is_last < 0) {
+        pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    if (is_last)
+        return;
+
+    pa_assert(o);
+
+    cv = o->volume;
+    volume_relative_adjust(&cv);
+    pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &cv, simple_callback, NULL));
+}
+
 static void stream_state_callback(pa_stream *s, void *userdata) {
     pa_assert(s);
 
@@ -1126,6 +1146,16 @@ static void context_state_callback(pa_context *c, void *userdata) {
                     }
                     break;
 
+                case SET_SOURCE_OUTPUT_VOLUME:
+                    if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
+                        pa_operation_unref(pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL));
+                    } else {
+                        pa_cvolume v;
+                        pa_cvolume_set(&v, 1, volume);
+                        pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &v, simple_callback, NULL));
+                    }
+                    break;
+
                 case SUBSCRIBE:
                     pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
 
@@ -1241,6 +1271,7 @@ static void help(const char *argv0) {
              "%s [options] set-sink-volume SINK VOLUME\n"
              "%s [options] set-source-volume SOURCE VOLUME\n"
              "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
+             "%s [options] set-source-output-volume SOURCEOUTPUT VOLUME\n"
              "%s [options] set-sink-mute SINK 1|0\n"
              "%s [options] set-source-mute SOURCE 1|0\n"
              "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
@@ -1253,7 +1284,7 @@ static void help(const char *argv0) {
            argv0, argv0, argv0, argv0, argv0,
            argv0, argv0, argv0, argv0, argv0,
            argv0, argv0, argv0, argv0, argv0,
-           argv0, argv0, argv0);
+           argv0, argv0, argv0, argv0);
 }
 
 enum {
@@ -1561,6 +1592,22 @@ int main(int argc, char *argv[]) {
             if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
                 goto quit;
 
+        } else if (pa_streq(argv[optind], "set-source-output-volume")) {
+            action = SET_SOURCE_OUTPUT_VOLUME;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a source output index and a volume"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
+                pa_log(_("Invalid source output index"));
+                goto quit;
+            }
+
+            if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
+                goto quit;
+
         } else if (pa_streq(argv[optind], "set-sink-mute")) {
             int b;
             action = SET_SINK_MUTE;

commit 4412bb8bb855c388108a62ded071268669d13943
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Sun May 22 13:11:02 2011 +0530

    source-output: Trivial code move
    
    Makes diff'ing with sink-input.c easier

diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 0ce7778..e6cec60 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -896,58 +896,6 @@ pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) {
 }
 
 /* Called from main context */
-void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
-    pa_source_output_assert_ref(o);
-    pa_assert_ctl_context();
-    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
-
-    source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
-}
-
-/* Called from main context */
-int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
-    pa_source_output_assert_ref(o);
-    pa_assert_ctl_context();
-    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
-    pa_return_val_if_fail(o->thread_info.resampler, -PA_ERR_BADSTATE);
-
-    if (o->sample_spec.rate == rate)
-        return 0;
-
-    o->sample_spec.rate = rate;
-
-    pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
-
-    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
-    return 0;
-}
-
-/* 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 && !strcmp(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 */
 void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
     pa_cvolume v;
 
@@ -1109,6 +1057,58 @@ void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode
 }
 
 /* Called from main context */
+void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    source_output_set_state(o, b ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING);
+}
+
+/* Called from main context */
+int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_return_val_if_fail(o->thread_info.resampler, -PA_ERR_BADSTATE);
+
+    if (o->sample_spec.rate == rate)
+        return 0;
+
+    o->sample_spec.rate = rate;
+
+    pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL);
+
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+    return 0;
+}
+
+/* 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 && !strcmp(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();

commit b837d040578f71207c3068321436743851b36a9f
Author: Colin Guthrie <colin at mageia.org>
Date:   Fri May 20 08:33:56 2011 +0100

    capture: Remove support for synchronised capture streams.
    
    This was added to ensure symmetry between playback and recording streams
    code, but in reality this makes little sense practically speaking and thus
    it is removed.

diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 82c6f54..0ce7778 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -423,16 +423,6 @@ int pa_source_output_new(
 
     o->muted = data->muted;
 
-    if (data->sync_base) {
-        o->sync_next = data->sync_base->sync_next;
-        o->sync_prev = data->sync_base;
-
-        if (data->sync_base->sync_next)
-            data->sync_base->sync_next->sync_prev = o;
-        data->sync_base->sync_next = o;
-    } else
-        o->sync_next = o->sync_prev = NULL;
-
     o->direct_on_input = data->direct_on_input;
 
     reset_callbacks(o);
@@ -498,7 +488,7 @@ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state)
 
 /* Called from main context */
 static void source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
-    pa_source_output *ssync;
+
     pa_assert(o);
     pa_assert_ctl_context();
 
@@ -510,24 +500,9 @@ static void source_output_set_state(pa_source_output *o, pa_source_output_state_
     update_n_corked(o, state);
     o->state = state;
 
-    for (ssync = o->sync_prev; ssync; ssync = ssync->sync_prev) {
-        update_n_corked(ssync, state);
-        ssync->state = state;
-    }
-    for (ssync = o->sync_next; ssync; ssync = ssync->sync_next) {
-        update_n_corked(ssync, state);
-        ssync->state = state;
-    }
-
     if (state != PA_SOURCE_OUTPUT_UNLINKED) {
         pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
 
-        for (ssync = o->sync_prev; ssync; ssync = ssync->sync_prev)
-            pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], ssync);
-
-        for (ssync = o->sync_next; ssync; ssync = ssync->sync_next)
-            pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], ssync);
-
         if (PA_SOURCE_OUTPUT_IS_LINKED(state))
             pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
     }
@@ -551,13 +526,6 @@ void pa_source_output_unlink(pa_source_output*o) {
     if (linked)
         pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
 
-    if (o->sync_prev)
-        o->sync_prev->sync_next = o->sync_next;
-    if (o->sync_next)
-        o->sync_next->sync_prev = o->sync_prev;
-
-    o->sync_prev = o->sync_next = NULL;
-
     if (o->direct_on_input)
         pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL);
 
@@ -1160,11 +1128,6 @@ pa_bool_t pa_source_output_may_move(pa_source_output *o) {
     if (o->direct_on_input)
         return FALSE;
 
-    if (o->sync_next || o->sync_prev) {
-        pa_log_warn("Moving synchronized streams not supported.");
-        return FALSE;
-    }
-
     return TRUE;
 }
 
@@ -1567,19 +1530,11 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
             pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
             return 0;
 
-        case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: {
-            pa_source_output *ssync;
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE:
 
             pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
 
-            for (ssync = o->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev)
-                pa_source_output_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
-
-            for (ssync = o->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next)
-                pa_source_output_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
-
             return 0;
-        }
 
         case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: {
             pa_usec_t *usec = userdata;
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index cd4b0d0..b9a2b6b 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -87,8 +87,6 @@ struct pa_source_output {
     pa_channel_map channel_map;
     pa_format_info *format;
 
-    pa_source_output *sync_prev, *sync_next;
-
     /* Also see http://pulseaudio.org/wiki/InternalVolumes */
     pa_cvolume volume;             /* The volume clients are informed about */
     pa_cvolume reference_ratio;    /* The ratio of the stream's volume to the source's reference volume */
@@ -208,8 +206,6 @@ struct pa_source_output {
          * don't implement rewind() */
         pa_memblockq *delay_memblockq;
 
-        pa_source_output *sync_prev, *sync_next;
-
         /* The requested latency for the source */
         pa_usec_t requested_source_latency;
 
@@ -254,8 +250,6 @@ typedef struct pa_source_output_new_data {
 
     pa_resample_method_t resample_method;
 
-    pa_source_output *sync_base;
-
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
     pa_format_info *format;
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 9c15d3f..78a5203 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -1665,22 +1665,6 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
 
             pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o));
 
-            /* Since the caller sleeps in pa_source_output_put(), we can
-             * safely access data outside of thread_info even though
-             * it is mutable */
-
-            if ((o->thread_info.sync_prev = o->sync_prev)) {
-                pa_assert(o->source == o->thread_info.sync_prev->source);
-                pa_assert(o->sync_prev->sync_next == o);
-                o->thread_info.sync_prev->thread_info.sync_next = o;
-            }
-
-            if ((o->thread_info.sync_next = o->sync_next)) {
-                pa_assert(o->source == o->thread_info.sync_next->source);
-                pa_assert(o->sync_next->sync_prev == o);
-                o->thread_info.sync_next->thread_info.sync_prev = o;
-            }
-
             if (o->direct_on_input) {
                 o->thread_info.direct_on_input = o->direct_on_input;
                 pa_hashmap_put(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index), o);
@@ -1720,23 +1704,6 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
             pa_assert(o->thread_info.attached);
             o->thread_info.attached = FALSE;
 
-            /* Since the caller sleeps in pa_sink_input_unlink(),
-             * we can safely access data outside of thread_info even
-             * though it is mutable */
-
-            pa_assert(!o->sync_prev);
-            pa_assert(!o->sync_next);
-
-            if (o->thread_info.sync_prev) {
-                o->thread_info.sync_prev->thread_info.sync_next = o->thread_info.sync_prev->sync_next;
-                o->thread_info.sync_prev = NULL;
-            }
-
-            if (o->thread_info.sync_next) {
-                o->thread_info.sync_next->thread_info.sync_prev = o->thread_info.sync_next->sync_prev;
-                o->thread_info.sync_next = NULL;
-            }
-
             if (o->thread_info.direct_on_input) {
                 pa_hashmap_remove(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index));
                 o->thread_info.direct_on_input = NULL;

commit 4eb513cbf45b46c83f4b3456d66277183615da8e
Author: Juho Hämäläinen <ext-juho.hamalainen at nokia.com>
Date:   Mon Dec 20 18:25:59 2010 +0200

    alsa-mixer: select nearest alsa volume step in sync-volume mode

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index e3673f8..dada122 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -849,7 +849,59 @@ static long decibel_fix_get_step(pa_alsa_decibel_fix *db_fix, long *db_value, in
     return i + db_fix->min_step;
 }
 
-static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw) {
+/* Alsa lib documentation says for snd_mixer_selem_set_playback_dB() direction argument,
+ * that "-1 = accurate or first below, 0 = accurate, 1 = accurate or first above".
+ * But even with accurate nearest dB volume step is not selected, so that is why we need
+ * this function. Returns 0 and nearest selectable volume in *value_dB on success or
+ * negative error code if fails. */
+static int element_get_nearest_alsa_dB(snd_mixer_elem_t *me, snd_mixer_selem_channel_id_t c, pa_alsa_direction_t d, long *value_dB) {
+
+    long alsa_val;
+    long value_high;
+    long value_low;
+    int r = -1;
+
+    pa_assert(me);
+    pa_assert(value_dB);
+
+    if (d == PA_ALSA_DIRECTION_OUTPUT) {
+        if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
+            r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_high);
+
+        if (r < 0)
+            return r;
+
+        if (value_high == *value_dB)
+            return r;
+
+        if ((r = snd_mixer_selem_ask_playback_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
+            r = snd_mixer_selem_ask_playback_vol_dB(me, alsa_val, &value_low);
+    } else {
+        if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, +1, &alsa_val)) >= 0)
+            r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_high);
+
+        if (r < 0)
+            return r;
+
+        if (value_high == *value_dB)
+            return r;
+
+        if ((r = snd_mixer_selem_ask_capture_dB_vol(me, *value_dB, -1, &alsa_val)) >= 0)
+            r = snd_mixer_selem_ask_capture_vol_dB(me, alsa_val, &value_low);
+    }
+
+    if (r < 0)
+        return r;
+
+    if (labs(value_high - *value_dB) < labs(value_low - *value_dB))
+        *value_dB = value_high;
+    else
+        *value_dB = value_low;
+
+    return r;
+}
+
+static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t sync_volume, pa_bool_t write_to_hw) {
 
     snd_mixer_selem_id_t *sid;
     pa_cvolume rv;
@@ -914,8 +966,13 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
 
                     } else {
                         if (write_to_hw) {
-                            if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0)
-                                r = snd_mixer_selem_get_playback_dB(me, c, &value);
+                            if (sync_volume) {
+                                if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_OUTPUT, &value)) >= 0)
+                                    r = snd_mixer_selem_set_playback_dB(me, c, value, 0);
+                            } else {
+                                if ((r = snd_mixer_selem_set_playback_dB(me, c, value, rounding)) >= 0)
+                                    r = snd_mixer_selem_get_playback_dB(me, c, &value);
+                           }
                         } else {
                             long alsa_val;
                             if ((r = snd_mixer_selem_ask_playback_dB_vol(me, value, rounding, &alsa_val)) >= 0)
@@ -937,8 +994,13 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
 
                     } else {
                         if (write_to_hw) {
-                            if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0)
-                                r = snd_mixer_selem_get_capture_dB(me, c, &value);
+                            if (sync_volume) {
+                                if ((r = element_get_nearest_alsa_dB(me, c, PA_ALSA_DIRECTION_INPUT, &value)) >= 0)
+                                    r = snd_mixer_selem_set_capture_dB(me, c, value, 0);
+                            } else {
+                                if ((r = snd_mixer_selem_set_capture_dB(me, c, value, rounding)) >= 0)
+                                    r = snd_mixer_selem_get_capture_dB(me, c, &value);
+                            }
                         } else {
                             long alsa_val;
                             if ((r = snd_mixer_selem_ask_capture_dB_vol(me, value, rounding, &alsa_val)) >= 0)
@@ -999,7 +1061,7 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
     return 0;
 }
 
-int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw) {
+int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t sync_volume, pa_bool_t write_to_hw) {
 
     pa_alsa_element *e;
     pa_cvolume rv;
@@ -1025,7 +1087,7 @@ int pa_alsa_path_set_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_ma
         pa_assert(!p->has_dB || e->has_dB);
 
         ev = rv;
-        if (element_set_volume(e, m, cm, &ev, write_to_hw) < 0)
+        if (element_set_volume(e, m, cm, &ev, sync_volume, write_to_hw) < 0)
             return -1;
 
         if (!p->has_dB) {
diff --git a/src/modules/alsa/alsa-mixer.h b/src/modules/alsa/alsa-mixer.h
index a29aed1..e1cf1f8 100644
--- a/src/modules/alsa/alsa-mixer.h
+++ b/src/modules/alsa/alsa-mixer.h
@@ -218,7 +218,7 @@ int pa_alsa_path_probe(pa_alsa_path *p, snd_mixer_t *m, pa_bool_t ignore_dB);
 void pa_alsa_path_dump(pa_alsa_path *p);
 int pa_alsa_path_get_volume(pa_alsa_path *p, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v);
 int pa_alsa_path_get_mute(pa_alsa_path *path, snd_mixer_t *m, pa_bool_t *muted);
-int pa_alsa_path_set_volume(pa_alsa_path *path, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t write_to_hw);
+int pa_alsa_path_set_volume(pa_alsa_path *path, snd_mixer_t *m, const pa_channel_map *cm, pa_cvolume *v, pa_bool_t sync_volume, pa_bool_t write_to_hw);
 int pa_alsa_path_set_mute(pa_alsa_path *path, snd_mixer_t *m, pa_bool_t muted);
 int pa_alsa_path_select(pa_alsa_path *p, snd_mixer_t *m);
 void pa_alsa_path_set_callback(pa_alsa_path *p, snd_mixer_t *m, snd_mixer_elem_callback_t cb, void *userdata);
diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 96812c7..a042c2d 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1253,7 +1253,7 @@ static void sink_set_volume_cb(pa_sink *s) {
     struct userdata *u = s->userdata;
     pa_cvolume r;
     char vol_str_pcnt[PA_CVOLUME_SNPRINT_MAX];
-    pa_bool_t write_to_hw = (s->flags & PA_SINK_SYNC_VOLUME) ? FALSE : TRUE;
+    pa_bool_t sync_volume = !!(s->flags & PA_SINK_SYNC_VOLUME);
 
     pa_assert(u);
     pa_assert(u->mixer_path);
@@ -1262,7 +1262,7 @@ static void sink_set_volume_cb(pa_sink *s) {
     /* Shift up by the base volume */
     pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume);
 
-    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, write_to_hw) < 0)
+    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, sync_volume, !sync_volume) < 0)
         return;
 
     /* Shift down by the base volume, so that 0dB becomes maximum volume */
@@ -1319,7 +1319,7 @@ static void sink_write_volume_cb(pa_sink *s) {
     /* Shift up by the base volume */
     pa_sw_cvolume_divide_scalar(&hw_vol, &hw_vol, s->base_volume);
 
-    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE) < 0)
+    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE, TRUE) < 0)
         pa_log_error("Writing HW volume failed");
     else {
         pa_cvolume tmp_vol;
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index adec8f6..fb96ed0 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -1129,7 +1129,7 @@ static void source_set_volume_cb(pa_source *s) {
     struct userdata *u = s->userdata;
     pa_cvolume r;
     char vol_str_pcnt[PA_CVOLUME_SNPRINT_MAX];
-    pa_bool_t write_to_hw = (s->flags & PA_SOURCE_SYNC_VOLUME) ? FALSE : TRUE;
+    pa_bool_t sync_volume = !!(s->flags & PA_SOURCE_SYNC_VOLUME);
 
     pa_assert(u);
     pa_assert(u->mixer_path);
@@ -1138,7 +1138,7 @@ static void source_set_volume_cb(pa_source *s) {
     /* Shift up by the base volume */
     pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume);
 
-    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, write_to_hw) < 0)
+    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, sync_volume, !sync_volume) < 0)
         return;
 
     /* Shift down by the base volume, so that 0dB becomes maximum volume */
@@ -1195,7 +1195,7 @@ static void source_write_volume_cb(pa_source *s) {
     /* Shift up by the base volume */
     pa_sw_cvolume_divide_scalar(&hw_vol, &hw_vol, s->base_volume);
 
-    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE) < 0)
+    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE, TRUE) < 0)
         pa_log_error("Writing HW volume failed");
     else {
         pa_cvolume tmp_vol;

commit 1e7c4dd3e6af5bb91bf5e9985725396ebd06293f
Author: Colin Guthrie <colin at mageia.org>
Date:   Sun May 8 12:44:50 2011 +0100

    alsa-mixer: When setting hw volume, always round up with playback and down with capture.
    
    The previous logic in ade0a6f88464d8aecf83982d400ccfc402341920
    does not work with for input volumes.
    
    This was discussed on the mailing list:
    
    https://tango.0pointer.de/pipermail/pulseaudio-discuss/2011-May/010091.html
    
    This approach can introduce a problem when setting the volumes
    for sources. What follows is Tanu Kaskinen's analysis:
    
    [quote]
    I'll quote the log:
    
    D: protocol-native.c: Client pavucontrol changes volume of source alsa_input.pci-0000_00_1b.0.analog-stereo.
    D: alsa-source.c: Requested volume: 0:  45% 1:  45%
    D: alsa-source.c:            in dB: 0: -20.71 dB 1: -20.71 dB
    D: alsa-source.c: Got hardware volume: 0:  45% 1:  45%
    D: alsa-source.c:               in dB: 0: -21.00 dB 1: -21.00 dB
    D: alsa-source.c: Calculated software volume: 0: 101% 1: 101% (accurate-enough=no)
    D: alsa-source.c:                      in dB: 0: 0.29 dB 1: 0.29 dB
    D: source.c: Volume going up to 29273 at 270475970821
    D: source.c: Volume change to 29273 at 270475970821 was written 34 usec late
    D: alsa-source.c: Written HW volume did not match with the request: 0: 45% 1:  45% (request) != 0:  42% 1:  42%
    D: alsa-source.c:                                            in dB: 0: -21.00 dB 1: -21.00 dB (request) != 0: -22.50 dB 1: -22.50 dB
    
    Looking at the last line, the requested volume seems to hit exactly the
    right step (-21.00dB), but for some reason Alsa decides to choose
    something else. I'm pretty sure that this happens because of rounding
    errors. In the first phase we ask Alsa what dB value we should set, and
    it returns -21.00 dB. The value is given as a long int, but we convert
    that to pa_cvolume. Then when we set the volume, we convert the
    pa_cvolume value back to a long integer. At this point I believe it gets
    converted to -2101. This is not visible in the debug message for some
    reason - the rounding algorithm must be different from what was used
    with the pa_cvolume -> long conversion.
    [/quote]
    
    The commit after this contains a patch that addresses this issue.

diff --git a/src/modules/alsa/alsa-mixer.c b/src/modules/alsa/alsa-mixer.c
index 03a5312..e3673f8 100644
--- a/src/modules/alsa/alsa-mixer.c
+++ b/src/modules/alsa/alsa-mixer.c
@@ -893,7 +893,7 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
 
         if (e->has_dB) {
             long value = to_alsa_dB(f);
-            int rounding = value > 0 ? -1 : +1;
+            int rounding;
 
             if (e->volume_limit >= 0 && value > (e->max_dB * 100))
                 value = e->max_dB * 100;
@@ -903,6 +903,7 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
                  * if the channel is available, ALSA behaves very
                  * strangely and doesn't fail the call */
                 if (snd_mixer_selem_has_playback_channel(me, c)) {
+                    rounding = +1;
                     if (e->db_fix) {
                         if (write_to_hw)
                             r = snd_mixer_selem_set_playback_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));
@@ -925,6 +926,7 @@ static int element_set_volume(pa_alsa_element *e, snd_mixer_t *m, const pa_chann
                     r = -1;
             } else {
                 if (snd_mixer_selem_has_capture_channel(me, c)) {
+                    rounding = -1;
                     if (e->db_fix) {
                         if (write_to_hw)
                             r = snd_mixer_selem_set_capture_volume(me, c, decibel_fix_get_step(e->db_fix, &value, rounding));

commit 8203402b9f28da14d03a7120d434f6da7d23f9e6
Author: Colin Guthrie <colin at mageia.org>
Date:   Wed May 11 11:54:04 2011 +0100

    streams: Tidy up includes

diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 38bad35..a2330fa 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -29,7 +29,6 @@ typedef struct pa_sink_input pa_sink_input;
 
 #include <pulse/sample.h>
 #include <pulse/format.h>
-#include <pulsecore/hook-list.h>
 #include <pulsecore/memblockq.h>
 #include <pulsecore/resampler.h>
 #include <pulsecore/module.h>
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 4bcddba..cd4b0d0 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -27,12 +27,13 @@
 typedef struct pa_source_output pa_source_output;
 
 #include <pulse/sample.h>
-#include <pulsecore/source.h>
 #include <pulse/format.h>
 #include <pulsecore/memblockq.h>
 #include <pulsecore/resampler.h>
 #include <pulsecore/module.h>
 #include <pulsecore/client.h>
+#include <pulsecore/source.h>
+#include <pulsecore/core.h>
 #include <pulsecore/sink-input.h>
 
 typedef enum pa_source_output_state {

commit d1a628855da868f776f9e7f0129fc88ecb1f10a1
Author: Colin Guthrie <colin at mageia.org>
Date:   Wed May 11 11:53:23 2011 +0100

    alsa: Remove unneeded include

diff --git a/src/modules/alsa/alsa-sink.h b/src/modules/alsa/alsa-sink.h
index b9a4ac2..e640b62 100644
--- a/src/modules/alsa/alsa-sink.h
+++ b/src/modules/alsa/alsa-sink.h
@@ -28,7 +28,6 @@
 #include <pulsecore/sink.h>
 
 #include "alsa-util.h"
-#include "alsa-mixer.h"
 
 pa_sink* pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, pa_alsa_mapping *mapping);
 

commit 4ab60d03778bf1e680d29c6ff37266978df44aa5
Author: Colin Guthrie <colin at mageia.org>
Date:   Mon May 16 22:23:24 2011 +0100

    introspect: Get format of source output
    
    This gets the negotiated format of source outputs in
    pa_context_get_source_output*(). Also prints the format and volume
    in 'pactl list'.

diff --git a/PROTOCOL b/PROTOCOL
index 9c6c8a8..6b999af 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -263,9 +263,10 @@ PA_COMMAND_GET_SOURCE_INFO_LIST)
     ...
     format_info formatn
 
-Four new fields in reply from PA_COMMAND_GET_SOURCE_OUTPUT_INFO (and thus
+Five new fields in reply from PA_COMMAND_GET_SOURCE_OUTPUT_INFO (and thus
 PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
 
+    format_info format
     volume
     bool mute
     bool has_volume
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index d6ad0f5..014eadf 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -1168,6 +1168,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
 
             pa_zero(i);
             i.proplist = pa_proplist_new();
+            i.format = pa_format_info_new();
 
             if (pa_tagstruct_getu32(t, &i.index) < 0 ||
                 pa_tagstruct_gets(t, &i.name) < 0 ||
@@ -1185,10 +1186,12 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
                 (o->context->version >= 22 && (pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
                                                pa_tagstruct_get_boolean(t, &mute) < 0 ||
                                                pa_tagstruct_get_boolean(t, &has_volume) < 0 ||
-                                               pa_tagstruct_get_boolean(t, &volume_writable) < 0))) {
+                                               pa_tagstruct_get_boolean(t, &volume_writable) < 0 ||
+                                               pa_tagstruct_get_format_info(t, i.format) < 0))) {
 
                 pa_context_fail(o->context, PA_ERR_PROTOCOL);
                 pa_proplist_free(i.proplist);
+                pa_format_info_free(i.format);
                 goto finish;
             }
 
@@ -1203,6 +1206,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
             }
 
             pa_proplist_free(i.proplist);
+            pa_format_info_free(i.format);
         }
     }
 
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 84a12b6..196d44d 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -561,6 +561,7 @@ typedef struct pa_source_output_info {
     int mute;                            /**< Stream muted \since 1.0 */
     int has_volume;                      /**< Stream has volume. If not set, then the meaning of this struct's volume member is unspecified. \since 1.0 */
     int volume_writable;                 /**< The volume can be set. If not set, the volume can still change even though clients can't control the volume. \since 1.0 */
+    pa_format_info *format;              /**< Stream format information. \since 1.0 */
 } pa_source_output_info;
 
 /** Callback prototype for pa_context_get_source_output_info() and friends*/
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index d90b470..6c7c897 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -3317,6 +3317,7 @@ static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *
         pa_tagstruct_put_boolean(t, pa_source_output_get_mute(s));
         pa_tagstruct_put_boolean(t, has_volume);
         pa_tagstruct_put_boolean(t, s->volume_writable);
+        pa_tagstruct_put_format_info(t, s->format);
     }
 }
 
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 894f8f7..3bf983e 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -639,7 +639,7 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
 }
 
 static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) {
-    char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], f[PA_FORMAT_INFO_SNPRINT_MAX];
     char *pl;
 
     if (is_last < 0) {
@@ -680,6 +680,11 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
              "\tSource: %u\n"
              "\tSample Specification: %s\n"
              "\tChannel Map: %s\n"
+             "\tFormat: %s\n"
+             "\tMute: %s\n"
+             "\tVolume: %s\n"
+             "\t        %s\n"
+             "\t        balance %0.2f\n"
              "\tBuffer Latency: %0.0f usec\n"
              "\tSource Latency: %0.0f usec\n"
              "\tResample method: %s\n"
@@ -691,6 +696,11 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu
            i->source,
            pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
            pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
+           pa_format_info_snprint(f, sizeof(f), i->format),
+           pa_yes_no(i->mute),
+           pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
+           pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
+           pa_cvolume_get_balance(&i->volume, &i->channel_map),
            (double) i->buffer_usec,
            (double) i->source_usec,
            i->resample_method ? i->resample_method : _("n/a"),

commit dffc4d18d3a9f608f8b316f1e7057d13978ef44f
Author: Colin Guthrie <colin at mageia.org>
Date:   Tue May 17 22:31:10 2011 +0100

    capture: Implement per-stream volume control for capture streams.
    
    This piggy backs onto the previous changes for protocol 22 and
    thus does not bump the version. This and the previous commits should be
    seen as mostly atomic. Apologies for any bisecting issues this causes
    (although I would expect these to be minimal)

diff --git a/PROTOCOL b/PROTOCOL
index 03a9cb5..9c6c8a8 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -262,3 +262,11 @@ PA_COMMAND_GET_SOURCE_INFO_LIST)
     format_info format1
     ...
     format_info formatn
+
+Four new fields in reply from PA_COMMAND_GET_SOURCE_OUTPUT_INFO (and thus
+PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
+
+    volume
+    bool mute
+    bool has_volume
+    bool volume_writable
diff --git a/src/map-file b/src/map-file
index 026ac10..a9d9aac 100644
--- a/src/map-file
+++ b/src/map-file
@@ -103,6 +103,8 @@ pa_context_set_sink_port_by_index;
 pa_context_set_sink_port_by_name;
 pa_context_set_sink_volume_by_index;
 pa_context_set_sink_volume_by_name;
+pa_context_set_source_output_mute;
+pa_context_set_source_output_volume;
 pa_context_set_source_mute_by_index;
 pa_context_set_source_mute_by_name;
 pa_context_set_source_port_by_index;
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c
index 3355fbd..adec8f6 100644
--- a/src/modules/alsa/alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -93,6 +93,7 @@ struct userdata {
     snd_pcm_t *pcm_handle;
 
     pa_alsa_fdlist *mixer_fdl;
+    pa_alsa_mixer_pdata *mixer_pd;
     snd_mixer_t *mixer_handle;
     pa_alsa_path_set *mixer_path_set;
     pa_alsa_path *mixer_path;
@@ -114,8 +115,8 @@ struct userdata {
 
     pa_usec_t watermark_dec_not_before;
 
-    char *device_name;
-    char *control_device;
+    char *device_name;  /* name of the PCM device */
+    char *control_device; /* name of the control device */
 
     pa_bool_t use_mmap:1, use_tsched:1;
 
@@ -185,10 +186,10 @@ static int reserve_init(struct userdata *u, const char *dname) {
     if (pa_in_system_mode())
         return 0;
 
-    /* We are resuming, try to lock the device */
     if (!(rname = pa_alsa_get_reserve_name(dname)))
         return 0;
 
+    /* We are resuming, try to lock the device */
     u->reserve = pa_reserve_wrapper_get(u->core, rname);
     pa_xfree(rname);
 
@@ -238,10 +239,10 @@ static int reserve_monitor_init(struct userdata *u, const char *dname) {
     if (pa_in_system_mode())
         return 0;
 
-    /* We are resuming, try to lock the device */
     if (!(rname = pa_alsa_get_reserve_name(dname)))
         return 0;
 
+    /* We are resuming, try to lock the device */
     u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname);
     pa_xfree(rname);
 
@@ -256,6 +257,7 @@ static int reserve_monitor_init(struct userdata *u, const char *dname) {
 
 static void fix_min_sleep_wakeup(struct userdata *u) {
     size_t max_use, max_use_2;
+
     pa_assert(u);
     pa_assert(u->use_tsched);
 
@@ -350,7 +352,7 @@ restart:
     u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC;
 }
 
-static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
+static void hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) {
     pa_usec_t wm, usec;
 
     pa_assert(sleep_usec);
@@ -378,8 +380,6 @@ static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_use
                  (unsigned long) (*sleep_usec / PA_USEC_PER_MSEC),
                  (unsigned long) (*process_usec / PA_USEC_PER_MSEC));
 #endif
-
-    return usec;
 }
 
 static int try_recover(struct userdata *u, const char *call, int err) {
@@ -444,9 +444,9 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t
         else if (left_to_record > u->watermark_dec_threshold) {
             reset_not_before = FALSE;
 
-            /* We decrease the watermark only if have actually been
-             * woken up by a timeout. If something else woke us up
-             * it's too easy to fulfill the deadlines... */
+            /* We decrease the watermark only if have actually
+             * been woken up by a timeout. If something else woke
+             * us up it's too easy to fulfill the deadlines... */
 
             if (on_timeout)
                 decrease_watermark(u);
@@ -521,6 +521,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
             break;
         }
 
+
         if (++j > 10) {
 #ifdef DEBUG_TIMING
             pa_log_debug("Not filling up, because already too many iterations.");
@@ -536,15 +537,14 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
 #endif
 
         for (;;) {
+            pa_memchunk chunk;
+            void *p;
             int err;
             const snd_pcm_channel_area_t *areas;
             snd_pcm_uframes_t offset, frames;
-            pa_memchunk chunk;
-            void *p;
             snd_pcm_sframes_t sframes;
 
             frames = (snd_pcm_uframes_t) (n_bytes / u->frame_size);
-
 /*             pa_log_debug("%lu frames to read", (unsigned long) frames); */
 
             if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) {
@@ -822,6 +822,7 @@ static int build_pollfd(struct userdata *u) {
     return 0;
 }
 
+/* Called from IO context */
 static int suspend(struct userdata *u) {
     pa_assert(u);
     pa_assert(u->pcm_handle);
@@ -842,6 +843,7 @@ static int suspend(struct userdata *u) {
     return 0;
 }
 
+/* Called from IO context */
 static int update_sw_params(struct userdata *u) {
     snd_pcm_uframes_t avail_min;
     int err;
@@ -894,6 +896,7 @@ static int update_sw_params(struct userdata *u) {
     return 0;
 }
 
+/* Called from IO context */
 static int unsuspend(struct userdata *u) {
     pa_sample_spec ss;
     int err;
@@ -971,6 +974,7 @@ fail:
     return -PA_ERR_IO;
 }
 
+/* Called from IO context */
 static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
     struct userdata *u = PA_SOURCE(o)->userdata;
 
@@ -993,6 +997,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
 
                 case PA_SOURCE_SUSPENDED: {
                     int r;
+
                     pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state));
 
                     if ((r = suspend(u)) < 0)
@@ -1049,7 +1054,7 @@ static int source_set_state_cb(pa_source *s, pa_source_state_t new_state) {
     return 0;
 }
 
-static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
+static int ctl_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
     struct userdata *u = snd_mixer_elem_get_callback_private(elem);
 
     pa_assert(u);
@@ -1069,6 +1074,24 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
     return 0;
 }
 
+static int io_mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {
+    struct userdata *u = snd_mixer_elem_get_callback_private(elem);
+
+    pa_assert(u);
+    pa_assert(u->mixer_handle);
+
+    if (mask == SND_CTL_EVENT_MASK_REMOVE)
+        return 0;
+
+    if (u->source->suspend_cause & PA_SUSPEND_SESSION)
+        return 0;
+
+    if (mask & SND_CTL_EVENT_MASK_VALUE)
+        pa_source_update_volume_and_mute(u->source);
+
+    return 0;
+}
+
 static void source_get_volume_cb(pa_source *s) {
     struct userdata *u = s->userdata;
     pa_cvolume r;
@@ -1095,7 +1118,7 @@ static void source_get_volume_cb(pa_source *s) {
     if (pa_cvolume_equal(&u->hardware_volume, &r))
         return;
 
-    s->volume = u->hardware_volume = r;
+    s->real_volume = u->hardware_volume = r;
 
     /* Hmm, so the hardware volume changed, let's reset our software volume */
     if (u->mixer_path->has_dB)
@@ -1106,15 +1129,16 @@ static void source_set_volume_cb(pa_source *s) {
     struct userdata *u = s->userdata;
     pa_cvolume r;
     char vol_str_pcnt[PA_CVOLUME_SNPRINT_MAX];
+    pa_bool_t write_to_hw = (s->flags & PA_SOURCE_SYNC_VOLUME) ? FALSE : TRUE;
 
     pa_assert(u);
     pa_assert(u->mixer_path);
     pa_assert(u->mixer_handle);
 
     /* Shift up by the base volume */
-    pa_sw_cvolume_divide_scalar(&r, &s->volume, s->base_volume);
+    pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume);
 
-    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, TRUE) < 0)
+    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r, write_to_hw) < 0)
         return;
 
     /* Shift down by the base volume, so that 0dB becomes maximum volume */
@@ -1128,7 +1152,7 @@ static void source_set_volume_cb(pa_source *s) {
         char vol_str_db[PA_SW_CVOLUME_SNPRINT_DB_MAX];
 
         /* Match exactly what the user requested by software */
-        pa_sw_cvolume_divide(&new_soft_volume, &s->volume, &u->hardware_volume);
+        pa_sw_cvolume_divide(&new_soft_volume, &s->real_volume, &u->hardware_volume);
 
         /* If the adjustment to do in software is only minimal we
          * can skip it. That saves us CPU at the expense of a bit of
@@ -1137,8 +1161,8 @@ static void source_set_volume_cb(pa_source *s) {
             (pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) &&
             (pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
 
-        pa_log_debug("Requested volume: %s", pa_cvolume_snprint(vol_str_pcnt, sizeof(vol_str_pcnt), &s->volume));
-        pa_log_debug("           in dB: %s", pa_sw_cvolume_snprint_dB(vol_str_db, sizeof(vol_str_db), &s->volume));
+        pa_log_debug("Requested volume: %s", pa_cvolume_snprint(vol_str_pcnt, sizeof(vol_str_pcnt), &s->real_volume));
+        pa_log_debug("           in dB: %s", pa_sw_cvolume_snprint_dB(vol_str_db, sizeof(vol_str_db), &s->real_volume));
         pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(vol_str_pcnt, sizeof(vol_str_pcnt), &u->hardware_volume));
         pa_log_debug("              in dB: %s", pa_sw_cvolume_snprint_dB(vol_str_db, sizeof(vol_str_db), &u->hardware_volume));
         pa_log_debug("Calculated software volume: %s (accurate-enough=%s)",
@@ -1155,7 +1179,49 @@ static void source_set_volume_cb(pa_source *s) {
         /* We can't match exactly what the user requested, hence let's
          * at least tell the user about it */
 
-        s->volume = r;
+        s->real_volume = r;
+    }
+}
+
+static void source_write_volume_cb(pa_source *s) {
+    struct userdata *u = s->userdata;
+    pa_cvolume hw_vol = s->thread_info.current_hw_volume;
+
+    pa_assert(u);
+    pa_assert(u->mixer_path);
+    pa_assert(u->mixer_handle);
+    pa_assert(s->flags & PA_SOURCE_SYNC_VOLUME);
+
+    /* Shift up by the base volume */
+    pa_sw_cvolume_divide_scalar(&hw_vol, &hw_vol, s->base_volume);
+
+    if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &hw_vol, TRUE) < 0)
+        pa_log_error("Writing HW volume failed");
+    else {
+        pa_cvolume tmp_vol;
+        pa_bool_t accurate_enough;
+
+        /* Shift down by the base volume, so that 0dB becomes maximum volume */
+        pa_sw_cvolume_multiply_scalar(&hw_vol, &hw_vol, s->base_volume);
+
+        pa_sw_cvolume_divide(&tmp_vol, &hw_vol, &s->thread_info.current_hw_volume);
+        accurate_enough =
+            (pa_cvolume_min(&tmp_vol) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) &&
+            (pa_cvolume_max(&tmp_vol) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
+
+        if (!accurate_enough) {
+            union {
+                char db[2][PA_SW_CVOLUME_SNPRINT_DB_MAX];
+                char pcnt[2][PA_CVOLUME_SNPRINT_MAX];
+            } vol;
+
+            pa_log_debug("Written HW volume did not match with the request: %s (request) != %s",
+                         pa_cvolume_snprint(vol.pcnt[0], sizeof(vol.pcnt[0]), &s->thread_info.current_hw_volume),
+                         pa_cvolume_snprint(vol.pcnt[1], sizeof(vol.pcnt[1]), &hw_vol));
+            pa_log_debug("                                           in dB: %s (request) != %s",
+                         pa_sw_cvolume_snprint_dB(vol.db[0], sizeof(vol.db[0]), &s->thread_info.current_hw_volume),
+                         pa_sw_cvolume_snprint_dB(vol.db[1], sizeof(vol.db[1]), &hw_vol));
+        }
     }
 }
 
@@ -1220,7 +1286,9 @@ static int source_set_port_cb(pa_source *s, pa_device_port *p) {
 static void source_update_requested_latency_cb(pa_source *s) {
     struct userdata *u = s->userdata;
     pa_assert(u);
-    pa_assert(u->use_tsched);
+    pa_assert(u->use_tsched); /* only when timer scheduling is used
+                               * we can dynamically adjust the
+                               * latency */
 
     if (!u->pcm_handle)
         return;
@@ -1243,6 +1311,7 @@ static void thread_func(void *userdata) {
 
     for (;;) {
         int ret;
+        pa_usec_t rtpoll_sleep = 0;
 
 #ifdef DEBUG_TIMING
         pa_log_debug("Loop");
@@ -1291,17 +1360,29 @@ static void thread_func(void *userdata) {
 /*                 pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); */
 
                 /* We don't trust the conversion, so we wake up whatever comes first */
-                pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(sleep_usec, cusec));
+                rtpoll_sleep = PA_MIN(sleep_usec, cusec);
             }
-        } else if (u->use_tsched)
+        }
+
+        if (u->source->flags & PA_SOURCE_SYNC_VOLUME) {
+            pa_usec_t volume_sleep;
+            pa_source_volume_change_apply(u->source, &volume_sleep);
+            if (volume_sleep > 0)
+                rtpoll_sleep = PA_MIN(volume_sleep, rtpoll_sleep);
+        }
 
-            /* OK, we're in an invalid state, let's disable our timers */
+        if (rtpoll_sleep > 0)
+            pa_rtpoll_set_timer_relative(u->rtpoll, rtpoll_sleep);
+        else
             pa_rtpoll_set_timer_disabled(u->rtpoll);
 
         /* Hmm, nothing to do. Let's sleep */
         if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
             goto fail;
 
+        if (u->source->flags & PA_SOURCE_SYNC_VOLUME)
+            pa_source_volume_change_apply(u->source, NULL);
+
         if (ret == 0)
             goto finish;
 
@@ -1419,7 +1500,7 @@ fail:
     }
 }
 
-static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
+static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_volume) {
     pa_assert(u);
 
     if (!u->mixer_handle)
@@ -1475,8 +1556,17 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
 
         u->source->get_volume = source_get_volume_cb;
         u->source->set_volume = source_set_volume_cb;
+        u->source->write_volume = source_write_volume_cb;
+
+        u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL;
+        if (u->mixer_path->has_dB) {
+            u->source->flags |= PA_SOURCE_DECIBEL_VOLUME;
+            if (sync_volume) {
+                u->source->flags |= PA_SOURCE_SYNC_VOLUME;
+                pa_log_info("Successfully enabled synchronous volume.");
+            }
+        }
 
-        u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->mixer_path->has_dB ? PA_SOURCE_DECIBEL_VOLUME : 0);
         pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->mixer_path->has_dB ? "supported" : "not supported");
     }
 
@@ -1489,17 +1579,31 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB) {
         pa_log_info("Using hardware mute control.");
     }
 
-    u->mixer_fdl = pa_alsa_fdlist_new();
+    if (u->source->flags & (PA_SOURCE_HW_VOLUME_CTRL|PA_SOURCE_HW_MUTE_CTRL)) {
+        int (*mixer_callback)(snd_mixer_elem_t *, unsigned int);
+        if (u->source->flags & PA_SOURCE_SYNC_VOLUME) {
+            u->mixer_pd = pa_alsa_mixer_pdata_new();
+            mixer_callback = io_mixer_callback;
 
-    if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, u->core->mainloop) < 0) {
-        pa_log("Failed to initialize file descriptor monitoring");
-        return -1;
-    }
+            if (pa_alsa_set_mixer_rtpoll(u->mixer_pd, u->mixer_handle, u->rtpoll) < 0) {
+                pa_log("Failed to initialize file descriptor monitoring");
+                return -1;
+            }
+        } else {
+            u->mixer_fdl = pa_alsa_fdlist_new();
+            mixer_callback = ctl_mixer_callback;
 
-    if (u->mixer_path_set)
-        pa_alsa_path_set_set_callback(u->mixer_path_set, u->mixer_handle, mixer_callback, u);
-    else
-        pa_alsa_path_set_callback(u->mixer_path, u->mixer_handle, mixer_callback, u);
+            if (pa_alsa_fdlist_set_mixer(u->mixer_fdl, u->mixer_handle, u->core->mainloop) < 0) {
+                pa_log("Failed to initialize file descriptor monitoring");
+                return -1;
+            }
+        }
+
+        if (u->mixer_path_set)
+            pa_alsa_path_set_set_callback(u->mixer_path_set, u->mixer_handle, mixer_callback, u);
+        else
+            pa_alsa_path_set_callback(u->mixer_path, u->mixer_handle, mixer_callback, u);
+    }
 
     return 0;
 }
@@ -1513,7 +1617,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark;
     snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames;
     size_t frame_size;
-    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE, namereg_fail = FALSE;
+    pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE, namereg_fail = FALSE, sync_volume = FALSE;
     pa_source_new_data data;
     pa_alsa_profile_set *profile_set = NULL;
 
@@ -1523,7 +1627,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     ss = m->core->default_sample_spec;
     map = m->core->default_channel_map;
     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
-        pa_log("Failed to parse sample specification");
+        pa_log("Failed to parse sample specification and channel map");
         goto fail;
     }
 
@@ -1557,7 +1661,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     }
 
     if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
-        pa_log("Failed to parse timer_scheduling argument.");
+        pa_log("Failed to parse tsched argument.");
         goto fail;
     }
 
@@ -1566,6 +1670,12 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
         goto fail;
     }
 
+    sync_volume = m->core->sync_volume;
+    if (pa_modargs_get_value_boolean(ma, "sync_volume", &sync_volume) < 0) {
+        pa_log("Failed to parse sync_volume argument.");
+        goto fail;
+    }
+
     use_tsched = pa_alsa_may_tsched(use_tsched);
 
     u = pa_xnew0(struct userdata, 1);
@@ -1728,6 +1838,18 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
         goto fail;
     }
 
+    if (pa_modargs_get_value_u32(ma, "sync_volume_safety_margin",
+                                 &u->source->thread_info.volume_change_safety_margin) < 0) {
+        pa_log("Failed to parse sync_volume_safety_margin parameter");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_s32(ma, "sync_volume_extra_delay",
+                                 &u->source->thread_info.volume_change_extra_delay) < 0) {
+        pa_log("Failed to parse sync_volume_extra_delay parameter");
+        goto fail;
+    }
+
     u->source->parent.process_msg = source_process_msg;
     if (u->use_tsched)
         u->source->update_requested_latency = source_update_requested_latency_cb;
@@ -1776,7 +1898,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
     if (update_sw_params(u) < 0)
         goto fail;
 
-    if (setup_mixer(u, ignore_dB) < 0)
+    if (setup_mixer(u, ignore_dB, sync_volume) < 0)
         goto fail;
 
     pa_alsa_dump(PA_LOG_DEBUG, u->pcm_handle);
@@ -1785,6 +1907,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
         pa_log("Failed to create thread.");
         goto fail;
     }
+
     /* Get initial mixer settings */
     if (data.volume_is_set) {
         if (u->source->set_volume)
@@ -1836,6 +1959,9 @@ static void userdata_free(struct userdata *u) {
     if (u->source)
         pa_source_unref(u->source);
 
+    if (u->mixer_pd)
+        pa_alsa_mixer_pdata_free(u->mixer_pd);
+
     if (u->alsa_rtpoll_item)
         pa_rtpoll_item_free(u->alsa_rtpoll_item);
 
diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c
index 90ffea5..478a2e8 100644
--- a/src/modules/alsa/module-alsa-source.c
+++ b/src/modules/alsa/module-alsa-source.c
@@ -79,7 +79,10 @@ PA_MODULE_USAGE(
         "tsched_buffer_size=<buffer size when using timer based scheduling> "
         "tsched_buffer_watermark=<upper fill watermark> "
         "ignore_dB=<ignore dB information from the device?> "
-        "control=<name of mixer control>");
+        "control=<name of mixer control>"
+        "sync_volume=<syncronize sw and hw voluchanges in IO-thread?> "
+        "sync_volume_safety_margin=<usec adjustment depending on volume direction> "
+        "sync_volume_extra_delay=<usec adjustment to HW volume changes>");
 
 static const char* const valid_modargs[] = {
     "name",
@@ -100,6 +103,9 @@ static const char* const valid_modargs[] = {
     "tsched_buffer_watermark",
     "ignore_dB",
     "control",
+    "sync_volume",
+    "sync_volume_safety_margin",
+    "sync_volume_extra_delay",
     NULL
 };
 
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 1ff752a..0639afd 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1868,7 +1868,7 @@ static void source_set_volume_cb(pa_source *s) {
     pa_assert(u->source == s);
     pa_assert(u->profile == PROFILE_HSP);
 
-    gain = (pa_cvolume_max(&s->volume) * HSP_MAX_GAIN) / PA_VOLUME_NORM;
+    gain = (pa_cvolume_max(&s->real_volume) * HSP_MAX_GAIN) / PA_VOLUME_NORM;
 
     if (gain > HSP_MAX_GAIN)
         gain = HSP_MAX_GAIN;
@@ -1879,7 +1879,7 @@ static void source_set_volume_cb(pa_source *s) {
     if (volume < PA_VOLUME_NORM)
         volume++;
 
-    pa_cvolume_set(&s->volume, u->sample_spec.channels, volume);
+    pa_cvolume_set(&s->real_volume, u->sample_spec.channels, volume);
 
     pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetMicrophoneGain"));
     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID));
diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c
index c5ba88e..652790f 100644
--- a/src/modules/dbus/iface-device.c
+++ b/src/modules/dbus/iface-device.c
@@ -447,7 +447,7 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessag
     if (d->type == DEVICE_TYPE_SINK)
         pa_sink_set_volume(d->sink, &new_vol, TRUE, TRUE);
     else
-        pa_source_set_volume(d->source, &new_vol, TRUE);
+        pa_source_set_volume(d->source, &new_vol, TRUE, TRUE);
 
     pa_dbus_send_empty_reply(conn, msg);
 }
diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
index 3d22ef8..e5ee5b1 100644
--- a/src/modules/echo-cancel/module-echo-cancel.c
+++ b/src/modules/echo-cancel/module-echo-cancel.c
@@ -526,8 +526,7 @@ static void source_set_volume_cb(pa_source *s) {
         !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
         return;
 
-    /* FIXME, no volume control in source_output, set volume at the master */
-    pa_source_set_volume(u->source_output->source, &s->volume, TRUE);
+    pa_source_output_set_volume(u->source_output, &s->real_volume, s->save_volume, TRUE);
 }
 
 /* Called from main context */
@@ -546,6 +545,7 @@ static void sink_set_volume_cb(pa_sink *s) {
 
 static void source_get_volume_cb(pa_source *s) {
     struct userdata *u;
+    pa_cvolume v;
 
     pa_source_assert_ref(s);
     pa_assert_se(u = s->userdata);
@@ -554,18 +554,16 @@ static void source_get_volume_cb(pa_source *s) {
         !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
         return;
 
-    /* FIXME, no volume control in source_output, get the info from the master */
-    pa_source_get_volume(u->source_output->source, TRUE);
+    pa_source_output_get_volume(u->source_output, &v, TRUE);
 
-    if (pa_cvolume_equal(&s->volume,&u->source_output->source->volume))
+    if (pa_cvolume_equal(&s->real_volume, &v))
         /* no change */
         return;
 
-    s->volume = u->source_output->source->volume;
+    s->real_volume = v;
     pa_source_set_soft_volume(s, NULL);
 }
 
-
 /* Called from main context */
 static void source_set_mute_cb(pa_source *s) {
     struct userdata *u;
@@ -577,8 +575,7 @@ static void source_set_mute_cb(pa_source *s) {
         !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
         return;
 
-    /* FIXME, no volume control in source_output, set mute at the master */
-    pa_source_set_mute(u->source_output->source, TRUE, TRUE);
+    pa_source_output_set_mute(u->source_output, s->muted, s->save_muted);
 }
 
 /* Called from main context */
@@ -606,8 +603,7 @@ static void source_get_mute_cb(pa_source *s) {
         !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
         return;
 
-    /* FIXME, no volume control in source_output, get the info from the master */
-    pa_source_get_mute(u->source_output->source, TRUE);
+    pa_source_output_get_mute(u->source_output);
 }
 
 /* must be called from the input thread context */
diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c
index 170fa4e..680e71a 100644
--- a/src/modules/module-virtual-source.c
+++ b/src/modules/module-virtual-source.c
@@ -61,6 +61,8 @@ PA_MODULE_USAGE(
           "rate=<sample rate> "
           "channels=<number of channels> "
           "channel_map=<channel map> "
+          "use_volume_sharing=<yes or no> "
+          "force_flat_volume=<yes or no> "
         ));
 
 #define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
@@ -96,6 +98,8 @@ static const char* const valid_modargs[] = {
     "rate",
     "channels",
     "channel_map",
+    "use_volume_sharing",
+    "force_flat_volume",
     NULL
 };
 
@@ -241,32 +245,9 @@ static void source_set_volume_cb(pa_source *s) {
         !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
         return;
 
-    /* FIXME, no volume control in source_output, set volume at the master */
-    pa_source_set_volume(u->source_output->source, &s->volume, TRUE);
+    pa_source_output_set_volume(u->source_output, &s->real_volume, s->save_volume, TRUE);
 }
 
-static void source_get_volume_cb(pa_source *s) {
-    struct userdata *u;
-
-    pa_source_assert_ref(s);
-    pa_assert_se(u = s->userdata);
-
-    if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
-        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
-        return;
-
-    /* FIXME, no volume control in source_output, get the info from the master */
-    pa_source_get_volume(u->source_output->source, TRUE);
-
-    if (pa_cvolume_equal(&s->volume,&u->source_output->source->volume))
-        /* no change */
-        return;
-
-    s->volume = u->source_output->source->volume;
-    pa_source_set_soft_volume(s, NULL);
-}
-
-
 /* Called from main context */
 static void source_set_mute_cb(pa_source *s) {
     struct userdata *u;
@@ -278,23 +259,7 @@ static void source_set_mute_cb(pa_source *s) {
         !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
         return;
 
-    /* FIXME, no volume control in source_output, set mute at the master */
-    pa_source_set_mute(u->source_output->source, TRUE, TRUE);
-}
-
-/* Called from main context */
-static void source_get_mute_cb(pa_source *s) {
-    struct userdata *u;
-
-    pa_source_assert_ref(s);
-    pa_assert_se(u = s->userdata);
-
-    if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) ||
-        !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output)))
-        return;
-
-    /* FIXME, no volume control in source_output, get the info from the master */
-    pa_source_get_mute(u->source_output->source, TRUE);
+    pa_source_output_set_mute(u->source_output, s->muted, s->save_muted);
 }
 
 /* Called from input thread context */
@@ -538,6 +503,8 @@ int pa__init(pa_module*m) {
     pa_source *master=NULL;
     pa_source_output_new_data source_output_data;
     pa_source_new_data source_data;
+    pa_bool_t use_volume_sharing = FALSE;
+    pa_bool_t force_flat_volume = FALSE;
 
     /* optional for uplink_sink */
     pa_sink_new_data sink_data;
@@ -565,6 +532,20 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
+    if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) {
+        pa_log("use_volume_sharing= expects a boolean argument");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) {
+        pa_log("force_flat_volume= expects a boolean argument");
+        goto fail;
+    }
+
+    if (use_volume_sharing && force_flat_volume) {
+        pa_log("Flat volume can't be forced when using volume sharing.");
+        goto fail;
+    }
 
     u = pa_xnew0(struct userdata, 1);
     if (!u) {
@@ -605,9 +586,10 @@ int pa__init(pa_module*m) {
         pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Source %s on %s", source_data.name, z ? z : master->name);
     }
 
-    u->source = pa_source_new(m->core, &source_data,
-                          PA_SOURCE_HW_MUTE_CTRL|PA_SOURCE_HW_VOLUME_CTRL|PA_SOURCE_DECIBEL_VOLUME|
-                          (master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY)));
+    u->source = pa_source_new(m->core, &source_data, (master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY))
+                                                     | (use_volume_sharing ? PA_SOURCE_SHARE_VOLUME_WITH_MASTER : 0)
+                                                     | (force_flat_volume ? PA_SOURCE_FLAT_VOLUME : 0));
+
     pa_source_new_data_done(&source_data);
 
     if (!u->source) {
@@ -618,10 +600,8 @@ int pa__init(pa_module*m) {
     u->source->parent.process_msg = source_process_msg_cb;
     u->source->set_state = source_set_state_cb;
     u->source->update_requested_latency = source_update_requested_latency_cb;
-    u->source->set_volume = source_set_volume_cb;
+    u->source->set_volume = use_volume_sharing ? NULL : source_set_volume_cb;
     u->source->set_mute = source_set_mute_cb;
-    u->source->get_volume = source_get_volume_cb;
-    u->source->get_mute = source_get_mute_cb;
     u->source->userdata = u;
 
     pa_source_set_asyncmsgq(u->source, master->asyncmsgq);
diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c
index 84dbbda..dea5628 100644
--- a/src/modules/oss/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -848,11 +848,11 @@ static void source_get_volume(pa_source *s) {
     pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
 
     if (u->mixer_devmask & SOUND_MASK_IGAIN)
-        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->volume) >= 0)
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->real_volume) >= 0)
             return;
 
     if (u->mixer_devmask & SOUND_MASK_RECLEV)
-        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume) >= 0)
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->real_volume) >= 0)
             return;
 
     pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
@@ -866,11 +866,11 @@ static void source_set_volume(pa_source *s) {
     pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
 
     if (u->mixer_devmask & SOUND_MASK_IGAIN)
-        if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->volume) >= 0)
+        if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->real_volume) >= 0)
             return;
 
     if (u->mixer_devmask & SOUND_MASK_RECLEV)
-        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->volume) >= 0)
+        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->real_volume) >= 0)
             return;
 
     pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index a781c14..d6ad0f5 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -1164,7 +1164,7 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
 
         while (!pa_tagstruct_eof(t)) {
             pa_source_output_info i;
-            pa_bool_t corked = FALSE;
+            pa_bool_t mute = FALSE, corked = FALSE, has_volume = FALSE, volume_writable = TRUE;
 
             pa_zero(i);
             i.proplist = pa_proplist_new();
@@ -1181,14 +1181,21 @@ static void context_get_source_output_info_callback(pa_pdispatch *pd, uint32_t c
                 pa_tagstruct_gets(t, &i.resample_method) < 0 ||
                 pa_tagstruct_gets(t, &i.driver) < 0 ||
                 (o->context->version >= 13 && pa_tagstruct_get_proplist(t, i.proplist) < 0) ||
-                (o->context->version >= 19 && pa_tagstruct_get_boolean(t, &corked) < 0)) {
+                (o->context->version >= 19 && pa_tagstruct_get_boolean(t, &corked) < 0) ||
+                (o->context->version >= 22 && (pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+                                               pa_tagstruct_get_boolean(t, &mute) < 0 ||
+                                               pa_tagstruct_get_boolean(t, &has_volume) < 0 ||
+                                               pa_tagstruct_get_boolean(t, &volume_writable) < 0))) {
 
                 pa_context_fail(o->context, PA_ERR_PROTOCOL);
                 pa_proplist_free(i.proplist);
                 goto finish;
             }
 
+            i.mute = (int) mute;
             i.corked = (int) corked;
+            i.has_volume = (int) has_volume;
+            i.volume_writable = (int) volume_writable;
 
             if (o->callback) {
                 pa_source_output_info_cb_t cb = (pa_source_output_info_cb_t) o->callback;
@@ -1487,6 +1494,56 @@ pa_operation* pa_context_set_source_mute_by_name(pa_context *c, const char *name
     return o;
 }
 
+pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(volume);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 22, PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, pa_cvolume_valid(volume), PA_ERR_INVALID);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_put_cvolume(t, volume);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, idx != PA_INVALID_INDEX, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 22, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_SOURCE_OUTPUT_MUTE, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_put_boolean(t, mute);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
 /** Sample Cache **/
 
 static void context_get_sample_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 65f12d1..84a12b6 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -164,9 +164,8 @@
  *
  * If an application desires to modify the volume of just a single stream
  * (commonly one of its own streams), this can be done by setting the volume
- * of its associated sink input, using pa_context_set_sink_input_volume().
- *
- * There is no support for modifying the volume of source outputs.
+ * of its associated sink input or source output, using
+ * pa_context_set_sink_input_volume() or pa_context_set_source_output_volume()
  *
  * It is also possible to remove sink inputs and source outputs, terminating
  * the streams associated with them:
@@ -558,6 +557,10 @@ typedef struct pa_source_output_info {
     const char *driver;                  /**< Driver name */
     pa_proplist *proplist;               /**< Property list \since 0.9.11 */
     int corked;                          /**< Stream corked \since 1.0 */
+    pa_cvolume volume;                   /**< The volume of this source output \since 1.0 */
+    int mute;                            /**< Stream muted \since 1.0 */
+    int has_volume;                      /**< Stream has volume. If not set, then the meaning of this struct's volume member is unspecified. \since 1.0 */
+    int volume_writable;                 /**< The volume can be set. If not set, the volume can still change even though clients can't control the volume. \since 1.0 */
 } pa_source_output_info;
 
 /** Callback prototype for pa_context_get_source_output_info() and friends*/
@@ -575,6 +578,12 @@ pa_operation* pa_context_move_source_output_by_name(pa_context *c, uint32_t idx,
 /** Move the specified source output to a different source. \since 0.9.5 */
 pa_operation* pa_context_move_source_output_by_index(pa_context *c, uint32_t idx, uint32_t source_idx, pa_context_success_cb_t cb, void* userdata);
 
+/** Set the volume of a source output stream \since 1.0 */
+pa_operation* pa_context_set_source_output_volume(pa_context *c, uint32_t idx, const pa_cvolume *volume, pa_context_success_cb_t cb, void *userdata);
+
+/** Set the mute switch of a source output stream \since 1.0 */
+pa_operation* pa_context_set_source_output_mute(pa_context *c, uint32_t idx, int mute, pa_context_success_cb_t cb, void *userdata);
+
 /** Kill a source output. */
 pa_operation* pa_context_kill_source_output(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
 
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 3293684..d60b864 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -1149,7 +1149,8 @@ static int create_stream(
 
     pa_tagstruct *t;
     uint32_t tag;
-    pa_bool_t volume_set = FALSE;
+    pa_bool_t volume_set = !!volume;
+    pa_cvolume cv;
     uint32_t i;
 
     pa_assert(s);
@@ -1246,9 +1247,18 @@ static int create_stream(
             PA_TAG_BOOLEAN, s->corked,
             PA_TAG_INVALID);
 
-    if (s->direction == PA_STREAM_PLAYBACK) {
-        pa_cvolume cv;
+    if (!volume) {
+        if (pa_sample_spec_valid(&s->sample_spec))
+            volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
+        else {
+            /* This is not really relevant, since no volume was set, and
+             * the real number of channels is embedded in the format_info
+             * structure */
+            volume = pa_cvolume_reset(&cv, PA_CHANNELS_MAX);
+        }
+    }
 
+    if (s->direction == PA_STREAM_PLAYBACK) {
         pa_tagstruct_put(
                 t,
                 PA_TAG_U32, s->buffer_attr.tlength,
@@ -1257,19 +1267,6 @@ static int create_stream(
                 PA_TAG_U32, s->syncid,
                 PA_TAG_INVALID);
 
-        volume_set = !!volume;
-
-        if (!volume) {
-            if (pa_sample_spec_valid(&s->sample_spec))
-                volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
-            else {
-                /* This is not really relevant, since no volume was set, and
-                 * the real number of channels is embedded in the format_info
-                 * structure */
-                volume = pa_cvolume_reset(&cv, PA_CHANNELS_MAX);
-            }
-        }
-
         pa_tagstruct_put_cvolume(t, volume);
     } else
         pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
@@ -1335,6 +1332,15 @@ static int create_stream(
             pa_tagstruct_put_format_info(t, s->req_formats[i]);
     }
 
+    if (s->context->version >= 22 && s->direction == PA_STREAM_RECORD) {
+        pa_tagstruct_put_cvolume(t, volume);
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_START_MUTED);
+        pa_tagstruct_put_boolean(t, volume_set);
+        pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_MUTED|PA_STREAM_START_UNMUTED));
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
+        pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
+    }
+
     pa_pstream_send_tagstruct(s->context->pstream, t);
     pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_create_stream_callback, s, NULL);
 
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 17b0e15..1db19ce 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -632,7 +632,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
     }
 
     pa_cvolume_set(&cvolume, 1, volume);
-    pa_source_set_volume(source, &cvolume, TRUE);
+    pa_source_set_volume(source, &cvolume, TRUE, TRUE);
     return 0;
 }
 
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index f49abb0..5d1ba6a 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -169,6 +169,10 @@ enum {
     PA_COMMAND_SET_SINK_PORT,
     PA_COMMAND_SET_SOURCE_PORT,
 
+    /* Supported since protocol v22 (1.0) */
+    PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME,
+    PA_COMMAND_SET_SOURCE_OUTPUT_MUTE,
+
     PA_COMMAND_MAX
 };
 
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index 69f5d9e..9a9ef4e 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -184,7 +184,12 @@ static const char *command_names[PA_COMMAND_MAX] = {
 
     /* Supported since protocol v16 (0.9.16) */
     [PA_COMMAND_SET_SINK_PORT] = "SET_SINK_PORT",
-    [PA_COMMAND_SET_SOURCE_PORT] = "SET_SOURCE_PORT"
+    [PA_COMMAND_SET_SOURCE_PORT] = "SET_SOURCE_PORT",
+
+    /* Supported since protocol v22 (1.0) */
+    [PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME] = "SET_SOURCE_OUTPUT_VOLUME",
+    [PA_COMMAND_SET_SOURCE_OUTPUT_MUTE] = "SET_SOURCE_OUTPUT_MUTE",
+
 };
 
 #endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 3574ca9..d90b470 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -338,10 +338,12 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_SET_SINK_VOLUME] = command_set_volume,
     [PA_COMMAND_SET_SINK_INPUT_VOLUME] = command_set_volume,
     [PA_COMMAND_SET_SOURCE_VOLUME] = command_set_volume,
+    [PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME] = command_set_volume,
 
     [PA_COMMAND_SET_SINK_MUTE] = command_set_mute,
     [PA_COMMAND_SET_SINK_INPUT_MUTE] = command_set_mute,
     [PA_COMMAND_SET_SOURCE_MUTE] = command_set_mute,
+    [PA_COMMAND_SET_SOURCE_OUTPUT_MUTE] = command_set_mute,
 
     [PA_COMMAND_SUSPEND_SINK] = command_suspend,
     [PA_COMMAND_SUSPEND_SOURCE] = command_suspend,
@@ -631,10 +633,14 @@ static record_stream* record_stream_new(
         pa_channel_map *map,
         pa_idxset *formats,
         pa_buffer_attr *attr,
+        pa_cvolume *volume,
+        pa_bool_t muted,
+        pa_bool_t muted_set,
         pa_source_output_flags_t flags,
         pa_proplist *p,
         pa_bool_t adjust_latency,
         pa_bool_t early_requests,
+        pa_bool_t relative_volume,
         pa_bool_t peak_detect,
         pa_sink_input *direct_on_input,
         int *ret) {
@@ -663,6 +669,15 @@ static record_stream* record_stream_new(
     if (formats)
         pa_source_output_new_data_set_formats(&data, formats);
     data.direct_on_input = direct_on_input;
+    if (volume) {
+        pa_source_output_new_data_set_volume(&data, volume);
+        data.volume_is_absolute = !relative_volume;
+        data.save_volume = TRUE;
+    }
+    if (muted_set) {
+        pa_source_output_new_data_set_muted(&data, muted);
+        data.save_muted = TRUE;
+    }
     if (peak_detect)
         data.resample_method = PA_RESAMPLER_PEAKS;
     data.flags = flags;
@@ -2215,6 +2230,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
     pa_channel_map map;
     pa_tagstruct *reply;
     pa_source *source = NULL;
+    pa_cvolume volume;
     pa_bool_t
         corked = FALSE,
         no_remap = FALSE,
@@ -2224,11 +2240,15 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         fix_channels = FALSE,
         no_move = FALSE,
         variable_rate = FALSE,
+        muted = FALSE,
         adjust_latency = FALSE,
         peak_detect = FALSE,
         early_requests = FALSE,
         dont_inhibit_auto_suspend = FALSE,
+        volume_set = TRUE,
+        muted_set = FALSE,
         fail_on_suspend = FALSE,
+        relative_volume = FALSE,
         passthrough = FALSE;
 
     pa_source_output_flags_t flags = 0;
@@ -2333,10 +2353,24 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
             }
             pa_idxset_put(formats, format, NULL);
         }
+
+        if (pa_tagstruct_get_cvolume(t, &volume) < 0 ||
+            pa_tagstruct_get_boolean(t, &muted) < 0 ||
+            pa_tagstruct_get_boolean(t, &volume_set) < 0 ||
+            pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
+            pa_tagstruct_get_boolean(t, &relative_volume) < 0 ||
+            pa_tagstruct_get_boolean(t, &passthrough) < 0) {
+
+            protocol_error(c);
+            goto finish;
+        }
+
+        CHECK_VALIDITY_GOTO(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID, finish);
     }
 
     if (n_formats == 0) {
         CHECK_VALIDITY_GOTO(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID, finish);
+        CHECK_VALIDITY_GOTO(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID, finish);
         CHECK_VALIDITY_GOTO(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID, finish);
     } else {
         PA_IDXSET_FOREACH(format, formats, i) {
@@ -2386,7 +2420,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0) |
         (passthrough ? PA_SOURCE_OUTPUT_PASSTHROUGH : 0);
 
-    s = record_stream_new(c, source, &ss, &map, formats, &attr, flags, p, adjust_latency, early_requests, peak_detect, direct_on_input, &ret);
+    s = record_stream_new(c, source, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, flags, p, adjust_latency, early_requests, relative_volume, peak_detect, direct_on_input, &ret);
 
     CHECK_VALIDITY_GOTO(c->pstream, s, tag, ret, finish);
 
@@ -3249,12 +3283,20 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t,
 static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source_output *s) {
     pa_sample_spec fixed_ss;
     pa_usec_t source_latency;
+    pa_cvolume v;
+    pa_bool_t has_volume = FALSE;
 
     pa_assert(t);
     pa_source_output_assert_ref(s);
 
     fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
 
+    has_volume = pa_source_output_is_volume_readable(s);
+    if (has_volume)
+        pa_source_output_get_volume(s, &v, TRUE);
+    else
+        pa_cvolume_reset(&v, fixed_ss.channels);
+
     pa_tagstruct_putu32(t, s->index);
     pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
     pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
@@ -3270,6 +3312,12 @@ static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *
         pa_tagstruct_put_proplist(t, s->proplist);
     if (c->version >= 19)
         pa_tagstruct_put_boolean(t, (pa_source_output_get_state(s) == PA_SOURCE_OUTPUT_CORKED));
+    if (c->version >= 22) {
+        pa_tagstruct_put_cvolume(t, &v);
+        pa_tagstruct_put_boolean(t, pa_source_output_get_mute(s));
+        pa_tagstruct_put_boolean(t, has_volume);
+        pa_tagstruct_put_boolean(t, s->volume_writable);
+    }
 }
 
 static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_scache_entry *e) {
@@ -3564,6 +3612,7 @@ static void command_set_volume(
     pa_sink *sink = NULL;
     pa_source *source = NULL;
     pa_sink_input *si = NULL;
+    pa_source_output *so = NULL;
     const char *name = NULL;
     const char *client_name;
 
@@ -3606,11 +3655,15 @@ static void command_set_volume(
             si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
             break;
 
+        case PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME:
+            so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
+            break;
+
         default:
             pa_assert_not_reached();
     }
 
-    CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, si || so || sink || source, tag, PA_ERR_NOENTITY);
 
     client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
 
@@ -3623,7 +3676,7 @@ static void command_set_volume(
         CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &source->sample_spec), tag, PA_ERR_INVALID);
 
         pa_log_debug("Client %s changes volume of source %s.", client_name, source->name);
-        pa_source_set_volume(source, &volume, TRUE);
+        pa_source_set_volume(source, &volume, TRUE, TRUE);
     } else if (si) {
         CHECK_VALIDITY(c->pstream, si->volume_writable, tag, PA_ERR_BADSTATE);
         CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &si->sample_spec), tag, PA_ERR_INVALID);
@@ -3632,6 +3685,13 @@ static void command_set_volume(
                      client_name,
                      pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
         pa_sink_input_set_volume(si, &volume, TRUE, TRUE);
+    } else if (so) {
+        CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &so->sample_spec), tag, PA_ERR_INVALID);
+
+        pa_log_debug("Client %s changes volume of source output %s.",
+                     client_name,
+                     pa_strnull(pa_proplist_gets(so->proplist, PA_PROP_MEDIA_NAME)));
+        pa_source_output_set_volume(so, &volume, TRUE, TRUE);
     }
 
     pa_pstream_send_simple_ack(c->pstream, tag);
@@ -3650,6 +3710,7 @@ static void command_set_mute(
     pa_sink *sink = NULL;
     pa_source *source = NULL;
     pa_sink_input *si = NULL;
+    pa_source_output *so = NULL;
     const char *name = NULL, *client_name;
 
     pa_native_connection_assert_ref(c);
@@ -3692,11 +3753,15 @@ static void command_set_mute(
             si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
             break;
 
+        case PA_COMMAND_SET_SOURCE_OUTPUT_MUTE:
+            so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
+            break;
+
         default:
             pa_assert_not_reached();
     }
 
-    CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, si || so || sink || source, tag, PA_ERR_NOENTITY);
 
     client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
 
@@ -3711,6 +3776,11 @@ static void command_set_mute(
                      client_name,
                      pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
         pa_sink_input_set_mute(si, mute, TRUE);
+    } else if (so) {
+        pa_log_debug("Client %s changes mute of source output %s.",
+                     client_name,
+                     pa_strnull(pa_proplist_gets(so->proplist, PA_PROP_MEDIA_NAME)));
+        pa_source_output_set_mute(so, mute, TRUE);
     }
 
     pa_pstream_send_simple_ack(c->pstream, tag);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index ed38013..45761a6 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -24,9 +24,9 @@
 #include <config.h>
 #endif
 
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <stdio.h>
 
 #include <pulse/introspect.h>
 #include <pulse/utf8.h>
@@ -2616,6 +2616,7 @@ pa_usec_t pa_sink_get_requested_latency(pa_sink *s) {
         return 0;
 
     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0);
+
     return usec;
 }
 
@@ -2873,8 +2874,8 @@ void pa_sink_set_fixed_latency_within_thread(pa_sink *s, pa_usec_t latency) {
 /* Called from main context */
 size_t pa_sink_get_max_rewind(pa_sink *s) {
     size_t r;
-    pa_sink_assert_ref(s);
     pa_assert_ctl_context();
+    pa_sink_assert_ref(s);
 
     if (!PA_SINK_IS_LINKED(s->state))
         return s->thread_info.max_rewind;
@@ -2902,6 +2903,7 @@ size_t pa_sink_get_max_request(pa_sink *s) {
 int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) {
     pa_device_port *port;
     int ret;
+
     pa_sink_assert_ref(s);
     pa_assert_ctl_context();
 
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 61e0695..82c6f54 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -45,6 +45,7 @@
 PA_DEFINE_PUBLIC_CLASS(pa_source_output, pa_msgobject);
 
 static void source_output_free(pa_object* mo);
+static void set_real_ratio(pa_source_output *o, const pa_cvolume *v);
 
 pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data) {
     pa_assert(data);
@@ -52,6 +53,7 @@ pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_d
     pa_zero(*data);
     data->resample_method = PA_RESAMPLER_INVALID;
     data->proplist = pa_proplist_new();
+    data->volume_writable = TRUE;
 
     return data;
 }
@@ -82,6 +84,45 @@ pa_bool_t pa_source_output_new_data_is_passthrough(pa_source_output_new_data *da
     return FALSE;
 }
 
+void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume) {
+    pa_assert(data);
+    pa_assert(data->volume_writable);
+
+    if ((data->volume_is_set = !!volume))
+        data->volume = *volume;
+}
+
+void pa_source_output_new_data_apply_volume_factor(pa_source_output_new_data *data, const pa_cvolume *volume_factor) {
+    pa_assert(data);
+    pa_assert(volume_factor);
+
+    if (data->volume_factor_is_set)
+        pa_sw_cvolume_multiply(&data->volume_factor, &data->volume_factor, volume_factor);
+    else {
+        data->volume_factor_is_set = TRUE;
+        data->volume_factor = *volume_factor;
+    }
+}
+
+void pa_source_output_new_data_apply_volume_factor_source(pa_source_output_new_data *data, const pa_cvolume *volume_factor) {
+    pa_assert(data);
+    pa_assert(volume_factor);
+
+    if (data->volume_factor_source_is_set)
+        pa_sw_cvolume_multiply(&data->volume_factor_source, &data->volume_factor_source, volume_factor);
+    else {
+        data->volume_factor_source_is_set = TRUE;
+        data->volume_factor_source = *volume_factor;
+    }
+}
+
+void pa_source_output_new_data_set_muted(pa_source_output_new_data *data, pa_bool_t mute) {
+    pa_assert(data);
+
+    data->muted_is_set = TRUE;
+    data->muted = !!mute;
+}
+
 pa_bool_t pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_source *s, pa_bool_t save) {
     pa_bool_t ret = TRUE;
     pa_idxset *formats = NULL;
@@ -167,6 +208,8 @@ static void reset_callbacks(pa_source_output *o) {
     o->state_change = NULL;
     o->may_move_to = NULL;
     o->send_event = NULL;
+    o->volume_changed = NULL;
+    o->mute_changed = NULL;
 }
 
 /* Called from main context */
@@ -192,6 +235,9 @@ int pa_source_output_new(
     if (data->client)
         pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
 
+    if (data->destination_source && (data->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
+        data->volume_writable = FALSE;
+
     if (!data->req_formats) {
         /* From this point on, we want to work only with formats, and get back
          * to using the sample spec and channel map after all decisions w.r.t.
@@ -250,6 +296,33 @@ int pa_source_output_new(
 
     pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
 
+    /* Don't restore (or save) stream volume for passthrough streams */
+    if (!pa_format_info_is_pcm(data->format)) {
+        data->volume_is_set = FALSE;
+        data->volume_factor_is_set = FALSE;
+    }
+
+    if (!data->volume_is_set) {
+        pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+        data->volume_is_absolute = FALSE;
+        data->save_volume = FALSE;
+    }
+
+    pa_return_val_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec), -PA_ERR_INVALID);
+
+    if (!data->volume_factor_is_set)
+        pa_cvolume_reset(&data->volume_factor, data->sample_spec.channels);
+
+    pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor, &data->sample_spec), -PA_ERR_INVALID);
+
+    if (!data->volume_factor_source_is_set)
+        pa_cvolume_reset(&data->volume_factor_source, data->source->sample_spec.channels);
+
+    pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor_source, &data->source->sample_spec), -PA_ERR_INVALID);
+
+    if (!data->muted_is_set)
+        data->muted = FALSE;
+
     if (data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
         data->sample_spec.format = data->source->sample_spec.format;
 
@@ -266,6 +339,9 @@ int pa_source_output_new(
     pa_assert(pa_sample_spec_valid(&data->sample_spec));
     pa_assert(pa_channel_map_valid(&data->channel_map));
 
+    /* Due to the fixing of the sample spec the volume might not match anymore */
+    pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map);
+
     if (data->resample_method == PA_RESAMPLER_INVALID)
         data->resample_method = core->resample_method;
 
@@ -324,6 +400,39 @@ int pa_source_output_new(
     o->channel_map = data->channel_map;
     o->format = pa_format_info_copy(data->format);
 
+    if (!data->volume_is_absolute && pa_source_flat_volume_enabled(o->source)) {
+        pa_cvolume remapped;
+
+        /* When the 'absolute' bool is not set then we'll treat the volume
+         * as relative to the source volume even in flat volume mode */
+        remapped = data->source->reference_volume;
+        pa_cvolume_remap(&remapped, &data->source->channel_map, &data->channel_map);
+        pa_sw_cvolume_multiply(&o->volume, &data->volume, &remapped);
+    } else
+        o->volume = data->volume;
+
+    o->volume_factor = data->volume_factor;
+    o->volume_factor_source = data->volume_factor_source;
+    o->real_ratio = o->reference_ratio = data->volume;
+    pa_cvolume_reset(&o->soft_volume, o->sample_spec.channels);
+    pa_cvolume_reset(&o->real_ratio, o->sample_spec.channels);
+    o->volume_writable = data->volume_writable;
+    o->save_volume = data->save_volume;
+    o->save_source = data->save_source;
+    o->save_muted = data->save_muted;
+
+    o->muted = data->muted;
+
+    if (data->sync_base) {
+        o->sync_next = data->sync_base->sync_next;
+        o->sync_prev = data->sync_base;
+
+        if (data->sync_base->sync_next)
+            data->sync_base->sync_next->sync_prev = o;
+        data->sync_base->sync_next = o;
+    } else
+        o->sync_next = o->sync_prev = NULL;
+
     o->direct_on_input = data->direct_on_input;
 
     reset_callbacks(o);
@@ -333,6 +442,8 @@ int pa_source_output_new(
     o->thread_info.attached = FALSE;
     o->thread_info.sample_spec = o->sample_spec;
     o->thread_info.resampler = resampler;
+    o->thread_info.soft_volume = o->soft_volume;
+    o->thread_info.muted = o->muted;
     o->thread_info.requested_source_latency = (pa_usec_t) -1;
     o->thread_info.direct_on_input = o->direct_on_input;
 
@@ -387,6 +498,7 @@ static void update_n_corked(pa_source_output *o, pa_source_output_state_t state)
 
 /* Called from main context */
 static void source_output_set_state(pa_source_output *o, pa_source_output_state_t state) {
+    pa_source_output *ssync;
     pa_assert(o);
     pa_assert_ctl_context();
 
@@ -398,9 +510,24 @@ static void source_output_set_state(pa_source_output *o, pa_source_output_state_
     update_n_corked(o, state);
     o->state = state;
 
+    for (ssync = o->sync_prev; ssync; ssync = ssync->sync_prev) {
+        update_n_corked(ssync, state);
+        ssync->state = state;
+    }
+    for (ssync = o->sync_next; ssync; ssync = ssync->sync_next) {
+        update_n_corked(ssync, state);
+        ssync->state = state;
+    }
+
     if (state != PA_SOURCE_OUTPUT_UNLINKED) {
         pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o);
 
+        for (ssync = o->sync_prev; ssync; ssync = ssync->sync_prev)
+            pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], ssync);
+
+        for (ssync = o->sync_next; ssync; ssync = ssync->sync_next)
+            pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], ssync);
+
         if (PA_SOURCE_OUTPUT_IS_LINKED(state))
             pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
     }
@@ -424,6 +551,13 @@ void pa_source_output_unlink(pa_source_output*o) {
     if (linked)
         pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o);
 
+    if (o->sync_prev)
+        o->sync_prev->sync_next = o->sync_next;
+    if (o->sync_next)
+        o->sync_next->sync_prev = o->sync_prev;
+
+    o->sync_prev = o->sync_next = NULL;
+
     if (o->direct_on_input)
         pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL);
 
@@ -439,9 +573,14 @@ void pa_source_output_unlink(pa_source_output*o) {
     update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED);
     o->state = PA_SOURCE_OUTPUT_UNLINKED;
 
-    if (linked && o->source)
+    if (linked && o->source) {
+        /* We might need to update the source's volume if we are in flat volume mode. */
+        if (pa_source_flat_volume_enabled(o->source))
+            pa_source_set_volume(o->source, NULL, FALSE, FALSE);
+
         if (o->source->asyncmsgq)
             pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
+    }
 
     reset_callbacks(o);
 
@@ -507,6 +646,21 @@ void pa_source_output_put(pa_source_output *o) {
     update_n_corked(o, state);
     o->state = state;
 
+    /* We might need to update the source's volume if we are in flat volume mode. */
+    if (pa_source_flat_volume_enabled(o->source))
+        pa_source_set_volume(o->source, NULL, FALSE, o->save_volume);
+    else {
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+            pa_assert(pa_cvolume_is_norm(&o->volume));
+            pa_assert(pa_cvolume_is_norm(&o->reference_ratio));
+        }
+
+        set_real_ratio(o, &o->volume);
+    }
+
+    o->thread_info.soft_volume = o->soft_volume;
+    o->thread_info.muted = o->muted;
+
     pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
 
     pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index);
@@ -545,6 +699,8 @@ pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_la
 
 /* Called from thread context */
 void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
+    pa_bool_t need_volume_factor_source;
+    pa_bool_t volume_is_norm;
     size_t length;
     size_t limit, mbs = 0;
 
@@ -566,6 +722,9 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
 
     limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind;
 
+    volume_is_norm = pa_cvolume_is_norm(&o->thread_info.soft_volume) && !o->thread_info.muted;
+    need_volume_factor_source = !pa_cvolume_is_norm(&o->volume_factor_source);
+
     if (limit > 0 && o->source->monitor_of) {
         pa_usec_t latency;
         size_t n;
@@ -588,6 +747,7 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
     /* Implement the delay queue */
     while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) {
         pa_memchunk qchunk;
+        pa_bool_t nvfs = need_volume_factor_source;
 
         length -= limit;
 
@@ -598,9 +758,36 @@ void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
 
         pa_assert(qchunk.length > 0);
 
-        if (!o->thread_info.resampler)
+        /* It might be necessary to adjust the volume here */
+        if (!volume_is_norm) {
+            pa_memchunk_make_writable(&qchunk, 0);
+
+            if (o->thread_info.muted) {
+                pa_silence_memchunk(&qchunk, &o->thread_info.sample_spec);
+                nvfs = FALSE;
+
+            } else if (!o->thread_info.resampler && nvfs) {
+                pa_cvolume v;
+
+                /* If we don't need a resampler we can merge the
+                 * post and the pre volume adjustment into one */
+
+                pa_sw_cvolume_multiply(&v, &o->thread_info.soft_volume, &o->volume_factor_source);
+                pa_volume_memchunk(&qchunk, &o->thread_info.sample_spec, &v);
+                nvfs = FALSE;
+
+            } else
+                pa_volume_memchunk(&qchunk, &o->thread_info.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->source->sample_spec, &o->volume_factor_source);
+            }
+
             o->push(o, &qchunk);
-        else {
+        } else {
             pa_memchunk rchunk;
 
             if (mbs == 0)
@@ -611,8 +798,14 @@ 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 (rchunk.length > 0) {
+                if (nvfs) {
+                    pa_memchunk_make_writable(&rchunk, 0);
+                    pa_volume_memchunk(&rchunk, &o->source->sample_spec, &o->volume_factor_source);
+                }
+
                 o->push(o, &rchunk);
+            }
 
             if (rchunk.memblock)
                 pa_memblock_unref(rchunk.memblock);
@@ -786,6 +979,85 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) {
     }
 }
 
+/* Called from main context */
+void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
+    pa_cvolume v;
+
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_assert(volume);
+    pa_assert(pa_cvolume_valid(volume));
+    pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &o->sample_spec));
+    pa_assert(o->volume_writable);
+
+    if (!absolute && pa_source_flat_volume_enabled(o->source)) {
+        v = o->source->reference_volume;
+        pa_cvolume_remap(&v, &o->source->channel_map, &o->channel_map);
+
+        if (pa_cvolume_compatible(volume, &o->sample_spec))
+            volume = pa_sw_cvolume_multiply(&v, &v, volume);
+        else
+            volume = pa_sw_cvolume_multiply_scalar(&v, &v, pa_cvolume_max(volume));
+    } else {
+        if (!pa_cvolume_compatible(volume, &o->sample_spec)) {
+            v = o->volume;
+            volume = pa_cvolume_scale(&v, pa_cvolume_max(volume));
+        }
+    }
+
+    if (pa_cvolume_equal(volume, &o->volume)) {
+        o->save_volume = o->save_volume || save;
+        return;
+    }
+
+    o->volume = *volume;
+    o->save_volume = save;
+
+    if (pa_source_flat_volume_enabled(o->source)) {
+        /* We are in flat volume mode, so let's update all source input
+         * volumes and update the flat volume of the source */
+
+        pa_source_set_volume(o->source, NULL, TRUE, save);
+
+    } else {
+        /* OK, we are in normal volume mode. The volume only affects
+         * ourselves */
+        set_real_ratio(o, volume);
+
+        /* Copy the new soft_volume to the thread_info struct */
+        pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
+    }
+
+    /* The volume changed, let's tell people so */
+    if (o->volume_changed)
+        o->volume_changed(o);
+
+    /* The virtual volume changed, let's tell people so */
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+}
+
+/* Called from main context */
+static void set_real_ratio(pa_source_output *o, const pa_cvolume *v) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_assert(!v || pa_cvolume_compatible(v, &o->sample_spec));
+
+    /* This basically calculates:
+     *
+     * o->real_ratio := v
+     * o->soft_volume := o->real_ratio * o->volume_factor */
+
+    if (v)
+        o->real_ratio = *v;
+    else
+        pa_cvolume_reset(&o->real_ratio, o->sample_spec.channels);
+
+    pa_sw_cvolume_multiply(&o->soft_volume, &o->real_ratio, &o->volume_factor);
+    /* We don't copy the data to the thread_info data. That's left for someone else to do */
+}
+
 /* Called from main or I/O context */
 pa_bool_t pa_source_output_is_passthrough(pa_source_output *o) {
     pa_source_output_assert_ref(o);
@@ -799,6 +1071,61 @@ pa_bool_t pa_source_output_is_passthrough(pa_source_output *o) {
     return FALSE;
 }
 
+/* Called from main context */
+pa_bool_t pa_source_output_is_volume_readable(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+
+    return !pa_source_output_is_passthrough(o);
+}
+
+/* Called from main context */
+pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume, pa_bool_t absolute) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+    pa_assert(pa_source_output_is_volume_readable(o));
+
+    if (absolute || !pa_source_flat_volume_enabled(o->source))
+        *volume = o->volume;
+    else
+        *volume = o->reference_ratio;
+
+    return volume;
+}
+
+/* Called from main context */
+void pa_source_output_set_mute(pa_source_output *o, pa_bool_t mute, pa_bool_t save) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    if (!o->muted == !mute) {
+        o->save_muted = o->save_muted || mute;
+        return;
+    }
+
+    o->muted = mute;
+    o->save_muted = save;
+
+    pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
+
+    /* The mute status changed, let's tell people so */
+    if (o->mute_changed)
+        o->mute_changed(o);
+
+    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+}
+
+/* Called from main context */
+pa_bool_t pa_source_output_get_mute(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
+
+    return o->muted;
+}
+
 /* Called from main thread */
 void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
     pa_source_output_assert_ref(o);
@@ -833,6 +1160,11 @@ pa_bool_t pa_source_output_may_move(pa_source_output *o) {
     if (o->direct_on_input)
         return FALSE;
 
+    if (o->sync_next || o->sync_prev) {
+        pa_log_warn("Moving synchronized streams not supported.");
+        return FALSE;
+    }
+
     return TRUE;
 }
 
@@ -883,6 +1215,11 @@ int pa_source_output_start_move(pa_source_output *o) {
     if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
         pa_assert_se(origin->n_corked-- >= 1);
 
+    if (pa_source_flat_volume_enabled(o->source))
+        /* We might need to update the source's volume if we are in flat
+         * volume mode. */
+        pa_source_set_volume(o->source, NULL, FALSE, FALSE);
+
     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);
@@ -893,6 +1230,156 @@ int pa_source_output_start_move(pa_source_output *o) {
     return 0;
 }
 
+/* Called from main context. If it has an origin source that uses volume sharing,
+ * then also the origin source and all streams connected to it need to update
+ * their volume - this function does all that by using recursion. */
+static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
+    pa_cvolume old_volume;
+
+    pa_assert(o);
+    pa_assert(dest);
+    pa_assert(o->source); /* The destination source should already be set. */
+
+    if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+        pa_source *root_source = o->source;
+        pa_source_output *destination_source_output;
+        uint32_t idx;
+
+        while (root_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)
+            root_source = root_source->output_from_master->source;
+
+        if (pa_source_flat_volume_enabled(o->source)) {
+            /* Ok, so the origin source uses volume sharing, and flat volume is
+             * enabled. The volume will have to be updated as follows:
+             *
+             *     o->volume := o->source->real_volume
+             *         (handled later by pa_source_set_volume)
+             *     o->reference_ratio := o->volume / o->source->reference_volume
+             *         (handled later by pa_source_set_volume)
+             *     o->real_ratio stays unchanged
+             *         (streams whose origin source uses volume sharing should
+             *          always have real_ratio of 0 dB)
+             *     o->soft_volume stays unchanged
+             *         (streams whose origin source uses volume sharing should
+             *          always have volume_factor as soft_volume, so no change
+             *          should be needed) */
+
+            pa_assert(pa_cvolume_is_norm(&o->real_ratio));
+            pa_assert(pa_cvolume_equal(&o->soft_volume, &o->volume_factor));
+
+            /* Notifications will be sent by pa_source_set_volume(). */
+
+        } else {
+            /* Ok, so the origin source uses volume sharing, and flat volume is
+             * disabled. The volume will have to be updated as follows:
+             *
+             *     o->volume := 0 dB
+             *     o->reference_ratio := 0 dB
+             *     o->real_ratio stays unchanged
+             *         (streams whose origin source uses volume sharing should
+             *          always have real_ratio of 0 dB)
+             *     o->soft_volume stays unchanged
+             *         (streams whose origin source uses volume sharing should
+             *          always have volume_factor as soft_volume, so no change
+             *          should be needed) */
+
+            old_volume = o->volume;
+            pa_cvolume_reset(&o->volume, o->volume.channels);
+            pa_cvolume_reset(&o->reference_ratio, o->reference_ratio.channels);
+            pa_assert(pa_cvolume_is_norm(&o->real_ratio));
+            pa_assert(pa_cvolume_equal(&o->soft_volume, &o->volume_factor));
+
+            /* Notify others about the changed source output volume. */
+            if (!pa_cvolume_equal(&o->volume, &old_volume)) {
+                if (o->volume_changed)
+                    o->volume_changed(o);
+
+                pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+            }
+        }
+
+        /* Additionally, the origin source volume needs updating:
+         *
+         *     o->destination_source->reference_volume := root_source->reference_volume
+         *     o->destination_source->real_volume := root_source->real_volume
+         *     o->destination_source->soft_volume stays unchanged
+         *         (sources that use volume sharing should always have
+         *          soft_volume of 0 dB) */
+
+        old_volume = o->destination_source->reference_volume;
+
+        o->destination_source->reference_volume = root_source->reference_volume;
+        pa_cvolume_remap(&o->destination_source->reference_volume, &root_source->channel_map, &o->destination_source->channel_map);
+
+        o->destination_source->real_volume = root_source->real_volume;
+        pa_cvolume_remap(&o->destination_source->real_volume, &root_source->channel_map, &o->destination_source->channel_map);
+
+        pa_assert(pa_cvolume_is_norm(&o->destination_source->soft_volume));
+
+        /* Notify others about the changed source volume. If you wonder whether
+         * o->destination_source->set_volume() should be called somewhere, that's not
+         * the case, because sources that use volume sharing shouldn't have any
+         * internal volume that set_volume() would update. If you wonder
+         * whether the thread_info variables should be synced, yes, they
+         * should, and it's done by the PA_SOURCE_MESSAGE_FINISH_MOVE message
+         * handler. */
+        if (!pa_cvolume_equal(&o->destination_source->reference_volume, &old_volume))
+            pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, o->destination_source->index);
+
+        /* Recursively update origin source outputs. */
+        PA_IDXSET_FOREACH(destination_source_output, o->destination_source->outputs, idx)
+            update_volume_due_to_moving(destination_source_output, dest);
+
+    } else {
+        old_volume = o->volume;
+
+        if (pa_source_flat_volume_enabled(o->source)) {
+            /* Ok, so this is a regular stream, and flat volume is enabled. The
+             * volume will have to be updated as follows:
+             *
+             *     o->volume := o->reference_ratio * o->source->reference_volume
+             *     o->reference_ratio stays unchanged
+             *     o->real_ratio := o->volume / o->source->real_volume
+             *         (handled later by pa_source_set_volume)
+             *     o->soft_volume := o->real_ratio * o->volume_factor
+             *         (handled later by pa_source_set_volume) */
+
+            o->volume = o->source->reference_volume;
+            pa_cvolume_remap(&o->volume, &o->source->channel_map, &o->channel_map);
+            pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
+
+        } else {
+            /* Ok, so this is a regular stream, and flat volume is disabled.
+             * The volume will have to be updated as follows:
+             *
+             *     o->volume := o->reference_ratio
+             *     o->reference_ratio stays unchanged
+             *     o->real_ratio := o->reference_ratio
+             *     o->soft_volume := o->real_ratio * o->volume_factor */
+
+            o->volume = o->reference_ratio;
+            o->real_ratio = o->reference_ratio;
+            pa_sw_cvolume_multiply(&o->soft_volume, &o->real_ratio, &o->volume_factor);
+        }
+
+        /* Notify others about the changed source output volume. */
+        if (!pa_cvolume_equal(&o->volume, &old_volume)) {
+            /* XXX: In case o->source has flat volume enabled, then real_ratio
+             * and soft_volume are not updated yet. Let's hope that the
+             * callback implementation doesn't care about those variables... */
+            if (o->volume_changed)
+                o->volume_changed(o);
+
+            pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+        }
+    }
+
+    /* If o->source == dest, then recursion has finished, and we can finally call
+     * pa_source_set_volume(), which will do the rest of the updates. */
+    if ((o->source == dest) && pa_source_flat_volume_enabled(o->source))
+        pa_source_set_volume(o->source, NULL, FALSE, o->save_volume);
+}
+
 /* Called from main context */
 int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save) {
     pa_resampler *new_resampler;
@@ -951,11 +1438,14 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
     o->save_source = save;
     pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL);
 
+    pa_cvolume_remap(&o->volume_factor_source, &o->channel_map, &o->source->channel_map);
+
     if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
         o->source->n_corked++;
 
     /* Replace resampler */
     if (new_resampler != o->thread_info.resampler) {
+
         if (o->thread_info.resampler)
             pa_resampler_free(o->thread_info.resampler);
         o->thread_info.resampler = new_resampler;
@@ -976,6 +1466,8 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
 
     pa_source_update_status(dest);
 
+    update_volume_due_to_moving(o, dest);
+
     pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0);
 
     pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name);
@@ -1075,10 +1567,19 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
             pa_resampler_set_output_rate(o->thread_info.resampler, PA_PTR_TO_UINT(userdata));
             return 0;
 
-        case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE:
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_STATE: {
+            pa_source_output *ssync;
 
             pa_source_output_set_state_within_thread(o, PA_PTR_TO_UINT(userdata));
+
+            for (ssync = o->thread_info.sync_prev; ssync; ssync = ssync->thread_info.sync_prev)
+                pa_source_output_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
+
+            for (ssync = o->thread_info.sync_next; ssync; ssync = ssync->thread_info.sync_next)
+                pa_source_output_set_state_within_thread(ssync, PA_PTR_TO_UINT(userdata));
+
             return 0;
+        }
 
         case PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY: {
             pa_usec_t *usec = userdata;
@@ -1094,6 +1595,18 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
             *r = o->thread_info.requested_source_latency;
             return 0;
         }
+
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_VOLUME:
+            if (!pa_cvolume_equal(&o->thread_info.soft_volume, &o->soft_volume)) {
+                o->thread_info.soft_volume = o->soft_volume;
+            }
+            return 0;
+
+        case PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_MUTE:
+            if (o->thread_info.muted != o->muted) {
+                o->thread_info.muted = o->muted;
+            }
+            return 0;
     }
 
     return -PA_ERR_NOTIMPLEMENTED;
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 88de4ae..4bcddba 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -86,10 +86,26 @@ struct pa_source_output {
     pa_channel_map channel_map;
     pa_format_info *format;
 
-    /* if TRUE then the source we are connected to is worth
-     * remembering, i.e. was explicitly chosen by the user and not
-     * automatically. module-stream-restore looks for this.*/
-    pa_bool_t save_source:1;
+    pa_source_output *sync_prev, *sync_next;
+
+    /* Also see http://pulseaudio.org/wiki/InternalVolumes */
+    pa_cvolume volume;             /* The volume clients are informed about */
+    pa_cvolume reference_ratio;    /* The ratio of the stream's volume to the source's reference volume */
+    pa_cvolume real_ratio;         /* The ratio of the stream's volume to the source's real volume */
+    pa_cvolume volume_factor;      /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */
+    pa_cvolume soft_volume;        /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as real_ratio * volume_factor */
+
+    pa_cvolume volume_factor_source; /* A second volume factor in format of the source this stream is connected to */
+
+    pa_bool_t volume_writable:1;
+
+    pa_bool_t muted:1;
+
+    /* if TRUE then the source we are connected to and/or the volume
+     * set is worth remembering, i.e. was explicitly chosen by the
+     * user and not automatically. module-stream-restore looks for
+     * this.*/
+    pa_bool_t save_source:1, save_volume:1, save_muted:1;
 
     pa_resample_method_t requested_resample_method, actual_resample_method;
 
@@ -118,7 +134,10 @@ struct pa_source_output {
     void (*update_source_fixed_latency) (pa_source_output *i); /* may be NULL */
 
     /* If non-NULL this function is called when the output is first
-     * connected to a source. Called from IO thread context */
+     * connected to a source or when the rtpoll/asyncmsgq fields
+     * change. You usually don't need to implement this function
+     * unless you rewrite a source that is piggy-backed onto
+     * another. Called from IO thread context */
     void (*attach) (pa_source_output *o);           /* may be NULL */
 
     /* If non-NULL this function is called when the output is
@@ -134,9 +153,9 @@ struct pa_source_output {
     void (*suspend_within_thread) (pa_source_output *o, pa_bool_t b);   /* may be NULL */
 
     /* If non-NULL called whenever the source output is moved to a new
-     * source. Called from main context after the stream was detached
-     * from the old source and before it is attached to the new
-     * source. If dest is NULL the move was executed in two
+     * source. Called from main context after the source output has been
+     * detached from the old source and before it has been attached to
+     * the new source. If dest is NULL the move was executed in two
      * phases and the second one failed; the stream will be destroyed
      * after this call. */
     void (*moving) (pa_source_output *o, pa_source *dest);   /* may be NULL */
@@ -164,9 +183,20 @@ struct pa_source_output {
      * control events. */
     void (*send_event)(pa_source_output *o, const char *event, pa_proplist* data);
 
+    /* If non-NULL this function is called whenever the source output
+     * volume changes. Called from main context */
+    void (*volume_changed)(pa_source_output *o); /* may be NULL */
+
+    /* If non-NULL this function is called whenever the source output
+     * mute status changes. Called from main context */
+    void (*mute_changed)(pa_source_output *o); /* may be NULL */
+
     struct {
         pa_source_output_state_t state;
 
+        pa_cvolume soft_volume;
+        pa_bool_t muted:1;
+
         pa_bool_t attached:1; /* True only between ->attach() and ->detach() calls */
 
         pa_sample_spec sample_spec;
@@ -177,6 +207,8 @@ struct pa_source_output {
          * don't implement rewind() */
         pa_memblockq *delay_memblockq;
 
+        pa_source_output *sync_prev, *sync_next;
+
         /* The requested latency for the source */
         pa_usec_t requested_source_latency;
 
@@ -195,6 +227,8 @@ enum {
     PA_SOURCE_OUTPUT_MESSAGE_SET_STATE,
     PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY,
     PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY,
+    PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_VOLUME,
+    PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_MUTE,
     PA_SOURCE_OUTPUT_MESSAGE_MAX
 };
 
@@ -219,22 +253,38 @@ typedef struct pa_source_output_new_data {
 
     pa_resample_method_t resample_method;
 
+    pa_source_output *sync_base;
+
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
     pa_format_info *format;
     pa_idxset *req_formats;
     pa_idxset *nego_formats;
 
+    pa_cvolume volume, volume_factor, volume_factor_source;
+    pa_bool_t muted:1;
+
     pa_bool_t sample_spec_is_set:1;
     pa_bool_t channel_map_is_set:1;
 
-    pa_bool_t save_source:1;
+    pa_bool_t volume_is_set:1, volume_factor_is_set:1, volume_factor_source_is_set:1;
+    pa_bool_t muted_is_set:1;
+
+    pa_bool_t volume_is_absolute:1;
+
+    pa_bool_t volume_writable:1;
+
+    pa_bool_t save_source:1, save_volume:1, save_muted:1;
 } pa_source_output_new_data;
 
 pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
 void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec);
 void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
 pa_bool_t pa_source_output_new_data_is_passthrough(pa_source_output_new_data *data);
+void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume);
+void pa_source_output_new_data_apply_volume_factor(pa_source_output_new_data *data, const pa_cvolume *volume_factor);
+void pa_source_output_new_data_apply_volume_factor_source(pa_source_output_new_data *data, const pa_cvolume *volume_factor);
+void pa_source_output_new_data_set_muted(pa_source_output_new_data *data, pa_bool_t mute);
 pa_bool_t pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_source *s, pa_bool_t save);
 pa_bool_t pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_idxset *formats);
 void pa_source_output_new_data_done(pa_source_output_new_data *data);
@@ -266,7 +316,13 @@ void pa_source_output_kill(pa_source_output*o);
 
 pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency);
 
+pa_bool_t pa_source_output_is_volume_readable(pa_source_output *o);
 pa_bool_t pa_source_output_is_passthrough(pa_source_output *o);
+void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute);
+pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume, pa_bool_t absolute);
+
+void pa_source_output_set_mute(pa_source_output *o, pa_bool_t mute, pa_bool_t save);
+pa_bool_t pa_source_output_get_mute(pa_source_output *o);
 
 void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p);
 
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 587291a..9c15d3f 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -32,6 +32,7 @@
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
 #include <pulse/util.h>
+#include <pulse/rtclock.h>
 #include <pulse/internal.h>
 
 #include <pulsecore/core-util.h>
@@ -40,6 +41,7 @@
 #include <pulsecore/core-subscribe.h>
 #include <pulsecore/log.h>
 #include <pulsecore/sample-util.h>
+#include <pulsecore/flist.h>
 
 #include "source.h"
 
@@ -49,8 +51,23 @@
 
 PA_DEFINE_PUBLIC_CLASS(pa_source, pa_msgobject);
 
+struct pa_source_volume_change {
+    pa_usec_t at;
+    pa_cvolume hw_volume;
+
+    PA_LLIST_FIELDS(pa_source_volume_change);
+};
+
+struct source_message_set_port {
+    pa_device_port *port;
+    int ret;
+};
+
 static void source_free(pa_object *o);
 
+static void pa_source_volume_change_push(pa_source *s);
+static void pa_source_volume_change_flush(pa_source *s);
+
 pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data) {
     pa_assert(data);
 
@@ -179,8 +196,14 @@ pa_source* pa_source_new(
     pa_return_null_if_fail(pa_channel_map_valid(&data->channel_map));
     pa_return_null_if_fail(data->channel_map.channels == data->sample_spec.channels);
 
-    if (!data->volume_is_set)
+    /* FIXME: There should probably be a general function for checking whether
+     * the source volume is allowed to be set, like there is for source outputs. */
+    pa_assert(!data->volume_is_set || !(flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
+
+    if (!data->volume_is_set) {
         pa_cvolume_reset(&data->volume, data->sample_spec.channels);
+        data->save_volume = FALSE;
+    }
 
     pa_return_null_if_fail(pa_cvolume_valid(&data->volume));
     pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));
@@ -225,7 +248,7 @@ pa_source* pa_source_new(
     s->monitor_of = NULL;
     s->output_from_master = NULL;
 
-    s->volume = data->volume;
+    s->reference_volume = s->real_volume = data->volume;
     pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
     s->base_volume = PA_VOLUME_NORM;
     s->n_volume_steps = PA_VOLUME_NORM+1;
@@ -280,6 +303,13 @@ pa_source* pa_source_new(
     s->thread_info.max_latency = ABSOLUTE_MAX_LATENCY;
     s->thread_info.fixed_latency = flags & PA_SOURCE_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
 
+    PA_LLIST_HEAD_INIT(pa_source_volume_change, s->thread_info.volume_changes);
+    s->thread_info.volume_changes_tail = NULL;
+    pa_sw_cvolume_multiply(&s->thread_info.current_hw_volume, &s->soft_volume, &s->real_volume);
+    s->thread_info.volume_change_safety_margin = core->sync_volume_safety_margin_usec;
+    s->thread_info.volume_change_extra_delay = core->sync_volume_extra_delay_usec;
+
+    /* FIXME: This should probably be moved to pa_source_put() */
     pa_assert_se(pa_idxset_put(core->sources, s, &s->index) >= 0);
 
     if (s->card)
@@ -358,24 +388,59 @@ void pa_source_put(pa_source *s) {
     pa_assert_ctl_context();
 
     pa_assert(s->state == PA_SOURCE_INIT);
+    pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) || s->output_from_master);
 
     /* The following fields must be initialized properly when calling _put() */
     pa_assert(s->asyncmsgq);
     pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
 
-    /* Generally, flags should be initialized via pa_source_new(). As
-     * a special exception we allow volume related flags to be set
+    /* Generally, flags should be initialized via pa_source_new(). As a
+     * special exception we allow volume related flags to be set
      * between _new() and _put(). */
 
-    if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL))
+    /* XXX: Currently decibel volume is disabled for all sources that use volume
+     * sharing. When the master source supports decibel volume, it would be good
+     * to have the flag also in the filter source, but currently we don't do that
+     * so that the flags of the filter source never change when it's moved from
+     * a master source to another. One solution for this problem would be to
+     * remove user-visible volume altogether from filter sources when volume
+     * sharing is used, but the current approach was easier to implement... */
+    if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL) && !(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
         s->flags |= PA_SOURCE_DECIBEL_VOLUME;
 
+    if ((s->flags & PA_SOURCE_DECIBEL_VOLUME) && s->core->flat_volumes)
+        s->flags |= PA_SOURCE_FLAT_VOLUME;
+
+    if (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) {
+        pa_source *root_source = s->output_from_master->source;
+
+        while (root_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)
+            root_source = root_source->output_from_master->source;
+
+        s->reference_volume = root_source->reference_volume;
+        pa_cvolume_remap(&s->reference_volume, &root_source->channel_map, &s->channel_map);
+
+        s->real_volume = root_source->real_volume;
+        pa_cvolume_remap(&s->real_volume, &root_source->channel_map, &s->channel_map);
+    } else
+        /* We assume that if the sink implementor changed the default
+         * volume he did so in real_volume, because that is the usual
+         * place where he is supposed to place his changes.  */
+        s->reference_volume = s->real_volume;
+
     s->thread_info.soft_volume = s->soft_volume;
     s->thread_info.soft_muted = s->muted;
+    pa_sw_cvolume_multiply(&s->thread_info.current_hw_volume, &s->soft_volume, &s->real_volume);
 
-    pa_assert((s->flags & PA_SOURCE_HW_VOLUME_CTRL) || (s->base_volume == PA_VOLUME_NORM && s->flags & PA_SOURCE_DECIBEL_VOLUME));
+    pa_assert((s->flags & PA_SOURCE_HW_VOLUME_CTRL)
+              || (s->base_volume == PA_VOLUME_NORM
+                  && ((s->flags & PA_SOURCE_DECIBEL_VOLUME || (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)))));
     pa_assert(!(s->flags & PA_SOURCE_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1);
     pa_assert(!(s->flags & PA_SOURCE_DYNAMIC_LATENCY) == (s->thread_info.fixed_latency != 0));
+    pa_assert(!(s->flags & PA_SOURCE_HW_VOLUME_CTRL) || s->set_volume);
+    pa_assert(!(s->flags & PA_SOURCE_SYNC_VOLUME) || (s->flags & PA_SOURCE_HW_VOLUME_CTRL));
+    pa_assert(!(s->flags & PA_SOURCE_SYNC_VOLUME) || s->write_volume);
+    pa_assert(!(s->flags & PA_SOURCE_HW_MUTE_CTRL) || s->set_mute);
 
     pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
 
@@ -529,7 +594,7 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause)
 
     pa_log_debug("Suspend cause of source %s is 0x%04x, %s", s->name, s->suspend_cause, s->suspend_cause ? "suspending" : "resuming");
 
-    if (suspend)
+    if (s->suspend_cause)
         return source_set_state(s, PA_SOURCE_SUSPENDED);
     else
         return source_set_state(s, pa_source_used_by(s) ? PA_SOURCE_RUNNING : PA_SOURCE_IDLE);
@@ -748,7 +813,7 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
 
     o = PA_MSGOBJECT(s);
 
-    /* We probably should make this a proper vtable callback instead of going through process_msg() */
+    /* FIXME: We probably should make this a proper vtable callback instead of going through process_msg() */
 
     if (o->process_msg(o, PA_SOURCE_MESSAGE_GET_LATENCY, &usec, 0, NULL) < 0)
         return -1;
@@ -756,6 +821,21 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
     return usec;
 }
 
+/* Called from the main thread (and also from the IO thread while the main
+ * thread is waiting).
+ *
+ * When a source uses volume sharing, it never has the PA_SOURCE_FLAT_VOLUME flag
+ * set. Instead, flat volume mode is detected by checking whether the root source
+ * has the flag set. */
+pa_bool_t pa_source_flat_volume_enabled(pa_source *s) {
+    pa_source_assert_ref(s);
+
+    while (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)
+        s = s->output_from_master->source;
+
+    return (s->flags & PA_SOURCE_FLAT_VOLUME);
+}
+
 /* Called from main context */
 pa_bool_t pa_source_is_passthrough(pa_source *s) {
 
@@ -765,59 +845,569 @@ pa_bool_t pa_source_is_passthrough(pa_source *s) {
     return (s->monitor_of && pa_sink_is_passthrough(s->monitor_of));
 }
 
+/* Called from main context. */
+static void compute_reference_ratio(pa_source_output *o) {
+    unsigned c = 0;
+    pa_cvolume remapped;
+
+    pa_assert(o);
+    pa_assert(pa_source_flat_volume_enabled(o->source));
+
+    /*
+     * Calculates the reference ratio from the source's reference
+     * volume. This basically calculates:
+     *
+     * o->reference_ratio = o->volume / o->source->reference_volume
+     */
+
+    remapped = o->source->reference_volume;
+    pa_cvolume_remap(&remapped, &o->source->channel_map, &o->channel_map);
+
+    o->reference_ratio.channels = o->sample_spec.channels;
+
+    for (c = 0; c < o->sample_spec.channels; c++) {
+
+        /* We don't update when the source volume is 0 anyway */
+        if (remapped.values[c] <= PA_VOLUME_MUTED)
+            continue;
+
+        /* Don't update the reference ratio unless necessary */
+        if (pa_sw_volume_multiply(
+                    o->reference_ratio.values[c],
+                    remapped.values[c]) == o->volume.values[c])
+            continue;
+
+        o->reference_ratio.values[c] = pa_sw_volume_divide(
+                o->volume.values[c],
+                remapped.values[c]);
+    }
+}
+
+/* Called from main context. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static void compute_reference_ratios(pa_source *s) {
+    uint32_t idx;
+    pa_source_output *o;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(pa_source_flat_volume_enabled(s));
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        compute_reference_ratio(o);
+
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
+            compute_reference_ratios(o->destination_source);
+    }
+}
+
+/* Called from main context. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static void compute_real_ratios(pa_source *s) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(pa_source_flat_volume_enabled(s));
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        unsigned c;
+        pa_cvolume remapped;
+
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+            /* The origin source uses volume sharing, so this input's real ratio
+             * is handled as a special case - the real ratio must be 0 dB, and
+             * as a result i->soft_volume must equal i->volume_factor. */
+            pa_cvolume_reset(&o->real_ratio, o->real_ratio.channels);
+            o->soft_volume = o->volume_factor;
+
+            compute_real_ratios(o->destination_source);
+
+            continue;
+        }
+
+        /*
+         * This basically calculates:
+         *
+         * i->real_ratio := i->volume / s->real_volume
+         * i->soft_volume := i->real_ratio * i->volume_factor
+         */
+
+        remapped = s->real_volume;
+        pa_cvolume_remap(&remapped, &s->channel_map, &o->channel_map);
+
+        o->real_ratio.channels = o->sample_spec.channels;
+        o->soft_volume.channels = o->sample_spec.channels;
+
+        for (c = 0; c < o->sample_spec.channels; c++) {
+
+            if (remapped.values[c] <= PA_VOLUME_MUTED) {
+                /* We leave o->real_ratio untouched */
+                o->soft_volume.values[c] = PA_VOLUME_MUTED;
+                continue;
+            }
+
+            /* Don't lose accuracy unless necessary */
+            if (pa_sw_volume_multiply(
+                        o->real_ratio.values[c],
+                        remapped.values[c]) != o->volume.values[c])
+
+                o->real_ratio.values[c] = pa_sw_volume_divide(
+                        o->volume.values[c],
+                        remapped.values[c]);
+
+            o->soft_volume.values[c] = pa_sw_volume_multiply(
+                    o->real_ratio.values[c],
+                    o->volume_factor.values[c]);
+        }
+
+        /* We don't copy the soft_volume to the thread_info data
+         * here. That must be done by the caller */
+    }
+}
+
+static pa_cvolume *cvolume_remap_minimal_impact(
+        pa_cvolume *v,
+        const pa_cvolume *template,
+        const pa_channel_map *from,
+        const pa_channel_map *to) {
+
+    pa_cvolume t;
+
+    pa_assert(v);
+    pa_assert(template);
+    pa_assert(from);
+    pa_assert(to);
+    pa_assert(pa_cvolume_compatible_with_channel_map(v, from));
+    pa_assert(pa_cvolume_compatible_with_channel_map(template, to));
+
+    /* Much like pa_cvolume_remap(), but tries to minimize impact when
+     * mapping from source output to source volumes:
+     *
+     * If template is a possible remapping from v it is used instead
+     * of remapping anew.
+     *
+     * If the channel maps don't match we set an all-channel volume on
+     * the source to ensure that changing a volume on one stream has no
+     * effect that cannot be compensated for in another stream that
+     * does not have the same channel map as the source. */
+
+    if (pa_channel_map_equal(from, to))
+        return v;
+
+    t = *template;
+    if (pa_cvolume_equal(pa_cvolume_remap(&t, to, from), v)) {
+        *v = *template;
+        return v;
+    }
+
+    pa_cvolume_set(v, to->channels, pa_cvolume_max(v));
+    return v;
+}
+
+/* Called from main thread. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static void get_maximum_output_volume(pa_source *s, pa_cvolume *max_volume, const pa_channel_map *channel_map) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert(max_volume);
+    pa_assert(channel_map);
+    pa_assert(pa_source_flat_volume_enabled(s));
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        pa_cvolume remapped;
+
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+            get_maximum_output_volume(o->destination_source, max_volume, channel_map);
+
+            /* Ignore this output. The origin source uses volume sharing, so this
+             * output's volume will be set to be equal to the root source's real
+             * volume. Obviously this outputs's current volume must not then
+             * affect what the root source's real volume will be. */
+            continue;
+        }
+
+        remapped = o->volume;
+        cvolume_remap_minimal_impact(&remapped, max_volume, &o->channel_map, channel_map);
+        pa_cvolume_merge(max_volume, max_volume, &remapped);
+    }
+}
+
+/* Called from main thread. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static pa_bool_t has_outputs(pa_source *s) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        if (!o->destination_source || !(o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) || has_outputs(o->destination_source))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+/* Called from main thread. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static void update_real_volume(pa_source *s, const pa_cvolume *new_volume, pa_channel_map *channel_map) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert(new_volume);
+    pa_assert(channel_map);
+
+    s->real_volume = *new_volume;
+    pa_cvolume_remap(&s->real_volume, channel_map, &s->channel_map);
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+            if (pa_source_flat_volume_enabled(s)) {
+                pa_cvolume old_volume = o->volume;
+
+                /* Follow the root source's real volume. */
+                o->volume = *new_volume;
+                pa_cvolume_remap(&o->volume, channel_map, &o->channel_map);
+                compute_reference_ratio(o);
+
+                /* The volume changed, let's tell people so */
+                if (!pa_cvolume_equal(&old_volume, &o->volume)) {
+                    if (o->volume_changed)
+                        o->volume_changed(o);
+
+                    pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+                }
+            }
+
+            update_real_volume(o->destination_source, new_volume, channel_map);
+        }
+    }
+}
+
+/* Called from main thread. Only called for the root source in shared volume
+ * cases. */
+static void compute_real_volume(pa_source *s) {
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(pa_source_flat_volume_enabled(s));
+    pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
+
+    /* This determines the maximum volume of all streams and sets
+     * s->real_volume accordingly. */
+
+    if (!has_outputs(s)) {
+        /* In the special case that we have no source outputs we leave the
+         * volume unmodified. */
+        update_real_volume(s, &s->reference_volume, &s->channel_map);
+        return;
+    }
+
+    pa_cvolume_mute(&s->real_volume, s->channel_map.channels);
+
+    /* First let's determine the new maximum volume of all outputs
+     * connected to this source */
+    get_maximum_output_volume(s, &s->real_volume, &s->channel_map);
+    update_real_volume(s, &s->real_volume, &s->channel_map);
+
+    /* Then, let's update the real ratios/soft volumes of all outputs
+     * connected to this source */
+    compute_real_ratios(s);
+}
+
+/* Called from main thread. Only called for the root source in shared volume
+ * cases, except for internal recursive calls. */
+static void propagate_reference_volume(pa_source *s) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(pa_source_flat_volume_enabled(s));
+
+    /* This is called whenever the source volume changes that is not
+     * caused by a source output volume change. We need to fix up the
+     * source output volumes accordingly */
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        pa_cvolume old_volume;
+
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+            propagate_reference_volume(o->destination_source);
+
+            /* Since the origin source uses volume sharing, this output's volume
+             * needs to be updated to match the root source's real volume, but
+             * that will be done later in update_shared_real_volume(). */
+            continue;
+        }
+
+        old_volume = o->volume;
+
+        /* This basically calculates:
+         *
+         * o->volume := o->reference_volume * o->reference_ratio  */
+
+        o->volume = s->reference_volume;
+        pa_cvolume_remap(&o->volume, &s->channel_map, &o->channel_map);
+        pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
+
+        /* The volume changed, let's tell people so */
+        if (!pa_cvolume_equal(&old_volume, &o->volume)) {
+
+            if (o->volume_changed)
+                o->volume_changed(o);
+
+            pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+        }
+    }
+}
+
+/* Called from main thread. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. The return value indicates
+ * whether any reference volume actually changed. */
+static pa_bool_t update_reference_volume(pa_source *s, const pa_cvolume *v, const pa_channel_map *channel_map, pa_bool_t save) {
+    pa_cvolume volume;
+    pa_bool_t reference_volume_changed;
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(v);
+    pa_assert(channel_map);
+    pa_assert(pa_cvolume_valid(v));
+
+    volume = *v;
+    pa_cvolume_remap(&volume, channel_map, &s->channel_map);
+
+    reference_volume_changed = !pa_cvolume_equal(&volume, &s->reference_volume);
+    s->reference_volume = volume;
+
+    s->save_volume = (!reference_volume_changed && s->save_volume) || save;
+
+    if (reference_volume_changed)
+        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    else if (!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
+        /* If the root source's volume doesn't change, then there can't be any
+         * changes in the other source in the source tree either.
+         *
+         * It's probably theoretically possible that even if the root source's
+         * volume changes slightly, some filter source doesn't change its volume
+         * due to rounding errors. If that happens, we still want to propagate
+         * the changed root source volume to the sources connected to the
+         * intermediate source that didn't change its volume. This theoretical
+         * possiblity is the reason why we have that !(s->flags &
+         * PA_SOURCE_SHARE_VOLUME_WITH_MASTER) condition. Probably nobody would
+         * notice even if we returned here FALSE always if
+         * reference_volume_changed is FALSE. */
+        return FALSE;
+
+    PA_IDXSET_FOREACH(o, s->outputs, idx) {
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
+            update_reference_volume(o->destination_source, v, channel_map, FALSE);
+    }
+
+    return TRUE;
+}
+
 /* Called from main thread */
 void pa_source_set_volume(
         pa_source *s,
         const pa_cvolume *volume,
+        pa_bool_t send_msg,
         pa_bool_t save) {
 
-    pa_bool_t real_changed;
-    pa_cvolume old_volume;
+    pa_cvolume new_reference_volume;
+    pa_source *root_source = s;
 
     pa_source_assert_ref(s);
     pa_assert_ctl_context();
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
-    pa_assert(pa_cvolume_valid(volume));
-    pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
+    pa_assert(!volume || pa_cvolume_valid(volume));
+    pa_assert(volume || pa_source_flat_volume_enabled(s));
+    pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
+
+    /* make sure we don't change the volume when a PASSTHROUGH output is connected */
+    if (pa_source_is_passthrough(s)) {
+        /* FIXME: Need to notify client that volume control is disabled */
+        pa_log_warn("Cannot change volume, Source is monitor of a PASSTHROUGH sink");
+        return;
+    }
 
-    old_volume = s->volume;
+    /* In case of volume sharing, the volume is set for the root source first,
+     * from which it's then propagated to the sharing sources. */
+    while (root_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)
+        root_source = root_source->output_from_master->source;
 
-    if (pa_cvolume_compatible(volume, &s->sample_spec))
-        s->volume = *volume;
-    else
-        pa_cvolume_scale(&s->volume, pa_cvolume_max(volume));
+    /* As a special exception we accept mono volumes on all sources --
+     * even on those with more complex channel maps */
 
-    real_changed = !pa_cvolume_equal(&old_volume, &s->volume);
-    s->save_volume = (!real_changed && s->save_volume) || save;
+    if (volume) {
+        if (pa_cvolume_compatible(volume, &s->sample_spec))
+            new_reference_volume = *volume;
+        else {
+            new_reference_volume = s->reference_volume;
+            pa_cvolume_scale(&new_reference_volume, pa_cvolume_max(volume));
+        }
 
-    if (s->set_volume) {
-        pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
-        s->set_volume(s);
-    } else
-        s->soft_volume = s->volume;
+        pa_cvolume_remap(&new_reference_volume, &s->channel_map, &root_source->channel_map);
+    }
 
-    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
+    /* If volume is NULL we synchronize the source's real and reference
+     * volumes with the stream volumes. If it is not NULL we update
+     * the reference_volume with it. */
 
-    if (real_changed)
-        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    if (volume) {
+        if (update_reference_volume(root_source, &new_reference_volume, &root_source->channel_map, save)) {
+            if (pa_source_flat_volume_enabled(root_source)) {
+                /* OK, propagate this volume change back to the outputs */
+                propagate_reference_volume(root_source);
+
+                /* And now recalculate the real volume */
+                compute_real_volume(root_source);
+            } else
+                update_real_volume(root_source, &root_source->reference_volume, &root_source->channel_map);
+        }
+
+    } else {
+        pa_assert(pa_source_flat_volume_enabled(root_source));
+
+        /* Ok, let's determine the new real volume */
+        compute_real_volume(root_source);
+
+        /* Let's 'push' the reference volume if necessary */
+        pa_cvolume_merge(&new_reference_volume, &s->reference_volume, &root_source->real_volume);
+        update_reference_volume(root_source, &new_reference_volume, &root_source->channel_map, save);
+
+        /* Now that the reference volume is updated, we can update the streams'
+         * reference ratios. */
+        compute_reference_ratios(root_source);
+    }
+
+    if (root_source->set_volume) {
+        /* If we have a function set_volume(), then we do not apply a
+         * soft volume by default. However, set_volume() is free to
+         * apply one to root_source->soft_volume */
+
+        pa_cvolume_reset(&root_source->soft_volume, root_source->sample_spec.channels);
+        if (!(root_source->flags & PA_SOURCE_SYNC_VOLUME))
+            root_source->set_volume(root_source);
+
+    } else
+        /* If we have no function set_volume(), then the soft volume
+         * becomes the real volume */
+        root_source->soft_volume = root_source->real_volume;
+
+    /* This tells the source that soft volume and/or real volume changed */
+    if (send_msg)
+        pa_assert_se(pa_asyncmsgq_send(root_source->asyncmsgq, PA_MSGOBJECT(root_source), PA_SOURCE_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL) == 0);
 }
 
-/* Called from main thread. Only to be called by source implementor */
+/* Called from the io thread if sync volume is used, otherwise from the main thread.
+ * Only to be called by source implementor */
 void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume) {
+
     pa_source_assert_ref(s);
-    pa_assert_ctl_context();
+    pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
+
+    if (s->flags & PA_SOURCE_SYNC_VOLUME)
+        pa_source_assert_io_context(s);
+    else
+        pa_assert_ctl_context();
 
     if (!volume)
         pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
     else
         s->soft_volume = *volume;
 
-    if (PA_SOURCE_IS_LINKED(s->state))
+    if (PA_SOURCE_IS_LINKED(s->state) && !(s->flags & PA_SOURCE_SYNC_VOLUME))
         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);
     else
         s->thread_info.soft_volume = s->soft_volume;
 }
 
+/* Called from the main thread. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static void propagate_real_volume(pa_source *s, const pa_cvolume *old_real_volume) {
+    pa_source_output *o;
+    uint32_t idx;
+
+    pa_source_assert_ref(s);
+    pa_assert(old_real_volume);
+    pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
+
+    /* This is called when the hardware's real volume changes due to
+     * some external event. We copy the real volume into our
+     * reference volume and then rebuild the stream volumes based on
+     * i->real_ratio which should stay fixed. */
+
+    if (!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+        if (pa_cvolume_equal(old_real_volume, &s->real_volume))
+            return;
+
+        /* 1. Make the real volume the reference volume */
+        update_reference_volume(s, &s->real_volume, &s->channel_map, TRUE);
+    }
+
+    if (pa_source_flat_volume_enabled(s)) {
+
+        PA_IDXSET_FOREACH(o, s->outputs, idx) {
+            pa_cvolume old_volume = o->volume;
+
+            /* 2. Since the source's reference and real volumes are equal
+             * now our ratios should be too. */
+            o->reference_ratio = o->real_ratio;
+
+            /* 3. Recalculate the new stream reference volume based on the
+             * reference ratio and the sink's reference volume.
+             *
+             * This basically calculates:
+             *
+             * o->volume = s->reference_volume * o->reference_ratio
+             *
+             * This is identical to propagate_reference_volume() */
+            o->volume = s->reference_volume;
+            pa_cvolume_remap(&o->volume, &s->channel_map, &o->channel_map);
+            pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
+
+            /* Notify if something changed */
+            if (!pa_cvolume_equal(&old_volume, &o->volume)) {
+
+                if (o->volume_changed)
+                    o->volume_changed(o);
+
+                pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+            }
+
+            if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
+                propagate_real_volume(o->destination_source, old_real_volume);
+        }
+    }
+
+    /* Something got changed in the hardware. It probably makes sense
+     * to save changed hw settings given that hw volume changes not
+     * triggered by PA are almost certainly done by the user. */
+    if (!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
+        s->save_volume = TRUE;
+}
+
+/* Called from io thread */
+void pa_source_update_volume_and_mute(pa_source *s) {
+    pa_assert(s);
+    pa_source_assert_io_context(s);
+
+    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_UPDATE_VOLUME_AND_MUTE, NULL, 0, NULL, NULL);
+}
+
 /* Called from main thread */
 const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {
     pa_source_assert_ref(s);
@@ -825,39 +1415,39 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     if (s->refresh_volume || force_refresh) {
-        pa_cvolume old_volume;
+        struct pa_cvolume old_real_volume;
+
+        pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
 
-        old_volume = s->volume;
+        old_real_volume = s->real_volume;
 
-        if (s->get_volume)
+        if (!(s->flags & PA_SOURCE_SYNC_VOLUME) && s->get_volume)
             s->get_volume(s);
 
         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0);
 
-        if (!pa_cvolume_equal(&old_volume, &s->volume)) {
-            s->save_volume = TRUE;
-            pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
-        }
+        update_real_volume(s, &s->real_volume, &s->channel_map);
+        propagate_real_volume(s, &old_real_volume);
     }
 
-    return &s->volume;
+    return &s->reference_volume;
 }
 
-/* Called from main thread */
-void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume) {
+/* Called from main thread. In volume sharing cases, only the root source may
+ * call this. */
+void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_real_volume) {
+    pa_cvolume old_real_volume;
+
     pa_source_assert_ref(s);
     pa_assert_ctl_context();
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
+    pa_assert(!(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER));
 
     /* The source implementor may call this if the volume changed to make sure everyone is notified */
 
-    if (pa_cvolume_equal(&s->volume, new_volume))
-        return;
-
-    s->volume = *new_volume;
-    s->save_volume = TRUE;
-
-    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+    old_real_volume = s->real_volume;
+    update_real_volume(s, new_real_volume, &s->channel_map);
+    propagate_real_volume(s, &old_real_volume);
 }
 
 /* Called from main thread */
@@ -872,7 +1462,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute, pa_bool_t save) {
     s->muted = mute;
     s->save_muted = (old_muted == s->muted && s->save_muted) || save;
 
-    if (s->set_mute)
+    if (!(s->flags & PA_SOURCE_SYNC_VOLUME) && s->set_mute)
         s->set_mute(s);
 
     pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_MUTE, NULL, 0, NULL) == 0);
@@ -883,6 +1473,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute, pa_bool_t save) {
 
 /* Called from main thread */
 pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
+
     pa_source_assert_ref(s);
     pa_assert_ctl_context();
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
@@ -890,7 +1481,7 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
     if (s->refresh_muted || force_refresh) {
         pa_bool_t old_muted = s->muted;
 
-        if (s->get_mute)
+        if (!(s->flags & PA_SOURCE_SYNC_VOLUME) && s->get_mute)
             s->get_mute(s);
 
         pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_MUTE, NULL, 0, NULL) == 0);
@@ -970,8 +1561,8 @@ void pa_source_set_description(pa_source *s, const char *description) {
 /* Called from main thread */
 unsigned pa_source_linked_by(pa_source *s) {
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_IS_LINKED(s->state));
     pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     return pa_idxset_size(s->outputs);
 }
@@ -981,8 +1572,8 @@ unsigned pa_source_used_by(pa_source *s) {
     unsigned ret;
 
     pa_source_assert_ref(s);
-    pa_assert(PA_SOURCE_IS_LINKED(s->state));
     pa_assert_ctl_context();
+    pa_assert(PA_SOURCE_IS_LINKED(s->state));
 
     ret = pa_idxset_size(s->outputs);
     pa_assert(ret >= s->n_corked);
@@ -1029,6 +1620,39 @@ unsigned pa_source_check_suspend(pa_source *s) {
     return ret;
 }
 
+/* Called from the IO thread */
+static void sync_output_volumes_within_thread(pa_source *s) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+    pa_source_assert_io_context(s);
+
+    PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state) {
+        if (pa_cvolume_equal(&o->thread_info.soft_volume, &o->soft_volume))
+            continue;
+
+        o->thread_info.soft_volume = o->soft_volume;
+        //pa_source_output_request_rewind(o, 0, TRUE, FALSE, FALSE);
+    }
+}
+
+/* Called from the IO thread. Only called for the root source in volume sharing
+ * cases, except for internal recursive calls. */
+static void set_shared_volume_within_thread(pa_source *s) {
+    pa_source_output *o;
+    void *state = NULL;
+
+    pa_source_assert_ref(s);
+
+    PA_MSGOBJECT(s)->process_msg(PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME_SYNCED, NULL, 0, NULL);
+
+    PA_HASHMAP_FOREACH(o, s->thread_info.outputs, state) {
+        if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
+            set_shared_volume_within_thread(o->destination_source);
+    }
+}
+
 /* Called from IO thread, except when it is not */
 int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
     pa_source *s = PA_SOURCE(object);
@@ -1041,6 +1665,22 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
 
             pa_hashmap_put(s->thread_info.outputs, PA_UINT32_TO_PTR(o->index), pa_source_output_ref(o));
 
+            /* Since the caller sleeps in pa_source_output_put(), we can
+             * safely access data outside of thread_info even though
+             * it is mutable */
+
+            if ((o->thread_info.sync_prev = o->sync_prev)) {
+                pa_assert(o->source == o->thread_info.sync_prev->source);
+                pa_assert(o->sync_prev->sync_next == o);
+                o->thread_info.sync_prev->thread_info.sync_next = o;
+            }
+
+            if ((o->thread_info.sync_next = o->sync_next)) {
+                pa_assert(o->source == o->thread_info.sync_next->source);
+                pa_assert(o->sync_next->sync_prev == o);
+                o->thread_info.sync_next->thread_info.sync_prev = o;
+            }
+
             if (o->direct_on_input) {
                 o->thread_info.direct_on_input = o->direct_on_input;
                 pa_hashmap_put(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index), o);
@@ -1064,7 +1704,9 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
              * requested latency. */
             pa_source_output_set_requested_latency_within_thread(o, o->thread_info.requested_source_latency);
 
-            return 0;
+            /* In flat volume mode we need to update the volume as
+             * well */
+            return object->process_msg(object, PA_SOURCE_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
         }
 
         case PA_SOURCE_MESSAGE_REMOVE_OUTPUT: {
@@ -1078,6 +1720,23 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
             pa_assert(o->thread_info.attached);
             o->thread_info.attached = FALSE;
 
+            /* Since the caller sleeps in pa_sink_input_unlink(),
+             * we can safely access data outside of thread_info even
+             * though it is mutable */
+
+            pa_assert(!o->sync_prev);
+            pa_assert(!o->sync_next);
+
+            if (o->thread_info.sync_prev) {
+                o->thread_info.sync_prev->thread_info.sync_next = o->thread_info.sync_prev->sync_next;
+                o->thread_info.sync_prev = NULL;
+            }
+
+            if (o->thread_info.sync_next) {
+                o->thread_info.sync_next->thread_info.sync_prev = o->thread_info.sync_next->sync_prev;
+                o->thread_info.sync_next = NULL;
+            }
+
             if (o->thread_info.direct_on_input) {
                 pa_hashmap_remove(o->thread_info.direct_on_input->thread_info.direct_outputs, PA_UINT32_TO_PTR(o->index));
                 o->thread_info.direct_on_input = NULL;
@@ -1088,21 +1747,72 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
 
             pa_source_invalidate_requested_latency(s, TRUE);
 
+            /* In flat volume mode we need to update the volume as
+             * well */
+            return object->process_msg(object, PA_SOURCE_MESSAGE_SET_SHARED_VOLUME, NULL, 0, NULL);
+        }
+
+        case PA_SOURCE_MESSAGE_SET_SHARED_VOLUME: {
+            pa_source *root_source = s;
+
+            while (root_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)
+                root_source = root_source->output_from_master->source;
+
+            set_shared_volume_within_thread(root_source);
             return 0;
         }
 
+        case PA_SOURCE_MESSAGE_SET_VOLUME_SYNCED:
+
+            if (s->flags & PA_SOURCE_SYNC_VOLUME) {
+                s->set_volume(s);
+                pa_source_volume_change_push(s);
+            }
+            /* Fall through ... */
+
         case PA_SOURCE_MESSAGE_SET_VOLUME:
-            s->thread_info.soft_volume = s->soft_volume;
+
+            if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
+                s->thread_info.soft_volume = s->soft_volume;
+            }
+
+            /* Fall through ... */
+
+        case PA_SOURCE_MESSAGE_SYNC_VOLUMES:
+            sync_output_volumes_within_thread(s);
             return 0;
 
         case PA_SOURCE_MESSAGE_GET_VOLUME:
+
+            if ((s->flags & PA_SOURCE_SYNC_VOLUME) && s->get_volume) {
+                s->get_volume(s);
+                pa_source_volume_change_flush(s);
+                pa_sw_cvolume_divide(&s->thread_info.current_hw_volume, &s->real_volume, &s->soft_volume);
+            }
+
+            /* In case source implementor reset SW volume. */
+            if (!pa_cvolume_equal(&s->thread_info.soft_volume, &s->soft_volume)) {
+                s->thread_info.soft_volume = s->soft_volume;
+            }
+
             return 0;
 
         case PA_SOURCE_MESSAGE_SET_MUTE:
-            s->thread_info.soft_muted = s->muted;
+
+            if (s->thread_info.soft_muted != s->muted) {
+                s->thread_info.soft_muted = s->muted;
+            }
+
+            if (s->flags & PA_SOURCE_SYNC_VOLUME && s->set_mute)
+                s->set_mute(s);
+
             return 0;
 
         case PA_SOURCE_MESSAGE_GET_MUTE:
+
+            if (s->flags & PA_SOURCE_SYNC_VOLUME && s->get_mute)
+                s->get_mute(s);
+
             return 0;
 
         case PA_SOURCE_MESSAGE_SET_STATE: {
@@ -1122,7 +1832,6 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
                         o->suspend_within_thread(o, s->thread_info.state == PA_SOURCE_SUSPENDED);
             }
 
-
             return 0;
         }
 
@@ -1143,6 +1852,9 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
             pa_usec_t *usec = userdata;
             *usec = pa_source_get_requested_latency_within_thread(s);
 
+            /* Yes, that's right, the IO thread will see -1 when no
+             * explicit requested latency is configured, the main
+             * thread will see max_latency */
             if (*usec == (pa_usec_t) -1)
                 *usec = s->thread_info.max_latency;
 
@@ -1196,6 +1908,23 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
             /* Implementors need to overwrite this implementation! */
             return -1;
 
+        case PA_SOURCE_MESSAGE_SET_PORT:
+
+            pa_assert(userdata);
+            if (s->set_port) {
+                struct source_message_set_port *msg_data = userdata;
+                msg_data->ret = s->set_port(s, msg_data->port);
+            }
+            return 0;
+
+        case PA_SOURCE_MESSAGE_UPDATE_VOLUME_AND_MUTE:
+            /* This message is sent from IO-thread and handled in main thread. */
+            pa_assert_ctl_context();
+
+            pa_source_get_volume(s, TRUE);
+            pa_source_get_mute(s, TRUE);
+            return 0;
+
         case PA_SOURCE_MESSAGE_MAX:
             ;
     }
@@ -1205,8 +1934,8 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
 
 /* Called from main thread */
 int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause) {
-    uint32_t idx;
     pa_source *source;
+    uint32_t idx;
     int ret = 0;
 
     pa_core_assert_ref(c);
@@ -1552,8 +2281,9 @@ size_t pa_source_get_max_rewind(pa_source *s) {
 /* Called from main context */
 int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) {
     pa_device_port *port;
+    int ret;
 
-    pa_assert(s);
+    pa_source_assert_ref(s);
     pa_assert_ctl_context();
 
     if (!s->set_port) {
@@ -1572,7 +2302,15 @@ int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) {
         return 0;
     }
 
-    if ((s->set_port(s, port)) < 0)
+    if (s->flags & PA_SOURCE_SYNC_VOLUME) {
+        struct source_message_set_port msg = { .port = port, .ret = 0 };
+        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_PORT, &msg, 0, NULL) == 0);
+        ret = msg.ret;
+    }
+    else
+        ret = s->set_port(s, port);
+
+    if (ret < 0)
         return -PA_ERR_NOENTITY;
 
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
@@ -1587,6 +2325,145 @@ int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) {
     return 0;
 }
 
+PA_STATIC_FLIST_DECLARE(pa_source_volume_change, 0, pa_xfree);
+
+/* Called from the IO thread. */
+static pa_source_volume_change *pa_source_volume_change_new(pa_source *s) {
+    pa_source_volume_change *c;
+    if (!(c = pa_flist_pop(PA_STATIC_FLIST_GET(pa_source_volume_change))))
+        c = pa_xnew(pa_source_volume_change, 1);
+
+    PA_LLIST_INIT(pa_source_volume_change, c);
+    c->at = 0;
+    pa_cvolume_reset(&c->hw_volume, s->sample_spec.channels);
+    return c;
+}
+
+/* Called from the IO thread. */
+static void pa_source_volume_change_free(pa_source_volume_change *c) {
+    pa_assert(c);
+    if (pa_flist_push(PA_STATIC_FLIST_GET(pa_source_volume_change), c) < 0)
+        pa_xfree(c);
+}
+
+/* Called from the IO thread. */
+void pa_source_volume_change_push(pa_source *s) {
+    pa_source_volume_change *c = NULL;
+    pa_source_volume_change *nc = NULL;
+    uint32_t safety_margin = s->thread_info.volume_change_safety_margin;
+
+    const char *direction = NULL;
+
+    pa_assert(s);
+    nc = pa_source_volume_change_new(s);
+
+    /* NOTE: There is already more different volumes in pa_source that I can remember.
+     *       Adding one more volume for HW would get us rid of this, but I am trying
+     *       to survive with the ones we already have. */
+    pa_sw_cvolume_divide(&nc->hw_volume, &s->real_volume, &s->soft_volume);
+
+    if (!s->thread_info.volume_changes && pa_cvolume_equal(&nc->hw_volume, &s->thread_info.current_hw_volume)) {
+        pa_log_debug("Volume not changing");
+        pa_source_volume_change_free(nc);
+        return;
+    }
+
+    nc->at = pa_source_get_latency_within_thread(s);
+    nc->at += pa_rtclock_now() + s->thread_info.volume_change_extra_delay;
+
+    if (s->thread_info.volume_changes_tail) {
+        for (c = s->thread_info.volume_changes_tail; c; c = c->prev) {
+            /* If volume is going up let's do it a bit late. If it is going
+             * down let's do it a bit early. */
+            if (pa_cvolume_avg(&nc->hw_volume) > pa_cvolume_avg(&c->hw_volume)) {
+                if (nc->at + safety_margin > c->at) {
+                    nc->at += safety_margin;
+                    direction = "up";
+                    break;
+                }
+            }
+            else if (nc->at - safety_margin > c->at) {
+                    nc->at -= safety_margin;
+                    direction = "down";
+                    break;
+            }
+        }
+    }
+
+    if (c == NULL) {
+        if (pa_cvolume_avg(&nc->hw_volume) > pa_cvolume_avg(&s->thread_info.current_hw_volume)) {
+            nc->at += safety_margin;
+            direction = "up";
+        } else {
+            nc->at -= safety_margin;
+            direction = "down";
+        }
+        PA_LLIST_PREPEND(pa_source_volume_change, s->thread_info.volume_changes, nc);
+    }
+    else {
+        PA_LLIST_INSERT_AFTER(pa_source_volume_change, s->thread_info.volume_changes, c, nc);
+    }
+
+    pa_log_debug("Volume going %s to %d at %llu", direction, pa_cvolume_avg(&nc->hw_volume), (long long unsigned) nc->at);
+
+    /* We can ignore volume events that came earlier but should happen later than this. */
+    PA_LLIST_FOREACH(c, nc->next) {
+        pa_log_debug("Volume change to %d at %llu was dropped", pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at);
+        pa_source_volume_change_free(c);
+    }
+    nc->next = NULL;
+    s->thread_info.volume_changes_tail = nc;
+}
+
+/* Called from the IO thread. */
+static void pa_source_volume_change_flush(pa_source *s) {
+    pa_source_volume_change *c = s->thread_info.volume_changes;
+    pa_assert(s);
+    s->thread_info.volume_changes = NULL;
+    s->thread_info.volume_changes_tail = NULL;
+    while (c) {
+        pa_source_volume_change *next = c->next;
+        pa_source_volume_change_free(c);
+        c = next;
+    }
+}
+
+/* Called from the IO thread. */
+pa_bool_t pa_source_volume_change_apply(pa_source *s, pa_usec_t *usec_to_next) {
+    pa_usec_t now = pa_rtclock_now();
+    pa_bool_t ret = FALSE;
+
+    pa_assert(s);
+    pa_assert(s->write_volume);
+
+    while (s->thread_info.volume_changes && now >= s->thread_info.volume_changes->at) {
+        pa_source_volume_change *c = s->thread_info.volume_changes;
+        PA_LLIST_REMOVE(pa_source_volume_change, s->thread_info.volume_changes, c);
+        pa_log_debug("Volume change to %d at %llu was written %llu usec late",
+                     pa_cvolume_avg(&c->hw_volume), (long long unsigned) c->at, (long long unsigned) (now - c->at));
+        ret = TRUE;
+        s->thread_info.current_hw_volume = c->hw_volume;
+        pa_source_volume_change_free(c);
+    }
+
+    if (s->write_volume && ret)
+        s->write_volume(s);
+
+    if (s->thread_info.volume_changes) {
+        if (usec_to_next)
+            *usec_to_next = s->thread_info.volume_changes->at - now;
+        if (pa_log_ratelimit(PA_LOG_DEBUG))
+            pa_log_debug("Next volume change in %lld usec", (long long) (s->thread_info.volume_changes->at - now));
+    }
+    else {
+        if (usec_to_next)
+            *usec_to_next = 0;
+        s->thread_info.volume_changes_tail = NULL;
+    }
+    return ret;
+}
+
+
 /* Called from the main thread */
 /* Gets the list of formats supported by the source. The members and idxset must
  * be freed by the caller. */
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 3cdc77a..902cc03 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -24,6 +24,7 @@
 ***/
 
 typedef struct pa_source pa_source;
+typedef struct pa_source_volume_change pa_source_volume_change;
 
 #include <inttypes.h>
 
@@ -80,12 +81,15 @@ struct pa_source {
     pa_volume_t base_volume; /* shall be constant */
     unsigned n_volume_steps; /* shall be constant */
 
-    pa_cvolume volume, soft_volume;
+    /* Also see http://pulseaudio.org/wiki/InternalVolumes */
+    pa_cvolume reference_volume; /* The volume exported and taken as reference base for relative source output volumes */
+    pa_cvolume real_volume;      /* The volume that the hardware is configured to  */
+    pa_cvolume soft_volume;      /* The internal software volume we apply to all PCM data while it passes through */
+
     pa_bool_t muted:1;
 
     pa_bool_t refresh_volume:1;
     pa_bool_t refresh_muted:1;
-
     pa_bool_t save_port:1;
     pa_bool_t save_volume:1;
     pa_bool_t save_muted:1;
@@ -115,6 +119,19 @@ struct pa_source {
      * will be sent to the IO thread instead. */
     void (*set_volume)(pa_source *s);         /* ditto */
 
+    /* Source drivers that set PA_SOURCE_SYNC_VOLUME must provide this
+     * callback. This callback is not used with source that do not set
+     * PA_SOURCE_SYNC_VOLUME. This is called from the IO thread when a
+     * pending hardware volume change has to be written to the
+     * hardware. The requested volume is passed to the callback
+     * implementation in s->thread_info.current_hw_volume.
+     *
+     * The call is done inside pa_source_volume_change_apply(), which is
+     * not called automatically - it is the driver's responsibility to
+     * schedule that function to be called at the right times in the
+     * IO thread. */
+    void (*write_volume)(pa_source *s);       /* ditto */
+
     /* Called when the mute setting is queried. Called from main loop
      * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message
      * will be sent to the IO thread instead. If refresh_mute is
@@ -160,6 +177,21 @@ struct pa_source {
         pa_usec_t max_latency; /* An upper limit for the latencies */
 
         pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */
+
+        /* Delayed volume change events are queued here. The events
+         * are stored in expiration order. The one expiring next is in
+         * the head of the list. */
+        PA_LLIST_HEAD(pa_source_volume_change, volume_changes);
+        pa_source_volume_change *volume_changes_tail;
+        /* This value is updated in pa_source_volume_change_apply() and
+         * used only by sources with PA_SOURCE_SYNC_VOLUME. */
+        pa_cvolume current_hw_volume;
+
+        /* The amount of usec volume up events are delayed and volume
+         * down events are made earlier. */
+        uint32_t volume_change_safety_margin;
+        /* Usec delay added to all volume change events, may be negative. */
+        int32_t volume_change_extra_delay;
 } thread_info;
 
     void *userdata;
@@ -172,7 +204,10 @@ typedef enum pa_source_message {
     PA_SOURCE_MESSAGE_ADD_OUTPUT,
     PA_SOURCE_MESSAGE_REMOVE_OUTPUT,
     PA_SOURCE_MESSAGE_GET_VOLUME,
+    PA_SOURCE_MESSAGE_SET_SHARED_VOLUME,
+    PA_SOURCE_MESSAGE_SET_VOLUME_SYNCED,
     PA_SOURCE_MESSAGE_SET_VOLUME,
+    PA_SOURCE_MESSAGE_SYNC_VOLUMES,
     PA_SOURCE_MESSAGE_GET_MUTE,
     PA_SOURCE_MESSAGE_SET_MUTE,
     PA_SOURCE_MESSAGE_GET_LATENCY,
@@ -186,6 +221,8 @@ typedef enum pa_source_message {
     PA_SOURCE_MESSAGE_GET_FIXED_LATENCY,
     PA_SOURCE_MESSAGE_GET_MAX_REWIND,
     PA_SOURCE_MESSAGE_SET_MAX_REWIND,
+    PA_SOURCE_MESSAGE_SET_PORT,
+    PA_SOURCE_MESSAGE_UPDATE_VOLUME_AND_MUTE,
     PA_SOURCE_MESSAGE_MAX
 } pa_source_message_t;
 
@@ -269,11 +306,14 @@ int pa_source_update_status(pa_source*s);
 int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause);
 int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause);
 
+/* Use this instead of checking s->flags & PA_SOURCE_FLAT_VOLUME directly. */
+pa_bool_t pa_source_flat_volume_enabled(pa_source *s);
+
 /* Is the source in passthrough mode? (that is, is this a monitor source for a sink
  * that has a passthrough sink input connected to it. */
 pa_bool_t pa_source_is_passthrough(pa_source *s);
 
-void pa_source_set_volume(pa_source *source, const pa_cvolume *volume, pa_bool_t save);
+void pa_source_set_volume(pa_source *source, const pa_cvolume *volume, pa_bool_t sendmsg, pa_bool_t save);
 const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh);
 
 void pa_source_set_mute(pa_source *source, pa_bool_t mute, pa_bool_t save);
@@ -315,6 +355,10 @@ void pa_source_set_max_rewind_within_thread(pa_source *s, size_t max_rewind);
 void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency);
 void pa_source_set_fixed_latency_within_thread(pa_source *s, pa_usec_t latency);
 
+void pa_source_update_volume_and_mute(pa_source *s);
+
+pa_bool_t pa_source_volume_change_apply(pa_source *s, pa_usec_t *usec_to_next);
+
 /*** To be called exclusively by source output drivers, from IO context */
 
 void pa_source_invalidate_requested_latency(pa_source *s, pa_bool_t dynamic);

commit fdf3a0881445560e962924a02319fc5cd2506231
Author: Colin Guthrie <colin at mageia.org>
Date:   Mon May 16 22:15:57 2011 +0100

    introspect: Get formats for sources
    
    This gets the list of supported formats for a source in
    pa_context_get_source_info*(). Also prints these in 'pactl list'.

diff --git a/PROTOCOL b/PROTOCOL
index 1097414..03a9cb5 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -254,3 +254,11 @@ New fields PA_COMMAND_CREATE_RECORD_STREAM:
 One new field in reply from PA_COMMAND_CREATE_RECORD_STREAM:
 
     format_info format
+
+New fields in reply from PA_COMMAND_GET_SOURCE_INFO (and thus
+PA_COMMAND_GET_SOURCE_INFO_LIST)
+
+    uint8_t n_formats
+    format_info format1
+    ...
+    format_info formatn
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index c400133..a781c14 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -405,11 +405,16 @@ pa_operation* pa_context_set_sink_port_by_name(pa_context *c, const char *name,
 static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_operation *o = userdata;
     int eol = 1;
+    pa_source_info i;
+    uint32_t j;
 
     pa_assert(pd);
     pa_assert(o);
     pa_assert(PA_REFCNT_VALUE(o) >= 1);
 
+    /* For safety incase someone use fail: outside the while loop below */
+    pa_zero(i);
+
     if (!o->context)
         goto finish;
 
@@ -421,11 +426,9 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
     } else {
 
         while (!pa_tagstruct_eof(t)) {
-            pa_source_info i;
             pa_bool_t mute;
             uint32_t flags;
             uint32_t state;
-            unsigned j;
             const char *ap;
 
             pa_zero(i);
@@ -460,9 +463,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
                 (o->context->version >= 16 &&
                  (pa_tagstruct_getu32(t, &i.n_ports)))) {
 
-                pa_context_fail(o->context, PA_ERR_PROTOCOL);
-                pa_proplist_free(i.proplist);
-                goto finish;
+                goto fail;
             }
 
             if (o->context->version >= 16) {
@@ -475,11 +476,7 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
                             pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 ||
                             pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) {
 
-                            pa_context_fail(o->context, PA_ERR_PROTOCOL);
-                            pa_xfree(i.ports[0]);
-                            pa_xfree(i.ports);
-                            pa_proplist_free(i.proplist);
-                            goto finish;
+                            goto fail;
                         }
 
                         i.ports[j] = &i.ports[0][j];
@@ -488,13 +485,8 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
                     i.ports[j] = NULL;
                 }
 
-                if (pa_tagstruct_gets(t, &ap) < 0) {
-                    pa_context_fail(o->context, PA_ERR_PROTOCOL);
-                    pa_xfree(i.ports[0]);
-                    pa_xfree(i.ports);
-                    pa_proplist_free(i.proplist);
-                    goto finish;
-                }
+                if (pa_tagstruct_gets(t, &ap) < 0)
+                    goto fail;
 
                 if (ap) {
                     for (j = 0; j < i.n_ports; j++)
@@ -505,6 +497,22 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
                 }
             }
 
+            if (o->context->version >= 22) {
+                uint8_t n_formats;
+                if (pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1)
+                    goto fail;
+
+                i.formats = pa_xnew0(pa_format_info*, n_formats);
+
+                for (j = 0; j < n_formats; j++) {
+                    i.n_formats++;
+                    i.formats[j] = pa_format_info_new();
+
+                    if (pa_tagstruct_get_format_info(t, i.formats[j]) < 0)
+                        goto fail;
+                }
+            }
+
             i.mute = (int) mute;
             i.flags = (pa_source_flags_t) flags;
             i.state = (pa_source_state_t) state;
@@ -514,6 +522,11 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
                 cb(o->context, &i, 0, o->userdata);
             }
 
+            if (i.formats) {
+                for (j = 0; j < i.n_formats; j++)
+                    pa_format_info_free(i.formats[j]);
+                pa_xfree(i.formats);
+            }
             if (i.ports) {
                 pa_xfree(i.ports[0]);
                 pa_xfree(i.ports);
@@ -530,6 +543,25 @@ static void context_get_source_info_callback(pa_pdispatch *pd, uint32_t command,
 finish:
     pa_operation_done(o);
     pa_operation_unref(o);
+    return;
+
+fail:
+    pa_assert(i.proplist);
+
+    pa_context_fail(o->context, PA_ERR_PROTOCOL);
+
+    if (i.formats) {
+        for (j = 0; j < i.n_formats; j++)
+            pa_format_info_free(i.formats[j]);
+        pa_xfree(i.formats);
+    }
+    if (i.ports) {
+        pa_xfree(i.ports[0]);
+        pa_xfree(i.ports);
+    }
+    pa_proplist_free(i.proplist);
+
+    goto finish;
 }
 
 pa_operation* pa_context_get_source_info_list(pa_context *c, pa_source_info_cb_t cb, void *userdata) {
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index a6b4a80..65f12d1 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -310,6 +310,8 @@ typedef struct pa_source_info {
     uint32_t n_ports;                   /**< Number of entries in port array \since 0.9.16 */
     pa_source_port_info** ports;        /**< Array of available ports, or NULL. Array is terminated by an entry set to NULL. The number of entries is stored in n_ports \since 0.9.16  */
     pa_source_port_info* active_port;   /**< Pointer to active port in the array, or NULL \since 0.9.16  */
+    uint8_t n_formats;                  /**< Number of formats supported by the source. \since 1.0 */
+    pa_format_info **formats;           /**< Array of formats supported by the source. \since 1.0 */
 } pa_source_info;
 
 /** Callback prototype for pa_context_get_source_info_by_name() and friends */
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 501ff37..3574ca9 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -3131,6 +3131,19 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
 
         pa_tagstruct_puts(t, source->active_port ? source->active_port->name : NULL);
     }
+
+    if (c->version >= 22) {
+        uint32_t i;
+        pa_format_info *f;
+        pa_idxset *formats = pa_source_get_formats(source);
+
+        pa_tagstruct_putu8(t, (uint8_t) pa_idxset_size(formats));
+        PA_IDXSET_FOREACH(f, formats, i) {
+            pa_tagstruct_put_format_info(t, f);
+        }
+
+        pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
+    }
 }
 
 static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 7be7049..894f8f7 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -333,7 +333,8 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
         cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
         v[PA_VOLUME_SNPRINT_MAX],
         vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
-        cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+        cm[PA_CHANNEL_MAP_SNPRINT_MAX],
+        f[PA_FORMAT_INFO_SNPRINT_MAX];
     char *pl;
 
     if (is_last < 0) {
@@ -418,6 +419,14 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int
     if (i->active_port)
         printf(_("\tActive Port: %s\n"),
                i->active_port->name);
+
+    if (i->formats) {
+        uint8_t j;
+
+        printf(_("\tFormats:\n"));
+        for (j = 0; j < i->n_formats; j++)
+            printf("\t\t%s\n", pa_format_info_snprint(f, sizeof(f), i->formats[j]));
+    }
 }
 
 static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {

commit 5d35375aa758fde7d9f3d6467e2506aca9784597
Author: Colin Guthrie <colin at mageia.org>
Date:   Tue May 17 21:56:10 2011 +0100

    capture: Add the passthrough format negotiation to capture streams.
    
    This helps to keep the API more symmetrical and also potentially
    allows support for passthrough monitor sources at some point in the future.

diff --git a/PROTOCOL b/PROTOCOL
index 4178274..1097414 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -241,3 +241,16 @@ One new field in reply from PA_COMMAND_GET_SINK_INPUT_INFO (and thus
 PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
 
     format_info format
+
+## v22, implemented by >= 1.0
+
+New fields PA_COMMAND_CREATE_RECORD_STREAM:
+
+    uint8_t n_formats
+    format_info format1
+    ...
+    format_info formatn
+
+One new field in reply from PA_COMMAND_CREATE_RECORD_STREAM:
+
+    format_info format
diff --git a/configure.ac b/configure.ac
index 88ad875..f95cbee 100644
--- a/configure.ac
+++ b/configure.ac
@@ -36,7 +36,7 @@ AC_SUBST(PA_MINOR, pa_minor)
 AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
 
 AC_SUBST(PA_API_VERSION, 12)
-AC_SUBST(PA_PROTOCOL_VERSION, 21)
+AC_SUBST(PA_PROTOCOL_VERSION, 22)
 
 # The stable ABI for client applications, for the version info x:y:z
 # always will hold y=z
diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
index 96eb9ef..3d22ef8 100644
--- a/src/modules/echo-cancel/module-echo-cancel.c
+++ b/src/modules/echo-cancel/module-echo-cancel.c
@@ -1599,7 +1599,7 @@ int pa__init(pa_module*m) {
     pa_source_output_new_data_init(&source_output_data);
     source_output_data.driver = __FILE__;
     source_output_data.module = m;
-    source_output_data.source = source_master;
+    pa_source_output_new_data_set_source(&source_output_data, source_master, FALSE);
     source_output_data.destination_source = u->source;
     /* FIXME
        source_output_data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; */
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 4138794..272fad2 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -871,10 +871,9 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
             if (PA_INVALID_INDEX != device_index) {
                 pa_source *source;
 
-                if ((source = pa_idxset_get_by_index(u->core->sources, device_index))) {
-                    new_data->source = source;
-                    new_data->save_source = FALSE;
-                }
+                if ((source = pa_idxset_get_by_index(u->core->sources, device_index)))
+                    if (!pa_source_output_new_data_set_source(new_data, source, FALSE))
+                        pa_log_debug("Not restoring device for stream because no supported format was found");
             }
         }
     }
diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c
index 9038562..2f9bba4 100644
--- a/src/modules/module-intended-roles.c
+++ b/src/modules/module-intended-roles.c
@@ -162,8 +162,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
     /* Prefer the default source over any other source, just in case... */
     if ((def = pa_namereg_get_default_source(c)))
         if (role_match(def->proplist, role)) {
-            new_data->source = def;
-            new_data->save_source = FALSE;
+            pa_source_output_new_data_set_source(new_data, def, FALSE);
             return PA_HOOK_OK;
         }
 
@@ -179,8 +178,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
 
         /* @todo: favour the highest priority device, not the first one we find? */
         if (role_match(s->proplist, role)) {
-            new_data->source = s;
-            new_data->save_source = FALSE;
+            pa_source_output_new_data_set_source(new_data, s, FALSE);
             return PA_HOOK_OK;
         }
     }
diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index a5b08bd..936133f 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -755,7 +755,7 @@ int pa__init(pa_module *m) {
     pa_source_output_new_data_init(&source_output_data);
     source_output_data.driver = __FILE__;
     source_output_data.module = m;
-    source_output_data.source = source;
+    pa_source_output_new_data_set_source(&source_output_data, source, FALSE);
 
     if ((n = pa_modargs_get_value(ma, "source_output_name", NULL)))
         pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_NAME, n);
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index fa2c021..617de5b 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -1217,7 +1217,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
         if (source_output->save_source) {
             pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
-            entry.device_valid = source_output->save_source;
+            entry.device_valid = TRUE;
 
             device_updated = !created_new_entry && (!old->device_valid || !pa_streq(entry.device, old->device));
 
@@ -1399,8 +1399,7 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
            interfere with that */
         if (s && PA_SOURCE_IS_LINKED(pa_source_get_state(s))) {
             pa_log_info("Restoring device for stream %s.", name);
-            new_data->source = s;
-            new_data->save_source = TRUE;
+            pa_source_output_new_data_set_source(new_data, s, TRUE);
         }
 
         pa_xfree(e);
diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c
index 835cf3c..170fa4e 100644
--- a/src/modules/module-virtual-source.c
+++ b/src/modules/module-virtual-source.c
@@ -630,7 +630,7 @@ int pa__init(pa_module*m) {
     pa_source_output_new_data_init(&source_output_data);
     source_output_data.driver = __FILE__;
     source_output_data.module = m;
-    source_output_data.source = master;
+    pa_source_output_new_data_set_source(&source_output_data, master, FALSE);
     source_output_data.destination_source = u->source;
     /* FIXME
        source_output_data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; */
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index f53020d..e0fed99 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -325,7 +325,7 @@ int pa__init(pa_module*m) {
     pa_proplist_setf(data.proplist, "rtp.ttl", "%lu", (unsigned long) ttl);
     data.driver = __FILE__;
     data.module = m;
-    data.source = s;
+    pa_source_output_new_data_set_source(&data, s, FALSE);
     pa_source_output_new_data_set_sample_spec(&data, &ss);
     pa_source_output_new_data_set_channel_map(&data, &cm);
     data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND;
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index aeb897c..3293684 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -1092,7 +1092,9 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
             s->timing_info.configured_sink_usec = usec;
     }
 
-    if (s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK) {
+    if ((s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK)
+        || s->context->version >= 22) {
+
         pa_format_info *f = pa_format_info_new();
         pa_tagstruct_get_format_info(t, f);
 
@@ -1319,26 +1321,18 @@ static int create_stream(
         pa_tagstruct_put_boolean(t, flags & PA_STREAM_FAIL_ON_SUSPEND);
     }
 
-    if (s->context->version >= 17) {
-
-        if (s->direction == PA_STREAM_PLAYBACK)
-            pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
-
-    }
-
-    if (s->context->version >= 18) {
+    if (s->context->version >= 17 && s->direction == PA_STREAM_PLAYBACK)
+        pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME);
 
-        if (s->direction == PA_STREAM_PLAYBACK)
-            pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
-    }
+    if (s->context->version >= 18 && s->direction == PA_STREAM_PLAYBACK)
+        pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
 
-    if (s->context->version >= 21) {
+    if ((s->context->version >= 21 && s->direction == PA_STREAM_PLAYBACK)
+        || s->context->version >= 22) {
 
-        if (s->direction == PA_STREAM_PLAYBACK) {
-            pa_tagstruct_putu8(t, s->n_formats);
-            for (i = 0; i < s->n_formats; i++)
-                pa_tagstruct_put_format_info(t, s->req_formats[i]);
-        }
+        pa_tagstruct_putu8(t, s->n_formats);
+        for (i = 0; i < s->n_formats; i++)
+            pa_tagstruct_put_format_info(t, s->req_formats[i]);
     }
 
     pa_pstream_send_tagstruct(s->context->pstream, t);
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index c2af77c..edca96a 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -524,7 +524,7 @@ static int esd_proto_stream_record(connection *c, esd_proto_t request, const voi
     sdata.driver = __FILE__;
     sdata.module = c->options->module;
     sdata.client = c->client;
-    sdata.source = source;
+    pa_source_output_new_data_set_source(&sdata, source, FALSE);
     pa_source_output_new_data_set_sample_spec(&sdata, &ss);
 
     pa_source_output_new(&c->source_output, c->protocol->core, &sdata);
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
index 83067f8..1de0434 100644
--- a/src/pulsecore/protocol-http.c
+++ b/src/pulsecore/protocol-http.c
@@ -560,7 +560,7 @@ static void handle_listen_prefix(struct connection *c, const char *source_name)
     data.driver = __FILE__;
     data.module = c->module;
     data.client = c->client;
-    data.source = source;
+    pa_source_output_new_data_set_source(&data, source, FALSE);
     pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
     pa_source_output_new_data_set_sample_spec(&data, &ss);
     pa_source_output_new_data_set_channel_map(&data, &cm);
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index b16793d..501ff37 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -629,13 +629,14 @@ static record_stream* record_stream_new(
         pa_source *source,
         pa_sample_spec *ss,
         pa_channel_map *map,
-        pa_bool_t peak_detect,
+        pa_idxset *formats,
         pa_buffer_attr *attr,
         pa_source_output_flags_t flags,
         pa_proplist *p,
         pa_bool_t adjust_latency,
-        pa_sink_input *direct_on_input,
         pa_bool_t early_requests,
+        pa_bool_t peak_detect,
+        pa_sink_input *direct_on_input,
         int *ret) {
 
     record_stream *s;
@@ -653,10 +654,15 @@ static record_stream* record_stream_new(
     data.driver = __FILE__;
     data.module = c->options->module;
     data.client = c->client;
-    data.source = source;
+    if (source)
+        pa_source_output_new_data_set_source(&data, source, TRUE);
+    if (pa_sample_spec_valid(ss))
+        pa_source_output_new_data_set_sample_spec(&data, ss);
+    if (pa_channel_map_valid(map))
+        pa_source_output_new_data_set_channel_map(&data, map);
+    if (formats)
+        pa_source_output_new_data_set_formats(&data, formats);
     data.direct_on_input = direct_on_input;
-    pa_source_output_new_data_set_sample_spec(&data, ss);
-    pa_source_output_new_data_set_channel_map(&data, map);
     if (peak_detect)
         data.resample_method = PA_RESAMPLER_PEAKS;
     data.flags = flags;
@@ -1028,13 +1034,13 @@ static playback_stream* playback_stream_new(
         pa_cvolume *volume,
         pa_bool_t muted,
         pa_bool_t muted_set,
-        uint32_t syncid,
-        uint32_t *missing,
         pa_sink_input_flags_t flags,
         pa_proplist *p,
         pa_bool_t adjust_latency,
         pa_bool_t early_requests,
         pa_bool_t relative_volume,
+        uint32_t syncid,
+        uint32_t *missing,
         int *ret) {
 
     /* Note: This function takes ownership of the 'formats' param, so we need
@@ -1900,6 +1906,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         adjust_latency = FALSE,
         early_requests = FALSE,
         dont_inhibit_auto_suspend = FALSE,
+        volume_set = TRUE,
         muted_set = FALSE,
         fail_on_suspend = FALSE,
         relative_volume = FALSE,
@@ -1907,7 +1914,6 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
 
     pa_sink_input_flags_t flags = 0;
     pa_proplist *p = NULL;
-    pa_bool_t volume_set = TRUE;
     int ret = PA_ERR_INVALID;
     uint8_t n_formats = 0;
     pa_format_info *format;
@@ -2081,7 +2087,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
      * flag. For older versions we synthesize it here */
     muted_set = muted_set || muted;
 
-    s = playback_stream_new(c, sink, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, syncid, &missing, flags, p, adjust_latency, early_requests, relative_volume, &ret);
+    s = playback_stream_new(c, sink, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, flags, p, adjust_latency, early_requests, relative_volume, syncid, &missing, &ret);
     /* We no longer own the formats idxset */
     formats = NULL;
 
@@ -2222,12 +2228,18 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         peak_detect = FALSE,
         early_requests = FALSE,
         dont_inhibit_auto_suspend = FALSE,
-        fail_on_suspend = FALSE;
+        fail_on_suspend = FALSE,
+        passthrough = FALSE;
+
     pa_source_output_flags_t flags = 0;
-    pa_proplist *p;
+    pa_proplist *p = NULL;
     uint32_t direct_on_input_idx = PA_INVALID_INDEX;
     pa_sink_input *direct_on_input = NULL;
     int ret = PA_ERR_INVALID;
+    uint8_t n_formats = 0;
+    pa_format_info *format;
+    pa_idxset *formats = NULL;
+    uint32_t i;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
@@ -2242,17 +2254,15 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         pa_tagstruct_getu32(t, &attr.maxlength) < 0 ||
         pa_tagstruct_get_boolean(t, &corked) < 0 ||
         pa_tagstruct_getu32(t, &attr.fragsize) < 0) {
+
         protocol_error(c);
-        return;
+        goto finish;
     }
 
-    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
-    CHECK_VALIDITY(c->pstream, !source_name || pa_namereg_is_valid_name_or_wildcard(source_name, PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, source_index == PA_INVALID_INDEX || !source_name, tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, !source_name || source_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
+    CHECK_VALIDITY_GOTO(c->pstream, c->authorized, tag, PA_ERR_ACCESS, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, !source_name || pa_namereg_is_valid_name_or_wildcard(source_name, PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, source_index == PA_INVALID_INDEX || !source_name, tag, PA_ERR_INVALID, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, !source_name || source_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID, finish);
 
     p = pa_proplist_new();
 
@@ -2271,8 +2281,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
             pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
 
             protocol_error(c);
-            pa_proplist_free(p);
-            return;
+            goto finish;
         }
     }
 
@@ -2282,9 +2291,9 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
             pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
             pa_tagstruct_get_proplist(t, p) < 0 ||
             pa_tagstruct_getu32(t, &direct_on_input_idx) < 0) {
+
             protocol_error(c);
-            pa_proplist_free(p);
-            return;
+            goto finish;
         }
     }
 
@@ -2292,8 +2301,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
 
         if (pa_tagstruct_get_boolean(t, &early_requests) < 0) {
             protocol_error(c);
-            pa_proplist_free(p);
-            return;
+            goto finish;
         }
     }
 
@@ -2301,32 +2309,59 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
 
         if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
             pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
+
             protocol_error(c);
-            pa_proplist_free(p);
-            return;
+            goto finish;
+        }
+    }
+
+    if (c->version >= 22) {
+
+        if (pa_tagstruct_getu8(t, &n_formats) < 0) {
+            protocol_error(c);
+            goto finish;
+        }
+
+        if (n_formats)
+            formats = pa_idxset_new(NULL, NULL);
+
+        for (i = 0; i < n_formats; i++) {
+            format = pa_format_info_new();
+            if (pa_tagstruct_get_format_info(t, format) < 0) {
+                protocol_error(c);
+                goto finish;
+            }
+            pa_idxset_put(formats, format, NULL);
         }
     }
 
+    if (n_formats == 0) {
+        CHECK_VALIDITY_GOTO(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID, finish);
+        CHECK_VALIDITY_GOTO(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID, finish);
+    } else {
+        PA_IDXSET_FOREACH(format, formats, i) {
+            CHECK_VALIDITY_GOTO(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID, finish);
+        }
+    }
+
+
     if (!pa_tagstruct_eof(t)) {
         protocol_error(c);
-        pa_proplist_free(p);
-        return;
+        goto finish;
     }
 
     if (source_index != PA_INVALID_INDEX) {
 
         if (!(source = pa_idxset_get_by_index(c->protocol->core->sources, source_index))) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
-            pa_proplist_free(p);
-            return;
+            goto finish;
         }
 
     } else if (source_name) {
 
         if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
-            pa_proplist_free(p);
-            return;
+            goto finish;
         }
     }
 
@@ -2334,8 +2369,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
 
         if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
-            pa_proplist_free(p);
-            return;
+            goto finish;
         }
     }
 
@@ -2349,12 +2383,12 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
         (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
         (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) |
         (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
-        (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0);
+        (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0) |
+        (passthrough ? PA_SOURCE_OUTPUT_PASSTHROUGH : 0);
 
-    s = record_stream_new(c, source, &ss, &map, peak_detect, &attr, flags, p, adjust_latency, direct_on_input, early_requests, &ret);
-    pa_proplist_free(p);
+    s = record_stream_new(c, source, &ss, &map, formats, &attr, flags, p, adjust_latency, early_requests, peak_detect, direct_on_input, &ret);
 
-    CHECK_VALIDITY(c->pstream, s, tag, ret);
+    CHECK_VALIDITY_GOTO(c->pstream, s, tag, ret, finish);
 
     reply = reply_new(tag);
     pa_tagstruct_putu32(reply, s->index);
@@ -2385,7 +2419,24 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin
     if (c->version >= 13)
         pa_tagstruct_put_usec(reply, s->configured_source_latency);
 
+    if (c->version >= 22) {
+        /* Send back the format we negotiated */
+        if (s->source_output->format)
+            pa_tagstruct_put_format_info(reply, s->source_output->format);
+        else {
+            pa_format_info *f = pa_format_info_new();
+            pa_tagstruct_put_format_info(reply, f);
+            pa_format_info_free(f);
+        }
+    }
+
     pa_pstream_send_tagstruct(c->pstream, reply);
+
+finish:
+    if (p)
+        pa_proplist_free(p);
+    if (formats)
+        pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
 }
 
 static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
index c1aaa81..41a3cc5 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -593,7 +593,7 @@ void pa_simple_protocol_connect(pa_simple_protocol *p, pa_iochannel *io, pa_simp
         data.driver = __FILE__;
         data.module = o->module;
         data.client = c->client;
-        data.source = source;
+        pa_source_output_new_data_set_source(&data, source, FALSE);
         pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
         pa_source_output_new_data_set_sample_spec(&data, &o->sample_spec);
 
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 963ef06..61e0695 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -30,6 +30,7 @@
 #include <pulse/utf8.h>
 #include <pulse/xmalloc.h>
 #include <pulse/util.h>
+#include <pulse/internal.h>
 
 #include <pulsecore/sample-util.h>
 #include <pulsecore/core-subscribe.h>
@@ -69,9 +70,80 @@ void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data,
         data->channel_map = *map;
 }
 
+pa_bool_t pa_source_output_new_data_is_passthrough(pa_source_output_new_data *data) {
+    pa_assert(data);
+
+    if (PA_LIKELY(data->format) && PA_UNLIKELY(!pa_format_info_is_pcm(data->format)))
+        return TRUE;
+
+    if (PA_UNLIKELY(data->flags & PA_SOURCE_OUTPUT_PASSTHROUGH))
+        return TRUE;
+
+    return FALSE;
+}
+
+pa_bool_t pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_source *s, pa_bool_t save) {
+    pa_bool_t ret = TRUE;
+    pa_idxset *formats = NULL;
+
+    pa_assert(data);
+    pa_assert(s);
+
+    if (!data->req_formats) {
+        /* We're not working with the extended API */
+        data->source = s;
+        data->save_source = save;
+    } else {
+        /* Extended API: let's see if this source supports the formats the client would like */
+        formats = pa_source_check_formats(s, data->req_formats);
+
+        if (formats && !pa_idxset_isempty(formats)) {
+            /* Source supports at least one of the requested formats */
+            data->source = s;
+            data->save_source = save;
+            if (data->nego_formats)
+                pa_idxset_free(data->nego_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
+            data->nego_formats = formats;
+        } else {
+            /* Source doesn't support any of the formats requested by the client */
+            if (formats)
+                pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
+            ret = FALSE;
+        }
+    }
+
+    return ret;
+}
+
+pa_bool_t pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_idxset *formats) {
+    pa_assert(data);
+    pa_assert(formats);
+
+    if (data->req_formats)
+        pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
+
+    data->req_formats = formats;
+
+    if (data->source) {
+        /* Trigger format negotiation */
+        return pa_source_output_new_data_set_source(data, data->source, data->save_source);
+    }
+
+    return TRUE;
+}
+
 void pa_source_output_new_data_done(pa_source_output_new_data *data) {
     pa_assert(data);
 
+    if (data->req_formats)
+        pa_idxset_free(data->req_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
+
+    if (data->nego_formats)
+        pa_idxset_free(data->nego_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
+
+    if (data->format)
+        pa_format_info_free(data->format);
+
     pa_proplist_free(data->proplist);
 }
 
@@ -106,8 +178,11 @@ int pa_source_output_new(
     pa_source_output *o;
     pa_resampler *resampler = NULL;
     char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    pa_channel_map original_cm;
     int r;
     char *pt;
+    pa_sample_spec ss;
+    pa_channel_map map;
 
     pa_assert(_o);
     pa_assert(core);
@@ -117,14 +192,44 @@ int pa_source_output_new(
     if (data->client)
         pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
 
+    if (!data->req_formats) {
+        /* From this point on, we want to work only with formats, and get back
+         * to using the sample spec and channel map after all decisions w.r.t.
+         * routing are complete. */
+        pa_idxset *tmp = pa_idxset_new(NULL, NULL);
+        pa_format_info *f = pa_format_info_from_sample_spec(&data->sample_spec, &data->channel_map);
+        pa_idxset_put(tmp, f, NULL);
+        pa_source_output_new_data_set_formats(data, tmp);
+    }
+
     if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0)
         return r;
 
     pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
 
-    if (!data->source) {
-        data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE);
-        data->save_source = FALSE;
+    if (!data->source)
+        pa_source_output_new_data_set_source(data, pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE), FALSE);
+
+    /* Routing's done, we have a source. Now let's fix the format and set up the
+     * sample spec */
+
+    /* If something didn't pick a format for us, pick the top-most format since
+     * we assume this is sorted in priority order */
+    if (!data->format && data->nego_formats && !pa_idxset_isempty(data->nego_formats))
+        data->format = pa_format_info_copy(pa_idxset_first(data->nego_formats, NULL));
+
+    pa_return_val_if_fail(data->format, -PA_ERR_NOTSUPPORTED);
+
+    /* Now populate the sample spec and format according to the final
+     * format that we've negotiated */
+    if (PA_LIKELY(data->format->encoding == PA_ENCODING_PCM)) {
+        pa_return_val_if_fail(pa_format_info_to_sample_spec(data->format, &ss, &map), -PA_ERR_INVALID);
+        pa_source_output_new_data_set_sample_spec(data, &ss);
+        if (pa_channel_map_valid(&map))
+            pa_source_output_new_data_set_channel_map(data, &map);
+    } else {
+        pa_return_val_if_fail(pa_format_info_to_sample_spec_fake(data->format, &ss), -PA_ERR_INVALID);
+        pa_source_output_new_data_set_sample_spec(data, &ss);
     }
 
     pa_return_val_if_fail(data->source, -PA_ERR_NOENTITY);
@@ -143,7 +248,6 @@ int pa_source_output_new(
             pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
     }
 
-    pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID);
     pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
 
     if (data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT)
@@ -152,6 +256,8 @@ int pa_source_output_new(
     if (data->flags & PA_SOURCE_OUTPUT_FIX_RATE)
         data->sample_spec.rate = data->source->sample_spec.rate;
 
+    original_cm = data->channel_map;
+
     if (data->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
         data->sample_spec.channels = data->source->sample_spec.channels;
         data->channel_map = data->source->channel_map;
@@ -183,18 +289,19 @@ int pa_source_output_new(
         !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) ||
         !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) {
 
-        if (!(resampler = pa_resampler_new(
-                      core->mempool,
-                      &data->source->sample_spec, &data->source->channel_map,
-                      &data->sample_spec, &data->channel_map,
-                      data->resample_method,
-                      ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
-                      ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
-                      (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
-                      (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
-            pa_log_warn("Unsupported resampling operation.");
-            return -PA_ERR_NOTSUPPORTED;
-        }
+        if (!pa_source_output_new_data_is_passthrough(data)) /* no resampler for passthrough content */
+            if (!(resampler = pa_resampler_new(
+                        core->mempool,
+                        &data->source->sample_spec, &data->source->channel_map,
+                        &data->sample_spec, &data->channel_map,
+                        data->resample_method,
+                        ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+                        ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+                        (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+                        (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) {
+                pa_log_warn("Unsupported resampling operation.");
+                return -PA_ERR_NOTSUPPORTED;
+            }
     }
 
     o = pa_msgobject_new(pa_source_output);
@@ -211,15 +318,14 @@ int pa_source_output_new(
     o->destination_source = data->destination_source;
     o->client = data->client;
 
-    o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
     o->requested_resample_method = data->resample_method;
+    o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
     o->sample_spec = data->sample_spec;
     o->channel_map = data->channel_map;
+    o->format = pa_format_info_copy(data->format);
 
     o->direct_on_input = data->direct_on_input;
 
-    o->save_source = data->save_source;
-
     reset_callbacks(o);
     o->userdata = NULL;
 
@@ -373,6 +479,9 @@ static void source_output_free(pa_object* mo) {
     if (o->thread_info.resampler)
         pa_resampler_free(o->thread_info.resampler);
 
+    if (o->format)
+        pa_format_info_free(o->format);
+
     if (o->proplist)
         pa_proplist_free(o->proplist);
 
@@ -677,6 +786,19 @@ void pa_source_output_set_name(pa_source_output *o, const char *name) {
     }
 }
 
+/* Called from main or I/O context */
+pa_bool_t pa_source_output_is_passthrough(pa_source_output *o) {
+    pa_source_output_assert_ref(o);
+
+    if (PA_UNLIKELY(!pa_format_info_is_pcm(o->format)))
+        return TRUE;
+
+    if (PA_UNLIKELY(o->flags & PA_SOURCE_OUTPUT_PASSTHROUGH))
+        return TRUE;
+
+    return FALSE;
+}
+
 /* Called from main thread */
 void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
     pa_source_output_assert_ref(o);
@@ -782,7 +904,18 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
     pa_source_assert_ref(dest);
 
     if (!pa_source_output_may_move_to(o, dest))
-        return -1;
+        return -PA_ERR_NOTSUPPORTED;
+
+    if (pa_source_output_is_passthrough(o) && !pa_source_check_format(dest, o->format)) {
+        pa_proplist *p = pa_proplist_new();
+        pa_log_debug("New source doesn't support stream format, sending format-changed and killing");
+        /* Tell the client what device we want to be on if it is going to
+         * reconnect */
+        pa_proplist_sets(p, "device", dest->name);
+        pa_source_output_send_event(o, PA_STREAM_EVENT_FORMAT_LOST, p);
+        pa_proplist_free(p);
+        return -PA_ERR_NOTSUPPORTED;
+    }
 
     if (o->thread_info.resampler &&
         pa_sample_spec_equal(pa_resampler_input_sample_spec(o->thread_info.resampler), &dest->sample_spec) &&
@@ -795,7 +928,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
              !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) ||
              !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) {
 
-        /* Okey, we need a new resampler for the new source */
+        /* Okay, we need a new resampler for the new source */
 
         if (!(new_resampler = pa_resampler_new(
                       o->core->mempool,
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index f16f952..88de4ae 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -28,6 +28,7 @@ typedef struct pa_source_output pa_source_output;
 
 #include <pulse/sample.h>
 #include <pulsecore/source.h>
+#include <pulse/format.h>
 #include <pulsecore/memblockq.h>
 #include <pulsecore/resampler.h>
 #include <pulsecore/module.h>
@@ -56,7 +57,8 @@ typedef enum pa_source_output_flags {
     PA_SOURCE_OUTPUT_FIX_CHANNELS = 128,
     PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND = 256,
     PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND = 512,
-    PA_SOURCE_OUTPUT_KILL_ON_SUSPEND = 1024
+    PA_SOURCE_OUTPUT_KILL_ON_SUSPEND = 1024,
+    PA_SOURCE_OUTPUT_PASSTHROUGH = 2048
 } pa_source_output_flags_t;
 
 struct pa_source_output {
@@ -82,6 +84,7 @@ struct pa_source_output {
 
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
+    pa_format_info *format;
 
     /* if TRUE then the source we are connected to is worth
      * remembering, i.e. was explicitly chosen by the user and not
@@ -218,6 +221,9 @@ typedef struct pa_source_output_new_data {
 
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
+    pa_format_info *format;
+    pa_idxset *req_formats;
+    pa_idxset *nego_formats;
 
     pa_bool_t sample_spec_is_set:1;
     pa_bool_t channel_map_is_set:1;
@@ -228,6 +234,9 @@ typedef struct pa_source_output_new_data {
 pa_source_output_new_data* pa_source_output_new_data_init(pa_source_output_new_data *data);
 void pa_source_output_new_data_set_sample_spec(pa_source_output_new_data *data, const pa_sample_spec *spec);
 void pa_source_output_new_data_set_channel_map(pa_source_output_new_data *data, const pa_channel_map *map);
+pa_bool_t pa_source_output_new_data_is_passthrough(pa_source_output_new_data *data);
+pa_bool_t pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_source *s, pa_bool_t save);
+pa_bool_t pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_idxset *formats);
 void pa_source_output_new_data_done(pa_source_output_new_data *data);
 
 /* To be called by the implementing module only */
@@ -257,6 +266,8 @@ void pa_source_output_kill(pa_source_output*o);
 
 pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency);
 
+pa_bool_t pa_source_output_is_passthrough(pa_source_output *o);
+
 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 15a5b8d..587291a 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -32,6 +32,7 @@
 #include <pulse/xmalloc.h>
 #include <pulse/timeval.h>
 #include <pulse/util.h>
+#include <pulse/internal.h>
 
 #include <pulsecore/core-util.h>
 #include <pulsecore/source-output.h>
@@ -130,6 +131,7 @@ static void reset_callbacks(pa_source *s) {
     s->set_mute = NULL;
     s->update_requested_latency = NULL;
     s->set_port = NULL;
+    s->get_formats = NULL;
 }
 
 /* Called from main context */
@@ -754,6 +756,15 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
     return usec;
 }
 
+/* Called from main context */
+pa_bool_t pa_source_is_passthrough(pa_source *s) {
+
+    pa_source_assert_ref(s);
+
+    /* NB Currently only monitor sources support passthrough mode */
+    return (s->monitor_of && pa_sink_is_passthrough(s->monitor_of));
+}
+
 /* Called from main thread */
 void pa_source_set_volume(
         pa_source *s,
@@ -1575,3 +1586,84 @@ int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) {
 
     return 0;
 }
+
+/* Called from the main thread */
+/* Gets the list of formats supported by the source. The members and idxset must
+ * be freed by the caller. */
+pa_idxset* pa_source_get_formats(pa_source *s) {
+    pa_idxset *ret;
+
+    pa_assert(s);
+
+    if (s->get_formats) {
+        /* Source supports format query, all is good */
+        ret = s->get_formats(s);
+    } else {
+        /* Source doesn't support format query, so assume it does PCM */
+        pa_format_info *f = pa_format_info_new();
+        f->encoding = PA_ENCODING_PCM;
+
+        ret = pa_idxset_new(NULL, NULL);
+        pa_idxset_put(ret, f, NULL);
+    }
+
+    return ret;
+}
+
+/* Called from the main thread */
+/* Checks if the source can accept this format */
+pa_bool_t pa_source_check_format(pa_source *s, pa_format_info *f)
+{
+    pa_idxset *formats = NULL;
+    pa_bool_t ret = FALSE;
+
+    pa_assert(s);
+    pa_assert(f);
+
+    formats = pa_source_get_formats(s);
+
+    if (formats) {
+        pa_format_info *finfo_device;
+        uint32_t i;
+
+        PA_IDXSET_FOREACH(finfo_device, formats, i) {
+            if (pa_format_info_is_compatible(finfo_device, f)) {
+                ret = TRUE;
+                break;
+            }
+        }
+
+        pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
+    }
+
+    return ret;
+}
+
+/* Called from the main thread */
+/* Calculates the intersection between formats supported by the source and
+ * in_formats, and returns these, in the order of the source's formats. */
+pa_idxset* pa_source_check_formats(pa_source *s, pa_idxset *in_formats) {
+    pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *source_formats = NULL;
+    pa_format_info *f_source, *f_in;
+    uint32_t i, j;
+
+    pa_assert(s);
+
+    if (!in_formats || pa_idxset_isempty(in_formats))
+        goto done;
+
+    source_formats = pa_source_get_formats(s);
+
+    PA_IDXSET_FOREACH(f_source, source_formats, i) {
+        PA_IDXSET_FOREACH(f_in, in_formats, j) {
+            if (pa_format_info_is_compatible(f_source, f_in))
+                pa_idxset_put(out_formats, pa_format_info_copy(f_in), NULL);
+        }
+    }
+
+done:
+    if (source_formats)
+        pa_idxset_free(source_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
+
+    return out_formats;
+}
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 9e8e2ad..3cdc77a 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -108,31 +108,35 @@ struct pa_source {
      * context. If this is NULL a PA_SOURCE_MESSAGE_GET_VOLUME message
      * will be sent to the IO thread instead. If refresh_volume is
      * FALSE neither this function is called nor a message is sent. */
-    void (*get_volume)(pa_source *s);         /* dito */
+    void (*get_volume)(pa_source *s);         /* ditto */
 
     /* Called when the volume shall be changed. Called from main loop
      * context. If this is NULL a PA_SOURCE_MESSAGE_SET_VOLUME message
      * will be sent to the IO thread instead. */
-    void (*set_volume)(pa_source *s);         /* dito */
+    void (*set_volume)(pa_source *s);         /* ditto */
 
     /* Called when the mute setting is queried. Called from main loop
      * context. If this is NULL a PA_SOURCE_MESSAGE_GET_MUTE message
      * will be sent to the IO thread instead. If refresh_mute is
      * FALSE neither this function is called nor a message is sent.*/
-    void (*get_mute)(pa_source *s);           /* dito */
+    void (*get_mute)(pa_source *s);           /* ditto */
 
     /* Called when the mute setting shall be changed. Called from main
      * loop context. If this is NULL a PA_SOURCE_MESSAGE_SET_MUTE
      * message will be sent to the IO thread instead. */
-    void (*set_mute)(pa_source *s);           /* dito */
+    void (*set_mute)(pa_source *s);           /* ditto */
 
     /* Called when a the requested latency is changed. Called from IO
      * thread context. */
-    void (*update_requested_latency)(pa_source *s); /* dito */
+    void (*update_requested_latency)(pa_source *s); /* ditto */
 
     /* Called whenever the port shall be changed. Called from main
      * thread. */
-    int (*set_port)(pa_source *s, pa_device_port *port); /*dito */
+    int (*set_port)(pa_source *s, pa_device_port *port); /*ditto */
+
+    /* Called to get the list of formats supported by the source, sorted
+     * in descending order of preference. */
+    pa_idxset* (*get_formats)(pa_source *s); /* ditto */
 
     /* Contains copies of the above data so that the real-time worker
      * thread can work without access locking */
@@ -265,6 +269,10 @@ int pa_source_update_status(pa_source*s);
 int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause);
 int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause);
 
+/* Is the source in passthrough mode? (that is, is this a monitor source for a sink
+ * that has a passthrough sink input connected to it. */
+pa_bool_t pa_source_is_passthrough(pa_source *s);
+
 void pa_source_set_volume(pa_source *source, const pa_cvolume *volume, pa_bool_t save);
 const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh);
 
@@ -285,6 +293,10 @@ pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q);
 void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save);
 void pa_source_move_all_fail(pa_queue *q);
 
+pa_idxset* pa_source_get_formats(pa_source *s);
+pa_bool_t pa_source_check_format(pa_source *s, pa_format_info *f);
+pa_idxset* pa_source_check_formats(pa_source *s, pa_idxset *in_formats);
+
 /*** To be called exclusively by the source driver, from IO context */
 
 void pa_source_post(pa_source*s, const pa_memchunk *chunk);

commit 30597b7c2747a52b1025e1172d73825e148fdec9
Author: Colin Guthrie <colin at mageia.org>
Date:   Tue May 17 21:48:29 2011 +0100

    def: Add some flags for source outputs.
    
    These flags will be required in upcoming work to integrate format and volume
    support for source outputs.

diff --git a/src/pulse/def.h b/src/pulse/def.h
index e01e667..16b2e59 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -839,9 +839,31 @@ typedef enum pa_source_flags {
     /**< Volume can be translated to dB with pa_sw_volume_to_dB()
      * \since 0.9.11 */
 
-    PA_SOURCE_DYNAMIC_LATENCY = 0x0040U
+    PA_SOURCE_DYNAMIC_LATENCY = 0x0040U,
     /**< The latency can be adjusted dynamically depending on the
      * needs of the connected streams. \since 0.9.15 */
+
+    PA_SOURCE_FLAT_VOLUME = 0x0080U,
+    /**< This source is in flat volume mode, i.e. always the maximum of
+     * the volume of all connected outputs. \since 1.0 */
+
+    PA_SOURCE_PASSTHROUGH = 0x0100U,
+    /**< This sink has support for passthrough mode. The data will be left
+     * as is and not reformatted, resampled, mixed.
+     * \since 1.0 */
+
+    PA_SOURCE_SYNC_VOLUME = 0x0200U,
+    /**< The HW volume changes are syncronized with SW volume.
+     * \since 1.0 */
+
+/** \cond fulldocs */
+    /* PRIVATE: Server-side values -- do not try to use these at client-side.
+     * The server will filter out these flags anyway, so you should never see
+     * these flags in sources. */
+
+    PA_SOURCE_SHARE_VOLUME_WITH_MASTER = 0x0400U,
+    /**< This source shares the volume with the master source (used by some filter
+     * sources). */
 } pa_source_flags_t;
 
 /** \cond fulldocs */
@@ -852,6 +874,11 @@ typedef enum pa_source_flags {
 #define PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
 #define PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
 #define PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
+#define PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
+#define PA_SOURCE_PASSTHROUGH PA_SOURCE_PASSTHROUGH
+#define PA_SOURCE_SYNC_VOLUME PA_SOURCE_SYNC_VOLUME
+#define PA_SOURCE_SHARE_VOLUME_WITH_MASTER PA_SOURCE_SHARE_VOLUME_WITH_MASTER
+
 /** \endcond */
 
 /** Source state. \since 0.9.15 */

commit 205265b7bbaa59d8bbb636f14400bfb436264208
Author: Colin Guthrie <colin at mageia.org>
Date:   Mon May 16 22:14:48 2011 +0100

    introspect: Clear out memory properly on error.

diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index e28a78c..c400133 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -140,11 +140,16 @@ pa_operation* pa_context_get_server_info(pa_context *c, pa_server_info_cb_t cb,
 static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_operation *o = userdata;
     int eol = 1;
+    pa_sink_info i;
+    uint32_t j;
 
     pa_assert(pd);
     pa_assert(o);
     pa_assert(PA_REFCNT_VALUE(o) >= 1);
 
+    /* For safety incase someone use fail: outside the while loop below */
+    pa_zero(i);
+
     if (!o->context)
         goto finish;
 
@@ -156,11 +161,9 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
     } else {
 
         while (!pa_tagstruct_eof(t)) {
-            pa_sink_info i;
             pa_bool_t mute;
             uint32_t flags;
             uint32_t state;
-            uint32_t j;
             const char *ap = NULL;
 
             pa_zero(i);
@@ -195,9 +198,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
                 (o->context->version >= 16 &&
                  (pa_tagstruct_getu32(t, &i.n_ports)))) {
 
-                pa_context_fail(o->context, PA_ERR_PROTOCOL);
-                pa_proplist_free(i.proplist);
-                goto finish;
+                goto fail;
             }
 
             if (o->context->version >= 16) {
@@ -210,11 +211,7 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
                             pa_tagstruct_gets(t, &i.ports[0][j].description) < 0 ||
                             pa_tagstruct_getu32(t, &i.ports[0][j].priority) < 0) {
 
-                            pa_context_fail(o->context, PA_ERR_PROTOCOL);
-                            pa_xfree(i.ports[0]);
-                            pa_xfree(i.ports);
-                            pa_proplist_free(i.proplist);
-                            goto finish;
+                            goto fail;
                         }
 
                         i.ports[j] = &i.ports[0][j];
@@ -223,13 +220,8 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
                     i.ports[j] = NULL;
                 }
 
-                if (pa_tagstruct_gets(t, &ap) < 0) {
-                    pa_context_fail(o->context, PA_ERR_PROTOCOL);
-                    pa_xfree(i.ports[0]);
-                    pa_xfree(i.ports);
-                    pa_proplist_free(i.proplist);
-                    goto finish;
-                }
+                if (pa_tagstruct_gets(t, &ap) < 0)
+                    goto fail;
 
                 if (ap) {
                     for (j = 0; j < i.n_ports; j++)
@@ -241,29 +233,18 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
             }
 
             if (o->context->version >= 21) {
-                i.formats = NULL;
-
-                if (pa_tagstruct_getu8(t, &i.n_formats)) {
-                    pa_context_fail(o->context, PA_ERR_PROTOCOL);
-                    pa_proplist_free(i.proplist);
-                    goto finish;
-                }
+                uint8_t n_formats;
+                if (pa_tagstruct_getu8(t, &n_formats) < 0 || n_formats < 1)
+                    goto fail;
 
-                pa_assert(i.n_formats > 0);
-                i.formats = pa_xnew0(pa_format_info*, i.n_formats);
+                i.formats = pa_xnew0(pa_format_info*, n_formats);
 
-                for (j = 0; j < i.n_formats; j++) {
+                for (j = 0; j < n_formats; j++) {
+                    i.n_formats++;
                     i.formats[j] = pa_format_info_new();
-                    if (pa_tagstruct_get_format_info(t, i.formats[j]) < 0) {
-                        do {
-                            pa_format_info_free(i.formats[j]);
-                        } while (j--);
-                        pa_xfree(i.formats);
 
-                        pa_context_fail(o->context, PA_ERR_PROTOCOL);
-                        pa_proplist_free(i.proplist);
-                        goto finish;
-                    }
+                    if (pa_tagstruct_get_format_info(t, i.formats[j]) < 0)
+                        goto fail;
                 }
             }
 
@@ -276,17 +257,15 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
                 cb(o->context, &i, 0, o->userdata);
             }
 
-            if (i.ports) {
-                pa_xfree(i.ports[0]);
-                pa_xfree(i.ports);
-            }
-
             if (i.formats) {
                 for (j = 0; j < i.n_formats; j++)
                     pa_format_info_free(i.formats[j]);
                 pa_xfree(i.formats);
             }
-
+            if (i.ports) {
+                pa_xfree(i.ports[0]);
+                pa_xfree(i.ports);
+            }
             pa_proplist_free(i.proplist);
         }
     }
@@ -299,6 +278,25 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
 finish:
     pa_operation_done(o);
     pa_operation_unref(o);
+    return;
+
+fail:
+    pa_assert(i.proplist);
+
+    pa_context_fail(o->context, PA_ERR_PROTOCOL);
+
+    if (i.formats) {
+        for (j = 0; j < i.n_formats; j++)
+            pa_format_info_free(i.formats[j]);
+        pa_xfree(i.formats);
+    }
+    if (i.ports) {
+        pa_xfree(i.ports[0]);
+        pa_xfree(i.ports);
+    }
+    pa_proplist_free(i.proplist);
+
+    goto finish;
 }
 
 pa_operation* pa_context_get_sink_info_list(pa_context *c, pa_sink_info_cb_t cb, void *userdata) {

commit dc6c272625c7801af68126bb1983207ad88400a5
Author: Colin Guthrie <colin at mageia.org>
Date:   Tue May 17 19:50:32 2011 +0100

    alsa-sink: Some trivial tidyups
    
    Mostly typo fixes but also a change to make a function relating
    to sink inputs use more generic variable names.

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index b98340b..96812c7 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -256,6 +256,7 @@ static int reserve_monitor_init(struct userdata *u, const char *dname) {
     if (!(rname = pa_alsa_get_reserve_name(dname)))
         return 0;
 
+    /* We are resuming, try to lock the device */
     u->monitor = pa_reserve_monitor_wrapper_get(u->core, rname);
     pa_xfree(rname);
 
@@ -1590,7 +1591,7 @@ static void thread_func(void *userdata) {
             pa_usec_t volume_sleep;
             pa_sink_volume_change_apply(u->sink, &volume_sleep);
             if (volume_sleep > 0)
-                rtpoll_sleep = MIN(volume_sleep, rtpoll_sleep);
+                rtpoll_sleep = PA_MIN(volume_sleep, rtpoll_sleep);
         }
 
         if (rtpoll_sleep > 0)
@@ -1954,7 +1955,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
                       SND_PCM_STREAM_PLAYBACK,
                       &period_frames, &buffer_frames, tsched_frames,
                       &b, &d, mapping)))
-
             goto fail;
 
     } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
@@ -1969,7 +1969,6 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
                       SND_PCM_STREAM_PLAYBACK,
                       &period_frames, &buffer_frames, tsched_frames,
                       &b, &d, profile_set, &mapping)))
-
             goto fail;
 
     } else {
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 553e3d9..9a43e04 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1616,7 +1616,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
              !pa_sample_spec_equal(&i->sample_spec, &dest->sample_spec) ||
              !pa_channel_map_equal(&i->channel_map, &dest->channel_map)) {
 
-        /* Okey, we need a new resampler for the new sink */
+        /* Okay, we need a new resampler for the new sink */
 
         if (!(new_resampler = pa_resampler_new(
                       i->core->mempool,
@@ -1664,6 +1664,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
                 &i->sink->silence);
         i->actual_resample_method = new_resampler ? pa_resampler_get_method(new_resampler) : PA_RESAMPLER_INVALID;
     }
+
     pa_sink_update_status(dest);
 
     update_volume_due_to_moving(i, dest);
@@ -1678,7 +1679,6 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
 
     /* Notify everyone */
     pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], i);
-
     pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
 
     return 0;
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 0ebb74a..38bad35 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -110,7 +110,7 @@ struct pa_sink_input {
 
     pa_bool_t muted:1;
 
-    /* if TRUE then the source we are connected to and/or the volume
+    /* if TRUE then the sink we are connected to and/or the volume
      * set is worth remembering, i.e. was explicitly chosen by the
      * user and not automatically. module-stream-restore looks for
      * this.*/
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index bc4ddd2..ed38013 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1728,8 +1728,10 @@ void pa_sink_set_volume(
 /* Called from the io thread if sync volume is used, otherwise from the main thread.
  * Only to be called by sink implementor */
 void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {
+
     pa_sink_assert_ref(s);
     pa_assert(!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER));
+
     if (s->flags & PA_SINK_SYNC_VOLUME)
         pa_sink_assert_io_context(s);
     else
@@ -3295,25 +3297,27 @@ pa_idxset* pa_sink_get_formats(pa_sink *s) {
 /* Checks if the sink can accept this format */
 pa_bool_t pa_sink_check_format(pa_sink *s, pa_format_info *f)
 {
-    pa_idxset *sink_formats = NULL;
-    pa_format_info *f_sink;
-    uint32_t i;
+    pa_idxset *formats = NULL;
     pa_bool_t ret = FALSE;
 
     pa_assert(s);
     pa_assert(f);
 
-    sink_formats = pa_sink_get_formats(s);
+    formats = pa_sink_get_formats(s);
 
-    PA_IDXSET_FOREACH(f_sink, sink_formats, i) {
-        if (pa_format_info_is_compatible(f_sink, f)) {
-            ret = TRUE;
-            break;
+    if (formats) {
+        pa_format_info *finfo_device;
+        uint32_t i;
+
+        PA_IDXSET_FOREACH(finfo_device, formats, i) {
+            if (pa_format_info_is_compatible(finfo_device, f)) {
+                ret = TRUE;
+                break;
+            }
         }
-    }
 
-    if (sink_formats)
-        pa_idxset_free(sink_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
+        pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
+    }
 
     return ret;
 }



More information about the pulseaudio-commits mailing list