[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, master-tx, updated. v0.9.15-test1-41-gcc526a0

Lennart Poettering gitmailer-noreply at 0pointer.de
Thu Feb 12 07:53:54 PST 2009


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-tx branch has been updated
      from  4bd9737725b85d90a7cf12b82528c2de70a7fbfe (commit)

- Log -----------------------------------------------------------------
cc526a0... prepare test2
e82b2fd... handle errors from BT service properly
c3b0d84... make module-hal-detect pick up all cards even when they have no device 0
84666db... properly free modargs object when init fails; don't abbreviate modargs in struct
a371306... tabs are evil
a7b992f... some minor fixups
6ada8d1... instead of reparsing the rate module argument when changing profile, simply restore the originally requested sample_spec, this also makes sure the channel count is properly reset
752f815... addendum to f56da9893: don't crash when s->sink is NULL
1837a96... call _kill functions instead of _unlink since the latter should only be called be the stream implementor
f0cc23d... Merge commit 'elmarco/bt-wip'
5d15425... minor reformatting
60c50bb... declare 'animation' stream role for Flash and suchlike
12db1a5... make gcc 4.4 shut up
a729786... implement a module that corks music/video streams automatically when a phone call is active
823431e... allow sending meta/policy events to clients
86bec09... pulsecore: add PA_CORE_HOOK_*_MOVE_FAIL
f56da98... suspend-on-idle: don't crash when so->source is NULL
cce4359... bluetooth: reinitialize the sample spec when switching profile
cac0f9e... bluetooth: export nrec
452e2b9... bluetooth: suspend SCO state when over PCM
c8a240c... bluetooth: SCO over PCM
b35ae7f... bluetooth: reconnect to audio service when switching profile
539abc3... bluetooth: typo
-----------------------------------------------------------------------

Summary of changes:
 configure.ac                                      |    2 +-
 src/Makefile.am                                   |   10 +-
 src/daemon/default.pa.in                          |    3 +
 src/map-file                                      |    2 +
 src/modules/bluetooth/module-bluetooth-device.c   |  319 ++++++++++++++++-----
 src/modules/bluetooth/module-bluetooth-discover.c |   31 ++-
 src/modules/module-augment-properties.c           |    4 +-
 src/modules/module-cork-music-on-phone.c          |  224 +++++++++++++++
 src/modules/module-hal-detect.c                   |    7 +-
 src/modules/module-suspend-on-idle.c              |    6 +
 src/modules/module-tunnel.c                       |    2 +-
 src/pulse/context.c                               |   62 ++++-
 src/pulse/context.h                               |   11 +
 src/pulse/def.h                                   |   10 +
 src/pulse/internal.h                              |    7 +
 src/pulse/proplist.h                              |    2 +-
 src/pulse/stream.c                                |   59 ++++
 src/pulse/stream.h                                |   14 +-
 src/pulsecore/client.c                            |   29 ++
 src/pulsecore/client.h                            |   10 +
 src/pulsecore/core.h                              |    5 +
 src/pulsecore/native-common.h                     |    4 +
 src/pulsecore/protocol-native.c                   |   66 +++++
 src/pulsecore/sink-input.c                        |   29 ++
 src/pulsecore/sink-input.h                        |   12 +
 src/pulsecore/sink.c                              |    8 +-
 src/pulsecore/source-output.c                     |   28 ++
 src/pulsecore/source-output.h                     |   12 +
 src/pulsecore/source.c                            |    8 +-
 src/utils/pacat.c                                 |   13 +
 30 files changed, 897 insertions(+), 102 deletions(-)
 create mode 100644 src/modules/module-cork-music-on-phone.c

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

commit 539abc365f8e12a5241694920a2e09e0bfe05710
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Mon Feb 2 11:06:31 2009 +0200

    bluetooth: typo

diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index f9540b3..0523648 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -123,7 +123,7 @@ int pa__init(pa_module* m) {
     }
 
     if (pa_modargs_get_value_boolean(ma, "async", &async) < 0) {
-        pa_log("Failed to parse tsched argument.");
+        pa_log("Failed to parse async argument.");
         goto fail;
     }
 

commit b35ae7f53116949d0cf5d991883e3b54f2c48aa5
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Mon Feb 9 22:11:46 2009 +0200

    bluetooth: reconnect to audio service when switching profile
    
    Currently, Bluez audio service crash when reusing the same control
    socket to switch to different profiles. This typically happen when
    first switching from HSP to A2DP on dual headsets.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index de6bded..0067d91 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1282,6 +1282,19 @@ static int add_source(struct userdata *u) {
 static int init_bt(struct userdata *u) {
     pa_assert(u);
 
+    /* shutdown bt */
+    if (u->stream_fd >= 0) {
+        pa_close(u->stream_fd);
+        u->stream_fd = -1;
+    }
+
+    if (u->service_fd >= 0) {
+        pa_close(u->service_fd);
+        u->service_fd = -1;
+    }
+
+    u->write_type = u->read_type = 0;
+
     /* connect to the bluez audio service */
     if ((u->service_fd = bt_audio_service_open()) < 0) {
         pa_log_error("Couldn't connect to bluetooth audio service");
@@ -1292,17 +1305,6 @@ static int init_bt(struct userdata *u) {
     return 0;
 }
 
-static void shutdown_bt(struct userdata *u) {
-    pa_assert(u);
-
-    if (u->stream_fd <= 0) {
-        pa_close(u->stream_fd);
-        u->stream_fd = -1;
-    }
-
-    u->write_type = u->read_type = 0;
-}
-
 static int setup_bt(struct userdata *u) {
     pa_assert(u);
 
@@ -1417,7 +1419,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
     }
 
     stop_thread(u);
-    shutdown_bt(u);
+    init_bt(u);
 
     if (u->write_memchunk.memblock) {
         pa_memblock_unref(u->write_memchunk.memblock);

commit c8a240cddd23eba83c4d3fc901fe2e42a871823f
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Tue Feb 3 17:15:41 2009 +0200

    bluetooth: SCO over PCM

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 0067d91..2302b1d 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -43,6 +43,7 @@
 #include <pulsecore/rtpoll.h>
 #include <pulsecore/time-smoother.h>
 #include <pulsecore/rtclock.h>
+#include <pulsecore/namereg.h>
 
 #include <modules/dbus-util.h>
 
@@ -71,7 +72,9 @@ PA_MODULE_USAGE(
         "profile=<a2dp|hsp> "
         "rate=<sample rate> "
         "channels=<number of channels> "
-        "path=<device object path>");
+        "path=<device object path> "
+	"sco_sink=<SCO over PCM sink name> "
+	"sco_source=<SCO over PCM source name>");
 
 static const char* const valid_modargs[] = {
     "name",
@@ -83,6 +86,8 @@ static const char* const valid_modargs[] = {
     "rate",
     "channels",
     "path",
+    "sco_sink",
+    "sco_source",
     NULL
 };
 
@@ -98,6 +103,11 @@ struct a2dp_info {
     uint16_t seq_num;                    /* Cumulative packet sequence */
 };
 
+struct hsp_info {
+    pa_sink *sco_sink;
+    pa_source *sco_source;
+};
+
 enum profile {
     PROFILE_A2DP,
     PROFILE_HSP,
@@ -132,6 +142,7 @@ struct userdata {
     size_t block_size;
 
     struct a2dp_info a2dp;
+    struct hsp_info hsp;
     pa_dbus_connection *connection;
 
     enum profile profile;
@@ -1215,67 +1226,97 @@ static char *get_name(const char *type, pa_modargs *ma, const char *device_id, p
     return pa_sprintf_malloc("bluez_%s.%s", type, n);
 }
 
+#define USE_SCO_OVER_PCM(u) (u->profile == PROFILE_HSP && (u->hsp.sco_sink && u->hsp.sco_source))
+
 static int add_sink(struct userdata *u) {
-    pa_sink_new_data data;
-    pa_bool_t b;
 
-    pa_sink_new_data_init(&data);
-    data.driver = __FILE__;
-    data.module = u->module;
-    pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
-    pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
-    data.card = u->card;
-    data.name = get_name("sink", u->modargs, u->device->address, &b);
-    data.namereg_fail = b;
+    if (USE_SCO_OVER_PCM(u)) {
+        pa_proplist *p;
 
-    u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
-    pa_sink_new_data_done(&data);
+        u->sink = u->hsp.sco_sink;
+        u->sink->card = u->card; /* FIXME! */
+        p = pa_proplist_new();
+        pa_proplist_sets(p, "bluetooth.protocol", "sco");
+        pa_proplist_update(u->sink->proplist, PA_UPDATE_MERGE, p);
+        pa_proplist_free(p);
 
-    if (!u->sink) {
-        pa_log_error("Failed to create sink");
-        return -1;
+    } else {
+        pa_sink_new_data data;
+        pa_bool_t b;
+
+        pa_sink_new_data_init(&data);
+        data.driver = __FILE__;
+        data.module = u->module;
+        pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
+        pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
+        data.card = u->card;
+        data.name = get_name("sink", u->modargs, u->device->address, &b);
+        data.namereg_fail = b;
+
+        u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
+        pa_sink_new_data_done(&data);
+
+        if (!u->sink) {
+            pa_log_error("Failed to create sink");
+            return -1;
+        }
+
+        u->sink->userdata = u;
+        u->sink->parent.process_msg = sink_process_msg;
+
+        pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
+        pa_sink_set_rtpoll(u->sink, u->rtpoll);
     }
 
-    u->sink->userdata = u;
-    u->sink->parent.process_msg = sink_process_msg;
 /*     u->sink->get_volume = sink_get_volume_cb; */
 /*     u->sink->set_volume = sink_set_volume_cb; */
 
-    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
-    pa_sink_set_rtpoll(u->sink, u->rtpoll);
-
     return 0;
 }
 
 static int add_source(struct userdata *u) {
-    pa_source_new_data data;
-    pa_bool_t b;
 
-    pa_source_new_data_init(&data);
-    data.driver = __FILE__;
-    data.module = u->module;
-    pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
-    pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
-    data.card = u->card;
-    data.name = get_name("source", u->modargs, u->device->address, &b);
-    data.namereg_fail = b;
+    if (USE_SCO_OVER_PCM(u)) {
+        pa_proplist *p;
 
-    u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
-    pa_source_new_data_done(&data);
+        u->source = u->hsp.sco_source;
+        u->source->card = u->card; /* FIXME! */
+        p = pa_proplist_new();
+        pa_proplist_sets(p, "bluetooth.protocol", "sco");
+        pa_proplist_update(u->source->proplist, PA_UPDATE_MERGE, p);
+        pa_proplist_free(p);
 
-    if (!u->source) {
-        pa_log_error("Failed to create source");
-        return -1;
+    } else {
+        pa_source_new_data data;
+        pa_bool_t b;
+
+        pa_source_new_data_init(&data);
+        data.driver = __FILE__;
+        data.module = u->module;
+        pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
+        pa_proplist_sets(data.proplist, "bluetooth.protocol", u->profile == PROFILE_A2DP ? "a2dp" : "sco");
+        data.card = u->card;
+        data.name = get_name("source", u->modargs, u->device->address, &b);
+        data.namereg_fail = b;
+
+        u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
+        pa_source_new_data_done(&data);
+
+        if (!u->source) {
+            pa_log_error("Failed to create source");
+            return -1;
+        }
+
+        u->source->userdata = u;
+        u->source->parent.process_msg = source_process_msg;
+
+        pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+        pa_source_set_rtpoll(u->source, u->rtpoll);
     }
 
-    u->source->userdata = u;
-    u->source->parent.process_msg = source_process_msg;
 /*     u->source->get_volume = source_get_volume_cb; */
 /*     u->source->set_volume = source_set_volume_cb; */
 
-    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
-    pa_source_set_rtpoll(u->source, u->rtpoll);
-
     return 0;
 }
 
@@ -1318,6 +1359,11 @@ static int setup_bt(struct userdata *u) {
 
     pa_log_debug("Connection to the device configured");
 
+    if (USE_SCO_OVER_PCM(u)) {
+        pa_log_debug("Configured to use SCO over PCM");
+        return 0;
+    }
+
     if (setup_stream_fd(u) < 0)
         return -1;
 
@@ -1377,6 +1423,12 @@ static int start_thread(struct userdata *u) {
     pa_assert(!u->thread);
     pa_assert(!u->rtpoll_item);
 
+    if (USE_SCO_OVER_PCM(u)) {
+        pa_sink_ref(u->sink);
+        pa_source_ref(u->source);
+        return 0;
+    }
+
     u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
     pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
     pollfd->fd = u->stream_fd;
@@ -1410,12 +1462,14 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
 
     if (u->sink) {
         inputs = pa_sink_move_all_start(u->sink);
-        pa_sink_unlink(u->sink);
+	if (!USE_SCO_OVER_PCM(u))
+            pa_sink_unlink(u->sink);
     }
 
     if (u->source) {
         outputs = pa_source_move_all_start(u->source);
-        pa_source_unlink(u->source);
+	if (!USE_SCO_OVER_PCM(u))
+            pa_source_unlink(u->source);
     }
 
     stop_thread(u);
@@ -1604,6 +1658,18 @@ int pa__init(pa_module* m) {
     u->sample_spec = m->core->default_sample_spec;
     u->modargs = ma;
 
+    if (pa_modargs_get_value(ma, "sco_sink", NULL) &&
+        !(u->hsp.sco_sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_sink", NULL), PA_NAMEREG_SINK))) {
+        pa_log("SCO sink not found");
+        goto fail;
+    }
+
+    if (pa_modargs_get_value(ma, "sco_source", NULL) &&
+        !(u->hsp.sco_source = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sco_source", NULL), PA_NAMEREG_SOURCE))) {
+        pa_log("SCO source not found");
+        goto fail;
+    }
+
     if (pa_modargs_get_value_u32(ma, "rate", &u->sample_spec.rate) < 0 ||
         u->sample_spec.rate <= 0 || u->sample_spec.rate > PA_RATE_MAX) {
         pa_log_error("Failed to get rate from module arguments");
@@ -1704,10 +1770,10 @@ void pa__done(pa_module *m) {
     if (!(u = m->userdata))
         return;
 
-    if (u->sink)
+    if (u->sink && !USE_SCO_OVER_PCM(u))
         pa_sink_unlink(u->sink);
 
-    if (u->source)
+    if (u->source && !USE_SCO_OVER_PCM(u))
         pa_source_unlink(u->source);
 
     stop_thread(u);
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 0523648..3f8a751 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -42,15 +42,20 @@
 PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
 PA_MODULE_DESCRIPTION("Detect available bluetooth audio devices and load bluetooth audio drivers");
 PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_USAGE("async=<Asynchronous initialization?>");
+PA_MODULE_USAGE("sco_sink=<name of sink> "
+		"sco_source=<name of source>"
+		"async=<Asynchronous initialization?>");
 
 static const char* const valid_modargs[] = {
+    "sco_sink",
+    "sco_source",
     "async",
     NULL
 };
 
 struct userdata {
     pa_module *module;
+    pa_modargs *ma;
     pa_core *core;
     pa_dbus_connection *connection;
     pa_bluetooth_discovery *discovery;
@@ -71,6 +76,16 @@ static void load_module_for_device(struct userdata *u, pa_bluetooth_device *d, p
             /* Oh, awesome, a new device has shown up and been connected! */
 
             args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path);
+
+            if (pa_modargs_get_value(u->ma, "sco_sink", NULL) &&
+                pa_modargs_get_value(u->ma, "sco_source", NULL)) {
+                char *tmp;
+
+                tmp = pa_sprintf_malloc("%s sco_sink=\"%s\" sco_source=\"%s\"", args, pa_modargs_get_value(u->ma, "sco_sink", NULL), pa_modargs_get_value(u->ma, "sco_source", NULL));
+                pa_xfree(args);
+                args = tmp;
+            }
+
             pa_log_debug("Loading module-bluetooth-device %s", args);
             m = pa_module_load(u->module->core, "module-bluetooth-device", args);
             pa_xfree(args);
@@ -130,6 +145,7 @@ int pa__init(pa_module* m) {
     m->userdata = u = pa_xnew0(struct userdata, 1);
     u->module = m;
     u->core = m->core;
+    u->ma = ma;
 
     if (setup_dbus(u) < 0)
         goto fail;
@@ -162,5 +178,8 @@ void pa__done(pa_module* m) {
     if (u->connection)
         pa_dbus_connection_unref(u->connection);
 
+    if (u->ma)
+        pa_modargs_free(u->ma);
+
     pa_xfree(u);
 }

commit 452e2b9fa073299e2f047dd4dda51a3d7032a7ff
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Tue Feb 3 18:17:20 2009 +0200

    bluetooth: suspend SCO state when over PCM

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 2302b1d..c70a79d 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -106,6 +106,8 @@ struct a2dp_info {
 struct hsp_info {
     pa_sink *sco_sink;
     pa_source *sco_source;
+    pa_hook_slot *sink_state_changed_slot;
+    pa_hook_slot *source_state_changed_slot;
 };
 
 enum profile {
@@ -154,6 +156,9 @@ struct userdata {
     int write_type, read_type;
 };
 
+static int init_bt(struct userdata *u);
+static int init_profile(struct userdata *u);
+
 static int service_send(int fd, const bt_audio_msg_header_t *msg) {
     size_t length;
     ssize_t r;
@@ -1228,6 +1233,57 @@ static char *get_name(const char *type, pa_modargs *ma, const char *device_id, p
 
 #define USE_SCO_OVER_PCM(u) (u->profile == PROFILE_HSP && (u->hsp.sco_sink && u->hsp.sco_source))
 
+static void sco_over_pcm_state_update(struct userdata *u) {
+    pa_assert(u);
+
+    if (PA_SINK_IS_OPENED(pa_sink_get_state(u->hsp.sco_sink)) ||
+        PA_SOURCE_IS_OPENED(pa_source_get_state(u->hsp.sco_source))) {
+
+        if (u->service_fd > 0)
+            return;
+
+        pa_log_debug("Resuming SCO over PCM");
+        if ((init_bt(u) < 0) || (init_profile(u) < 0))
+            pa_log("Can't resume SCO over PCM");
+
+    } else {
+
+        if (u->service_fd <= 0)
+            return;
+
+        pa_log_debug("Closing SCO over PCM");
+        pa_close(u->service_fd);
+        u->service_fd = 0;
+
+    }
+}
+
+static pa_hook_result_t sink_state_changed_cb(pa_core *c, pa_sink *s, struct userdata *u) {
+    pa_assert(c);
+    pa_sink_assert_ref(s);
+    pa_assert(u);
+
+    if (s != u->hsp.sco_sink)
+        return PA_HOOK_OK;
+
+    sco_over_pcm_state_update(u);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_state_changed_cb(pa_core *c, pa_source *s, struct userdata *u) {
+    pa_assert(c);
+    pa_source_assert_ref(s);
+    pa_assert(u);
+
+    if (s != u->hsp.sco_source)
+        return PA_HOOK_OK;
+
+    sco_over_pcm_state_update(u);
+
+    return PA_HOOK_OK;
+}
+
 static int add_sink(struct userdata *u) {
 
     if (USE_SCO_OVER_PCM(u)) {
@@ -1240,6 +1296,9 @@ static int add_sink(struct userdata *u) {
         pa_proplist_update(u->sink->proplist, PA_UPDATE_MERGE, p);
         pa_proplist_free(p);
 
+	if (!u->hsp.sink_state_changed_slot)
+            u->hsp.sink_state_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_cb, u);
+
     } else {
         pa_sink_new_data data;
         pa_bool_t b;
@@ -1286,6 +1345,9 @@ static int add_source(struct userdata *u) {
         pa_proplist_update(u->source->proplist, PA_UPDATE_MERGE, p);
         pa_proplist_free(p);
 
+        if (!u->hsp.source_state_changed_slot)
+            u->hsp.source_state_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SOURCE_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_state_changed_cb, u);
+
     } else {
         pa_source_new_data data;
         pa_bool_t b;
@@ -1405,6 +1467,16 @@ static void stop_thread(struct userdata *u) {
         u->rtpoll_item = NULL;
     }
 
+    if (u->hsp.sink_state_changed_slot) {
+        pa_hook_slot_free(u->hsp.sink_state_changed_slot);
+        u->hsp.sink_state_changed_slot = NULL;
+    }
+
+    if (u->hsp.source_state_changed_slot) {
+        pa_hook_slot_free(u->hsp.source_state_changed_slot);
+        u->hsp.source_state_changed_slot = NULL;
+    }
+
     if (u->sink) {
         pa_sink_unref(u->sink);
         u->sink = NULL;

commit cac0f9ef2b91462778600821f456248efffb2517
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Tue Feb 3 18:43:54 2009 +0200

    bluetooth: export nrec

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index c70a79d..fa9d6bf 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -104,6 +104,7 @@ struct a2dp_info {
 };
 
 struct hsp_info {
+    pcm_capabilities_t pcm_capabilities;
     pa_sink *sco_sink;
     pa_source *sco_source;
     pa_hook_slot *sink_state_changed_slot;
@@ -271,23 +272,33 @@ static int parse_caps(struct userdata *u, const struct bt_get_capabilities_rsp *
         return -1;
     }
 
-    if (u->profile != PROFILE_A2DP)
-        return 0;
+    if (u->profile == PROFILE_HSP) {
+        if (bytes_left <= 0 || codec->length != sizeof(u->hsp.pcm_capabilities))
+            return -1;
 
-    while (bytes_left > 0) {
-        if (codec->type == BT_A2DP_CODEC_SBC)
-            break;
+        pa_assert(codec->type == BT_HFP_CODEC_PCM);
 
-        bytes_left -= codec->length;
-        codec = (const codec_capabilities_t*) ((const uint8_t*) codec + codec->length);
+        memcpy(&u->hsp.pcm_capabilities, codec, sizeof(u->hsp.pcm_capabilities));
     }
 
-    if (bytes_left <= 0 || codec->length != sizeof(u->a2dp.sbc_capabilities))
-        return -1;
+    if (u->profile == PROFILE_A2DP) {
+
+        while (bytes_left > 0) {
+            if (codec->type == BT_A2DP_CODEC_SBC)
+                break;
+
+            bytes_left -= codec->length;
+            codec = (const codec_capabilities_t*) ((const uint8_t*) codec + codec->length);
+        }
+
+        if (bytes_left <= 0 || codec->length != sizeof(u->a2dp.sbc_capabilities))
+            return -1;
+
+        pa_assert(codec->type == BT_A2DP_CODEC_SBC);
 
-    pa_assert(codec->type == BT_A2DP_CODEC_SBC);
+        memcpy(&u->a2dp.sbc_capabilities, codec, sizeof(u->a2dp.sbc_capabilities));
+    }
 
-    memcpy(&u->a2dp.sbc_capabilities, codec, sizeof(u->a2dp.sbc_capabilities));
     return 0;
 }
 
@@ -1334,10 +1345,9 @@ static int add_sink(struct userdata *u) {
 }
 
 static int add_source(struct userdata *u) {
+    pa_proplist *p;
 
     if (USE_SCO_OVER_PCM(u)) {
-        pa_proplist *p;
-
         u->source = u->hsp.sco_source;
         u->source->card = u->card; /* FIXME! */
         p = pa_proplist_new();
@@ -1379,6 +1389,11 @@ static int add_source(struct userdata *u) {
 /*     u->source->get_volume = source_get_volume_cb; */
 /*     u->source->set_volume = source_set_volume_cb; */
 
+    p = pa_proplist_new();
+    pa_proplist_sets(p, "bluetooth.nrec", pa_yes_no(u->hsp.pcm_capabilities.flags & BT_PCM_FLAG_NREC));
+    pa_proplist_update(u->source->proplist, PA_UPDATE_MERGE, p);
+    pa_proplist_free(p);
+
     return 0;
 }
 

commit cce4359831b5ff02f63e9748671fa744d500d27f
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Tue Feb 10 23:48:37 2009 +0200

    bluetooth: reinitialize the sample spec when switching profile
    
    When doing init_profile(), the value u->sample_spec is modified to the
    one which has been last configured. In case of HSP, it will be 8kHz.
    
    Later on, when setting the profile to A2DP, it picks up the lower rate
    available which match with current u->sample_spec.
    
    In my case, it would be 16kHz. To circunvent the issue, I decided to
    reinitialize the u->sample_spec to default value with user module
    argument requested rate.

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index fa9d6bf..666dc6a 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -1568,6 +1568,14 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
     }
 
     u->profile = *d;
+
+    /* Reinitialize the sample spec to default with module argument rate */
+    u->sample_spec = u->module->core->default_sample_spec;
+    if (pa_modargs_get_value_u32(u->modargs, "rate", &u->sample_spec.rate) < 0 ||
+        u->sample_spec.rate <= 0 || u->sample_spec.rate > PA_RATE_MAX) {
+        u->sample_spec = u->module->core->default_sample_spec;
+    }
+
     init_profile(u);
 
     if (u->sink || u->source)

commit f56da989372bedc4817f8b37b78ad8f2daf5429d
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Wed Feb 11 16:06:54 2009 +0200

    suspend-on-idle: don't crash when so->source is NULL

diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index 5e5e53e..22d49f7 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -174,6 +174,9 @@ static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_outpu
     pa_source_output_assert_ref(s);
     pa_assert(u);
 
+    if (!s->source)
+        return PA_HOOK_OK;
+
     if (pa_source_check_suspend(s->source) <= 0) {
         struct device_info *d;
         if ((d = pa_hashmap_get(u->device_infos, s->source)))

commit 86bec09fa843160dd671cf53a32c13fafa0cb932
Author: Marc-André Lureau <marcandre.lureau at gmail.com>
Date:   Wed Feb 11 19:16:42 2009 +0200

    pulsecore: add PA_CORE_HOOK_*_MOVE_FAIL
    
    In case pa_*_move_all_fail(), it is nicer to let a module override the
    default behavior to fallback on a different sink/source. (instead of
    unlinking the sink_input/source_output)

diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 2b8f819..8fa9761 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -70,6 +70,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SINK_INPUT_UNLINK_POST,
     PA_CORE_HOOK_SINK_INPUT_MOVE_START,
     PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH,
+    PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL,
     PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_SET_VOLUME,
@@ -80,6 +81,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
     PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START,
     PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH,
+    PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL,
     PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
     PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
     PA_CORE_HOOK_CLIENT_NEW,
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 0c297ec..558da8c 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -533,8 +533,10 @@ void pa_sink_move_all_fail(pa_queue *q) {
     pa_assert(q);
 
     while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
-        pa_sink_input_unlink(i);
-        pa_sink_input_unref(i);
+        if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], i) == PA_HOOK_OK) {
+            pa_sink_input_unlink(i);
+            pa_sink_input_unref(i);
+        }
     }
 
     pa_queue_free(q, NULL, NULL);
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 04c7f8b..c31c89c 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -472,8 +472,10 @@ void pa_source_move_all_fail(pa_queue *q) {
     pa_assert(q);
 
     while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
-        pa_source_output_unlink(o);
-        pa_source_output_unref(o);
+        if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], o) == PA_HOOK_OK) {
+            pa_source_output_unlink(o);
+            pa_source_output_unref(o);
+        }
     }
 
     pa_queue_free(q, NULL, NULL);

commit 823431e44732a0824658c82de29aaa92f8f39f79
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 03:18:05 2009 +0100

    allow sending meta/policy events to clients

diff --git a/src/map-file b/src/map-file
index ca4e7a7..d949659 100644
--- a/src/map-file
+++ b/src/map-file
@@ -87,6 +87,7 @@ pa_context_set_card_profile_by_index;
 pa_context_set_card_profile_by_name;
 pa_context_set_default_sink;
 pa_context_set_default_source;
+pa_context_set_event_callback;
 pa_context_set_name;
 pa_context_set_sink_input_mute;
 pa_context_set_sink_input_volume;
@@ -232,6 +233,7 @@ pa_stream_proplist_update;
 pa_stream_readable_size;
 pa_stream_ref;
 pa_stream_set_buffer_attr;
+pa_stream_set_event_callback;
 pa_stream_set_latency_update_callback;
 pa_stream_set_monitor_stream;
 pa_stream_set_moved_callback;
diff --git a/src/pulse/context.c b/src/pulse/context.c
index d41e62e..8105091 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -95,7 +95,10 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended,
     [PA_COMMAND_STARTED] = pa_command_stream_started,
     [PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event,
-    [PA_COMMAND_EXTENSION] = pa_command_extension
+    [PA_COMMAND_EXTENSION] = pa_command_extension,
+    [PA_COMMAND_PLAYBACK_STREAM_EVENT] = pa_command_stream_event,
+    [PA_COMMAND_RECORD_STREAM_EVENT] = pa_command_stream_event,
+    [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event
 };
 static void context_free(pa_context *c);
 
@@ -112,6 +115,9 @@ static void reset_callbacks(pa_context *c) {
     c->subscribe_callback = NULL;
     c->subscribe_userdata = NULL;
 
+    c->event_callback = NULL;
+    c->event_userdata = NULL;
+
     c->ext_stream_restore.callback = NULL;
     c->ext_stream_restore.userdata = NULL;
 }
@@ -917,6 +923,17 @@ void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, voi
     c->state_userdata = userdata;
 }
 
+void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void *userdata) {
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+        return;
+
+    c->event_callback = cb;
+    c->event_userdata = userdata;
+}
+
 int pa_context_is_pending(pa_context *c) {
     pa_assert(c);
     pa_assert(PA_REFCNT_VALUE(c) >= 1);
@@ -1245,6 +1262,11 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
 
     pa_context_ref(c);
 
+    if (c->version < 15) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
         pa_tagstruct_gets(t, &name) < 0) {
         pa_context_fail(c, PA_ERR_PROTOCOL);
@@ -1259,3 +1281,41 @@ void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_t
 finish:
     pa_context_unref(c);
 }
+
+
+void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_proplist *pl = NULL;
+    const char *event;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_CLIENT_EVENT);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 15) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    pl = pa_proplist_new();
+
+    if (pa_tagstruct_gets(t, &event) < 0 ||
+        pa_tagstruct_get_proplist(t, pl) < 0 ||
+        !pa_tagstruct_eof(t) || !event) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (c->event_callback)
+        c->event_callback(c, event, pl, c->event_userdata);
+
+finish:
+    pa_context_unref(c);
+
+    if (pl)
+        pa_proplist_free(pl);
+}
diff --git a/src/pulse/context.h b/src/pulse/context.h
index dfb7e4a..2ae4c01 100644
--- a/src/pulse/context.h
+++ b/src/pulse/context.h
@@ -164,6 +164,13 @@ typedef void (*pa_context_notify_cb_t)(pa_context *c, void *userdata);
 /** A generic callback for operation completion */
 typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata);
 
+/** A callback for asynchronous meta/policy event messages. The set
+ * of defined events can be extended at any time. Also, server modules
+ * may introduce additional message types so make sure that your
+ * callback function ignores messages it doesn't know. \since
+ * 0.9.15 */
+typedef void (*pa_context_event_cb_t)(pa_context *c, const char *name, pa_proplist *p, void *userdata);
+
 /** Instantiate a new connection context with an abstract mainloop API
  * and an application name. It is recommended to use pa_context_new_with_proplist()
  * instead and specify some initial properties.*/
@@ -183,6 +190,10 @@ pa_context* pa_context_ref(pa_context *c);
 /** Set a callback function that is called whenever the context status changes */
 void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata);
 
+/** Set a callback function that is called whenver a meta/policy
+ * control event is received. \since 0.9.15 */
+void pa_context_set_event_callback(pa_context *p, pa_context_event_cb_t cb, void *userdata);
+
 /** Return the error number of the last failed operation */
 int pa_context_errno(pa_context *c);
 
diff --git a/src/pulse/def.h b/src/pulse/def.h
index d4fa821..7f3a0c5 100644
--- a/src/pulse/def.h
+++ b/src/pulse/def.h
@@ -840,6 +840,16 @@ static inline int PA_SOURCE_IS_OPENED(pa_source_state_t x) {
 /** A generic free() like callback prototype */
 typedef void (*pa_free_cb_t)(void *p);
 
+/** A stream policy/meta event requesting that an application should
+ * cork a specific stream. See pa_stream_event_cb_t for more
+ * information, \since 0.9.15 */
+#define PA_STREAM_EVENT_REQUEST_CORK "request-cork"
+
+/** A stream policy/meta event requesting that an application should
+ * cork a specific stream. See pa_stream_event_cb_t for more
+ * information, \since 0.9.15 */
+#define PA_STREAM_EVENT_REQUEST_UNCORK "request-uncork"
+
 PA_C_DECL_END
 
 #endif
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 9a2d645..e533625 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -71,6 +71,8 @@ struct pa_context {
     void *state_userdata;
     pa_context_subscribe_cb_t subscribe_callback;
     void *subscribe_userdata;
+    pa_context_event_cb_t event_callback;
+    void *event_userdata;
 
     pa_mempool *mempool;
 
@@ -181,6 +183,8 @@ struct pa_stream {
     void *suspended_userdata;
     pa_stream_notify_cb_t started_callback;
     void *started_userdata;
+    pa_stream_event_cb_t event_callback;
+    void *event_userdata;
 };
 
 typedef void (*pa_operation_cb_t)(void);
@@ -207,6 +211,9 @@ void pa_command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32
 void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
 void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
 pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
 void pa_operation_done(pa_operation *o);
 
diff --git a/src/pulse/stream.c b/src/pulse/stream.c
index fe2514d..b36bf9b 100644
--- a/src/pulse/stream.c
+++ b/src/pulse/stream.c
@@ -69,6 +69,8 @@ static void reset_callbacks(pa_stream *s) {
     s->suspended_userdata = NULL;
     s->started_callback = NULL;
     s->started_userdata = NULL;
+    s->event_callback = NULL;
+    s->event_userdata = NULL;
 }
 
 pa_stream *pa_stream_new_with_proplist(
@@ -565,6 +567,52 @@ finish:
     pa_context_unref(c);
 }
 
+void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    pa_stream *s;
+    uint32_t channel;
+    pa_proplist *pl = NULL;
+    const char *event;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_EVENT || command == PA_COMMAND_RECORD_STREAM_EVENT);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (c->version < 15) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    pl = pa_proplist_new();
+
+    if (pa_tagstruct_getu32(t, &channel) < 0 ||
+        pa_tagstruct_gets(t, &event) < 0 ||
+        pa_tagstruct_get_proplist(t, pl) < 0 ||
+        !pa_tagstruct_eof(t) || !event) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_EVENT ? c->playback_streams : c->record_streams, channel)))
+        goto finish;
+
+    if (s->state != PA_STREAM_READY)
+        goto finish;
+
+    if (s->event_callback)
+        s->event_callback(s, event, pl, s->event_userdata);
+
+finish:
+    pa_context_unref(c);
+
+    if (pl)
+        pa_proplist_free(pl);
+}
+
 void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_stream *s;
     pa_context *c = userdata;
@@ -1696,6 +1744,17 @@ void pa_stream_set_started_callback(pa_stream *s, pa_stream_notify_cb_t cb, void
     s->started_userdata = userdata;
 }
 
+void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *userdata) {
+    pa_assert(s);
+    pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+    if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+        return;
+
+    s->event_callback = cb;
+    s->event_userdata = userdata;
+}
+
 void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_operation *o = userdata;
     int success = 1;
diff --git a/src/pulse/stream.h b/src/pulse/stream.h
index 1bec1eb..3965e9a 100644
--- a/src/pulse/stream.h
+++ b/src/pulse/stream.h
@@ -324,6 +324,14 @@ typedef void (*pa_stream_request_cb_t)(pa_stream *p, size_t bytes, void *userdat
 /** A generic notification callback */
 typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata);
 
+/** A callback for asynchronous meta/policy event messages. Well known
+ * event names are PA_STREAM_EVENT_REQUEST_CORK and
+ * PA_STREAM_EVENT_REQUEST_UNCORK. The set of defined events can be
+ * extended at any time. Also, server modules may introduce additional
+ * message types so make sure that your callback function ignores messages
+ * it doesn't know. \since 0.9.15 */
+typedef void (*pa_stream_event_cb_t)(pa_stream *p, const char *name, pa_proplist *pl, void *userdata);
+
 /** Create a new, unconnected stream with the specified name and
  * sample type. It is recommended to use pa_stream_new_with_proplist()
  * instead and specify some initial properties. */
@@ -500,6 +508,10 @@ void pa_stream_set_moved_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *
  * 0.9.8. \since 0.9.8 */
 void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
 
+/** Set the callback function that is called whenver a meta/policy
+ * control event is received.\since 0.9.15 */
+void pa_stream_set_event_callback(pa_stream *p, pa_stream_event_cb_t cb, void *userdata);
+
 /** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. */
 pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
 
@@ -518,7 +530,7 @@ pa_operation* pa_stream_prebuf(pa_stream *s, pa_stream_success_cb_t cb, void *us
  * temporarily. Available for playback streams only. */
 pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
 
-/** Rename the stream. */
+/** Rename the stream.*/
 pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);
 
 /** Return the current playback/recording time. This is based on the
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
index 6f3e08e..7ca7b97 100644
--- a/src/pulsecore/client.c
+++ b/src/pulsecore/client.c
@@ -73,6 +73,7 @@ pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) {
 
     c->userdata = NULL;
     c->kill = NULL;
+    c->send_event = NULL;
 
     pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0);
 
@@ -144,3 +145,31 @@ void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist
     pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c);
     pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
 }
+
+void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data) {
+    pa_proplist *pl = NULL;
+    pa_client_send_event_hook_data hook_data;
+
+    pa_assert(c);
+    pa_assert(event);
+
+    if (!c->send_event)
+        return;
+
+    if (!data)
+        data = pl = pa_proplist_new();
+
+    hook_data.client = c;
+    hook_data.data = data;
+    hook_data.event = event;
+
+    if (pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_SEND_EVENT], &hook_data) < 0)
+        goto finish;
+
+    c->send_event(c, event, data);
+
+finish:
+
+    if (pl)
+        pa_proplist_free(pl);
+}
diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h
index 8ac80dd..845a8ba 100644
--- a/src/pulsecore/client.h
+++ b/src/pulsecore/client.h
@@ -48,6 +48,8 @@ struct pa_client {
     void *userdata;
 
     void (*kill)(pa_client *c);
+
+    void (*send_event)(pa_client *c, const char *name, pa_proplist *data);
 };
 
 typedef struct pa_client_new_data {
@@ -73,4 +75,12 @@ void pa_client_set_name(pa_client *c, const char *name);
 
 void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist *p);
 
+void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data);
+
+typedef struct pa_client_send_event_hook_data {
+    pa_client *client;
+    const char *event;
+    pa_proplist *data;
+} pa_client_send_event_hook_data;
+
 #endif
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 1407b1b..aca96bb 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -73,6 +73,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
     PA_CORE_HOOK_SINK_INPUT_SET_VOLUME,
+    PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
     PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
     PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
     PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
@@ -82,10 +83,12 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH,
     PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
     PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
+    PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
     PA_CORE_HOOK_CLIENT_NEW,
     PA_CORE_HOOK_CLIENT_PUT,
     PA_CORE_HOOK_CLIENT_UNLINK,
     PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED,
+    PA_CORE_HOOK_CLIENT_SEND_EVENT,
     PA_CORE_HOOK_CARD_NEW,
     PA_CORE_HOOK_CARD_PUT,
     PA_CORE_HOOK_CARD_UNLINK,
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index b31a5da..6951e10 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -157,6 +157,10 @@ enum {
     PA_COMMAND_GET_CARD_INFO_LIST,
     PA_COMMAND_SET_CARD_PROFILE,
 
+    PA_COMMAND_CLIENT_EVENT,
+    PA_COMMAND_PLAYBACK_STREAM_EVENT,
+    PA_COMMAND_RECORD_STREAM_EVENT,
+
     PA_COMMAND_MAX
 };
 
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 65b2bb9..c303261 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -207,6 +207,7 @@ static void sink_input_moved_cb(pa_sink_input *i);
 static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
 static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
 static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl);
 
 static void native_connection_send_memblock(pa_native_connection *c);
 static void playback_stream_request_bytes(struct playback_stream*s);
@@ -216,6 +217,7 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk)
 static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
 static void source_output_moved_cb(pa_source_output *o);
 static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl);
 
 static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
 
@@ -636,6 +638,7 @@ static record_stream* record_stream_new(
     s->source_output->get_latency = source_output_get_latency_cb;
     s->source_output->moved = source_output_moved_cb;
     s->source_output->suspend = source_output_suspend_cb;
+    s->source_output->send_event = source_output_send_event_cb;
     s->source_output->userdata = s;
 
     fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize);
@@ -1048,6 +1051,7 @@ static playback_stream* playback_stream_new(
     s->sink_input->kill = sink_input_kill_cb;
     s->sink_input->moved = sink_input_moved_cb;
     s->sink_input->suspend = sink_input_suspend_cb;
+    s->sink_input->send_event = sink_input_send_event_cb;
     s->sink_input->userdata = s;
 
     start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
@@ -1494,6 +1498,27 @@ static void sink_input_kill_cb(pa_sink_input *i) {
 }
 
 /* Called from main context */
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl) {
+    playback_stream *s;
+    pa_tagstruct *t;
+
+    pa_sink_input_assert_ref(i);
+    s = PLAYBACK_STREAM(i->userdata);
+    playback_stream_assert_ref(s);
+
+    if (s->connection->version < 15)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
 static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
     playback_stream *s;
     pa_tagstruct *t;
@@ -1595,6 +1620,27 @@ static pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
 }
 
 /* Called from main context */
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl) {
+    record_stream *s;
+    pa_tagstruct *t;
+
+    pa_source_output_assert_ref(o);
+    s = RECORD_STREAM(o->userdata);
+    record_stream_assert_ref(s);
+
+    if (s->connection->version < 15)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, s->index);
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
+/* Called from main context */
 static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
     record_stream *s;
     pa_tagstruct *t;
@@ -4188,6 +4234,25 @@ static void client_kill_cb(pa_client *c) {
     pa_log_info("Connection killed.");
 }
 
+static void client_send_event_cb(pa_client *client, const char*event, pa_proplist *pl) {
+    pa_tagstruct *t;
+    pa_native_connection *c;
+
+    pa_assert(client);
+    c = PA_NATIVE_CONNECTION(client->userdata);
+    pa_native_connection_assert_ref(c);
+
+    if (c->version < 15)
+      return;
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_CLIENT_EVENT);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_puts(t, event);
+    pa_tagstruct_put_proplist(t, pl);
+    pa_pstream_send_tagstruct(c->pstream, t);
+}
+
 /*** module entry points ***/
 
 static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
@@ -4265,6 +4330,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
 
     c->client = client;
     c->client->kill = client_kill_cb;
+    c->client->send_event = client_send_event_cb;
     c->client->userdata = c;
 
     c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 9c0bf76..22419ee 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -122,6 +122,7 @@ static void reset_callbacks(pa_sink_input *i) {
     i->get_latency = NULL;
     i->state_change = NULL;
     i->may_move_to = NULL;
+    i->send_event = NULL;
 }
 
 /* Called from main context */
@@ -1446,3 +1447,31 @@ pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret) {
 
     return ret;
 }
+
+/* Called from main context */
+void pa_sink_input_send_event(pa_sink_input *i, const char *event, pa_proplist *data) {
+    pa_proplist *pl = NULL;
+    pa_sink_input_send_event_hook_data hook_data;
+
+    pa_sink_input_assert_ref(i);
+    pa_assert(event);
+
+    if (!i->send_event)
+        return;
+
+    if (!data)
+        data = pl = pa_proplist_new();
+
+    hook_data.sink_input = i;
+    hook_data.data = data;
+    hook_data.event = event;
+
+    if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT], &hook_data) < 0)
+        goto finish;
+
+    i->send_event(i, event, data);
+
+finish:
+    if (pl)
+        pa_proplist_free(pl);
+}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 20e2cfa..b4f0531 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -171,6 +171,10 @@ struct pa_sink_input {
      * be allowed */
     pa_bool_t (*may_move_to) (pa_sink_input *i, pa_sink *s); /* may be NULL */
 
+    /* If non-NULL this function is used to dispatch asynchronous
+     * control events. */
+    void (*send_event)(pa_sink_input *i, const char *event, pa_proplist* data);
+
     struct {
         pa_sink_input_state_t state;
         pa_atomic_t drained;
@@ -217,6 +221,12 @@ enum {
     PA_SINK_INPUT_MESSAGE_MAX
 };
 
+typedef struct pa_sink_input_send_event_hook_data {
+    pa_sink_input *sink_input;
+    const char *event;
+    pa_proplist *data;
+} pa_sink_input_send_event_hook_data;
+
 typedef struct pa_sink_input_new_data {
     pa_proplist *proplist;
 
@@ -298,6 +308,8 @@ void pa_sink_input_update_proplist(pa_sink_input *i, pa_update_mode_t mode, pa_p
 
 pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
 
+void pa_sink_input_send_event(pa_sink_input *i, const char *name, pa_proplist *data);
+
 int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save);
 pa_bool_t pa_sink_input_may_move(pa_sink_input *i); /* may this sink input move at all? */
 pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may this sink input move to this sink? */
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index f2f2593..382fb88 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -92,6 +92,7 @@ static void reset_callbacks(pa_source_output *o) {
     o->get_latency = NULL;
     o->state_change = NULL;
     o->may_move_to = NULL;
+    o->send_event = NULL;
 }
 
 /* Called from main context */
@@ -867,3 +868,30 @@ int pa_source_output_process_msg(pa_msgobject *mo, int code, void *userdata, int
 
     return -PA_ERR_NOTIMPLEMENTED;
 }
+
+void pa_source_output_send_event(pa_source_output *o, const char *event, pa_proplist *data) {
+    pa_proplist *pl = NULL;
+    pa_source_output_send_event_hook_data hook_data;
+
+    pa_source_output_assert_ref(o);
+    pa_assert(event);
+
+    if (!o->send_event)
+        return;
+
+    if (!data)
+        data = pl = pa_proplist_new();
+
+    hook_data.source_output = o;
+    hook_data.data = data;
+    hook_data.event = event;
+
+    if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], &hook_data) < 0)
+        goto finish;
+
+    o->send_event(o, event, data);
+
+finish:
+    if (pl)
+        pa_proplist_free(pl);
+}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index aba2510..9369568 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -143,6 +143,10 @@ struct pa_source_output {
      * will not be allowed */
     pa_bool_t (*may_move_to) (pa_source_output *o, pa_source *s); /* may be NULL */
 
+    /* If non-NULL this function is used to dispatch asynchronous
+     * control events. */
+    void (*send_event)(pa_source_output *o, const char *event, pa_proplist* data);
+
     struct {
         pa_source_output_state_t state;
 
@@ -177,6 +181,12 @@ enum {
     PA_SOURCE_OUTPUT_MESSAGE_MAX
 };
 
+typedef struct pa_source_output_send_event_hook_data {
+    pa_source_output *source_output;
+    const char *event;
+    pa_proplist *data;
+} pa_source_output_send_event_hook_data;
+
 typedef struct pa_source_output_new_data {
     pa_proplist *proplist;
     pa_sink_input *direct_on_input;
@@ -233,6 +243,8 @@ void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode
 
 pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
 
+void pa_source_output_send_event(pa_source_output *o, const char *name, pa_proplist *data);
+
 pa_bool_t pa_source_output_may_move(pa_source_output *o);
 pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest);
 int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save);
diff --git a/src/utils/pacat.c b/src/utils/pacat.c
index 2d88e7f..10015ce 100644
--- a/src/utils/pacat.c
+++ b/src/utils/pacat.c
@@ -238,6 +238,18 @@ static void stream_moved_callback(pa_stream *s, void *userdata) {
         fprintf(stderr, _("Stream moved to device %s (%u, %ssuspended).%s \n"), pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : _("not "),  CLEAR_LINE);
 }
 
+static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *pl, void *userdata) {
+    char *t;
+
+    assert(s);
+    assert(name);
+    assert(pl);
+
+    t = pa_proplist_to_string_sep(pl, ", ");
+    fprintf(stderr, "Got event '%s', properties '%s'\n", name, t);
+    pa_xfree(t);
+}
+
 /* This is called whenever the context status changes */
 static void context_state_callback(pa_context *c, void *userdata) {
     assert(c);
@@ -271,6 +283,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
             pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
             pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
             pa_stream_set_started_callback(stream, stream_started_callback, NULL);
+            pa_stream_set_event_callback(stream, stream_event_callback, NULL);
 
             if (latency > 0) {
                 memset(&buffer_attr, 0, sizeof(buffer_attr));

commit a729786637ddcccf41591c74eca357a8e8a63f2c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 03:19:40 2009 +0100

    implement a module that corks music/video streams automatically when a phone call is active

diff --git a/src/Makefile.am b/src/Makefile.am
index ca8240f..fc47803 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -896,7 +896,8 @@ modlibexec_LTLIBRARIES += \
 		module-tunnel-sink.la \
 		module-tunnel-source.la \
 		module-position-event-sounds.la \
-		module-augment-properties.la
+		module-augment-properties.la \
+		module-cork-music-on-phone.la
 
 
 # See comment at librtp.la above
@@ -1086,6 +1087,7 @@ SYMDEF_FILES = \
 		modules/gconf/module-gconf-symdef.h \
 		modules/module-position-event-sounds-symdef.h \
 		modules/module-augment-properties-symdef.h \
+		modules/module-cork-music-on-phone-symdef.h \
 		modules/module-console-kit-symdef.h
 
 EXTRA_DIST += $(SYMDEF_FILES)
@@ -1347,6 +1349,12 @@ module_augment_properties_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMI
 #module_augment_properties_la_CFLAGS = $(AM_CFLAGS) -DDESKTOPFILEDIR=\"$(datadir)/applications\"
 module_augment_properties_la_CFLAGS = $(AM_CFLAGS) -DDESKTOPFILEDIR=\"/usr/share/applications\"
 
+# Cork music streams while a phone stream is active
+module_cork_music_on_phone_la_SOURCES = modules/module-cork-music-on-phone.c
+module_cork_music_on_phone_la_LDFLAGS = $(MODULE_LDFLAGS)
+module_cork_music_on_phone_la_LIBADD = $(AM_LIBADD) libpulsecore- at PA_MAJORMINORMICRO@.la libpulsecommon- at PA_MAJORMINORMICRO@.la libpulse.la
+module_cork_music_on_phone_la_CFLAGS = $(AM_CFLAGS)
+
 # Device volume/muted restore module
 module_device_restore_la_SOURCES = modules/module-device-restore.c
 module_device_restore_la_LDFLAGS = $(MODULE_LDFLAGS)
diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
index 2606190..02ac8e5 100755
--- a/src/daemon/default.pa.in
+++ b/src/daemon/default.pa.in
@@ -110,6 +110,9 @@ load-module module-console-kit
 ### Enable positioned event sounds
 load-module module-position-event-sounds
 
+### Cork music streams when a phone stream is active
+load-module module-cork-music-on-phone
+
 # X11 modules should not be started from default.pa so that one daemon
 # can be shared by multiple sessions.
 
diff --git a/src/modules/module-cork-music-on-phone.c b/src/modules/module-cork-music-on-phone.c
new file mode 100644
index 0000000..fb90cf3
--- /dev/null
+++ b/src/modules/module-cork-music-on-phone.c
@@ -0,0 +1,224 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2009 Lennart Poettering
+
+  PulseAudio is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License as published
+  by the Free Software Foundation; either version 2 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 <pulsecore/macro.h>
+#include <pulsecore/hashmap.h>
+#include <pulsecore/hook-list.h>
+#include <pulsecore/core.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sink-input.h>
+#include <pulsecore/modargs.h>
+
+#include "module-cork-music-on-phone-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Mute or cork music while a phone stream exists");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+static const char* const valid_modargs[] = {
+    NULL
+};
+
+struct userdata {
+    pa_core *core;
+    pa_hashmap *cork_state;
+    pa_hook_slot
+        *sink_input_put_slot,
+        *sink_input_unlink_slot,
+        *sink_input_move_start_slot,
+        *sink_input_move_finish_slot;
+};
+
+static pa_bool_t shall_cork(pa_sink *s, pa_sink_input *ignore) {
+    pa_sink_input *j;
+    uint32_t idx;
+    pa_sink_assert_ref(s);
+
+    for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
+        const char *role;
+
+        if (j == ignore)
+            continue;
+
+        if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
+            continue;
+
+        if (pa_streq(role, "phone"))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+static void apply_cork(struct userdata *u, pa_sink *s, pa_sink_input *ignore, pa_bool_t cork) {
+    pa_sink_input *j;
+    uint32_t idx;
+
+    pa_assert(u);
+    pa_sink_assert_ref(s);
+
+    for (j = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); j; j = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
+        pa_bool_t corked;
+        const char *role;
+
+        if (j == ignore)
+            continue;
+
+        if (!(role = pa_proplist_gets(j->proplist, PA_PROP_MEDIA_ROLE)))
+            continue;
+
+        if (!pa_streq(role, "video") &&
+            !pa_streq(role, "music"))
+            continue;
+
+        corked = !!pa_hashmap_get(u->cork_state, j);
+
+        if (cork && !corked) {
+            pa_hashmap_put(u->cork_state, j, PA_INT_TO_PTR(1));
+            pa_sink_input_set_mute(j, TRUE, FALSE);
+            pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_CORK, NULL);
+        } else if (!cork) {
+            pa_hashmap_remove(u->cork_state, j);
+
+            if (corked) {
+                pa_sink_input_set_mute(j, FALSE, FALSE);
+                pa_sink_input_send_event(j, PA_STREAM_EVENT_REQUEST_UNCORK, NULL);
+            }
+        }
+    }
+}
+
+static pa_hook_result_t process(struct userdata *u, pa_sink_input *i, pa_bool_t create) {
+    pa_bool_t cork = FALSE;
+    const char *role;
+
+    pa_assert(u);
+    pa_sink_input_assert_ref(i);
+
+    if (!create)
+        pa_hashmap_remove(u->cork_state, i);
+
+    if (!(role = pa_proplist_gets(i->proplist, PA_PROP_MEDIA_ROLE)))
+        return PA_HOOK_OK;
+
+    if (!pa_streq(role, "phone") &&
+        !pa_streq(role, "music") &&
+        !pa_streq(role, "video"))
+        return PA_HOOK_OK;
+
+    cork = shall_cork(i->sink, create ? NULL : i);
+    apply_cork(u, i->sink, create ? NULL : i, cork);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_input_put_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    return process(u, i, TRUE);
+}
+
+static pa_hook_result_t sink_input_unlink_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    return process(u, i, FALSE);
+}
+
+static pa_hook_result_t sink_input_move_start_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    return process(u, i, FALSE);
+}
+
+static pa_hook_result_t sink_input_move_finish_cb(pa_core *core, pa_sink_input *i, struct userdata *u) {
+    pa_core_assert_ref(core);
+    pa_sink_input_assert_ref(i);
+
+    return process(u, i, TRUE);
+}
+
+int pa__init(pa_module *m) {
+    pa_modargs *ma = NULL;
+    struct userdata *u;
+
+    pa_assert(m);
+
+    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+        pa_log("Failed to parse module arguments");
+        goto fail;
+    }
+
+    m->userdata = u = pa_xnew(struct userdata, 1);
+
+    u->core = m->core;
+    u->cork_state = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->sink_input_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_put_cb, u);
+    u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_unlink_cb, u);
+    u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_start_cb, u);
+    u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_move_finish_cb, u);
+
+    pa_modargs_free(ma);
+
+    return 0;
+
+fail:
+    pa__done(m);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return  -1;
+
+
+}
+
+void pa__done(pa_module *m) {
+    struct userdata* u;
+
+    pa_assert(m);
+
+    if (!(u = m->userdata))
+        return;
+
+    if (u->sink_input_put_slot)
+        pa_hook_slot_free(u->sink_input_put_slot);
+    if (u->sink_input_unlink_slot)
+        pa_hook_slot_free(u->sink_input_unlink_slot);
+    if (u->sink_input_move_start_slot)
+        pa_hook_slot_free(u->sink_input_move_start_slot);
+    if (u->sink_input_move_finish_slot)
+        pa_hook_slot_free(u->sink_input_move_finish_slot);
+
+    if (u->cork_state)
+        pa_hashmap_free(u->cork_state, NULL, NULL);
+
+    pa_xfree(u);
+
+}

commit 12db1a559de4552da119a35781bab19c888dbeb3
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 03:19:59 2009 +0100

    make gcc 4.4 shut up

diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index 5c7a6e5..26da257 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -662,7 +662,7 @@ fail:
 /* Called from main context */
 static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     struct userdata *u = userdata;
-    pa_usec_t sink_usec, source_usec, transport_usec;
+    pa_usec_t sink_usec, source_usec, transport_usec = 0;
     pa_bool_t playing;
     int64_t write_index, read_index;
     struct timeval local, remote, now;

commit 60c50bbf10fecec50f8fa6f9e9ff420c9456de69
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 03:20:38 2009 +0100

    declare 'animation' stream role for Flash and suchlike

diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h
index 9e78aec..57a23d9 100644
--- a/src/pulse/proplist.h
+++ b/src/pulse/proplist.h
@@ -39,7 +39,7 @@ PA_C_DECL_BEGIN
  *    media.filename
  *    media.icon                    Binary blob containing PNG icon data
  *    media.icon_name               Name from XDG icon naming spec
- *    media.role                    video, music, game, event, phone, production, filter, abstract, stream
+ *    media.role                    video, music, game, event, phone, animation, production, filter, abstract, stream
  *    event.id                      Name from XDG sound naming spec
  *    event.description             "Button blabla clicked" for a11y
  *    event.mouse.x

commit 5d154255cbab0726ab30ec1f9e0b745face778fa
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 03:20:52 2009 +0100

    minor reformatting

diff --git a/src/modules/module-augment-properties.c b/src/modules/module-augment-properties.c
index a2475db..90bfbe7 100644
--- a/src/modules/module-augment-properties.c
+++ b/src/modules/module-augment-properties.c
@@ -230,7 +230,7 @@ static pa_hook_result_t client_proplist_changed_cb(pa_core *core, pa_client *cli
     return process(u, client->proplist);
 }
 
-int pa__init(pa_module*m) {
+int pa__init(pa_module *m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
 
@@ -260,7 +260,7 @@ fail:
     return  -1;
 }
 
-void pa__done(pa_module*m) {
+void pa__done(pa_module *m) {
     struct userdata* u;
 
     pa_assert(m);

commit f0cc23d6e5dd9ce718e66978d7de2dbc94ded537
Merge: 5d15425... 86bec09...
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 03:35:57 2009 +0100

    Merge commit 'elmarco/bt-wip'

diff --cc src/pulsecore/core.h
index aca96bb,8fa9761..53e2d2c
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@@ -81,9 -81,9 +82,10 @@@ typedef enum pa_core_hook 
      PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST,
      PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START,
      PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH,
+     PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL,
      PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
      PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
 +    PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
      PA_CORE_HOOK_CLIENT_NEW,
      PA_CORE_HOOK_CLIENT_PUT,
      PA_CORE_HOOK_CLIENT_UNLINK,

commit 1837a96e645149bf09ed09aa2f1d276ad608e749
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 03:39:56 2009 +0100

    call _kill functions instead of _unlink since the latter should only be called be the stream implementor

diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index eadef80..4f39d67 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -519,7 +519,7 @@ void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save) {
 
     while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
         if (pa_sink_input_finish_move(i, s, save) < 0)
-            pa_sink_input_unlink(i);
+            pa_sink_input_kill(i);
 
         pa_sink_input_unref(i);
     }
@@ -534,7 +534,7 @@ void pa_sink_move_all_fail(pa_queue *q) {
 
     while ((i = PA_SINK_INPUT(pa_queue_pop(q)))) {
         if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL], i) == PA_HOOK_OK) {
-            pa_sink_input_unlink(i);
+            pa_sink_input_kill(i);
             pa_sink_input_unref(i);
         }
     }
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index c31c89c..0009d85 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -458,7 +458,7 @@ void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save) {
 
     while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
         if (pa_source_output_finish_move(o, s, save) < 0)
-            pa_source_output_unlink(o);
+            pa_source_output_kill(o);
 
         pa_source_output_unref(o);
     }
@@ -473,7 +473,7 @@ void pa_source_move_all_fail(pa_queue *q) {
 
     while ((o = PA_SOURCE_OUTPUT(pa_queue_pop(q)))) {
         if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FAIL], o) == PA_HOOK_OK) {
-            pa_source_output_unlink(o);
+            pa_source_output_kill(o);
             pa_source_output_unref(o);
         }
     }

commit 752f8151833506975fe96e0d4ca2fe9cf3555126
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 03:42:28 2009 +0100

    addendum to f56da9893: don't crash when s->sink is NULL

diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index 22d49f7..a5a3571 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -160,6 +160,9 @@ static pa_hook_result_t sink_input_unlink_hook_cb(pa_core *c, pa_sink_input *s,
     pa_sink_input_assert_ref(s);
     pa_assert(u);
 
+    if (!s->sink)
+        return PA_HOOK_OK;
+
     if (pa_sink_check_suspend(s->sink) <= 0) {
         struct device_info *d;
         if ((d = pa_hashmap_get(u->device_infos, s->sink)))

commit 6ada8d1fea43aa7ee8c45663d52e58e8611c9e1f
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 03:47:27 2009 +0100

    instead of reparsing the rate module argument when changing profile, simply restore the originally requested sample_spec, this also makes sure the channel count is properly reset

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 9419595..bf288f4 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -136,7 +136,7 @@ struct userdata {
 
     pa_memchunk write_memchunk;
 
-    pa_sample_spec sample_spec;
+    pa_sample_spec sample_spec, requested_sample_spec;
 
     int service_fd;
     int stream_fd;
@@ -1571,13 +1571,7 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
     }
 
     u->profile = *d;
-
-    /* Reinitialize the sample spec to default with module argument rate */
-    u->sample_spec = u->module->core->default_sample_spec;
-    if (pa_modargs_get_value_u32(u->modargs, "rate", &u->sample_spec.rate) < 0 ||
-        u->sample_spec.rate <= 0 || u->sample_spec.rate > PA_RATE_MAX) {
-        u->sample_spec = u->module->core->default_sample_spec;
-    }
+    u->sample_spec = u->requested_sample_spec;
 
     init_profile(u);
 
@@ -1781,6 +1775,7 @@ int pa__init(pa_module* m) {
         goto fail;
     }
     u->sample_spec.channels = (uint8_t) channels;
+    u->requested_sample_spec = u->sample_spec;
 
     if (setup_dbus(u) < 0)
         goto fail;

commit a7b992faceb1dbb0ac0c39f81987d78c31eb9e5a
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 03:56:01 2009 +0100

    some minor fixups

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index bf288f4..0e0542e 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -273,15 +273,15 @@ static int parse_caps(struct userdata *u, const struct bt_get_capabilities_rsp *
     }
 
     if (u->profile == PROFILE_HSP) {
+
         if (bytes_left <= 0 || codec->length != sizeof(u->hsp.pcm_capabilities))
             return -1;
 
         pa_assert(codec->type == BT_HFP_CODEC_PCM);
 
         memcpy(&u->hsp.pcm_capabilities, codec, sizeof(u->hsp.pcm_capabilities));
-    }
 
-    if (u->profile == PROFILE_A2DP) {
+    } else if (u->profile == PROFILE_A2DP) {
 
         while (bytes_left > 0) {
             if (codec->type == BT_A2DP_CODEC_SBC)
@@ -1249,6 +1249,7 @@ static char *get_name(const char *type, pa_modargs *ma, const char *device_id, p
 
 static void sco_over_pcm_state_update(struct userdata *u) {
     pa_assert(u);
+    pa_assert(USE_SCO_OVER_PCM(u));
 
     if (PA_SINK_IS_OPENED(pa_sink_get_state(u->hsp.sco_sink)) ||
         PA_SOURCE_IS_OPENED(pa_source_get_state(u->hsp.sco_source))) {
@@ -1267,8 +1268,7 @@ static void sco_over_pcm_state_update(struct userdata *u) {
 
         pa_log_debug("Closing SCO over PCM");
         pa_close(u->service_fd);
-        u->service_fd = 0;
-
+        u->service_fd = -1;
     }
 }
 
@@ -1304,7 +1304,6 @@ static int add_sink(struct userdata *u) {
         pa_proplist *p;
 
         u->sink = u->hsp.sco_sink;
-        u->sink->card = u->card; /* FIXME! */
         p = pa_proplist_new();
         pa_proplist_sets(p, "bluetooth.protocol", "sco");
         pa_proplist_update(u->sink->proplist, PA_UPDATE_MERGE, p);
@@ -1352,7 +1351,6 @@ static int add_source(struct userdata *u) {
 
     if (USE_SCO_OVER_PCM(u)) {
         u->source = u->hsp.sco_source;
-        u->source->card = u->card; /* FIXME! */
         p = pa_proplist_new();
         pa_proplist_sets(p, "bluetooth.protocol", "sco");
         pa_proplist_update(u->source->proplist, PA_UPDATE_MERGE, p);

commit a371306710b7267a2c9fa6ebece90e17ee7bff2e
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 03:57:59 2009 +0100

    tabs are evil

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 0e0542e..7011220 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -73,8 +73,8 @@ PA_MODULE_USAGE(
         "rate=<sample rate> "
         "channels=<number of channels> "
         "path=<device object path> "
-	"sco_sink=<SCO over PCM sink name> "
-	"sco_source=<SCO over PCM source name>");
+        "sco_sink=<SCO over PCM sink name> "
+        "sco_source=<SCO over PCM source name>");
 
 static const char* const valid_modargs[] = {
     "name",
@@ -1309,7 +1309,7 @@ static int add_sink(struct userdata *u) {
         pa_proplist_update(u->sink->proplist, PA_UPDATE_MERGE, p);
         pa_proplist_free(p);
 
-	if (!u->hsp.sink_state_changed_slot)
+        if (!u->hsp.sink_state_changed_slot)
             u->hsp.sink_state_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_cb, u);
 
     } else {
@@ -1550,13 +1550,13 @@ static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
 
     if (u->sink) {
         inputs = pa_sink_move_all_start(u->sink);
-	if (!USE_SCO_OVER_PCM(u))
+        if (!USE_SCO_OVER_PCM(u))
             pa_sink_unlink(u->sink);
     }
 
     if (u->source) {
         outputs = pa_source_move_all_start(u->source);
-	if (!USE_SCO_OVER_PCM(u))
+        if (!USE_SCO_OVER_PCM(u))
             pa_source_unlink(u->source);
     }
 
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 3f8a751..01340e1 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -43,8 +43,8 @@ PA_MODULE_AUTHOR("Joao Paulo Rechi Vita");
 PA_MODULE_DESCRIPTION("Detect available bluetooth audio devices and load bluetooth audio drivers");
 PA_MODULE_VERSION(PACKAGE_VERSION);
 PA_MODULE_USAGE("sco_sink=<name of sink> "
-		"sco_source=<name of source>"
-		"async=<Asynchronous initialization?>");
+                "sco_source=<name of source>"
+                "async=<Asynchronous initialization?>");
 
 static const char* const valid_modargs[] = {
     "sco_sink",

commit 84666dbc88d6ace253bd4c5839dcde127ca02100
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 04:00:40 2009 +0100

    properly free modargs object when init fails; don't abbreviate modargs in struct

diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 01340e1..e974074 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -55,7 +55,7 @@ static const char* const valid_modargs[] = {
 
 struct userdata {
     pa_module *module;
-    pa_modargs *ma;
+    pa_modargs *modargs;
     pa_core *core;
     pa_dbus_connection *connection;
     pa_bluetooth_discovery *discovery;
@@ -77,11 +77,13 @@ static void load_module_for_device(struct userdata *u, pa_bluetooth_device *d, p
 
             args = pa_sprintf_malloc("address=\"%s\" path=\"%s\"", d->address, d->path);
 
-            if (pa_modargs_get_value(u->ma, "sco_sink", NULL) &&
-                pa_modargs_get_value(u->ma, "sco_source", NULL)) {
+            if (pa_modargs_get_value(u->modargs, "sco_sink", NULL) &&
+                pa_modargs_get_value(u->modargs, "sco_source", NULL)) {
                 char *tmp;
 
-                tmp = pa_sprintf_malloc("%s sco_sink=\"%s\" sco_source=\"%s\"", args, pa_modargs_get_value(u->ma, "sco_sink", NULL), pa_modargs_get_value(u->ma, "sco_source", NULL));
+                tmp = pa_sprintf_malloc("%s sco_sink=\"%s\" sco_source=\"%s\"", args,
+                                        pa_modargs_get_value(u->modargs, "sco_sink", NULL),
+                                        pa_modargs_get_value(u->modargs, "sco_source", NULL));
                 pa_xfree(args);
                 args = tmp;
             }
@@ -127,7 +129,7 @@ static int setup_dbus(struct userdata *u) {
 
 int pa__init(pa_module* m) {
     struct userdata *u;
-    pa_modargs *ma;
+    pa_modargs *ma = NULL;
     pa_bool_t async = FALSE;
 
     pa_assert(m);
@@ -145,7 +147,8 @@ int pa__init(pa_module* m) {
     m->userdata = u = pa_xnew0(struct userdata, 1);
     u->module = m;
     u->core = m->core;
-    u->ma = ma;
+    u->modargs = ma;
+    ma = NULL;
 
     if (setup_dbus(u) < 0)
         goto fail;
@@ -161,6 +164,9 @@ int pa__init(pa_module* m) {
 fail:
     pa__done(m);
 
+    if (ma)
+        pa_modargs_free(ma);
+
     return -1;
 }
 
@@ -178,8 +184,8 @@ void pa__done(pa_module* m) {
     if (u->connection)
         pa_dbus_connection_unref(u->connection);
 
-    if (u->ma)
-        pa_modargs_free(u->ma);
+    if (u->modargs)
+        pa_modargs_free(u->modargs);
 
     pa_xfree(u);
 }

commit c3b0d841d19898cd268a65173b6c0ca5f88a64c7
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 04:15:15 2009 +0100

    make module-hal-detect pick up all cards even when they have no device 0

diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
index 8b1f0b2..e603738 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -180,7 +180,7 @@ finish:
 
 static int hal_device_load_alsa(struct userdata *u, const char *udi, struct device *d) {
     enum alsa_type type;
-    int device, card;
+    int card;
     DBusError error;
     pa_module *m;
     char *args, *originating_udi = NULL, *card_name = NULL;
@@ -200,11 +200,6 @@ static int hal_device_load_alsa(struct userdata *u, const char *udi, struct devi
     if (hal_alsa_device_is_modem(u->context, udi))
         goto fail;
 
-    /* We only care for the main device */
-    device = libhal_device_get_property_int(u->context, udi, "alsa.device", &error);
-    if (dbus_error_is_set(&error) || device != 0)
-        goto fail;
-
     /* We store only one entry per card, hence we look for the originating device */
     originating_udi = libhal_device_get_property_string(u->context, udi, "alsa.originating_device", &error);
     if (dbus_error_is_set(&error) || !originating_udi)

commit e82b2fd098fda285e03aaa452e6bb6dd80bbe7b1
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 16:51:02 2009 +0100

    handle errors from BT service properly

diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index 7011220..b04834d 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -193,6 +193,9 @@ static int service_recv(int fd, bt_audio_msg_header_t *msg, size_t expected_leng
 
     length = expected_length ? expected_length : BT_SUGGESTED_BUFFER_SIZE;
 
+    if (length < sizeof(bt_audio_error_t))
+        length = sizeof(bt_audio_error_t);
+
     pa_log_debug("Trying to receive message from audio service...");
 
     r = recv(fd, msg, length, 0);
@@ -212,6 +215,7 @@ static int service_recv(int fd, bt_audio_msg_header_t *msg, size_t expected_leng
         pa_log_debug("Received %s <- %s",
                      pa_strnull(bt_audio_strtype(msg->type)),
                      pa_strnull(bt_audio_strname(msg->name)));
+
         return 0;
     }
 
@@ -223,7 +227,7 @@ static int service_recv(int fd, bt_audio_msg_header_t *msg, size_t expected_leng
     return -1;
 }
 
-static int service_expect(int fd, bt_audio_msg_header_t *rsp, uint8_t expected_name, size_t expected_length) {
+static ssize_t service_expect(int fd, bt_audio_msg_header_t *rsp, uint8_t expected_name, size_t expected_length) {
     int r;
 
     pa_assert(fd >= 0);
@@ -232,16 +236,14 @@ static int service_expect(int fd, bt_audio_msg_header_t *rsp, uint8_t expected_n
     if ((r = service_recv(fd, rsp, expected_length)) < 0)
         return r;
 
-    if (rsp->name != expected_name) {
-        pa_log_error("Bogus message %s received while %s was expected",
-                     pa_strnull(bt_audio_strname(rsp->name)),
-                     pa_strnull(bt_audio_strname(expected_name)));
-        return -1;
-    }
+    if (rsp->type != BT_RESPONSE || rsp->name != expected_name) {
 
-    if (rsp->type == BT_ERROR) {
-        bt_audio_error_t *error = (bt_audio_error_t *) rsp;
-        pa_log_error("%s failed: %s", pa_strnull(bt_audio_strname(rsp->name)), pa_cstrerror(error->posix_errno));
+        if (rsp->type == BT_ERROR && rsp->length == sizeof(bt_audio_error_t))
+            pa_log_error("Received error condition: %s", pa_cstrerror(((bt_audio_error_t*) rsp)->posix_errno));
+        else
+            pa_log_error("Bogus message %s received while %s was expected",
+                         pa_strnull(bt_audio_strname(rsp->name)),
+                         pa_strnull(bt_audio_strname(expected_name)));
         return -1;
     }
 
@@ -306,6 +308,7 @@ static int get_caps(struct userdata *u) {
     union {
         struct bt_get_capabilities_req getcaps_req;
         struct bt_get_capabilities_rsp getcaps_rsp;
+        bt_audio_error_t error;
         uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
     } msg;
 
@@ -575,6 +578,7 @@ static int set_conf(struct userdata *u) {
     union {
         struct bt_set_configuration_req setconf_req;
         struct bt_set_configuration_rsp setconf_rsp;
+        bt_audio_error_t error;
         uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
     } msg;
 
@@ -644,6 +648,7 @@ static int setup_stream_fd(struct userdata *u) {
         struct bt_start_stream_req start_req;
         struct bt_start_stream_rsp start_rsp;
         struct bt_new_stream_ind streamfd_ind;
+        bt_audio_error_t error;
         uint8_t buf[BT_SUGGESTED_BUFFER_SIZE];
     } msg;
 

commit cc526a0b5fb87a21cf3fe1bd36218ae166ebb1b4
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Feb 12 16:51:52 2009 +0100

    prepare test2

diff --git a/configure.ac b/configure.ac
index 4852049..9189dd0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -26,7 +26,7 @@ m4_define(pa_major, [0])
 m4_define(pa_minor, [9])
 m4_define(pa_micro, [15])
 
-AC_INIT([pulseaudio],[pa_major.pa_minor.pa_micro-test1],[mzchyfrnhqvb (at) 0pointer (dot) net])
+AC_INIT([pulseaudio],[pa_major.pa_minor.pa_micro-test2],[mzchyfrnhqvb (at) 0pointer (dot) net])
 AC_CONFIG_SRCDIR([src/daemon/main.c])
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_HEADERS([config.h])

-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list