[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, master, updated. v1.0-dev-354-g2bc2abc

Colin Guthrie gitmailer-noreply at 0pointer.de
Mon May 16 03:47:48 PDT 2011


This is an automated email from the git hooks/post-receive script. It was
generated because of a push to the "PulseAudio Sound Server" repository.

The master branch has been updated
      from  be4208d0799f21663f58f50430b4883e87709480 (commit)

- Log -----------------------------------------------------------------
2bc2abc build-sys: Link libpulse directly to libdbus-1 if needed
4ff2e85 protocol-native: Fix memory leaks introduced in protocol 21 (passthrough support)
e59fc6c sink-input: Fix memory leak of proplist when sending format-changed events
3c4accf loopback: Add a modarg for disabling remixing.
ce8b03b bluetooth: Fix early return styling and add missing return value
ff79b31 suspend-on-idle: Trigger mempool vacuuming
47b7ca8 protocol-native: Stop auto timing updates if connected to suspended sink or source
d5cb59a i18n: Fix POTFILES
3e0332b tests: Fix resampler-test.
26b4bd7 Merge branch 'passthrough'
7ebc503 module-tunnel: Update for recent protocol changes
cebb4e0 doxygen: generate documentation for format.h
1404db3 format: Add some convenience API for setting properties
8d076d0 format: Extend properties to handle lists/ranges
62f56a9 sink-input: Provide more information to client when format is lost
d1f13fa format: Add correct sample spec conversion for E-AC3
13a33ab format: Export pa_format_info_is_compatible in API
53091cc sink-input: Add a format-lost event
cb3dcb1 sink-input: Don't restore volume for passthrough streams
a199bfb sink-input: Don't print an error if a passthrough connection fails
4fb68b9 core: Factor out passthrough checks into their own functions
9a39a3d format: Add a type for DTS
7aa84e8 introspect: Get format of sink input
322980e introspect: Get formats for sinks
dedbc94 stream: Add API to get a stream's pa_format_info
8631f4e format: Add some convenience functions for printing
a3a0042 format: Const-ify some parameters
49b10ba alsa: Reconfigure sink sample rate for passthrough inputs
f94bcae core: Suspend monitor when a sink enters passthrough mode
4c9d53f sink: Trivial typo fix in comment
20f1fa1 alsa-mixer: Remove passthrough profiles
e11770b core: Fix some FIXMEs for the extended API
658a915 sink-input: Kill passthrough streams if moving to an unsupported sink
8b3e68a sink: Fix leak in pa_sink_check_formats()
e418e49 format: Avoid some code duplication
13229fb sink-input: Don't assert on bad formats
8ec0548 sink-input: Return NOTSUPPORTED if format negotiation fails
5d55236 sink-input: Minor cleanups
e64f02a tests: Add a trivial test for the extended API
71ec957 sink: Remove PASSTHROUGH flag
54c391e format: Add convenience API to check if a format is PCM or not
0ac2cfc core: Add extended stream API to support compressed formats
47e0f91 sink: Extend API for compressed formats support
3767c9c format: Add some properties and internal API
c3839c7 core: Add a pa_format_info structure
802c8b6 sample: Use PA_SAMPLE_INVALID instead of numeric value
f4d1f2b sink: Trivial typo fix
8810749 sink-input: Don't resample passthrough inputs
233ef98 filter-apply: Mark modules as being autoloaded
-----------------------------------------------------------------------

Summary of changes:
 PROTOCOL                                           |   28 ++
 configure.ac                                       |    8 +-
 doxygen/doxygen.conf.in                            |    2 +-
 po/POTFILES.in                                     |    3 +
 src/Makefile.am                                    |   29 +-
 src/map-file                                       |   19 +
 src/modules/alsa/alsa-sink.c                       |   59 +++-
 .../mixer/paths/iec958-passthrough-output.conf     |   19 -
 src/modules/alsa/mixer/profile-sets/default.conf   |    7 -
 src/modules/bluetooth/module-bluetooth-device.c    |   11 +-
 src/modules/echo-cancel/module-echo-cancel.c       |    2 +-
 src/modules/module-combine-sink.c                  |    2 +-
 src/modules/module-device-manager.c                |    4 +-
 src/modules/module-equalizer-sink.c                |    2 +-
 src/modules/module-intended-roles.c                |   10 +-
 src/modules/module-ladspa-sink.c                   |    2 +-
 src/modules/module-loopback.c                      |   20 +-
 src/modules/module-remap-sink.c                    |    2 +-
 src/modules/module-sine.c                          |    2 +-
 src/modules/module-stream-restore.c                |    8 +-
 src/modules/module-suspend-on-idle.c               |   41 ++-
 src/modules/module-tunnel.c                        |   59 +++
 src/modules/module-virtual-sink.c                  |    2 +-
 src/modules/rtp/module-rtp-recv.c                  |    2 +-
 src/pulse/def.h                                    |   17 +-
 src/pulse/format.c                                 |  460 ++++++++++++++++++++
 src/pulse/format.h                                 |  129 ++++++
 src/pulse/internal.h                               |   12 +
 src/pulse/introspect.c                             |   40 ++-
 src/pulse/introspect.h                             |    4 +
 src/pulse/proplist.h                               |   12 +
 src/pulse/pulseaudio.h                             |    1 +
 src/pulse/sample.c                                 |    2 +-
 src/pulse/stream.c                                 |  180 +++++++-
 src/pulse/stream.h                                 |   14 +
 src/pulsecore/core.h                               |    1 +
 src/pulsecore/play-memblockq.c                     |    2 +-
 src/pulsecore/protocol-esound.c                    |    2 +-
 src/pulsecore/protocol-native.c                    |  142 +++++--
 src/pulsecore/protocol-simple.c                    |    2 +-
 src/pulsecore/sink-input.c                         |  233 ++++++++--
 src/pulsecore/sink-input.h                         |    9 +
 src/pulsecore/sink.c                               |  120 +++++-
 src/pulsecore/sink.h                               |   26 +-
 src/pulsecore/sound-file-stream.c                  |    2 +-
 src/pulsecore/source.c                             |    2 +-
 src/pulsecore/tagstruct.c                          |   42 ++
 src/pulsecore/tagstruct.h                          |    6 +-
 src/tests/{sync-playback.c => extended-test.c}     |   15 +-
 src/tests/format-test.c                            |  106 +++++
 src/tests/resampler-test.c                         |    7 +-
 src/utils/pactl.c                                  |   15 +-
 52 files changed, 1712 insertions(+), 234 deletions(-)
 delete mode 100644 src/modules/alsa/mixer/paths/iec958-passthrough-output.conf
 create mode 100644 src/pulse/format.c
 create mode 100644 src/pulse/format.h
 copy src/tests/{sync-playback.c => extended-test.c} (90%)
 create mode 100644 src/tests/format-test.c

-----------------------------------------------------------------------

commit 233ef98bf19ab311c1cb19867b1e8bcb4c36f66d
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Mon May 2 10:08:27 2011 +0530

    filter-apply: Mark modules as being autoloaded
    
    (Based on Colin's review) We mark modules as being autoloaded so that
    they can handle this as a special case if needed (which is required by
    module-echo-cancel for now). This inverts how things were done and makes
    using these modules manually less error-prone.

diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
index 746028b..3787962 100644
--- a/src/modules/echo-cancel/module-echo-cancel.c
+++ b/src/modules/echo-cancel/module-echo-cancel.c
@@ -78,7 +78,7 @@ PA_MODULE_USAGE(
           "aec_method=<implementation to use> "
           "aec_args=<parameters for the AEC engine> "
           "save_aec=<save AEC data in /tmp> "
-          "manual_load=<set if this module is being loaded manually> "
+          "autoloaded=<set if this module is being loaded automatically> "
         ));
 
 /* NOTE: Make sure the enum and ec_table are maintained in the correct order */
@@ -107,7 +107,7 @@ static const pa_echo_canceller ec_table[] = {
 
 #define DEFAULT_ADJUST_TIME_USEC (1*PA_USEC_PER_SEC)
 #define DEFAULT_SAVE_AEC 0
-#define DEFAULT_MANUAL_LOAD FALSE
+#define DEFAULT_AUTOLOADED FALSE
 
 #define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
 
@@ -158,7 +158,7 @@ struct userdata {
     pa_core *core;
     pa_module *module;
 
-    pa_bool_t manual_load;
+    pa_bool_t autoloaded;
     uint32_t save_aec;
 
     pa_echo_canceller *ec;
@@ -213,7 +213,7 @@ static const char* const valid_modargs[] = {
     "aec_method",
     "aec_args",
     "save_aec",
-    "manual_load",
+    "autoloaded",
     NULL
 };
 
@@ -1398,9 +1398,9 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
-    u->manual_load = DEFAULT_MANUAL_LOAD;
-    if (pa_modargs_get_value_boolean(ma, "manual_load", &u->manual_load) < 0) {
-        pa_log("Failed to parse manual_load value");
+    u->autoloaded = DEFAULT_AUTOLOADED;
+    if (pa_modargs_get_value_boolean(ma, "autoloaded", &u->autoloaded) < 0) {
+        pa_log("Failed to parse autoloaded value");
         goto fail;
     }
 
@@ -1423,7 +1423,7 @@ int pa__init(pa_module*m) {
     pa_source_new_data_set_channel_map(&source_data, &source_map);
     pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, source_master->name);
     pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
-    if (u->manual_load)
+    if (!u->autoloaded)
         pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
     pa_proplist_sets(source_data.proplist, "device.echo-cancel.name", source_data.name);
 
@@ -1471,7 +1471,7 @@ int pa__init(pa_module*m) {
     pa_sink_new_data_set_channel_map(&sink_data, &sink_map);
     pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, sink_master->name);
     pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter");
-    if (u->manual_load)
+    if (!u->autoloaded)
         pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
     pa_proplist_sets(sink_data.proplist, "device.echo-cancel.name", sink_data.name);
 
diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 0bbb23a..9a85fe5 100644
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -83,14 +83,18 @@ PA_MODULE_USAGE(
           "format=<sample format> "
           "rate=<sample rate> "
           "channels=<number of channels> "
-          "channel_map=<channel map>"));
+          "channel_map=<channel map> "
+          "autoloaded=<set if this module is being loaded automatically> "
+         ));
 
 #define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
+#define DEFAULT_AUTOLOADED FALSE
 
 struct userdata {
     pa_module *module;
     pa_sink *sink;
     pa_sink_input *sink_input;
+    pa_bool_t autoloaded;
 
     size_t channels;
     size_t fft_size;//length (res) of fft
@@ -138,6 +142,7 @@ static const char* const valid_modargs[] = {
     "rate",
     "channels",
     "channel_map",
+    "autoloaded",
     NULL
 };
 
@@ -1170,6 +1175,12 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
+    u->autoloaded = DEFAULT_AUTOLOADED;
+    if (pa_modargs_get_value_boolean(ma, "autoloaded", &u->autoloaded) < 0) {
+        pa_log("Failed to parse autoloaded value");
+        goto fail;
+    }
+
     u->sink = pa_sink_new(m->core, &sink_data,
                           PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME|
                           (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)));
diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c
index 79558f2..e9c9f65 100644
--- a/src/modules/module-filter-apply.c
+++ b/src/modules/module-filter-apply.c
@@ -314,7 +314,7 @@ static pa_hook_result_t process(struct userdata *u, pa_object *o, pa_bool_t is_s
             char *args;
             pa_module *m;
 
-            args = pa_sprintf_malloc("%s_master=%s", is_sink_input ? "sink" : "source", parent_name);
+            args = pa_sprintf_malloc("autoloaded=1 %s_master=%s", is_sink_input ? "sink" : "source", parent_name);
             pa_log_debug("Loading %s with arguments '%s'", module_name, args);
 
             if ((m = pa_module_load(u->core, module_name, args))) {
diff --git a/src/modules/module-virtual-sink.c b/src/modules/module-virtual-sink.c
index f772314..9bcff8c 100644
--- a/src/modules/module-virtual-sink.c
+++ b/src/modules/module-virtual-sink.c
@@ -66,6 +66,9 @@ PA_MODULE_USAGE(
 struct userdata {
     pa_module *module;
 
+    /* FIXME: Uncomment this and take "autoloaded" as a modarg if this is a filter */
+    /* pa_bool_t autoloaded; */
+
     pa_sink *sink;
     pa_sink_input *sink_input;
 
diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c
index b8f2ab0..835cf3c 100644
--- a/src/modules/module-virtual-source.c
+++ b/src/modules/module-virtual-source.c
@@ -69,6 +69,9 @@ PA_MODULE_USAGE(
 struct userdata {
     pa_module *module;
 
+    /* FIXME: Uncomment this and take "autoloaded" as a modarg if this is a filter */
+    /* pa_bool_t autoloaded; */
+
     pa_source *source;
     pa_source_output *source_output;
 

commit 881074907e04b23d713815fe11093e9081fe71e8
Author: Pierre-Louis Bossart <pierre-louis.bossart at intel.com>
Date:   Tue Mar 8 15:12:56 2011 +0530

    sink-input: Don't resample passthrough inputs

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 1931d99..194ec99 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -295,18 +295,20 @@ int pa_sink_input_new(
         !pa_sample_spec_equal(&data->sample_spec, &data->sink->sample_spec) ||
         !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) {
 
-        if (!(resampler = pa_resampler_new(
-                      core->mempool,
-                      &data->sample_spec, &data->channel_map,
-                      &data->sink->sample_spec, &data->sink->channel_map,
-                      data->resample_method,
-                      ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
-                      ((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
-                      (core->disable_remixing || (data->flags & PA_SINK_INPUT_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;
-        }
+        /* Note: for passthrough content we need to adjust the output rate to that of the current sink-input */
+        if (!(data->flags & PA_SINK_INPUT_PASSTHROUGH)) /* no resampler for passthrough content */
+            if (!(resampler = pa_resampler_new(
+                          core->mempool,
+                          &data->sample_spec, &data->channel_map,
+                          &data->sink->sample_spec, &data->sink->channel_map,
+                          data->resample_method,
+                          ((data->flags & PA_SINK_INPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
+                          ((data->flags & PA_SINK_INPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
+                          (core->disable_remixing || (data->flags & PA_SINK_INPUT_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;
+            }
     }
 
     i = pa_msgobject_new(pa_sink_input);

commit f4d1f2bdcfe5e598e926c190c80ec4c6d38dc9f4
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Fri Feb 25 12:44:53 2011 +0530

    sink: Trivial typo fix

diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index ef69881..b61ba33 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -161,7 +161,7 @@ struct pa_sink {
      * s->real_volume and/or s->soft_volume so that they together
      * match the actual hardware volume that will be set later in the
      * write_volume callback. */
-    void (*set_volume)(pa_sink *s);             /* dito */
+    void (*set_volume)(pa_sink *s);             /* ditto */
 
     /* Sink drivers that set PA_SINK_SYNC_VOLUME must provide this
      * callback. This callback is not used with sinks that do not set
@@ -174,30 +174,30 @@ struct pa_sink {
      * 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_sink *s);           /* dito */
+    void (*write_volume)(pa_sink *s);           /* ditto */
 
     /* Called when the mute setting is queried. A PA_SINK_MESSAGE_GET_MUTE
      * message will also be sent. Called from IO thread if PA_SINK_SYNC_VOLUME
      * flag is set otherwise from main loop context. If refresh_mute is FALSE
      * neither this function is called nor a message is sent.*/
-    void (*get_mute)(pa_sink *s);               /* dito */
+    void (*get_mute)(pa_sink *s);               /* ditto */
 
     /* Called when the mute setting shall be changed. A PA_SINK_MESSAGE_SET_MUTE
      * message will also be sent. Called from IO thread if PA_SINK_SYNC_VOLUME
      * flag is set otherwise from main loop context. */
-    void (*set_mute)(pa_sink *s);               /* dito */
+    void (*set_mute)(pa_sink *s);               /* ditto */
 
     /* Called when a rewind request is issued. Called from IO thread
      * context. */
-    void (*request_rewind)(pa_sink *s);        /* dito */
+    void (*request_rewind)(pa_sink *s);        /* ditto */
 
     /* Called when a the requested latency is changed. Called from IO
      * thread context. */
-    void (*update_requested_latency)(pa_sink *s); /* dito */
+    void (*update_requested_latency)(pa_sink *s); /* ditto */
 
     /* Called whenever the port shall be changed. Called from main
      * thread. */
-    int (*set_port)(pa_sink *s, pa_device_port *port); /* dito */
+    int (*set_port)(pa_sink *s, pa_device_port *port); /* ditto */
 
     /* Contains copies of the above data so that the real-time worker
      * thread can work without access locking */

commit 802c8b6fcc9349505d00c1f6f6132dd513904b68
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Thu Feb 24 14:46:01 2011 +0530

    sample: Use PA_SAMPLE_INVALID instead of numeric value

diff --git a/src/pulse/sample.c b/src/pulse/sample.c
index 9698d8a..50d5521 100644
--- a/src/pulse/sample.c
+++ b/src/pulse/sample.c
@@ -242,7 +242,7 @@ pa_sample_format_t pa_parse_sample_format(const char *format) {
     else if (strcasecmp(format, "s24-32re") == 0)
         return PA_SAMPLE_S24_32RE;
 
-    return -1;
+    return PA_SAMPLE_INVALID;
 }
 
 int pa_sample_format_is_le(pa_sample_format_t f) {

commit c3839c76377ea7662f29364ae66718e8ac6233af
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Fri Feb 25 12:35:14 2011 +0530

    core: Add a pa_format_info structure
    
    This will be used to represent the format of data provided by the client
    for both compressed and PCM formats in a new extended API.

diff --git a/po/POTFILES.in b/po/POTFILES.in
index ba7e9b7..35c20be 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -177,6 +177,7 @@ src/pulse/thread-mainloop.c
 src/pulse/scache.c
 src/pulse/glib-mainloop.c
 src/pulse/timeval.c
+src/pulse/format.c
 src/utils/pacat.c
 src/utils/pasuspender.c
 src/utils/pabrowse.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 23d809d..1b8b447 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -589,6 +589,7 @@ libpulsecommon_ at PA_MAJORMINOR@_la_SOURCES = \
 		pulse/util.c pulse/util.h \
 		pulse/timeval.c pulse/timeval.h \
 		pulse/rtclock.c pulse/rtclock.h \
+		pulse/format.c pulse/format.h \
 		pulsecore/atomic.h \
 		pulsecore/authkey.c pulsecore/authkey.h \
 		pulsecore/conf-parser.c pulsecore/conf-parser.h \
@@ -719,6 +720,7 @@ pulseinclude_HEADERS = \
 		pulse/error.h \
 		pulse/ext-device-manager.h \
 		pulse/ext-stream-restore.h \
+		pulse/format.h \
 		pulse/gccmacro.h \
 		pulse/introspect.h \
 		pulse/mainloop-api.h \
diff --git a/src/map-file b/src/map-file
index 1fffaff..181af9e 100644
--- a/src/map-file
+++ b/src/map-file
@@ -160,6 +160,10 @@ pa_ext_stream_restore_set_subscribe_cb;
 pa_ext_stream_restore_subscribe;
 pa_ext_stream_restore_test;
 pa_ext_stream_restore_write;
+pa_format_info_copy;
+pa_format_info_free;
+pa_format_info_new;
+pa_format_info_valid;
 pa_frame_size;
 pa_get_binary_name;
 pa_get_fqdn;
diff --git a/src/pulse/format.c b/src/pulse/format.c
new file mode 100644
index 0000000..372ae1c
--- /dev/null
+++ b/src/pulse/format.c
@@ -0,0 +1,71 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2011 Intel Corporation
+  Copyright 2011 Collabora Multimedia
+  Copyright 2011 Arun Raghavan <arun.raghavan at collabora.co.uk>
+
+  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/internal.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/macro.h>
+
+#include "format.h"
+
+pa_format_info* pa_format_info_new(void) {
+    pa_format_info *f = pa_xnew(pa_format_info, 1);
+
+    f->encoding = PA_ENCODING_INVALID;
+    f->plist = pa_proplist_new();
+
+    return f;
+}
+
+pa_format_info* pa_format_info_copy(const pa_format_info *src) {
+    pa_format_info *dest;
+
+    pa_assert(src);
+
+    dest = pa_xnew(pa_format_info, 1);
+
+    dest->encoding = src->encoding;
+
+    if (src->plist)
+        dest->plist = pa_proplist_copy(src->plist);
+    else
+        dest->plist = NULL;
+
+    return dest;
+}
+
+void pa_format_info_free(pa_format_info *f) {
+    pa_assert(f);
+
+    pa_proplist_free(f->plist);
+    pa_xfree(f);
+}
+
+int pa_format_info_valid(pa_format_info *f) {
+    return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL);
+}
diff --git a/src/pulse/format.h b/src/pulse/format.h
new file mode 100644
index 0000000..fdf4f37
--- /dev/null
+++ b/src/pulse/format.h
@@ -0,0 +1,79 @@
+#ifndef fooformathfoo
+#define fooformathfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2011 Intel Corporation
+  Copyright 2011 Collabora Multimedia
+  Copyright 2011 Arun Raghavan <arun.raghavan at collabora.co.uk>
+
+  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/cdecl.h>
+#include <pulse/proplist.h>
+
+PA_C_DECL_BEGIN
+
+/**< Represents the type of encoding used in a stream or accepted by a sink. \since 1.0 */
+typedef enum pa_encoding {
+    PA_ENCODING_ANY,
+    /**< Any encoding format, PCM or compressed */
+
+    PA_ENCODING_PCM,
+    /**< Any PCM format */
+
+    PA_ENCODING_AC3_IEC61937,
+    /**< AC3 data encapsulated in IEC 61937 header/padding */
+
+    PA_ENCODING_EAC3_IEC61937,
+    /**< EAC3 data encapsulated in IEC 61937 header/padding */
+
+    PA_ENCODING_MPEG_IEC61937,
+    /**< MPEG-1 or MPEG-2 (Part 3, not AAC) data encapsulated in IEC 61937 header/padding */
+
+    PA_ENCODING_MAX,
+    /**< Valid encoding types must be less than this value */
+
+    PA_ENCODING_INVALID = -1,
+    /**< Represents an invalid encoding */
+} pa_encoding_t;
+
+/**< Represents the format of data provided in a stream or processed by a sink. \since 1.0 */
+typedef struct pa_format_info {
+    pa_encoding_t encoding;
+    /**< The encoding used for the format */
+
+    pa_proplist *plist;
+    /**< Additional encoding-specific properties such as sample rate, bitrate, etc. */
+} pa_format_info;
+
+/**< Allocates a new \a pa_format_info structure. Clients must initialise at least the encoding field themselves. */
+pa_format_info* pa_format_info_new(void);
+
+/**< Returns a new \a pa_format_info struct and representing the same format as \a src */
+pa_format_info* pa_format_info_copy(const pa_format_info *src);
+
+/**< Frees a \a pa_format_info structure */
+void pa_format_info_free(pa_format_info *f);
+
+/** Returns non-zero when the format info structure is valid */
+int pa_format_info_valid(pa_format_info *f);
+
+PA_C_DECL_END
+
+#endif
diff --git a/src/pulse/pulseaudio.h b/src/pulse/pulseaudio.h
index 793ba9b..a399ed9 100644
--- a/src/pulse/pulseaudio.h
+++ b/src/pulse/pulseaudio.h
@@ -25,6 +25,7 @@
 
 #include <pulse/mainloop-api.h>
 #include <pulse/sample.h>
+#include <pulse/format.h>
 #include <pulse/def.h>
 #include <pulse/context.h>
 #include <pulse/stream.h>
diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c
index 804b9f9..5694a0d 100644
--- a/src/pulsecore/tagstruct.c
+++ b/src/pulsecore/tagstruct.c
@@ -291,6 +291,17 @@ void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p) {
     pa_tagstruct_puts(t, NULL);
 }
 
+void pa_tagstruct_put_format_info(pa_tagstruct *t, pa_format_info *f) {
+    pa_assert(t);
+    pa_assert(f);
+
+    extend(t, 1);
+
+    t->data[t->length++] = PA_TAG_FORMAT_INFO;
+    pa_tagstruct_putu8(t, (uint8_t) f->encoding);
+    pa_tagstruct_put_proplist(t, f->plist);
+}
+
 int pa_tagstruct_gets(pa_tagstruct*t, const char **s) {
     int error = 0;
     size_t n;
@@ -631,6 +642,37 @@ fail:
     return -1;
 }
 
+int pa_tagstruct_get_format_info(pa_tagstruct *t, pa_format_info *f) {
+    size_t saved_rindex;
+    uint8_t encoding;
+
+    pa_assert(t);
+    pa_assert(f);
+
+    if (t->rindex+1 > t->length)
+        return -1;
+
+    if (t->data[t->rindex] != PA_TAG_FORMAT_INFO)
+        return -1;
+
+    saved_rindex = t->rindex;
+    t->rindex++;
+
+    if (pa_tagstruct_getu8(t, &encoding) < 0)
+        goto fail;
+
+    f->encoding = encoding;
+
+    if (pa_tagstruct_get_proplist(t, f->plist) < 0)
+        goto fail;
+
+    return 0;
+
+fail:
+    t->rindex = saved_rindex;
+    return -1;
+}
+
 void pa_tagstruct_put(pa_tagstruct *t, ...) {
     va_list va;
     pa_assert(t);
diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h
index b6553ad..0091eeb 100644
--- a/src/pulsecore/tagstruct.h
+++ b/src/pulsecore/tagstruct.h
@@ -27,6 +27,7 @@
 #include <sys/time.h>
 
 #include <pulse/sample.h>
+#include <pulse/format.h>
 #include <pulse/channelmap.h>
 #include <pulse/volume.h>
 #include <pulse/proplist.h>
@@ -58,7 +59,8 @@ enum {
     PA_TAG_CHANNEL_MAP = 'm',
     PA_TAG_CVOLUME = 'v',
     PA_TAG_PROPLIST = 'P',
-    PA_TAG_VOLUME = 'V'
+    PA_TAG_VOLUME = 'V',
+    PA_TAG_FORMAT_INFO = 'f',
 };
 
 pa_tagstruct *pa_tagstruct_new(const uint8_t* data, size_t length);
@@ -84,6 +86,7 @@ void pa_tagstruct_put_channel_map(pa_tagstruct *t, const pa_channel_map *map);
 void pa_tagstruct_put_cvolume(pa_tagstruct *t, const pa_cvolume *cvolume);
 void pa_tagstruct_put_proplist(pa_tagstruct *t, pa_proplist *p);
 void pa_tagstruct_put_volume(pa_tagstruct *t, pa_volume_t volume);
+void pa_tagstruct_put_format_info(pa_tagstruct *t, pa_format_info *f);
 
 int pa_tagstruct_get(pa_tagstruct *t, ...);
 
@@ -101,5 +104,6 @@ int pa_tagstruct_get_channel_map(pa_tagstruct *t, pa_channel_map *map);
 int pa_tagstruct_get_cvolume(pa_tagstruct *t, pa_cvolume *v);
 int pa_tagstruct_get_proplist(pa_tagstruct *t, pa_proplist *p);
 int pa_tagstruct_get_volume(pa_tagstruct *t, pa_volume_t *v);
+int pa_tagstruct_get_format_info(pa_tagstruct *t, pa_format_info *f);
 
 #endif

commit 3767c9c4e8107a3e78f387466b45384d64644d94
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Mon Feb 28 10:53:41 2011 +0530

    format: Add some properties and internal API
    
    The properties will be used by clients to set the sample format,
    sampling rate, etc. The functions will be used internally.

diff --git a/src/pulse/format.c b/src/pulse/format.c
index 372ae1c..c3962de 100644
--- a/src/pulse/format.c
+++ b/src/pulse/format.c
@@ -69,3 +69,90 @@ void pa_format_info_free(pa_format_info *f) {
 int pa_format_info_valid(pa_format_info *f) {
     return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL);
 }
+
+pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) {
+    const char *key;
+    void *state = NULL;
+
+    pa_assert(first);
+    pa_assert(second);
+
+    if (first->encoding != second->encoding)
+        return FALSE;
+
+    while ((key = pa_proplist_iterate(first->plist, &state))) {
+        const char *value_one, *value_two;
+
+        value_one = pa_proplist_gets(first->plist, key);
+        value_two = pa_proplist_gets(second->plist, key);
+
+        if (!value_two || !pa_streq(value_one, value_two))
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map) {
+    char cm[PA_CHANNEL_MAP_SNPRINT_MAX];
+    pa_format_info *f;
+
+    pa_assert(ss && pa_sample_spec_valid(ss));
+    pa_assert(!map || pa_channel_map_valid(map));
+
+    f = pa_format_info_new();
+    f->encoding = PA_ENCODING_PCM;
+
+    pa_proplist_sets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(ss->format));
+    pa_proplist_setf(f->plist, PA_PROP_FORMAT_RATE, "%u", (unsigned int) ss->rate);
+    pa_proplist_setf(f->plist, PA_PROP_FORMAT_CHANNELS, "%u", (unsigned int) ss->channels);
+
+    if (map) {
+        pa_channel_map_snprint(cm, sizeof(cm), map);
+        pa_proplist_setf(f->plist, PA_PROP_FORMAT_CHANNEL_MAP, "%s", cm);
+    }
+
+    return f;
+}
+
+/* For PCM streams */
+void pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
+    const char *sf, *r, *ch;
+    uint32_t channels;
+
+    pa_assert(f);
+    pa_assert(ss);
+    pa_assert(f->encoding == PA_ENCODING_PCM);
+
+    pa_assert(sf = pa_proplist_gets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT));
+    pa_assert(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE));
+    pa_assert(ch = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNELS));
+
+    pa_assert((ss->format = pa_parse_sample_format(sf)) != PA_SAMPLE_INVALID);
+    pa_assert(pa_atou(r, &ss->rate) == 0);
+    pa_assert(pa_atou(ch, &channels) == 0);
+    ss->channels = (uint8_t) channels;
+
+    if (map) {
+        const char *m = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNEL_MAP);
+        pa_channel_map_init(map);
+
+        if (m)
+            pa_assert(pa_channel_map_parse(map, m) != NULL);
+    }
+}
+
+/* For compressed streams */
+void pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) {
+    const char *r;
+
+    pa_assert(f);
+    pa_assert(ss);
+    pa_assert(f->encoding != PA_ENCODING_PCM);
+
+    ss->format = PA_SAMPLE_S16LE;
+    ss->channels = 2;
+
+    pa_assert(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE));
+    pa_assert(pa_atou(r, &ss->rate) == 0);
+}
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index ab702b9..53fcca6 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -291,6 +291,11 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
 void pa_ext_device_manager_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);
 
+pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second);
+pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map);
+void pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
+void pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss);
+
 pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m);
 
 #endif
diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 7d02699..a641f24 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -254,6 +254,18 @@ PA_C_DECL_BEGIN
 /** For modules: a version string for the module. e.g. "0.9.15" */
 #define PA_PROP_MODULE_VERSION                 "module.version"
 
+/** For PCM formats: the sample format used as returned by pa_sample_format_to_string() \since 1.0 */
+#define PA_PROP_FORMAT_SAMPLE_FORMAT           "format.sample_format"
+
+/** For all formats: the sample rate (unsigned integer) \since 1.0 */
+#define PA_PROP_FORMAT_RATE                    "format.rate"
+
+/** For all formats: the number of channels (unsigned integer) \since 1.0 */
+#define PA_PROP_FORMAT_CHANNELS                "format.channels"
+
+/** For PCM formats: the channel map of the stream as returned by pa_channel_map_snprint() \since 1.0 */
+#define PA_PROP_FORMAT_CHANNEL_MAP             "format.channel_map"
+
 /** A property list object. Basically a dictionary with ASCII strings
  * as keys and arbitrary data as values. \since 0.9.11 */
 typedef struct pa_proplist pa_proplist;

commit 47e0f91aa2ca6eb3ea8b7be8aa03cd03a28c3fbe
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Mon Feb 28 13:00:20 2011 +0530

    sink: Extend API for compressed formats support
    
    This adds a get_formats() vfunc for sinks to provide a list of formats
    they can support. pa_sink_check_formats() can be used during or after
    routing to determine what formats from a stream the sink can support.

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 839b7d4..345d090 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -35,6 +35,7 @@
 #include <pulse/util.h>
 #include <pulse/i18n.h>
 #include <pulse/rtclock.h>
+#include <pulse/internal.h>
 
 #include <pulsecore/sink-input.h>
 #include <pulsecore/namereg.h>
@@ -177,6 +178,7 @@ static void reset_callbacks(pa_sink *s) {
     s->request_rewind = NULL;
     s->update_requested_latency = NULL;
     s->set_port = NULL;
+    s->get_formats = NULL;
 }
 
 /* Called from main context */
@@ -3258,3 +3260,52 @@ static void pa_sink_volume_change_rewind(pa_sink *s, size_t nbytes) {
     }
     pa_sink_volume_change_apply(s, NULL);
 }
+
+/* Called from the main thread */
+/* Gets the list of formats supported by the sink. The members and idxset must
+ * be freed by the caller. */
+pa_idxset* pa_sink_get_formats(pa_sink *s) {
+    pa_idxset *ret;
+
+    pa_assert(s);
+
+    if (s->get_formats) {
+        /* Sink supports format query, all is good */
+        ret = s->get_formats(s);
+    } else {
+        /* Sink 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 */
+/* Calculates the intersection between formats supported by the sink and
+ * in_formats, and returns these, in the order of the sink's formats. */
+pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats) {
+    pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *sink_formats;
+    pa_format_info *f_sink, *f_in;
+    uint32_t i, j;
+
+    pa_assert(s);
+
+    if (!in_formats || pa_idxset_isempty(in_formats))
+        goto done;
+
+    sink_formats = pa_sink_get_formats(s);
+
+    PA_IDXSET_FOREACH(f_sink, sink_formats, i) {
+        PA_IDXSET_FOREACH(f_in, in_formats, j) {
+            if (pa_format_info_is_compatible(f_sink, f_in))
+                pa_idxset_put(out_formats, pa_format_info_copy(f_in), NULL);
+        }
+    }
+
+done:
+    return out_formats;
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index b61ba33..a96dd90 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -199,6 +199,10 @@ struct pa_sink {
      * thread. */
     int (*set_port)(pa_sink *s, pa_device_port *port); /* ditto */
 
+    /* Called to get the list of formats supported by the sink, sorted
+     * in descending order of preference. */
+    pa_idxset* (*get_formats)(pa_sink *s); /* ditto */
+
     /* Contains copies of the above data so that the real-time worker
      * thread can work without access locking */
     struct {
@@ -398,6 +402,9 @@ pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q);
 void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save);
 void pa_sink_move_all_fail(pa_queue *q);
 
+pa_idxset* pa_sink_get_formats(pa_sink *s);
+pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats);
+
 /*** To be called exclusively by the sink driver, from IO context */
 
 void pa_sink_render(pa_sink*s, size_t length, pa_memchunk *result);

commit 0ac2cfce6d1a3d7ab5af6aca659e46625c32d3c4
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Mon Feb 28 13:23:23 2011 +0530

    core: Add extended stream API to support compressed formats
    
    This is the beginning of work to support compressed formats natively in
    PulseAudio. This adds a pa_stream_new_extended() that takes a format
    structure, sends it to the server (=> protocol extension) and has the
    server negotiate with the appropropriate sink to figure out what format
    it should use.
    
    This is work in progress, and works only with PCM streams. Actual
    compressed format support in some sink needs to be implemented, and
    extensive testing is required.
    
    More details on how this is supposed to work is available at:
    http://pulseaudio.org/wiki/PassthroughSupport

diff --git a/PROTOCOL b/PROTOCOL
index a15d116..d06cb98 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -213,3 +213,18 @@ Two new flags at the end of sink input introspection data:
 
     bool has_volume
     bool volume_writable
+
+## v21, implemented by >= 1.0
+
+Changes for format negotiation in the extended API.
+
+New fields PA_COMMAND_CREATE_PLAYBACK_STREAM:
+
+    uint8_t n_formats
+    format_info format1
+    ...
+    format_info formatn
+
+One new field in reply from PA_COMMAND_CREATE_PLAYBACK_STREAM:
+
+    format_info format
diff --git a/configure.ac b/configure.ac
index ec1099b..9edae0e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -37,7 +37,7 @@ AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
 AC_SUBST(PACKAGE_URL, [http://pulseaudio.org/])
 
 AC_SUBST(PA_API_VERSION, 12)
-AC_SUBST(PA_PROTOCOL_VERSION, 20)
+AC_SUBST(PA_PROTOCOL_VERSION, 21)
 
 # The stable ABI for client applications, for the version info x:y:z
 # always will hold y=z
diff --git a/src/map-file b/src/map-file
index 181af9e..b4196f1 100644
--- a/src/map-file
+++ b/src/map-file
@@ -265,6 +265,7 @@ pa_stream_get_timing_info;
 pa_stream_is_corked;
 pa_stream_is_suspended;
 pa_stream_new;
+pa_stream_new_extended;
 pa_stream_new_with_proplist;
 pa_stream_peek;
 pa_stream_prebuf;
diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c
index 3787962..a06e481 100644
--- a/src/modules/echo-cancel/module-echo-cancel.c
+++ b/src/modules/echo-cancel/module-echo-cancel.c
@@ -1549,7 +1549,7 @@ int pa__init(pa_module*m) {
     pa_sink_input_new_data_init(&sink_input_data);
     sink_input_data.driver = __FILE__;
     sink_input_data.module = m;
-    sink_input_data.sink = sink_master;
+    pa_sink_input_new_data_set_sink(&sink_input_data, sink_master, FALSE);
     sink_input_data.origin_sink = u->sink;
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Sink Stream");
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
diff --git a/src/modules/module-combine-sink.c b/src/modules/module-combine-sink.c
index 09af942..f6d6453 100644
--- a/src/modules/module-combine-sink.c
+++ b/src/modules/module-combine-sink.c
@@ -845,7 +845,7 @@ static int output_create_sink_input(struct output *o) {
         return 0;
 
     pa_sink_input_new_data_init(&data);
-    data.sink = o->sink;
+    pa_sink_input_new_data_set_sink(&data, o->sink, FALSE);
     data.driver = __FILE__;
     pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)));
     pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "filter");
diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c
index 47469b0..c28affd 100644
--- a/src/modules/module-device-manager.c
+++ b/src/modules/module-device-manager.c
@@ -832,8 +832,8 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
                 pa_sink *sink;
 
                 if ((sink = pa_idxset_get_by_index(u->core->sinks, device_index))) {
-                    new_data->sink = sink;
-                    new_data->save_sink = FALSE;
+                    if (!pa_sink_input_new_data_set_sink(new_data, sink, FALSE))
+                        pa_log_debug("Not restoring device for stream because no supported format was found");
                 }
             }
         }
diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c
index 9a85fe5..e20ee4a 100644
--- a/src/modules/module-equalizer-sink.c
+++ b/src/modules/module-equalizer-sink.c
@@ -1212,7 +1212,7 @@ int pa__init(pa_module*m) {
     pa_sink_input_new_data_init(&sink_input_data);
     sink_input_data.driver = __FILE__;
     sink_input_data.module = m;
-    sink_input_data.sink = master;
+    pa_sink_input_new_data_set_sink(&sink_input_data, master, FALSE);
     sink_input_data.origin_sink = u->sink;
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream");
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c
index d19444d..9038562 100644
--- a/src/modules/module-intended-roles.c
+++ b/src/modules/module-intended-roles.c
@@ -117,11 +117,8 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
 
     /* Prefer the default sink over any other sink, just in case... */
     if ((def = pa_namereg_get_default_sink(c)))
-        if (role_match(def->proplist, role)) {
-            new_data->sink = def;
-            new_data->save_sink = FALSE;
+        if (role_match(def->proplist, role) && pa_sink_input_new_data_set_sink(new_data, def, FALSE))
             return PA_HOOK_OK;
-        }
 
     /* @todo: favour the highest priority device, not the first one we find? */
     PA_IDXSET_FOREACH(s, c->sinks, idx) {
@@ -131,11 +128,8 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
         if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)))
             continue;
 
-        if (role_match(s->proplist, role)) {
-            new_data->sink = s;
-            new_data->save_sink = FALSE;
+        if (role_match(s->proplist, role) && pa_sink_input_new_data_set_sink(new_data, s, FALSE))
             return PA_HOOK_OK;
-        }
     }
 
     return PA_HOOK_OK;
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index f6430f2..6489f3f 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -907,7 +907,7 @@ int pa__init(pa_module*m) {
     pa_sink_input_new_data_init(&sink_input_data);
     sink_input_data.driver = __FILE__;
     sink_input_data.module = m;
-    sink_input_data.sink = master;
+    pa_sink_input_new_data_set_sink(&sink_input_data, master, FALSE);
     sink_input_data.origin_sink = u->sink;
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream");
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index 9a8640b..ca813b0 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -695,7 +695,7 @@ int pa__init(pa_module *m) {
     pa_sink_input_new_data_init(&sink_input_data);
     sink_input_data.driver = __FILE__;
     sink_input_data.module = m;
-    sink_input_data.sink = sink;
+    pa_sink_input_new_data_set_sink(&sink_input_data, sink, FALSE);
 
     if ((n = pa_modargs_get_value(ma, "sink_input_name", NULL)))
         pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, n);
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index 7f64f30..79627f7 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -419,7 +419,7 @@ int pa__init(pa_module*m) {
     pa_sink_input_new_data_init(&sink_input_data);
     sink_input_data.driver = __FILE__;
     sink_input_data.module = m;
-    sink_input_data.sink = master;
+    pa_sink_input_new_data_set_sink(&sink_input_data, master, FALSE);
     sink_input_data.origin_sink = u->sink;
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream");
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
index 69b2002..cee01f1 100644
--- a/src/modules/module-sine.c
+++ b/src/modules/module-sine.c
@@ -157,7 +157,7 @@ int pa__init(pa_module*m) {
     pa_sink_input_new_data_init(&data);
     data.driver = __FILE__;
     data.module = m;
-    data.sink = sink;
+    pa_sink_input_new_data_set_sink(&data, sink, FALSE);
     pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "%u Hz Sine", frequency);
     pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "abstract");
     pa_proplist_setf(data.proplist, "sine.hz", "%u", frequency);
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 77b6949..fa2c021 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -1301,11 +1301,9 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
         /* It might happen that a stream and a sink are set up at the
            same time, in which case we want to make sure we don't
            interfere with that */
-        if (s && PA_SINK_IS_LINKED(pa_sink_get_state(s))) {
-            pa_log_info("Restoring device for stream %s.", name);
-            new_data->sink = s;
-            new_data->save_sink = TRUE;
-        }
+        if (s && PA_SINK_IS_LINKED(pa_sink_get_state(s)))
+            if (pa_sink_input_new_data_set_sink(new_data, s, TRUE))
+                pa_log_info("Restoring device for stream %s.", name);
 
         pa_xfree(e);
     }
diff --git a/src/modules/module-virtual-sink.c b/src/modules/module-virtual-sink.c
index 9bcff8c..fe26930 100644
--- a/src/modules/module-virtual-sink.c
+++ b/src/modules/module-virtual-sink.c
@@ -579,7 +579,7 @@ int pa__init(pa_module*m) {
     pa_sink_input_new_data_init(&sink_input_data);
     sink_input_data.driver = __FILE__;
     sink_input_data.module = m;
-    sink_input_data.sink = master;
+    pa_sink_input_new_data_set_sink(&sink_input_data, master, FALSE);
     sink_input_data.origin_sink = u->sink;
     pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Sink Stream from %s", pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION));
     pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter");
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index 1144169..fb3bccb 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -512,7 +512,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
         goto fail;
 
     pa_sink_input_new_data_init(&data);
-    data.sink = sink;
+    pa_sink_input_new_data_set_sink(&data, sink, FALSE);
     data.driver = __FILE__;
     pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "stream");
     pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME,
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 53fcca6..d715165 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -122,6 +122,8 @@ typedef struct pa_index_correction {
     pa_bool_t corrupt:1;
 } pa_index_correction;
 
+#define PA_MAX_FORMATS (PA_ENCODING_MAX)
+
 struct pa_stream {
     PA_REFCNT_DECLARE;
     PA_LLIST_FIELDS(pa_stream);
@@ -137,6 +139,9 @@ struct pa_stream {
 
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
+    uint8_t n_formats;
+    pa_format_info *req_formats[PA_MAX_FORMATS];
+    pa_format_info *format;
 
     pa_proplist *proplist;
 
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index aac18a3..f5bf42c 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -80,31 +80,24 @@ static void reset_callbacks(pa_stream *s) {
     s->buffer_attr_userdata = NULL;
 }
 
-pa_stream *pa_stream_new_with_proplist(
+static pa_stream *pa_stream_new_with_proplist_internal(
         pa_context *c,
         const char *name,
         const pa_sample_spec *ss,
         const pa_channel_map *map,
+        pa_format_info * const *formats,
         pa_proplist *p) {
 
     pa_stream *s;
     int i;
-    pa_channel_map tmap;
 
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert((ss == NULL && map == NULL) || formats == NULL);
 
     PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED);
-    PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
     PA_CHECK_VALIDITY_RETURN_NULL(c, name || (p && pa_proplist_contains(p, PA_PROP_MEDIA_NAME)), PA_ERR_INVALID);
 
-    if (!map)
-        PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
-
     s = pa_xnew(pa_stream, 1);
     PA_REFCNT_INIT(s);
     s->context = c;
@@ -114,8 +107,28 @@ pa_stream *pa_stream_new_with_proplist(
     s->state = PA_STREAM_UNCONNECTED;
     s->flags = 0;
 
-    s->sample_spec = *ss;
-    s->channel_map = *map;
+    if (ss)
+        s->sample_spec = *ss;
+    else
+        s->sample_spec.format = PA_SAMPLE_INVALID;
+
+    if (map)
+        s->channel_map = *map;
+    else
+        pa_channel_map_init(&s->channel_map);
+
+    s->n_formats = 0;
+    if (formats) {
+        for (i = 0; formats[i] && i < PA_MAX_FORMATS; i++) {
+            s->n_formats++;
+            s->req_formats[i] = pa_format_info_copy(formats[i]);
+        }
+        /* Make sure the input array was NULL-terminated */
+        pa_assert(formats[i] == NULL);
+    }
+
+    /* We'll get the final negotiated format after connecting */
+    s->format = NULL;
 
     s->direct_on_input = PA_INVALID_INDEX;
 
@@ -136,7 +149,10 @@ pa_stream *pa_stream_new_with_proplist(
      * what older PA versions provided. */
 
     s->buffer_attr.maxlength = (uint32_t) -1;
-    s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */
+    if (ss)
+        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */
+    else
+        /* XXX: How do we apply worst case conversion here? */
     s->buffer_attr.minreq = (uint32_t) -1;
     s->buffer_attr.prebuf = (uint32_t) -1;
     s->buffer_attr.fragsize = (uint32_t) -1;
@@ -179,6 +195,40 @@ pa_stream *pa_stream_new_with_proplist(
     return s;
 }
 
+pa_stream *pa_stream_new_with_proplist(
+        pa_context *c,
+        const char *name,
+        const pa_sample_spec *ss,
+        const pa_channel_map *map,
+        pa_proplist *p) {
+
+    pa_channel_map tmap;
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, ss && pa_sample_spec_valid(ss), PA_ERR_INVALID);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 12 || (ss->format != PA_SAMPLE_S32LE && ss->format != PA_SAMPLE_S32BE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24LE && ss->format != PA_SAMPLE_S24BE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 15 || (ss->format != PA_SAMPLE_S24_32LE && ss->format != PA_SAMPLE_S24_32BE), PA_ERR_NOTSUPPORTED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !map || (pa_channel_map_valid(map) && map->channels == ss->channels), PA_ERR_INVALID);
+
+    if (!map)
+        PA_CHECK_VALIDITY_RETURN_NULL(c, map = pa_channel_map_init_auto(&tmap, ss->channels, PA_CHANNEL_MAP_DEFAULT), PA_ERR_INVALID);
+
+    return pa_stream_new_with_proplist_internal(c, name, ss, map, NULL, p);
+}
+
+pa_stream *pa_stream_new_extended(
+        pa_context *c,
+        const char *name,
+        pa_format_info * const *formats,
+        pa_proplist *p) {
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 21, PA_ERR_NOTSUPPORTED);
+
+    /* XXX: For the single-format PCM case, pass ss/map instead of formats */
+
+    return pa_stream_new_with_proplist_internal(c, name, NULL, NULL, formats, p);
+}
+
 static void stream_unlink(pa_stream *s) {
     pa_operation *o, *n;
     pa_assert(s);
@@ -220,6 +270,8 @@ static void stream_unlink(pa_stream *s) {
 }
 
 static void stream_free(pa_stream *s) {
+    unsigned int i;
+
     pa_assert(s);
 
     stream_unlink(s);
@@ -244,6 +296,9 @@ static void stream_free(pa_stream *s) {
     if (s->smoother)
         pa_smoother_free(s->smoother);
 
+    for (i = 0; i < s->n_formats; i++)
+        pa_xfree(s->req_formats[i]);
+
     pa_xfree(s->device_name);
     pa_xfree(s);
 }
@@ -970,9 +1025,11 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
             ss.channels != cm.channels ||
             !pa_channel_map_valid(&cm) ||
             !pa_sample_spec_valid(&ss) ||
-            (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
-            (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) ||
-            (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))) {
+            (s->n_formats == 0 && (
+                (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
+                (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) ||
+                (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))))) {
+            /* XXX: checks for the n_formats > 0 case? */
             pa_context_fail(s->context, PA_ERR_PROTOCOL);
             goto finish;
         }
@@ -999,6 +1056,16 @@ 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) {
+        pa_format_info *f = pa_format_info_new();
+        pa_tagstruct_get_format_info(t, f);
+
+        if (pa_format_info_valid(f))
+            s->format = f;
+        else
+            pa_format_info_free(f);
+    }
+
     if (!pa_tagstruct_eof(t)) {
         pa_context_fail(s->context, PA_ERR_PROTOCOL);
         goto finish;
@@ -1039,6 +1106,7 @@ static int create_stream(
     pa_tagstruct *t;
     uint32_t tag;
     pa_bool_t volume_set = FALSE;
+    uint32_t i;
 
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
@@ -1079,7 +1147,7 @@ static int create_stream(
 
     PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_PLAYBACK || !(flags & (PA_STREAM_START_MUTED)), PA_ERR_INVALID);
     PA_CHECK_VALIDITY(s->context, direction == PA_STREAM_RECORD || !(flags & (PA_STREAM_PEAK_DETECT)), PA_ERR_INVALID);
-    PA_CHECK_VALIDITY(s->context, !volume || volume->channels == s->sample_spec.channels, PA_ERR_INVALID);
+    PA_CHECK_VALIDITY(s->context, !volume || (pa_sample_spec_valid(&s->sample_spec) && volume->channels == s->sample_spec.channels), PA_ERR_INVALID);
     PA_CHECK_VALIDITY(s->context, !sync_stream || (direction == PA_STREAM_PLAYBACK && sync_stream->direction == PA_STREAM_PLAYBACK), PA_ERR_INVALID);
     PA_CHECK_VALIDITY(s->context, (flags & (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS)) != (PA_STREAM_ADJUST_LATENCY|PA_STREAM_EARLY_REQUESTS), PA_ERR_INVALID);
 
@@ -1147,8 +1215,16 @@ static int create_stream(
 
         volume_set = !!volume;
 
-        if (!volume)
-            volume = pa_cvolume_reset(&cv, s->sample_spec.channels);
+        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
@@ -1214,6 +1290,15 @@ static int create_stream(
             pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH));
     }
 
+    if (s->context->version >= 21) {
+
+        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_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/pulse/stream.h b/src/pulse/stream.h
index dd67db7..48bf09d 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 
 #include <pulse/sample.h>
+#include <pulse/format.h>
 #include <pulse/channelmap.h>
 #include <pulse/volume.h>
 #include <pulse/def.h>
@@ -356,6 +357,16 @@ pa_stream* pa_stream_new_with_proplist(
         const pa_channel_map *map         /**< The desired channel map, or NULL for default */,
         pa_proplist *p                    /**< The initial property list */);
 
+/* Create a new, unconnected stream with the specified name, the set of formats
+ * this client can provide, and an initial list of properties. While
+ * connecting, the server will select the most appropriate format which the
+ * client must then provide. \since 1.0 */
+pa_stream *pa_stream_new_extended(
+        pa_context *c                     /**< The context to create this stream in */,
+        const char *name                  /**< A name for this stream */,
+        pa_format_info * const * formats  /**< The list of formats that can be provided */,
+        pa_proplist *p                    /**< The initial property list */);
+
 /** Decrease the reference counter by one */
 void pa_stream_unref(pa_stream *s);
 
diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index 66e47ea..9455340 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -202,7 +202,7 @@ pa_sink_input* pa_memblockq_sink_input_new(
     u->memblockq = NULL;
 
     pa_sink_input_new_data_init(&data);
-    data.sink = sink;
+    pa_sink_input_new_data_set_sink(&data, sink, FALSE);
     data.driver = __FILE__;
     pa_sink_input_new_data_set_sample_spec(&data, ss);
     pa_sink_input_new_data_set_channel_map(&data, map);
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
index 66fd73c..c2af77c 100644
--- a/src/pulsecore/protocol-esound.c
+++ b/src/pulsecore/protocol-esound.c
@@ -426,7 +426,7 @@ 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;
-    sdata.sink = 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);
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 4952ee4..8332179 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -1014,6 +1014,7 @@ static playback_stream* playback_stream_new(
         pa_sink *sink,
         pa_sample_spec *ss,
         pa_channel_map *map,
+        pa_idxset *formats,
         pa_buffer_attr *a,
         pa_cvolume *volume,
         pa_bool_t muted,
@@ -1067,12 +1068,14 @@ static playback_stream* playback_stream_new(
     data.driver = __FILE__;
     data.module = c->options->module;
     data.client = c->client;
-    if (sink) {
-        data.sink = sink;
-        data.save_sink = TRUE;
-    }
-    pa_sink_input_new_data_set_sample_spec(&data, ss);
-    pa_sink_input_new_data_set_channel_map(&data, map);
+    if (sink)
+        pa_sink_input_new_data_set_sink(&data, sink, TRUE);
+    if (pa_sample_spec_valid(ss))
+        pa_sink_input_new_data_set_sample_spec(&data, ss);
+    if (pa_channel_map_valid(map))
+        pa_sink_input_new_data_set_channel_map(&data, map);
+    if (formats)
+        pa_sink_input_new_data_set_formats(&data, formats);
     if (volume) {
         pa_sink_input_new_data_set_volume(&data, volume);
         data.volume_is_absolute = !relative_volume;
@@ -1846,6 +1849,10 @@ static pa_tagstruct *reply_new(uint32_t tag) {
     return reply;
 }
 
+static void free_format_info(pa_format_info *f, void *userdata) {
+    pa_format_info_free(f);
+}
+
 static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     playback_stream *s;
@@ -1876,9 +1883,13 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         passthrough = FALSE;
 
     pa_sink_input_flags_t flags = 0;
-    pa_proplist *p;
+    pa_proplist *p = NULL;
     pa_bool_t volume_set = TRUE;
     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);
@@ -1901,17 +1912,14 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
                 PA_TAG_INVALID) < 0) {
 
         protocol_error(c);
-        return;
+        goto error;
     }
 
     CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
     CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID);
     CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID);
     CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
     CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID);
 
     p = pa_proplist_new();
 
@@ -1930,8 +1938,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
             pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
 
             protocol_error(c);
-            pa_proplist_free(p);
-            return;
+            goto error;
         }
     }
 
@@ -1940,9 +1947,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         if (pa_tagstruct_get_boolean(t, &muted) < 0 ||
             pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
             pa_tagstruct_get_proplist(t, p) < 0) {
+
             protocol_error(c);
-            pa_proplist_free(p);
-            return;
+            goto error;
         }
     }
 
@@ -1950,9 +1957,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
 
         if (pa_tagstruct_get_boolean(t, &volume_set) < 0 ||
             pa_tagstruct_get_boolean(t, &early_requests) < 0) {
+
             protocol_error(c);
-            pa_proplist_free(p);
-            return;
+            goto error;
         }
     }
 
@@ -1961,18 +1968,18 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         if (pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
             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 error;
         }
     }
 
     if (c->version >= 17) {
 
         if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) {
+
             protocol_error(c);
-            pa_proplist_free(p);
-            return;
+            goto error;
         }
     }
 
@@ -1980,31 +1987,52 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
 
         if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) {
             protocol_error(c);
-            pa_proplist_free(p);
-            return;
+            goto error;
+        }
+    }
+
+    if (c->version >= 21) {
+
+        if (pa_tagstruct_getu8(t, &n_formats) < 0) {
+            protocol_error(c);
+            goto error;
+        }
+
+        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 error;
+            }
+            pa_idxset_put(formats, format, NULL);
         }
     }
 
+    CHECK_VALIDITY(c->pstream, n_formats > 0 || pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, n_formats > 0 || (map.channels == ss.channels && volume.channels == ss.channels), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, n_formats > 0 || pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
+    /* XXX: add checks on formats. At least inverse checks of the 3 above */
+
     if (!pa_tagstruct_eof(t)) {
         protocol_error(c);
-        pa_proplist_free(p);
-        return;
+        goto error;
     }
 
     if (sink_index != PA_INVALID_INDEX) {
 
         if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
-            pa_proplist_free(p);
-            return;
+            goto error;
         }
 
     } else if (sink_name) {
 
         if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
-            pa_proplist_free(p);
-            return;
+            goto error;
         }
     }
 
@@ -2025,7 +2053,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, &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, syncid, &missing, flags, p, adjust_latency, early_requests, relative_volume, &ret);
     pa_proplist_free(p);
 
     CHECK_VALIDITY(c->pstream, s, tag, ret);
@@ -2064,7 +2092,26 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     if (c->version >= 13)
         pa_tagstruct_put_usec(reply, s->configured_sink_latency);
 
+    if (c->version >= 21) {
+        /* Send back the format we negotiated */
+        if (s->sink_input->format)
+            pa_tagstruct_put_format_info(reply, s->sink_input->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);
+    return;
+
+error:
+    if (p)
+        pa_proplist_free(p);
+    if (formats)
+        pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL);
+    return;
 }
 
 static void command_delete_stream(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 77277e1..c1aaa81 100644
--- a/src/pulsecore/protocol-simple.c
+++ b/src/pulsecore/protocol-simple.c
@@ -538,7 +538,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.sink = sink;
+        pa_sink_input_new_data_set_sink(&data, sink, FALSE);
         pa_proplist_update(data.proplist, PA_UPDATE_MERGE, c->client->proplist);
         pa_sink_input_new_data_set_sample_spec(&data, &o->sample_spec);
 
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 194ec99..3df499e 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -31,6 +31,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>
@@ -146,9 +147,75 @@ void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mu
     data->muted = !!mute;
 }
 
+static void free_format_info(pa_format_info *f, void *userdata) {
+    pa_format_info_free(f);
+}
+
+pa_bool_t pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *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->sink = s;
+        data->save_sink = save;
+    } else {
+        /* Extended API: let's see if this sink supports the formats the client can provide */
+        formats = pa_sink_check_formats(s, data->req_formats);
+
+        if (formats && !pa_idxset_isempty(formats)) {
+            /* Sink supports at least one of the requested formats */
+            data->sink = s;
+            data->save_sink = save;
+            if (data->nego_formats)
+                pa_idxset_free(data->nego_formats, (pa_free2_cb_t) free_format_info, NULL);
+            data->nego_formats = formats;
+        } else {
+            /* Sink doesn't support any of the formats requested by the client */
+            if (formats)
+                pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL);
+            ret = FALSE;
+        }
+    }
+
+    return ret;
+}
+
+pa_bool_t pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_idxset *formats) {
+    pa_assert(data);
+    pa_assert(formats);
+
+    if (data->req_formats)
+        pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL);
+
+    data->req_formats = formats;
+
+    if (data->sink) {
+        /* Trigger format negotiation */
+        return pa_sink_input_new_data_set_sink(data, data->sink, data->save_sink);
+    }
+
+    return TRUE;
+}
+
 void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
+    pa_format_info *f;
+    int i;
+
     pa_assert(data);
 
+    if (data->req_formats)
+        pa_idxset_free(data->req_formats, (pa_free2_cb_t) free_format_info, NULL);
+
+    if (data->nego_formats)
+        pa_idxset_free(data->nego_formats, (pa_free2_cb_t) free_format_info, NULL);
+
+    if (data->format)
+        pa_format_info_free(data->format);
+
     pa_proplist_free(data->proplist);
 }
 
@@ -189,6 +256,8 @@ int pa_sink_input_new(
     pa_channel_map original_cm;
     int r;
     char *pt;
+    pa_sample_spec ss;
+    pa_channel_map map;
 
     pa_assert(_i);
     pa_assert(core);
@@ -201,14 +270,43 @@ int pa_sink_input_new(
     if (data->origin_sink && (data->origin_sink->flags & PA_SINK_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.
+         * 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_sink_input_new_data_set_formats(data, tmp);
+    }
+
     if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0)
         return r;
 
     pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
 
-    if (!data->sink) {
-        data->sink = pa_namereg_get(core, NULL, PA_NAMEREG_SINK);
-        data->save_sink = FALSE;
+    if (!data->sink)
+        pa_sink_input_new_data_set_sink(data, pa_namereg_get(core, NULL, PA_NAMEREG_SINK), FALSE);
+
+    /* Routing's done, we have a sink. Now let's fix the format and set up the
+     * sample spec */
+    pa_return_val_if_fail(data->format || (data->nego_formats && !pa_idxset_isempty(data->nego_formats)), -PA_ERR_INVALID);
+    /* 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->format = pa_format_info_copy(pa_idxset_first(data->nego_formats, NULL));
+    /* 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_format_info_to_sample_spec(data->format, &ss, &map);
+        pa_sink_input_new_data_set_sample_spec(data, &ss);
+        if (pa_channel_map_valid(&map))
+            pa_sink_input_new_data_set_channel_map(data, &map);
+    } else {
+        pa_format_info_to_sample_spec_fake(data->format, &ss);
+        pa_sink_input_new_data_set_sample_spec(data, &ss);
+        /* XXX: this is redundant - we can just check the encoding */
+        data->flags |= PA_SINK_INPUT_PASSTHROUGH;
     }
 
     pa_return_val_if_fail(data->sink, -PA_ERR_NOENTITY);
@@ -329,6 +427,7 @@ int pa_sink_input_new(
     i->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID;
     i->sample_spec = data->sample_spec;
     i->channel_map = data->channel_map;
+    i->format = pa_format_info_copy(data->format);
 
     if (!data->volume_is_absolute && pa_sink_flat_volume_enabled(i->sink)) {
         pa_cvolume remapped;
@@ -564,6 +663,9 @@ static void sink_input_free(pa_object *o) {
     if (i->thread_info.resampler)
         pa_resampler_free(i->thread_info.resampler);
 
+    if (i->format)
+        pa_format_info_free(i->format);
+
     if (i->proplist)
         pa_proplist_free(i->proplist);
 
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 11f6608..72a1d5a 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -28,6 +28,7 @@
 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>
@@ -92,6 +93,7 @@ struct pa_sink_input {
 
     pa_sample_spec sample_spec;
     pa_channel_map channel_map;
+    pa_format_info *format;
 
     pa_sink_input *sync_prev, *sync_next;
 
@@ -279,6 +281,9 @@ typedef struct pa_sink_input_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_cvolume volume, volume_factor, volume_factor_sink;
     pa_bool_t muted:1;
@@ -303,6 +308,8 @@ void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cv
 void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
 void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
 void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mute);
+pa_bool_t pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, pa_bool_t save);
+pa_bool_t pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_idxset *formats);
 void pa_sink_input_new_data_done(pa_sink_input_new_data *data);
 
 /* To be called by the implementing module only */
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index 1ec1942..d33eca5 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -299,7 +299,7 @@ int pa_play_file(
     u->readf_function = pa_sndfile_readf_function(&ss);
 
     pa_sink_input_new_data_init(&data);
-    data.sink = sink;
+    pa_sink_input_new_data_set_sink(&data, sink, FALSE);
     data.driver = __FILE__;
     pa_sink_input_new_data_set_sample_spec(&data, &ss);
     pa_sink_input_new_data_set_channel_map(&data, &cm);

commit 54c391e6db550c5519df0ebb37f2197eed440c92
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Tue Mar 1 16:34:06 2011 +0530

    format: Add convenience API to check if a format is PCM or not

diff --git a/src/map-file b/src/map-file
index b4196f1..3b1de10 100644
--- a/src/map-file
+++ b/src/map-file
@@ -162,6 +162,7 @@ pa_ext_stream_restore_test;
 pa_ext_stream_restore_write;
 pa_format_info_copy;
 pa_format_info_free;
+pa_format_info_is_pcm;
 pa_format_info_new;
 pa_format_info_valid;
 pa_frame_size;
diff --git a/src/pulse/format.c b/src/pulse/format.c
index c3962de..4ecd75e 100644
--- a/src/pulse/format.c
+++ b/src/pulse/format.c
@@ -70,6 +70,10 @@ int pa_format_info_valid(pa_format_info *f) {
     return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL);
 }
 
+int pa_format_info_is_pcm(pa_format_info *f) {
+    return f->encoding == PA_ENCODING_PCM;
+}
+
 pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) {
     const char *key;
     void *state = NULL;
diff --git a/src/pulse/format.h b/src/pulse/format.h
index fdf4f37..7e3ab08 100644
--- a/src/pulse/format.h
+++ b/src/pulse/format.h
@@ -74,6 +74,9 @@ void pa_format_info_free(pa_format_info *f);
 /** Returns non-zero when the format info structure is valid */
 int pa_format_info_valid(pa_format_info *f);
 
+/** Returns non-zero when the format info structure represents a PCM (i.e. uncompressed data) format */
+int pa_format_info_is_pcm(pa_format_info *f);
+
 PA_C_DECL_END
 
 #endif

commit 71ec9577cf052558cfddb051c7d038c91b397bc5
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Mar 2 02:06:54 2011 +0530

    sink: Remove PASSTHROUGH flag
    
    This removes the passthrough flag from sinks since we will drop
    exclusively passthrough sinks in favour of providing a list of formats
    supported by each sink. We can still determine whether a sink is in
    passthrough mode by checking if any non-PCM streams are attached to it.

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index ccbc062..ec840af 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1707,13 +1707,6 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v
             return 0;
     }
 
-    /* FIXME: need automatic detection rather than hard-coded path */
-    if (!strcmp(u->mixer_path->name, "iec958-passthrough-output")) {
-        u->sink->flags |= PA_SINK_PASSTHROUGH;
-    } else {
-        u->sink->flags &= ~PA_SINK_PASSTHROUGH;
-    }
-
     if (!u->mixer_path->has_volume)
         pa_log_info("Driver does not support hardware volume control, falling back to software volume control.");
     else {
diff --git a/src/pulse/def.h b/src/pulse/def.h
index a3b8622..91a027e 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -739,12 +739,7 @@ typedef enum pa_sink_flags {
     /**< The latency can be adjusted dynamically depending on the
      * needs of the connected streams. \since 0.9.15 */
 
-    PA_SINK_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_SINK_SYNC_VOLUME = 0x0200U,
+    PA_SINK_SYNC_VOLUME = 0x0100U,
     /**< The HW volume changes are syncronized with SW volume.
      * \since 1.0 */
 
@@ -753,7 +748,7 @@ typedef enum pa_sink_flags {
      * The server will filter out these flags anyway, so you should never see
      * these flags in sinks. */
 
-    PA_SINK_SHARE_VOLUME_WITH_MASTER = 0x0400U,
+    PA_SINK_SHARE_VOLUME_WITH_MASTER = 0x0200U,
     /**< This sink shares the volume with the master sink (used by some filter
      * sinks). */
 /** \endcond */
@@ -769,7 +764,6 @@ typedef enum pa_sink_flags {
 #define PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
 #define PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
 #define PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
-#define PA_SINK_PASSTHROUGH PA_SINK_PASSTHROUGH
 #define PA_SINK_SYNC_VOLUME PA_SINK_SYNC_VOLUME
 #define PA_SINK_SHARE_VOLUME_WITH_MASTER PA_SINK_SHARE_VOLUME_WITH_MASTER
 
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 3df499e..b05373f 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -50,36 +50,19 @@ PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject);
 static void sink_input_free(pa_object *o);
 static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);
 
-static int check_passthrough_connection(pa_sink_input_flags_t flags, pa_sink *dest) {
+static int check_passthrough_connection(pa_format_info *format, pa_sink *dest) {
 
-    if (dest->flags & PA_SINK_PASSTHROUGH) {
-
-        if (pa_idxset_size(dest->inputs) > 0) {
-
-            pa_sink_input *alt_i;
-            uint32_t idx;
-
-            alt_i = pa_idxset_first(dest->inputs, &idx);
-
-            /* only need to check the first input is not PASSTHROUGH */
-            if (alt_i->flags & PA_SINK_INPUT_PASSTHROUGH) {
-                pa_log_warn("Sink is already connected to PASSTHROUGH input");
-                return -PA_ERR_BUSY;
-            }
-
-            /* Current inputs are PCM, check new input is not PASSTHROUGH */
-            if (flags & PA_SINK_INPUT_PASSTHROUGH) {
-                pa_log_warn("Sink is already connected, cannot accept new PASSTHROUGH INPUT");
-                return -PA_ERR_BUSY;
-            }
-        }
+    if (pa_sink_is_passthrough(dest)) {
+        pa_log_warn("Sink is already connected to PASSTHROUGH input");
+        return -PA_ERR_BUSY;
+    }
 
-    } else {
-        if (flags & PA_SINK_INPUT_PASSTHROUGH) {
-            pa_log_warn("Cannot connect PASSTHROUGH sink input to sink without PASSTHROUGH capabilities");
-            return -PA_ERR_INVALID;
-        }
+    /* If current input(s) exist, check new input is not PASSTHROUGH */
+    if (pa_idxset_size(dest->inputs) > 0 && !pa_format_info_is_pcm(format)) {
+        pa_log_warn("Sink is already connected, cannot accept new PASSTHROUGH INPUT");
+        return -PA_ERR_BUSY;
     }
+
     return PA_OK;
 }
 
@@ -313,7 +296,7 @@ int pa_sink_input_new(
     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);
 
-    r = check_passthrough_connection(data->flags, data->sink);
+    r = check_passthrough_connection(data->format, data->sink);
     pa_return_val_if_fail(r == PA_OK, r);
 
     if (!data->sample_spec_is_set)
@@ -1355,7 +1338,7 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
         return FALSE;
     }
 
-    if (check_passthrough_connection(i->flags, dest) < 0)
+    if (check_passthrough_connection(i->format, dest) < 0)
         return FALSE;
 
     if (i->may_move_to)
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 345d090..33923e1 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1241,6 +1241,24 @@ pa_bool_t pa_sink_flat_volume_enabled(pa_sink *s) {
     return (s->flags & PA_SINK_FLAT_VOLUME);
 }
 
+/* Called from main context */
+pa_bool_t pa_sink_is_passthrough(pa_sink *s) {
+    pa_sink_input *alt_i;
+    uint32_t idx;
+
+    pa_sink_assert_ref(s);
+
+    /* one and only one PASSTHROUGH input can possibly be connected */
+    if (pa_idxset_size(s->inputs) == 1) {
+        alt_i = pa_idxset_first(s->inputs, &idx);
+
+        if (!pa_format_info_is_pcm(alt_i->format))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
 /* Called from main context. */
 static void compute_reference_ratio(pa_sink_input *i) {
     unsigned c = 0;
@@ -1632,21 +1650,10 @@ void pa_sink_set_volume(
     pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
 
     /* make sure we don't change the volume when a PASSTHROUGH input is connected */
-    if (s->flags & PA_SINK_PASSTHROUGH) {
-        pa_sink_input *alt_i;
-        uint32_t idx;
-
-        /* one and only one PASSTHROUGH input can possibly be connected */
-        if (pa_idxset_size(s->inputs) == 1) {
-
-            alt_i = pa_idxset_first(s->inputs, &idx);
-
-            if (alt_i->flags & PA_SINK_INPUT_PASSTHROUGH) {
-                /* FIXME: Need to notify client that volume control is disabled */
-                pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input");
-                return;
-            }
-        }
+    if (pa_sink_is_passthrough(s)) {
+        /* FIXME: Need to notify client that volume control is disabled */
+        pa_log_warn("Cannot change volume, Sink is connected to PASSTHROUGH input");
+        return;
     }
 
     /* In case of volume sharing, the volume is set for the root sink first,
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index a96dd90..492abf6 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -382,6 +382,10 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause)
 /* Use this instead of checking s->flags & PA_SINK_FLAT_VOLUME directly. */
 pa_bool_t pa_sink_flat_volume_enabled(pa_sink *s);
 
+/* Is the sink in passthrough mode? (that is, is there a passthrough sink input
+ * connected to this sink? */
+pa_bool_t pa_sink_is_passthrough(pa_sink *s);
+
 void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t sendmsg, pa_bool_t save);
 const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
 

commit e64f02ad9ec7d027e0360e3964e88a497d5df02f
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Mar 2 10:51:56 2011 +0530

    tests: Add a trivial test for the extended API
    
    This is just sync-playback.c modified to use the extended API. We need
    something more sophisticated for properly testing the compressed
    formats, but that is a non-trivial task in itself.

diff --git a/src/Makefile.am b/src/Makefile.am
index 1b8b447..e321704 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -274,6 +274,7 @@ TESTS_BINARIES = \
 		mcalign-test \
 		pacat-simple \
 		parec-simple \
+		extended-test \
 		strlist-test \
 		close-test \
 		voltest \
@@ -439,6 +440,11 @@ parec_simple_LDADD = $(AM_LDADD) libpulse.la libpulse-simple.la libpulsecommon-@
 parec_simple_CFLAGS = $(AM_CFLAGS)
 parec_simple_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
 
+extended_test_SOURCES = tests/extended-test.c
+extended_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon- at PA_MAJORMINOR@.la
+extended_test_CFLAGS = $(AM_CFLAGS)
+extended_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
 strlist_test_SOURCES = tests/strlist-test.c
 strlist_test_CFLAGS = $(AM_CFLAGS)
 strlist_test_LDADD = $(AM_LDADD) $(WINSOCK_LIBS) libpulsecore- at PA_MAJORMINOR@.la libpulse.la libpulsecommon- at PA_MAJORMINOR@.la
diff --git a/src/tests/extended-test.c b/src/tests/extended-test.c
new file mode 100644
index 0000000..ec3cc53
--- /dev/null
+++ b/src/tests/extended-test.c
@@ -0,0 +1,197 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  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 <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>
+#include <pulse/mainloop.h>
+
+#define NSTREAMS 4
+#define SINE_HZ 440
+#define SAMPLE_HZ 8000
+
+static pa_context *context = NULL;
+static pa_stream *streams[NSTREAMS];
+static pa_mainloop_api *mainloop_api = NULL;
+
+static float data[SAMPLE_HZ]; /* one second space */
+
+static int n_streams_ready = 0;
+
+static const pa_buffer_attr buffer_attr = {
+    .maxlength = SAMPLE_HZ*sizeof(float)*NSTREAMS, /* exactly space for the entire play time */
+    .tlength = (uint32_t) -1,
+    .prebuf = 0, /* Setting prebuf to 0 guarantees us the the streams will run synchronously, no matter what */
+    .minreq = (uint32_t) -1,
+    .fragsize = 0
+};
+
+static void nop_free_cb(void *p) {}
+
+static void underflow_cb(struct pa_stream *s, void *userdata) {
+    int i = (int) (long) userdata;
+
+    fprintf(stderr, "Stream %i finished\n", i);
+
+    if (++n_streams_ready >= 2*NSTREAMS) {
+        fprintf(stderr, "We're done\n");
+        mainloop_api->quit(mainloop_api, 0);
+    }
+}
+
+/* This routine is called whenever the stream state changes */
+static void stream_state_callback(pa_stream *s, void *userdata) {
+    assert(s);
+
+    switch (pa_stream_get_state(s)) {
+        case PA_STREAM_UNCONNECTED:
+        case PA_STREAM_CREATING:
+        case PA_STREAM_TERMINATED:
+            break;
+
+        case PA_STREAM_READY: {
+
+            int r, i = (int) (long) userdata;
+
+            fprintf(stderr, "Writing data to stream %i.\n", i);
+
+            r = pa_stream_write(s, data, sizeof(data), nop_free_cb, (int64_t) sizeof(data) * (int64_t) i, PA_SEEK_ABSOLUTE);
+            assert(r == 0);
+
+            /* Be notified when this stream is drained */
+            pa_stream_set_underflow_callback(s, underflow_cb, userdata);
+
+            /* All streams have been set up, let's go! */
+            if (++n_streams_ready >= NSTREAMS) {
+                fprintf(stderr, "Uncorking\n");
+                pa_operation_unref(pa_stream_cork(s, 0, NULL, NULL));
+            }
+
+            break;
+        }
+
+        default:
+        case PA_STREAM_FAILED:
+            fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
+            abort();
+    }
+}
+
+/* This is called whenever the context status changes */
+static void context_state_callback(pa_context *c, void *userdata) {
+    assert(c);
+
+    switch (pa_context_get_state(c)) {
+        case PA_CONTEXT_CONNECTING:
+        case PA_CONTEXT_AUTHORIZING:
+        case PA_CONTEXT_SETTING_NAME:
+            break;
+
+        case PA_CONTEXT_READY: {
+
+            int i;
+            fprintf(stderr, "Connection established.\n");
+
+            for (i = 0; i < NSTREAMS; i++) {
+                char name[64];
+                pa_format_info *formats[2] = { NULL };
+
+                formats[0] = pa_format_info_new();
+                formats[0]->encoding = PA_ENCODING_PCM;
+                pa_proplist_sets(formats[0]->plist, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(PA_SAMPLE_FLOAT32));
+                pa_proplist_setf(formats[0]->plist, PA_PROP_FORMAT_RATE, "%u", SAMPLE_HZ);
+                pa_proplist_setf(formats[0]->plist, PA_PROP_FORMAT_CHANNELS, "%u", 1);
+
+                fprintf(stderr, "Creating stream %i\n", i);
+
+                snprintf(name, sizeof(name), "stream #%i", i);
+
+                streams[i] = pa_stream_new_extended(c, name, formats, NULL);
+                assert(streams[i]);
+                pa_stream_set_state_callback(streams[i], stream_state_callback, (void*) (long) i);
+                pa_stream_connect_playback(streams[i], NULL, &buffer_attr, PA_STREAM_START_CORKED, NULL, i == 0 ? NULL : streams[0]);
+            }
+
+            break;
+        }
+
+        case PA_CONTEXT_TERMINATED:
+            mainloop_api->quit(mainloop_api, 0);
+            break;
+
+        case PA_CONTEXT_FAILED:
+        default:
+            fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c)));
+            abort();
+    }
+}
+
+int main(int argc, char *argv[]) {
+    pa_mainloop* m = NULL;
+    int i, ret = 0;
+
+    for (i = 0; i < SAMPLE_HZ; i++)
+        data[i] = (float) sin(((double) i/SAMPLE_HZ)*2*M_PI*SINE_HZ)/2;
+
+    for (i = 0; i < NSTREAMS; i++)
+        streams[i] = NULL;
+
+    /* Set up a new main loop */
+    m = pa_mainloop_new();
+    assert(m);
+
+    mainloop_api = pa_mainloop_get_api(m);
+
+    context = pa_context_new(mainloop_api, argv[0]);
+    assert(context);
+
+    pa_context_set_state_callback(context, context_state_callback, NULL);
+
+    /* Connect the context */
+    if (pa_context_connect(context, NULL, 0, NULL) < 0) {
+        fprintf(stderr, "pa_context_connect() failed.\n");
+        goto quit;
+    }
+
+    if (pa_mainloop_run(m, &ret) < 0)
+        fprintf(stderr, "pa_mainloop_run() failed.\n");
+
+quit:
+    pa_context_unref(context);
+
+    for (i = 0; i < NSTREAMS; i++)
+        if (streams[i])
+            pa_stream_unref(streams[i]);
+
+    pa_mainloop_free(m);
+
+    return ret;
+}

commit 5d5523604f03ac7d6b8f67569ed4ac6c06c72463
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Mar 2 10:53:45 2011 +0530

    sink-input: Minor cleanups
    
    Removes a couple of warnings and simplifies the assertion logic that
    verifies format negotiation was successful.

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index b05373f..92e364f 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -185,9 +185,6 @@ pa_bool_t pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_id
 }
 
 void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
-    pa_format_info *f;
-    int i;
-
     pa_assert(data);
 
     if (data->req_formats)
@@ -273,11 +270,14 @@ int pa_sink_input_new(
 
     /* Routing's done, we have a sink. Now let's fix the format and set up the
      * sample spec */
-    pa_return_val_if_fail(data->format || (data->nego_formats && !pa_idxset_isempty(data->nego_formats)), -PA_ERR_INVALID);
+
     /* 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)
+    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_INVALID);
+
     /* 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)) {

commit 8ec0548f5f4c7ce3213e4639722d9cb78fc13b90
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Mar 2 11:00:49 2011 +0530

    sink-input: Return NOTSUPPORTED if format negotiation fails
    
    This is easier for clients to grok than INVALID.

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 92e364f..1706a7f 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -276,7 +276,7 @@ int pa_sink_input_new(
     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_INVALID);
+    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 */

commit 13229fb39e7b8cff3f114c98f8236d9123442243
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Mar 2 11:16:07 2011 +0530

    sink-input: Don't assert on bad formats
    
    Handles bad format input more gracefully and returns an error instead.

diff --git a/src/pulse/format.c b/src/pulse/format.c
index 4ecd75e..af24eb2 100644
--- a/src/pulse/format.c
+++ b/src/pulse/format.c
@@ -120,21 +120,21 @@ pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_m
 }
 
 /* For PCM streams */
-void pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
+pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
     const char *sf, *r, *ch;
     uint32_t channels;
 
     pa_assert(f);
     pa_assert(ss);
-    pa_assert(f->encoding == PA_ENCODING_PCM);
+    pa_return_val_if_fail(f->encoding == PA_ENCODING_PCM, FALSE);
 
-    pa_assert(sf = pa_proplist_gets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT));
-    pa_assert(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE));
-    pa_assert(ch = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNELS));
+    pa_return_val_if_fail(sf = pa_proplist_gets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT), FALSE);
+    pa_return_val_if_fail(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE), FALSE);
+    pa_return_val_if_fail(ch = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNELS), FALSE);
 
-    pa_assert((ss->format = pa_parse_sample_format(sf)) != PA_SAMPLE_INVALID);
-    pa_assert(pa_atou(r, &ss->rate) == 0);
-    pa_assert(pa_atou(ch, &channels) == 0);
+    pa_return_val_if_fail((ss->format = pa_parse_sample_format(sf)) != PA_SAMPLE_INVALID, FALSE);
+    pa_return_val_if_fail(pa_atou(r, &ss->rate) == 0, FALSE);
+    pa_return_val_if_fail(pa_atou(ch, &channels) == 0, FALSE);
     ss->channels = (uint8_t) channels;
 
     if (map) {
@@ -142,21 +142,25 @@ void pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_cha
         pa_channel_map_init(map);
 
         if (m)
-            pa_assert(pa_channel_map_parse(map, m) != NULL);
+            pa_return_val_if_fail(pa_channel_map_parse(map, m) != NULL, FALSE);
     }
+
+    return TRUE;
 }
 
 /* For compressed streams */
-void pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) {
+pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) {
     const char *r;
 
     pa_assert(f);
     pa_assert(ss);
-    pa_assert(f->encoding != PA_ENCODING_PCM);
+    pa_return_val_if_fail(f->encoding != PA_ENCODING_PCM, FALSE);
 
     ss->format = PA_SAMPLE_S16LE;
     ss->channels = 2;
 
-    pa_assert(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE));
-    pa_assert(pa_atou(r, &ss->rate) == 0);
+    pa_return_val_if_fail(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE), FALSE);
+    pa_return_val_if_fail(pa_atou(r, &ss->rate) == 0, FALSE);
+
+    return TRUE;
 }
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index d715165..228e8dc 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -298,8 +298,8 @@ void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t)
 
 pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second);
 pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map);
-void pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
-void pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss);
+pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
+pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss);
 
 pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m);
 
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 1706a7f..a465955 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -281,12 +281,12 @@ int pa_sink_input_new(
     /* 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_format_info_to_sample_spec(data->format, &ss, &map);
+        pa_return_val_if_fail(pa_format_info_to_sample_spec(data->format, &ss, &map), -PA_ERR_INVALID);
         pa_sink_input_new_data_set_sample_spec(data, &ss);
         if (pa_channel_map_valid(&map))
             pa_sink_input_new_data_set_channel_map(data, &map);
     } else {
-        pa_format_info_to_sample_spec_fake(data->format, &ss);
+        pa_return_val_if_fail(pa_format_info_to_sample_spec_fake(data->format, &ss), -PA_ERR_INVALID);
         pa_sink_input_new_data_set_sample_spec(data, &ss);
         /* XXX: this is redundant - we can just check the encoding */
         data->flags |= PA_SINK_INPUT_PASSTHROUGH;

commit e418e49ecbd643f3cac87438d00baaf86275927c
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Mar 2 11:31:51 2011 +0530

    format: Avoid some code duplication
    
    We frequently need to free an idxset containing pa_format_infos, so
    define an internal free function that can be used directly with this
    (instead of defining it once-per-file).

diff --git a/src/pulse/format.c b/src/pulse/format.c
index af24eb2..9bb7673 100644
--- a/src/pulse/format.c
+++ b/src/pulse/format.c
@@ -66,6 +66,10 @@ void pa_format_info_free(pa_format_info *f) {
     pa_xfree(f);
 }
 
+void pa_format_info_free2(pa_format_info *f, void *userdata) {
+    pa_format_info_free(f);
+}
+
 int pa_format_info_valid(pa_format_info *f) {
     return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL);
 }
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 228e8dc..351aeec 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -296,6 +296,7 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
 void pa_ext_device_manager_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);
 pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second);
 pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map);
 pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 8332179..ee2bc4f 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -35,6 +35,7 @@
 #include <pulse/utf8.h>
 #include <pulse/util.h>
 #include <pulse/xmalloc.h>
+#include <pulse/internal.h>
 
 #include <pulsecore/native-common.h>
 #include <pulsecore/packet.h>
@@ -1849,10 +1850,6 @@ static pa_tagstruct *reply_new(uint32_t tag) {
     return reply;
 }
 
-static void free_format_info(pa_format_info *f, void *userdata) {
-    pa_format_info_free(f);
-}
-
 static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     playback_stream *s;
@@ -2110,7 +2107,7 @@ error:
     if (p)
         pa_proplist_free(p);
     if (formats)
-        pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL);
+        pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
     return;
 }
 
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index a465955..0f34e65 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -130,10 +130,6 @@ void pa_sink_input_new_data_set_muted(pa_sink_input_new_data *data, pa_bool_t mu
     data->muted = !!mute;
 }
 
-static void free_format_info(pa_format_info *f, void *userdata) {
-    pa_format_info_free(f);
-}
-
 pa_bool_t pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink *s, pa_bool_t save) {
     pa_bool_t ret = TRUE;
     pa_idxset *formats = NULL;
@@ -154,12 +150,12 @@ pa_bool_t pa_sink_input_new_data_set_sink(pa_sink_input_new_data *data, pa_sink
             data->sink = s;
             data->save_sink = save;
             if (data->nego_formats)
-                pa_idxset_free(data->nego_formats, (pa_free2_cb_t) free_format_info, NULL);
+                pa_idxset_free(data->nego_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
             data->nego_formats = formats;
         } else {
             /* Sink doesn't support any of the formats requested by the client */
             if (formats)
-                pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL);
+                pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
             ret = FALSE;
         }
     }
@@ -172,7 +168,7 @@ pa_bool_t pa_sink_input_new_data_set_formats(pa_sink_input_new_data *data, pa_id
     pa_assert(formats);
 
     if (data->req_formats)
-        pa_idxset_free(formats, (pa_free2_cb_t) free_format_info, NULL);
+        pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
 
     data->req_formats = formats;
 
@@ -188,10 +184,10 @@ void pa_sink_input_new_data_done(pa_sink_input_new_data *data) {
     pa_assert(data);
 
     if (data->req_formats)
-        pa_idxset_free(data->req_formats, (pa_free2_cb_t) free_format_info, NULL);
+        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) free_format_info, NULL);
+        pa_idxset_free(data->nego_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
 
     if (data->format)
         pa_format_info_free(data->format);

commit 8b3e68a202084467086af6c83e89c07adb9aa18a
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Mar 2 11:16:48 2011 +0530

    sink: Fix leak in pa_sink_check_formats()
    
    We weren't freeing the sink formats idxset.

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 33923e1..2b9402a 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -3295,7 +3295,7 @@ pa_idxset* pa_sink_get_formats(pa_sink *s) {
 /* Calculates the intersection between formats supported by the sink and
  * in_formats, and returns these, in the order of the sink's formats. */
 pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats) {
-    pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *sink_formats;
+    pa_idxset *out_formats = pa_idxset_new(NULL, NULL), *sink_formats = NULL;
     pa_format_info *f_sink, *f_in;
     uint32_t i, j;
 
@@ -3314,5 +3314,8 @@ pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats) {
     }
 
 done:
+    if (sink_formats)
+        pa_idxset_free(sink_formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
+
     return out_formats;
 }

commit 658a9153f094db9a30ac94428f4e46985e7096eb
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Mar 2 11:38:01 2011 +0530

    sink-input: Kill passthrough streams if moving to an unsupported sink
    
    This will eventually be replaced by a hook to let clients know that the
    stream has moved so that they can gracefully reconnect and renegotiate a
    supported format.

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 0f34e65..6e1b81f 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1552,6 +1552,11 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
     if (!pa_sink_input_may_move_to(i, dest))
         return -PA_ERR_NOTSUPPORTED;
 
+    if (!pa_format_info_is_pcm(i->format) && !pa_sink_check_format(dest, i->format)) {
+        /* FIXME: Fire a message here so the client can renegotiate */
+        return -PA_ERR_NOTSUPPORTED;
+    }
+
     if (i->thread_info.resampler &&
         pa_sample_spec_equal(pa_resampler_output_sample_spec(i->thread_info.resampler), &dest->sample_spec) &&
         pa_channel_map_equal(pa_resampler_output_channel_map(i->thread_info.resampler), &dest->channel_map))
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 2b9402a..626c727 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -3292,6 +3292,33 @@ pa_idxset* pa_sink_get_formats(pa_sink *s) {
 }
 
 /* Called from the main thread */
+/* 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_bool_t ret = FALSE;
+
+    pa_assert(s);
+    pa_assert(f);
+
+    sink_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 (sink_formats)
+        pa_idxset_free(sink_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 sink and
  * in_formats, and returns these, in the order of the sink's formats. */
 pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats) {
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 492abf6..cbff5ca 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -407,6 +407,7 @@ void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save);
 void pa_sink_move_all_fail(pa_queue *q);
 
 pa_idxset* pa_sink_get_formats(pa_sink *s);
+pa_bool_t pa_sink_check_format(pa_sink *s, pa_format_info *f);
 pa_idxset* pa_sink_check_formats(pa_sink *s, pa_idxset *in_formats);
 
 /*** To be called exclusively by the sink driver, from IO context */

commit e11770b64ffbe0a5e5d172244c175fa0bc4034db
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Mar 2 12:54:02 2011 +0530

    core: Fix some FIXMEs for the extended API
    
    This adds some checks that I'd postponed and adds a
    "should-be-good-enough" guess for tlength when using a compressed
    format.

diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index f5bf42c..10e431c 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -151,8 +151,16 @@ static pa_stream *pa_stream_new_with_proplist_internal(
     s->buffer_attr.maxlength = (uint32_t) -1;
     if (ss)
         s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, ss); /* 250ms of buffering */
-    else
-        /* XXX: How do we apply worst case conversion here? */
+    else {
+        /* FIXME: We assume a worst-case compressed format corresponding to
+         * 48000 Hz, 2 ch, S16 PCM, but this can very well be incorrect */
+        pa_sample_spec tmp_ss = {
+            .format   = PA_SAMPLE_S16NE,
+            .rate     = 48000,
+            .channels = 2,
+        };
+        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes(250*PA_USEC_PER_MSEC, &tmp_ss); /* 250ms of buffering */
+    }
     s->buffer_attr.minreq = (uint32_t) -1;
     s->buffer_attr.prebuf = (uint32_t) -1;
     s->buffer_attr.fragsize = (uint32_t) -1;
@@ -224,8 +232,6 @@ pa_stream *pa_stream_new_extended(
 
     PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 21, PA_ERR_NOTSUPPORTED);
 
-    /* XXX: For the single-format PCM case, pass ss/map instead of formats */
-
     return pa_stream_new_with_proplist_internal(c, name, NULL, NULL, formats, p);
 }
 
@@ -1029,7 +1035,6 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
                 (!(s->flags & PA_STREAM_FIX_FORMAT) && ss.format != s->sample_spec.format) ||
                 (!(s->flags & PA_STREAM_FIX_RATE) && ss.rate != s->sample_spec.rate) ||
                 (!(s->flags & PA_STREAM_FIX_CHANNELS) && !pa_channel_map_equal(&cm, &s->channel_map))))) {
-            /* XXX: checks for the n_formats > 0 case? */
             pa_context_fail(s->context, PA_ERR_PROTOCOL);
             goto finish;
         }
@@ -1062,8 +1067,14 @@ void pa_create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag,
 
         if (pa_format_info_valid(f))
             s->format = f;
-        else
+        else {
             pa_format_info_free(f);
+            if (s->n_formats > 0) {
+                /* We used the extended API, so we should have got back a proper format */
+                pa_context_fail(s->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+        }
     }
 
     if (!pa_tagstruct_eof(t)) {
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index ee2bc4f..956670b 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2008,10 +2008,15 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         }
     }
 
-    CHECK_VALIDITY(c->pstream, n_formats > 0 || pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, n_formats > 0 || (map.channels == ss.channels && volume.channels == ss.channels), tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, n_formats > 0 || pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
-    /* XXX: add checks on formats. At least inverse checks of the 3 above */
+    if (n_formats == 0) {
+        CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
+        CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID);
+        CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
+    } else {
+        PA_IDXSET_FOREACH(format, formats, i) {
+            CHECK_VALIDITY(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID);
+        }
+    }
 
     if (!pa_tagstruct_eof(t)) {
         protocol_error(c);

commit 20f1fa17bebe086cb9cff09a5157dc7ca009ba61
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Thu Mar 3 12:52:21 2011 +0530

    alsa-mixer: Remove passthrough profiles
    
    These aren't used any more - we handle passthrough mode in the iec958*
    profiles now.

diff --git a/src/Makefile.am b/src/Makefile.am
index e321704..a68cdc2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1166,8 +1166,7 @@ dist_alsapaths_DATA = \
 		modules/alsa/mixer/paths/analog-output-headphones-2.conf \
 		modules/alsa/mixer/paths/analog-output-lfe-on-mono.conf \
 		modules/alsa/mixer/paths/analog-output-mono.conf \
-		modules/alsa/mixer/paths/iec958-stereo-output.conf \
-		modules/alsa/mixer/paths/iec958-passthrough-output.conf
+		modules/alsa/mixer/paths/iec958-stereo-output.conf
 
 endif
 
diff --git a/src/modules/alsa/mixer/paths/iec958-passthrough-output.conf b/src/modules/alsa/mixer/paths/iec958-passthrough-output.conf
deleted file mode 100644
index 8506a58..0000000
--- a/src/modules/alsa/mixer/paths/iec958-passthrough-output.conf
+++ /dev/null
@@ -1,19 +0,0 @@
-# This file is part of PulseAudio.
-#
-# PulseAudio is free software; you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as
-# published by the Free Software Foundation; either version 2.1 of the
-# License, or (at your option) any later version.
-#
-# PulseAudio is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# 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.
-
-
-[Element IEC958]
-switch = mute
diff --git a/src/modules/alsa/mixer/profile-sets/default.conf b/src/modules/alsa/mixer/profile-sets/default.conf
index 9f7b5f2..283edfb 100644
--- a/src/modules/alsa/mixer/profile-sets/default.conf
+++ b/src/modules/alsa/mixer/profile-sets/default.conf
@@ -155,13 +155,6 @@ paths-input = iec958-stereo-input
 paths-output = iec958-stereo-output
 priority = 5
 
-[Mapping iec958-passthrough]
-device-strings = iec958:%f
-channel-map = left,right
-direction = output
-paths-output = iec958-passthrough-output
-priority = 5
-
 [Mapping iec958-ac3-surround-40]
 device-strings = a52:%f
 channel-map = front-left,front-right,rear-left,rear-right

commit 4c9d53f3f50162e19ad7934e62c8f35453bdbfbc
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Thu Mar 3 18:35:14 2011 +0530

    sink: Trivial typo fix in comment

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 626c727..5959a5c 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -2153,7 +2153,7 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
 
             /* If you change anything here, make sure to change the
              * sink input handling a few lines down at
-             * PA_SINK_MESSAGE_PREPAPRE_MOVE, too. */
+             * PA_SINK_MESSAGE_START_MOVE, too. */
 
             if (i->detach)
                 i->detach(i);

commit f94bcae6bd6a3021a9474de5703360817f1ec1a8
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Thu Mar 3 19:02:45 2011 +0530

    core: Suspend monitor when a sink enters passthrough mode
    
    In most cases it is expected that clients cannot consume compressed
    data from monitor sources, so we suspend the monitor source when the
    sink goes into passthrough mode.
    
    Eventually, when the extended API includes client notifications for
    changed formats, we should emit a notification on the monitor so that
    clients can decide what they want to do when this happens (disconnect or
    consume the data anyway).

diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 358b98d..6b25fba 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -35,6 +35,7 @@ typedef enum pa_suspend_cause {
     PA_SUSPEND_APPLICATION = 2,  /* Used by the device reservation logic */
     PA_SUSPEND_IDLE = 4,         /* Used by module-suspend-on-idle */
     PA_SUSPEND_SESSION = 8,      /* Used by module-hal for mark inactive sessions */
+    PA_SUSPEND_PASSTHROUGH = 16, /* Used to suspend monitor sources when the sink is in passthrough mode */
     PA_SUSPEND_ALL = 0xFFFF      /* Magic cause that can be used to resume forcibly */
 } pa_suspend_cause_t;
 
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 6e1b81f..d77eb2c 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -599,6 +599,10 @@ void pa_sink_input_unlink(pa_sink_input *i) {
 
         if (i->sink->asyncmsgq)
             pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
+
+        /* We suspend the monitor if there was a passthrough sink, unsuspend now if required */
+        if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source)
+            pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH);
     }
 
     reset_callbacks(i);
@@ -689,6 +693,10 @@ void pa_sink_input_put(pa_sink_input *i) {
         set_real_ratio(i, &i->volume);
     }
 
+    /* If we're entering passthrough mode, disable the monitor */
+    if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source)
+        pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH);
+
     i->thread_info.soft_volume = i->soft_volume;
     i->thread_info.muted = i->muted;
 
@@ -1380,6 +1388,10 @@ int pa_sink_input_start_move(pa_sink_input *i) {
 
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
 
+    /* We suspend the monitor if there was a passthrough sink, unsuspend now if required */
+    if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source)
+        pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH);
+
     pa_sink_update_status(i->sink);
     pa_cvolume_remap(&i->volume_factor_sink, &i->sink->channel_map, &i->channel_map);
     i->sink = NULL;
@@ -1621,6 +1633,10 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
 
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
 
+    /* If we're entering passthrough mode, disable the monitor */
+    if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source)
+        pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH);
+
     pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);
 
     /* Notify everyone */
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 92fb80e..15a5b8d 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -514,7 +514,7 @@ int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause)
     pa_assert(PA_SOURCE_IS_LINKED(s->state));
     pa_assert(cause != 0);
 
-    if (s->monitor_of)
+    if (s->monitor_of && cause != PA_SUSPEND_PASSTHROUGH)
         return -PA_ERR_NOTSUPPORTED;
 
     if (suspend)

commit 49b10ba6941ea61e551ccee482b12bb78db8a4b3
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Tue Mar 8 14:22:24 2011 +0530

    alsa: Reconfigure sink sample rate for passthrough inputs
    
    When a passthrough sink-input is added, we need to reconfigure the
    sink's sample rate since no resampling occurs. We revert to the original
    rate when the passthrough sink-input is removed.

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index ec840af..3f8f6d2 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -108,6 +108,8 @@ struct userdata {
 
     pa_cvolume hardware_volume;
 
+    uint32_t old_rate;
+
     size_t
         frame_size,
         fragment_size,
@@ -1051,6 +1053,56 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
 
     switch (code) {
 
+        case PA_SINK_MESSAGE_FINISH_MOVE:
+        case PA_SINK_MESSAGE_ADD_INPUT: {
+            pa_sink_input *i = PA_SINK_INPUT(data);
+            int r = 0;
+
+            if (PA_LIKELY(pa_format_info_is_pcm(i->format)))
+                break;
+
+            u->old_rate = u->sink->sample_spec.rate;
+
+            /* Passthrough format, see if we need to reset sink sample rate */
+            if (u->sink->sample_spec.rate == i->thread_info.sample_spec.rate)
+                break;
+
+            /* .. we do */
+            if ((r = suspend(u)) < 0)
+                return r;
+
+            u->sink->sample_spec.rate = i->thread_info.sample_spec.rate;
+
+            if ((r = unsuspend(u)) < 0)
+                return r;
+
+            break;
+        }
+
+        case PA_SINK_MESSAGE_START_MOVE:
+        case PA_SINK_MESSAGE_REMOVE_INPUT: {
+            pa_sink_input *i = PA_SINK_INPUT(data);
+            int r = 0;
+
+            if (PA_LIKELY(pa_format_info_is_pcm(i->format)))
+                break;
+
+            /* Passthrough format, see if we need to reset sink sample rate */
+            if (u->sink->sample_spec.rate == u->old_rate)
+                break;
+
+            /* .. we do */
+            if ((r = suspend(u)) < 0)
+                return r;
+
+            u->sink->sample_spec.rate = u->old_rate;
+
+            if ((r = unsuspend(u)) < 0)
+                return r;
+
+            break;
+        }
+
         case PA_SINK_MESSAGE_GET_LATENCY: {
             pa_usec_t r = 0;
 

commit a3a004214404c6f91a82c1e2164444e5e08c26cf
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Tue Mar 8 20:14:46 2011 +0530

    format: Const-ify some parameters

diff --git a/src/pulse/format.c b/src/pulse/format.c
index 9bb7673..7ddfa7a 100644
--- a/src/pulse/format.c
+++ b/src/pulse/format.c
@@ -70,11 +70,11 @@ void pa_format_info_free2(pa_format_info *f, void *userdata) {
     pa_format_info_free(f);
 }
 
-int pa_format_info_valid(pa_format_info *f) {
+int pa_format_info_valid(const pa_format_info *f) {
     return (f->encoding >= 0 && f->encoding < PA_ENCODING_MAX && f->plist != NULL);
 }
 
-int pa_format_info_is_pcm(pa_format_info *f) {
+int pa_format_info_is_pcm(const pa_format_info *f) {
     return f->encoding == PA_ENCODING_PCM;
 }
 
diff --git a/src/pulse/format.h b/src/pulse/format.h
index 7e3ab08..03b3405 100644
--- a/src/pulse/format.h
+++ b/src/pulse/format.h
@@ -72,10 +72,10 @@ pa_format_info* pa_format_info_copy(const pa_format_info *src);
 void pa_format_info_free(pa_format_info *f);
 
 /** Returns non-zero when the format info structure is valid */
-int pa_format_info_valid(pa_format_info *f);
+int pa_format_info_valid(const pa_format_info *f);
 
 /** Returns non-zero when the format info structure represents a PCM (i.e. uncompressed data) format */
-int pa_format_info_is_pcm(pa_format_info *f);
+int pa_format_info_is_pcm(const pa_format_info *f);
 
 PA_C_DECL_END
 

commit 8631f4e2c44b47db76795bebdbab54914a1f3ea0
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Tue Mar 8 20:15:36 2011 +0530

    format: Add some convenience functions for printing

diff --git a/src/map-file b/src/map-file
index 3b1de10..5283a5e 100644
--- a/src/map-file
+++ b/src/map-file
@@ -146,6 +146,7 @@ pa_cvolume_set_fade;
 pa_cvolume_set_position;
 pa_cvolume_snprint;
 pa_cvolume_valid;
+pa_encoding_to_string;
 pa_ext_device_manager_delete;
 pa_ext_device_manager_enable_role_device_priority_routing;
 pa_ext_device_manager_read;
@@ -164,6 +165,7 @@ pa_format_info_copy;
 pa_format_info_free;
 pa_format_info_is_pcm;
 pa_format_info_new;
+pa_format_info_snprint;
 pa_format_info_valid;
 pa_frame_size;
 pa_get_binary_name;
diff --git a/src/pulse/format.c b/src/pulse/format.c
index 7ddfa7a..0c5c24b 100644
--- a/src/pulse/format.c
+++ b/src/pulse/format.c
@@ -27,12 +27,28 @@
 
 #include <pulse/internal.h>
 #include <pulse/xmalloc.h>
+#include <pulse/i18n.h>
 
 #include <pulsecore/core-util.h>
 #include <pulsecore/macro.h>
 
 #include "format.h"
 
+const char *pa_encoding_to_string(pa_encoding_t e) {
+    static const char* const table[]= {
+        [PA_ENCODING_PCM] = "pcm",
+        [PA_ENCODING_AC3_IEC61937] = "ac3-iec61937",
+        [PA_ENCODING_EAC3_IEC61937] = "eac3-iec61937",
+        [PA_ENCODING_MPEG_IEC61937] = "mpeg-iec61937",
+        [PA_ENCODING_ANY] = "any",
+    };
+
+    if (e < 0 || e >= PA_ENCODING_MAX)
+        return NULL;
+
+    return table[e];
+}
+
 pa_format_info* pa_format_info_new(void) {
     pa_format_info *f = pa_xnew(pa_format_info, 1);
 
@@ -78,6 +94,26 @@ int pa_format_info_is_pcm(const pa_format_info *f) {
     return f->encoding == PA_ENCODING_PCM;
 }
 
+char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) {
+    char *tmp;
+
+    pa_assert(s);
+    pa_assert(l > 0);
+    pa_assert(f);
+
+    pa_init_i18n();
+
+    if (!pa_format_info_valid(f))
+        pa_snprintf(s, l, _("(invalid)"));
+    else {
+        tmp = pa_proplist_to_string_sep(f->plist, ", ");
+        pa_snprintf(s, l, _("%s, %s"), pa_encoding_to_string(f->encoding), tmp[0] ? tmp : _("(no properties)"));
+        pa_xfree(tmp);
+    }
+
+    return s;
+}
+
 pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) {
     const char *key;
     void *state = NULL;
diff --git a/src/pulse/format.h b/src/pulse/format.h
index 03b3405..51c9426 100644
--- a/src/pulse/format.h
+++ b/src/pulse/format.h
@@ -53,6 +53,9 @@ typedef enum pa_encoding {
     /**< Represents an invalid encoding */
 } pa_encoding_t;
 
+/** Returns a printable string representing the given encoding type. \since 1.0 */
+const char *pa_encoding_to_string(pa_encoding_t e) PA_GCC_CONST;
+
 /**< Represents the format of data provided in a stream or processed by a sink. \since 1.0 */
 typedef struct pa_format_info {
     pa_encoding_t encoding;
@@ -77,6 +80,16 @@ int pa_format_info_valid(const pa_format_info *f);
 /** Returns non-zero when the format info structure represents a PCM (i.e. uncompressed data) format */
 int pa_format_info_is_pcm(const pa_format_info *f);
 
+/** Maximum required string length for
+ * pa_format_info_snprint(). Please note that this value can change
+ * with any release without warning and without being considered API
+ * or ABI breakage. You should not use this definition anywhere where
+ * it might become part of an ABI. \since 1.0 */
+#define PA_FORMAT_INFO_SNPRINT_MAX 256
+
+/** Return a human-readable string representing the given format. \since 1.0 */
+char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f);
+
 PA_C_DECL_END
 
 #endif

commit dedbc942ab2090320856c36d6673db12026efdc1
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Tue Mar 8 20:16:00 2011 +0530

    stream: Add API to get a stream's pa_format_info

diff --git a/src/map-file b/src/map-file
index 5283a5e..1acc6e9 100644
--- a/src/map-file
+++ b/src/map-file
@@ -258,6 +258,7 @@ pa_stream_get_channel_map;
 pa_stream_get_context;
 pa_stream_get_device_index;
 pa_stream_get_device_name;
+pa_stream_get_format_info;
 pa_stream_get_index;
 pa_stream_get_latency;
 pa_stream_get_monitor_stream;
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 10e431c..bd2e6ca 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -2470,6 +2470,16 @@ const pa_channel_map* pa_stream_get_channel_map(pa_stream *s) {
     return &s->channel_map;
 }
 
+const pa_format_info* pa_stream_get_format_info(pa_stream *s) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    /* We don't have the format till routing is done */
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, s->state == PA_STREAM_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(s->context, !pa_detect_fork(), PA_ERR_FORKED);
+
+    return s->format;
+}
 const pa_buffer_attr* pa_stream_get_buffer_attr(pa_stream *s) {
     pa_assert(s);
     pa_assert(PA_REFCNT_VALUE(s) >= 1);
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 48bf09d..b265fae 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -709,6 +709,9 @@ const pa_sample_spec* pa_stream_get_sample_spec(pa_stream *s);
 /** Return a pointer to the stream's channel map. */
 const pa_channel_map* pa_stream_get_channel_map(pa_stream *s);
 
+/** Return a pointer to the stream's format \since 1.0 */
+const pa_format_info* pa_stream_get_format_info(pa_stream *s);
+
 /** Return the per-stream server-side buffer metrics of the
  * stream. Only valid after the stream has been connected successfuly
  * and if the server is at least PulseAudio 0.9. This will return the

commit 322980e2e3844abf837dcc8cc5317406b3d8cb94
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Tue Mar 8 23:30:24 2011 +0530

    introspect: Get formats for sinks
    
    This gets the list of supported formats for a sink in
    pa_context_get_sink_info*(). Also prints these in 'pactl list'.

diff --git a/PROTOCOL b/PROTOCOL
index d06cb98..419f993 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -228,3 +228,11 @@ New fields PA_COMMAND_CREATE_PLAYBACK_STREAM:
 One new field in reply from PA_COMMAND_CREATE_PLAYBACK_STREAM:
 
     format_info format
+
+New fields in reply from PA_COMMAND_GET_SINK_INFO (and thus
+PA_COMMAND_GET_SINK_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 c93fb06..28a6bf4 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -240,6 +240,33 @@ 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;
+                }
+
+                pa_assert(i.n_formats > 0);
+                i.formats = pa_xnew0(pa_format_info*, i.n_formats);
+
+                for (j = 0; j < i.n_formats; j++) {
+                    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;
+                    }
+                }
+            }
+
             i.mute = (int) mute;
             i.flags = (pa_sink_flags_t) flags;
             i.state = (pa_sink_state_t) state;
@@ -253,6 +280,13 @@ static void context_get_sink_info_callback(pa_pdispatch *pd, uint32_t command, u
                 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);
+            }
+
             pa_proplist_free(i.proplist);
         }
     }
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 297b4ba..49c1de6 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -32,6 +32,7 @@
 #include <pulse/channelmap.h>
 #include <pulse/volume.h>
 #include <pulse/proplist.h>
+#include <pulse/format.h>
 #include <pulse/version.h>
 
 /** \page introspect Server Query and Control
@@ -230,6 +231,8 @@ typedef struct pa_sink_info {
     uint32_t n_ports;                  /**< Number of entries in port array \since 0.9.16 */
     pa_sink_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_sink_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 sink. \since 1.0 */
+    pa_format_info **formats;          /**< Array of formats supported by the sink. \since 1.0 */
 } pa_sink_info;
 
 /** Callback prototype for pa_context_get_sink_info_by_name() and friends */
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 956670b..f151bd2 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -2984,6 +2984,19 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
 
         pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL);
     }
+
+    if (c->version >= 21) {
+        uint32_t i;
+        pa_format_info *f;
+        pa_idxset *formats = pa_sink_get_formats(sink);
+
+        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 source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 194183d..109d31b 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -222,7 +222,8 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
         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) {
@@ -307,6 +308,14 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_
     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_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {

commit 7aa84e82089a88a542f15cbf6f38c808b4f04db1
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Tue Mar 8 23:31:59 2011 +0530

    introspect: Get format of sink input
    
    This gets the negotiated format of sink inputs in
    pa_context_get_sink_input*(). Also prints the format in 'pactl list'.

diff --git a/PROTOCOL b/PROTOCOL
index 419f993..4178274 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -236,3 +236,8 @@ PA_COMMAND_GET_SINK_INFO_LIST)
     format_info format1
     ...
     format_info formatn
+
+One new field in reply from PA_COMMAND_GET_SINK_INPUT_INFO (and thus
+PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
+
+    format_info format
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 28a6bf4..e28a78c 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -1033,6 +1033,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
 
             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 ||
@@ -1050,10 +1051,12 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
                 (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 >= 20 && (pa_tagstruct_get_boolean(t, &has_volume) < 0 ||
-                                               pa_tagstruct_get_boolean(t, &volume_writable) < 0))) {
+                                               pa_tagstruct_get_boolean(t, &volume_writable) < 0)) ||
+                (o->context->version >= 21 && 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;
             }
 
@@ -1068,6 +1071,7 @@ static void context_get_sink_input_info_callback(pa_pdispatch *pd, uint32_t comm
             }
 
             pa_proplist_free(i.proplist);
+            pa_format_info_free(i.format);
         }
     }
 
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 49c1de6..a6b4a80 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -508,6 +508,7 @@ typedef struct pa_sink_input_info {
     int corked;                          /**< Stream corked \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_sink_input_info;
 
 /** Callback prototype for pa_context_get_sink_input_info() and friends*/
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index f151bd2..59b8724 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -3153,6 +3153,8 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t,
         pa_tagstruct_put_boolean(t, has_volume);
         pa_tagstruct_put_boolean(t, s->volume_writable);
     }
+    if (c->version >= 21)
+        pa_tagstruct_put_format_info(t, s->format);
 }
 
 static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source_output *s) {
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index 109d31b..7be7049 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -560,7 +560,7 @@ static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_
 }
 
 static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
-    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];
+    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) {
@@ -600,6 +600,7 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
              "\tSink: %u\n"
              "\tSample Specification: %s\n"
              "\tChannel Map: %s\n"
+             "\tFormat: %s\n"
              "\tMute: %s\n"
              "\tVolume: %s\n"
              "\t        %s\n"
@@ -615,6 +616,7 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info
            i->sink,
            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),

commit 9a39a3df10c3874bf0b5b091f721a6d8ab8750a5
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Mar 9 20:50:25 2011 +0530

    format: Add a type for DTS

diff --git a/src/pulse/format.c b/src/pulse/format.c
index 0c5c24b..e601d90 100644
--- a/src/pulse/format.c
+++ b/src/pulse/format.c
@@ -40,6 +40,7 @@ const char *pa_encoding_to_string(pa_encoding_t e) {
         [PA_ENCODING_AC3_IEC61937] = "ac3-iec61937",
         [PA_ENCODING_EAC3_IEC61937] = "eac3-iec61937",
         [PA_ENCODING_MPEG_IEC61937] = "mpeg-iec61937",
+        [PA_ENCODING_DTS_IEC61937] = "dts-iec61937",
         [PA_ENCODING_ANY] = "any",
     };
 
diff --git a/src/pulse/format.h b/src/pulse/format.h
index 51c9426..bd32ba9 100644
--- a/src/pulse/format.h
+++ b/src/pulse/format.h
@@ -46,6 +46,9 @@ typedef enum pa_encoding {
     PA_ENCODING_MPEG_IEC61937,
     /**< MPEG-1 or MPEG-2 (Part 3, not AAC) data encapsulated in IEC 61937 header/padding */
 
+    PA_ENCODING_DTS_IEC61937,
+    /**< DTS data encapsulated in IEC 61937 header/padding */
+
     PA_ENCODING_MAX,
     /**< Valid encoding types must be less than this value */
 

commit 4fb68b91acef3cb37c014814d9e9de8ca9f22bf4
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Mar 16 16:08:23 2011 +0530

    core: Factor out passthrough checks into their own functions
    
    Since we currently have two mechanisms to signal a passthrough
    connection (non-PCM format or PA_SINK_INPUT_PASSTHROUGH flag), we move
    all the related checks into functions and use those everywhere.
    
    This makes things more consistent, and should we decide to get rid of
    the flag, we only need to change pa_sink_input_*_is_passthrough()
    accordingly.

diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c
index 3f8f6d2..b98340b 100644
--- a/src/modules/alsa/alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -1058,7 +1058,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             pa_sink_input *i = PA_SINK_INPUT(data);
             int r = 0;
 
-            if (PA_LIKELY(pa_format_info_is_pcm(i->format)))
+            if (PA_LIKELY(!pa_sink_input_is_passthrough(i)))
                 break;
 
             u->old_rate = u->sink->sample_spec.rate;
@@ -1084,7 +1084,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
             pa_sink_input *i = PA_SINK_INPUT(data);
             int r = 0;
 
-            if (PA_LIKELY(pa_format_info_is_pcm(i->format)))
+            if (PA_LIKELY(!pa_sink_input_is_passthrough(i)))
                 break;
 
             /* Passthrough format, see if we need to reset sink sample rate */
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index d77eb2c..5e7cfd1 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -50,15 +50,14 @@ PA_DEFINE_PUBLIC_CLASS(pa_sink_input, pa_msgobject);
 static void sink_input_free(pa_object *o);
 static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);
 
-static int check_passthrough_connection(pa_format_info *format, pa_sink *dest) {
-
+static int check_passthrough_connection(pa_bool_t passthrough, pa_sink *dest) {
     if (pa_sink_is_passthrough(dest)) {
         pa_log_warn("Sink is already connected to PASSTHROUGH input");
         return -PA_ERR_BUSY;
     }
 
     /* If current input(s) exist, check new input is not PASSTHROUGH */
-    if (pa_idxset_size(dest->inputs) > 0 && !pa_format_info_is_pcm(format)) {
+    if (pa_idxset_size(dest->inputs) > 0 && passthrough) {
         pa_log_warn("Sink is already connected, cannot accept new PASSTHROUGH INPUT");
         return -PA_ERR_BUSY;
     }
@@ -91,6 +90,18 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const
         data->channel_map = *map;
 }
 
+pa_bool_t pa_sink_input_new_data_is_passthrough(pa_sink_input_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_SINK_INPUT_PASSTHROUGH))
+        return TRUE;
+
+    return FALSE;
+}
+
 void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) {
     pa_assert(data);
     pa_assert(data->volume_writable);
@@ -284,15 +295,13 @@ int pa_sink_input_new(
     } else {
         pa_return_val_if_fail(pa_format_info_to_sample_spec_fake(data->format, &ss), -PA_ERR_INVALID);
         pa_sink_input_new_data_set_sample_spec(data, &ss);
-        /* XXX: this is redundant - we can just check the encoding */
-        data->flags |= PA_SINK_INPUT_PASSTHROUGH;
     }
 
     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);
 
-    r = check_passthrough_connection(data->format, data->sink);
+    r = check_passthrough_connection(pa_sink_input_new_data_is_passthrough(data), data->sink);
     pa_return_val_if_fail(r == PA_OK, r);
 
     if (!data->sample_spec_is_set)
@@ -373,7 +382,7 @@ int pa_sink_input_new(
         !pa_channel_map_equal(&data->channel_map, &data->sink->channel_map)) {
 
         /* Note: for passthrough content we need to adjust the output rate to that of the current sink-input */
-        if (!(data->flags & PA_SINK_INPUT_PASSTHROUGH)) /* no resampler for passthrough content */
+        if (!pa_sink_input_new_data_is_passthrough(data)) /* no resampler for passthrough content */
             if (!(resampler = pa_resampler_new(
                           core->mempool,
                           &data->sample_spec, &data->channel_map,
@@ -601,7 +610,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
             pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0);
 
         /* We suspend the monitor if there was a passthrough sink, unsuspend now if required */
-        if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source)
+        if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source)
             pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH);
     }
 
@@ -694,7 +703,7 @@ void pa_sink_input_put(pa_sink_input *i) {
     }
 
     /* If we're entering passthrough mode, disable the monitor */
-    if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source)
+    if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source)
         pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH);
 
     i->thread_info.soft_volume = i->soft_volume;
@@ -1178,12 +1187,25 @@ static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) {
     /* 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_sink_input_is_passthrough(pa_sink_input *i) {
+    pa_sink_input_assert_ref(i);
+
+    if (PA_UNLIKELY(!pa_format_info_is_pcm(i->format)))
+        return TRUE;
+
+    if (PA_UNLIKELY(i->flags & PA_SINK_INPUT_PASSTHROUGH))
+        return TRUE;
+
+    return FALSE;
+}
+
 /* Called from main context */
 pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i) {
     pa_sink_input_assert_ref(i);
     pa_assert_ctl_context();
 
-    return !(i->flags & PA_SINK_INPUT_PASSTHROUGH);
+    return !pa_sink_input_is_passthrough(i);
 }
 
 /* Called from main context */
@@ -1342,7 +1364,7 @@ pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
         return FALSE;
     }
 
-    if (check_passthrough_connection(i->format, dest) < 0)
+    if (check_passthrough_connection(pa_sink_input_is_passthrough(i), dest) < 0)
         return FALSE;
 
     if (i->may_move_to)
@@ -1389,7 +1411,7 @@ int pa_sink_input_start_move(pa_sink_input *i) {
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
 
     /* We suspend the monitor if there was a passthrough sink, unsuspend now if required */
-    if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source)
+    if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source)
         pa_source_suspend(i->sink->monitor_source, FALSE, PA_SUSPEND_PASSTHROUGH);
 
     pa_sink_update_status(i->sink);
@@ -1564,7 +1586,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
     if (!pa_sink_input_may_move_to(i, dest))
         return -PA_ERR_NOTSUPPORTED;
 
-    if (!pa_format_info_is_pcm(i->format) && !pa_sink_check_format(dest, i->format)) {
+    if (pa_sink_input_is_passthrough(i) && !pa_sink_check_format(dest, i->format)) {
         /* FIXME: Fire a message here so the client can renegotiate */
         return -PA_ERR_NOTSUPPORTED;
     }
@@ -1634,7 +1656,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
     pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
 
     /* If we're entering passthrough mode, disable the monitor */
-    if (!pa_format_info_is_pcm(i->format) && i->sink->monitor_source)
+    if (pa_sink_input_is_passthrough(i) && i->sink->monitor_source)
         pa_source_suspend(i->sink->monitor_source, TRUE, PA_SUSPEND_PASSTHROUGH);
 
     pa_log_debug("Successfully moved sink input %i to %s.", i->index, dest->name);
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 72a1d5a..0ebb74a 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -304,6 +304,7 @@ typedef struct pa_sink_input_new_data {
 pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data);
 void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec);
 void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map);
+pa_bool_t pa_sink_input_new_data_is_passthrough(pa_sink_input_new_data *data);
 void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume);
 void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
 void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor);
@@ -350,6 +351,7 @@ void pa_sink_input_kill(pa_sink_input*i);
 
 pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);
 
+pa_bool_t pa_sink_input_is_passthrough(pa_sink_input *i);
 pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i);
 void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute);
 pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 5959a5c..bc4ddd2 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -1252,7 +1252,7 @@ pa_bool_t pa_sink_is_passthrough(pa_sink *s) {
     if (pa_idxset_size(s->inputs) == 1) {
         alt_i = pa_idxset_first(s->inputs, &idx);
 
-        if (!pa_format_info_is_pcm(alt_i->format))
+        if (pa_sink_input_is_passthrough(alt_i))
             return TRUE;
     }
 

commit a199bfb765147b3938d268a67f671646a16845c3
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Mon Mar 28 08:45:31 2011 +0530

    sink-input: Don't print an error if a passthrough connection fails
    
    The assertion message is misleading, since the passthrough connection
    can fail for reasons the client has no control over (like other sink
    inputs being connected).

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 5e7cfd1..979dc76 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -302,7 +302,8 @@ int pa_sink_input_new(
     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);
 
     r = check_passthrough_connection(pa_sink_input_new_data_is_passthrough(data), data->sink);
-    pa_return_val_if_fail(r == PA_OK, r);
+    if (r != PA_OK)
+        return r;
 
     if (!data->sample_spec_is_set)
         data->sample_spec = data->sink->sample_spec;

commit cb3dcb14f868e10c0fde582e9284561af974fab4
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Mon Mar 28 08:46:20 2011 +0530

    sink-input: Don't restore volume for passthrough streams

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 979dc76..196d771 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -319,6 +319,12 @@ int pa_sink_input_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;

commit 53091cc5f0a766e0bff36b59600f99cd2771bbd6
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Mon Mar 28 08:46:40 2011 +0530

    sink-input: Add a format-lost event
    
    This event is emitted if the sink-input could not be moved to a new sink
    because it doesn't support the format of the sink-input. Clients can
    reconnect their stream with a different format if they wish or
    gracefully exit.

diff --git a/src/pulse/def.h b/src/pulse/def.h
index 91a027e..e01e667 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -912,6 +912,13 @@ typedef void (*pa_free_cb_t)(void *p);
  * information, \since 0.9.15 */
 #define PA_STREAM_EVENT_REQUEST_UNCORK "request-uncork"
 
+/** A stream event notifying that the stream is going to be
+ * disconnected because the underlying sink changed and no longer
+ * supports the format that was originally negotiated. Clients need
+ * to connect a new stream to renegotiate a format and continue
+ * playback, \since 1.0 */
+#define PA_STREAM_EVENT_FORMAT_LOST "format-lost"
+
 PA_C_DECL_END
 
 #endif
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 196d771..f0558f4 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1594,7 +1594,8 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
         return -PA_ERR_NOTSUPPORTED;
 
     if (pa_sink_input_is_passthrough(i) && !pa_sink_check_format(dest, i->format)) {
-        /* FIXME: Fire a message here so the client can renegotiate */
+        pa_log_debug("New sink doesn't support stream format, sending format-changed and killing");
+        pa_sink_input_send_event(i, PA_STREAM_EVENT_FORMAT_LOST, NULL);
         return -PA_ERR_NOTSUPPORTED;
     }
 

commit 13a33abf45f31417076f283ca7da9d9f74892286
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Tue Mar 29 17:16:08 2011 +0530

    format: Export pa_format_info_is_compatible in API
    
    This allows clients to perform checks between formats as well.

diff --git a/src/map-file b/src/map-file
index 1acc6e9..8d68340 100644
--- a/src/map-file
+++ b/src/map-file
@@ -163,6 +163,7 @@ pa_ext_stream_restore_test;
 pa_ext_stream_restore_write;
 pa_format_info_copy;
 pa_format_info_free;
+pa_format_info_is_compatible;
 pa_format_info_is_pcm;
 pa_format_info_new;
 pa_format_info_snprint;
diff --git a/src/pulse/format.c b/src/pulse/format.c
index e601d90..a634b27 100644
--- a/src/pulse/format.c
+++ b/src/pulse/format.c
@@ -115,7 +115,7 @@ char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f) {
     return s;
 }
 
-pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) {
+int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second) {
     const char *key;
     void *state = NULL;
 
diff --git a/src/pulse/format.h b/src/pulse/format.h
index bd32ba9..b0efe50 100644
--- a/src/pulse/format.h
+++ b/src/pulse/format.h
@@ -83,6 +83,15 @@ int pa_format_info_valid(const pa_format_info *f);
 /** Returns non-zero when the format info structure represents a PCM (i.e. uncompressed data) format */
 int pa_format_info_is_pcm(const pa_format_info *f);
 
+/** Returns non-zero if the format represented \a first is a subset of
+ * the format represented by \second. This means that \a second must
+ * have all the fields that \a first does, but the reverse need not
+ * be true. This is typically expected to be used to check if a
+ * stream's format is compatible with a given sink. In such a case,
+ * \a first would be the sink's format and \a second would be the
+ * stream's.*/
+int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second);
+
 /** Maximum required string length for
  * pa_format_info_snprint(). Please note that this value can change
  * with any release without warning and without being considered API
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 351aeec..a659576 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -297,7 +297,6 @@ void pa_ext_device_manager_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);
-pa_bool_t pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second);
 pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map);
 pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
 pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss);

commit d1f13fa781ec69e4dbed000902123d255baaba5f
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Sat Apr 9 09:36:25 2011 +0530

    format: Add correct sample spec conversion for E-AC3
    
    IEC61937-encapsulated E-AC3 frames contain 6 audio blocks per substream,
    which corresponds to 1536 samples contained a 24576-byte frame. To cope
    with this, we maintain the s16le stereo sample spec, but quadruple the
    sample rate so that the conversion remains accurate.

diff --git a/src/pulse/format.c b/src/pulse/format.c
index a634b27..84df76b 100644
--- a/src/pulse/format.c
+++ b/src/pulse/format.c
@@ -203,5 +203,8 @@ pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *
     pa_return_val_if_fail(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE), FALSE);
     pa_return_val_if_fail(pa_atou(r, &ss->rate) == 0, FALSE);
 
+    if (f->encoding == PA_ENCODING_EAC3_IEC61937)
+        ss->rate *= 4;
+
     return TRUE;
 }

commit 62f56a9f6b01c277a8c4f15625473df4b73bd208
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Sun Apr 10 16:24:33 2011 +0530

    sink-input: Provide more information to client when format is lost
    
    When the sink format changes and we kill the stream, clients need a way
    to know (a) what device they should reconnect to, and (b) what the
    stream running time was when the stream got killed (pa_stream_get_time()
    won't work after the stream has been killed). This adds these two bits
    of information in the event callback's proplist parameter.

diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index bd2e6ca..6c055a5 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -766,6 +766,14 @@ void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
     if (s->state != PA_STREAM_READY)
         goto finish;
 
+    if (pa_streq(event, PA_STREAM_EVENT_FORMAT_LOST)) {
+        /* Let client know what the running time was when the stream had to be
+         * killed  */
+        pa_usec_t time;
+        if (pa_stream_get_time(s, &time) == 0)
+            pa_proplist_setf(pl, "stream-time", "%llu", (unsigned long long) time);
+    }
+
     if (s->event_callback)
         s->event_callback(s, event, pl, s->event_userdata);
 
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index f0558f4..b21cfae 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1594,8 +1594,12 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
         return -PA_ERR_NOTSUPPORTED;
 
     if (pa_sink_input_is_passthrough(i) && !pa_sink_check_format(dest, i->format)) {
+        pa_proplist *p = pa_proplist_new();
         pa_log_debug("New sink doesn't support stream format, sending format-changed and killing");
-        pa_sink_input_send_event(i, PA_STREAM_EVENT_FORMAT_LOST, NULL);
+        /* Tell the client what device we want to be on if it is going to
+         * reconnect */
+        pa_proplist_sets(p, "device", dest->name);
+        pa_sink_input_send_event(i, PA_STREAM_EVENT_FORMAT_LOST, p);
         return -PA_ERR_NOTSUPPORTED;
     }
 

commit 8d076d09902fe618e69f3d71e42299bffe2af437
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Apr 13 14:05:18 2011 +0530

    format: Extend properties to handle lists/ranges
    
    This replaces the simple string used by pa_format_info's proplist with a
    JSON string (accessed via new API only). This allows us to express lists
    and ranges more cleanly, and embed type information for future
    extensibility.
    
    We use json-c for JSON parsing. This is a lightweight depdency (32 KB on
    my system) and avoids the hassle of having to reinvent a JSON parser.
    
    Also included is a test which verifies functionality and is
    valgrind-clean.

diff --git a/configure.ac b/configure.ac
index 9edae0e..9d388ef 100644
--- a/configure.ac
+++ b/configure.ac
@@ -603,6 +603,12 @@ fi
 
 AC_CHECK_HEADERS_ONCE([valgrind/memcheck.h])
 
+#### json parsing ####
+
+PKG_CHECK_MODULES(LIBJSON, [ json >= 0.9 ])
+AC_SUBST(LIBJSON_CFLAGS)
+AC_SUBST(LIBJSON_LIBS)
+
 #### Sound file ####
 
 PKG_CHECK_MODULES(LIBSNDFILE, [ sndfile >= 1.0.20 ])
diff --git a/src/Makefile.am b/src/Makefile.am
index a68cdc2..7a4a32d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -252,6 +252,7 @@ TESTS = \
 		channelmap-test \
 		thread-mainloop-test \
 		utf8-test \
+		format-test \
 		get-binary-name-test \
 		ipacl-test \
 		hook-list-test \
@@ -285,6 +286,7 @@ TESTS_BINARIES = \
 		channelmap-test \
 		thread-mainloop-test \
 		utf8-test \
+		format-test \
 		get-binary-name-test \
 		ipacl-test \
 		hook-list-test \
@@ -370,6 +372,11 @@ utf8_test_CFLAGS = $(AM_CFLAGS)
 utf8_test_LDADD = $(AM_LDADD) libpulsecore- at PA_MAJORMINOR@.la libpulse.la libpulsecommon- at PA_MAJORMINOR@.la
 utf8_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
 
+format_test_SOURCES = tests/format-test.c
+format_test_CFLAGS = $(AM_CFLAGS)
+format_test_LDADD = $(AM_LDADD) libpulsecore- at PA_MAJORMINOR@.la libpulse.la libpulsecommon- at PA_MAJORMINOR@.la
+format_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
+
 get_binary_name_test_SOURCES = tests/get-binary-name-test.c
 get_binary_name_test_CFLAGS = $(AM_CFLAGS)
 get_binary_name_test_LDADD = $(AM_LDADD) libpulse.la libpulsecommon- at PA_MAJORMINOR@.la
@@ -652,9 +659,9 @@ libpulsecommon_ at PA_MAJORMINOR@_la_SOURCES = \
 		pulsecore/sndfile-util.c pulsecore/sndfile-util.h \
 		pulsecore/socket.h
 
-libpulsecommon_ at PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS)
+libpulsecommon_ at PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSNDFILE_CFLAGS) $(LIBJSON_CFLAGS)
 libpulsecommon_ at PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version
-libpulsecommon_ at PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS)
+libpulsecommon_ at PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBWRAP_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) $(LIBSAMPLERATE_LIBS) $(LIBSNDFILE_LIBS) $(LIBJSON_LIBS)
 
 if HAVE_X11
 libpulsecommon_ at PA_MAJORMINOR@_la_SOURCES += pulse/client-conf-x11.c pulse/client-conf-x11.h
diff --git a/src/map-file b/src/map-file
index 8d68340..a12f30d 100644
--- a/src/map-file
+++ b/src/map-file
@@ -166,6 +166,11 @@ pa_format_info_free;
 pa_format_info_is_compatible;
 pa_format_info_is_pcm;
 pa_format_info_new;
+pa_format_info_set_prop_int;
+pa_format_info_set_prop_int_array;
+pa_format_info_set_prop_int_range;
+pa_format_info_set_prop_string;
+pa_format_info_set_prop_string_array;
 pa_format_info_snprint;
 pa_format_info_valid;
 pa_frame_size;
diff --git a/src/pulse/format.c b/src/pulse/format.c
index 84df76b..1cb804e 100644
--- a/src/pulse/format.c
+++ b/src/pulse/format.c
@@ -25,6 +25,8 @@
 #include <config.h>
 #endif
 
+#include <json.h>
+
 #include <pulse/internal.h>
 #include <pulse/xmalloc.h>
 #include <pulse/i18n.h>
@@ -34,6 +36,11 @@
 
 #include "format.h"
 
+#define PA_JSON_MIN_KEY "min"
+#define PA_JSON_MAX_KEY "max"
+
+static int pa_format_info_prop_compatible(const char *one, const char *two);
+
 const char *pa_encoding_to_string(pa_encoding_t e) {
     static const char* const table[]= {
         [PA_ENCODING_PCM] = "pcm",
@@ -131,7 +138,7 @@ int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second)
         value_one = pa_proplist_gets(first->plist, key);
         value_two = pa_proplist_gets(second->plist, key);
 
-        if (!value_two || !pa_streq(value_one, value_two))
+        if (!value_two || !pa_format_info_prop_compatible(value_one, value_two))
             return FALSE;
     }
 
@@ -148,13 +155,13 @@ pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_m
     f = pa_format_info_new();
     f->encoding = PA_ENCODING_PCM;
 
-    pa_proplist_sets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(ss->format));
-    pa_proplist_setf(f->plist, PA_PROP_FORMAT_RATE, "%u", (unsigned int) ss->rate);
-    pa_proplist_setf(f->plist, PA_PROP_FORMAT_CHANNELS, "%u", (unsigned int) ss->channels);
+    pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(ss->format));
+    pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, ss->rate);
+    pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, ss->channels);
 
     if (map) {
         pa_channel_map_snprint(cm, sizeof(cm), map);
-        pa_proplist_setf(f->plist, PA_PROP_FORMAT_CHANNEL_MAP, "%s", cm);
+        pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, cm);
     }
 
     return f;
@@ -162,36 +169,51 @@ pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_m
 
 /* For PCM streams */
 pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map) {
-    const char *sf, *r, *ch;
-    uint32_t channels;
+    char *sf = NULL, *m = NULL;
+    int rate, channels;
+    pa_bool_t ret = FALSE;
 
     pa_assert(f);
     pa_assert(ss);
     pa_return_val_if_fail(f->encoding == PA_ENCODING_PCM, FALSE);
 
-    pa_return_val_if_fail(sf = pa_proplist_gets(f->plist, PA_PROP_FORMAT_SAMPLE_FORMAT), FALSE);
-    pa_return_val_if_fail(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE), FALSE);
-    pa_return_val_if_fail(ch = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNELS), FALSE);
+    if (!pa_format_info_get_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, &sf))
+        goto out;
+    if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate))
+        goto out;
+    if (!pa_format_info_get_prop_int(f, PA_PROP_FORMAT_CHANNELS, &channels))
+        goto out;
 
-    pa_return_val_if_fail((ss->format = pa_parse_sample_format(sf)) != PA_SAMPLE_INVALID, FALSE);
-    pa_return_val_if_fail(pa_atou(r, &ss->rate) == 0, FALSE);
-    pa_return_val_if_fail(pa_atou(ch, &channels) == 0, FALSE);
+    if ((ss->format = pa_parse_sample_format(sf)) == PA_SAMPLE_INVALID)
+        goto out;
+
+    ss->rate = (uint32_t) rate;
     ss->channels = (uint8_t) channels;
 
     if (map) {
-        const char *m = pa_proplist_gets(f->plist, PA_PROP_FORMAT_CHANNEL_MAP);
         pa_channel_map_init(map);
 
-        if (m)
-            pa_return_val_if_fail(pa_channel_map_parse(map, m) != NULL, FALSE);
+        if (!pa_format_info_get_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, &m))
+            goto out;
+
+        if (m && pa_channel_map_parse(map, m) == NULL)
+            goto out;
     }
 
-    return TRUE;
+    ret = TRUE;
+
+out:
+    if (sf)
+        pa_xfree(sf);
+    if (m)
+        pa_xfree(m);
+
+    return ret;
 }
 
 /* For compressed streams */
 pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss) {
-    const char *r;
+    int rate;
 
     pa_assert(f);
     pa_assert(ss);
@@ -200,11 +222,219 @@ pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *
     ss->format = PA_SAMPLE_S16LE;
     ss->channels = 2;
 
-    pa_return_val_if_fail(r = pa_proplist_gets(f->plist, PA_PROP_FORMAT_RATE), FALSE);
-    pa_return_val_if_fail(pa_atou(r, &ss->rate) == 0, FALSE);
+    pa_return_val_if_fail(pa_format_info_get_prop_int(f, PA_PROP_FORMAT_RATE, &rate), FALSE);
+    ss->rate = (uint32_t) rate;
 
     if (f->encoding == PA_ENCODING_EAC3_IEC61937)
         ss->rate *= 4;
 
     return TRUE;
 }
+
+pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v) {
+    const char *str;
+    json_object *o;
+
+    pa_assert(f);
+    pa_assert(key);
+    pa_assert(v);
+
+    pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE);
+    o = json_tokener_parse(str);
+    pa_return_val_if_fail(!is_error(o), FALSE);
+    if (json_object_get_type(o) != json_type_int) {
+        json_object_put(o);
+        return FALSE;
+    }
+
+    *v = json_object_get_int(o);
+    json_object_put(o);
+
+    return TRUE;
+}
+
+pa_bool_t pa_format_info_get_prop_string(pa_format_info *f, const char *key, char **v) {
+    const char *str = NULL;
+    json_object *o;
+
+    pa_assert(f);
+    pa_assert(key);
+    pa_assert(v);
+
+    pa_return_val_if_fail(str = pa_proplist_gets(f->plist, key), FALSE);
+    o = json_tokener_parse(str);
+    pa_return_val_if_fail(!is_error(o), FALSE);
+    if (json_object_get_type(o) != json_type_string) {
+        json_object_put(o);
+        return FALSE;
+    }
+
+    *v = pa_xstrdup(json_object_get_string(o));
+    json_object_put(o);
+
+    return TRUE;
+}
+
+void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value) {
+    json_object *o;
+
+    pa_assert(f);
+    pa_assert(key);
+
+    o = json_object_new_int(value);
+
+    pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
+
+    json_object_put(o);
+}
+
+void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values) {
+    json_object *o;
+    int i;
+
+    pa_assert(f);
+    pa_assert(key);
+
+    o = json_object_new_array();
+
+    for (i = 0; i < n_values; i++)
+        json_object_array_add(o, json_object_new_int(values[i]));
+
+    pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
+
+    json_object_put(o);
+}
+
+void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max) {
+    json_object *o;
+
+    pa_assert(f);
+    pa_assert(key);
+
+    o = json_object_new_object();
+
+    json_object_object_add(o, PA_JSON_MIN_KEY, json_object_new_int(min));
+    json_object_object_add(o, PA_JSON_MAX_KEY, json_object_new_int(max));
+
+    pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
+
+    json_object_put(o);
+}
+
+void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value) {
+    json_object *o;
+
+    pa_assert(f);
+    pa_assert(key);
+
+    o = json_object_new_string(value);
+
+    pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
+
+    json_object_put(o);
+}
+
+void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values) {
+    json_object *o;
+    int i;
+
+    pa_assert(f);
+    pa_assert(key);
+
+    o = json_object_new_array();
+
+    for (i = 0; i < n_values; i++)
+        json_object_array_add(o, json_object_new_string(values[i]));
+
+    pa_proplist_sets(f->plist, key, json_object_to_json_string(o));
+
+    json_object_put(o);
+}
+
+static pa_bool_t pa_json_is_fixed_type(json_object *o)
+{
+    switch(json_object_get_type(o)) {
+        case json_type_object:
+        case json_type_array:
+            return FALSE;
+
+        default:
+            return TRUE;
+    }
+}
+
+static int pa_json_value_equal(json_object *o1, json_object *o2) {
+    return (json_object_get_type(o1) == json_object_get_type(o2)) &&
+        pa_streq(json_object_to_json_string(o1), json_object_to_json_string(o2));
+}
+
+static int pa_format_info_prop_compatible(const char *one, const char *two) {
+    json_object *o1 = NULL, *o2 = NULL;
+    int i, ret = 0;
+
+    o1 = json_tokener_parse(one);
+    if (is_error(o1))
+        goto out;
+
+    o2 = json_tokener_parse(two);
+    if (is_error(o2))
+        goto out;
+
+    /* We don't deal with both values being non-fixed - just because there is no immediate need (FIXME) */
+    pa_return_val_if_fail(pa_json_is_fixed_type(o1) || pa_json_is_fixed_type(o2), FALSE);
+
+    if (pa_json_is_fixed_type(o1) && pa_json_is_fixed_type(o2)) {
+        ret = pa_json_value_equal(o1, o2);
+        goto out;
+    }
+
+    if (pa_json_is_fixed_type(o1)) {
+        json_object *tmp = o2;
+        o2 = o1;
+        o1 = tmp;
+    }
+
+    /* o2 is now a fixed type, and o1 is not */
+
+    if (json_object_get_type(o1) == json_type_array) {
+        for (i = 0; i < json_object_array_length(o1); i++) {
+            if (pa_json_value_equal(json_object_array_get_idx(o1, i), o2)) {
+                ret = 1;
+                break;
+            }
+        }
+    } else if (json_object_get_type(o1) == json_type_object) {
+        /* o1 should be a range type */
+        int min, max, v;
+        json_object *o_min = NULL, *o_max = NULL;
+
+        if (json_object_get_type(o2) != json_type_int) {
+            /* We don't support non-integer ranges */
+            goto out;
+        }
+
+        o_min = json_object_object_get(o1, PA_JSON_MIN_KEY);
+        if (!o_min || json_object_get_type(o_min) != json_type_int)
+            goto out;
+
+        o_max = json_object_object_get(o1, PA_JSON_MAX_KEY);
+        if (!o_max || json_object_get_type(o_max) != json_type_int)
+            goto out;
+
+        v = json_object_get_int(o2);
+        min = json_object_get_int(o_min);
+        max = json_object_get_int(o_max);
+
+        ret = v >= min && v <= max;
+    } else {
+        pa_log_warn("Got a format type that we don't support");
+    }
+
+out:
+    if (o1)
+        json_object_put(o1);
+    if (o2)
+        json_object_put(o2);
+
+    return ret;
+}
diff --git a/src/pulse/format.h b/src/pulse/format.h
index b0efe50..0498e68 100644
--- a/src/pulse/format.h
+++ b/src/pulse/format.h
@@ -102,6 +102,17 @@ int pa_format_info_is_compatible(pa_format_info *first, pa_format_info *second);
 /** Return a human-readable string representing the given format. \since 1.0 */
 char *pa_format_info_snprint(char *s, size_t l, const pa_format_info *f);
 
+/** Sets an integer property on the given format info */
+void pa_format_info_set_prop_int(pa_format_info *f, const char *key, int value);
+/** Sets a property with a list of integer values on the given format info */
+void pa_format_info_set_prop_int_array(pa_format_info *f, const char *key, const int *values, int n_values);
+/** Sets a property which can have any value in a given integer range on the given format info */
+void pa_format_info_set_prop_int_range(pa_format_info *f, const char *key, int min, int max);
+/** Sets a string property on the given format info */
+void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const char *value);
+/** Sets a property with a list of string values on the given format info */
+void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values);
+
 PA_C_DECL_END
 
 #endif
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index a659576..40f6804 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -300,6 +300,8 @@ void pa_format_info_free2(pa_format_info *f, void *userdata);
 pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_map *map);
 pa_bool_t pa_format_info_to_sample_spec(pa_format_info *f, pa_sample_spec *ss, pa_channel_map *map);
 pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *ss);
+pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v);
+pa_bool_t pa_format_info_get_prop_string(pa_format_info *f, const char *key, char **v);
 
 pa_bool_t pa_mainloop_is_our_api(pa_mainloop_api*m);
 
diff --git a/src/tests/format-test.c b/src/tests/format-test.c
new file mode 100644
index 0000000..888db8c
--- /dev/null
+++ b/src/tests/format-test.c
@@ -0,0 +1,106 @@
+/***
+  This file is part of PulseAudio.
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2.1 of the License,
+  or (at your option) any later version.
+
+  PulseAudio is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with PulseAudio; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+  USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#include <pulsecore/macro.h>
+#include <pulse/format.h>
+
+#define INIT(f) f = pa_format_info_new()
+#define DEINIT(f) pa_format_info_free(f);
+#define REINIT(f) { DEINIT(f); INIT(f); }
+
+int main(int argc, char *argv[]) {
+    pa_format_info *f1 = NULL, *f2 = NULL;
+    int rates1[] = { 32000, 44100, 48000 };
+    const char *strings[] = { "thing1", "thing2", "thing3" };
+
+    /* 1. Simple fixed format int check */
+    INIT(f1); INIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f1, PA_PROP_FORMAT_RATE, 32000);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 44100);
+    pa_assert(!pa_format_info_is_compatible(f1, f2));
+
+    /* 2. Check int array membership - positive */
+    REINIT(f1); REINIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int_array(f1, PA_PROP_FORMAT_RATE, rates1, PA_ELEMENTSOF(rates1));
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 44100);
+    pa_assert(pa_format_info_is_compatible(f1, f2));
+    pa_assert(pa_format_info_is_compatible(f2, f1));
+
+    /* 3. Check int array memebership - negative */
+    REINIT(f2);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 96000);
+    pa_assert(!pa_format_info_is_compatible(f1, f2));
+    pa_assert(!pa_format_info_is_compatible(f2, f1));
+
+    /* 4. Check int range - positive */
+    REINIT(f1); REINIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int_range(f1, PA_PROP_FORMAT_RATE, 32000, 48000);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 44100);
+    pa_assert(pa_format_info_is_compatible(f1, f2));
+    pa_assert(pa_format_info_is_compatible(f2, f1));
+
+    /* 5. Check int range - negative */
+    REINIT(f2);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_int(f2, PA_PROP_FORMAT_RATE, 96000);
+    pa_assert(!pa_format_info_is_compatible(f1, f2));
+    pa_assert(!pa_format_info_is_compatible(f2, f1));
+
+    /* 6. Simple fixed format string check */
+    REINIT(f1); REINIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string(f1, "format.test_string", "thing1");
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string(f2, "format.test_string", "notthing1");
+    pa_assert(!pa_format_info_is_compatible(f1, f2));
+
+    /* 7. Check string array membership - positive */
+    REINIT(f1); REINIT(f2);
+    f1->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string_array(f1, "format.test_string", strings, PA_ELEMENTSOF(strings));
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string(f2, "format.test_string", "thing3");
+    pa_assert(pa_format_info_is_compatible(f1, f2));
+    pa_assert(pa_format_info_is_compatible(f2, f1));
+
+    /* 8. Check string array memebership - negative */
+    REINIT(f2);
+    f2->encoding = PA_ENCODING_AC3_IEC61937;
+    pa_format_info_set_prop_string(f2, "format.test_string", "thing5");
+    pa_assert(!pa_format_info_is_compatible(f1, f2));
+    pa_assert(!pa_format_info_is_compatible(f2, f1));
+
+    DEINIT(f1);
+    DEINIT(f2);
+
+    return 0;
+}

commit 1404db3d47c77ef3a360feed4ae8932cc8e443a0
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Wed Apr 13 15:37:25 2011 +0530

    format: Add some convenience API for setting properties
    
    Adds functions to set sample format, rate, channels and channel map on a
    format to make life easier for users of the API.

diff --git a/src/map-file b/src/map-file
index a12f30d..026ac10 100644
--- a/src/map-file
+++ b/src/map-file
@@ -166,11 +166,15 @@ pa_format_info_free;
 pa_format_info_is_compatible;
 pa_format_info_is_pcm;
 pa_format_info_new;
+pa_format_info_set_channel_map;
+pa_format_info_set_channels;
 pa_format_info_set_prop_int;
 pa_format_info_set_prop_int_array;
 pa_format_info_set_prop_int_range;
 pa_format_info_set_prop_string;
 pa_format_info_set_prop_string_array;
+pa_format_info_set_rate;
+pa_format_info_set_sample_format;
 pa_format_info_snprint;
 pa_format_info_valid;
 pa_frame_size;
diff --git a/src/pulse/format.c b/src/pulse/format.c
index 1cb804e..a1a0981 100644
--- a/src/pulse/format.c
+++ b/src/pulse/format.c
@@ -155,9 +155,9 @@ pa_format_info* pa_format_info_from_sample_spec(pa_sample_spec *ss, pa_channel_m
     f = pa_format_info_new();
     f->encoding = PA_ENCODING_PCM;
 
-    pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(ss->format));
-    pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, ss->rate);
-    pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, ss->channels);
+    pa_format_info_set_sample_format(f, ss->format);
+    pa_format_info_set_rate(f, ss->rate);
+    pa_format_info_set_channels(f, ss->channels);
 
     if (map) {
         pa_channel_map_snprint(cm, sizeof(cm), map);
@@ -231,6 +231,26 @@ pa_bool_t pa_format_info_to_sample_spec_fake(pa_format_info *f, pa_sample_spec *
     return TRUE;
 }
 
+void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf) {
+    pa_format_info_set_prop_string(f, PA_PROP_FORMAT_SAMPLE_FORMAT, pa_sample_format_to_string(sf));
+}
+
+void pa_format_info_set_rate(pa_format_info *f, int rate) {
+    pa_format_info_set_prop_int(f, PA_PROP_FORMAT_RATE, rate);
+}
+
+void pa_format_info_set_channels(pa_format_info *f, int channels) {
+    pa_format_info_set_prop_int(f, PA_PROP_FORMAT_CHANNELS, channels);
+}
+
+void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map) {
+    char map_str[PA_CHANNEL_MAP_SNPRINT_MAX];
+
+    pa_channel_map_snprint(map_str, sizeof(map_str), map);
+
+    pa_format_info_set_prop_string(f, PA_PROP_FORMAT_CHANNEL_MAP, map_str);
+}
+
 pa_bool_t pa_format_info_get_prop_int(pa_format_info *f, const char *key, int *v) {
     const char *str;
     json_object *o;
diff --git a/src/pulse/format.h b/src/pulse/format.h
index 0498e68..06e1fe6 100644
--- a/src/pulse/format.h
+++ b/src/pulse/format.h
@@ -26,6 +26,8 @@
 
 #include <pulse/cdecl.h>
 #include <pulse/proplist.h>
+#include <pulse/sample.h>
+#include <pulse/channelmap.h>
 
 PA_C_DECL_BEGIN
 
@@ -113,6 +115,15 @@ void pa_format_info_set_prop_string(pa_format_info *f, const char *key, const ch
 /** Sets a property with a list of string values on the given format info */
 void pa_format_info_set_prop_string_array(pa_format_info *f, const char *key, const char **values, int n_values);
 
+/** Convenience method to set the sample format as a property on the given format */
+void pa_format_info_set_sample_format(pa_format_info *f, pa_sample_format_t sf);
+/** Convenience method to set the sampling rate as a property on the given format */
+void pa_format_info_set_rate(pa_format_info *f, int rate);
+/** Convenience method to set the number of channels as a property on the given format */
+void pa_format_info_set_channels(pa_format_info *f, int channels);
+/** Convenience method to set the channel map as a property on the given format */
+void pa_format_info_set_channel_map(pa_format_info *f, const pa_channel_map *map);
+
 PA_C_DECL_END
 
 #endif

commit cebb4e031d3ec5c99b1d633d6582d35b3379a6cc
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Thu May 5 15:58:46 2011 +0530

    doxygen: generate documentation for format.h

diff --git a/doxygen/doxygen.conf.in b/doxygen/doxygen.conf.in
index 7dc0f8b..0306114 100644
--- a/doxygen/doxygen.conf.in
+++ b/doxygen/doxygen.conf.in
@@ -417,7 +417,7 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h ../src/pulse/proplist.h ../src/pulse/gccmacro.h ../src/pulse/ext-stream-restore.h ../src/pulse/rtclock.h
+INPUT                  = ../src/pulse/context.h ../src/pulse/stream.h ../src/pulse/pulseaudio.h ../src/pulse/sample.h ../src/pulse/def.h ../src/pulse/subscribe.h ../src/pulse/introspect.h ../src/pulse/scache.h ../src/pulse/mainloop-api.h ../src/pulse/glib-mainloop.h ../src/pulse/mainloop.h ../src/pulse/mainloop-signal.h ../src/pulse/error.h ../src/pulse/operation.h ../src/pulse/simple.h ../src/pulse/version.h ../src/pulse/volume.h ../src/pulse/channelmap.h ../src/pulse/thread-mainloop.h ../src/pulse/xmalloc.h ../src/pulse/utf8.h ../src/pulse/util.h ../src/pulse/timeval.h ../src/pulse/proplist.h ../src/pulse/gccmacro.h ../src/pulse/ext-stream-restore.h ../src/pulse/rtclock.h ../src/pulse/format.h
 
 # If the value of the INPUT tag contains directories, you can use the
 # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp

commit 7ebc5033637d7f0ca8ece80259d8a5dc6b30557b
Author: Arun Raghavan <arun.raghavan at collabora.co.uk>
Date:   Sun May 15 09:54:17 2011 +0530

    module-tunnel: Update for recent protocol changes
    
    This updates the tunnel module for protocol version >= 19.
    
    module-tunnel-sink does not proxy server-side passthrough support (yet).
    This would require a few more changes, namely keeping track of what
    formats are available and if any other sink inputs are connected on the
    server-side.

diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index 6cb22e0..1b2d3a1 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -1095,6 +1095,23 @@ static void sink_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_t
         }
     }
 
+    if (u->version >= 21) {
+        uint8_t n_formats;
+        pa_format_info format;
+
+        if (pa_tagstruct_getu8(t, &n_formats) < 0) { /* no. of formats */
+            pa_log("Parse failure");
+            goto fail;
+        }
+
+        for (uint8_t j = 0; j < n_formats; j++) {
+            if (pa_tagstruct_get_format_info(t, &format)) { /* format info */
+                pa_log("Parse failure");
+                goto fail;
+            }
+        }
+    }
+
     if (!pa_tagstruct_eof(t)) {
         pa_log("Packet too long");
         goto fail;
@@ -1128,6 +1145,7 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag
     pa_channel_map channel_map;
     pa_cvolume volume;
     pa_proplist *pl;
+    pa_bool_t b;
 
     pa_assert(pd);
     pa_assert(u);
@@ -1175,6 +1193,33 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag
         }
     }
 
+    if (u->version >= 19) {
+        if (pa_tagstruct_get_boolean(t, &b) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (u->version >= 20) {
+        if (pa_tagstruct_get_boolean(t, &b) < 0 ||
+            pa_tagstruct_get_boolean(t, &b) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
+    if (u->version >= 21) {
+        pa_format_info format;
+
+        if (pa_tagstruct_get_format_info(t, &format) < 0) {
+
+            pa_log("Parse failure");
+            goto fail;
+        }
+    }
+
     if (!pa_tagstruct_eof(t)) {
         pa_log("Packet too long");
         goto fail;
@@ -1491,6 +1536,13 @@ static void create_stream_callback(pa_pdispatch *pd, uint32_t command,  uint32_t
 /* #endif */
     }
 
+    if (u->version >= 21) {
+        pa_format_info format;
+
+        if (pa_tagstruct_get_format_info(t, &format) < 0)
+            goto parse_error;
+    }
+
     if (!pa_tagstruct_eof(t))
         goto parse_error;
 
@@ -1693,6 +1745,13 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
         pa_tagstruct_put_boolean(reply, FALSE); /* passthrough stream */
 #endif
 
+#ifdef TUNNEL_SINK
+    if (u->version >= 21) {
+        /* We're not using the extended API, so n_formats = 0 and that's that */
+        pa_tagstruct_putu8(t, 0);
+    }
+#endif
+
     pa_pstream_send_tagstruct(u->pstream, reply);
     pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);
 

commit 26b4bd74aa92ed51a33a59af46ec32d7e5b66628
Merge: be4208d 7ebc503
Author: Colin Guthrie <colin at mageia.org>
Date:   Sun May 15 13:14:33 2011 +0100

    Merge branch 'passthrough'


commit 3e0332b0719a037dda99e09125b7969c377f5654
Author: Colin Guthrie <colin at mageia.org>
Date:   Sun May 15 14:16:52 2011 +0100

    tests: Fix resampler-test.
    
    This has been broken since c376ac5920fdeb46ca844d9518e22f17adffb635 when run
    without any arguments. Passing in -v (verbose) caused the test to work fine.
    
    I think this oversight is just a thinko in the original work but it obviously
    broke 'make check' and thus distcheck.
    
    Also fix a couple compiler warnings.

diff --git a/src/tests/resampler-test.c b/src/tests/resampler-test.c
index 69d7ab0..097465a 100644
--- a/src/tests/resampler-test.c
+++ b/src/tests/resampler-test.c
@@ -346,7 +346,6 @@ int main(int argc, char *argv[]) {
             case 'v':
                 pa_log_set_level(PA_LOG_DEBUG);
                 verbose = 1;
-                ret = 0;
                 break;
 
             case ARG_VERSION:
@@ -403,6 +402,7 @@ int main(int argc, char *argv[]) {
         }
     }
 
+    ret = 0;
     pa_assert_se(pool = pa_mempool_new(FALSE, 0));
 
     if (!all_formats) {
@@ -420,7 +420,7 @@ int main(int argc, char *argv[]) {
 
         ts = pa_rtclock_now();
         pa_assert_se(resampler = pa_resampler_new(pool, &a, NULL, &b, NULL, method, 0));
-        printf("init: %llu\n", pa_rtclock_now() - ts);
+        printf("init: %llu\n", (long long unsigned)(pa_rtclock_now() - ts));
 
         i.memblock = pa_memblock_new(pool, pa_usec_to_bytes(1*PA_USEC_PER_SEC, &a) / pa_frame_size(&a));
 
@@ -431,12 +431,11 @@ int main(int argc, char *argv[]) {
             pa_resampler_run(resampler, &i, &j);
             pa_memblock_unref(j.memblock);
         }
-        printf("resampling: %llu\n", pa_rtclock_now() - ts);
+        printf("resampling: %llu\n", (long long unsigned)(pa_rtclock_now() - ts));
         pa_memblock_unref(i.memblock);
 
         pa_resampler_free(resampler);
 
-        ret = 0;
         goto quit;
     }
 

commit d5cb59a8d676f2487fa7448f778f608ec3f6d3b8
Author: Colin Guthrie <colin at mageia.org>
Date:   Sun May 15 14:31:22 2011 +0100

    i18n: Fix POTFILES

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 35c20be..db5a940 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -195,3 +195,5 @@ src/modules/module-rygel-media-server.c
 src/modules/alsa/alsa-mixer.c
 src/modules/echo-cancel/module-echo-cancel.c
 src/modules/module-equalizer-sink.c
+src/modules/module-filter-apply.c
+src/tests/resampler-test.c

commit 47b7ca830e132ee585f9df6efb4aef82f2713d09
Author: Jyri Sarha <jyri.sarha at nokia.com>
Date:   Fri Apr 8 17:18:12 2011 +0300

    protocol-native: Stop auto timing updates if connected to suspended sink or source
    
    This quite is an old patch. It was added to N900 to avoid unnecessary
    wake-ups when the phone is in power save mode (= blank screen and
    no user interaction). In this situation if the user had a browser
    window with flash animation open pulseaudio kept waking up every
    10 seconds, causing a severe hit to use times.
    
    Anyway I do not see any reason to send timing updates if the sink or
    source where the stream is connected to is suspended.

diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index 6c055a5..cd5182a 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -389,12 +389,18 @@ static void request_auto_timing_update(pa_stream *s, pa_bool_t force) {
     }
 
     if (s->auto_timing_update_event) {
-        if (force)
-            s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
+        if (s->suspended && !force) {
+            pa_assert(s->mainloop);
+            s->mainloop->time_free(s->auto_timing_update_event);
+            s->auto_timing_update_event = NULL;
+        } else {
+            if (force)
+                s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
 
-        pa_context_rttime_restart(s->context, s->auto_timing_update_event, pa_rtclock_now() + s->auto_timing_interval_usec);
+            pa_context_rttime_restart(s->context, s->auto_timing_update_event, pa_rtclock_now() + s->auto_timing_interval_usec);
 
-        s->auto_timing_interval_usec = PA_MIN(AUTO_TIMING_INTERVAL_END_USEC, s->auto_timing_interval_usec*2);
+            s->auto_timing_interval_usec = PA_MIN(AUTO_TIMING_INTERVAL_END_USEC, s->auto_timing_interval_usec*2);
+        }
     }
 }
 
@@ -475,6 +481,8 @@ static void check_smoother_status(pa_stream *s, pa_bool_t aposteriori, pa_bool_t
      * if prebuf is non-zero! */
 }
 
+static void auto_timing_update_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata);
+
 void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_context *c = userdata;
     pa_stream *s;
@@ -562,6 +570,12 @@ void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
 
     s->suspended = suspended;
 
+    if ((s->flags & PA_STREAM_AUTO_TIMING_UPDATE) && !suspended && !s->auto_timing_update_event) {
+        s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
+        s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
+        request_auto_timing_update(s, TRUE);
+    }
+
     check_smoother_status(s, TRUE, FALSE, FALSE);
     request_auto_timing_update(s, TRUE);
 
@@ -680,6 +694,12 @@ void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t ta
 
     s->suspended = suspended;
 
+    if ((s->flags & PA_STREAM_AUTO_TIMING_UPDATE) && !suspended && !s->auto_timing_update_event) {
+        s->auto_timing_interval_usec = AUTO_TIMING_INTERVAL_START_USEC;
+        s->auto_timing_update_event = pa_context_rttime_new(s->context, pa_rtclock_now() + s->auto_timing_interval_usec, &auto_timing_update_callback, s);
+        request_auto_timing_update(s, TRUE);
+    }
+
     check_smoother_status(s, TRUE, FALSE, FALSE);
     request_auto_timing_update(s, TRUE);
 

commit ff79b3147cd9ecee353b0948e28b180088a49382
Author: Jyri Sarha <jyri.sarha at nokia.com>
Date:   Fri Apr 8 17:18:13 2011 +0300

    suspend-on-idle: Trigger mempool vacuuming
    
    In a setup with one or more filter sinks or sources there is always at
    least one stream existing. In such a situation normal mempool
    vacuuming never happens. This patch causes suspend-on-idle module to
    vacuum memory when ever it notices that all sinks and sources are
    suspended. The behavior can be enabled with a module parameter.

diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index 2bc424b..0069270 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -41,10 +41,13 @@ PA_MODULE_AUTHOR("Lennart Poettering");
 PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE("timeout=<timeout>");
+PA_MODULE_USAGE(
+        "timeout=<timeout> "
+        "mempool_vacuum=<vacuum memory if all sinks and sources are suspended?>");
 
 static const char* const valid_modargs[] = {
     "timeout",
+    "mempool_vacuum",
     NULL,
 };
 
@@ -71,6 +74,8 @@ struct userdata {
         *source_output_move_finish_slot,
         *sink_input_state_changed_slot,
         *source_output_state_changed_slot;
+
+    pa_bool_t mempool_vacuum:1;
 };
 
 struct device_info {
@@ -81,6 +86,29 @@ struct device_info {
     pa_time_event *time_event;
 };
 
+static void check_meempool_vacuum(struct device_info *d) {
+    pa_sink *si;
+    pa_source *so;
+    uint32_t idx;
+
+    pa_assert(d);
+    pa_assert(d->userdata);
+    pa_assert(d->userdata->core);
+
+    idx = 0;
+    PA_IDXSET_FOREACH(si, d->userdata->core->sinks, idx)
+        if (pa_sink_get_state(si) != PA_SINK_SUSPENDED)
+            return;
+
+    idx = 0;
+    PA_IDXSET_FOREACH(so, d->userdata->core->sources, idx)
+        if (pa_source_get_state(so) != PA_SOURCE_SUSPENDED)
+            return;
+
+    pa_log_info("All sinks and sources are suspended, vacuuming memory");
+    pa_mempool_vacuum(d->userdata->core->mempool);
+}
+
 static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
     struct device_info *d = userdata;
 
@@ -91,11 +119,15 @@ static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval
     if (d->sink && pa_sink_check_suspend(d->sink) <= 0 && !(d->sink->suspend_cause & PA_SUSPEND_IDLE)) {
         pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name);
         pa_sink_suspend(d->sink, TRUE, PA_SUSPEND_IDLE);
+        if (d->userdata->mempool_vacuum)
+            check_meempool_vacuum(d);
     }
 
     if (d->source && pa_source_check_suspend(d->source) <= 0 && !(d->source->suspend_cause & PA_SUSPEND_IDLE)) {
         pa_log_info("Source %s idle for too long, suspending ...", d->source->name);
         pa_source_suspend(d->source, TRUE, PA_SUSPEND_IDLE);
+        if (d->userdata->mempool_vacuum)
+            check_meempool_vacuum(d);
     }
 }
 
@@ -417,6 +449,7 @@ int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
     uint32_t timeout = 5;
+    pa_bool_t mempool_vacuum = FALSE;
     uint32_t idx;
     pa_sink *sink;
     pa_source *source;
@@ -433,10 +466,16 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
+    if (pa_modargs_get_value_boolean(ma, "mempool_vacuum", &mempool_vacuum) < 0) {
+        pa_log("Failed to parse mempool_vacuum boolean parameter.");
+        goto fail;
+    }
+
     m->userdata = u = pa_xnew(struct userdata, 1);
     u->core = m->core;
     u->timeout = timeout;
     u->device_infos = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+    u->mempool_vacuum = mempool_vacuum;
 
     for (sink = pa_idxset_first(m->core->sinks, &idx); sink; sink = pa_idxset_next(m->core->sinks, &idx))
         device_new_hook_cb(m->core, PA_OBJECT(sink), u);

commit ce8b03bb26243f01623699a115a34706b1348b77
Author: Colin Guthrie <colin at mageia.org>
Date:   Sun May 15 15:05:44 2011 +0100

    bluetooth: Fix early return styling and add missing return value
    
    Thanks to Tanu Kaskinen for pointing out the missing return.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 086fce9..1ff752a 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1936,10 +1936,11 @@ static int sco_over_pcm_state_update(struct userdata *u, pa_bool_t changed) {
 
         if (u->transport)
             return bt_transport_acquire(u, TRUE);
-        else
-            return start_stream_fd(u);
 
-    } else if (changed) {
+        return start_stream_fd(u);
+    }
+
+    if (changed) {
         if (u->service_fd < 0 && u->stream_fd < 0)
             return 0;
 
@@ -1954,9 +1955,9 @@ static int sco_over_pcm_state_update(struct userdata *u, pa_bool_t changed) {
             pa_close(u->service_fd);
             u->service_fd = -1;
         }
-
-        return 0;
     }
+
+    return 0;
 }
 
 static pa_hook_result_t sink_state_changed_cb(pa_core *c, pa_sink *s, struct userdata *u) {

commit 3c4accfde22da87e9e1990a08ca9a86a2608228b
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Sat May 14 16:11:25 2011 +0300

    loopback: Add a modarg for disabling remixing.

diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c
index ca813b0..a5b08bd 100644
--- a/src/modules/module-loopback.c
+++ b/src/modules/module-loopback.c
@@ -59,7 +59,8 @@ PA_MODULE_USAGE(
         "sink_input_role=<media.role for the sink input> "
         "source_output_role=<media.role for the source output> "
         "source_dont_move=<boolean> "
-        "sink_dont_move=<boolean>");
+        "sink_dont_move=<boolean> "
+        "remix=<remix channels?> ");
 
 #define DEFAULT_LATENCY_MSEC 200
 
@@ -120,6 +121,7 @@ static const char* const valid_modargs[] = {
     "source_output_role",
     "source_dont_move",
     "sink_dont_move",
+    "remix",
     NULL,
 };
 
@@ -407,7 +409,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
     u->in_pop = FALSE;
 
     if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
-        pa_log_info("Coud not peek into queue");
+        pa_log_info("Could not peek into queue");
         return -1;
     }
 
@@ -645,6 +647,7 @@ int pa__init(pa_module *m) {
     pa_memchunk silence;
     uint32_t adjust_time_sec;
     const char *n;
+    pa_bool_t remix = TRUE;
 
     pa_assert(m);
 
@@ -663,6 +666,11 @@ int pa__init(pa_module *m) {
         goto fail;
     }
 
+    if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) {
+        pa_log("Invalid boolean remix parameter");
+        goto fail;
+    }
+
     ss = sink->sample_spec;
     map = sink->channel_map;
     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
@@ -713,7 +721,7 @@ int pa__init(pa_module *m) {
 
     pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss);
     pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
-    sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE;
+    sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE | (remix ? 0 : PA_SINK_INPUT_NO_REMIX);
 
     sink_dont_move = FALSE;
     if (pa_modargs_get_value_boolean(ma, "sink_dont_move", &sink_dont_move) < 0) {
@@ -764,8 +772,8 @@ int pa__init(pa_module *m) {
         pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ICON_NAME, n);
 
     pa_source_output_new_data_set_sample_spec(&source_output_data, &ss);
-    pa_sink_input_new_data_set_channel_map(&sink_input_data, &map);
-    source_output_data.flags = (pa_source_output_flags_t)0;
+    pa_source_output_new_data_set_channel_map(&source_output_data, &map);
+    source_output_data.flags = (remix ? 0 : PA_SOURCE_OUTPUT_NO_REMIX);
 
     source_dont_move = FALSE;
     if (pa_modargs_get_value_boolean(ma, "source_dont_move", &source_dont_move) < 0) {

commit e59fc6c6bcfc266e1f9ddaef3ed37d576ffa0b69
Author: Colin Guthrie <colin at mageia.org>
Date:   Sun May 15 18:41:31 2011 +0100

    sink-input: Fix memory leak of proplist when sending format-changed events

diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index b21cfae..300b728 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -1600,6 +1600,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
          * reconnect */
         pa_proplist_sets(p, "device", dest->name);
         pa_sink_input_send_event(i, PA_STREAM_EVENT_FORMAT_LOST, p);
+        pa_proplist_free(p);
         return -PA_ERR_NOTSUPPORTED;
     }
 

commit 4ff2e858948a8bfc82a012aef24a305fbbf4c6af
Author: Colin Guthrie <colin at mageia.org>
Date:   Mon May 16 09:26:32 2011 +0100

    protocol-native: Fix memory leaks introduced in protocol 21 (passthrough support)
    
    The proplist used may never be freed if an error condition was found with
    CHECK_VALIDITY macro and the formats idxset was never freed regardless
    of error state.
    
    This change fixes adds a new maco CHECK_VALIDITY_GOTO() which allows
    for cleanup to be done before returning.

diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 59b8724..e0a195e 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -1841,6 +1841,13 @@ if (!(expression)) { \
 } \
 } while(0);
 
+#define CHECK_VALIDITY_GOTO(pstream, expression, tag, error, label) do { \
+if (!(expression)) { \
+    pa_pstream_send_error((pstream), (tag), (error)); \
+    goto label; \
+} \
+} while(0);
+
 static pa_tagstruct *reply_new(uint32_t tag) {
     pa_tagstruct *reply;
 
@@ -1909,14 +1916,14 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
                 PA_TAG_INVALID) < 0) {
 
         protocol_error(c);
-        goto error;
+        goto finish;
     }
 
-    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
-    CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
-    CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY_GOTO(c->pstream, c->authorized, tag, PA_ERR_ACCESS, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID, finish);
+    CHECK_VALIDITY_GOTO(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID, finish);
 
     p = pa_proplist_new();
 
@@ -1935,7 +1942,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
             pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
 
             protocol_error(c);
-            goto error;
+            goto finish;
         }
     }
 
@@ -1946,7 +1953,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
             pa_tagstruct_get_proplist(t, p) < 0) {
 
             protocol_error(c);
-            goto error;
+            goto finish;
         }
     }
 
@@ -1956,7 +1963,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
             pa_tagstruct_get_boolean(t, &early_requests) < 0) {
 
             protocol_error(c);
-            goto error;
+            goto finish;
         }
     }
 
@@ -1967,7 +1974,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
             pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
 
             protocol_error(c);
-            goto error;
+            goto finish;
         }
     }
 
@@ -1976,7 +1983,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
         if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) {
 
             protocol_error(c);
-            goto error;
+            goto finish;
         }
     }
 
@@ -1984,7 +1991,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
 
         if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) {
             protocol_error(c);
-            goto error;
+            goto finish;
         }
     }
 
@@ -1992,7 +1999,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
 
         if (pa_tagstruct_getu8(t, &n_formats) < 0) {
             protocol_error(c);
-            goto error;
+            goto finish;
         }
 
         if (n_formats)
@@ -2002,39 +2009,39 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
             format = pa_format_info_new();
             if (pa_tagstruct_get_format_info(t, format) < 0) {
                 protocol_error(c);
-                goto error;
+                goto finish;
             }
             pa_idxset_put(formats, format, NULL);
         }
     }
 
     if (n_formats == 0) {
-        CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
-        CHECK_VALIDITY(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID);
-        CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
+        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) {
-            CHECK_VALIDITY(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID);
+            CHECK_VALIDITY_GOTO(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID, finish);
         }
     }
 
     if (!pa_tagstruct_eof(t)) {
         protocol_error(c);
-        goto error;
+        goto finish;
     }
 
     if (sink_index != PA_INVALID_INDEX) {
 
         if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
-            goto error;
+            goto finish;
         }
 
     } else if (sink_name) {
 
         if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
-            goto error;
+            goto finish;
         }
     }
 
@@ -2056,9 +2063,8 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     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);
-    pa_proplist_free(p);
 
-    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);
@@ -2106,14 +2112,12 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u
     }
 
     pa_pstream_send_tagstruct(c->pstream, reply);
-    return;
 
-error:
+finish:
     if (p)
         pa_proplist_free(p);
     if (formats)
         pa_idxset_free(formats, (pa_free2_cb_t) pa_format_info_free2, NULL);
-    return;
 }
 
 static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {

commit 2bc2abc68af3b091257d38662db3c65f7dab6273
Author: Sjoerd Simons <sjoerd.simons at collabora.co.uk>
Date:   Mon May 16 10:00:37 2011 +0000

    build-sys: Link libpulse directly to libdbus-1 if needed
    
    Linking libpulse with gold or when using ld --no-add-needed fails
    as libpulse uses dbus methods directly but isn't explicitly linked to it.
    So link to it when needed :)

diff --git a/src/Makefile.am b/src/Makefile.am
index 7a4a32d..d4f595c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -806,10 +806,15 @@ libpulse_la_SOURCES = \
 		pulse/volume.c pulse/volume.h \
 		pulse/xmalloc.c pulse/xmalloc.h
 
-libpulse_la_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+libpulse_la_CFLAGS = $(AM_CFLAGS)
 libpulse_la_LIBADD = $(AM_LIBADD) $(WINSOCK_LIBS) $(LTLIBICONV) libpulsecommon- at PA_MAJORMINOR@.la
 libpulse_la_LDFLAGS = $(AM_LDFLAGS) $(VERSIONING_LDFLAGS) -version-info $(LIBPULSE_VERSION_INFO)
 
+if HAVE_DBUS
+libpulse_la_CFLAGS += $(DBUS_CFLAGS)
+libpulse_la_LIBADD += $(DBUS_LIBS)
+endif
+
 libpulse_simple_la_SOURCES = pulse/simple.c pulse/simple.h
 libpulse_simple_la_CFLAGS = $(AM_CFLAGS)
 libpulse_simple_la_LIBADD = $(AM_LIBADD) libpulse.la libpulsecommon- at PA_MAJORMINOR@.la

-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list