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