[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