mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-01 22:58:50 -04:00
audioadapter: add support for passthrough mode
Allows audioadapter to behave as its follower
This commit is contained in:
parent
49c1c0386f
commit
cad64bc992
2 changed files with 172 additions and 6 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue