diff --git a/spa/include/spa/filter-graph/filter-graph.h b/spa/include/spa/filter-graph/filter-graph.h index 05904c7f3..b9c5ea426 100644 --- a/spa/include/spa/filter-graph/filter-graph.h +++ b/spa/include/spa/filter-graph/filter-graph.h @@ -46,6 +46,7 @@ struct spa_filter_graph_info { #define SPA_FILTER_GRAPH_CHANGE_MASK_FLAGS (1u<<0) #define SPA_FILTER_GRAPH_CHANGE_MASK_PROPS (1u<<1) +#define SPA_FILTER_GRAPH_CHANGE_MASK_PORTS (1u<<2) uint64_t change_mask; uint64_t flags; diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index 9cb580d7d..1b1f5faf8 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -995,10 +995,10 @@ struct spa_filter_graph_events graph_events = { .props_changed = graph_props_changed, }; -static int setup_filter_graph(struct impl *this, struct spa_filter_graph *graph) +static int setup_filter_graph(struct impl *this, struct spa_filter_graph *graph, uint32_t channels) { int res; - char rate_str[64]; + char rate_str[64], in_ports[64]; struct dir *dir; if (graph == NULL) @@ -1006,11 +1006,15 @@ static int setup_filter_graph(struct impl *this, struct spa_filter_graph *graph) dir = &this->dir[SPA_DIRECTION_REVERSE(this->direction)]; snprintf(rate_str, sizeof(rate_str), "%d", dir->format.info.raw.rate); + if (channels) + snprintf(in_ports, sizeof(in_ports), "%d", channels); spa_filter_graph_deactivate(graph); res = spa_filter_graph_activate(graph, &SPA_DICT_ITEMS( - SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, rate_str))); + SPA_DICT_ITEM(SPA_KEY_AUDIO_RATE, rate_str), + SPA_DICT_ITEM("filter-graph.n_inputs", channels ? in_ports : NULL))); + return res; } @@ -1104,7 +1108,7 @@ static int load_filter_graph(struct impl *impl, const char *graph, int order) goto error; /* prepare new filter and swap it */ - res = setup_filter_graph(impl, iface); + res = setup_filter_graph(impl, iface, 0); if (res < 0) goto error; pending->graph = iface; @@ -1909,7 +1913,7 @@ static char *format_position(char *str, size_t len, uint32_t channels, uint32_t return str; } -static int setup_channelmix(struct impl *this) +static int setup_channelmix(struct impl *this, uint32_t channels) { struct dir *in = &this->dir[SPA_DIRECTION_INPUT]; struct dir *out = &this->dir[SPA_DIRECTION_OUTPUT]; @@ -1918,7 +1922,7 @@ static int setup_channelmix(struct impl *this) char str[1024]; int res; - src_chan = in->format.info.raw.channels; + src_chan = channels; dst_chan = out->format.info.raw.channels; for (i = 0, src_mask = 0; i < src_chan; i++) { @@ -1929,6 +1933,13 @@ static int setup_channelmix(struct impl *this) p = out->format.info.raw.position[i]; dst_mask |= 1ULL << (p < 64 ? p : 0); } + + /* if we needed a channel conversion but we already did one before this + * stage, assume we are now with the dst layout */ + if ((out->format.info.raw.channels != in->format.info.raw.channels) && + channels != in->format.info.raw.channels) + src_mask = dst_mask; + spa_log_info(this->log, "in %s (%016"PRIx64")", format_position(str, sizeof(str), src_chan, in->format.info.raw.position), src_mask); spa_log_info(this->log, "out %s (%016"PRIx64")", format_position(str, sizeof(str), @@ -2222,7 +2233,7 @@ static inline bool resample_is_passthrough(struct impl *this) static int setup_convert(struct impl *this) { struct dir *in, *out; - uint32_t i, rate, maxsize, maxports, duration; + uint32_t i, rate, maxsize, maxports, duration, channels; struct port *p; int res; @@ -2269,16 +2280,19 @@ static int setup_convert(struct impl *this) if (in->format.info.raw.channels == 0 || out->format.info.raw.channels == 0) return -EINVAL; + channels = in->format.info.raw.channels; + if ((res = setup_in_convert(this)) < 0) return res; - for (i = 0; i < MAX_GRAPH; i++) { - struct filter_graph *g = &this->filter_graph[i]; + for (i = 0; i < this->n_graph; i++) { + struct filter_graph *g = &this->filter_graph[this->graph_index[i]]; if (!g->active) continue; - if ((res = setup_filter_graph(this, g->graph)) < 0) + if ((res = setup_filter_graph(this, g->graph, channels)) < 0) return res; + channels = g->n_outputs; } - if ((res = setup_channelmix(this)) < 0) + if ((res = setup_channelmix(this, channels)) < 0) return res; if ((res = setup_resample(this)) < 0) return res; diff --git a/spa/plugins/filter-graph/filter-graph.c b/spa/plugins/filter-graph/filter-graph.c index 91afd62a5..5a7410db7 100644 --- a/spa/plugins/filter-graph/filter-graph.c +++ b/spa/plugins/filter-graph/filter-graph.c @@ -175,9 +175,16 @@ struct graph { uint32_t n_control; struct port **control_port; + uint32_t n_input_names; + char **input_names; + + uint32_t n_output_names; + char **output_names; + struct volume volume[2]; unsigned activated:1; + unsigned setup:1; }; struct impl { @@ -1422,6 +1429,8 @@ static int impl_deactivate(void *object) return 0; } +static int setup_graph(struct graph *graph); + static int impl_activate(void *object, const struct spa_dict *props) { struct impl *impl = object; @@ -1432,10 +1441,10 @@ static int impl_activate(void *object, const struct spa_dict *props) struct descriptor *desc; const struct spa_fga_descriptor *d; const struct spa_fga_plugin *p; - uint32_t i, j, max_samples = impl->quantum_limit; + uint32_t i, j, max_samples = impl->quantum_limit, n_ports; int res; float *sd, *dd, *data; - const char *rate; + const char *rate, *str; if (graph->activated) return 0; @@ -1445,6 +1454,31 @@ static int impl_activate(void *object, const struct spa_dict *props) rate = spa_dict_lookup(props, SPA_KEY_AUDIO_RATE); impl->rate = rate ? atoi(rate) : DEFAULT_RATE; + if ((str = spa_dict_lookup(props, "filter-graph.n_inputs")) != NULL) { + if (spa_atou32(str, &n_ports, 0) && + n_ports != impl->info.n_inputs) { + impl->info.n_inputs = n_ports; + impl->info.n_outputs = 0; + impl->info.change_mask |= SPA_FILTER_GRAPH_CHANGE_MASK_PORTS; + graph->setup = false; + } + } + if ((str = spa_dict_lookup(props, "filter-graph.n_outputs")) != NULL) { + if (spa_atou32(str, &n_ports, 0) && + n_ports != impl->info.n_outputs) { + impl->info.n_outputs = n_ports; + impl->info.n_inputs = 0; + impl->info.change_mask |= SPA_FILTER_GRAPH_CHANGE_MASK_PORTS; + graph->setup = false; + } + } + if (!graph->setup) { + if ((res = setup_graph(graph)) < 0) + return res; + graph->setup = true; + spa_filter_graph_emit_info(&impl->hooks, &impl->info); + } + /* first make instances */ spa_list_for_each(node, &graph->node_list, link) { node_cleanup(node); @@ -1569,7 +1603,19 @@ static struct node *find_next_node(struct graph *graph) return NULL; } -static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_json *outputs) +static void unsetup_graph(struct graph *graph) +{ + free(graph->input); + graph->input = NULL; + free(graph->output); + graph->output = NULL; + free(graph->hndl); + graph->hndl = NULL; + free(graph->control_port); + graph->control_port = NULL; + +} +static int setup_graph(struct graph *graph) { struct impl *impl = graph->impl; struct node *node, *first, *last; @@ -1577,33 +1623,35 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_ struct link *link; struct graph_port *gp; struct graph_hndl *gh; - uint32_t i, j, n_nodes, n_input, n_output, n_control, n_hndl = 0; + uint32_t i, j, n, n_nodes, n_input, n_output, n_control, n_hndl = 0; int res; struct descriptor *desc; const struct spa_fga_descriptor *d; - char v[256]; + char *pname; bool allow_unused; + unsetup_graph(graph); + first = spa_list_first(&graph->node_list, struct node, link); last = spa_list_last(&graph->node_list, struct node, link); /* calculate the number of inputs and outputs into the graph. - * If we have a list of inputs/outputs, just count them. Otherwise + * If we have a list of inputs/outputs, just use them. Otherwise * we count all input ports of the first node and all output * ports of the last node */ - if (inputs != NULL) - n_input = count_array(inputs); + if (graph->n_input_names != 0) + n_input = graph->n_input_names; else n_input = first->desc->n_input; - if (outputs != NULL) - n_output = count_array(outputs); + if (graph->n_output_names != 0) + n_output = graph->n_output_names; else n_output = last->desc->n_output; /* we allow unconnected ports when not explicitly given and the nodes support * NULL data */ - allow_unused = inputs == NULL && outputs == NULL && + allow_unused = graph->n_input_names == 0 && graph->n_output_names == 0 && SPA_FLAG_IS_SET(first->desc->desc->flags, SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA) && SPA_FLAG_IS_SET(last->desc->desc->flags, SPA_FGA_DESCRIPTOR_SUPPORTS_NULL_DATA); @@ -1675,7 +1723,7 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_ /* now collect all input and output ports for all the handles. */ for (i = 0; i < n_hndl; i++) { - if (inputs == NULL) { + if (graph->n_input_names == 0) { desc = first->desc; d = desc->desc; for (j = 0; j < desc->n_input; j++) { @@ -1687,15 +1735,15 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_ gp->port = desc->input[j]; } } else { - struct spa_json it = *inputs; - while (spa_json_get_string(&it, v, sizeof(v)) > 0) { - if (spa_streq(v, "null")) { + for (n = 0; n < graph->n_input_names; n++) { + pname = graph->input_names[n]; + if (spa_streq(pname, "null")) { gp = &graph->input[graph->n_input++]; gp->desc = NULL; spa_log_info(impl->log, "ignore input port %d", graph->n_input); - } else if ((port = find_port(first, v, SPA_FGA_PORT_INPUT)) == NULL) { + } else if ((port = find_port(first, pname, SPA_FGA_PORT_INPUT)) == NULL) { res = -ENOENT; - spa_log_error(impl->log, "input port %s not found", v); + spa_log_error(impl->log, "input port %s not found", pname); goto error; } else { bool disabled = false; @@ -1754,7 +1802,7 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_ } } } - if (outputs == NULL) { + if (graph->n_output_names == 0) { desc = last->desc; d = desc->desc; for (j = 0; j < desc->n_output; j++) { @@ -1766,15 +1814,15 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_ gp->port = desc->output[j]; } } else { - struct spa_json it = *outputs; - while (spa_json_get_string(&it, v, sizeof(v)) > 0) { + for (n = 0; n < graph->n_output_names; n++) { + pname = graph->output_names[n]; gp = &graph->output[graph->n_output]; - if (spa_streq(v, "null")) { + if (spa_streq(pname, "null")) { gp->desc = NULL; spa_log_info(impl->log, "silence output port %d", graph->n_output); - } else if ((port = find_port(last, v, SPA_FGA_PORT_OUTPUT)) == NULL) { + } else if ((port = find_port(last, pname, SPA_FGA_PORT_OUTPUT)) == NULL) { res = -ENOENT; - spa_log_error(impl->log, "output port %s not found", v); + spa_log_error(impl->log, "output port %s not found", pname); goto error; } else { desc = port->node->desc; @@ -1979,21 +2027,41 @@ static int load_graph(struct graph *graph, const struct spa_dict *props) return res; } } - return setup_graph(graph, pinputs, poutputs); + if (pinputs != NULL) { + graph->n_input_names = count_array(pinputs); + graph->input_names = calloc(graph->n_input_names, sizeof(char *)); + graph->n_input_names = 0; + while (spa_json_get_string(pinputs, key, sizeof(key)) > 0) + graph->input_names[graph->n_input_names++] = strdup(key); + } + if (poutputs != NULL) { + graph->n_output_names = count_array(poutputs); + graph->output_names = calloc(graph->n_output_names, sizeof(char *)); + graph->n_output_names = 0; + while (spa_json_get_string(poutputs, key, sizeof(key)) > 0) + graph->output_names[graph->n_output_names++] = strdup(key); + } + return 0; } static void graph_free(struct graph *graph) { struct link *link; struct node *node; + uint32_t i; + + unsetup_graph(graph); + spa_list_consume(link, &graph->link_list, link) link_free(link); spa_list_consume(node, &graph->node_list, link) node_free(node); - free(graph->input); - free(graph->output); - free(graph->hndl); - free(graph->control_port); + for (i = 0; i < graph->n_input_names; i++) + free(graph->input_names[i]); + free(graph->input_names); + for (i = 0; i < graph->n_output_names; i++) + free(graph->output_names[i]); + free(graph->output_names); } static const struct spa_filter_graph_methods impl_filter_graph = {