[pulseaudio-discuss] [PATCH] protocol-native: Add command to set or get module parameters

Georg Chini georg at chini.tk
Sun May 14 13:23:28 UTC 2017


This patch adds two new commands set_module_parameter and get_module_parameter
to the native API. These commands allow for dynamic reconfiguration of a module
during run time. A parameter string can be passed by set_module_parameter to a
callback function set_parameter_callback() within the module.
Likewise get_module_parameter passes a parameter name and get_parameter_callback()
within the module is expected to return a string containing the value of the named
parameter.
The subscription API can be used to notify clients if a parameter has changed.
---
 src/map-file                    |   4 ++
 src/pulse/introspect.c          | 112 +++++++++++++++++++++++++++++++++++++++
 src/pulse/introspect.h          |  15 ++++++
 src/pulsecore/module.c          |   2 +
 src/pulsecore/module.h          |   6 +++
 src/pulsecore/native-common.h   |   3 ++
 src/pulsecore/pdispatch.c       |   3 ++
 src/pulsecore/protocol-native.c | 114 ++++++++++++++++++++++++++++++++++++++++
 src/utils/pactl.c               |  55 +++++++++++++++++++
 9 files changed, 314 insertions(+)

diff --git a/src/map-file b/src/map-file
index 93a62b86..026b6df3 100644
--- a/src/map-file
+++ b/src/map-file
@@ -118,6 +118,10 @@ pa_context_suspend_sink_by_name;
 pa_context_suspend_source_by_index;
 pa_context_suspend_source_by_name;
 pa_context_unload_module;
+pa_context_set_module_parameter_by_name;
+pa_context_set_module_parameter_by_index;
+pa_context_get_module_parameter_by_name;
+pa_context_get_module_parameter_by_index;
 pa_context_unref;
 pa_cvolume_avg;
 pa_cvolume_avg_mask;
diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c
index 510d784a..3943c3fe 100644
--- a/src/pulse/introspect.c
+++ b/src/pulse/introspect.c
@@ -2184,3 +2184,115 @@ pa_operation* pa_context_suspend_source_by_index(pa_context *c, uint32_t idx, in
 
     return o;
 }
+
+static pa_operation* set_module_parameter_by_index_or_name(pa_context *c, uint32_t idx, const char *module_name, const char *parameter, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    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);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_SET_MODULE_PARAMETER, &tag);
+
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, module_name);
+    pa_tagstruct_puts(t, parameter);
+
+    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_context_set_module_parameter_by_name(pa_context *c, const char *module_name, const char *parameter, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    o =  set_module_parameter_by_index_or_name(c, PA_INVALID_INDEX, module_name, parameter, cb, userdata);
+    return o;
+}
+
+pa_operation* pa_context_set_module_parameter_by_index(pa_context *c, uint32_t idx, const char *parameter, pa_context_success_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    o =  set_module_parameter_by_index_or_name(c, idx, NULL, parameter, cb, userdata);
+    return o;
+}
+
+/** Module parameter string **/
+
+static void context_string_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+    pa_operation *o = userdata;
+    const char *parameter_value;
+
+    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;
+
+        parameter_value = NULL;
+    } else if (pa_tagstruct_gets(t, &parameter_value) ||
+               !pa_tagstruct_eof(t)) {
+        pa_context_fail(o->context, PA_ERR_PROTOCOL);
+        goto finish;
+    }
+
+    if (o->callback) {
+        pa_context_string_cb_t cb = (pa_context_string_cb_t) o->callback;
+        cb(o->context, parameter_value, o->userdata);
+    }
+
+finish:
+    pa_operation_done(o);
+    pa_operation_unref(o);
+}
+
+static pa_operation* get_module_parameter_by_index_or_name(pa_context *c, uint32_t idx, const char *module_name, const char *parameter, pa_context_string_cb_t cb, void *userdata) {
+    pa_operation *o;
+    pa_tagstruct *t;
+    uint32_t tag;
+
+    pa_assert(c);
+    pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+    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);
+
+    o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
+
+    t = pa_tagstruct_command(c, PA_COMMAND_GET_MODULE_PARAMETER, &tag);
+
+    pa_tagstruct_putu32(t, idx);
+    pa_tagstruct_puts(t, module_name);
+    pa_tagstruct_puts(t, parameter);
+
+    pa_pstream_send_tagstruct(c->pstream, t);
+    pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, context_string_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
+
+    return o;
+}
+
+pa_operation* pa_context_get_module_parameter_by_name(pa_context *c, const char *module_name, const char *parameter, pa_context_string_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    o =  get_module_parameter_by_index_or_name(c, PA_INVALID_INDEX, module_name, parameter, cb, userdata);
+    return o;
+}
+
+pa_operation* pa_context_get_module_parameter_by_index(pa_context *c, uint32_t idx, const char *parameter, pa_context_string_cb_t cb, void *userdata) {
+    pa_operation *o;
+
+    o =  get_module_parameter_by_index_or_name(c, idx, NULL, parameter, cb, userdata);
+    return o;
+}
diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h
index 43389b73..764dbcf8 100644
--- a/src/pulse/introspect.h
+++ b/src/pulse/introspect.h
@@ -432,12 +432,27 @@ pa_operation* pa_context_get_module_info_list(pa_context *c, pa_module_info_cb_t
 /** Callback prototype for pa_context_load_module() */
 typedef void (*pa_context_index_cb_t)(pa_context *c, uint32_t idx, void *userdata);
 
+/** Callback prototype for pa_context_get_module_parameter() */
+typedef void (*pa_context_string_cb_t)(pa_context *c, const char *parameter, void *userdata);
+
 /** Load a module. */
 pa_operation* pa_context_load_module(pa_context *c, const char*name, const char *argument, pa_context_index_cb_t cb, void *userdata);
 
 /** Unload a module. */
 pa_operation* pa_context_unload_module(pa_context *c, uint32_t idx, pa_context_success_cb_t cb, void *userdata);
 
+/** Send parameter string to module by its index. */
+pa_operation* pa_context_set_module_parameter_by_index(pa_context *c, uint32_t idx, const char *parameter, pa_context_success_cb_t cb, void *userdata);
+
+/** Send parameter string to module by its name. */
+pa_operation* pa_context_set_module_parameter_by_name(pa_context *c, const char *module_name, const char *parameter, pa_context_success_cb_t cb, void *userdata);
+
+/** Get parameter value from module by its index. */
+pa_operation* pa_context_get_module_parameter_by_index(pa_context *c, uint32_t idx, const char *parameter, pa_context_string_cb_t cb, void *userdata);
+
+/** Get parameter value from module by its name. */
+pa_operation* pa_context_get_module_parameter_by_name(pa_context *c, const char *module_name, const char *parameter, pa_context_string_cb_t cb, void *userdata);
+
 /** @} */
 
 /** @{ \name Clients */
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
index ac158159..261c6395 100644
--- a/src/pulsecore/module.c
+++ b/src/pulsecore/module.c
@@ -171,6 +171,8 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
     m->done = (void (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_DONE);
     m->get_n_used = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_N_USED);
     m->userdata = NULL;
+    m->set_parameter_callback = NULL;
+    m->get_parameter_callback = NULL;
     m->core = c;
     m->unload_requested = false;
 
diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h
index 41e2189c..ff558c86 100644
--- a/src/pulsecore/module.h
+++ b/src/pulsecore/module.h
@@ -29,6 +29,7 @@ typedef struct pa_module pa_module;
 #include <pulsecore/dynarray.h>
 
 #include <pulsecore/core.h>
+#include <pulsecore/tagstruct.h>
 
 struct pa_module {
     pa_core *core;
@@ -41,6 +42,11 @@ struct pa_module {
     void (*done)(pa_module*m);
     int (*get_n_used)(pa_module *m);
 
+    /* Functions used for changing and querying parameters during run time.
+     * May be NULL. */
+    int (*set_parameter_callback)(pa_module *m, const char *parameters);
+    int (*get_parameter_callback)(pa_module *m, pa_tagstruct *t, const char *parameter);
+
     void *userdata;
 
     bool load_once:1;
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index 70338b9f..95a9cfc5 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -187,6 +187,9 @@ enum {
      * BOTH DIRECTIONS */
     PA_COMMAND_REGISTER_MEMFD_SHMID,
 
+    PA_COMMAND_SET_MODULE_PARAMETER,
+    PA_COMMAND_GET_MODULE_PARAMETER,
+
     PA_COMMAND_MAX
 };
 
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index ab632a5a..f9423f19 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -199,6 +199,9 @@ static const char *command_names[PA_COMMAND_MAX] = {
     /* Supported since protocol v31 (9.0) */
     /* BOTH DIRECTIONS */
     [PA_COMMAND_REGISTER_MEMFD_SHMID] = "REGISTER_MEMFD_SHMID",
+
+    [PA_COMMAND_SET_MODULE_PARAMETER] = "SET_MODULE_PARAMETER",
+    [PA_COMMAND_GET_MODULE_PARAMETER] = "GET_MODULE_PARAMETER",
 };
 
 #endif
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 266b676d..d13dfd1a 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -4648,6 +4648,117 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa
     pa_pstream_send_simple_ack(c->pstream, tag);
 }
 
+/* Find a unique instance of a module. If more than one instance
+ * is loaded, it is considered an error. */
+static int find_module_by_name_or_index(pa_native_connection *c, pa_module **m, uint32_t idx, const char *name) {
+    pa_module *mod_search;
+
+    *m = NULL;
+
+    if (idx != PA_INVALID_INDEX)
+        *m = pa_idxset_get_by_index(c->protocol->core->modules, idx);
+    else {
+        pa_module *mod = NULL;
+
+        PA_IDXSET_FOREACH(mod_search, c->protocol->core->modules, idx) {
+            if (pa_streq(name, mod_search->name)) {
+                if (!mod)
+                    mod = mod_search;
+                else {
+                    *m = NULL;
+                    return -1;
+                }
+            }
+        }
+        *m = mod;
+    }
+    return 0;
+}
+
+/* Send parameter string to module. */
+static void command_set_module_parameter(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 = PA_INVALID_INDEX;
+    const char *name = NULL;
+    const char *parameter_string = NULL;
+    pa_module *m;
+    int ret;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &parameter_string) ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !name || pa_utf8_valid(name), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
+
+    if (find_module_by_name_or_index(c, &m, idx, name) < 0) {
+       pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+       return;
+    }
+
+    CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, m->set_parameter_callback, tag, PA_ERR_NOTSUPPORTED);
+
+
+    if ((ret = m->set_parameter_callback(m, parameter_string)) < 0) {
+        pa_pstream_send_error(c->pstream, tag, -ret);
+        return;
+    }
+
+    pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+/* Query parameter value from module. Result must be returned as string. */
+static void command_get_module_parameter(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 = PA_INVALID_INDEX;
+    const char *name = NULL;
+    const char *parameter_name = NULL;
+    pa_module *m;
+    pa_tagstruct *reply;
+    int ret;
+
+    pa_native_connection_assert_ref(c);
+    pa_assert(t);
+
+    if (pa_tagstruct_getu32(t, &idx) < 0 ||
+        pa_tagstruct_gets(t, &name) < 0 ||
+        pa_tagstruct_gets(t, &parameter_name) ||
+        !pa_tagstruct_eof(t)) {
+        protocol_error(c);
+        return;
+    }
+
+    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+    CHECK_VALIDITY(c->pstream, !name || pa_utf8_valid(name), tag, PA_ERR_INVALID);
+    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
+
+    if (find_module_by_name_or_index(c, &m, idx, name) < 0) {
+       pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+       return;
+    }
+
+    CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOENTITY);
+    CHECK_VALIDITY(c->pstream, m->get_parameter_callback, tag, PA_ERR_NOTSUPPORTED);
+
+    reply = reply_new(tag);
+
+    if ((ret = m->get_parameter_callback(m, reply, parameter_name)) < 0) {
+        pa_pstream_send_error(c->pstream, tag, -ret);
+        return;
+    }
+
+    pa_pstream_send_tagstruct(c->pstream, reply);
+}
+
 static void command_extension(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 = PA_INVALID_INDEX;
@@ -4936,6 +5047,9 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
 
     [PA_COMMAND_REGISTER_MEMFD_SHMID] = command_register_memfd_shmid,
 
+    [PA_COMMAND_SET_MODULE_PARAMETER] = command_set_module_parameter,
+    [PA_COMMAND_GET_MODULE_PARAMETER] = command_get_module_parameter,
+
     [PA_COMMAND_EXTENSION] = command_extension
 };
 
diff --git a/src/utils/pactl.c b/src/utils/pactl.c
index e9bf005b..3988374b 100644
--- a/src/utils/pactl.c
+++ b/src/utils/pactl.c
@@ -52,6 +52,7 @@ static char
     *sink_name = NULL,
     *source_name = NULL,
     *module_name = NULL,
+    *module_parameter = NULL,
     *module_args = NULL,
     *card_name = NULL,
     *profile_name = NULL,
@@ -130,6 +131,8 @@ static enum {
     SET_SOURCE_OUTPUT_MUTE,
     SET_SINK_FORMATS,
     SET_PORT_LATENCY_OFFSET,
+    SET_MODULE_PARAMETER,
+    GET_MODULE_PARAMETER,
     SUBSCRIBE
 } action = NONE;
 
@@ -834,6 +837,18 @@ static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
     complete_action();
 }
 
+static void string_callback(pa_context *c, const char *parameter, void *userdata) {
+    if (!parameter) {
+        pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
+        quit(1);
+        return;
+    }
+
+    printf("%s\n", parameter);
+
+    complete_action();
+}
+
 static void volume_relative_adjust(pa_cvolume *cv) {
     pa_assert(volume_flags & VOL_RELATIVE);
 
@@ -1404,6 +1419,20 @@ static void context_state_callback(pa_context *c, void *userdata) {
                     o = pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL);
                     break;
 
+                case SET_MODULE_PARAMETER:
+                    if (module_name)
+                        o=pa_context_set_module_parameter_by_name(c, module_name, module_parameter, simple_callback, NULL);
+                    else
+                        o=pa_context_set_module_parameter_by_index(c, module_index, module_parameter, simple_callback, NULL);
+                    break;
+
+                case GET_MODULE_PARAMETER:
+                    if (module_name)
+                        o=pa_context_get_module_parameter_by_name(c, module_name, module_parameter, string_callback, NULL);
+                    else
+                        o=pa_context_get_module_parameter_by_index(c, module_index, module_parameter, string_callback, NULL);
+                    break;
+
                 case SUBSCRIBE:
                     pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
 
@@ -1580,6 +1609,8 @@ static void help(const char *argv0) {
     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-mute", _("#N 1|0|toggle"));
     printf("%s %s %s %s\n", argv0, _("[options]"), "set-sink-formats", _("#N FORMATS"));
     printf("%s %s %s %s\n", argv0, _("[options]"), "set-port-latency-offset", _("CARD-NAME|CARD-#N PORT OFFSET"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "set-module-parameter", _("MODULE-NAME|MODULE-#N PARAMETER"));
+    printf("%s %s %s %s\n", argv0, _("[options]"), "get-module-parameter", _("MODULE-NAME|MODULE-#N PARAMETER"));
     printf("%s %s %s\n",    argv0, _("[options]"), "subscribe");
     printf(_("\nThe special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
              "can be used to specify the default sink, source and monitor.\n"));
@@ -1799,6 +1830,30 @@ int main(int argc, char *argv[]) {
             if (pa_atou(argv[optind + 1], &module_index) < 0)
                 module_name = argv[optind + 1];
 
+        } else if (pa_streq(argv[optind], "set-module-parameter")) {
+            action = SET_MODULE_PARAMETER;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a module index or name and a parameter string"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind + 1], &module_index) < 0)
+                module_name = argv[optind + 1];
+            module_parameter = argv[optind + 2];
+
+        } else if (pa_streq(argv[optind], "get-module-parameter")) {
+            action = GET_MODULE_PARAMETER;
+
+            if (argc != optind+3) {
+                pa_log(_("You have to specify a module index or name and a parameter name"));
+                goto quit;
+            }
+
+            if (pa_atou(argv[optind + 1], &module_index) < 0)
+                module_name = argv[optind + 1];
+            module_parameter = argv[optind + 2];
+
         } else if (pa_streq(argv[optind], "suspend-sink")) {
             int b;
 
-- 
2.11.0



More information about the pulseaudio-discuss mailing list