mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-03 09:01:54 -05:00
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.
This commit is contained in:
parent
7b3e7e2813
commit
24117b33c7
8 changed files with 131 additions and 106 deletions
|
|
@ -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; \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue