diff --git a/spa/plugins/control/mixer.c b/spa/plugins/control/mixer.c index 9085da5c5..c19d82ebc 100644 --- a/spa/plugins/control/mixer.c +++ b/spa/plugins/control/mixer.c @@ -39,6 +39,8 @@ struct buffer { }; struct port { + struct spa_list link; + uint32_t direction; uint32_t id; @@ -48,7 +50,6 @@ struct port { struct spa_port_info info; struct spa_param_info params[8]; - unsigned int valid:1; unsigned int have_format:1; uint32_t types; @@ -57,6 +58,9 @@ struct port { uint32_t n_buffers; struct spa_list queue; + + struct spa_list mix_link; + bool active; }; struct impl { @@ -77,10 +81,10 @@ struct impl { struct spa_hook_list hooks; - uint32_t port_count; - uint32_t last_port; struct port *in_ports[MAX_PORTS]; struct port out_ports[1]; + struct spa_list port_list; + struct spa_list free_list; struct spa_pod_control *mix_ctrl[MAX_PORTS]; struct spa_pod_sequence *mix_seq[MAX_PORTS]; @@ -89,12 +93,13 @@ struct impl { unsigned int have_format:1; unsigned int started:1; + + struct spa_list mix_list; }; -#define PORT_VALID(p) ((p) != NULL && (p)->valid) #define CHECK_ANY_IN(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) == SPA_ID_INVALID) -#define CHECK_FREE_IN_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS && !PORT_VALID(this->in_ports[(p)])) -#define CHECK_IN_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS && PORT_VALID(this->in_ports[(p)])) +#define CHECK_FREE_IN_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS && this->in_ports[(p)] == NULL) +#define CHECK_IN_PORT(this,d,p) ((d) == SPA_DIRECTION_INPUT && (p) < MAX_PORTS && this->in_ports[(p)] != NULL) #define CHECK_OUT_PORT(this,d,p) ((d) == SPA_DIRECTION_OUTPUT && (p) == 0) #define CHECK_PORT(this,d,p) (CHECK_OUT_PORT(this,d,p) || CHECK_IN_PORT (this,d,p)) #define CHECK_PORT_ANY(this,d,p) (CHECK_ANY_IN(this,d,p) || CHECK_PORT(this,d,p)) @@ -180,7 +185,7 @@ static int impl_node_add_listener(void *object, { struct impl *this = object; struct spa_hook_list save; - uint32_t i; + struct port *port; spa_return_val_if_fail(this != NULL, -EINVAL); @@ -188,10 +193,8 @@ static int impl_node_add_listener(void *object, emit_node_info(this, true); emit_port_info(this, GET_OUT_PORT(this, 0), true); - for (i = 0; i < this->last_port; i++) { - if (PORT_VALID(this->in_ports[i])) - emit_port_info(this, GET_IN_PORT(this, i), true); - } + spa_list_for_each(port, &this->port_list, link) + emit_port_info(this, port, true); spa_hook_list_join(&this->hooks, &save); @@ -206,6 +209,18 @@ impl_node_set_callbacks(void *object, return 0; } +static struct port *get_free_port(struct impl *this) +{ + struct port *port; + if (!spa_list_is_empty(&this->free_list)) { + port = spa_list_first(&this->free_list, struct port, link); + spa_list_remove(&port->link); + } else { + port = calloc(1, sizeof(struct port)); + } + return port; +} + static int impl_node_add_port(void *object, enum spa_direction direction, uint32_t port_id, const struct spa_dict *props) { @@ -216,12 +231,8 @@ static int impl_node_add_port(void *object, enum spa_direction direction, uint32 spa_return_val_if_fail(CHECK_FREE_IN_PORT(this, direction, port_id), -EINVAL); port = GET_IN_PORT (this, port_id); - if (port == NULL) { - port = calloc(1, sizeof(struct port)); - if (port == NULL) - return -errno; - this->in_ports[port_id] = port; - } + if ((port = get_free_port(this)) == NULL) + return -errno; port->direction = direction; port->id = port_id; @@ -242,12 +253,10 @@ static int impl_node_add_port(void *object, enum spa_direction direction, uint32 port->info.params = port->params; port->info.n_params = 5; - this->port_count++; - if (this->last_port <= port_id) - this->last_port = port_id + 1; - port->valid = true; + this->in_ports[port_id] = port; + spa_list_append(&this->port_list, &port->link); - spa_log_debug(this->log, "%p: add port %d %d", this, port_id, this->last_port); + spa_log_debug(this->log, "%p: add port %d:%d", this, direction, port_id); emit_port_info(this, port, true); return 0; @@ -263,25 +272,17 @@ impl_node_remove_port(void *object, enum spa_direction direction, uint32_t port_ spa_return_val_if_fail(CHECK_IN_PORT(this, direction, port_id), -EINVAL); port = GET_IN_PORT (this, port_id); + this->in_ports[port_id] = NULL; + spa_list_remove(&port->link); - port->valid = false; - this->port_count--; if (port->have_format && this->have_format) { if (--this->n_formats == 0) this->have_format = false; } spa_memzero(port, sizeof(struct port)); + spa_list_append(&this->free_list, &port->link); - if (port_id + 1 == this->last_port) { - int i; - - for (i = this->last_port - 1; i >= 0; i--) - if (GET_IN_PORT (this, i)->valid) - break; - - this->last_port = i + 1; - } - spa_log_debug(this->log, "%p: remove port %d %d", this, port_id, this->last_port); + spa_log_debug(this->log, "%p: remove port %d:%d", this, direction, port_id); spa_node_emit_port_info(&this->hooks, direction, port_id, NULL); @@ -558,6 +559,7 @@ impl_node_port_use_buffers(void *object, } struct io_info { + struct impl *impl; struct port *port; void *data; size_t size; @@ -567,16 +569,28 @@ static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq, const void *data, size_t size, void *user_data) { struct io_info *info = user_data; - if (info->size >= sizeof(struct spa_io_async_buffers)) { - struct spa_io_async_buffers *ab = info->data; - info->port->io[0] = &ab->buffers[0]; - info->port->io[1] = &ab->buffers[1]; - } else if (info->size >= sizeof(struct spa_io_buffers)) { - info->port->io[0] = info->data; - info->port->io[1] = info->data; + struct port *port = info->port; + + if (info->data == NULL || info->size < sizeof(struct spa_io_buffers)) { + port->io[0] = NULL; + port->io[1] = NULL; + if (port->active) { + spa_list_remove(&port->mix_link); + port->active = false; + } } else { - info->port->io[0] = NULL; - info->port->io[1] = NULL; + if (info->size >= sizeof(struct spa_io_async_buffers)) { + struct spa_io_async_buffers *ab = info->data; + port->io[0] = &ab->buffers[port->direction]; + port->io[1] = &ab->buffers[port->direction^1]; + } else { + port->io[0] = info->data; + port->io[1] = info->data; + } + if (!port->active) { + spa_list_append(&info->impl->mix_list, &port->mix_link); + port->active = true; + } } return 0; } @@ -598,6 +612,7 @@ impl_node_port_set_io(void *object, spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL); port = GET_PORT(this, direction, port_id); + info.impl = this; info.port = port; info.data = data; info.size = size; @@ -682,14 +697,14 @@ static inline bool control_needs_conversion(struct port *port, uint32_t type) static int impl_node_process(void *object) { struct impl *this = object; - struct port *outport; + struct port *outport, *inport; struct spa_io_buffers *outio; - uint32_t n_seq, i; - struct spa_pod_sequence **seq; - struct spa_pod_control **ctrl; + uint32_t n_seq; + struct spa_pod_sequence **seq; + struct spa_pod_control **ctrl; struct spa_pod_builder builder; struct spa_pod_frame f; - struct buffer *outb; + struct buffer *outb; struct spa_data *d; uint32_t cycle = this->position->clock.cycle & 1; @@ -724,17 +739,10 @@ static int impl_node_process(void *object) n_seq = 0; /* collect all sequence pod on input ports */ - for (i = 0; i < this->last_port; i++) { - struct port *inport = GET_IN_PORT(this, i); - struct spa_io_buffers *inio = NULL; + spa_list_for_each(inport, &this->mix_list, mix_link) { + struct spa_io_buffers *inio = inport->io[cycle]; void *pod; - if (SPA_UNLIKELY(!PORT_VALID(inport) || (inio = inport->io[cycle]) == NULL)) { - spa_log_trace_fp(this->log, "%p: skip input idx:%d valid:%d io:%p/%p/%d", - this, i, PORT_VALID(inport), - inport->io[0], inport->io[1], cycle); - continue; - } if (inio->buffer_id >= inport->n_buffers || inio->status != SPA_STATUS_HAVE_DATA) { spa_log_trace_fp(this->log, "%p: skip input idx:%d " @@ -775,7 +783,7 @@ static int impl_node_process(void *object) /* merge sort all sequences into output buffer */ while (true) { struct spa_pod_control *next = NULL; - uint32_t next_index = 0; + uint32_t i, next_index = 0; for (i = 0; i < n_seq; i++) { if (!spa_pod_control_is_inside(&seq[i]->body, @@ -883,14 +891,18 @@ static int impl_get_interface(struct spa_handle *handle, const char *type, void static int impl_clear(struct spa_handle *handle) { struct impl *this; - uint32_t i; + struct port *port; spa_return_val_if_fail(handle != NULL, -EINVAL); this = (struct impl *) handle; - for (i = 0; i < MAX_PORTS; i++) - free(this->in_ports[i]); + spa_list_insert_list(&this->free_list, &this->port_list); + spa_list_insert_list(&this->free_list, &this->mix_list); + spa_list_consume(port, &this->free_list, link) { + spa_list_remove(&port->link); + free(port); + } return 0; } @@ -939,6 +951,9 @@ impl_init(const struct spa_handle_factory *factory, } spa_hook_list_init(&this->hooks); + spa_list_init(&this->port_list); + spa_list_init(&this->free_list); + spa_list_init(&this->mix_list); this->node.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_Node, @@ -951,7 +966,6 @@ impl_init(const struct spa_handle_factory *factory, this->info.flags = SPA_NODE_FLAG_RT | SPA_NODE_FLAG_IN_DYNAMIC_PORTS; port = GET_OUT_PORT(this, 0); - port->valid = true; port->direction = SPA_DIRECTION_OUTPUT; port->id = 0; port->info = SPA_PORT_INFO_INIT();