From b9b89b92b254e089586129edaa36667783d920f2 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 24 May 2021 18:01:19 +0200 Subject: [PATCH] spa: add some latency helpers Add more fields to the latency object. Add methods to create, parse and process latency info. --- pipewire-jack/src/pipewire-jack.c | 35 +++----- spa/include/spa/param/latency-utils.h | 117 ++++++++++++++++++++++++++ spa/include/spa/param/param.h | 9 +- spa/include/spa/param/type-info.h | 9 +- spa/plugins/alsa/alsa-pcm-sink.c | 11 +-- spa/plugins/alsa/alsa-pcm-source.c | 12 +-- spa/plugins/alsa/alsa-pcm.c | 2 +- spa/plugins/alsa/alsa-pcm.h | 4 +- spa/plugins/alsa/alsa-seq-bridge.c | 13 +-- spa/plugins/audioconvert/merger.c | 28 +++--- spa/plugins/audioconvert/splitter.c | 28 +++--- src/pipewire/impl-link.c | 20 ++--- src/pipewire/impl-port.c | 82 ++++++++++-------- src/pipewire/private.h | 9 +- src/tools/pw-cat.c | 11 ++- 15 files changed, 245 insertions(+), 145 deletions(-) create mode 100644 spa/include/spa/param/latency-utils.h diff --git a/pipewire-jack/src/pipewire-jack.c b/pipewire-jack/src/pipewire-jack.c index a0b4dcf53..a229bf447 100644 --- a/pipewire-jack/src/pipewire-jack.c +++ b/pipewire-jack/src/pipewire-jack.c @@ -142,9 +142,7 @@ struct object { struct port *port; bool is_monitor; struct object *node; - float latency_quantum[2]; - uint32_t latency_min[2]; - uint32_t latency_max[2]; + struct spa_latency_info latency[2]; } port; }; struct pw_proxy *proxy; @@ -2188,22 +2186,10 @@ static void port_param(void *object, int seq, switch (id) { case SPA_PARAM_Latency: { - uint32_t direction; - float latency_quantum; - uint32_t latency_min, latency_max; - - if (spa_pod_parse_object(param, - SPA_TYPE_OBJECT_ParamLatency, NULL, - SPA_PARAM_LATENCY_direction,SPA_POD_Id(&direction), - SPA_PARAM_LATENCY_quantum,SPA_POD_Float(&latency_quantum), - SPA_PARAM_LATENCY_min, SPA_POD_Int(&latency_min), - SPA_PARAM_LATENCY_max, SPA_POD_Int(&latency_max)) < 0) + struct spa_latency_info info; + if (spa_latency_parse(param, &info) < 0) return; - - direction &= 1; - o->port.latency_quantum[direction] = latency_quantum; - o->port.latency_min[direction] = latency_min; - o->port.latency_max[direction] = latency_max; + o->port.latency[info.direction] = info; break; } default: @@ -4422,8 +4408,9 @@ void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_ { struct object *o = (struct object *) port; struct client *c; - jack_nframes_t nframes; + jack_nframes_t nframes, rate; int direction; + struct spa_latency_info *info; spa_return_if_fail(o != NULL); c = o->client; @@ -4434,9 +4421,13 @@ void jack_port_get_latency_range (jack_port_t *port, jack_latency_callback_mode_ direction = SPA_DIRECTION_INPUT; nframes = jack_get_buffer_size((jack_client_t*)c); - nframes *= o->port.latency_quantum[direction]; - range->min = nframes + o->port.latency_min[direction]; - range->max = nframes + o->port.latency_max[direction]; + rate = jack_get_sample_rate((jack_client_t*)c); + info = &o->port.latency[direction]; + + range->min = (info->min_quantum * nframes) + + info->min_rate + (info->min_ns * rate) / SPA_NSEC_PER_SEC; + range->max = (info->max_quantum * nframes) + + info->max_rate + (info->max_ns * rate) / SPA_NSEC_PER_SEC; } SPA_EXPORT diff --git a/spa/include/spa/param/latency-utils.h b/spa/include/spa/param/latency-utils.h new file mode 100644 index 000000000..8e67d3cfd --- /dev/null +++ b/spa/include/spa/param/latency-utils.h @@ -0,0 +1,117 @@ +/* Simple Plugin API + * + * Copyright © 2021 Wim Taymans + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef SPA_PARAM_LATENCY_UTILS_H +#define SPA_PARAM_LATENCY_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#include + +struct spa_latency_info { + enum spa_direction direction; + float min_quantum; + float max_quantum; + uint32_t min_rate; + uint32_t max_rate; + uint64_t min_ns; + uint64_t max_ns; +}; + +#define SPA_LATENCY_INFO(dir) (struct spa_latency_info) { .direction = (dir) } + +static inline int +spa_latency_info_compare(const struct spa_latency_info *a, struct spa_latency_info *b) +{ + if (a->min_quantum == b->min_quantum && + a->max_quantum == b->max_quantum && + a->min_rate == b->min_rate && + a->max_rate == b->max_rate && + a->min_ns == b->min_ns && + a->max_ns == b->max_ns) + return 0; + return 1; +} + +static inline int +spa_latency_info_combine(struct spa_latency_info *info, const struct spa_latency_info *other) +{ + if (info->direction != other->direction) + return -EINVAL; + if (info->min_quantum == 0.0f || other->min_quantum < info->min_quantum) + info->min_quantum = other->min_quantum; + if (other->max_quantum > info->max_quantum) + info->max_quantum = other->max_quantum; + if (info->min_rate == 0U || other->min_rate < info->min_rate) + info->min_rate = other->min_rate; + if (other->max_rate > info->max_rate) + info->max_rate = other->max_rate; + if (info->min_ns == 0UL || other->min_ns < info->min_ns) + info->min_ns = other->min_ns; + if (other->max_ns > info->max_ns) + info->max_ns = other->max_ns; + return 0; +} + +static inline int +spa_latency_parse(const struct spa_pod *latency, struct spa_latency_info *info) +{ + int res; + spa_zero(*info); + if ((res = spa_pod_parse_object(latency, + SPA_TYPE_OBJECT_ParamLatency, NULL, + SPA_PARAM_LATENCY_direction, SPA_POD_Id(&info->direction), + SPA_PARAM_LATENCY_minQuantum, SPA_POD_OPT_Float(&info->min_quantum), + SPA_PARAM_LATENCY_maxQuantum, SPA_POD_OPT_Float(&info->max_quantum), + SPA_PARAM_LATENCY_minRate, SPA_POD_OPT_Int(&info->min_rate), + SPA_PARAM_LATENCY_maxRate, SPA_POD_OPT_Int(&info->max_rate), + SPA_PARAM_LATENCY_minNs, SPA_POD_OPT_Long(&info->min_ns), + SPA_PARAM_LATENCY_maxNs, SPA_POD_OPT_Long(&info->max_ns))) < 0) + return res; + info->direction &= 1; + return 0; +} + +static inline struct spa_pod * +spa_latency_build(struct spa_pod_builder *builder, uint32_t id, const struct spa_latency_info *info) +{ + return spa_pod_builder_add_object(builder, + SPA_TYPE_OBJECT_ParamLatency, id, + SPA_PARAM_LATENCY_direction, SPA_POD_Id(info->direction), + SPA_PARAM_LATENCY_minQuantum, SPA_POD_Float(info->min_quantum), + SPA_PARAM_LATENCY_maxQuantum, SPA_POD_Float(info->max_quantum), + SPA_PARAM_LATENCY_minRate, SPA_POD_Int(info->min_rate), + SPA_PARAM_LATENCY_maxRate, SPA_POD_Int(info->max_rate), + SPA_PARAM_LATENCY_minNs, SPA_POD_Long(info->min_ns), + SPA_PARAM_LATENCY_maxNs, SPA_POD_Long(info->max_ns)); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SPA_PARAM_LATENCY_UTILS_H */ diff --git a/spa/include/spa/param/param.h b/spa/include/spa/param/param.h index 96d3dedc3..902663791 100644 --- a/spa/include/spa/param/param.h +++ b/spa/include/spa/param/param.h @@ -177,9 +177,12 @@ enum spa_param_route { enum spa_param_latency { SPA_PARAM_LATENCY_START, SPA_PARAM_LATENCY_direction, /**< direction, input/output (Id enum spa_direction) */ - SPA_PARAM_LATENCY_quantum, /**< latency relative to quantum (Float) */ - SPA_PARAM_LATENCY_min, /**< min latency (Int) relative to rate */ - SPA_PARAM_LATENCY_max, /**< max latency (Int) relative to rate */ + SPA_PARAM_LATENCY_minQuantum, /**< min latency relative to quantum (Float) */ + SPA_PARAM_LATENCY_maxQuantum, /**< max latency relative to quantum (Float) */ + SPA_PARAM_LATENCY_minRate, /**< min latency (Int) relative to rate */ + SPA_PARAM_LATENCY_maxRate, /**< max latency (Int) relative to rate */ + SPA_PARAM_LATENCY_minNs, /**< min latency (Long) in nanoseconds */ + SPA_PARAM_LATENCY_maxNs, /**< max latency (Long) in nanoseconds */ }; /** diff --git a/spa/include/spa/param/type-info.h b/spa/include/spa/param/type-info.h index f5f43c741..663154fe4 100644 --- a/spa/include/spa/param/type-info.h +++ b/spa/include/spa/param/type-info.h @@ -390,9 +390,12 @@ static const struct spa_type_info spa_type_profiler[] = { static const struct spa_type_info spa_type_param_latency[] = { { SPA_PARAM_LATENCY_START, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_LATENCY_BASE, spa_type_param, }, { SPA_PARAM_LATENCY_direction, SPA_TYPE_Id, SPA_TYPE_INFO_PARAM_LATENCY_BASE "direction", spa_type_direction, }, - { SPA_PARAM_LATENCY_quantum, SPA_TYPE_Float, SPA_TYPE_INFO_PARAM_LATENCY_BASE "quantum", NULL, }, - { SPA_PARAM_LATENCY_min, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_LATENCY_BASE "min", NULL, }, - { SPA_PARAM_LATENCY_max, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_LATENCY_BASE "max", NULL, }, + { SPA_PARAM_LATENCY_minQuantum, SPA_TYPE_Float, SPA_TYPE_INFO_PARAM_LATENCY_BASE "minQuantum", NULL, }, + { SPA_PARAM_LATENCY_maxQuantum, SPA_TYPE_Float, SPA_TYPE_INFO_PARAM_LATENCY_BASE "maxQuantum", NULL, }, + { SPA_PARAM_LATENCY_minRate, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_LATENCY_BASE "minRate", NULL, }, + { SPA_PARAM_LATENCY_maxRate, SPA_TYPE_Int, SPA_TYPE_INFO_PARAM_LATENCY_BASE "maxRate", NULL, }, + { SPA_PARAM_LATENCY_minNs, SPA_TYPE_Long, SPA_TYPE_INFO_PARAM_LATENCY_BASE "minNs", NULL, }, + { SPA_PARAM_LATENCY_maxNs, SPA_TYPE_Long, SPA_TYPE_INFO_PARAM_LATENCY_BASE "maxNs", NULL, }, { 0, 0, NULL, NULL }, }; diff --git a/spa/plugins/alsa/alsa-pcm-sink.c b/spa/plugins/alsa/alsa-pcm-sink.c index 8fcf83910..75dde72c2 100644 --- a/spa/plugins/alsa/alsa-pcm-sink.c +++ b/spa/plugins/alsa/alsa-pcm-sink.c @@ -459,12 +459,7 @@ impl_node_port_enum_params(void *object, int seq, case SPA_PARAM_Latency: switch (result.index) { case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamLatency, id, - SPA_PARAM_LATENCY_direction, SPA_POD_Id(direction), - SPA_PARAM_LATENCY_quantum, SPA_POD_Float(this->latency_quantum), - SPA_PARAM_LATENCY_min, SPA_POD_Int(this->latency), - SPA_PARAM_LATENCY_max, SPA_POD_Int(this->latency)); + param = spa_latency_build(&b, id, &this->latency); break; default: return 0; @@ -775,7 +770,9 @@ impl_init(const struct spa_handle_factory *factory, spa_hook_list_init(&this->hooks); this->stream = SND_PCM_STREAM_PLAYBACK; - this->latency_quantum = 1.0f; + this->latency = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT); + this->latency.min_quantum = 1.0f; + this->latency.max_quantum = 1.0f; this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | SPA_NODE_CHANGE_MASK_PROPS | diff --git a/spa/plugins/alsa/alsa-pcm-source.c b/spa/plugins/alsa/alsa-pcm-source.c index 5a1681980..f82ecee35 100644 --- a/spa/plugins/alsa/alsa-pcm-source.c +++ b/spa/plugins/alsa/alsa-pcm-source.c @@ -457,12 +457,7 @@ impl_node_port_enum_params(void *object, int seq, case SPA_PARAM_Latency: switch (result.index) { case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamLatency, id, - SPA_PARAM_LATENCY_direction, SPA_POD_Id(direction), - SPA_PARAM_LATENCY_quantum, SPA_POD_Float(this->latency_quantum), - SPA_PARAM_LATENCY_min, SPA_POD_Int(this->latency), - SPA_PARAM_LATENCY_max, SPA_POD_Int(this->latency)); + param = spa_latency_build(&b, id, &this->latency); break; default: return 0; @@ -794,8 +789,9 @@ impl_init(const struct spa_handle_factory *factory, spa_hook_list_init(&this->hooks); this->stream = SND_PCM_STREAM_CAPTURE; - - this->latency_quantum = 1.0f; + this->latency = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT); + this->latency.min_quantum = 1.0f; + this->latency.max_quantum = 1.0f; this->info_all = SPA_NODE_CHANGE_MASK_FLAGS | SPA_NODE_CHANGE_MASK_PROPS | diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 6d9166026..8834d7648 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -630,7 +630,7 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_ state->headroom = SPA_MIN(state->headroom, state->buffer_frames); state->start_delay = state->default_start_delay; - state->latency = state->headroom; + state->latency.min_rate = state->latency.max_rate = state->headroom; state->period_frames = period_size; periods = state->buffer_frames / state->period_frames; diff --git a/spa/plugins/alsa/alsa-pcm.h b/spa/plugins/alsa/alsa-pcm.h index 7b79fc382..7e642ff0d 100644 --- a/spa/plugins/alsa/alsa-pcm.h +++ b/spa/plugins/alsa/alsa-pcm.h @@ -45,6 +45,7 @@ extern "C" { #include #include #include +#include #include #include "dll.h" @@ -192,8 +193,7 @@ struct state { struct spa_dll dll; double max_error; - float latency_quantum; - uint32_t latency; + struct spa_latency_info latency; }; int diff --git a/spa/plugins/alsa/alsa-seq-bridge.c b/spa/plugins/alsa/alsa-seq-bridge.c index 0bbccdfde..5280e5619 100644 --- a/spa/plugins/alsa/alsa-seq-bridge.c +++ b/spa/plugins/alsa/alsa-seq-bridge.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #define NAME "alsa-bridge" @@ -578,13 +579,13 @@ impl_node_port_enum_params(void *object, int seq, case SPA_PARAM_Latency: switch (result.index) { case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamLatency, id, - SPA_PARAM_LATENCY_direction, SPA_POD_Id(direction), - SPA_PARAM_LATENCY_quantum, SPA_POD_Float(1.0f), - SPA_PARAM_LATENCY_min, SPA_POD_Int(0), - SPA_PARAM_LATENCY_max, SPA_POD_Int(0)); + { + struct spa_latency_info info = { + .direction = direction, + .min_quantum = 1.0f, .max_quantum = 1.0f }; + param = spa_latency_build(&b, id, &info); break; + } default: return 0; } diff --git a/spa/plugins/audioconvert/merger.c b/spa/plugins/audioconvert/merger.c index fd416b795..ba2b1b2d9 100644 --- a/spa/plugins/audioconvert/merger.c +++ b/spa/plugins/audioconvert/merger.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -176,9 +177,7 @@ struct impl { uint32_t src_remap[SPA_AUDIO_MAX_CHANNELS]; uint32_t dst_remap[SPA_AUDIO_MAX_CHANNELS]; - float latency_quantum[2]; - uint32_t latency_min[2]; - uint32_t latency_max[2]; + struct spa_latency_info latency[2]; float empty[MAX_SAMPLES + MAX_ALIGN]; }; @@ -814,12 +813,7 @@ impl_node_port_enum_params(void *object, int seq, case SPA_PARAM_Latency: switch (result.index) { case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamLatency, id, - SPA_PARAM_LATENCY_direction, SPA_POD_Id(direction), - SPA_PARAM_LATENCY_quantum, SPA_POD_Float(this->latency_quantum[direction]), - SPA_PARAM_LATENCY_min, SPA_POD_Int(this->latency_min[direction]), - SPA_PARAM_LATENCY_max, SPA_POD_Int(this->latency_max[direction])); + param = spa_latency_build(&b, id, &this->latency[direction]); break; default: return 0; @@ -936,18 +930,16 @@ static int port_set_latency(void *object, enum spa_direction other = SPA_DIRECTION_REVERSE(direction); uint32_t i; - spa_log_debug(this->log, NAME " %p: set latency", this); + spa_log_info(this->log, NAME " %p: set latency", this); + if (latency == NULL) { - this->latency_quantum[other] = 0; - this->latency_min[other] = 0; - this->latency_max[other] = 0; + this->latency[other] = SPA_LATENCY_INFO(other); } else { - if (spa_pod_parse_object(latency, - SPA_TYPE_OBJECT_ParamLatency, NULL, - SPA_PARAM_LATENCY_quantum,SPA_POD_Float(&this->latency_quantum[other]), - SPA_PARAM_LATENCY_min, SPA_POD_Int(&this->latency_min[other]), - SPA_PARAM_LATENCY_max, SPA_POD_Int(&this->latency_max[other])) < 0) + struct spa_latency_info info; + if (spa_latency_parse(latency, &info) < 0 || + info.direction != other) return -EINVAL; + this->latency[other] = info; } if (direction == SPA_DIRECTION_OUTPUT) { for (i = 0; i < this->port_count; i++) { diff --git a/spa/plugins/audioconvert/splitter.c b/spa/plugins/audioconvert/splitter.c index 234bf512f..6da7bab7e 100644 --- a/spa/plugins/audioconvert/splitter.c +++ b/spa/plugins/audioconvert/splitter.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -126,9 +127,7 @@ struct impl { unsigned int is_passthrough:1; unsigned int started:1; - float latency_quantum[2]; - uint32_t latency_min[2]; - uint32_t latency_max[2]; + struct spa_latency_info latency[2]; uint32_t src_remap[SPA_AUDIO_MAX_CHANNELS]; uint32_t dst_remap[SPA_AUDIO_MAX_CHANNELS]; @@ -572,12 +571,7 @@ impl_node_port_enum_params(void *object, int seq, case SPA_PARAM_Latency: switch (result.index) { case 0: - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamLatency, id, - SPA_PARAM_LATENCY_direction, SPA_POD_Id(direction), - SPA_PARAM_LATENCY_quantum, SPA_POD_Float(this->latency_quantum[direction]), - SPA_PARAM_LATENCY_min, SPA_POD_Int(this->latency_min[direction]), - SPA_PARAM_LATENCY_max, SPA_POD_Int(this->latency_max[direction])); + param = spa_latency_build(&b, id, &this->latency[direction]); break; default: return 0; @@ -700,16 +694,13 @@ static int port_set_latency(void *object, spa_log_debug(this->log, NAME " %p: set latency", this); if (latency == NULL) { - this->latency_quantum[other] = 0; - this->latency_min[other] = 0; - this->latency_max[other] = 0; + this->latency[other] = SPA_LATENCY_INFO(other); } else { - if (spa_pod_parse_object(latency, - SPA_TYPE_OBJECT_ParamLatency, NULL, - SPA_PARAM_LATENCY_quantum,SPA_POD_Float(&this->latency_quantum[other]), - SPA_PARAM_LATENCY_min, SPA_POD_Int(&this->latency_min[other]), - SPA_PARAM_LATENCY_max, SPA_POD_Int(&this->latency_max[other])) < 0) + struct spa_latency_info info; + if (spa_latency_parse(latency, &info) < 0 || + info.direction != other) return -EINVAL; + this->latency[other] = info; } if (direction == SPA_DIRECTION_INPUT) { for (i = 0; i < this->port_count; i++) { @@ -1137,6 +1128,9 @@ impl_init(const struct spa_handle_factory *factory, spa_hook_list_init(&this->hooks); + this->latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT); + this->latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT); + this->node.iface = SPA_INTERFACE_INIT( SPA_TYPE_INTERFACE_Node, SPA_VERSION_NODE, diff --git a/src/pipewire/impl-link.c b/src/pipewire/impl-link.c index 6e00621d1..fccec63e2 100644 --- a/src/pipewire/impl-link.c +++ b/src/pipewire/impl-link.c @@ -637,6 +637,8 @@ static void input_remove(struct pw_impl_link *this, struct pw_impl_port *port) spa_list_remove(&this->input_link); pw_impl_port_emit_link_removed(this->input, this); + pw_impl_port_recalc_latency(this->input); + if ((res = pw_impl_port_use_buffers(port, mix, 0, NULL, 0)) < 0) { pw_log_warn(NAME" %p: port %p clear error %s", this, port, spa_strerror(res)); } @@ -657,6 +659,8 @@ static void output_remove(struct pw_impl_link *this, struct pw_impl_port *port) spa_list_remove(&this->output_link); pw_impl_port_emit_link_removed(this->output, this); + pw_impl_port_recalc_latency(this->output); + /* we don't clear output buffers when the link goes away. They will get * cleared when the node goes to suspend */ pw_impl_port_release_mix(port, mix); @@ -932,17 +936,6 @@ static bool pw_impl_node_can_reach(struct pw_impl_node *output, struct pw_impl_n return false; } -static void recalculate_latencies(struct impl *impl) -{ - struct pw_impl_link *this = &impl->this; - /* from output port we get capture latency and propagate this - * on the input port */ - pw_impl_port_recalc_latency(this->output); - /* from input port we get playback latency and propagate that - * on the output port */ - pw_impl_port_recalc_latency(this->input); -} - static void try_link_controls(struct impl *impl, struct pw_impl_port *output, struct pw_impl_port *input) { struct pw_control *cin, *cout; @@ -1156,10 +1149,11 @@ struct pw_impl_link *pw_context_create_link(struct pw_context *context, pw_impl_port_emit_link_added(output, this); pw_impl_port_emit_link_added(input, this); - recalculate_latencies(impl); - try_link_controls(impl, output, input); + pw_impl_port_recalc_latency(this->output); + pw_impl_port_recalc_latency(this->input); + pw_impl_node_emit_peer_added(impl->onode, impl->inode); return this; diff --git a/src/pipewire/impl-port.c b/src/pipewire/impl-port.c index c9c3636ea..f1de2c25f 100644 --- a/src/pipewire/impl-port.c +++ b/src/pipewire/impl-port.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -355,24 +356,23 @@ static int process_latency_param(void *data, int seq, uint32_t id, uint32_t index, uint32_t next, struct spa_pod *param) { struct pw_impl_port *this = data; - uint32_t direction; - struct pw_port_latency latency; + struct spa_latency_info latency; if (id != SPA_PARAM_Latency) return -EINVAL; - if (spa_pod_parse_object(param, - SPA_TYPE_OBJECT_ParamLatency, NULL, - SPA_PARAM_LATENCY_direction,SPA_POD_Id(&direction), - SPA_PARAM_LATENCY_quantum,SPA_POD_Float(&latency.quantum), - SPA_PARAM_LATENCY_min, SPA_POD_Int(&latency.min), - SPA_PARAM_LATENCY_max, SPA_POD_Int(&latency.max)) < 0) + if (spa_latency_parse(param, &latency) < 0) return 0; - if (direction != this->direction) + if (latency.direction != this->direction) return 0; - pw_log_info("got latency %f %d %d", latency.quantum, latency.min, latency.max); - this->latency[direction] = latency; + pw_log_info("port %p: got %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, this, + pw_direction_as_string(this->direction), + latency.min_quantum, latency.max_quantum, + latency.min_rate, latency.max_rate, + latency.min_ns, latency.max_ns); + + this->latency[this->direction] = latency; return 0; } @@ -515,6 +515,9 @@ struct pw_impl_port *pw_context_create_port( pw_map_init(&this->mix_port_map, 64, 64); + this->latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT); + this->latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT); + if (info) update_info(this, info); @@ -1288,55 +1291,66 @@ int pw_impl_port_for_each_link(struct pw_impl_port *port, return res; } -static void port_set_latency(struct pw_impl_port *port, struct pw_port_latency *latency) +static void port_set_latency(struct pw_impl_port *port, struct spa_latency_info *latency) { - struct pw_port_latency *current = &port->latency[port->direction]; + struct spa_latency_info *current = &port->latency[latency->direction]; struct spa_pod *param; struct spa_pod_builder b = { 0 }; uint8_t buffer[1024]; - if (current->quantum == latency->quantum && - current->min == latency->min && - current->max == latency->max) + if (spa_latency_info_compare(current, latency) == 0) return; *current = *latency; - pw_log_info("port set latency %d %d %f", latency->min, latency->max, latency->quantum); + pw_log_info("port %p: set %s latency %f-%f %d-%d %"PRIu64"-%"PRIu64, port, + pw_direction_as_string(latency->direction), + latency->min_quantum, latency->max_quantum, + latency->min_rate, latency->max_rate, + latency->min_ns, latency->max_ns); spa_pod_builder_init(&b, buffer, sizeof(buffer)); - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamLatency, SPA_PARAM_Latency, - SPA_PARAM_LATENCY_direction, SPA_POD_Id(port->direction), - SPA_PARAM_LATENCY_quantum, SPA_POD_Float(latency->quantum), - SPA_PARAM_LATENCY_min, SPA_POD_Int(latency->min), - SPA_PARAM_LATENCY_max, SPA_POD_Int(latency->max)); - + param = spa_latency_build(&b, SPA_PARAM_Latency, latency); pw_impl_port_set_param(port, SPA_PARAM_Latency, 0, param); } int pw_impl_port_recalc_latency(struct pw_impl_port *port) { struct pw_impl_link *l; - struct pw_port_latency latency = { .quantum = 0.0f, .min = UINT32_MAX, .max = 0 }; + struct spa_latency_info latency; struct pw_impl_port *other; + latency = SPA_LATENCY_INFO(SPA_DIRECTION_REVERSE(port->direction)); + latency.min_quantum = FLT_MAX; + latency.min_rate = INT32_MAX; + latency.min_ns = INT64_MAX; + if (port->direction == PW_DIRECTION_OUTPUT) { spa_list_for_each(l, &port->links, output_link) { other = l->input; - latency.quantum = SPA_MAX(latency.quantum, other->latency[other->direction].quantum); - latency.min = SPA_MIN(latency.min, other->latency[other->direction].min); - latency.max = SPA_MAX(latency.max, other->latency[other->direction].max); + spa_latency_info_combine(&latency, &other->latency[other->direction]); + pw_log_info("port %p: peer %p: latency %f-%f %d-%d %"PRIu64"-%"PRIu64, + port, other, + latency.min_quantum, latency.max_quantum, + latency.min_rate, latency.max_rate, + latency.min_ns, latency.max_ns); } } else { spa_list_for_each(l, &port->links, input_link) { - other = l->input; - latency.quantum = SPA_MAX(latency.quantum, other->latency[other->direction].quantum); - latency.min = SPA_MIN(latency.min, other->latency[other->direction].min); - latency.max = SPA_MAX(latency.max, other->latency[other->direction].max); + other = l->output; + spa_latency_info_combine(&latency, &other->latency[other->direction]); + pw_log_info("port %p: peer %p: latency %f-%f %d-%d %"PRIu64"-%"PRIu64, + port, other, + latency.min_quantum, latency.max_quantum, + latency.min_rate, latency.max_rate, + latency.min_ns, latency.max_ns); } } - if (latency.min == UINT32_MAX) - latency.min = 0; + if (latency.min_quantum == FLT_MAX) + latency.min_quantum = 0.0f; + if (latency.min_rate == INT32_MAX) + latency.min_rate = 0U; + if (latency.min_ns == INT64_MAX) + latency.min_ns = 0UL; port_set_latency(port, &latency); return 0; diff --git a/src/pipewire/private.h b/src/pipewire/private.h index 0cffae983..9513c4c9a 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -36,6 +36,7 @@ extern "C" { #include #include +#include #include #include @@ -686,12 +687,6 @@ struct pw_impl_node { void *user_data; /**< extra user data */ }; -struct pw_port_latency { - float quantum; /** quantum multiplier */ - uint32_t min; /** min of all peers */ - uint32_t max; /** max of all peers */ -}; - struct pw_impl_port_mix { struct spa_list link; struct spa_list rt_link; @@ -796,7 +791,7 @@ struct pw_impl_port { } rt; /**< data only accessed from the data thread */ unsigned int added:1; - struct pw_port_latency latency[2]; /**< latencies */ + struct spa_latency_info latency[2]; /**< latencies */ void *owner_data; /**< extra owner data */ void *user_data; /**< extra user data */ diff --git a/src/tools/pw-cat.c b/src/tools/pw-cat.c index ee64c432b..fb4ab439a 100644 --- a/src/tools/pw-cat.c +++ b/src/tools/pw-cat.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -835,13 +836,15 @@ on_io_changed(void *userdata, uint32_t id, void *data, uint32_t size) } static void -on_param_changed(void *userdata, uint32_t id, const struct spa_pod *format) +on_param_changed(void *userdata, uint32_t id, const struct spa_pod *param) { struct data *data = userdata; - if (data->verbose) - printf("stream param change: id=%"PRIu32"\n", - id); + if (data->verbose) { + printf("stream param change: id=%"PRIu32"\n", id); + if (param != NULL) + spa_debug_pod(0, NULL, param); + } } static void on_process(void *userdata)