adapter: add adapter.auto-port-config property

This automatically configures the node in a supported format with the
highest amount of channels. Options are available to enable monitor
and to override the port channels as AUX or UNKNOWN.
This commit is contained in:
Wim Taymans 2022-01-20 18:06:19 +01:00
parent aa128ed489
commit 5a72e2769a
2 changed files with 143 additions and 20 deletions

View file

@ -38,6 +38,8 @@
#include <spa/utils/string.h>
#include <spa/utils/type-info.h>
#include <spa/param/format.h>
#include <spa/param/audio/format.h>
#include <spa/param/audio/format-utils.h>
#include <spa/param/format-utils.h>
#include <spa/debug/types.h>
#include <spa/debug/pod.h>
@ -227,6 +229,114 @@ static int find_format(struct pw_impl_node *node, enum pw_direction direction,
return 0;
}
static int do_auto_port_config(struct node *n, const char *str)
{
uint32_t state = 0, i;
uint8_t buffer[4096];
struct spa_pod_builder b;
#define POSITION_PRESERVE 0
#define POSITION_AUX 1
#define POSITION_UNKNOWN 2
int res, position = POSITION_PRESERVE;
struct spa_pod *param;
uint32_t media_type, media_subtype;
bool have_format = false, monitor = false;
struct spa_audio_info format = { 0, };
enum spa_param_port_config_mode mode = SPA_PARAM_PORT_CONFIG_MODE_none;
struct spa_json it[2];
char key[1024], val[256];
spa_json_init(&it[0], str, strlen(str));
if (spa_json_enter_object(&it[0], &it[1]) <= 0)
return -EINVAL;
while (spa_json_get_string(&it[1], key, sizeof(key)) > 0) {
if (spa_json_get_string(&it[1], val, sizeof(val)) <= 0)
break;
if (spa_streq(key, "mode")) {
mode = spa_debug_type_find_type_short(spa_type_param_port_config_mode, val);
if (mode == SPA_ID_INVALID)
mode = SPA_PARAM_PORT_CONFIG_MODE_none;
} else if (spa_streq(key, "monitor")) {
monitor = spa_atob(val);
} else if (spa_streq(key, "position")) {
if (spa_streq(val, "unknown"))
position = POSITION_UNKNOWN;
else if (spa_streq(val, "aux"))
position = POSITION_AUX;
else
position = POSITION_PRESERVE;
}
}
while (true) {
struct spa_audio_info info = { 0, };
struct spa_pod *position = NULL;
uint32_t n_position = 0;
spa_pod_builder_init(&b, buffer, sizeof(buffer));
if ((res = spa_node_port_enum_params_sync(pw_impl_node_get_implementation(n->follower),
n->direction == PW_DIRECTION_INPUT ?
SPA_DIRECTION_INPUT :
SPA_DIRECTION_OUTPUT, 0,
SPA_PARAM_EnumFormat, &state,
NULL, &param, &b)) != 1)
break;
if ((res = spa_format_parse(param, &media_type, &media_subtype)) < 0)
continue;
if (media_type != SPA_MEDIA_TYPE_audio ||
media_subtype != SPA_MEDIA_SUBTYPE_raw)
continue;
spa_pod_object_fixate((struct spa_pod_object*)param);
if (spa_pod_parse_object(param,
SPA_TYPE_OBJECT_Format, NULL,
SPA_FORMAT_AUDIO_format, SPA_POD_Id(&info.info.raw.format),
SPA_FORMAT_AUDIO_rate, SPA_POD_Int(&info.info.raw.rate),
SPA_FORMAT_AUDIO_channels, SPA_POD_Int(&info.info.raw.channels),
SPA_FORMAT_AUDIO_position, SPA_POD_OPT_Pod(&position)) < 0)
continue;
if (position != NULL)
n_position = spa_pod_copy_array(position, SPA_TYPE_Id,
info.info.raw.position, SPA_AUDIO_MAX_CHANNELS);
if (n_position == 0 || n_position != info.info.raw.channels)
SPA_FLAG_SET(info.info.raw.flags, SPA_AUDIO_FLAG_UNPOSITIONED);
if (format.info.raw.channels >= info.info.raw.channels)
continue;
format = info;
have_format = true;
}
if (!have_format)
return -ENOENT;
if (position == POSITION_AUX) {
for (i = 0; i < format.info.raw.channels; i++)
format.info.raw.position[i] = SPA_AUDIO_CHANNEL_START_Aux + i;
} else if (position == POSITION_UNKNOWN) {
for (i = 0; i < format.info.raw.channels; i++)
format.info.raw.position[i] = SPA_AUDIO_CHANNEL_UNKNOWN;
}
spa_pod_builder_init(&b, buffer, sizeof(buffer));
param = spa_format_audio_raw_build(&b, SPA_PARAM_Format, &format.info.raw);
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamPortConfig, SPA_PARAM_PortConfig,
SPA_PARAM_PORT_CONFIG_direction, SPA_POD_Id(n->direction),
SPA_PARAM_PORT_CONFIG_mode, SPA_POD_Id(mode),
SPA_PARAM_PORT_CONFIG_monitor, SPA_POD_Bool(monitor),
SPA_PARAM_PORT_CONFIG_format, SPA_POD_Pod(param));
pw_impl_node_set_param(n->node, SPA_PARAM_PortConfig, 0, param);
return 0;
}
struct pw_impl_node *pw_adapter_new(struct pw_context *context,
struct pw_impl_node *follower,
struct pw_properties *props,
@ -330,6 +440,9 @@ struct pw_impl_node *pw_adapter_new(struct pw_context *context,
pw_impl_node_add_listener(node, &n->node_listener, &node_events, n);
if ((str = pw_properties_get(props, "adapter.auto-port-config")) != NULL)
do_auto_port_config(n, str);
spa_dict_for_each(it, &props->dict) {
if (spa_strstartswith(it->key, "node.param.")) {
if ((res = handle_node_param(node, &it->key[11], it->value)) < 0)