audioconvert: implement graph latency reporting

Keep per graph latency. Sum all the graph latencies together and keep
this around as the process-latency.

Refactor the port latency setter. Make a function to recalculate the
latency of all other ports. Take into account the graph latencies.

Update the port latencies when the total graph latency changes.
This commit is contained in:
Wim Taymans 2025-05-14 09:21:31 +02:00
parent c334bfb0bb
commit 093b3eea21

View file

@ -235,7 +235,7 @@ struct filter_graph {
uint32_t inputs_position[SPA_AUDIO_MAX_CHANNELS]; uint32_t inputs_position[SPA_AUDIO_MAX_CHANNELS];
uint32_t n_outputs; uint32_t n_outputs;
uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS]; uint32_t outputs_position[SPA_AUDIO_MAX_CHANNELS];
struct spa_process_latency_info latency; uint32_t latency;
bool removing; bool removing;
bool setup; bool setup;
}; };
@ -255,6 +255,7 @@ struct impl {
struct spa_list free_graphs; struct spa_list free_graphs;
struct spa_list active_graphs; struct spa_list active_graphs;
struct filter_graph graphs[MAX_GRAPH]; struct filter_graph graphs[MAX_GRAPH];
struct spa_process_latency_info latency;
int in_filter_props; int in_filter_props;
int filter_props_count; int filter_props_count;
@ -1006,6 +1007,75 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
return 0; return 0;
} }
static void port_update_latency(struct port *port,
const struct spa_latency_info *info, bool valid)
{
if (spa_latency_info_compare(info, &port->latency[info->direction]) != 0) {
port->latency[info->direction] = *info;
port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
port->params[IDX_Latency].user++;
}
port->have_latency = valid;
}
static void recalc_latencies(struct impl *this, enum spa_direction direction)
{
struct spa_latency_info info;
enum spa_direction other = SPA_DIRECTION_REVERSE(direction);
struct port *port;
uint32_t i;
bool have_latency = false;
spa_latency_info_combine_start(&info, other);
for (i = 0; i < this->dir[direction].n_ports; i++) {
port = GET_PORT(this, direction, i);
if ((port->is_monitor) || !port->have_latency)
continue;
spa_log_debug(this->log, "%p: combine %d", this, i);
spa_latency_info_combine(&info, &port->latency[other]);
have_latency = true;
}
spa_latency_info_combine_finish(&info);
spa_process_latency_info_add(&this->latency, &info);
spa_log_debug(this->log, "%p: combined %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, this,
info.direction == SPA_DIRECTION_INPUT ? "input" : "output",
info.min_quantum, info.max_quantum,
info.min_rate, info.max_rate,
info.min_ns, info.max_ns);
for (i = 0; i < this->dir[other].n_ports; i++) {
port = GET_PORT(this, other, i);
if (port->is_monitor)
continue;
port_update_latency(port, &info, have_latency);
}
}
static void recalc_graph_latency(struct impl *impl)
{
struct filter_graph *g;
int32_t latency = 0;
spa_list_for_each(g, &impl->active_graphs, link)
latency += g->latency;
if (latency != impl->latency.rate) {
impl->latency.rate = latency;
recalc_latencies(impl, SPA_DIRECTION_INPUT);
recalc_latencies(impl, SPA_DIRECTION_OUTPUT);
}
}
static void update_graph_latency(struct filter_graph *g, uint32_t latency)
{
if (g->latency != latency) {
g->latency = latency;
recalc_graph_latency(g->impl);
}
}
static void graph_info(void *object, const struct spa_filter_graph_info *info) static void graph_info(void *object, const struct spa_filter_graph_info *info)
{ {
struct filter_graph *g = object; struct filter_graph *g = object;
@ -1033,9 +1103,10 @@ static void graph_info(void *object, const struct spa_filter_graph_info *info)
else if (spa_streq(k, "latency")) { else if (spa_streq(k, "latency")) {
double latency; double latency;
if (spa_atod(s, &latency)) if (spa_atod(s, &latency))
g->latency.rate = (uint32_t)latency; update_graph_latency(g, (uint32_t)latency);
} }
} }
emit_info(g->impl, false);
} }
static int apply_props(struct impl *impl, const struct spa_pod *props); static int apply_props(struct impl *impl, const struct spa_pod *props);
@ -1245,6 +1316,7 @@ static void clean_filter_handles(struct impl *impl, bool force)
spa_zero(*g); spa_zero(*g);
spa_list_append(&impl->free_graphs, &g->link); spa_list_append(&impl->free_graphs, &g->link);
} }
recalc_graph_latency(impl);
} }
static inline void insert_graph(struct spa_list *graphs, struct filter_graph *pending) static inline void insert_graph(struct spa_list *graphs, struct filter_graph *pending)
@ -2841,8 +2913,7 @@ static int port_set_latency(void *object,
struct port *port, *oport; struct port *port, *oport;
enum spa_direction other = SPA_DIRECTION_REVERSE(direction); enum spa_direction other = SPA_DIRECTION_REVERSE(direction);
struct spa_latency_info info; struct spa_latency_info info;
bool have_latency, emit = false;; bool have_latency;;
uint32_t i;
spa_log_debug(this->log, "%p: set latency direction:%d id:%d %p", spa_log_debug(this->log, "%p: set latency direction:%d id:%d %p",
this, direction, port_id, latency); this, direction, port_id, latency);
@ -2857,11 +2928,8 @@ static int port_set_latency(void *object,
return -EINVAL; return -EINVAL;
have_latency = true; have_latency = true;
} }
emit = spa_latency_info_compare(&info, &port->latency[other]) != 0 ||
port->have_latency == have_latency;
port->latency[other] = info; port_update_latency(port, &info, have_latency);
port->have_latency = have_latency;
spa_log_debug(this->log, "%p: set %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, this, spa_log_debug(this->log, "%p: set %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, this,
info.direction == SPA_DIRECTION_INPUT ? "input" : "output", info.direction == SPA_DIRECTION_INPUT ? "input" : "output",
@ -2877,45 +2945,10 @@ static int port_set_latency(void *object,
else else
return 0; return 0;
if (oport != NULL && if (oport != NULL)
spa_latency_info_compare(&info, &oport->latency[other]) != 0) { port_update_latency(oport, &info, have_latency);
oport->latency[other] = info;
oport->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
oport->params[IDX_Latency].user++;
}
} else {
spa_latency_info_combine_start(&info, other);
for (i = 0; i < this->dir[direction].n_ports; i++) {
oport = GET_PORT(this, direction, i);
if ((oport->is_monitor) || !oport->have_latency)
continue;
spa_log_debug(this->log, "%p: combine %d", this, i);
spa_latency_info_combine(&info, &oport->latency[other]);
}
spa_latency_info_combine_finish(&info);
spa_log_debug(this->log, "%p: combined %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, this,
info.direction == SPA_DIRECTION_INPUT ? "input" : "output",
info.min_quantum, info.max_quantum,
info.min_rate, info.max_rate,
info.min_ns, info.max_ns);
for (i = 0; i < this->dir[other].n_ports; i++) {
oport = GET_PORT(this, other, i);
if (oport->is_monitor)
continue;
spa_log_debug(this->log, "%p: change %d", this, i);
if (spa_latency_info_compare(&info, &oport->latency[other]) != 0) {
oport->latency[other] = info;
oport->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
oport->params[IDX_Latency].user++;
}
}
}
if (emit) {
port->info.change_mask |= SPA_PORT_CHANGE_MASK_PARAMS;
port->params[IDX_Latency].user++;
} }
recalc_latencies(this, direction);
return 0; return 0;
} }