filter-graph: make the filter-graph ports dynamic

When parsing the graph, parse the input and output port names into
a separate string array. This was we can keep them around when
setting up the graph.

Instead of setting up the graph right after loading it, do the graph
setup when we activate the graph. This makes it possible to pass the
input channels to the filter-graph and let it create the right amount of
plugins and ouput channels.

When setting up the graphs in the audioconverter, pass the current
number of channels as the input to the graph and keep track of the
channels that each filter produces.

This way we can also load a custom upmix or downmix graph, for example.
This commit is contained in:
Wim Taymans 2025-03-14 10:10:18 +01:00
parent 76619eaa1d
commit f0e09ae363
3 changed files with 122 additions and 39 deletions

View file

@ -996,10 +996,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)
@ -1007,11 +1007,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;
}
@ -1105,7 +1109,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;
@ -1906,7 +1910,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];
@ -1915,7 +1919,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++) {
@ -1926,6 +1930,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),
@ -2219,7 +2230,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;
@ -2266,16 +2277,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;