Merge branch '1.4-filter-graph-backports' into '1.4'

[1.4] Backport some filter-graph fixes

See merge request pipewire/pipewire!2634
This commit is contained in:
Arun Raghavan 2025-12-13 01:25:09 +00:00
commit 06b9aaabec
2 changed files with 350 additions and 151 deletions

View file

@ -224,13 +224,17 @@ struct stage {
struct filter_graph {
struct impl *impl;
struct spa_list link;
int order;
struct spa_handle *handle;
struct spa_filter_graph *graph;
struct spa_hook listener;
uint32_t n_inputs;
uint32_t inputs_position[SPA_AUDIO_MAX_CHANNELS];
uint32_t n_outputs;
bool active;
uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS];
bool removing;
bool setup;
};
struct impl {
@ -243,9 +247,12 @@ struct impl {
struct spa_plugin_loader *loader;
uint32_t n_graph;
uint32_t graph_index[MAX_GRAPH];
struct filter_graph *filter_graph[MAX_GRAPH];
struct spa_list free_graphs;
struct spa_list active_graphs;
struct filter_graph graphs[MAX_GRAPH];
struct filter_graph filter_graph[MAX_GRAPH];
int in_filter_props;
int filter_props_count;
@ -302,6 +309,8 @@ struct impl {
char group_name[128];
uint32_t maxsize;
uint32_t maxports;
uint32_t scratch_size;
uint32_t scratch_ports;
float *empty;
@ -815,8 +824,8 @@ static int impl_node_enum_params(void *object, int seq,
SPA_PROP_INFO_params, SPA_POD_Bool(true));
break;
default:
if (this->filter_graph[0].graph) {
res = spa_filter_graph_enum_prop_info(this->filter_graph[0].graph,
if (this->filter_graph[0] && this->filter_graph[0]->graph) {
res = spa_filter_graph_enum_prop_info(this->filter_graph[0]->graph,
result.index - 30, &b, &param);
if (res <= 0)
return res;
@ -908,13 +917,13 @@ static int impl_node_enum_params(void *object, int seq,
param = spa_pod_builder_pop(&b, &f[0]);
break;
default:
if (result.index > MAX_GRAPH)
if (result.index-1 >= this->n_graph)
return 0;
if (this->filter_graph[result.index-1].graph == NULL)
if (this->filter_graph[result.index-1]->graph == NULL)
goto next;
res = spa_filter_graph_get_props(this->filter_graph[result.index-1].graph,
res = spa_filter_graph_get_props(this->filter_graph[result.index-1]->graph,
&b, &param);
if (res < 0)
return res;
@ -960,10 +969,28 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
static void graph_info(void *object, const struct spa_filter_graph_info *info)
{
struct filter_graph *g = object;
if (!g->active)
struct spa_dict *props = info->props;
uint32_t i;
if (g->removing)
return;
g->n_inputs = info->n_inputs;
g->n_outputs = info->n_outputs;
for (i = 0; props && i < props->n_items; i++) {
const char *k = props->items[i].key;
const char *s = props->items[i].value;
if (spa_streq(k, "n_inputs"))
spa_atou32(s, &g->n_inputs, 0);
else if (spa_streq(k, "n_outputs"))
spa_atou32(s, &g->n_outputs, 0);
else if (spa_streq(k, "inputs.audio.position"))
spa_audio_parse_position(s, strlen(s),
g->inputs_position, &g->n_inputs);
else if (spa_streq(k, "outputs.audio.position"))
spa_audio_parse_position(s, strlen(s),
g->outputs_position, &g->n_outputs);
}
}
static int apply_props(struct impl *impl, const struct spa_pod *props);
@ -972,7 +999,7 @@ static void graph_apply_props(void *object, enum spa_direction direction, const
{
struct filter_graph *g = object;
struct impl *impl = g->impl;
if (!g->active)
if (g->removing)
return;
if (apply_props(impl, props) > 0)
emit_node_info(impl, false);
@ -982,7 +1009,7 @@ static void graph_props_changed(void *object, enum spa_direction direction)
{
struct filter_graph *g = object;
struct impl *impl = g->impl;
if (!g->active)
if (g->removing)
return;
impl->info.change_mask |= SPA_NODE_CHANGE_MASK_PARAMS;
impl->params[IDX_Props].user++;
@ -995,98 +1022,145 @@ 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 filter_graph *g,
uint32_t channels, uint32_t *position)
{
int res;
char rate_str[64];
char rate_str[64], in_ports[64];
struct dir *dir;
if (graph == NULL)
if (g == NULL || g->graph == NULL || g->setup)
return 0;
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);
g->n_inputs = channels;
if (position) {
memcpy(g->inputs_position, position, sizeof(uint32_t) * channels);
memcpy(g->outputs_position, position, sizeof(uint32_t) * channels);
}
}
spa_filter_graph_deactivate(graph);
res = spa_filter_graph_activate(graph,
spa_filter_graph_deactivate(g->graph);
res = spa_filter_graph_activate(g->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)));
g->setup = res >= 0;
return res;
}
static int setup_channelmix(struct impl *this, uint32_t channels, uint32_t *position);
static int setup_filter_graphs(struct impl *impl)
{
int res;
uint32_t channels, *position;
struct dir *in, *out;
struct filter_graph *g, *t;
in = &impl->dir[SPA_DIRECTION_INPUT];
out = &impl->dir[SPA_DIRECTION_OUTPUT];
channels = in->format.info.raw.channels;
position = in->format.info.raw.position;
impl->maxports = SPA_MAX(in->format.info.raw.channels, out->format.info.raw.channels);
spa_list_for_each_safe(g, t, &impl->active_graphs, link) {
if (g->removing)
continue;
if ((res = setup_filter_graph(impl, g, channels, position)) < 0) {
g->removing = true;
spa_log_warn(impl->log, "failed to activate graph %d: %s", g->order,
spa_strerror(res));
} else {
channels = g->n_outputs;
position = g->outputs_position;
impl->maxports = SPA_MAX(impl->maxports, channels);
}
}
if ((res = setup_channelmix(impl, channels, position)) < 0)
return res;
return 0;
}
static int do_sync_filter_graph(struct spa_loop *loop, bool async, uint32_t seq,
const void *data, size_t size, void *user_data)
{
struct impl *impl = user_data;
uint32_t i, j;
impl->n_graph = 0;
for (i = 0; i < MAX_GRAPH; i++) {
struct filter_graph *g = &impl->filter_graph[i];
if (g->graph == NULL || !g->active)
continue;
impl->graph_index[impl->n_graph++] = i;
struct filter_graph *g;
impl->n_graph = 0;
spa_list_for_each(g, &impl->active_graphs, link)
if (g->setup && !g->removing)
impl->filter_graph[impl->n_graph++] = g;
for (j = impl->n_graph-1; j > 0; j--) {
if (impl->filter_graph[impl->graph_index[j]].order >=
impl->filter_graph[impl->graph_index[j-1]].order)
break;
SPA_SWAP(impl->graph_index[j], impl->graph_index[j-1]);
}
}
impl->recalc = true;
return 0;
}
static void clean_filter_handles(struct impl *impl, bool force)
{
uint32_t i;
for (i = 0; i < MAX_GRAPH; i++) {
struct filter_graph *g = &impl->filter_graph[i];
if (!g->active || force) {
if (g->graph)
spa_hook_remove(&g->listener);
if (g->handle)
spa_plugin_loader_unload(impl->loader, g->handle);
spa_zero(*g);
}
struct filter_graph *g, *t;
spa_list_for_each_safe(g, t, &impl->active_graphs, link) {
if (!g->removing)
continue;
spa_list_remove(&g->link);
if (g->graph)
spa_hook_remove(&g->listener);
if (g->handle)
spa_plugin_loader_unload(impl->loader, g->handle);
spa_zero(*g);
spa_list_append(&impl->free_graphs, &g->link);
}
}
static inline void insert_graph(struct spa_list *graphs, struct filter_graph *pending)
{
struct filter_graph *g;
spa_list_for_each(g, graphs, link) {
if (g->order < pending->order)
break;
}
spa_list_append(&g->link, &pending->link);
}
static int load_filter_graph(struct impl *impl, const char *graph, int order)
{
char qlimit[64];
int res;
void *iface;
struct spa_handle *new_handle = NULL;
uint32_t i, idx, n_graph;
struct filter_graph *pending, *old_active = NULL;
struct filter_graph *pending, *g, *t;
if (impl->props.filter_graph_disabled)
return -EPERM;
/* find graph spot */
idx = SPA_ID_INVALID;
n_graph = 0;
for (i = 0; i < MAX_GRAPH; i++) {
pending = &impl->filter_graph[i];
/* find the first free spot for our new filter */
if (!pending->active && idx == SPA_ID_INVALID)
idx = i;
/* deactivate an existing filter of the same order */
if (pending->active) {
if (pending->order == order)
old_active = pending;
else
n_graph++;
}
}
/* we can at most have MAX_GRAPH-1 active filters */
if (n_graph >= MAX_GRAPH-1)
if (spa_list_is_empty(&impl->free_graphs))
return -ENOSPC;
pending = &impl->filter_graph[idx];
/* find free graph for our new filter */
pending = spa_list_first(&impl->free_graphs, struct filter_graph, link);
pending->impl = impl;
pending->order = order;
pending->removing = false;
/* move active graphs with same order to inactive list */
spa_list_for_each_safe(g, t, &impl->active_graphs, link) {
if (g->order == order) {
g->removing = true;
spa_log_info(impl->log, "removing filter-graph order:%d", order);
}
}
if (graph != NULL && graph[0] != '\0') {
snprintf(qlimit, sizeof(qlimit), "%u", impl->quantum_limit);
@ -1104,32 +1178,19 @@ 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);
if (res < 0)
goto error;
pending->graph = iface;
pending->active = true;
spa_log_info(impl->log, "loading filter-graph order:%d in %d active:%d",
order, idx, n_graph + 1);
} else {
pending->active = false;
spa_log_info(impl->log, "removing filter-graph order:%d active:%d",
order, n_graph);
}
if (old_active)
old_active->active = false;
/* we call this here on the pending_graph so that the n_input/n_output is updated
* before we switch */
if (pending->active)
pending->handle = new_handle;
spa_filter_graph_add_listener(pending->graph,
&pending->listener, &graph_events, pending);
spa_list_remove(&pending->link);
insert_graph(&impl->active_graphs, pending);
spa_log_info(impl->log, "loading filter-graph order:%d", order);
}
res = setup_filter_graphs(impl);
spa_loop_invoke(impl->data_loop, do_sync_filter_graph, 0, NULL, 0, true, impl);
if (pending->active)
pending->handle = new_handle;
if (impl->in_filter_props == 0)
clean_filter_handles(impl, false);
@ -1714,16 +1775,15 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
}
case SPA_PARAM_Props:
{
uint32_t i;
bool have_graph = false;
struct filter_graph *g, *t;
this->filter_props_count = 0;
for (i = 0; i < MAX_GRAPH; i++) {
struct filter_graph *g = &this->filter_graph[i];
if (!g->active)
spa_list_for_each_safe(g, t, &this->active_graphs, link) {
if (g->removing)
continue;
have_graph = true;
this->in_filter_props++;
spa_filter_graph_set_props(g->graph,
SPA_DIRECTION_INPUT, param);
@ -1909,7 +1969,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, uint32_t *position)
{
struct dir *in = &this->dir[SPA_DIRECTION_INPUT];
struct dir *out = &this->dir[SPA_DIRECTION_OUTPUT];
@ -1918,19 +1978,20 @@ 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++) {
p = in->format.info.raw.position[i];
p = position[i];
src_mask |= 1ULL << (p < 64 ? p : 0);
}
for (i = 0, dst_mask = 0; i < dst_chan; i++) {
p = out->format.info.raw.position[i];
dst_mask |= 1ULL << (p < 64 ? p : 0);
}
spa_log_info(this->log, "in %s (%016"PRIx64")", format_position(str, sizeof(str),
src_chan, in->format.info.raw.position), src_mask);
src_chan, position), src_mask);
spa_log_info(this->log, "out %s (%016"PRIx64")", format_position(str, sizeof(str),
dst_chan, out->format.info.raw.position), dst_mask);
@ -2125,8 +2186,9 @@ static void free_tmp(struct impl *this)
}
}
static int ensure_tmp(struct impl *this, uint32_t maxsize, uint32_t maxports)
static int ensure_tmp(struct impl *this)
{
uint32_t maxsize = this->maxsize, maxports = this->maxports;
if (maxsize > this->scratch_size || maxports > this->scratch_ports) {
float *empty, *scratch, *tmp[2];
uint32_t i;
@ -2222,7 +2284,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, duration;
struct port *p;
int res;
@ -2271,31 +2333,23 @@ static int setup_convert(struct impl *this)
if ((res = setup_in_convert(this)) < 0)
return res;
for (i = 0; i < MAX_GRAPH; i++) {
struct filter_graph *g = &this->filter_graph[i];
if (!g->active)
continue;
if ((res = setup_filter_graph(this, g->graph)) < 0)
return res;
}
if ((res = setup_channelmix(this)) < 0)
if ((res = setup_filter_graphs(this)) < 0)
return res;
if ((res = setup_resample(this)) < 0)
return res;
if ((res = setup_out_convert(this)) < 0)
return res;
maxsize = this->quantum_limit * sizeof(float);
this->maxsize = this->quantum_limit * sizeof(float);
for (i = 0; i < in->n_ports; i++) {
p = GET_IN_PORT(this, i);
maxsize = SPA_MAX(maxsize, p->maxsize);
this->maxsize = SPA_MAX(this->maxsize, p->maxsize);
}
for (i = 0; i < out->n_ports; i++) {
p = GET_OUT_PORT(this, i);
maxsize = SPA_MAX(maxsize, p->maxsize);
this->maxsize = SPA_MAX(this->maxsize, p->maxsize);
}
maxports = SPA_MAX(in->format.info.raw.channels, out->format.info.raw.channels);
if ((res = ensure_tmp(this, maxsize, maxports)) < 0)
if ((res = ensure_tmp(this)) < 0)
return res;
resample_update_rate_match(this, resample_is_passthrough(this), duration, 0);
@ -2310,11 +2364,12 @@ static int setup_convert(struct impl *this)
static void reset_node(struct impl *this)
{
uint32_t i;
for (i = 0; i < MAX_GRAPH; i++) {
struct filter_graph *g = &this->filter_graph[i];
struct filter_graph *g;
spa_list_for_each(g, &this->active_graphs, link) {
if (g->graph)
spa_filter_graph_deactivate(g->graph);
g->setup = false;
}
if (this->resample.reset)
resample_reset(&this->resample);
@ -3491,7 +3546,7 @@ static void recalc_stages(struct impl *this, struct stage_context *ctx)
}
if (!filter_passthrough) {
for (i = 0; i < this->n_graph; i++) {
struct filter_graph *fg = &this->filter_graph[this->graph_index[i]];
struct filter_graph *fg = this->filter_graph[i];
if (mix_passthrough && resample_passthrough && out_passthrough &&
i + 1 == this->n_graph)
@ -4023,6 +4078,13 @@ impl_init(const struct spa_handle_factory *factory,
props_reset(&this->props);
filter_graph_disabled = this->props.filter_graph_disabled;
spa_list_init(&this->active_graphs);
spa_list_init(&this->free_graphs);
for (i = 0; i < MAX_GRAPH; i++) {
struct filter_graph *g = &this->graphs[i];
g->impl = this;
spa_list_append(&this->free_graphs, &g->link);
}
this->rate_limit.interval = 2 * SPA_NSEC_PER_SEC;
this->rate_limit.burst = 1;

View file

@ -175,9 +175,23 @@ 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];
uint32_t n_inputs;
uint32_t n_outputs;
uint32_t inputs_position[SPA_AUDIO_MAX_CHANNELS];
uint32_t n_inputs_position;
uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS];
uint32_t n_outputs_position;
unsigned activated:1;
unsigned setup:1;
};
struct impl {
@ -205,14 +219,52 @@ struct impl {
float *discard_data;
};
static inline void print_channels(char *buffer, size_t max_size, uint32_t n_channels, uint32_t *positions)
{
uint32_t i;
struct spa_strbuf buf;
spa_strbuf_init(&buf, buffer, max_size);
spa_strbuf_append(&buf, "[");
for (i = 0; i < n_channels; i++) {
spa_strbuf_append(&buf, "%s%s", i ? "," : "",
spa_type_audio_channel_to_short_name(positions[i]));
}
spa_strbuf_append(&buf, "]");
}
static void emit_filter_graph_info(struct impl *impl, bool full)
{
uint64_t old = full ? impl->info.change_mask : 0;
struct graph *graph = &impl->graph;
if (full)
impl->info.change_mask = impl->info_all;
if (impl->info.change_mask || full) {
char n_inputs[64], n_outputs[64];
struct spa_dict_item items[6];
struct spa_dict dict = SPA_DICT(items, 0);
char in_pos[SPA_AUDIO_MAX_CHANNELS * 8];
char out_pos[SPA_AUDIO_MAX_CHANNELS * 8];
snprintf(n_inputs, sizeof(n_inputs), "%d", impl->graph.n_inputs);
snprintf(n_outputs, sizeof(n_outputs), "%d", impl->graph.n_outputs);
items[dict.n_items++] = SPA_DICT_ITEM("n_inputs", n_inputs);
items[dict.n_items++] = SPA_DICT_ITEM("n_outputs", n_outputs);
if (graph->n_inputs_position) {
print_channels(in_pos, sizeof(in_pos),
graph->n_inputs_position, graph->inputs_position);
items[dict.n_items++] = SPA_DICT_ITEM("inputs.audio.position", in_pos);
}
if (graph->n_outputs_position) {
print_channels(out_pos, sizeof(out_pos),
graph->n_outputs_position, graph->outputs_position);
items[dict.n_items++] = SPA_DICT_ITEM("outputs.audio.position", out_pos);
}
impl->info.props = &dict;
spa_filter_graph_emit_info(&impl->hooks, &impl->info);
impl->info.props = NULL;
impl->info.change_mask = old;
}
}
@ -243,7 +295,7 @@ static int impl_process(void *object,
uint32_t i, j, n_hndl = graph->n_hndl;
struct graph_port *port;
for (i = 0, j = 0; i < impl->info.n_inputs; i++) {
for (i = 0, j = 0; i < graph->n_inputs; i++) {
while (j < graph->n_input) {
port = &graph->input[j++];
if (port->desc && in[i])
@ -252,7 +304,7 @@ static int impl_process(void *object,
break;
}
}
for (i = 0; i < impl->info.n_outputs; i++) {
for (i = 0; i < graph->n_outputs; i++) {
if (out[i] == NULL)
continue;
@ -1422,6 +1474,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 +1486,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 +1499,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 != graph->n_inputs) {
graph->n_inputs = n_ports;
graph->n_outputs = 0;
impl->info.change_mask |= SPA_FILTER_GRAPH_CHANGE_MASK_PROPS;
graph->setup = false;
}
}
if ((str = spa_dict_lookup(props, "filter-graph.n_outputs")) != NULL) {
if (spa_atou32(str, &n_ports, 0) &&
n_ports != graph->n_outputs) {
graph->n_outputs = n_ports;
graph->n_inputs = 0;
impl->info.change_mask |= SPA_FILTER_GRAPH_CHANGE_MASK_PROPS;
graph->setup = false;
}
}
if (!graph->setup) {
if ((res = setup_graph(graph)) < 0)
return res;
graph->setup = true;
emit_filter_graph_info(impl, false);
}
/* first make instances */
spa_list_for_each(node, &graph->node_list, link) {
node_cleanup(node);
@ -1569,7 +1648,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 +1668,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);
@ -1617,24 +1710,28 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_
res = -EINVAL;
goto error;
}
if (graph->n_inputs == 0)
graph->n_inputs = impl->info.n_inputs;
if (graph->n_inputs == 0)
graph->n_inputs = n_input;
if (impl->info.n_inputs == 0)
impl->info.n_inputs = n_input;
if (graph->n_outputs == 0)
graph->n_outputs = impl->info.n_outputs;
/* compare to the requested number of inputs and duplicate the
* graph n_hndl times when needed. */
n_hndl = impl->info.n_inputs / n_input;
n_hndl = graph->n_inputs / n_input;
if (impl->info.n_outputs == 0)
impl->info.n_outputs = n_output * n_hndl;
if (graph->n_outputs == 0)
graph->n_outputs = n_output * n_hndl;
if (n_hndl != impl->info.n_outputs / n_output) {
if (n_hndl != graph->n_outputs / n_output) {
spa_log_error(impl->log, "invalid ports. The input stream has %1$d ports and "
"the filter has %2$d inputs. The output stream has %3$d ports "
"and the filter has %4$d outputs. input:%1$d / input:%2$d != "
"output:%3$d / output:%4$d. Check inputs and outputs objects.",
impl->info.n_inputs, n_input,
impl->info.n_outputs, n_output);
graph->n_inputs, n_input,
graph->n_outputs, n_output);
res = -EINVAL;
goto error;
}
@ -1650,11 +1747,11 @@ static int setup_graph(struct graph *graph, struct spa_json *inputs, struct spa_
"the filter has %2$d inputs. The output stream has %3$d ports "
"and the filter has %4$d outputs. Some filter ports will be "
"unconnected..",
impl->info.n_inputs, n_input,
impl->info.n_outputs, n_output);
graph->n_inputs, n_input,
graph->n_outputs, n_output);
if (impl->info.n_outputs == 0)
impl->info.n_outputs = n_output * n_hndl;
if (graph->n_outputs == 0)
graph->n_outputs = n_output * n_hndl;
}
spa_log_info(impl->log, "using %d instances %d %d", n_hndl, n_input, n_output);
@ -1675,7 +1772,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 +1784,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 +1851,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 +1863,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;
@ -1900,6 +1997,26 @@ static int load_graph(struct graph *graph, const struct spa_dict *props)
}
impl->info.n_outputs = res;
}
else if (spa_streq("inputs.audio.position", key)) {
if (!spa_json_is_array(val, len) ||
(len = spa_json_container_len(&it[0], val, len)) < 0) {
spa_log_error(impl->log, "%s expects an array", key);
return -EINVAL;
}
spa_audio_parse_position(val, len, graph->inputs_position,
&graph->n_inputs_position);
impl->info.n_inputs = graph->n_inputs_position;
}
else if (spa_streq("outputs.audio.position", key)) {
if (!spa_json_is_array(val, len) ||
(len = spa_json_container_len(&it[0], val, len)) < 0) {
spa_log_error(impl->log, "%s expects an array", key);
return -EINVAL;
}
spa_audio_parse_position(val, len, graph->outputs_position,
&graph->n_outputs_position);
impl->info.n_outputs = graph->n_outputs_position;
}
else if (spa_streq("nodes", key)) {
if (!spa_json_is_array(val, len)) {
spa_log_error(impl->log, "%s expects an array", key);
@ -1979,21 +2096,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 = {