module-loopback: add ProcessLatency support

Improve the Latency reporting, we always report Input and Output latency
pairs.

Keep ProcessLatency on the capture and playback streams. The capture
stream process latency is reported as input latency and the playback
process latency as output latency.

Setting ProcessLatency on the capture stream (Sink), for example, will
propagate the added latency upstream. This would then instruct players
to send the audio earlier to compensate for the delay.

See #4270
This commit is contained in:
Wim Taymans 2024-09-10 15:24:45 +02:00
parent b8c6bfae1a
commit 1e5c86e1dc

View file

@ -251,6 +251,9 @@ struct impl {
struct spa_hook playback_listener;
struct spa_audio_info_raw playback_info;
struct spa_process_latency_info process_latency[2];
struct spa_latency_info latency[2];
unsigned int do_disconnect:1;
unsigned int recalc_delay:1;
@ -391,24 +394,61 @@ static void playback_process(void *d)
pw_stream_queue_buffer(impl->playback, out);
}
static void build_latency_params(struct impl *impl, struct spa_pod_builder *b,
const struct spa_pod *params[], uint32_t max_params)
{
struct spa_latency_info latency;
latency = impl->latency[0];
spa_process_latency_info_add(&impl->process_latency[0], &latency);
params[0] = spa_latency_build(b, SPA_PARAM_Latency, &latency);
latency = impl->latency[1];
spa_process_latency_info_add(&impl->process_latency[1], &latency);
params[1] = spa_latency_build(b, SPA_PARAM_Latency, &latency);
}
static void param_latency_changed(struct impl *impl, const struct spa_pod *param,
struct pw_stream *other)
struct pw_stream *stream, struct pw_stream *other)
{
struct spa_latency_info latency;
uint8_t buffer[1024];
struct spa_pod_builder b;
const struct spa_pod *params[1];
const struct spa_pod *params[2];
if (param == NULL || spa_latency_parse(param, &latency) < 0)
return;
impl->latency[latency.direction] = latency;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
params[0] = spa_latency_build(&b, SPA_PARAM_Latency, &latency);
pw_stream_update_params(other, params, 1);
build_latency_params(impl, &b, params, 2);
pw_stream_update_params(stream, params, 2);
pw_stream_update_params(other, params, 2);
impl->recalc_delay = true;
}
static void param_process_latency_changed(struct impl *impl, const struct spa_pod *param,
enum spa_direction direction, struct pw_stream *stream)
{
struct spa_process_latency_info info;
uint8_t buffer[4096];
struct spa_pod_builder b;
const struct spa_pod *params[3];
if (param == NULL)
spa_zero(info);
else if (spa_process_latency_parse(param, &info) < 0)
return;
impl->process_latency[direction] = info;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
params[0] = spa_process_latency_build(&b, SPA_PARAM_ProcessLatency, &info);
build_latency_params(impl, &b, &params[1], 2);
pw_stream_update_params(stream, params, 3);
}
static void param_tag_changed(struct impl *impl, const struct spa_pod *param,
struct pw_stream *other)
{
@ -527,7 +567,10 @@ static void capture_param_changed(void *data, uint32_t id, const struct spa_pod
param_format_changed(impl, param, impl->capture, true);
break;
case SPA_PARAM_Latency:
param_latency_changed(impl, param, impl->playback);
param_latency_changed(impl, param, impl->capture, impl->playback);
break;
case SPA_PARAM_ProcessLatency:
param_process_latency_changed(impl, param, SPA_DIRECTION_INPUT, impl->capture);
break;
case SPA_PARAM_Tag:
param_tag_changed(impl, param, impl->playback);
@ -572,7 +615,10 @@ static void playback_param_changed(void *data, uint32_t id, const struct spa_pod
param_format_changed(impl, param, impl->playback, false);
break;
case SPA_PARAM_Latency:
param_latency_changed(impl, param, impl->capture);
param_latency_changed(impl, param, impl->playback, impl->capture);
break;
case SPA_PARAM_ProcessLatency:
param_process_latency_changed(impl, param, SPA_DIRECTION_OUTPUT, impl->playback);
break;
case SPA_PARAM_Tag:
param_tag_changed(impl, param, impl->capture);
@ -804,6 +850,8 @@ int pipewire__module_init(struct pw_impl_module *module, const char *args)
impl->module = module;
impl->context = context;
impl->latency[SPA_DIRECTION_INPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_INPUT);
impl->latency[SPA_DIRECTION_OUTPUT] = SPA_LATENCY_INFO(SPA_DIRECTION_OUTPUT);
if (pw_properties_get(props, PW_KEY_NODE_GROUP) == NULL)
pw_properties_setf(props, PW_KEY_NODE_GROUP, "loopback-%u-%u", pid, id);