mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-01 22:58:50 -04:00
filter-graph: add support for channel positions
Make it possible to define a channel position in filter-graph. Use the channel positions to perform the final channelmix.
This commit is contained in:
parent
9b2b420cf5
commit
de2ab7cac9
3 changed files with 139 additions and 39 deletions
|
|
@ -229,7 +229,9 @@ struct filter_graph {
|
|||
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;
|
||||
uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS];
|
||||
bool active;
|
||||
};
|
||||
|
||||
|
|
@ -961,10 +963,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;
|
||||
struct spa_dict *props = info->props;
|
||||
uint32_t i;
|
||||
|
||||
if (!g->active)
|
||||
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);
|
||||
|
|
@ -996,22 +1016,29 @@ 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, uint32_t channels)
|
||||
static int setup_filter_graph(struct impl *this, struct filter_graph *g,
|
||||
uint32_t channels, uint32_t *position)
|
||||
{
|
||||
int res;
|
||||
char rate_str[64], in_ports[64];
|
||||
struct dir *dir;
|
||||
|
||||
if (graph == NULL)
|
||||
if (g == NULL || g->graph == NULL)
|
||||
return 0;
|
||||
|
||||
dir = &this->dir[SPA_DIRECTION_REVERSE(this->direction)];
|
||||
snprintf(rate_str, sizeof(rate_str), "%d", dir->format.info.raw.rate);
|
||||
if (channels)
|
||||
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("filter-graph.n_inputs", channels ? in_ports : NULL)));
|
||||
|
|
@ -1109,10 +1136,12 @@ 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, 0);
|
||||
if (res < 0)
|
||||
goto error;
|
||||
pending->graph = iface;
|
||||
res = setup_filter_graph(impl, pending, 0, NULL);
|
||||
if (res < 0) {
|
||||
pending->graph = NULL;
|
||||
goto error;
|
||||
}
|
||||
pending->active = true;
|
||||
spa_log_info(impl->log, "loading filter-graph order:%d in %d active:%d",
|
||||
order, idx, n_graph + 1);
|
||||
|
|
@ -1908,7 +1937,7 @@ static char *format_position(char *str, size_t len, uint32_t channels, uint32_t
|
|||
return str;
|
||||
}
|
||||
|
||||
static int setup_channelmix(struct impl *this, uint32_t channels)
|
||||
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];
|
||||
|
|
@ -1921,7 +1950,7 @@ static int setup_channelmix(struct impl *this, uint32_t 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++) {
|
||||
|
|
@ -1936,7 +1965,7 @@ static int setup_channelmix(struct impl *this, uint32_t 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);
|
||||
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);
|
||||
|
||||
|
|
@ -2228,7 +2257,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, channels;
|
||||
uint32_t i, rate, maxsize, maxports, duration, channels, *position;
|
||||
struct port *p;
|
||||
int res;
|
||||
|
||||
|
|
@ -2276,6 +2305,8 @@ static int setup_convert(struct impl *this)
|
|||
return -EINVAL;
|
||||
|
||||
channels = in->format.info.raw.channels;
|
||||
position = in->format.info.raw.position;
|
||||
maxports = SPA_MAX(in->format.info.raw.channels, out->format.info.raw.channels);
|
||||
|
||||
if ((res = setup_in_convert(this)) < 0)
|
||||
return res;
|
||||
|
|
@ -2283,11 +2314,13 @@ static int setup_convert(struct impl *this)
|
|||
struct filter_graph *g = &this->filter_graph[this->graph_index[i]];
|
||||
if (!g->active)
|
||||
continue;
|
||||
if ((res = setup_filter_graph(this, g->graph, channels)) < 0)
|
||||
if ((res = setup_filter_graph(this, g, channels, position)) < 0)
|
||||
return res;
|
||||
channels = g->n_outputs;
|
||||
position = g->outputs_position;
|
||||
maxports = SPA_MAX(maxports, channels);
|
||||
}
|
||||
if ((res = setup_channelmix(this, channels)) < 0)
|
||||
if ((res = setup_channelmix(this, channels, position)) < 0)
|
||||
return res;
|
||||
if ((res = setup_resample(this)) < 0)
|
||||
return res;
|
||||
|
|
@ -2303,7 +2336,6 @@ static int setup_convert(struct impl *this)
|
|||
p = GET_OUT_PORT(this, i);
|
||||
maxsize = SPA_MAX(maxsize, p->maxsize);
|
||||
}
|
||||
maxports = SPA_MAX(in->format.info.raw.channels, out->format.info.raw.channels);
|
||||
if ((res = ensure_tmp(this, maxsize, maxports)) < 0)
|
||||
return res;
|
||||
|
||||
|
|
|
|||
|
|
@ -183,6 +183,13 @@ struct graph {
|
|||
|
||||
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;
|
||||
};
|
||||
|
|
@ -212,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;
|
||||
}
|
||||
}
|
||||
|
|
@ -250,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])
|
||||
|
|
@ -259,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;
|
||||
|
||||
|
|
@ -1457,19 +1502,19 @@ static int impl_activate(void *object, const struct spa_dict *props)
|
|||
|
||||
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;
|
||||
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 != 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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1477,7 +1522,7 @@ static int impl_activate(void *object, const struct spa_dict *props)
|
|||
if ((res = setup_graph(graph)) < 0)
|
||||
return res;
|
||||
graph->setup = true;
|
||||
spa_filter_graph_emit_info(&impl->hooks, &impl->info);
|
||||
emit_filter_graph_info(impl, false);
|
||||
}
|
||||
|
||||
/* first make instances */
|
||||
|
|
@ -1666,24 +1711,28 @@ static int setup_graph(struct graph *graph)
|
|||
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;
|
||||
}
|
||||
|
|
@ -1699,11 +1748,11 @@ static int setup_graph(struct graph *graph)
|
|||
"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);
|
||||
|
||||
|
|
@ -1949,6 +1998,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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue