mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-12-20 06:50:03 +01:00
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:
commit
06b9aaabec
2 changed files with 350 additions and 151 deletions
|
|
@ -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, ¶m);
|
||||
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, ¶m);
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue