audioadapter: add support for passthrough mode

Allows audioadapter to behave as its follower
This commit is contained in:
Julian Bouzas 2021-08-13 11:36:48 -04:00 committed by Wim Taymans
parent 49c1c0386f
commit cad64bc992
2 changed files with 172 additions and 6 deletions

View file

@ -293,6 +293,9 @@ static int negotiate_buffers(struct impl *this)
spa_log_debug(this->log, NAME" %p: %d", this, this->n_buffers);
if (this->target == this->follower)
return 0;
if (this->n_buffers > 0)
return 0;
@ -392,7 +395,7 @@ static int configure_format(struct impl *this, uint32_t flags, const struct spa_
if (format && spa_log_level_enabled(this->log, SPA_LOG_LEVEL_DEBUG))
spa_debug_format(0, NULL, format);
if (this->convert) {
if (this->target != this->follower && this->convert) {
if ((res = spa_node_port_set_param(this->convert,
SPA_DIRECTION_REVERSE(this->direction), 0,
SPA_PARAM_Format, flags,
@ -416,6 +419,35 @@ static int configure_format(struct impl *this, uint32_t flags, const struct spa_
return res;
}
static int reconfigure_mode(struct impl *this, bool passthrough,
enum spa_direction direction, struct spa_audio_info *info)
{
int res = 0;
spa_log_debug(this->log, NAME " %p: passthrough mode %d", this, passthrough);
/* 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;
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,
const struct spa_pod *param)
{
@ -444,13 +476,66 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
break;
case SPA_PARAM_PortConfig:
{
enum spa_direction dir;
enum spa_param_port_config_mode mode;
struct spa_pod *format = NULL;
struct spa_audio_info info = { 0, }, *infop = NULL;
int monitor = false;
if (this->started)
return -EIO;
if (spa_pod_parse_object(param,
SPA_TYPE_OBJECT_ParamPortConfig, NULL,
SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(&dir),
SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(&mode),
SPA_PARAM_PORT_CONFIG_monitor, SPA_POD_OPT_Bool(&monitor),
SPA_PARAM_PORT_CONFIG_format, SPA_POD_OPT_Pod(&format)) < 0)
return -EINVAL;
if (format) {
if (!spa_pod_is_object_type(format, SPA_TYPE_OBJECT_Format))
return -EINVAL;
if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0)
return res;
if (info.media_type != SPA_MEDIA_TYPE_audio ||
info.media_subtype != SPA_MEDIA_SUBTYPE_raw)
return -ENOTSUP;
if (spa_format_audio_raw_parse(format, &info.info.raw) < 0)
return -EINVAL;
if (info.info.raw.channels == 0 || info.info.raw.rate == 0)
return -EINVAL;
infop = &info;
}
switch (mode) {
case SPA_PARAM_PORT_CONFIG_MODE_none:
return -ENOTSUP;
case SPA_PARAM_PORT_CONFIG_MODE_passthrough:
if ((res = reconfigure_mode(this, true, dir, infop)) < 0)
return res;
break;
case SPA_PARAM_PORT_CONFIG_MODE_convert:
case SPA_PARAM_PORT_CONFIG_MODE_dsp:
if ((res = reconfigure_mode(this, false, dir, NULL)) < 0)
return res;
break;
default:
return -EINVAL;
}
if (this->target != this->follower) {
if ((res = spa_node_set_param(this->target, id, flags, param)) < 0)
return res;
}
break;
}
case SPA_PARAM_Props:
if (this->target != this->follower)
@ -497,6 +582,9 @@ static int negotiate_format(struct impl *this)
if (this->have_format)
return 0;
if (this->target == this->follower)
return 0;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
spa_log_debug(this->log, NAME " %p: negiotiate", this);
@ -655,12 +743,17 @@ static void convert_port_info(void *data,
spa_log_trace(this->log, NAME" %p: port info %d:%d", this,
direction, port_id);
spa_node_emit_port_info(&this->hooks, direction, port_id, info);
if (this->target != this->follower)
spa_node_emit_port_info(&this->hooks, direction, port_id, info);
}
static void convert_result(void *data, int seq, int res, uint32_t type, const void *result)
{
struct impl *this = data;
if (this->target == this->follower)
return;
spa_log_trace(this->log, NAME" %p: result %d %d", this, seq, res);
spa_node_emit_result(&this->hooks, seq, res, type, result);
}
@ -737,6 +830,8 @@ static int recalc_latency(struct impl *this, enum spa_direction direction, uint3
spa_log_debug(this->log, NAME" %p: ", this);
if (this->target == this->follower)
return 0;
while (true) {
spa_pod_builder_init(&b, buffer, sizeof(buffer));
@ -803,12 +898,27 @@ static void follower_port_info(void *data,
}
}
emit_node_info(this, false);
if (this->target == this->follower)
spa_node_emit_port_info(&this->hooks, direction, port_id, info);
}
static void follower_result(void *data, int seq, int res, uint32_t type, const void *result)
{
struct impl *this = data;
if (this->target != this->follower)
return;
spa_log_trace(this->log, NAME" %p: result %d %d", this, seq, res);
spa_node_emit_result(&this->hooks, seq, res, type, result);
}
static const struct spa_node_events follower_node_events = {
SPA_VERSION_NODE_EVENTS,
.info = follower_info,
.port_info = follower_port_info,
.result = follower_result,
};
static int follower_ready(void *data, int status)
@ -817,10 +927,12 @@ static int follower_ready(void *data, int status)
spa_log_trace_fp(this->log, NAME " %p: ready %d", this, status);
this->driver = true;
if (this->target != this->follower) {
this->driver = true;
if (this->direction == SPA_DIRECTION_OUTPUT)
status = spa_node_process(this->convert);
if (this->direction == SPA_DIRECTION_OUTPUT)
status = spa_node_process(this->convert);
}
return spa_node_call_ready(&this->callbacks, status);
}
@ -830,7 +942,7 @@ static int follower_reuse_buffer(void *data, uint32_t port_id, uint32_t buffer_i
int res;
struct impl *this = data;
if (this->convert)
if (this->target != this->follower && this->convert)
res = spa_node_port_reuse_buffer(this->convert, port_id, buffer_id);
else
res = spa_node_call_reuse_buffer(&this->callbacks, port_id, buffer_id);
@ -1051,6 +1163,9 @@ 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)
return spa_node_process(this->follower);
if (this->direction == SPA_DIRECTION_INPUT) {
/* an input node (sink).
* First we run the converter to process the input for the follower

View file

@ -171,6 +171,14 @@ static void port_info_5_1(void *data,
spa_assert_se(port < 6);
}
static void port_info_1_1(void *data,
enum spa_direction direction, uint32_t port,
const struct spa_port_info *info)
{
spa_assert_se(direction == SPA_DIRECTION_OUTPUT);
spa_assert_se(port < 2);
}
static int test_split_setup(struct context *ctx)
{
struct spa_pod_builder b = { 0 };
@ -233,6 +241,48 @@ static int test_split_setup(struct context *ctx)
return 0;
}
static int test_passthrough_setup(struct context *ctx)
{
struct spa_pod_builder b = { 0 };
uint8_t buffer[1024];
struct spa_pod *param;
struct spa_audio_info_raw info;
int res;
struct spa_hook listener;
static const struct spa_node_events node_events = {
SPA_VERSION_NODE_EVENTS,
.port_info = port_info_1_1,
};
/* internal format */
spa_zero(info);
info.format = SPA_AUDIO_FORMAT_S16;
info.channels = 2;
info.rate = 44100;
info.position[0] = SPA_AUDIO_CHANNEL_FL;
info.position[1] = SPA_AUDIO_CHANNEL_FR;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &info);
spa_log_debug(&logger.log, "set profile %d@%d", info.channels, info.rate);
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig,
SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(SPA_DIRECTION_OUTPUT),
SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(SPA_PARAM_PORT_CONFIG_MODE_passthrough),
SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param));
res = spa_node_set_param(ctx->adapter_node, SPA_PARAM_PortConfig, 0, param);
spa_assert_se(res == 0);
spa_zero(listener);
spa_node_add_listener(ctx->adapter_node,
&listener, &node_events, ctx);
spa_hook_remove(&listener);
return 0;
}
int main(int argc, char *argv[])
{
@ -244,6 +294,7 @@ int main(int argc, char *argv[])
test_init_state(&ctx);
test_split_setup(&ctx);
test_passthrough_setup(&ctx);
clean_context(&ctx);