From f9237eb0dbcf0a45f212446a12d8033d1c154d0e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 7 Feb 2018 11:52:55 +0100 Subject: [PATCH] control: keep track of linked controls Keep track of what controls are linked together. Clean up links on destroy. Implement unlink, reset io area. Add events for control link/unlink --- src/pipewire/control.c | 87 ++++++++++++++++++++++++++++++++++++------ src/pipewire/control.h | 6 +++ src/pipewire/port.c | 6 +++ src/pipewire/private.h | 5 +++ 4 files changed, 93 insertions(+), 11 deletions(-) diff --git a/src/pipewire/control.c b/src/pipewire/control.c index 45e7def4f..c37bfac85 100644 --- a/src/pipewire/control.c +++ b/src/pipewire/control.c @@ -60,6 +60,8 @@ pw_control_new(struct pw_core *core, this->param = pw_spa_pod_copy(param); this->direction = direction; + spa_list_init(&this->inputs); + if (user_data_size > 0) this->user_data = SPA_MEMBER(impl, sizeof(struct impl), void); @@ -83,11 +85,21 @@ 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; pw_log_debug("control %p: destroy", control); spa_hook_list_call(&control->listener_list, struct pw_control_events, destroy); + if (control->direction == SPA_DIRECTION_OUTPUT) { + spa_list_for_each(other, &control->inputs, inputs_link) + pw_control_unlink(control, other); + } + else { + if (control->output) + pw_control_unlink(control->output, control); + } + spa_list_remove(&control->link); if (control->port) { @@ -99,9 +111,10 @@ void pw_control_destroy(struct pw_control *control) pw_log_debug("control %p: free", control); spa_hook_list_call(&control->listener_list, struct pw_control_events, free); - if (control->direction == SPA_DIRECTION_OUTPUT) + if (control->direction == SPA_DIRECTION_OUTPUT) { if (impl->mem) pw_memblock_free(impl->mem); + } free(control->param); @@ -135,6 +148,10 @@ int pw_control_link(struct pw_control *control, struct pw_control *other) 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", control, other); @@ -147,15 +164,6 @@ int pw_control_link(struct pw_control *control, struct pw_control *other) &impl->mem)) < 0) goto exit; - 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, control->size)) < 0) { - goto exit; - } - } } if (other->port) { @@ -168,11 +176,68 @@ int pw_control_link(struct pw_control *control, struct pw_control *other) } } + 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, control->size)) < 0) { + goto exit; + } + } + } + + other->output = control; + spa_list_append(&control->inputs, &other->inputs_link); + + spa_hook_list_call(&control->listener_list, struct pw_control_events, linked, other); + exit: return res; } int pw_control_unlink(struct pw_control *control, struct pw_control *other) { - return -ENOTSUP; + int res = 0; + + 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; + + if (other->output != control) + return -EINVAL; + + pw_log_debug("control %p: unlink from %p", control, other); + + 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) { + 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, NULL, 0)) < 0) { + goto exit; + } + } + + spa_hook_list_call(&control->listener_list, struct pw_control_events, unlinked, other); + + exit: + return res; } diff --git a/src/pipewire/control.h b/src/pipewire/control.h index f3b5b13ba..b3e46386d 100644 --- a/src/pipewire/control.h +++ b/src/pipewire/control.h @@ -55,6 +55,12 @@ struct pw_control_events { /** The control is freed */ void (*free) (void *data); + + /** control is linked to another control */ + void (*linked) (void *data, struct pw_control *other); + /** control is unlinked from another control */ + void (*unlinked) (void *data, struct pw_control *other); + }; /** Get the control parent port or NULL when not set */ diff --git a/src/pipewire/port.c b/src/pipewire/port.c index 1c20b304a..ac47d59ff 100644 --- a/src/pipewire/port.c +++ b/src/pipewire/port.c @@ -352,6 +352,7 @@ static int do_remove_port(struct spa_loop *loop, void pw_port_destroy(struct pw_port *port) { struct pw_node *node = port->node; + struct pw_control *control, *ctemp; pw_log_debug("port %p: destroy", port); @@ -374,6 +375,11 @@ void pw_port_destroy(struct pw_port *port) spa_hook_list_call(&node->listener_list, struct pw_node_events, port_removed, port); } + spa_list_for_each_safe(control, ctemp, &port->control_list[0], port_link) + pw_control_destroy(control); + spa_list_for_each_safe(control, ctemp, &port->control_list[1], port_link) + pw_control_destroy(control); + pw_log_debug("port %p: free", port); spa_hook_list_call(&port->listener_list, struct pw_port_events, free); diff --git a/src/pipewire/private.h b/src/pipewire/private.h index d840588a9..1e920004b 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -418,6 +418,11 @@ struct pw_control { enum spa_direction direction; /**< the direction */ struct spa_pod *param; /**< control params */ + 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 */ + uint32_t id; int32_t size;