[pulseaudio-discuss] [PATCH v3 17/18] native: Add node querying to the protocol

Tanu Kaskinen tanu.kaskinen at linux.intel.com
Wed Jul 3 04:09:20 PDT 2013


---
 PROTOCOL                        |  31 +++++++++++
 configure.ac                    |   2 +-
 src/map-file                    |   3 ++
 src/pulse/introspect.c          | 111 ++++++++++++++++++++++++++++++++++++++++
 src/pulse/introspect.h          |  41 +++++++++++++++
 src/pulsecore/native-common.h   |   4 ++
 src/pulsecore/protocol-native.c |  46 +++++++++++++----
 7 files changed, 226 insertions(+), 12 deletions(-)

diff --git a/PROTOCOL b/PROTOCOL
index 185b91a..e0c0a64 100644
--- a/PROTOCOL
+++ b/PROTOCOL
@@ -342,6 +342,37 @@ SUBCOMMAND_SAVE_FORMATS, in reply from SUBCOMMAND_READ_FORMATS[_ALL]
 
     (uint8_t ) PA_ENCODING_MPEG2_AAC_IEC61937 := 6
 
+## v29, implemented by >= 5.0
+
+New client->server commands:
+    GET_NODE_INFO
+    GET_NODE_INFO_LIST
+
+GET_NODE_INFO parameters:
+    uint32_t index
+    string name
+
+When getting the node info by index, the index parameter must not be
+PA_INVALID_INDEX and the name parameter must be NULL.
+
+When getting the node info by name, the index parameter must be
+PA_INVALID_INDEX and the name parameter must not be NULL or an empty string.
+
+GET_NODE_INFO_LIST doesn't have parameters.
+
+GET_NODE_INFO reply parameters:
+    uint32_t index
+    string name
+    string description
+    pa_direction_t direction
+
+The index must not be PA_INVALID_INDEX. The name must not be NULL or empty. The
+description must not be NULL or empty. The direction must be either OUTPUT or
+INPUT, not both.
+
+GET_NODE_INFO_LIST reply parameters are otherwise the same as with
+GET_NODE_INFO, but the parameter list is repeated for each node.
+
 #### If you just changed the protocol, read this
 ## module-tunnel depends on the sink/source/sink-input/source-input protocol
 ## internals, so if you changed these, you might have broken module-tunnel.
diff --git a/configure.ac b/configure.ac
index 9cbcb6d..5a2bbab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -41,7 +41,7 @@ AC_SUBST(PA_MINOR, pa_minor)
 AC_SUBST(PA_MAJORMINOR, pa_major.pa_minor)
 
 AC_SUBST(PA_API_VERSION, 12)
-AC_SUBST(PA_PROTOCOL_VERSION, 28)
+AC_SUBST(PA_PROTOCOL_VERSION, 29)
 
 # The stable ABI for client applications, for the version info x:y:z
 # always will hold y=z
diff --git a/src/map-file b/src/map-file
index 90c8ea2..562c48c 100644
--- a/src/map-file
+++ b/src/map-file
@@ -42,6 +42,9 @@ pa_context_get_client_info_list;
 pa_context_get_index;
 pa_context_get_module_info;
 pa_context_get_module_info_list;
+pa_context_get_node_info_by_index;
+pa_context_get_node_info_by_name;
+pa_context_get_node_info_list;
 pa_context_get_protocol_version;
 pa_context_get_sample_info_by_index;
 pa_context_get_sample_info_by_name;
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 704e96e..53c5011 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -1777,6 +1777,117 @@ pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t
     return pa_context_send_simple_command(c, PA_COMMAND_GET_SAMPLE_INFO_LIST, context_get_sample_info_callback, (pa_operation_cb_t) cb, userdata);
 }
 
+/*** Routing node info ***/
+
+static void context_get_node_info_callback(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);
+
+    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_node_info info;
+
+            pa_zero(info);
+
+            if (pa_tagstruct_getu32(t, &info.index) < 0 ||
+                info.index == PA_INVALID_INDEX ||
+                pa_tagstruct_gets(t, &info.name) < 0 ||
+                !info.name || !*info.name ||
+                pa_tagstruct_gets(t, &info.description) < 0 ||
+                !info.description || !*info.description ||
+                pa_tagstruct_get_direction(t, &info.direction) ||
+                (info.direction != PA_DIRECTION_OUTPUT && info.direction != PA_DIRECTION_INPUT)) {
+
+                pa_context_fail(o->context, PA_ERR_PROTOCOL);
+                goto finish;
+            }
+
+            if (o->callback) {
+                pa_node_info_cb_t cb = (pa_node_info_cb_t) o->callback;
+                cb(o->context, &info, 0, o->userdata);
+            }
+        }
+    }
+
+    if (o->callback) {
+        pa_node_info_cb_t cb = (pa_node_info_cb_t) o->callback;
+        cb(o->context, NULL, eol, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+pa_operation *pa_context_get_node_info_by_name(pa_context *c, const char *name, pa_node_info_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(name);
+    pa_assert(*name);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 29, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_NODE_INFO, &tag);
+    pa_tagstruct_putu32(t, PA_INVALID_INDEX);
+    pa_tagstruct_puts(t, name);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_node_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_context_get_node_info_by_index(pa_context *c, uint32_t idx, pa_node_info_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(idx != PA_INVALID_INDEX);
+    pa_assert(cb);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 29, PA_ERR_NOTSUPPORTED);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_NODE_INFO, &tag);
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, NULL);
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_get_node_info_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation *pa_context_get_node_info_list(pa_context *c, pa_node_info_cb_t cb, void *userdata) {
+    pa_assert(c);
+
+    PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 29, PA_ERR_NOTSUPPORTED);
+
+    return pa_context_send_simple_command(c, PA_COMMAND_GET_NODE_INFO_LIST, context_get_node_info_callback, (pa_operation_cb_t) cb, userdata);
+}
+
 static pa_operation* command_kill(pa_context *c, uint32_t command, uint32_t idx, pa_context_success_cb_t cb, void *userdata) {
     pa_operation *o;
     pa_tagstruct *t;
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index a833471..dadde48 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -665,6 +665,47 @@ pa_operation* pa_context_get_sample_info_list(pa_context *c, pa_sample_info_cb_t
 
 /** @} */
 
+/** @{ \name Routing Nodes */
+
+/** Routing node information. Please note that this structure can be extended
+ * as part of evolutionary API updates at any time in any new release.
+ *
+ * \since 5.0 */
+typedef struct pa_node_info {
+    /** The index of this node. */
+    uint32_t index;
+
+    /** The name of this node. While one name can refer to only one node at any
+     * given time, the same name can still refer to multiple nodes over time,
+     * and the same node may have a different name e.g. after server restart.
+     * This means that nodes don't have any identifier that could be e.g. saved
+     * to disk for referring to the same node later. There may be some nodes in
+     * the future that have a fixed name, but currently there are none. */
+    const char *name;
+
+    /** The human-readable description of this node. This is localized and
+     * suitable to be shown in user interfaces as the label for this node. */
+    const char *description;
+
+    /** The direction of this node. */
+    pa_direction_t direction;
+} pa_node_info;
+
+/** Callback prototype for pa_context_get_node_info_by_name() and friends.
+ * \since 5.0 */
+typedef void (*pa_node_info_cb_t)(pa_context *c, const pa_node_info *info, int eol, void *userdata);
+
+/** Get information about a node by its name. \since 5.0 */
+pa_operation *pa_context_get_node_info_by_name(pa_context *c, const char *name, pa_node_info_cb_t cb, void *userdata);
+
+/** Get information about a node by its index. \since 5.0 */
+pa_operation *pa_context_get_node_info_by_index(pa_context *c, uint32_t idx, pa_node_info_cb_t cb, void *userdata);
+
+/** Get a list of all nodes. \since 5.0 */
+pa_operation *pa_context_get_node_info_list(pa_context *c, pa_node_info_cb_t cb, void *userdata);
+
+/** @} */
+
 /** \cond fulldocs */
 
 /** @{ \name Autoload Entries */
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index dad82e0..7898118 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -176,6 +176,10 @@ enum {
     /* Supported since protocol v27 (3.0) */
     PA_COMMAND_SET_PORT_LATENCY_OFFSET,
 
+    /* Supported since protocol v29 (5.0) */
+    PA_COMMAND_GET_NODE_INFO,
+    PA_COMMAND_GET_NODE_INFO_LIST,
+
     PA_COMMAND_MAX
 };
 
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index a73787d..94b6e9f 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -325,6 +325,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info,
     [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info,
     [PA_COMMAND_GET_SAMPLE_INFO] = command_get_info,
+    [PA_COMMAND_GET_NODE_INFO] = command_get_info,
     [PA_COMMAND_GET_SINK_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list,
@@ -333,6 +334,7 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
     [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list,
+    [PA_COMMAND_GET_NODE_INFO_LIST] = command_get_info_list,
     [PA_COMMAND_GET_SERVER_INFO] = command_get_server_info,
     [PA_COMMAND_SUBSCRIBE] = command_subscribe,
 
@@ -3452,6 +3454,16 @@ static void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
         pa_tagstruct_put_proplist(t, e->proplist);
 }
 
+static void node_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_node *node) {
+    pa_assert(t);
+    pa_assert(node);
+
+    pa_tagstruct_putu32(t, node->index);
+    pa_tagstruct_puts(t, node->name);
+    pa_tagstruct_puts(t, node->description);
+    pa_tagstruct_put_direction(t, node->direction);
+}
+
 static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
     pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
     uint32_t idx;
@@ -3463,6 +3475,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
     pa_sink_input *si = NULL;
     pa_source_output *so = NULL;
     pa_scache_entry *sce = NULL;
+    pa_node *node = NULL;
     const char *name = NULL;
     pa_tagstruct *reply;
 
@@ -3516,15 +3529,20 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
         si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
     else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO)
         so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
-    else {
-        pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO);
+    else if (command == PA_COMMAND_GET_SAMPLE_INFO) {
         if (idx != PA_INVALID_INDEX)
             sce = pa_idxset_get_by_index(c->protocol->core->scache, idx);
         else
             sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE);
-    }
+    } else if (command == PA_COMMAND_GET_NODE_INFO) {
+        if (idx != PA_INVALID_INDEX)
+            node = pa_idxset_get_by_index(c->protocol->core->nodes, idx);
+        else
+            node = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_NODE);
+    } else
+        pa_assert_not_reached();
 
-    if (!sink && !source && !client && !card && !module && !si && !so && !sce) {
+    if (!sink && !source && !client && !card && !module && !si && !so && !sce && !node) {
         pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
         return;
     }
@@ -3544,8 +3562,10 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p
         sink_input_fill_tagstruct(c, reply, si);
     else if (so)
         source_output_fill_tagstruct(c, reply, so);
-    else
+    else if (sce)
         scache_fill_tagstruct(c, reply, sce);
+    else if (node)
+        node_fill_tagstruct(c, reply, node);
     pa_pstream_send_tagstruct(c->pstream, reply);
 }
 
@@ -3582,10 +3602,12 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
         i = c->protocol->core->sink_inputs;
     else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
         i = c->protocol->core->source_outputs;
-    else {
-        pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
+    else if (command == PA_COMMAND_GET_SAMPLE_INFO_LIST)
         i = c->protocol->core->scache;
-    }
+    else if (command == PA_COMMAND_GET_NODE_INFO_LIST)
+        i = c->protocol->core->nodes;
+    else
+        pa_assert_not_reached();
 
     if (i) {
         PA_IDXSET_FOREACH(p, i, idx) {
@@ -3603,10 +3625,12 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t
                 sink_input_fill_tagstruct(c, reply, p);
             else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
                 source_output_fill_tagstruct(c, reply, p);
-            else {
-                pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
+            else if (command == PA_COMMAND_GET_SAMPLE_INFO_LIST)
                 scache_fill_tagstruct(c, reply, p);
-            }
+            else if (command == PA_COMMAND_GET_NODE_INFO_LIST)
+                node_fill_tagstruct(c, reply, p);
+            else
+                pa_assert_not_reached();
         }
     }
 
-- 
1.8.1.2



More information about the pulseaudio-discuss mailing list