[pulseaudio-discuss] [PATCH] added two new commands to native API to control the combine sink slaves after the combine sink has been created

Steffen Pfendtner steffen at pfendtner.de
Thu Jan 5 19:13:26 UTC 2017


---
 src/modules/module-combine-sink.c | 92 +++++++++++++++++++++++++++++++++++++++
 src/pulsecore/cli-command.c       | 78 +++++++++++++++++++++++++++++++++
 src/pulsecore/sink.c              | 32 ++++++++++++++
 src/pulsecore/sink.h              |  8 ++++
 4 files changed, 210 insertions(+)

diff --git a/src/modules/module-combine-sink.c b/src/modules/module-combine-sink.c
index b6322c6..4a91901 100644
--- a/src/modules/module-combine-sink.c
+++ b/src/modules/module-combine-sink.c
@@ -1249,6 +1249,95 @@ static pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struc
     return PA_HOOK_OK;
 }
 
+void update_slaves_prop(pa_sink *combine_sink, struct userdata *u) {
+
+    uint32_t idx;
+    char *t;
+    bool first = true;
+    struct output *o;
+    pa_proplist *pl;
+
+    PA_IDXSET_FOREACH(o, u->outputs, idx) {
+        char *e;
+        if (first) {
+            e = pa_sprintf_malloc("%s", pa_strnull(o->sink->name));
+            first = false;
+        } else {
+            e = pa_sprintf_malloc("%s, %s", t, pa_strnull(o->sink->name));
+            pa_xfree(t);
+        }
+        t = e;
+    }
+    /* if still first we have no outputs in the list */
+    if (first) {
+        t = pa_sprintf_malloc("");
+    }
+
+    pl = pa_proplist_new();
+    pa_proplist_setf(pl, "combine.slaves", t);
+    pa_xfree(t);
+    pa_sink_update_proplist(combine_sink, PA_UPDATE_REPLACE, pl);
+    pa_proplist_free(pl);
+}
+
+/* Called from main context on native API call */
+static int add_output(pa_sink *combine_sink, pa_sink *slave_sink) {
+    struct userdata *u;
+    struct output *o;
+
+    pa_sink_assert_ref(combine_sink);
+    pa_sink_assert_ref(slave_sink);
+    pa_assert_se(u = combine_sink->userdata);
+
+    if (u->automatic) {
+        pa_log("combine sink is automatic, cannot add: '%s'.", slave_sink->name);
+        return -2;
+    }
+
+    if ((o = find_output(u, slave_sink))) {
+        pa_log("Sink already on combine sink: '%s'.", slave_sink->name);
+        return -3;
+    }
+
+    if (!(o = output_new(u, slave_sink))) {
+        pa_log("Failed to add sink to combine sink: '%s'.", slave_sink->name);
+        return -4;
+    }
+    output_verify(o);
+    update_slaves_prop(combine_sink, u);
+
+    pa_log("Add output sink");
+    return 0;
+}
+
+/* Called from main context on native API call */
+static int del_output(pa_sink *combine_sink, pa_sink *slave_sink) {
+    struct userdata *u;
+    struct output *o;
+
+    pa_sink_assert_ref(combine_sink);
+    pa_sink_assert_ref(slave_sink);
+    pa_assert_se(u = combine_sink->userdata);
+
+    if (u->automatic) {
+        pa_log("combine sink is automatic, cannot del: '%s'.", slave_sink->name);
+        return -2;
+    }
+
+    if (!(o = find_output(u, slave_sink))) {
+        pa_log("Could not remove %s from combine sink, output was not found",
+            slave_sink->name);
+        return -3;
+    }
+
+    pa_idxset_remove_by_data(u->outputs, o, NULL);
+    output_free(o);
+    update_description(u);
+    update_slaves_prop(combine_sink, u);
+    pa_log("Del output sink");
+    return 0;
+}
+
 int pa__init(pa_module*m) {
     struct userdata *u;
     pa_modargs *ma = NULL;
@@ -1401,6 +1490,9 @@ int pa__init(pa_module*m) {
     u->sink->set_state = sink_set_state;
     u->sink->update_requested_latency = sink_update_requested_latency;
     u->sink->userdata = u;
+    u->sink->module = m;
+    u->sink->combine_add_output = add_output;
+    u->sink->combine_del_output = del_output;
 
     pa_sink_set_rtpoll(u->sink, u->rtpoll);
     pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 9a73605..ba168d5 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -135,6 +135,8 @@ static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
 static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
 static int pa_cli_command_port_offset(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
 static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_combine_sink_add_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
+static int pa_cli_command_combine_sink_del_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail);
 
 /* A method table for all available commands */
 
@@ -191,6 +193,8 @@ static const struct command commands[] = {
     { "set-log-meta",            pa_cli_command_log_meta,           "Show source code location in log messages (args: bool)", 2},
     { "set-log-time",            pa_cli_command_log_time,           "Show timestamps in log messages (args: bool)", 2},
     { "set-log-backtrace",       pa_cli_command_log_backtrace,      "Show backtrace in log messages (args: frames)", 2},
+    { "combine-sink-add-output", pa_cli_command_combine_sink_add_output, "Add a output sink to the specified combine sink (args: ?, ?", 3},
+    { "combine-sink-del-output", pa_cli_command_combine_sink_del_output, "Delete a output sink from the specified combine sink (args: ?, ?", 3},
     { "play-file",               pa_cli_command_play_file,          "Play a sound file (args: filename, sink|index)", 3},
     { "dump",                    pa_cli_command_dump,               "Dump daemon configuration", 1},
     { "dump-volumes",            pa_cli_command_dump_volumes,       "Debug: Show the state of all volumes", 1 },
@@ -1992,6 +1996,80 @@ static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *b
     return 0;
 }
 
+int pa_cli_command_combine_sink_add_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *cs, *os;
+    pa_sink *combine_sink, *out_sink;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(cs = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a combine sink either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(os = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify an output sink either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(combine_sink = pa_namereg_get(c, cs, PA_NAMEREG_SINK))) {
+        pa_strbuf_puts(buf, "No combine sink found by this name or index.\n");
+        return -1;
+    }
+
+    if (!(out_sink = pa_namereg_get(c, os, PA_NAMEREG_SINK))) {
+        pa_strbuf_puts(buf, "No output sink found by this name or index.\n");
+        return -1;
+    }
+
+    if (pa_sink_combine_add_output(combine_sink, out_sink) < 0) {
+        pa_strbuf_puts(buf, "Add to combine sink failed!\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+int pa_cli_command_combine_sink_del_output(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) {
+    const char *cs, *os;
+    pa_sink *combine_sink, *out_sink;
+
+    pa_core_assert_ref(c);
+    pa_assert(t);
+    pa_assert(buf);
+    pa_assert(fail);
+
+    if (!(cs = pa_tokenizer_get(t, 1))) {
+        pa_strbuf_puts(buf, "You need to specify a combine sink either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(os = pa_tokenizer_get(t, 2))) {
+        pa_strbuf_puts(buf, "You need to specify an output sink either by its name or its index.\n");
+        return -1;
+    }
+
+    if (!(combine_sink = pa_namereg_get(c, cs, PA_NAMEREG_SINK))) {
+        pa_strbuf_puts(buf, "No combine sink found by this name or index.\n");
+        return -1;
+    }
+
+    if (!(out_sink = pa_namereg_get(c, os, PA_NAMEREG_SINK))) {
+        pa_strbuf_puts(buf, "No output sink found by this name or index.\n");
+        return -1;
+    }
+
+    if (pa_sink_combine_del_output(combine_sink, out_sink) < 0) {
+        pa_strbuf_puts(buf, "Delete from combine sink failed!\n");
+        return -1;
+    }
+
+    return 0;
+}
+
 int pa_cli_command_execute_line_stateful(pa_core *c, const char *s, pa_strbuf *buf, bool *fail, int *ifstate) {
     const char *cs;
 
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 5c6a9c6..6379504 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -3835,3 +3835,35 @@ void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume) {
     pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
     pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_VOLUME_CHANGED], s);
 }
+
+int pa_sink_combine_add_output(pa_sink *combine_sink, pa_sink *slave_sink) {
+    int r;
+
+    pa_assert_ctl_context();
+    pa_assert(combine_sink);
+    pa_assert(slave_sink);
+
+    if (combine_sink->combine_add_output != NULL) {
+        r = combine_sink->combine_add_output(combine_sink, slave_sink);
+        return r;
+    } else {
+        return -1;
+    }
+
+    return 0;
+}
+
+int pa_sink_combine_del_output(pa_sink *combine_sink, pa_sink *slave_sink) {
+    int r;
+
+    pa_assert_ctl_context();
+    pa_assert(combine_sink);
+    pa_assert(slave_sink);
+
+    if (combine_sink->combine_del_output != NULL) {
+        r = combine_sink->combine_del_output(combine_sink, slave_sink);
+        return r;
+    } else {
+        return -1;
+    }
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index c549869..756a0f6 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -248,6 +248,11 @@ struct pa_sink {
      * main thread. */
     int (*update_rate)(pa_sink *s, uint32_t rate);
 
+    /* Called for manual combine sink change. Must not be NULL for a combine
+     * sink. Can be NULL for all other sink types. */
+    int (*combine_add_output)(pa_sink *combine_sink, pa_sink *slave_sink);
+    int (*combine_del_output)(pa_sink *combine_sink, pa_sink *slave_sink);
+
     /* Contains copies of the above data so that the real-time worker
      * thread can work without access locking */
     struct {
@@ -528,6 +533,9 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s);
  * s->reference_volume and fires change notifications. */
 void pa_sink_set_reference_volume_direct(pa_sink *s, const pa_cvolume *volume);
 
+int pa_sink_combine_add_output(pa_sink *combine_sink, pa_sink *slave_sink);
+int pa_sink_combine_del_output(pa_sink *combine_sink, pa_sink *slave_sink);
+
 /* Verify that we called in IO context (aka 'thread context), or that
  * the sink is not yet set up, i.e. the thread not set up yet. See
  * pa_assert_io_context() in thread-mq.h for more information. */
-- 
1.9.1



More information about the pulseaudio-discuss mailing list