impl-link: handle mixer failures better

It is possible that the mixer input can't be created because of hitting
the max limits of dsp mixer or client-node. Make sure we handle those
errors, destroy the link and clean up properly.
This commit is contained in:
Wim Taymans 2022-01-24 09:39:52 +01:00
parent a67f38f790
commit 7e387d842b
3 changed files with 38 additions and 25 deletions

View file

@ -219,6 +219,8 @@ static struct mix *find_mix(struct port *p, uint32_t mix_id)
if (mix_id >= len) {
size_t need = sizeof(struct mix) * (mix_id + 1 - len);
void *ptr = pw_array_add(&p->mix, need);
if (ptr == NULL)
return NULL;
memset(ptr, 0, need);
}
mix = pw_array_get_unchecked(&p->mix, mix_id, struct mix);

View file

@ -451,7 +451,7 @@ static int port_set_io(struct pw_impl_link *this, struct pw_impl_port *port, uin
return res;
}
static int select_io(struct pw_impl_link *this)
static void select_io(struct pw_impl_link *this)
{
struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
struct spa_io_buffers *io;
@ -461,13 +461,9 @@ static int select_io(struct pw_impl_link *this)
io = this->rt.out_mix.io;
if (io == NULL)
io = &impl->io;
if (io == NULL)
return -EIO;
this->io = io;
*this->io = SPA_IO_BUFFERS_INIT;
return 0;
}
static int do_allocation(struct pw_impl_link *this)
@ -1229,6 +1225,16 @@ struct pw_impl_link *pw_context_create_link(struct pw_context *context,
spa_hook_list_init(&this->listener_list);
impl->format_filter = format_filter;
this->info.format = NULL;
this->info.props = &this->properties->dict;
this->rt.out_mix.peer_id = input->global->id;
this->rt.in_mix.peer_id = output->global->id;
if ((res = pw_impl_port_init_mix(output, &this->rt.out_mix)) < 0)
goto error_output_mix;
if ((res = pw_impl_port_init_mix(input, &this->rt.in_mix)) < 0)
goto error_input_mix;
pw_impl_port_add_listener(input, &impl->input_port_listener, &input_port_events, impl);
pw_impl_node_add_listener(input_node, &impl->input_node_listener, &input_node_events, impl);
@ -1245,19 +1251,9 @@ struct pw_impl_link *pw_context_create_link(struct pw_context *context,
spa_list_append(&output->links, &this->output_link);
spa_list_append(&input->links, &this->input_link);
this->info.format = NULL;
this->info.props = &this->properties->dict;
impl->io = SPA_IO_BUFFERS_INIT;
this->rt.out_mix.peer_id = input->global->id;
this->rt.in_mix.peer_id = output->global->id;
pw_impl_port_init_mix(output, &this->rt.out_mix);
pw_impl_port_init_mix(input, &this->rt.in_mix);
if ((res = select_io(this)) < 0)
goto error_no_io;
select_io(this);
if (this->feedback) {
impl->inode = output_node;
@ -1317,8 +1313,12 @@ error_work_queue:
res = -errno;
pw_log_debug("work queue failed: %m");
goto error_free;
error_no_io:
pw_log_debug("%p: can't set io %d (%s)", this, res, spa_strerror(res));
error_output_mix:
pw_log_error("%p: can't get output mix %d (%s)", this, res, spa_strerror(res));
goto error_free;
error_input_mix:
pw_log_error("%p: can't get input mix %d (%s)", this, res, spa_strerror(res));
pw_impl_port_release_mix(output, &this->rt.out_mix);
goto error_free;
error_free:
free(impl);

View file

@ -214,16 +214,16 @@ int pw_impl_port_init_mix(struct pw_impl_port *port, struct pw_impl_port_mix *mi
if (port_id == SPA_ID_INVALID)
return -errno;
if ((res = spa_node_add_port(port->mix, port->direction, port_id, NULL)) < 0 &&
res != -ENOTSUP)
goto error_remove_map;
mix->port.direction = port->direction;
mix->port.port_id = port_id;
spa_list_append(&port->mix_list, &mix->link);
port->n_mix++;
mix->p = port;
spa_node_add_port(port->mix, port->direction, port_id, NULL);
res = pw_impl_port_call_init_mix(port, mix);
if ((res = pw_impl_port_call_init_mix(port, mix)) < 0)
goto error_remove_port;
/* set the same format on the mixer as on the port if any */
{
@ -242,11 +242,20 @@ int pw_impl_port_init_mix(struct pw_impl_port *port, struct pw_impl_port_mix *mi
}
}
spa_list_append(&port->mix_list, &mix->link);
port->n_mix++;
pw_log_debug("%p: init mix n_mix:%d %d.%d io:%p: (%s)", port,
port->n_mix, port->port_id, mix->port.port_id,
mix->io, spa_strerror(res));
return res;
error_remove_port:
spa_node_remove_port(port->mix, port->direction, port_id);
error_remove_map:
pw_map_remove(&port->mix_port_map, port_id);
return res;
}
SPA_EXPORT
@ -261,7 +270,9 @@ int pw_impl_port_release_mix(struct pw_impl_port *port, struct pw_impl_port_mix
res = pw_impl_port_call_release_mix(port, mix);
spa_node_remove_port(port->mix, port->direction, port_id);
if ((res = spa_node_remove_port(port->mix, port->direction, port_id)) < 0 &&
res != -ENOTSUP)
pw_log_warn("can't remove mix port %d: %s", port_id, spa_strerror(res));
pw_log_debug("%p: release mix %d %d.%d", port,
port->n_mix, port->port_id, mix->port.port_id);