From 24117b33c721888f16fd13fc5091930a3e19ad68 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 14 Mar 2019 12:04:20 +0100 Subject: [PATCH] control: make control links many to many Output controls can be linked to many input controls and many input controls can receive input from many output controls. Keep the control link information inside the link. --- spa/include/spa/utils/defs.h | 6 + src/modules/module-client-node/client-node.c | 4 +- .../module-client-node/client-stream.c | 13 +- src/pipewire/control.c | 141 +++++++++--------- src/pipewire/control.h | 3 - src/pipewire/link.c | 43 +++--- src/pipewire/private.h | 26 +++- src/tools/pipewire-monitor.c | 1 + 8 files changed, 131 insertions(+), 106 deletions(-) diff --git a/spa/include/spa/utils/defs.h b/spa/include/spa/utils/defs.h index 58d6a9151..4654d4a10 100644 --- a/spa/include/spa/utils/defs.h +++ b/spa/include/spa/utils/defs.h @@ -107,6 +107,12 @@ struct spa_param_info { SPA_MIN(SPA_MAX(_v, _low), _high); \ }) +#define SPA_SWAP(a,b) \ +({ \ + __typeof__(a) _t = (a); \ + a = b; b = _t; \ +}) + #define SPA_TYPECHECK(type,x) \ ({ type _dummy; \ typeof(x) _dummy2; \ diff --git a/src/modules/module-client-node/client-node.c b/src/modules/module-client-node/client-node.c index 3b2973802..b00eb7f6a 100644 --- a/src/modules/module-client-node/client-node.c +++ b/src/modules/module-client-node/client-node.c @@ -792,10 +792,10 @@ impl_node_port_set_io(struct spa_node *node, uint32_t id, void *data, size_t size) { - /* ignore io on the node itself, weonly care about the io on the + /* ignore io on the node itself, we only care about the io on the * port mixers, the io on the node ports itself is handled on the * client side */ - return 0; + return -EINVAL; } static int diff --git a/src/modules/module-client-node/client-stream.c b/src/modules/module-client-node/client-stream.c index 7a02fa7e4..87062ad95 100644 --- a/src/modules/module-client-node/client-stream.c +++ b/src/modules/module-client-node/client-stream.c @@ -106,6 +106,9 @@ struct impl { struct spa_buffer **buffers; uint32_t n_buffers; struct pw_memblock *mem; + + struct pw_control_link control; + struct pw_control_link notify; }; /** \endcond */ @@ -187,18 +190,19 @@ static void try_link_controls(struct impl *impl) port = impl->client_port; - pw_log_debug(NAME " %p: trying controls", impl); spa_list_for_each(cout, &port->control_list[SPA_DIRECTION_OUTPUT], port_link) { spa_list_for_each(cin, &target->control_list[SPA_DIRECTION_INPUT], port_link) { - if ((res = pw_control_link(cout, cin)) < 0) + if ((res = pw_control_add_link(cout, 0, cin, 0, &impl->control)) < 0) pw_log_error("failed to link controls: %s", spa_strerror(res)); + break; } } spa_list_for_each(cin, &port->control_list[SPA_DIRECTION_INPUT], port_link) { spa_list_for_each(cout, &target->control_list[SPA_DIRECTION_OUTPUT], port_link) { - if ((res = pw_control_link(cout, cin)) < 0) + if ((res = pw_control_add_link(cout, 0, cin, 0, &impl->notify)) < 0) pw_log_error("failed to link controls: %s", spa_strerror(res)); + break; } } } @@ -868,7 +872,8 @@ static int impl_node_process(struct spa_node *node) if (impl->direction == SPA_DIRECTION_OUTPUT) { if (!(status & SPA_STATUS_HAVE_BUFFER)) - spa_log_warn(this->log, "%p: process underrun", this); + spa_log_warn(this->log, "%p: '%s' id:%d underrun", + this, impl->this.node->info.name, impl->this.node->info.id); trigger = status & SPA_STATUS_NEED_BUFFER; } else diff --git a/src/pipewire/control.c b/src/pipewire/control.c index c9933079c..688a7a4f8 100644 --- a/src/pipewire/control.c +++ b/src/pipewire/control.c @@ -70,7 +70,7 @@ pw_control_new(struct pw_core *core, this->port = port; this->direction = direction; - spa_list_init(&this->inputs); + spa_list_init(&this->links); if (user_data_size > 0) this->user_data = SPA_MEMBER(impl, sizeof(struct impl), void); @@ -92,19 +92,19 @@ pw_control_new(struct pw_core *core, void pw_control_destroy(struct pw_control *control) { struct impl *impl = SPA_CONTAINER_OF(control, struct impl, this); - struct pw_control *other, *tmp; + struct pw_control_link *link, *tmp; pw_log_debug("control %p: destroy", control); pw_control_emit_destroy(control); if (control->direction == SPA_DIRECTION_OUTPUT) { - spa_list_for_each_safe(other, tmp, &control->inputs, inputs_link) - pw_control_unlink(control, other); + spa_list_for_each_safe(link, tmp, &control->links, out_link) + pw_control_remove_link(link); } else { - if (control->output) - pw_control_unlink(control->output, control); + spa_list_for_each_safe(link, tmp, &control->links, in_link) + pw_control_remove_link(link); } spa_list_remove(&control->link); @@ -139,26 +139,45 @@ void pw_control_add_listener(struct pw_control *control, spa_hook_list_append(&control->listener_list, listener, events, data); } +static int port_set_io(struct pw_port *port, uint32_t mix, uint32_t id, void *data, uint32_t size) +{ + struct spa_node *n; + uint32_t p; + int res; + + if (port->mix && port->mix->port_set_io) { + n = port->mix; + p = mix; + } else { + n = port->node->node; + p = port->port_id; + } + if ((res = spa_node_port_set_io(n, + port->direction, p, + id, data, size)) < 0) { + pw_log_warn("port %p: set io failed %d %s", port, + res, spa_strerror(res)); + } + return res; +} + SPA_EXPORT -int pw_control_link(struct pw_control *control, struct pw_control *other) +int pw_control_add_link(struct pw_control *control, uint32_t cmix, + struct pw_control *other, uint32_t omix, + struct pw_control_link *link) { int res = 0; struct impl *impl; uint32_t size; if (control->direction == SPA_DIRECTION_INPUT) { - struct pw_control *tmp = control; - control = other; - other = tmp; + SPA_SWAP(control, other); + SPA_SWAP(cmix, omix); } if (control->direction != SPA_DIRECTION_OUTPUT || other->direction != SPA_DIRECTION_INPUT) return -EINVAL; - /* input control already has a linked output control */ - if (other->output != NULL) - return -EEXIST; - impl = SPA_CONTAINER_OF(control, struct impl, this); pw_log_debug("control %p: link to %p %s", control, other, @@ -173,43 +192,36 @@ int pw_control_link(struct pw_control *control, struct pw_control *other) size, &impl->mem)) < 0) goto exit; + } + if (spa_list_is_empty(&control->links)) { + if (control->port) { + if ((res = port_set_io(control->port, cmix, + control->id, + impl->mem->ptr, size)) < 0) { + pw_log_warn("control %p: set io failed %d %s", control, + res, spa_strerror(res)); + goto exit; + } + } } if (other->port) { - struct pw_port *port = other->port; - if ((res = spa_node_port_set_io(port->node->node, - port->direction, port->port_id, - other->id, - impl->mem->ptr, size)) < 0) { + if ((res = port_set_io(other->port, omix, + other->id, impl->mem->ptr, size)) < 0) { pw_log_warn("control %p: set io failed %d %s", control, res, spa_strerror(res)); goto exit; } } - if (spa_list_is_empty(&control->inputs)) { - if (control->port) { - struct pw_port *port = control->port; - if ((res = spa_node_port_set_io(port->node->node, - port->direction, port->port_id, - control->id, - impl->mem->ptr, size)) < 0) { - pw_log_warn("control %p: set io failed %d %s", control, - res, spa_strerror(res)); - /* undo */ - port = other->port; - spa_node_port_set_io(port->node->node, - port->direction, port->port_id, - other->id, - NULL, 0); - goto exit; - } - } - } - - other->output = control; - spa_list_append(&control->inputs, &other->inputs_link); + link->output = control; + link->input = other; + link->out_port = cmix; + link->in_port = omix; + link->valid = true; + spa_list_append(&control->links, &link->out_link); + spa_list_append(&other->links, &link->in_link); pw_control_emit_linked(control, other); pw_control_emit_linked(other, control); @@ -219,47 +231,34 @@ int pw_control_link(struct pw_control *control, struct pw_control *other) } SPA_EXPORT -int pw_control_unlink(struct pw_control *control, struct pw_control *other) +int pw_control_remove_link(struct pw_control_link *link) { int res = 0; + struct pw_control *output = link->output; + struct pw_control *input = link->input; - pw_log_debug("control %p: unlink from %p", control, other); + pw_log_debug("control %p: unlink from %p", output, input); - if (control->direction == SPA_DIRECTION_INPUT) { - struct pw_control *tmp = control; - control = other; - other = tmp; - } - if (control->direction != SPA_DIRECTION_OUTPUT || - other->direction != SPA_DIRECTION_INPUT) - return -EINVAL; + spa_list_remove(&link->in_link); + spa_list_remove(&link->out_link); + link->valid = false; - if (other->output != control) - return -EINVAL; - - other->output = NULL; - spa_list_remove(&other->inputs_link); - - if (spa_list_is_empty(&control->inputs)) { - struct pw_port *port = control->port; - if ((res = spa_node_port_set_io(port->node->node, - port->direction, port->port_id, - control->id, NULL, 0)) < 0) { - pw_log_warn("control %p: can't unset port control io", control); + if (spa_list_is_empty(&output->links)) { + if ((res = port_set_io(output->port, link->out_port, + output->id, NULL, 0)) < 0) { + pw_log_warn("control %p: can't unset port control io", output); } } - if (other->port) { - struct pw_port *port = other->port; - if ((res = spa_node_port_set_io(port->node->node, - port->direction, port->port_id, - other->id, NULL, 0)) < 0) { - pw_log_warn("control %p: can't unset port control io", control); + if (input->port) { + if ((res = port_set_io(input->port, link->in_port, + input->id, NULL, 0)) < 0) { + pw_log_warn("control %p: can't unset port control io", output); } } - pw_control_emit_unlinked(control, other); - pw_control_emit_unlinked(other, control); + pw_control_emit_unlinked(output, input); + pw_control_emit_unlinked(input, output); return res; } diff --git a/src/pipewire/control.h b/src/pipewire/control.h index aae79d930..26e5be98e 100644 --- a/src/pipewire/control.h +++ b/src/pipewire/control.h @@ -74,9 +74,6 @@ void pw_control_add_listener(struct pw_control *control, const struct pw_control_events *events, void *data); -int pw_control_link(struct pw_control *control, struct pw_control *other); -int pw_control_unlink(struct pw_control *control, struct pw_control *other); - #ifdef __cplusplus } #endif diff --git a/src/pipewire/link.c b/src/pipewire/link.c index fbae8ecad..8729b68d0 100644 --- a/src/pipewire/link.c +++ b/src/pipewire/link.c @@ -1143,43 +1143,46 @@ static bool pw_node_can_reach(struct pw_node *output, struct pw_node *input) return false; } -static void try_link_controls(struct impl *impl, struct pw_port *port, struct pw_port *target) +static void try_link_controls(struct impl *impl, struct pw_port *output, struct pw_port *input) { struct pw_control *cin, *cout; + struct pw_link *this = &impl->this; + uint32_t omix, imix; int res; + imix = this->rt.in_mix.port.port_id; + omix = this->rt.out_mix.port.port_id; + pw_log_debug("link %p: trying controls", impl); - spa_list_for_each(cout, &port->control_list[SPA_DIRECTION_OUTPUT], port_link) { - spa_list_for_each(cin, &target->control_list[SPA_DIRECTION_INPUT], port_link) { - if ((res = pw_control_link(cout, cin)) < 0) + spa_list_for_each(cout, &output->control_list[SPA_DIRECTION_OUTPUT], port_link) { + spa_list_for_each(cin, &input->control_list[SPA_DIRECTION_INPUT], port_link) { + if ((res = pw_control_add_link(cout, omix, cin, imix, &this->control)) < 0) pw_log_error("failed to link controls: %s", spa_strerror(res)); + break; } } - spa_list_for_each(cin, &port->control_list[SPA_DIRECTION_INPUT], port_link) { - spa_list_for_each(cout, &target->control_list[SPA_DIRECTION_OUTPUT], port_link) { - if ((res = pw_control_link(cout, cin)) < 0) + spa_list_for_each(cin, &output->control_list[SPA_DIRECTION_INPUT], port_link) { + spa_list_for_each(cout, &input->control_list[SPA_DIRECTION_OUTPUT], port_link) { + if ((res = pw_control_add_link(cout, imix, cin, omix, &this->notify)) < 0) pw_log_error("failed to link controls: %s", spa_strerror(res)); + break; } } } -static void try_unlink_controls(struct impl *impl, struct pw_port *port, struct pw_port *target) +static void try_unlink_controls(struct impl *impl, struct pw_port *output, struct pw_port *input) { - struct pw_control *cin, *cout; + struct pw_link *this = &impl->this; int res; pw_log_debug("link %p: unlinking controls", impl); - spa_list_for_each(cout, &port->control_list[SPA_DIRECTION_OUTPUT], port_link) { - spa_list_for_each(cin, &target->control_list[SPA_DIRECTION_INPUT], port_link) { - if ((res = pw_control_unlink(cout, cin)) < 0) - pw_log_error("failed to unlink controls: %s", spa_strerror(res)); - } + if (this->control.valid) { + if ((res = pw_control_remove_link(&this->control)) < 0) + pw_log_error("failed to unlink controls: %s", spa_strerror(res)); } - spa_list_for_each(cin, &port->control_list[SPA_DIRECTION_INPUT], port_link) { - spa_list_for_each(cout, &target->control_list[SPA_DIRECTION_OUTPUT], port_link) { - if ((res = pw_control_unlink(cout, cin)) < 0) - pw_log_error("failed to unlink controls: %s", spa_strerror(res)); - } + if (this->notify.valid) { + if ((res = pw_control_remove_link(&this->notify)) < 0) + pw_log_error("failed to unlink controls: %s", spa_strerror(res)); } } @@ -1387,7 +1390,7 @@ void pw_link_destroy(struct pw_link *link) pw_node_emit_peer_removed(link->output->node, link->input->node); - try_unlink_controls(impl, link->input, link->output); + try_unlink_controls(impl, link->output, link->input); input_remove(link, link->input); output_remove(link, link->output); diff --git a/src/pipewire/private.h b/src/pipewire/private.h index 162ca4b02..304ae476c 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -510,6 +510,16 @@ struct pw_port { void *user_data; /**< extra user data */ }; +struct pw_control_link { + struct spa_list out_link; + struct spa_list in_link; + struct pw_control *output; + struct pw_control *input; + uint32_t out_port; + uint32_t in_port; + int valid:1; +}; + #define pw_link_emit(o,m,v,...) spa_hook_list_call(&o->listener_list, struct pw_link_events, m, v, ##__VA_ARGS__) #define pw_link_emit_destroy(l) pw_link_emit(l, destroy, 0) #define pw_link_emit_free(l) pw_link_emit(l, free, 0) @@ -535,6 +545,9 @@ struct pw_link { struct spa_hook_list listener_list; + struct pw_control_link control; + struct pw_control_link notify; + struct { struct pw_port_mix out_mix; /**< port added to the output mixer */ struct pw_port_mix in_mix; /**< port added to the input mixer */ @@ -690,11 +703,7 @@ struct pw_control { struct spa_list port_link; /**< link in port control_list */ enum spa_direction direction; /**< the direction */ - - struct pw_control *output; /**< pointer to linked output control */ - - struct spa_list inputs; /**< list of linked input controls */ - struct spa_list inputs_link; /**< link in linked input control */ + struct spa_list links; /**< list of pw_control_link */ uint32_t id; int32_t size; @@ -704,7 +713,6 @@ struct pw_control { void *user_data; }; - /** Find a good format between 2 ports */ int pw_core_find_format(struct pw_core *core, struct pw_port *output, @@ -835,6 +843,12 @@ pw_control_new(struct pw_core *core, uint32_t id, uint32_t size, size_t user_data_size /**< extra user data */); +int pw_control_add_link(struct pw_control *control, uint32_t cmix, + struct pw_control *other, uint32_t omix, + struct pw_control_link *link); + +int pw_control_remove_link(struct pw_control_link *link); + void pw_control_destroy(struct pw_control *control); /** \endcond */ diff --git a/src/tools/pipewire-monitor.c b/src/tools/pipewire-monitor.c index f39a88293..0dc410b72 100644 --- a/src/tools/pipewire-monitor.c +++ b/src/tools/pipewire-monitor.c @@ -162,6 +162,7 @@ static void print_params(struct proxy_data *data, char mark) printf("%c\tparams:\n", mark); spa_list_for_each(p, &data->param_list, link) { + printf("%c\t id:%u\n", mark, p->id); if (spa_pod_is_object_type(p->param, SPA_TYPE_OBJECT_Format)) spa_debug_format(10, NULL, p->param); else