[pulseaudio-commits] [SCM] PulseAudio Sound Server branch, master, updated. v0.9.11-48-g0cc674d

Lennart Poettering gitmailer-noreply at 0pointer.de
Mon Aug 4 10:02:41 PDT 2008


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  5f69b5d7fd84fbbd2db84178985db3ada11b683f (commit)

- Log -----------------------------------------------------------------
0cc674d... wrap protocol extension of module-stream-restore
88c3db6... add protocol extension to module-stream-restore
6cc3a61... store channel map in database and remap volumes if necessary
eec623a... add hooks for connection creation/deletion, for that export pa_native_connection
c01f0bc... split out save trigger function
32cf9db... store channel map in database and remap volumes if ncessary
5880516... add new API function pa_cvolume_remap()
cd5afb8... don't hit an assert if  when process_rewind() is called with nbytes=0
-----------------------------------------------------------------------

Summary of changes:
 src/Makefile.am                     |    6 +-
 src/modules/module-device-restore.c |   54 +++--
 src/modules/module-stream-restore.c |  365 ++++++++++++++++++++++++++++--
 src/modules/module-x11-publish.c    |    2 +-
 src/pulse/context.c                 |   37 +++-
 src/pulse/ext-stream-restore.c      |  331 +++++++++++++++++++++++++++
 src/pulse/ext-stream-restore.h      |   86 +++++++
 src/pulse/internal.h                |    9 +
 src/pulse/subscribe.c               |    1 -
 src/pulse/volume.c                  |   85 +++++++
 src/pulse/volume.h                  |    4 +
 src/pulsecore/play-memblockq.c      |    1 -
 src/pulsecore/protocol-native.c     |  429 ++++++++++++++++++----------------
 src/pulsecore/protocol-native.h     |   23 ++-
 src/pulsecore/sound-file-stream.c   |    3 -
 15 files changed, 1185 insertions(+), 251 deletions(-)
 create mode 100644 src/pulse/ext-stream-restore.c
 create mode 100644 src/pulse/ext-stream-restore.h

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

commit cd5afb80f791f10d82519aed6f9e4ffabea1b5c9
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 4 15:33:41 2008 +0200

    don't hit an assert if  when process_rewind() is called with nbytes=0

diff --git a/src/pulsecore/play-memblockq.c b/src/pulsecore/play-memblockq.c
index 8b3e79b..86edfe9 100644
--- a/src/pulsecore/play-memblockq.c
+++ b/src/pulsecore/play-memblockq.c
@@ -146,7 +146,6 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     memblockq_stream *u;
 
     pa_sink_input_assert_ref(i);
-    pa_assert(nbytes > 0);
     u = MEMBLOCKQ_STREAM(i->userdata);
     memblockq_stream_assert_ref(u);
 
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
index 8eedf83..8b2a29b 100644
--- a/src/pulsecore/sound-file-stream.c
+++ b/src/pulsecore/sound-file-stream.c
@@ -206,12 +206,9 @@ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
     file_stream *u;
 
     pa_sink_input_assert_ref(i);
-    pa_assert(nbytes > 0);
     u = FILE_STREAM(i->userdata);
     file_stream_assert_ref(u);
 
-    pa_log("backwards %lu", (unsigned long) nbytes);
-
     if (!u->memblockq)
         return;
 

commit 5880516076843edb60c96ea622f966ca2fb4e9e6
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 4 18:40:53 2008 +0200

    add new API function pa_cvolume_remap()

diff --git a/src/pulse/volume.c b/src/pulse/volume.c
index 70d6f86..625eb19 100644
--- a/src/pulse/volume.c
+++ b/src/pulse/volume.c
@@ -176,3 +176,88 @@ int pa_cvolume_valid(const pa_cvolume *v) {
 
     return 1;
 }
+
+static pa_bool_t on_left(pa_channel_position_t p) {
+
+    return
+        p == PA_CHANNEL_POSITION_FRONT_LEFT ||
+        p == PA_CHANNEL_POSITION_REAR_LEFT ||
+        p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
+        p == PA_CHANNEL_POSITION_SIDE_LEFT ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
+}
+
+static pa_bool_t on_right(pa_channel_position_t p) {
+
+    return
+        p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
+        p == PA_CHANNEL_POSITION_REAR_RIGHT ||
+        p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
+        p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
+}
+
+static pa_bool_t on_center(pa_channel_position_t p) {
+
+    return
+        p == PA_CHANNEL_POSITION_FRONT_CENTER ||
+        p == PA_CHANNEL_POSITION_REAR_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_FRONT_CENTER ||
+        p == PA_CHANNEL_POSITION_TOP_REAR_CENTER;
+}
+
+static pa_bool_t on_lfe(pa_channel_position_t p) {
+    return
+        p == PA_CHANNEL_POSITION_LFE;
+}
+
+pa_cvolume *pa_cvolume_remap(pa_cvolume *v, pa_channel_map *from, pa_channel_map *to) {
+    int a, b;
+    pa_cvolume result;
+
+    pa_assert(v);
+    pa_assert(from);
+    pa_assert(to);
+    pa_assert(v->channels == from->channels);
+
+    if (pa_channel_map_equal(from, to))
+        return v;
+
+    result.channels = to->channels;
+
+    for (b = 0; b < to->channels; b++) {
+        pa_volume_t k = 0;
+        int n = 0;
+
+        for (a = 0; a < from->channels; a++)
+            if (from->map[a] == to->map[b]) {
+                k += v->values[a];
+                n ++;
+            }
+
+        if (n <= 0) {
+            for (a = 0; a < from->channels; a++)
+                if ((on_left(from->map[a]) && on_left(to->map[b])) ||
+                    (on_right(from->map[a]) && on_right(to->map[b])) ||
+                    (on_center(from->map[a]) && on_center(to->map[b])) ||
+                    (on_lfe(from->map[a]) && on_lfe(to->map[b]))) {
+
+                    k += v->values[a];
+                    n ++;
+                }
+        }
+
+        if (n <= 0)
+            k = pa_cvolume_avg(v);
+        else
+            k /= n;
+
+        result.values[b] = k;
+    }
+
+    *v = result;
+    return v;
+}
diff --git a/src/pulse/volume.h b/src/pulse/volume.h
index 3befb1d..4fdbf65 100644
--- a/src/pulse/volume.h
+++ b/src/pulse/volume.h
@@ -28,6 +28,7 @@
 #include <pulse/cdecl.h>
 #include <pulse/gccmacro.h>
 #include <pulse/sample.h>
+#include <pulse/channelmap.h>
 
 /** \page volume Volume Control
  *
@@ -170,6 +171,9 @@ double pa_sw_volume_to_linear(pa_volume_t v) PA_GCC_CONST;
 #define PA_DECIBEL_MININFTY ((double) -200.0)
 #endif
 
+/** Remap a volume from one channel mapping to a different channel mapping. \since 0.9.12 */
+pa_cvolume *pa_cvolume_remap(pa_cvolume *v, pa_channel_map *from, pa_channel_map *to);
+
 PA_C_DECL_END
 
 #endif

commit 32cf9db43475b33fd43f28897e4fa9ca149aba2c
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 4 18:55:50 2008 +0200

    store channel map in database and remap volumes if ncessary

diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index c0d2ddb..d93b9e6 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -1,7 +1,7 @@
 /***
   This file is part of PulseAudio.
 
-  Copyright 2006 Lennart Poettering
+  Copyright 2006-2008 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
@@ -64,6 +64,7 @@ static const char* const valid_modargs[] = {
 
 struct userdata {
     pa_core *core;
+    pa_module *module;
     pa_subscription *subscription;
     pa_hook_slot
         *sink_fixate_hook_slot,
@@ -76,6 +77,7 @@ struct userdata {
 };
 
 struct entry {
+    pa_channel_map channel_map;
     pa_cvolume volume;
     pa_bool_t muted:1;
 };
@@ -123,6 +125,16 @@ static struct entry* read_entry(struct userdata *u, char *name) {
         goto fail;
     }
 
+    if (!(pa_channel_map_valid(&e->channel_map))) {
+        pa_log_warn("Invalid channel map stored in database for device %s", name);
+        goto fail;
+    }
+
+    if (e->volume.channels != e->channel_map.channels) {
+        pa_log_warn("Volume and channel map don't match in database entry for device %s", name);
+        goto fail;
+    }
+
     return e;
 
 fail:
@@ -153,6 +165,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
             return;
 
         name = pa_sprintf_malloc("sink:%s", sink->name);
+        entry.channel_map = sink->channel_map;
         entry.volume = *pa_sink_get_volume(sink);
         entry.muted = pa_sink_get_mute(sink);
 
@@ -165,13 +178,14 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
             return;
 
         name = pa_sprintf_malloc("source:%s", source->name);
+        entry.channel_map = source->channel_map;
         entry.volume = *pa_source_get_volume(source);
         entry.muted = pa_source_get_mute(source);
     }
 
     if ((old = read_entry(u, name))) {
 
-        if (pa_cvolume_equal(&old->volume, &entry.volume) &&
+        if (pa_cvolume_equal(pa_cvolume_remap(&old->volume, &old->channel_map, &entry.channel_map), &entry.volume) &&
             !old->muted == !entry.muted) {
 
             pa_xfree(old);
@@ -212,11 +226,9 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *
 
     if ((e = read_entry(u, name))) {
 
-        if (u->restore_volume &&
-            e->volume.channels == new_data->sample_spec.channels) {
-
+        if (u->restore_volume) {
             pa_log_info("Restoring volume for sink %s.", new_data->name);
-            pa_sink_new_data_set_volume(new_data, &e->volume);
+            pa_sink_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
         }
 
         if (u->restore_muted) {
@@ -242,11 +254,9 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da
 
     if ((e = read_entry(u, name))) {
 
-        if (u->restore_volume &&
-            e->volume.channels == new_data->sample_spec.channels) {
-
+        if (u->restore_volume) {
             pa_log_info("Restoring volume for source %s.", new_data->name);
-            pa_source_new_data_set_volume(new_data, &e->volume);
+            pa_source_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
         }
 
         if (u->restore_muted) {
@@ -290,9 +300,11 @@ int pa__init(pa_module*m) {
 
     m->userdata = u = pa_xnew(struct userdata, 1);
     u->core = m->core;
+    u->module = m;
     u->save_time_event = NULL;
     u->restore_volume = restore_volume;
     u->restore_muted = restore_muted;
+    u->gdbm_file = NULL;
 
     u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE, subscribe_callback, u);
 

commit c01f0bc01ff1c4bdd3cbb66ae79e45c73add5011
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 4 18:56:12 2008 +0200

    split out save trigger function

diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index d93b9e6..fcd4021 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -143,6 +143,17 @@ fail:
     return NULL;
 }
 
+static void trigger_save(struct userdata *u) {
+    struct timeval tv;
+
+    if (u->save_time_event)
+        return;
+
+    pa_gettimeofday(&tv);
+    tv.tv_sec += SAVE_INTERVAL;
+    u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
+}
+
 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     struct userdata *u = userdata;
     struct entry entry, *old;
@@ -158,6 +169,8 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         t != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE))
         return;
 
+    memset(&entry, 0, sizeof(entry));
+
     if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
         pa_sink *sink;
 
@@ -206,14 +219,9 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
     gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
 
-    if (!u->save_time_event) {
-        struct timeval tv;
-        pa_gettimeofday(&tv);
-        tv.tv_sec += SAVE_INTERVAL;
-        u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
-    }
-
     pa_xfree(name);
+
+    trigger_save(u);
 }
 
 static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {

commit eec623a23b6c30508d8bb4ecbdd1fff7c715a3f8
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 4 18:58:29 2008 +0200

    add hooks for connection creation/deletion, for that export pa_native_connection

diff --git a/src/modules/module-x11-publish.c b/src/modules/module-x11-publish.c
index c29535e..1dbc939 100644
--- a/src/modules/module-x11-publish.c
+++ b/src/modules/module-x11-publish.c
@@ -152,7 +152,7 @@ int pa__init(pa_module*m) {
     u->x11_client = NULL;
     u->x11_wrapper = NULL;
 
-    u->hook_slot = pa_hook_connect(pa_native_protocol_servers_changed(u->protocol), PA_HOOK_NORMAL, servers_changed_cb, u);
+    u->hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_SERVERS_CHANGED], PA_HOOK_NORMAL, servers_changed_cb, u);
 
     if (!(u->auth_cookie = pa_auth_cookie_get(m->core, pa_modargs_get_value(ma, "cookie", PA_NATIVE_COOKIE_FILE), PA_NATIVE_COOKIE_LENGTH)))
         goto fail;
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index ef56a6f..0eac3c2 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -73,13 +73,12 @@
 #define DEFAULT_PROCESS_MSEC 20   /* 20ms */
 #define DEFAULT_FRAGSIZE_MSEC DEFAULT_TLENGTH_MSEC
 
-typedef struct connection connection;
 struct pa_native_protocol;
 
 typedef struct record_stream {
     pa_msgobject parent;
 
-    connection *connection;
+    pa_native_connection *connection;
     uint32_t index;
 
     pa_source_output *source_output;
@@ -103,7 +102,7 @@ static PA_DEFINE_CHECK_TYPE(output_stream, pa_msgobject);
 typedef struct playback_stream {
     output_stream parent;
 
-    connection *connection;
+    pa_native_connection *connection;
     uint32_t index;
 
     pa_sink_input *sink_input;
@@ -129,7 +128,7 @@ static PA_DEFINE_CHECK_TYPE(playback_stream, output_stream);
 typedef struct upload_stream {
     output_stream parent;
 
-    connection *connection;
+    pa_native_connection *connection;
     uint32_t index;
 
     pa_memchunk memchunk;
@@ -144,7 +143,7 @@ PA_DECLARE_CLASS(upload_stream);
 #define UPLOAD_STREAM(o) (upload_stream_cast(o))
 static PA_DEFINE_CHECK_TYPE(upload_stream, output_stream);
 
-struct connection {
+struct pa_native_connection {
     pa_msgobject parent;
     pa_native_protocol *protocol;
     pa_native_options *options;
@@ -160,9 +159,9 @@ struct connection {
     pa_time_event *auth_timeout_event;
 };
 
-PA_DECLARE_CLASS(connection);
-#define CONNECTION(o) (connection_cast(o))
-static PA_DEFINE_CHECK_TYPE(connection, pa_msgobject);
+PA_DECLARE_CLASS(pa_native_connection);
+#define PA_NATIVE_CONNECTION(o) (pa_native_connection_cast(o))
+static PA_DEFINE_CHECK_TYPE(pa_native_connection, pa_msgobject);
 
 struct pa_native_protocol {
     PA_REFCNT_DECLARE;
@@ -171,10 +170,9 @@ struct pa_native_protocol {
     pa_idxset *connections;
 
     pa_strlist *servers;
-    pa_hook servers_changed;
+    pa_hook hooks[PA_NATIVE_HOOK_MAX];
 
     pa_hashmap *extensions;
-
 };
 
 enum {
@@ -212,8 +210,8 @@ 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 send_memblock(connection *c);
-static void request_bytes(struct playback_stream*s);
+static void native_connection_send_memblock(pa_native_connection *c);
+static void playback_stream_request_bytes(struct playback_stream*s);
 
 static void source_output_kill_cb(pa_source_output *o);
 static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
@@ -389,7 +387,7 @@ static void upload_stream_free(pa_object *o) {
 }
 
 static upload_stream* upload_stream_new(
-        connection *c,
+        pa_native_connection *c,
         const pa_sample_spec *ss,
         const pa_channel_map *map,
         const char *name,
@@ -464,7 +462,7 @@ static int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, i
             }
 
             if (!pa_pstream_is_pending(s->connection->pstream))
-                send_memblock(s->connection);
+                native_connection_send_memblock(s->connection);
 
             break;
     }
@@ -531,7 +529,7 @@ static void fix_record_buffer_attr_post(record_stream *s, uint32_t *maxlength, u
 }
 
 static record_stream* record_stream_new(
-        connection *c,
+        pa_native_connection *c,
         pa_source *source,
         pa_sample_spec *ss,
         pa_channel_map *map,
@@ -615,6 +613,17 @@ static record_stream* record_stream_new(
     return s;
 }
 
+static void record_stream_send_killed(record_stream *r) {
+    pa_tagstruct *t;
+    record_stream_assert_ref(r);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, r->index);
+    pa_pstream_send_tagstruct(r->connection->pstream, t);
+}
+
 static void playback_stream_unlink(playback_stream *s) {
     pa_assert(s);
 
@@ -846,7 +855,7 @@ static void fix_playback_buffer_attr_post(playback_stream *s, uint32_t *maxlengt
 }
 
 static playback_stream* playback_stream_new(
-        connection *c,
+        pa_native_connection *c,
         pa_sink *sink,
         pa_sample_spec *ss,
         pa_channel_map *map,
@@ -974,9 +983,42 @@ static playback_stream* playback_stream_new(
     return s;
 }
 
-static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
-    connection *c = CONNECTION(o);
-    connection_assert_ref(c);
+
+/* Called from thread context */
+static void playback_stream_request_bytes(playback_stream *s) {
+    size_t m, previous_missing;
+
+    playback_stream_assert_ref(s);
+
+    m = pa_memblockq_pop_missing(s->memblockq);
+
+    if (m <= 0)
+        return;
+
+/*     pa_log("request_bytes(%lu)", (unsigned long) m); */
+
+    previous_missing = pa_atomic_add(&s->missing, m);
+
+    if (pa_memblockq_prebuf_active(s->memblockq) ||
+        (previous_missing < s->minreq && previous_missing+m >= s->minreq))
+        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
+}
+
+
+static void playback_stream_send_killed(playback_stream *p) {
+    pa_tagstruct *t;
+    playback_stream_assert_ref(p);
+
+    t = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED);
+    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+    pa_tagstruct_putu32(t, p->index);
+    pa_pstream_send_tagstruct(p->connection->pstream, t);
+}
+
+static int native_connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(o);
+    pa_native_connection_assert_ref(c);
 
     if (!c->protocol)
         return -1;
@@ -995,7 +1037,7 @@ static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int6
     return 0;
 }
 
-static void connection_unlink(connection *c) {
+static void native_connection_unlink(pa_native_connection *c) {
     record_stream *r;
     output_stream *o;
 
@@ -1004,6 +1046,8 @@ static void connection_unlink(connection *c) {
     if (!c->protocol)
         return;
 
+    pa_hook_fire(&c->protocol->hooks[PA_NATIVE_HOOK_CONNECTION_UNLINK], c);
+
     if (c->options)
         pa_native_options_unref(c->options);
 
@@ -1029,15 +1073,15 @@ static void connection_unlink(connection *c) {
 
     pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
     c->protocol = NULL;
-    connection_unref(c);
+    pa_native_connection_unref(c);
 }
 
-static void connection_free(pa_object *o) {
-    connection *c = CONNECTION(o);
+static void native_connection_free(pa_object *o) {
+    pa_native_connection *c = PA_NATIVE_CONNECTION(o);
 
     pa_assert(c);
 
-    connection_unlink(c);
+    native_connection_unlink(c);
 
     pa_idxset_free(c->record_streams, NULL, NULL);
     pa_idxset_free(c->output_streams, NULL, NULL);
@@ -1049,27 +1093,7 @@ static void connection_free(pa_object *o) {
     pa_xfree(c);
 }
 
-/* Called from thread context */
-static void request_bytes(playback_stream *s) {
-    size_t m, previous_missing;
-
-    playback_stream_assert_ref(s);
-
-    m = pa_memblockq_pop_missing(s->memblockq);
-
-    if (m <= 0)
-        return;
-
-/*     pa_log("request_bytes(%lu)", (unsigned long) m); */
-
-    previous_missing = pa_atomic_add(&s->missing, m);
-
-    if (pa_memblockq_prebuf_active(s->memblockq) ||
-        (previous_missing < s->minreq && previous_missing+m >= s->minreq))
-        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
-}
-
-static void send_memblock(connection *c) {
+static void native_connection_send_memblock(pa_native_connection *c) {
     uint32_t start;
     record_stream *r;
 
@@ -1101,28 +1125,6 @@ static void send_memblock(connection *c) {
     }
 }
 
-static void send_playback_stream_killed(playback_stream *p) {
-    pa_tagstruct *t;
-    playback_stream_assert_ref(p);
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED);
-    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
-    pa_tagstruct_putu32(t, p->index);
-    pa_pstream_send_tagstruct(p->connection->pstream, t);
-}
-
-static void send_record_stream_killed(record_stream *r) {
-    pa_tagstruct *t;
-    record_stream_assert_ref(r);
-
-    t = pa_tagstruct_new(NULL, 0);
-    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED);
-    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
-    pa_tagstruct_putu32(t, r->index);
-    pa_pstream_send_tagstruct(r->connection->pstream, t);
-}
-
 /*** sink input callbacks ***/
 
 static void handle_seek(playback_stream *s, int64_t indexw) {
@@ -1159,7 +1161,7 @@ static void handle_seek(playback_stream *s, int64_t indexw) {
         }
     }
 
-    request_bytes(s);
+    playback_stream_request_bytes(s);
 }
 
 /* Called from thread context */
@@ -1321,7 +1323,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
 
         s->is_underrun = TRUE;
 
-        request_bytes(s);
+        playback_stream_request_bytes(s);
     }
 
     /* This call will not fail with prebuf=0, hence we check for
@@ -1335,7 +1337,7 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
         pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL);
 
     pa_memblockq_drop(s->memblockq, chunk->length);
-    request_bytes(s);
+    playback_stream_request_bytes(s);
 
     return 0;
 }
@@ -1386,7 +1388,7 @@ static void sink_input_kill_cb(pa_sink_input *i) {
     s = PLAYBACK_STREAM(i->userdata);
     playback_stream_assert_ref(s);
 
-    send_playback_stream_killed(s);
+    playback_stream_send_killed(s);
     playback_stream_unlink(s);
 }
 
@@ -1475,7 +1477,7 @@ static void source_output_kill_cb(pa_source_output *o) {
     s = RECORD_STREAM(o->userdata);
     record_stream_assert_ref(s);
 
-    send_record_stream_killed(s);
+    record_stream_send_killed(s);
     record_stream_unlink(s);
 }
 
@@ -1550,9 +1552,9 @@ static void source_output_moved_cb(pa_source_output *o) {
 
 /*** pdispatch callbacks ***/
 
-static void protocol_error(connection *c) {
+static void protocol_error(pa_native_connection *c) {
     pa_log("protocol error, kicking client");
-    connection_unlink(c);
+    native_connection_unlink(c);
 }
 
 #define CHECK_VALIDITY(pstream, expression, tag, error) do { \
@@ -1572,7 +1574,7 @@ static pa_tagstruct *reply_new(uint32_t tag) {
 }
 
 static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     playback_stream *s;
     uint32_t maxlength, tlength, prebuf, minreq, sink_index, syncid, missing;
     const char *name = NULL, *sink_name;
@@ -1596,7 +1598,7 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
     pa_sink_input_flags_t flags = 0;
     pa_proplist *p;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
@@ -1735,10 +1737,10 @@ static void command_create_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
 }
 
 static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t channel;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &channel) < 0 ||
@@ -1793,7 +1795,7 @@ static void command_delete_stream(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
 }
 
 static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     record_stream *s;
     uint32_t maxlength, fragment_size;
     uint32_t source_index;
@@ -1818,7 +1820,7 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
     uint32_t direct_on_input_idx = PA_INVALID_INDEX;
     pa_sink_input *direct_on_input = NULL;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
@@ -1953,9 +1955,9 @@ static void command_create_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
 }
 
 static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (!pa_tagstruct_eof(t)) {
@@ -1970,12 +1972,12 @@ static void command_exit(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
 }
 
 static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     const void*cookie;
     pa_tagstruct *reply;
     pa_bool_t shm_on_remote, do_shm;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &c->version) < 0 ||
@@ -1992,7 +1994,7 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
     }
 
     /* Starting with protocol version 13 the MSB of the version tag
-       reflects if shm is available for this connection or
+       reflects if shm is available for this pa_native_connection or
        not. */
     if (c->version >= 13) {
         shm_on_remote = !!(c->version & 0x80000000U);
@@ -2101,12 +2103,12 @@ static void command_auth(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
 }
 
 static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     const char *name = NULL;
     pa_proplist *p;
     pa_tagstruct *reply;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     p = pa_proplist_new();
@@ -2141,11 +2143,11 @@ static void command_set_client_name(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE
 }
 
 static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     const char *name;
     uint32_t idx = PA_IDXSET_INVALID;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_gets(t, &name) < 0 ||
@@ -2179,11 +2181,11 @@ static void command_lookup(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uin
 }
 
 static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     playback_stream *s;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2201,11 +2203,11 @@ static void command_drain_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC
 }
 
 static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     pa_tagstruct *reply;
     const pa_mempool_stat *stat;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (!pa_tagstruct_eof(t)) {
@@ -2227,14 +2229,14 @@ static void command_stat(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t
 }
 
 static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     pa_tagstruct *reply;
     playback_stream *s;
     struct timeval tv, now;
     uint32_t idx;
     pa_usec_t latency;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2273,13 +2275,13 @@ static void command_get_playback_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
 }
 
 static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     pa_tagstruct *reply;
     record_stream *s;
     struct timeval tv, now;
     uint32_t idx;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2305,7 +2307,7 @@ static void command_get_record_latency(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
 }
 
 static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     upload_stream *s;
     uint32_t length;
     const char *name = NULL;
@@ -2314,7 +2316,7 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
     pa_tagstruct *reply;
     pa_proplist *p;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_gets(t, &name) < 0 ||
@@ -2360,12 +2362,12 @@ static void command_create_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
 }
 
 static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t channel;
     upload_stream *s;
     uint32_t idx;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &channel) < 0 ||
@@ -2389,7 +2391,7 @@ static void command_finish_upload_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
 }
 
 static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t sink_index;
     pa_volume_t volume;
     pa_sink *sink;
@@ -2398,7 +2400,7 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
     pa_proplist *p;
     pa_tagstruct *reply;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
@@ -2447,10 +2449,10 @@ static void command_play_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
 }
 
 static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     const char *name;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_gets(t, &name) < 0 ||
@@ -2470,7 +2472,7 @@ static void command_remove_sample(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
 
-static void fixup_sample_spec(connection *c, pa_sample_spec *fixed, const pa_sample_spec *original) {
+static void fixup_sample_spec(pa_native_connection *c, pa_sample_spec *fixed, const pa_sample_spec *original) {
     pa_assert(c);
     pa_assert(fixed);
     pa_assert(original);
@@ -2488,7 +2490,7 @@ static void fixup_sample_spec(connection *c, pa_sample_spec *fixed, const pa_sam
     }
 }
 
-static void sink_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink *sink) {
+static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink *sink) {
     pa_sample_spec fixed_ss;
 
     pa_assert(t);
@@ -2519,7 +2521,7 @@ static void sink_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink *sink) {
     }
 }
 
-static void source_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source *source) {
+static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
     pa_sample_spec fixed_ss;
 
     pa_assert(t);
@@ -2551,7 +2553,7 @@ static void source_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source *sou
 }
 
 
-static void client_fill_tagstruct(connection *c, pa_tagstruct *t, pa_client *client) {
+static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
     pa_assert(t);
     pa_assert(client);
 
@@ -2576,7 +2578,7 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) {
     pa_tagstruct_put_boolean(t, module->auto_unload);
 }
 
-static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_input *s) {
+static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
     pa_sample_spec fixed_ss;
     pa_usec_t sink_latency;
 
@@ -2603,7 +2605,7 @@ static void sink_input_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sink_in
         pa_tagstruct_put_proplist(t, s->proplist);
 }
 
-static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_source_output *s) {
+static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source_output *s) {
     pa_sample_spec fixed_ss;
     pa_usec_t source_latency;
 
@@ -2628,7 +2630,7 @@ static void source_output_fill_tagstruct(connection *c, pa_tagstruct *t, pa_sour
         pa_tagstruct_put_proplist(t, s->proplist);
 }
 
-static void scache_fill_tagstruct(connection *c, pa_tagstruct *t, pa_scache_entry *e) {
+static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_scache_entry *e) {
     pa_sample_spec fixed_ss;
 
     pa_assert(t);
@@ -2654,7 +2656,7 @@ static void scache_fill_tagstruct(connection *c, pa_tagstruct *t, pa_scache_entr
 }
 
 static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     pa_sink *sink = NULL;
     pa_source *source = NULL;
@@ -2666,7 +2668,7 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
     const char *name;
     pa_tagstruct *reply;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2733,13 +2735,13 @@ static void command_get_info(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, u
 }
 
 static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     pa_idxset *i;
     uint32_t idx;
     void *p;
     pa_tagstruct *reply;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (!pa_tagstruct_eof(t)) {
@@ -2793,13 +2795,13 @@ static void command_get_info_list(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t comma
 }
 
 static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     pa_tagstruct *reply;
     char txt[256];
     const char *n;
     pa_sample_spec fixed_ss;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (!pa_tagstruct_eof(t)) {
@@ -2830,9 +2832,9 @@ static void command_get_server_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSE
 
 static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint32_t idx, void *userdata) {
     pa_tagstruct *t;
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
 
     t = pa_tagstruct_new(NULL, 0);
     pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT);
@@ -2843,10 +2845,10 @@ static void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint3
 }
 
 static void command_subscribe(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     pa_subscription_mask_t m;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &m) < 0 ||
@@ -2877,7 +2879,7 @@ static void command_set_volume(
         pa_tagstruct *t,
         void *userdata) {
 
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     pa_cvolume volume;
     pa_sink *sink = NULL;
@@ -2885,7 +2887,7 @@ static void command_set_volume(
     pa_sink_input *si = NULL;
     const char *name = NULL;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -2944,7 +2946,7 @@ static void command_set_mute(
         pa_tagstruct *t,
         void *userdata) {
 
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     pa_bool_t mute;
     pa_sink *sink = NULL;
@@ -2952,7 +2954,7 @@ static void command_set_mute(
     pa_sink_input *si = NULL;
     const char *name = NULL;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3007,12 +3009,12 @@ static void command_set_mute(
 }
 
 static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     pa_bool_t b;
     playback_stream *s;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3033,11 +3035,11 @@ static void command_cork_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_
 }
 
 static void command_trigger_or_flush_or_prebuf_playback_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     playback_stream *s;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3073,12 +3075,12 @@ static void command_trigger_or_flush_or_prebuf_playback_stream(PA_GCC_UNUSED pa_
 }
 
 static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     record_stream *s;
     pa_bool_t b;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3098,11 +3100,11 @@ static void command_cork_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UN
 }
 
 static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     record_stream *s;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3120,12 +3122,12 @@ static void command_flush_record_stream(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_U
 }
 
 static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     uint32_t maxlength, tlength, prebuf, minreq, fragsize;
     pa_tagstruct *reply;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0) {
@@ -3207,11 +3209,11 @@ static void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, u
 }
 
 static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     uint32_t rate;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3247,12 +3249,12 @@ static void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command
 }
 
 static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     uint32_t mode;
     pa_proplist *p;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
@@ -3312,13 +3314,13 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t
 }
 
 static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     unsigned changed = 0;
     pa_proplist *p;
     pa_strlist *l = NULL;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
@@ -3409,10 +3411,10 @@ static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t
 }
 
 static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     const char *s;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_gets(t, &s) < 0 ||
@@ -3429,11 +3431,11 @@ static void command_set_default_sink_or_source(PA_GCC_UNUSED pa_pdispatch *pd, u
 }
 
 static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     const char *name;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3469,10 +3471,10 @@ static void command_set_stream_name(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t com
 }
 
 static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3489,7 +3491,7 @@ static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint3
         client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
         CHECK_VALIDITY(c->pstream, client, tag, PA_ERR_NOENTITY);
 
-        connection_ref(c);
+        pa_native_connection_ref(c);
         pa_client_kill(client);
 
     } else if (command == PA_COMMAND_KILL_SINK_INPUT) {
@@ -3498,7 +3500,7 @@ static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint3
         s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
         CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
 
-        connection_ref(c);
+        pa_native_connection_ref(c);
         pa_sink_input_kill(s);
     } else {
         pa_source_output *s;
@@ -3508,21 +3510,21 @@ static void command_kill(PA_GCC_UNUSED pa_pdispatch *pd, uint32_t command, uint3
         s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
         CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
 
-        connection_ref(c);
+        pa_native_connection_ref(c);
         pa_source_output_kill(s);
     }
 
     pa_pstream_send_simple_ack(c->pstream, tag);
-    connection_unref(c);
+    pa_native_connection_unref(c);
 }
 
 static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     pa_module *m;
     const char *name, *argument;
     pa_tagstruct *reply;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_gets(t, &name) < 0 ||
@@ -3547,11 +3549,11 @@ static void command_load_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED ui
 }
 
 static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
     pa_module *m;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3569,13 +3571,13 @@ static void command_unload_module(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED
 }
 
 static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     const char *name, *module, *argument;
     uint32_t type;
     uint32_t idx;
     pa_tagstruct *reply;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_gets(t, &name) < 0 ||
@@ -3604,12 +3606,12 @@ static void command_add_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED u
 }
 
 static void command_remove_autoload(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     const char *name = NULL;
     uint32_t type, idx = PA_IDXSET_INVALID;
     int r;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if ((pa_tagstruct_getu32(t, &idx) < 0 &&
@@ -3645,13 +3647,13 @@ static void autoload_fill_tagstruct(pa_tagstruct *t, const pa_autoload_entry *e)
 }
 
 static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     const pa_autoload_entry *a = NULL;
     uint32_t type, idx;
     const char *name;
     pa_tagstruct *reply;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if ((pa_tagstruct_getu32(t, &idx) < 0 &&
@@ -3679,10 +3681,10 @@ static void command_get_autoload_info(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNU
 }
 
 static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GCC_UNUSED uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     pa_tagstruct *reply;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (!pa_tagstruct_eof(t)) {
@@ -3706,11 +3708,11 @@ static void command_get_autoload_info_list(PA_GCC_UNUSED pa_pdispatch *pd, PA_GC
 }
 
 static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX;
     const char *name = NULL;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3767,12 +3769,12 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag
 }
 
 static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx = PA_INVALID_INDEX;
     const char *name = NULL;
     pa_bool_t b;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3841,13 +3843,13 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
 }
 
 static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx = PA_INVALID_INDEX;
     const char *name = NULL;
     pa_module *m;
     pa_native_protocol_ext_cb_t cb;
 
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(t);
 
     if (pa_tagstruct_getu32(t, &idx) < 0 ||
@@ -3873,32 +3875,33 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag,
     cb = (pa_native_protocol_ext_cb_t) pa_hashmap_get(c->protocol->extensions, m);
     CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
 
-    cb(c->protocol, m, c->pstream, tag, t);
+    if (cb(c->protocol, m, c, tag, t) < 0)
+        protocol_error(c);
 }
 
 
 /*** pstream callbacks ***/
 
 static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, const pa_creds *creds, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
 
     pa_assert(p);
     pa_assert(packet);
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
 
     if (pa_pdispatch_run(c->pdispatch, packet, creds, c) < 0) {
         pa_log("invalid packet.");
-        connection_unlink(c);
+        native_connection_unlink(c);
     }
 }
 
 static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     output_stream *stream;
 
     pa_assert(p);
     pa_assert(chunk);
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
 
     if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
         pa_log("client sent block for invalid stream.");
@@ -3954,22 +3957,22 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
 }
 
 static void pstream_die_callback(pa_pstream *p, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
 
     pa_assert(p);
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
 
-    connection_unlink(c);
-    pa_log_info("connection died.");
+    native_connection_unlink(c);
+    pa_log_info("Connection died.");
 }
 
 static void pstream_drain_callback(pa_pstream *p, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
 
     pa_assert(p);
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
 
-    send_memblock(c);
+    native_connection_send_memblock(c);
 }
 
 static void pstream_revoke_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
@@ -3995,25 +3998,28 @@ static void pstream_release_callback(pa_pstream *p, uint32_t block_id, void *use
 static void client_kill_cb(pa_client *c) {
     pa_assert(c);
 
-    connection_unlink(CONNECTION(c->userdata));
+    native_connection_unlink(PA_NATIVE_CONNECTION(c->userdata));
+    pa_log_info("Connection killed.");
 }
 
 /*** module entry points ***/
 
 static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
-    connection *c = CONNECTION(userdata);
+    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
 
     pa_assert(m);
     pa_assert(tv);
-    connection_assert_ref(c);
+    pa_native_connection_assert_ref(c);
     pa_assert(c->auth_timeout_event == e);
 
-    if (!c->authorized)
-        connection_unlink(c);
+    if (!c->authorized) {
+        native_connection_unlink(c);
+        pa_log_info("Connection terminated due to authentication timeout.");
+    }
 }
 
 void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_native_options *o) {
-    connection *c;
+    pa_native_connection *c;
     char cname[256], pname[128];
 
     pa_assert(p);
@@ -4026,9 +4032,9 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
         return;
     }
 
-    c = pa_msgobject_new(connection);
-    c->parent.parent.free = connection_free;
-    c->parent.process_msg = connection_process_msg;
+    c = pa_msgobject_new(pa_native_connection);
+    c->parent.parent.free = native_connection_free;
+    c->parent.process_msg = native_connection_process_msg;
     c->protocol = p;
     c->options = pa_native_options_ref(o);
     c->authorized = FALSE;
@@ -4087,10 +4093,12 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
     if (pa_iochannel_creds_supported(io))
         pa_iochannel_creds_enable(io);
 #endif
+
+    pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_CONNECTION_PUT], c);
 }
 
 void pa_native_protocol_disconnect(pa_native_protocol *p, pa_module *m) {
-    connection *c;
+    pa_native_connection *c;
     void *state = NULL;
 
     pa_assert(p);
@@ -4098,11 +4106,12 @@ void pa_native_protocol_disconnect(pa_native_protocol *p, pa_module *m) {
 
     while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
         if (c->options->module == m)
-            connection_unlink(c);
+            native_connection_unlink(c);
 }
 
 static pa_native_protocol* native_protocol_new(pa_core *c) {
     pa_native_protocol *p;
+    pa_native_hook_t h;
 
     pa_assert(c);
 
@@ -4112,7 +4121,11 @@ static pa_native_protocol* native_protocol_new(pa_core *c) {
     p->connections = pa_idxset_new(NULL, NULL);
 
     p->servers = NULL;
-    pa_hook_init(&p->servers_changed, p);
+
+    p->extensions = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    for (h = 0; h < PA_NATIVE_HOOK_MAX; h++)
+        pa_hook_init(&p->hooks[h], p);
 
     pa_assert_se(pa_shared_set(c, "native-protocol", p) >= 0);
 
@@ -4138,7 +4151,9 @@ pa_native_protocol* pa_native_protocol_ref(pa_native_protocol *p) {
 }
 
 void pa_native_protocol_unref(pa_native_protocol *p) {
-    connection *c;
+    pa_native_connection *c;
+    pa_native_hook_t h;
+
     pa_assert(p);
     pa_assert(PA_REFCNT_VALUE(p) >= 1);
 
@@ -4146,12 +4161,16 @@ void pa_native_protocol_unref(pa_native_protocol *p) {
         return;
 
     while ((c = pa_idxset_first(p->connections, NULL)))
-        connection_unlink(c);
+        native_connection_unlink(c);
 
     pa_idxset_free(p->connections, NULL, NULL);
 
     pa_strlist_free(p->servers);
-    pa_hook_done(&p->servers_changed);
+
+    for (h = 0; h < PA_NATIVE_HOOK_MAX; h++)
+        pa_hook_done(&p->hooks[h]);
+
+    pa_hashmap_free(p->extensions, NULL, NULL);
 
     pa_assert_se(pa_shared_remove(p->core, "native-protocol") >= 0);
 
@@ -4165,7 +4184,7 @@ void pa_native_protocol_add_server_string(pa_native_protocol *p, const char *nam
 
     p->servers = pa_strlist_prepend(p->servers, name);
 
-    pa_hook_fire(&p->servers_changed, p->servers);
+    pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_SERVERS_CHANGED], p->servers);
 }
 
 void pa_native_protocol_remove_server_string(pa_native_protocol *p, const char *name) {
@@ -4175,14 +4194,14 @@ void pa_native_protocol_remove_server_string(pa_native_protocol *p, const char *
 
     p->servers = pa_strlist_remove(p->servers, name);
 
-    pa_hook_fire(&p->servers_changed, p->servers);
+    pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_SERVERS_CHANGED], p->servers);
 }
 
-pa_hook *pa_native_protocol_servers_changed(pa_native_protocol *p) {
+pa_hook *pa_native_protocol_hooks(pa_native_protocol *p) {
     pa_assert(p);
     pa_assert(PA_REFCNT_VALUE(p) >= 1);
 
-    return &p->servers_changed;
+    return p->hooks;
 }
 
 pa_strlist *pa_native_protocol_servers(pa_native_protocol *p) {
@@ -4314,3 +4333,9 @@ int pa_native_options_parse(pa_native_options *o, pa_core *c, pa_modargs *ma) {
 
     return 0;
 }
+
+pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c) {
+    pa_native_connection_assert_ref(c);
+
+    return c->pstream;
+}
diff --git a/src/pulsecore/protocol-native.h b/src/pulsecore/protocol-native.h
index b3db305..06731c0 100644
--- a/src/pulsecore/protocol-native.h
+++ b/src/pulsecore/protocol-native.h
@@ -36,6 +36,8 @@
 
 typedef struct pa_native_protocol pa_native_protocol;
 
+typedef struct pa_native_connection pa_native_connection;
+
 typedef struct pa_native_options {
     PA_REFCNT_DECLARE;
 
@@ -48,22 +50,37 @@ typedef struct pa_native_options {
 
 } pa_native_options;
 
+typedef enum pa_native_hook {
+    PA_NATIVE_HOOK_SERVERS_CHANGED,
+    PA_NATIVE_HOOK_CONNECTION_PUT,
+    PA_NATIVE_HOOK_CONNECTION_UNLINK,
+    PA_NATIVE_HOOK_MAX
+} pa_native_hook_t;
+
 pa_native_protocol* pa_native_protocol_get(pa_core *core);
 pa_native_protocol* pa_native_protocol_ref(pa_native_protocol *p);
 void pa_native_protocol_unref(pa_native_protocol *p);
 void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_native_options *a);
 void pa_native_protocol_disconnect(pa_native_protocol *p, pa_module *m);
 
+pa_hook *pa_native_protocol_hooks(pa_native_protocol *p);
+
 void pa_native_protocol_add_server_string(pa_native_protocol *p, const char *name);
 void pa_native_protocol_remove_server_string(pa_native_protocol *p, const char *name);
-
-pa_hook *pa_native_protocol_servers_changed(pa_native_protocol *p);
 pa_strlist *pa_native_protocol_servers(pa_native_protocol *p);
 
-typedef void (*pa_native_protocol_ext_cb_t)(pa_native_protocol *p, pa_module *m, pa_pstream *ps, uint32_t tag, pa_tagstruct *t);
+typedef int (*pa_native_protocol_ext_cb_t)(
+        pa_native_protocol *p,
+        pa_module *m,
+        pa_native_connection *c,
+        uint32_t tag,
+        pa_tagstruct *t);
+
 int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_native_protocol_ext_cb_t cb);
 void pa_native_protocol_remove_ext(pa_native_protocol *p, pa_module *m);
 
+pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c);
+
 pa_native_options* pa_native_options_new(void);
 pa_native_options* pa_native_options_ref(pa_native_options *o);
 void pa_native_options_unref(pa_native_options *o);

commit 6cc3a615fa4f2c0f53e77d431d77422433cda1f0
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 4 19:00:43 2008 +0200

    store channel map in database and remap volumes if necessary

diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index 22e2ff6..cac8a9b 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -65,6 +65,7 @@ static const char* const valid_modargs[] = {
 
 struct userdata {
     pa_core *core;
+    pa_module *module;
     pa_subscription *subscription;
     pa_hook_slot
         *sink_input_new_hook_slot,
@@ -79,8 +80,9 @@ struct userdata {
 };
 
 struct entry {
-    pa_cvolume volume;
     char device[PA_NAME_MAX];
+    pa_channel_map channel_map;
+    pa_cvolume volume;
     pa_bool_t muted:1;
 };
 
@@ -146,7 +148,17 @@ static struct entry* read_entry(struct userdata *u, char *name) {
     }
 
     if (!(pa_cvolume_valid(&e->volume))) {
-        pa_log_warn("Invalid volume stored in database for device %s", name);
+        pa_log_warn("Invalid volume stored in database for stream %s", name);
+        goto fail;
+    }
+
+    if (!(pa_channel_map_valid(&e->channel_map))) {
+        pa_log_warn("Invalid channel map stored in database for stream %s", name);
+        goto fail;
+    }
+
+    if (e->volume.channels != e->channel_map.channels) {
+        pa_log_warn("Volume and channel map don't match in database entry for stream %s", name);
         goto fail;
     }
 
@@ -182,6 +194,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         if (!(name = get_name(sink_input->proplist, "sink-input")))
             return;
 
+        entry.channel_map = sink_input->channel_map;
         entry.volume = *pa_sink_input_get_volume(sink_input);
         entry.muted = pa_sink_input_get_mute(sink_input);
         pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
@@ -197,15 +210,16 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         if (!(name = get_name(source_output->proplist, "source-output")))
             return;
 
-        memset(&entry.volume, 0, sizeof(entry.volume));
-        entry.muted = FALSE;
-
+        /* The following fields are filled in to make the entry valid
+         * according to read_entry(). They are otherwise useless */
+        entry.channel_map = source_output->channel_map;
+        pa_cvolume_reset(&entry.volume, entry.channel_map.channels);
         pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
     }
 
     if ((old = read_entry(u, name))) {
 
-        if (pa_cvolume_equal(&old->volume, &entry.volume) &&
+        if (pa_cvolume_equal(pa_cvolume_remap(&old->volume, &old->channel_map, &entry.channel_map), &entry.volume) &&
             !old->muted == !entry.muted &&
             strcmp(old->device, entry.device) == 0) {
 
@@ -276,11 +290,9 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
 
     if ((e = read_entry(u, name))) {
 
-        if (u->restore_volume &&
-            e->volume.channels == new_data->sample_spec.channels) {
-
+        if (u->restore_volume) {
             pa_log_info("Restoring volume for sink input %s.", name);
-            pa_sink_input_new_data_set_volume(new_data, &e->volume);
+            pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
         }
 
         if (u->restore_muted) {
@@ -353,6 +365,7 @@ int pa__init(pa_module*m) {
 
     m->userdata = u = pa_xnew(struct userdata, 1);
     u->core = m->core;
+    u->module = m;
     u->save_time_event = NULL;
     u->restore_device = restore_device;
     u->restore_volume = restore_volume;

commit 88c3db6636988e39c99220ba4969625b709e97ed
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 4 19:01:13 2008 +0200

    add protocol extension to module-stream-restore

diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index cac8a9b..ee6fab4 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -46,6 +46,9 @@
 #include <pulsecore/sink-input.h>
 #include <pulsecore/source-output.h>
 #include <pulsecore/namereg.h>
+#include <pulsecore/protocol-native.h>
+#include <pulsecore/pstream.h>
+#include <pulsecore/pstream-util.h>
 
 #include "module-stream-restore-symdef.h"
 
@@ -70,13 +73,17 @@ struct userdata {
     pa_hook_slot
         *sink_input_new_hook_slot,
         *sink_input_fixate_hook_slot,
-        *source_output_new_hook_slot;
+        *source_output_new_hook_slot,
+        *connection_unlink_hook_slot;
     pa_time_event *save_time_event;
     GDBM_FILE gdbm_file;
 
     pa_bool_t restore_device:1;
     pa_bool_t restore_volume:1;
     pa_bool_t restore_muted:1;
+
+    pa_native_protocol *protocol;
+    pa_idxset *subscribed;
 };
 
 struct entry {
@@ -86,6 +93,16 @@ struct entry {
     pa_bool_t muted:1;
 };
 
+
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_READ,
+    SUBCOMMAND_WRITE,
+    SUBCOMMAND_DELETE,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT
+};
+
 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
     struct userdata *u = userdata;
 
@@ -170,6 +187,32 @@ fail:
     return NULL;
 }
 
+static void trigger_save(struct userdata *u) {
+    struct timeval tv;
+    pa_native_connection *c;
+    uint32_t idx;
+
+    for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
+        pa_tagstruct *t;
+
+        t = pa_tagstruct_new(NULL, 0);
+        pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
+        pa_tagstruct_putu32(t, 0);
+        pa_tagstruct_putu32(t, u->module->index);
+        pa_tagstruct_puts(t, u->module->name);
+        pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
+
+        pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
+    }
+
+    if (u->save_time_event)
+        return;
+
+    pa_gettimeofday(&tv);
+    tv.tv_sec += SAVE_INTERVAL;
+    u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
+}
+
 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
     struct userdata *u = userdata;
     struct entry entry, *old;
@@ -185,6 +228,8 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
         t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
         return;
 
+    memset(&entry, 0, sizeof(entry));
+
     if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
         pa_sink_input *sink_input;
 
@@ -241,14 +286,9 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
 
     gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
 
-    if (!u->save_time_event) {
-        struct timeval tv;
-        pa_gettimeofday(&tv);
-        tv.tv_sec += SAVE_INTERVAL;
-        u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
-    }
-
     pa_xfree(name);
+
+    trigger_save(u);
 }
 
 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
@@ -264,7 +304,6 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
         pa_sink *s;
 
         if (u->restore_device &&
-            e->device[0] &&
             (s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK, TRUE))) {
 
             pa_log_info("Restoring device for stream %s.", name);
@@ -321,7 +360,6 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
         pa_source *s;
 
         if (u->restore_device &&
-            e->device[0] &&
             (s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE, TRUE))) {
 
             pa_log_info("Restoring device for stream %s.", name);
@@ -336,6 +374,262 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
     return PA_HOOK_OK;
 }
 
+#define EXT_VERSION 1
+
+static void clear_db(struct userdata *u) {
+    datum key;
+
+    pa_assert(u);
+
+    key = gdbm_firstkey(u->gdbm_file);
+    while (key.dptr) {
+        datum next_key;
+        next_key = gdbm_nextkey(u->gdbm_file, key);
+
+        gdbm_delete(u->gdbm_file, key);
+        pa_xfree(key.dptr);
+
+        key = next_key;
+    }
+
+    gdbm_reorganize(u->gdbm_file);
+}
+
+static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
+    pa_sink_input *si;
+    pa_source_output *so;
+    uint32_t idx;
+
+    pa_assert(u);
+    pa_assert(name);
+    pa_assert(e);
+
+    for (si = pa_idxset_first(u->core->sink_inputs, &idx); si; si = pa_idxset_next(u->core->sink_inputs, &idx)) {
+        char *n;
+        pa_sink *s;
+
+        if (!(n = get_name(si->proplist, "sink_input")))
+            continue;
+
+        if (strcmp(name, n)) {
+            pa_xfree(n);
+            continue;
+        }
+
+        if (u->restore_volume) {
+            pa_log_info("Restoring volume for sink input %s.", name);
+            pa_sink_input_set_volume(si, pa_cvolume_remap(&e->volume, &e->channel_map, &si->channel_map));
+        }
+
+        if (u->restore_muted) {
+            pa_log_info("Restoring mute state for sink input %s.", name);
+            pa_sink_input_set_mute(si, e->muted);
+        }
+
+        if (u->restore_device &&
+            (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE, TRUE))) {
+
+            pa_log_info("Restoring device for stream %s.", name);
+            pa_sink_input_move_to(si, s);
+        }
+    }
+
+    for (so = pa_idxset_first(u->core->source_outputs, &idx); so; so = pa_idxset_next(u->core->source_outputs, &idx)) {
+        char *n;
+        pa_source *s;
+
+        if (!(n = get_name(so->proplist, "source-output")))
+            continue;
+
+        if (strcmp(name, n)) {
+            pa_xfree(n);
+            continue;
+        }
+
+        if (u->restore_device &&
+            (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE, TRUE))) {
+
+            pa_log_info("Restoring device for stream %s.", name);
+            pa_source_output_move_to(so, s);
+        }
+    }
+}
+
+static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
+    struct userdata *u;
+    uint32_t command;
+    pa_tagstruct *reply;
+
+    pa_assert(p);
+    pa_assert(m);
+    pa_assert(c);
+    pa_assert(t);
+
+    u = m->userdata;
+
+    if (pa_tagstruct_getu32(t, &command) < 0)
+        goto fail;
+
+    reply = pa_tagstruct_new(NULL, 0);
+    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
+    pa_tagstruct_putu32(reply, tag);
+
+    switch (command) {
+        case SUBCOMMAND_TEST: {
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            pa_tagstruct_putu32(reply, EXT_VERSION);
+            break;
+        }
+
+        case SUBCOMMAND_READ: {
+            datum key;
+
+            if (!pa_tagstruct_eof(t))
+                goto fail;
+
+            key = gdbm_firstkey(u->gdbm_file);
+            while (key.dptr) {
+                datum next_key;
+                struct entry *e;
+                char *name;
+
+                next_key = gdbm_nextkey(u->gdbm_file, key);
+
+                name = pa_xstrndup(key.dptr, key.dsize);
+                pa_xfree(key.dptr);
+
+                if ((e = read_entry(u, name))) {
+                    pa_tagstruct_puts(reply, name);
+                    pa_tagstruct_put_channel_map(reply, &e->channel_map);
+                    pa_tagstruct_put_cvolume(reply, &e->volume);
+                    pa_tagstruct_puts(reply, e->device);
+                    pa_tagstruct_put_boolean(reply, e->muted);
+
+                    pa_xfree(e);
+                }
+
+                pa_xfree(name);
+
+                key = next_key;
+            }
+
+            break;
+        }
+
+        case SUBCOMMAND_WRITE: {
+            uint32_t mode;
+            pa_bool_t apply_immediately;
+
+            if (pa_tagstruct_getu32(t, &mode) < 0 ||
+                pa_tagstruct_get_boolean(t, &apply_immediately) < 0)
+                goto fail;
+
+            if (mode != PA_UPDATE_MERGE &&
+                mode != PA_UPDATE_REPLACE &&
+                mode != PA_UPDATE_SET)
+                goto fail;
+
+            if (mode == PA_UPDATE_SET)
+                clear_db(u);
+
+            while (!pa_tagstruct_eof(t)) {
+                const char *name, *device;
+                pa_bool_t muted;
+                struct entry entry;
+                datum key, data;
+
+                memset(&entry, 0, sizeof(entry));
+
+                if (pa_tagstruct_gets(t, &name) < 0 ||
+                    pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
+                    pa_tagstruct_get_cvolume(t, &entry.volume) < 0 ||
+                    pa_tagstruct_gets(t, &device) < 0 ||
+                    pa_tagstruct_get_boolean(t, &muted) < 0)
+                    goto fail;
+
+                if (entry.channel_map.channels != entry.volume.channels)
+                    goto fail;
+
+                entry.muted = muted;
+                pa_strlcpy(entry.device, device, sizeof(entry.device));
+
+                key.dptr = (void*) name;
+                key.dsize = strlen(name);
+
+                data.dptr = (void*) &entry;
+                data.dsize = sizeof(entry);
+
+                if (gdbm_store(u->gdbm_file, key, data, mode == PA_UPDATE_REPLACE ? GDBM_REPLACE : GDBM_INSERT) == 1)
+                    if (apply_immediately)
+                        apply_entry(u, name, &entry);
+            }
+
+            trigger_save(u);
+
+            break;
+        }
+
+        case SUBCOMMAND_DELETE:
+
+            while (!pa_tagstruct_eof(t)) {
+                const char *name;
+                datum key;
+
+                if (pa_tagstruct_gets(t, &name) < 0)
+                    goto fail;
+
+                key.dptr = (void*) name;
+                key.dsize = strlen(name);
+
+                gdbm_delete(u->gdbm_file, key);
+            }
+
+            trigger_save(u);
+
+            break;
+
+        case SUBCOMMAND_SUBSCRIBE: {
+
+            pa_bool_t enabled;
+
+            if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
+                !pa_tagstruct_eof(t))
+                goto fail;
+
+            if (enabled)
+                pa_idxset_put(u->subscribed, c, NULL);
+            else
+                pa_idxset_remove_by_data(u->subscribed, c, NULL);
+
+            break;
+        }
+
+        default:
+            goto fail;
+    }
+
+    pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
+    return 0;
+
+fail:
+
+    if (reply)
+        pa_tagstruct_free(reply);
+
+    return -1;
+}
+
+static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
+    pa_assert(p);
+    pa_assert(c);
+    pa_assert(u);
+
+    pa_idxset_remove_by_data(u->subscribed, c, NULL);
+    return PA_HOOK_OK;
+}
+
 int pa__init(pa_module*m) {
     pa_modargs *ma = NULL;
     struct userdata *u;
@@ -370,6 +664,13 @@ int pa__init(pa_module*m) {
     u->restore_device = restore_device;
     u->restore_volume = restore_volume;
     u->restore_muted = restore_muted;
+    u->gdbm_file = NULL;
+    u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
+
+    u->protocol = pa_native_protocol_get(m->core);
+    pa_native_protocol_install_ext(u->protocol, m, extension_cb);
+
+    u->connection_unlink_hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
 
     u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
 
@@ -436,11 +737,22 @@ void pa__done(pa_module*m) {
     if (u->source_output_new_hook_slot)
         pa_hook_slot_free(u->source_output_new_hook_slot);
 
+    if (u->connection_unlink_hook_slot)
+        pa_hook_slot_free(u->connection_unlink_hook_slot);
+
     if (u->save_time_event)
         u->core->mainloop->time_free(u->save_time_event);
 
     if (u->gdbm_file)
         gdbm_close(u->gdbm_file);
 
+    if (u->protocol) {
+        pa_native_protocol_remove_ext(u->protocol, m);
+        pa_native_protocol_unref(u->protocol);
+    }
+
+    if (u->subscribed)
+        pa_idxset_free(u->subscribed, NULL, NULL);
+
     pa_xfree(u);
 }

commit 0cc674d96198b26cec81b38c32232e95735e4cca
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Aug 4 19:02:20 2008 +0200

    wrap protocol extension of module-stream-restore

diff --git a/src/Makefile.am b/src/Makefile.am
index 55b6fb3..f6828b3 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -479,7 +479,8 @@ pulseinclude_HEADERS = \
 		pulse/volume.h \
 		pulse/xmalloc.h \
 		pulse/proplist.h \
-		pulse/gccmacro.h
+		pulse/gccmacro.h \
+		pulse/ext-stream-restore.h
 
 if HAVE_AVAHI
 pulseinclude_HEADERS += \
@@ -530,7 +531,8 @@ libpulse_la_SOURCES = \
 		pulse/util.c pulse/util.h \
 		pulse/volume.c pulse/volume.h \
 		pulse/xmalloc.c pulse/xmalloc.h \
-		pulse/proplist.c pulse/proplist.h
+		pulse/proplist.c pulse/proplist.h \
+		pulse/ext-stream-restore.c pulse/ext-stream-restore.h
 
 # Internal stuff that is shared with libpulsecore
 libpulse_la_SOURCES += \
diff --git a/src/pulse/context.c b/src/pulse/context.c
index f56cb24..b20093d 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -82,6 +82,8 @@
 
 #define AUTOSPAWN_LOCK "autospawn.lock"
 
+void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
 static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_REQUEST] = pa_command_request,
     [PA_COMMAND_OVERFLOW] = pa_command_overflow_or_underflow,
@@ -93,7 +95,8 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = pa_command_stream_suspended,
     [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_SUBSCRIBE_EVENT] = pa_command_subscribe_event,
+    [PA_COMMAND_EXTENSION] = pa_command_extension
 };
 
 static void unlock_autospawn_lock_file(pa_context *c) {
@@ -126,6 +129,9 @@ static void reset_callbacks(pa_context *c) {
 
     c->subscribe_callback = NULL;
     c->subscribe_userdata = NULL;
+
+    c->ext_stream_restore.callback = NULL;
+    c->ext_stream_restore.userdata = NULL;
 }
 
 pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *name, pa_proplist *p) {
@@ -1230,3 +1236,32 @@ pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[]
 
     return o;
 }
+
+void pa_command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_context *c = userdata;
+    uint32_t idx;
+    const char *name;
+
+    pa_assert(pd);
+    pa_assert(command == PA_COMMAND_EXTENSION);
+    pa_assert(t);
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    pa_context_ref(c);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        !pa_tagstruct_eof(t)) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (!strcmp(name, "module-stream-restore"))
+        pa_ext_stream_restore_command(c, tag, t);
+    else
+        pa_log("Received message for unknown extension '%s'", name);
+
+finish:
+    pa_context_unref(c);
+}
diff --git a/src/pulse/ext-stream-restore.c b/src/pulse/ext-stream-restore.c
new file mode 100644
index 0000000..d64f79d
--- /dev/null
+++ b/src/pulse/ext-stream-restore.c
@@ -0,0 +1,331 @@
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 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 <pulse/context.h>
+#include <pulse/gccmacro.h>
+
+#include <pulsecore/macro.h>
+#include <pulsecore/pstream-util.h>
+
+#include "internal.h"
+
+#include "ext-stream-restore.h"
+
+enum {
+    SUBCOMMAND_TEST,
+    SUBCOMMAND_READ,
+    SUBCOMMAND_WRITE,
+    SUBCOMMAND_DELETE,
+    SUBCOMMAND_SUBSCRIBE,
+    SUBCOMMAND_EVENT
+};
+
+static void ext_stream_restore_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    uint32_t version = PA_INVALID_INDEX;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+    } else if (pa_tagstruct_getu32(t, &version) < 0 ||
+               !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_ext_stream_restore_test_cb_t cb = (pa_ext_stream_restore_test_cb_t) o->callback;
+        cb(o->context, version, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_stream_restore_test(
+        pa_context *c,
+        pa_ext_stream_restore_test_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-stream-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_stream_restore_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+static void ext_stream_restore_read_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    int eol = 1;
+
+    pa_assert(pd);
+    pa_assert(o);
+    pa_assert(PA_REFCNT_VALUE(o) >= 1);
+
+    if (!o->context)
+        goto finish;
+
+    if (command != PA_COMMAND_REPLY) {
+        if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
+            goto finish;
+
+        eol = -1;
+    } else {
+
+        while (!pa_tagstruct_eof(t)) {
+            pa_ext_stream_restore_info i;
+            pa_bool_t mute = FALSE;
+
+            memset(&i, 0, sizeof(i));
+
+            if (pa_tagstruct_gets(t, &i.name) < 0 ||
+                pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
+                pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
+                pa_tagstruct_gets(t, &i.device) < 0 ||
+                pa_tagstruct_get_boolean(t, &mute) < 0) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            i.mute = (int) mute;
+
+            if (o->callback) {
+                pa_ext_stream_restore_read_cb_t cb = (pa_ext_stream_restore_read_cb_t) o->callback;
+                cb(o->context, &i, 0, o->userdata);
+            }
+        }
+    }
+
+    if (o->callback) {
+        pa_ext_stream_restore_read_cb_t cb = (pa_ext_stream_restore_read_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_ext_stream_restore_read(
+        pa_context *c,
+        pa_ext_stream_restore_read_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-stream-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_READ);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_stream_restore_read_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_stream_restore_write(
+        pa_context *c,
+        pa_update_mode_t mode,
+        const pa_ext_stream_restore_info data[],
+        unsigned n,
+        pa_bool_t apply_immediately,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE || mode == PA_UPDATE_SET);
+    pa_assert(data);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-stream-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_WRITE);
+
+    pa_tagstruct_putu32(t, mode);
+    pa_tagstruct_put_boolean(t, apply_immediately);
+
+    for (; n > 0; n--, data++) {
+        pa_tagstruct_puts(t, data->name);
+        pa_tagstruct_put_channel_map(t, &data->channel_map);
+        pa_tagstruct_put_cvolume(t, &data->volume);
+        pa_tagstruct_puts(t, data->device);
+        pa_tagstruct_put_boolean(t, data->mute);
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_stream_restore_delete(
+        pa_context *c,
+        const char *const s[],
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+    const char *const *k;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(s);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-stream-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_DELETE);
+
+    for (k = s; *k; k++)
+        pa_tagstruct_puts(t, *k);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_ext_stream_restore_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata) {
+
+    uint32_t tag;
+    pa_operation *o;
+    pa_tagstruct *t;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, "module-stream-restore");
+    pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE);
+    pa_tagstruct_put_boolean(t, enable);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+void pa_ext_stream_restore_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_stream_restore_subscribe_cb_t cb,
+        void *userdata) {
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    c->ext_stream_restore.callback = cb;
+    c->ext_stream_restore.userdata = userdata;
+}
+
+void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t) {
+    uint32_t subcommand;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &subcommand) < 0 ||
+        !pa_tagstruct_eof(t)) {
+
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (subcommand != SUBCOMMAND_EVENT) {
+        pa_context_fail(c, PA_ERR_PROTOCOL);
+        return;
+    }
+
+    if (c->ext_stream_restore.callback)
+        c->ext_stream_restore.callback(c, c->ext_stream_restore.userdata);
+
+}
diff --git a/src/pulse/ext-stream-restore.h b/src/pulse/ext-stream-restore.h
new file mode 100644
index 0000000..3c77b46
--- /dev/null
+++ b/src/pulse/ext-stream-restore.h
@@ -0,0 +1,86 @@
+#ifndef foopulseextstreamrestorehfoo
+#define foopulseextstreamrestorehfoo
+
+/***
+  This file is part of PulseAudio.
+
+  Copyright 2008 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.
+***/
+
+#include <pulse/context.h>
+
+typedef struct pa_ext_stream_restore_info {
+    const char *name;
+    pa_channel_map channel_map;
+    pa_cvolume volume;
+    const char *device;
+    int mute;
+} pa_ext_stream_restore_info;
+
+typedef void (*pa_ext_stream_restore_test_cb_t)(
+        pa_context *c,
+        uint32_t version,
+        void *userdata);
+
+pa_operation *pa_ext_stream_restore_test(
+        pa_context *c,
+        pa_ext_stream_restore_test_cb_t cb,
+        void *userdata);
+
+typedef void (*pa_ext_stream_restore_read_cb_t)(
+        pa_context *c,
+        const pa_ext_stream_restore_info *info,
+        int eol,
+        void *userdata);
+
+pa_operation *pa_ext_stream_restore_read(
+        pa_context *c,
+        pa_ext_stream_restore_read_cb_t cb,
+        void *userdata);
+
+pa_operation *pa_ext_stream_restore_write(
+        pa_context *c,
+        pa_update_mode_t mode,
+        const pa_ext_stream_restore_info data[],
+        unsigned n,
+        pa_bool_t apply_immediately,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+pa_operation *pa_ext_stream_restore_delete(
+        pa_context *c,
+        const char *const s[],
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+pa_operation *pa_ext_stream_restore_subscribe(
+        pa_context *c,
+        int enable,
+        pa_context_success_cb_t cb,
+        void *userdata);
+
+typedef void (*pa_ext_stream_restore_subscribe_cb_t)(
+        pa_context *c,
+        void *userdata);
+
+void pa_ext_stream_restore_set_subscribe_cb(
+        pa_context *c,
+        pa_ext_stream_restore_subscribe_cb_t cb,
+        void *userdata);
+
+#endif
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 9ed541d..bfe888e 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -28,6 +28,7 @@
 #include <pulse/stream.h>
 #include <pulse/operation.h>
 #include <pulse/subscribe.h>
+#include <pulse/ext-stream-restore.h>
 
 #include <pulsecore/socket-client.h>
 #include <pulsecore/pstream.h>
@@ -86,6 +87,12 @@ struct pa_context {
     pa_client_conf *conf;
 
     uint32_t client_index;
+
+    /* Extension specific data */
+    struct {
+        pa_ext_stream_restore_subscribe_cb_t callback;
+        void *userdata;
+    } ext_stream_restore;
 };
 
 #define PA_MAX_WRITE_INDEX_CORRECTIONS 32
@@ -233,4 +240,6 @@ pa_tagstruct *pa_tagstruct_command(pa_context *c, uint32_t command, uint32_t *ta
 
 #define PA_CHECK_VALIDITY_RETURN_NULL(context, expression, error) PA_CHECK_VALIDITY_RETURN_ANY(context, expression, error, NULL)
 
+void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t);
+
 #endif
diff --git a/src/pulse/subscribe.c b/src/pulse/subscribe.c
index d9c06b7..b8d3be8 100644
--- a/src/pulse/subscribe.c
+++ b/src/pulse/subscribe.c
@@ -61,7 +61,6 @@ finish:
     pa_context_unref(c);
 }
 
-
 pa_operation* pa_context_subscribe(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb, void *userdata) {
     pa_operation *o;
     pa_tagstruct *t;

-- 
hooks/post-receive
PulseAudio Sound Server



More information about the pulseaudio-commits mailing list