[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