[pulseaudio-discuss] [PATCH v2 4/4] native-protocol: send supported extensions in client & server

Marc-André Lureau marc-andre.lureau at nokia.com
Mon May 11 15:35:45 PDT 2009


The client will send a list of extension-id along with SET_NAME.
---
 PROTOCOL                            |   12 ++++++
 src/modules/module-stream-restore.c |    4 +-
 src/pulse/context.c                 |   51 ++++++++++++++++++++++++--
 src/pulse/context.h                 |    3 ++
 src/pulse/ext-stream-restore.c      |    9 +++--
 src/pulse/internal.h                |    3 +-
 src/pulse/xmalloc.h                 |   13 +++++++
 src/pulsecore/core-util.h           |   16 ++++++++
 src/pulsecore/native-common.h       |    1 +
 src/pulsecore/protocol-native.c     |   69 ++++++++++++++++++++++++++++++++---
 src/pulsecore/protocol-native.h     |    1 +
 src/pulsecore/strlist.c             |   37 +++++++++++++++++++
 src/pulsecore/strlist.h             |    9 +++++
 src/tests/strlist-test.c            |    9 +++++
 14 files changed, 220 insertions(+), 17 deletions(-)

diff --git a/PROTOCOL b/PROTOCOL
index 9955da5..9603051 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -183,6 +183,18 @@ new messages:
   PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED
 
 
+### v15, implemented by >= 0.9.16
+
+PA_COMMAND_SET_CLIENT_NAME, add at the end:
+
+  uint32 n number of extension-version supported by the client
+  string n times of the form "extension-name-version"
+
+On response of PA_COMMAND_SET_CLIENT_NAME, add at the end:
+
+  uint32 n number of extension-version supported by the server
+  string n times of the form "extension-name-version"
+
 ### ext-stream-restore v1, implemented by >= 0.9.12
 
 First version supported.
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index db30528..2fc34a2 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -648,7 +648,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                     pa_tagstruct_put_cvolume(reply, e->volume_valid ? &e->volume : pa_cvolume_init(&r));
                     pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
                     pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
-		    if (c->version >= 16) /* FIXME: should be client side extension version */
+		    if (pa_native_connection_extension_supported(c, "stream-restore-2"))
                         pa_tagstruct_put_boolean(reply, e->volume_is_absolute_valid ? e->volume_is_absolute : FALSE);
 
                     pa_xfree(e);
@@ -695,7 +695,7 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
                     pa_tagstruct_get_boolean(t, &muted) < 0)
                     goto fail;
 
-                if (c->version >= 16) {
+		if (pa_native_connection_extension_supported(c, "stream-restore-2")) {
                     if (pa_tagstruct_get_boolean(t, &volume_is_absolute) < 0)
                         goto fail;
 
diff --git a/src/pulse/context.c b/src/pulse/context.c
index 90c15b1..5443bac 100644
--- a/src/pulse/context.c
+++ b/src/pulse/context.c
@@ -168,8 +168,6 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
 
     reset_callbacks(c);
 
-    c->ext_stream_restore.version = 1; /* default version, updated by TEST message */
-
     c->is_local = FALSE;
     c->server_list = NULL;
     c->server = NULL;
@@ -205,6 +203,8 @@ pa_context *pa_context_new_with_proplist(pa_mainloop_api *mainloop, const char *
         }
     }
 
+    c->supported_extensions = NULL;
+
     return c;
 }
 
@@ -276,6 +276,9 @@ static void context_free(pa_context *c) {
     if (c->proplist)
         pa_proplist_free(c->proplist);
 
+
+    pa_strv_free(c->supported_extensions);
+
     pa_xfree(c->server);
     pa_xfree(c);
 }
@@ -498,6 +501,12 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
             } else
                 pa_tagstruct_puts(reply, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
 
+            if (c->version >= 16) {
+                pa_tagstruct_putu32(reply, 2);
+                pa_tagstruct_puts(reply, "stream-restore-1");
+                pa_tagstruct_puts(reply, "stream-restore-2");
+            }
+
             pa_pstream_send_tagstruct(c->pstream, reply);
             pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, c, NULL);
 
@@ -508,8 +517,35 @@ static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t
         case PA_CONTEXT_SETTING_NAME :
 
             if ((c->version >= 13 && (pa_tagstruct_getu32(t, &c->client_index) < 0 ||
-                                      c->client_index == PA_INVALID_INDEX)) ||
-                !pa_tagstruct_eof(t)) {
+                                      c->client_index == PA_INVALID_INDEX))) {
+                pa_context_fail(c, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            if (c->version >= 16) {
+                uint32_t n;
+                const char *supported;
+
+                if (pa_tagstruct_getu32(t, &n) < 0) {
+                    pa_context_fail(c, PA_ERR_PROTOCOL);
+                    goto finish;
+                }
+
+		c->supported_extensions = pa_xnew(char *, n + 1);
+		c->supported_extensions[n] = NULL;
+
+		while (n) {
+                    --n;
+                    if (pa_tagstruct_gets(t, &supported) < 0) {
+                        pa_context_fail(c, PA_ERR_PROTOCOL);
+                        goto finish;
+                    }
+
+		    c->supported_extensions[n] = pa_xstrdup(supported);
+                }
+            }
+
+            if (!pa_tagstruct_eof(t)) {
                 pa_context_fail(c, PA_ERR_PROTOCOL);
                 goto finish;
             }
@@ -1444,3 +1480,10 @@ finish:
     if (pl)
         pa_proplist_free(pl);
 }
+
+int pa_context_extension_supported(pa_context *c, const char *extension) {
+    pa_assert(c);
+    pa_assert(extension);
+
+    return !pa_strv_index((const char * const *)c->supported_extensions, extension, NULL);
+}
diff --git a/src/pulse/context.h b/src/pulse/context.h
index 139d0e0..8762b51 100644
--- a/src/pulse/context.h
+++ b/src/pulse/context.h
@@ -260,6 +260,9 @@ pa_operation *pa_context_proplist_remove(pa_context *c, const char *const keys[]
  * introspection functions, such as pa_context_get_client_info(). \since 0.9.11 */
 uint32_t pa_context_get_index(pa_context *s);
 
+/** Check if "extension" is supported in server side. \since 0.9.16 */
+int pa_context_extension_supported(pa_context *c, const char *extension);
+
 PA_C_DECL_END
 
 #endif
diff --git a/src/pulse/ext-stream-restore.c b/src/pulse/ext-stream-restore.c
index a361ff4..ba05a3f 100644
--- a/src/pulse/ext-stream-restore.c
+++ b/src/pulse/ext-stream-restore.c
@@ -46,6 +46,7 @@ enum {
 };
 
 static void ext_stream_restore_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    uint32_t version;
     pa_operation *o = userdata;
 
     pa_assert(pd);
@@ -59,7 +60,7 @@ static void ext_stream_restore_test_cb(pa_pdispatch *pd, uint32_t command, uint3
         if (pa_context_handle_error(o->context, command, t, FALSE) < 0)
             goto finish;
 
-    } else if (pa_tagstruct_getu32(t, &o->context->ext_stream_restore.version) < 0 ||
+    } else if (pa_tagstruct_getu32(t, &version) < 0 ||
                !pa_tagstruct_eof(t)) {
 
         pa_context_fail(o->context, PA_ERR_PROTOCOL);
@@ -68,7 +69,7 @@ static void ext_stream_restore_test_cb(pa_pdispatch *pd, uint32_t command, uint3
 
     if (o->callback) {
         pa_ext_stream_restore_test_cb_t cb = (pa_ext_stream_restore_test_cb_t) o->callback;
-        cb(o->context, o->context->ext_stream_restore.version, o->userdata);
+        cb(o->context, version, o->userdata);
     }
 
 finish:
@@ -173,7 +174,7 @@ static void ext_stream_restore2_read_cb(pa_pdispatch *pd, uint32_t command, uint
                 goto finish;
             }
 
-            if (o->context->ext_stream_restore.version >= 2 &&
+            if (pa_context_extension_supported(o->context, "stream-restore-2") &&
                 pa_tagstruct_get_boolean(t, &volume_is_absolute) < 0) {
 
                 pa_context_fail(o->context, PA_ERR_PROTOCOL);
@@ -307,7 +308,7 @@ pa_operation *pa_ext_stream_restore2_write(
         pa_tagstruct_put_cvolume(t, &data[i]->volume);
         pa_tagstruct_puts(t, data[i]->device);
         pa_tagstruct_put_boolean(t, data[i]->mute);
-        if (c->ext_stream_restore.version >= 2)
+        if (pa_context_extension_supported(c, "stream-restore-2"))
             pa_tagstruct_put_boolean(t, data[i]->volume_is_absolute);
     }
 
diff --git a/src/pulse/internal.h b/src/pulse/internal.h
index 3c58c49..d1ac12c 100644
--- a/src/pulse/internal.h
+++ b/src/pulse/internal.h
@@ -99,11 +99,12 @@ struct pa_context {
 
     uint32_t client_index;
 
+    char **supported_extensions;
+
     /* Extension specific data */
     struct {
         pa_ext_stream_restore_subscribe_cb_t callback;
         void *userdata;
-        uint32_t version;
     } ext_stream_restore;
 };
 
diff --git a/src/pulse/xmalloc.h b/src/pulse/xmalloc.h
index db20496..39f2d0d 100644
--- a/src/pulse/xmalloc.h
+++ b/src/pulse/xmalloc.h
@@ -91,6 +91,19 @@ static inline void* _pa_xnewdup_internal(const void *p, size_t n, size_t k) {
 /** Same as pa_xnew() but set the memory to zero */
 #define pa_xnewdup(type, p, n) ((type*) _pa_xnewdup_internal((p), (n), sizeof(type)))
 
+/** Free a string array and its strings */
+static inline void pa_strv_free(char *strv[]) {
+    unsigned i;
+
+    if (!strv)
+        return;
+
+    for (i = 0; strv[i]; ++i)
+        pa_xfree(strv[i]);
+
+    pa_xfree(strv);
+}
+
 PA_C_DECL_END
 
 #endif
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index f96fa44..6f929e4 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -201,6 +201,22 @@ pa_bool_t pa_in_system_mode(void);
 
 #define pa_streq(a,b) (!strcmp((a),(b)))
 
+static inline int pa_strv_index(const char * const *strv, const char *needle, unsigned *idx) {
+    unsigned i;
+
+    if (!strv)
+        return -1;
+
+    for (i = 0; strv[i]; ++i)
+        if (pa_streq(strv[i], needle)) {
+            if (idx)
+                *idx = i;
+            return 0;
+        }
+
+    return -1;
+}
+
 char *pa_machine_id(void);
 char *pa_session_id(void);
 char *pa_uname_string(void);
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index d4d7f3e..19b8c22 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -25,6 +25,7 @@
 
 #include <pulse/cdecl.h>
 #include <pulse/def.h>
+#include <pulsecore/macro.h>
 
 PA_C_DECL_BEGIN
 
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 5db080f..38c8409 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -178,6 +178,7 @@ struct pa_native_connection {
     uint32_t rrobin_index;
     pa_subscription *subscription;
     pa_time_event *auth_timeout_event;
+    char **supported_extensions;
 };
 
 PA_DECLARE_CLASS(pa_native_connection);
@@ -1233,6 +1234,8 @@ static void native_connection_free(pa_object *o) {
     pa_pstream_unref(c->pstream);
     pa_client_free(c->client);
 
+    pa_strv_free(c->supported_extensions);
+
     pa_xfree(c);
 }
 
@@ -2398,11 +2401,25 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
 #endif
 }
 
+static pa_strlist *get_extension_list(pa_native_connection *c) {
+    void *state = NULL, *lstate = NULL;
+    native_protocol_ext *ext;
+    pa_strlist *l = NULL;
+    const char *e;
+
+    while ((ext = pa_hashmap_iterate(c->protocol->extensions, &state, NULL)))
+        while ((e = pa_strlist_iterate(ext->supported, &lstate)))
+            l = pa_strlist_prepend(l, e);
+
+    return l;
+}
+
 static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
-    const char *name = NULL;
+    const char *name = NULL, *ext;
     pa_proplist *p;
     pa_tagstruct *reply;
+    void *state = NULL;
 
     pa_native_connection_assert_ref(c);
     pa_assert(t);
@@ -2410,14 +2427,29 @@ static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t
     p = pa_proplist_new();
 
     if ((c->version < 13 && pa_tagstruct_gets(t, &name) < 0) ||
-        (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
-        !pa_tagstruct_eof(t)) {
+        (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0))
+        goto error;
 
-        protocol_error(c);
-        pa_proplist_free(p);
-        return;
+    if (c->version >= 16) {
+        uint32_t n;
+
+        if (pa_tagstruct_getu32(t, &n) < 0)
+            goto error;
+
+        c->supported_extensions = pa_xnew(char *, n + 1);
+        c->supported_extensions[n] = NULL;
+
+        while (n) {
+            --n;
+            if (pa_tagstruct_gets(t, &ext) < 0)
+                goto error;
+            c->supported_extensions[n] = pa_xstrdup(ext);
+        }
     }
 
+    if (!pa_tagstruct_eof(t))
+        goto error;
+
     if (name)
         if (pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name) < 0) {
             pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
@@ -2433,7 +2465,25 @@ static void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t
     if (c->version >= 13)
         pa_tagstruct_putu32(reply, c->client->index);
 
+    if (c->version >= 16) {
+        pa_strlist *extl;
+
+        extl = get_extension_list(c);
+
+        pa_tagstruct_putu32(reply, pa_strlist_size(extl));
+
+        while ((ext = pa_strlist_iterate(extl, &state)))
+            pa_tagstruct_puts(reply, ext);
+
+        pa_strlist_free(extl);
+    }
+
     pa_pstream_send_tagstruct(c->pstream, reply);
+    return;
+
+error:
+    protocol_error(c);
+    pa_proplist_free(p);
 }
 
 static void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
@@ -4474,6 +4524,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati
 
     c->rrobin_index = PA_IDXSET_INVALID;
     c->subscription = NULL;
+    c->supported_extensions = NULL;
 
     pa_idxset_put(p->connections, c, NULL);
 
@@ -4751,3 +4802,9 @@ pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c) {
 
     return c->pstream;
 }
+
+int pa_native_connection_extension_supported(pa_native_connection *c, const char *extension) {
+    pa_native_connection_assert_ref(c);
+
+    return !pa_strv_index((const char * const *)c->supported_extensions, extension, NULL);
+}
diff --git a/src/pulsecore/protocol-native.h b/src/pulsecore/protocol-native.h
index 7bd159a..9962125 100644
--- a/src/pulsecore/protocol-native.h
+++ b/src/pulsecore/protocol-native.h
@@ -82,6 +82,7 @@ int pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_nativ
 void pa_native_protocol_remove_ext(pa_native_protocol *p, pa_module *m);
 
 pa_pstream* pa_native_connection_get_pstream(pa_native_connection *c);
+int pa_native_connection_extension_supported(pa_native_connection *c, const char *extension);
 
 pa_native_options* pa_native_options_new(void);
 pa_native_options* pa_native_options_ref(pa_native_options *o);
diff --git a/src/pulsecore/strlist.c b/src/pulsecore/strlist.c
index cbafbba..86f5797 100644
--- a/src/pulsecore/strlist.c
+++ b/src/pulsecore/strlist.c
@@ -159,3 +159,40 @@ pa_strlist *pa_strlist_reverse(pa_strlist *l) {
 
     return r;
 }
+
+const char* pa_strlist_iterate(pa_strlist *l, void **state) {
+    pa_strlist *e;
+
+    pa_assert(state);
+
+    if (*state == (void*) -1)
+        goto at_end;
+
+    if (!*state && !l)
+        goto at_end;
+
+    e = *state ? *state : l;
+
+    if (e->next)
+        *state = e->next;
+    else
+        *state = (void*) -1;
+
+    return ITEM_TO_TEXT(e);
+
+at_end:
+    *state = (void *) -1;
+
+    return NULL;
+}
+
+unsigned pa_strlist_size(pa_strlist *l) {
+    unsigned n = 0;
+
+    while (l) {
+        l = l->next;
+        ++n;
+    }
+
+    return n;
+}
diff --git a/src/pulsecore/strlist.h b/src/pulsecore/strlist.h
index 2584e86..b85dcc4 100644
--- a/src/pulsecore/strlist.h
+++ b/src/pulsecore/strlist.h
@@ -47,4 +47,13 @@ pa_strlist* pa_strlist_parse(const char *s);
 /* Reverse string list */
 pa_strlist *pa_strlist_reverse(pa_strlist *l);
 
+/* May be used to iterate through the strlist. Initially the opaque
+   pointer *state has to be set to NULL. The strlist may not be
+   modified during iteration. After the last entry in the strlist NULL
+   is returned. */
+const char* pa_strlist_iterate(pa_strlist *l, void **state);
+
+/* Return the current number of entries of the strlist */
+unsigned pa_strlist_size(pa_strlist *l);
+
 #endif
diff --git a/src/tests/strlist-test.c b/src/tests/strlist-test.c
index 10f370c..adbf492 100644
--- a/src/tests/strlist-test.c
+++ b/src/tests/strlist-test.c
@@ -6,8 +6,10 @@
 #include <pulsecore/strlist.h>
 
 int main(int argc, char* argv[]) {
+    const char *c;
     char *t, *u;
     pa_strlist *l = NULL;
+    void *state = NULL;
 
     l = pa_strlist_prepend(l, "e");
     l = pa_strlist_prepend(l, "d");
@@ -37,6 +39,13 @@ int main(int argc, char* argv[]) {
     fprintf(stderr, "4: %s\n", t);
     pa_xfree(t);
 
+    fprintf(stderr, "5:");
+    while ((c = pa_strlist_iterate(l, &state)))
+        fprintf(stderr, " %s", c);
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "6: %d\n", pa_strlist_size(l));
+
     pa_strlist_free(l);
 
     return 0;
-- 
1.6.2.4




More information about the pulseaudio-discuss mailing list