From 9090f19b0a74bb02995983de61332d28d0da0bd3 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 23 Aug 2021 11:11:11 +0200 Subject: [PATCH] audioconvert: improve passthrough mode When in passthrough mode, use the position io to update the io_rate_match fields for the follower. This makes it possible for the follower to also provide the right amount of data when the converter is not selected in passthrough. Add an option to configure the converter in None port config where it removes all the ports. We can use this when removing the converter to make sure all it's ports are removed. When we remove the converter, make sure we expose the follower ports directly so we can use them for passthrough. --- spa/plugins/audioconvert/audioadapter.c | 116 +++++++++++++++--------- spa/plugins/audioconvert/audioconvert.c | 36 +++++--- 2 files changed, 97 insertions(+), 55 deletions(-) diff --git a/spa/plugins/audioconvert/audioadapter.c b/spa/plugins/audioconvert/audioadapter.c index 7c9b9989f..465ad73d1 100644 --- a/spa/plugins/audioconvert/audioadapter.c +++ b/spa/plugins/audioconvert/audioadapter.c @@ -75,6 +75,7 @@ struct impl { struct spa_io_buffers io_buffers; struct spa_io_rate_match io_rate_match; + struct spa_io_position *io_position; uint64_t info_all; struct spa_node_info info; @@ -419,33 +420,68 @@ static int configure_format(struct impl *this, uint32_t flags, const struct spa_ return res; } +static int configure_convert(struct impl *this, uint32_t mode) +{ + struct spa_pod_builder b = { 0 }; + uint8_t buffer[1024]; + struct spa_pod *param; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + + spa_log_debug(this->log, "%p: configure convert %p", this, this->target); + + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig, + SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(this->direction), + SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(mode)); + + return spa_node_set_param(this->convert, SPA_PARAM_PortConfig, 0, param); +} + +extern const struct spa_handle_factory spa_audioconvert_factory; + +static const struct spa_node_events follower_node_events; + static int reconfigure_mode(struct impl *this, bool passthrough, enum spa_direction direction, struct spa_audio_info *info) { - int res = 0; + int res = 0; + char buf[1024]; + struct spa_pod_builder b = { 0 }; + struct spa_pod *format = NULL; - spa_log_debug(this->log, NAME " %p: passthrough mode %d", this, passthrough); + spa_log_debug(this->log, NAME " %p: passthrough mode %d", this, passthrough); + + if (passthrough) + configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_none); + else + configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_dsp); /* set new target */ this->target = passthrough ? this->follower : this->convert; - if (info) { - char buf[1024]; - struct spa_pod_builder b = { 0 }; - struct spa_pod *format; - spa_pod_builder_init(&b, buf, sizeof(buf)); - format = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &info->info.raw); - if ((res = configure_format (this, 0, format)) < 0) - return res; + spa_pod_builder_init(&b, buf, sizeof(buf)); + if (info) + format = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &info->info.raw); - this->info.change_mask |= SPA_NODE_CHANGE_MASK_FLAGS | SPA_NODE_CHANGE_MASK_PARAMS; - this->info.flags &= ~SPA_NODE_FLAG_NEED_CONFIGURE; - this->params[IDX_Props].user++; - } + if ((res = configure_format (this, 0, format)) < 0) + return res; - emit_node_info(this, false); + if (passthrough) { + struct spa_hook l; - return 0; + spa_zero(l); + spa_node_add_listener(this->follower, &l, &follower_node_events, this); + spa_hook_remove(&l); + } + + this->info.change_mask |= SPA_NODE_CHANGE_MASK_FLAGS | SPA_NODE_CHANGE_MASK_PARAMS; + this->info.flags &= ~SPA_NODE_FLAG_NEED_CONFIGURE; + this->params[IDX_Props].user++; + + emit_node_info(this, false); + + return 0; } static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, @@ -483,8 +519,10 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, struct spa_audio_info info = { 0, }, *infop = NULL; int monitor = false; - if (this->started) + if (this->started) { + spa_log_error(this->log, "was started"); return -EIO; + } if (spa_pod_parse_object(param, SPA_TYPE_OBJECT_ParamPortConfig, NULL, @@ -562,6 +600,14 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size) spa_return_val_if_fail(this != NULL, -EINVAL); + switch (id) { + case SPA_IO_Position: + this->io_position = data; + break; + default: + break; + } + if (this->target) res = spa_node_set_io(this->target, id, data, size); @@ -667,15 +713,17 @@ static int impl_node_send_command(void *object, const struct spa_command *comman } if ((res = spa_node_send_command(this->target, command)) < 0) { - spa_log_error(this->log, NAME " %p: can't send command: %s", - this, spa_strerror(res)); + spa_log_error(this->log, NAME " %p: can't send command %d: %s", + this, SPA_NODE_COMMAND_ID(command), + spa_strerror(res)); return res; } if (this->target != this->follower) { if ((res = spa_node_send_command(this->follower, command)) < 0) { - spa_log_error(this->log, NAME " %p: can't send command: %s", - this, spa_strerror(res)); + spa_log_error(this->log, NAME " %p: can't send command %d: %s", + this, SPA_NODE_COMMAND_ID(command), + spa_strerror(res)); return res; } } @@ -1163,8 +1211,11 @@ static int impl_node_process(void *object) spa_log_trace_fp(this->log, "%p: process convert:%p driver:%d", this, this->convert, this->driver); - if (this->target == this->follower) + if (this->target == this->follower) { + if (this->io_position) + this->io_rate_match.size = this->io_position->clock.duration; return spa_node_process(this->follower); + } if (this->direction == SPA_DIRECTION_INPUT) { /* an input node (sink). @@ -1304,25 +1355,6 @@ static int impl_clear(struct spa_handle *handle) return 0; } -static int configure_adapt(struct impl *this) -{ - struct spa_pod_builder b = { 0 }; - uint8_t buffer[1024]; - struct spa_pod *param; - - spa_pod_builder_init(&b, buffer, sizeof(buffer)); - - spa_log_debug(this->log, "%p: configure convert %p", this, this->target); - - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig, - SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(this->direction), - SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_dsp)); - - return spa_node_set_param(this->target, SPA_PARAM_PortConfig, 0, param); -} - -extern const struct spa_handle_factory spa_audioconvert_factory; static size_t impl_get_size(const struct spa_handle_factory *factory, @@ -1410,7 +1442,7 @@ impl_init(const struct spa_handle_factory *factory, spa_node_add_listener(this->convert, &this->convert_listener, &convert_node_events, this); - configure_adapt(this); + configure_convert(this, SPA_PARAM_PORT_CONFIG_MODE_dsp); link_io(this); diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index bbf547970..6606b0842 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -580,14 +580,16 @@ static void fmt_input_port_info(void *data, const struct spa_port_info *info) { struct impl *this = data; + bool is_monitor = IS_MONITOR_PORT(this, direction, port); if (this->fmt_removing[direction]) info = NULL; + if (is_monitor && this->fmt_removing[SPA_DIRECTION_INPUT]) + info = NULL; spa_log_debug(this->log, "%p: %d.%d info", this, direction, port); - if (direction == SPA_DIRECTION_INPUT || - IS_MONITOR_PORT(this, direction, port)) + if (direction == SPA_DIRECTION_INPUT || is_monitor) spa_node_emit_port_info(&this->hooks, direction, port, info); } @@ -687,11 +689,13 @@ static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode m case SPA_PARAM_PORT_CONFIG_MODE_dsp: new = direction == SPA_DIRECTION_INPUT ? this->merger : this->splitter; break; + case SPA_PARAM_PORT_CONFIG_MODE_none: + new = NULL; + break; default: return -EIO; } - this->mode[direction] = mode; clean_convert(this); this->fmt[direction] = new; @@ -700,7 +704,7 @@ static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode m do_signal = this->fmt[direction] != old || mode == SPA_PARAM_PORT_CONFIG_MODE_dsp; - if (do_signal) { + if (do_signal && old != NULL) { /* change, remove old ports. We trigger a new port_info event * on the old node with info set to NULL to mark delete */ if (this->have_fmt_listener[direction]) { @@ -719,7 +723,9 @@ static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode m } } - if (info) { + this->mode[direction] = mode; + + if (info && new != NULL) { struct spa_pod_builder b = { 0 }; uint8_t buffer[1024]; struct spa_pod *param; @@ -752,7 +758,7 @@ static int reconfigure_mode(struct impl *this, enum spa_param_port_config_mode m } /* notify ports of new node */ - if (do_signal) { + if (do_signal && new != NULL) { if (this->have_fmt_listener[direction]) spa_hook_remove(&this->fmt_listener[direction]); @@ -816,10 +822,10 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags, spa_log_debug(this->log, "mode:%d direction:%d %d", mode, dir, monitor); switch (mode) { - case SPA_PARAM_PORT_CONFIG_MODE_none: case SPA_PARAM_PORT_CONFIG_MODE_passthrough: return -ENOTSUP; + case SPA_PARAM_PORT_CONFIG_MODE_none: case SPA_PARAM_PORT_CONFIG_MODE_convert: break; @@ -912,19 +918,23 @@ impl_node_add_listener(void *object, this->add_listener = true; spa_zero(l); - spa_node_add_listener(this->fmt[SPA_DIRECTION_INPUT], - &l[0], &fmt_input_events, this); + if (this->fmt[SPA_DIRECTION_INPUT]) + spa_node_add_listener(this->fmt[SPA_DIRECTION_INPUT], + &l[0], &fmt_input_events, this); spa_node_add_listener(this->channelmix, &l[1], &channelmix_events, this); spa_node_add_listener(this->resample, &l[2], &resample_events, this); - spa_node_add_listener(this->fmt[SPA_DIRECTION_OUTPUT], - &l[3], &fmt_output_events, this); + if (this->fmt[SPA_DIRECTION_OUTPUT]) + spa_node_add_listener(this->fmt[SPA_DIRECTION_OUTPUT], + &l[3], &fmt_output_events, this); - spa_hook_remove(&l[0]); + if (this->fmt[SPA_DIRECTION_INPUT]) + spa_hook_remove(&l[0]); spa_hook_remove(&l[1]); spa_hook_remove(&l[2]); - spa_hook_remove(&l[3]); + if (this->fmt[SPA_DIRECTION_OUTPUT]) + spa_hook_remove(&l[3]); this->add_listener = false;