pulse-server: refactor card profiles parsing

This commit is contained in:
Wim Taymans 2020-10-28 15:20:36 +01:00
parent 0320eca79e
commit d518e02b19

View file

@ -2681,244 +2681,23 @@ static int fill_module_info(struct client *client, struct message *m,
return 0; return 0;
} }
static int fill_card_info(struct client *client, struct message *m,
struct pw_manager_object *o)
{
struct pw_device_info *info = o->info;
const char *str;
const char **names;
uint32_t module_id = SPA_ID_INVALID, i;
struct pw_manager_param *p;
uint32_t n_profiles, n_ports, active_profile = SPA_ID_INVALID;
const char *active_name = NULL;
if (o == NULL || info == NULL || info->props == NULL || !is_card(o))
return ERR_NOENTITY;
if ((str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID)) != NULL)
module_id = (uint32_t)atoi(str);
message_put(m,
TAG_U32, o->id, /* card index */
TAG_STRING, spa_dict_lookup(info->props, PW_KEY_DEVICE_NAME),
TAG_U32, module_id,
TAG_STRING, spa_dict_lookup(info->props, PW_KEY_DEVICE_API),
TAG_INVALID);
n_profiles = n_ports = 0;
spa_list_for_each(p, &o->param_list, link) {
switch (p->id) {
case SPA_PARAM_Profile:
spa_pod_parse_object(p->param,
SPA_TYPE_OBJECT_ParamProfile, NULL,
SPA_PARAM_PROFILE_index, SPA_POD_Int(&active_profile));
break;
case SPA_PARAM_EnumProfile:
n_profiles++;
break;
case SPA_PARAM_EnumRoute:
n_ports++;
break;
}
}
message_put(m,
TAG_U32, n_profiles, /* n_profiles */
TAG_INVALID);
names = alloca(n_profiles * sizeof(char*));
i = 0;
spa_list_for_each(p, &o->param_list, link) {
uint32_t id, priority = 0, available = 0, n_sources = 0, n_sinks = 0;
const char *name = NULL;
const char *description = NULL;
struct spa_pod *classes = NULL;
if (p->id != SPA_PARAM_EnumProfile)
continue;
if (spa_pod_parse_object(p->param,
SPA_TYPE_OBJECT_ParamProfile, NULL,
SPA_PARAM_PROFILE_index, SPA_POD_Int(&id),
SPA_PARAM_PROFILE_name, SPA_POD_String(&name),
SPA_PARAM_PROFILE_description, SPA_POD_OPT_String(&description),
SPA_PARAM_PROFILE_priority, SPA_POD_OPT_Int(&priority),
SPA_PARAM_PROFILE_available, SPA_POD_OPT_Id(&available),
SPA_PARAM_PROFILE_classes, SPA_POD_OPT_Pod(&classes)) < 0) {
pw_log_warn("device %d: can't parse profile", o->id);
continue;
}
if (id == active_profile)
active_name = name;
names[i++] = name;
if (classes != NULL) {
struct spa_pod *iter;
SPA_POD_STRUCT_FOREACH(classes, iter) {
struct spa_pod_parser prs;
struct spa_pod_frame f[1];
char *class;
uint32_t count;
spa_pod_parser_pod(&prs, iter);
if (spa_pod_parser_get_struct(&prs,
SPA_POD_String(&class),
SPA_POD_Int(&count)) < 0)
continue;
if (strcmp(class, "Audio/Sink") == 0)
n_sinks += count;
else if (strcmp(class, "Audio/Source") == 0)
n_sources += count;
spa_pod_parser_pop(&prs, &f[0]);
}
}
message_put(m,
TAG_STRING, name, /* profile name */
TAG_STRING, description, /* profile description */
TAG_U32, n_sinks, /* n_sinks */
TAG_U32, n_sources, /* n_sources */
TAG_U32, priority, /* priority */
TAG_INVALID);
if (client->version >= 29) {
message_put(m,
TAG_U32, available != SPA_PARAM_AVAILABILITY_no, /* available */
TAG_INVALID);
}
}
message_put(m,
TAG_STRING, active_name, /* active profile name */
TAG_PROPLIST, info->props,
TAG_INVALID);
if (client->version < 26)
return 0;
message_put(m,
TAG_U32, n_ports, /* n_ports */
TAG_INVALID);
spa_list_for_each(p, &o->param_list, link) {
uint32_t id, priority, *pr = NULL, n_pr = 0, port_type = 0;
enum spa_direction direction;
const char *name = NULL, *description = NULL, *availability_group = NULL;
enum spa_param_availability available = SPA_PARAM_AVAILABILITY_unknown;
struct spa_pod *profiles = NULL, *info = NULL, *devices = NULL;
struct spa_dict dict, *pdict = NULL;
struct spa_dict_item *items;
if (p->id != SPA_PARAM_EnumRoute)
continue;
if (spa_pod_parse_object(p->param,
SPA_TYPE_OBJECT_ParamRoute, NULL,
SPA_PARAM_ROUTE_index, SPA_POD_Int(&id),
SPA_PARAM_ROUTE_direction, SPA_POD_Id(&direction),
SPA_PARAM_ROUTE_name, SPA_POD_String(&name),
SPA_PARAM_ROUTE_description, SPA_POD_OPT_String(&description),
SPA_PARAM_ROUTE_priority, SPA_POD_OPT_Int(&priority),
SPA_PARAM_ROUTE_available, SPA_POD_OPT_Id(&available),
SPA_PARAM_ROUTE_info, SPA_POD_OPT_Pod(&info),
SPA_PARAM_ROUTE_devices, SPA_POD_OPT_Pod(&devices),
SPA_PARAM_ROUTE_profiles, SPA_POD_OPT_Pod(&profiles)) < 0)
continue;
while (info) {
struct spa_pod_parser prs;
struct spa_pod_frame f[1];
int32_t n, n_items;
spa_pod_parser_pod(&prs, info);
if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 ||
spa_pod_parser_get_int(&prs, &n_items) < 0)
break;
items = alloca(n_items * sizeof(*items));
for (n = 0; n < n_items; n++) {
if (spa_pod_parser_get(&prs,
SPA_POD_String(&items[n].key),
SPA_POD_String(&items[n].value),
NULL) < 0)
break;
if (strcmp(items[n].key, "port.availability-group") == 0)
availability_group = items[n].value;
else if (strcmp(items[n].key, "port.type") == 0)
port_type = port_type_value(items[n].value);
}
spa_pod_parser_pop(&prs, &f[0]);
dict = SPA_DICT_INIT(items, n);
pdict = &dict;
break;
}
message_put(m,
TAG_STRING, name, /* port name */
TAG_STRING, description, /* port description */
TAG_U32, priority, /* port priority */
TAG_U32, available, /* port available */
TAG_U8, direction == SPA_DIRECTION_INPUT ? 2 : 1, /* port direction */
TAG_PROPLIST, pdict, /* port proplist */
TAG_INVALID);
if (profiles)
pr = spa_pod_get_array(profiles, &n_pr);
message_put(m,
TAG_U32, n_pr, /* n_profiles */
TAG_INVALID);
for (i = 0; i < n_pr; i++) {
message_put(m,
TAG_STRING, names[pr[i]], /* profile name */
TAG_INVALID);
}
if (client->version >= 27) {
message_put(m,
TAG_S64, 0LL, /* port latency */
TAG_INVALID);
}
if (client->version >= 34) {
message_put(m,
TAG_STRING, availability_group, /* available group */
TAG_U32, port_type, /* port type */
TAG_INVALID);
}
}
return 0;
}
static bool array_contains(struct spa_pod *pod, uint32_t val)
{
uint32_t *vals, n, n_vals;
if (pod == NULL)
return true;
vals = spa_pod_get_array(pod, &n_vals);
if (vals == NULL || n_vals == 0)
return false;
for (n = 0; n < n_vals; n++)
if (vals[n] == val)
return true;
return false;
}
struct device_info { struct device_info {
enum pw_direction direction; uint32_t direction;
uint32_t device; uint32_t device;
uint32_t n_profiles; uint32_t n_profiles;
uint32_t active_profile; uint32_t active_profile;
const char *active_profile_name;
uint32_t n_ports; uint32_t n_ports;
uint32_t active_port; uint32_t active_port;
const char *active_port_name; const char *active_port_name;
struct volume_info volume_info; struct volume_info volume_info;
unsigned int have_volume:1; unsigned int have_volume:1;
}; };
#define CARD_INFO_INIT(_d) (struct device_info) { \ #define DEVICE_INFO_INIT(_d) (struct device_info) { \
.direction = _d, \ .direction = _d, \
.device = SPA_ID_INVALID, \ .device = SPA_ID_INVALID, \
.active_profile = SPA_ID_INVALID, \ .active_profile = SPA_ID_INVALID, \
@ -2969,13 +2748,104 @@ static void collect_device_info(struct pw_manager_object *card, struct device_in
} }
} }
struct port_info { struct profile_info {
uint32_t id;
const char *name; const char *name;
const char *description; const char *description;
uint32_t priority; uint32_t priority;
uint32_t available; uint32_t available;
uint32_t n_sources;
uint32_t n_sinks;
};
static uint32_t collect_profile_info(struct pw_manager_object *card, struct device_info *dev_info,
struct profile_info *profile_info)
{
struct pw_manager_param *p;
uint32_t n;
n = 0;
spa_list_for_each(p, &card->param_list, link) {
struct profile_info *pi;
struct spa_pod *classes = NULL;
if (p->id != SPA_PARAM_EnumProfile)
continue;
pi = &profile_info[n];
spa_zero(*pi);
if (spa_pod_parse_object(p->param,
SPA_TYPE_OBJECT_ParamProfile, NULL,
SPA_PARAM_PROFILE_index, SPA_POD_Int(&pi->id),
SPA_PARAM_PROFILE_name, SPA_POD_String(&pi->name),
SPA_PARAM_PROFILE_description, SPA_POD_OPT_String(&pi->description),
SPA_PARAM_PROFILE_priority, SPA_POD_OPT_Int(&pi->priority),
SPA_PARAM_PROFILE_available, SPA_POD_OPT_Id(&pi->available),
SPA_PARAM_PROFILE_classes, SPA_POD_OPT_Pod(&classes)) < 0) {
continue;
}
if (pi->id == dev_info->active_profile)
dev_info->active_profile_name = pi->name;
if (classes != NULL) {
struct spa_pod *iter;
SPA_POD_STRUCT_FOREACH(classes, iter) {
struct spa_pod_parser prs;
struct spa_pod_frame f[1];
char *class;
uint32_t count;
spa_pod_parser_pod(&prs, iter);
if (spa_pod_parser_get_struct(&prs,
SPA_POD_String(&class),
SPA_POD_Int(&count)) < 0)
continue;
if (strcmp(class, "Audio/Sink") == 0)
pi->n_sinks += count;
else if (strcmp(class, "Audio/Source") == 0)
pi->n_sources += count;
spa_pod_parser_pop(&prs, &f[0]);
}
}
n++;
}
return n;
}
static bool array_contains(uint32_t *vals, uint32_t n_vals, uint32_t val)
{
uint32_t n;
if (vals == NULL || n_vals == 0)
return false;
for (n = 0; n < n_vals; n++)
if (vals[n] == val)
return true;
return false;
}
struct port_info {
uint32_t id;
uint32_t direction;
const char *name;
const char *description;
uint32_t priority;
uint32_t available;
const char *availability_group; const char *availability_group;
uint32_t type; uint32_t type;
uint32_t n_devices;
uint32_t *devices;
uint32_t n_profiles;
uint32_t *profiles;
uint32_t n_props;
struct spa_pod *info;
}; };
static uint32_t collect_port_info(struct pw_manager_object *card, struct device_info *dev_info, static uint32_t collect_port_info(struct pw_manager_object *card, struct device_info *dev_info,
@ -2989,8 +2859,7 @@ static uint32_t collect_port_info(struct pw_manager_object *card, struct device_
n = 0; n = 0;
spa_list_for_each(p, &card->param_list, link) { spa_list_for_each(p, &card->param_list, link) {
uint32_t id, direction; struct spa_pod *devices = NULL, *profiles = NULL;
struct spa_pod *devices = NULL, *profiles = NULL, *info = NULL;
struct port_info *pi; struct port_info *pi;
if (p->id != SPA_PARAM_EnumRoute) if (p->id != SPA_PARAM_EnumRoute)
@ -3001,38 +2870,46 @@ static uint32_t collect_port_info(struct pw_manager_object *card, struct device_
if (spa_pod_parse_object(p->param, if (spa_pod_parse_object(p->param,
SPA_TYPE_OBJECT_ParamRoute, NULL, SPA_TYPE_OBJECT_ParamRoute, NULL,
SPA_PARAM_ROUTE_index, SPA_POD_Int(&id), SPA_PARAM_ROUTE_index, SPA_POD_Int(&pi->id),
SPA_PARAM_ROUTE_direction, SPA_POD_Id(&direction), SPA_PARAM_ROUTE_direction, SPA_POD_Id(&pi->direction),
SPA_PARAM_ROUTE_name, SPA_POD_String(&pi->name), SPA_PARAM_ROUTE_name, SPA_POD_String(&pi->name),
SPA_PARAM_ROUTE_description, SPA_POD_OPT_String(&pi->description), SPA_PARAM_ROUTE_description, SPA_POD_OPT_String(&pi->description),
SPA_PARAM_ROUTE_priority, SPA_POD_OPT_Int(&pi->priority), SPA_PARAM_ROUTE_priority, SPA_POD_OPT_Int(&pi->priority),
SPA_PARAM_ROUTE_available, SPA_POD_OPT_Id(&pi->available), SPA_PARAM_ROUTE_available, SPA_POD_OPT_Id(&pi->available),
SPA_PARAM_ROUTE_info, SPA_POD_OPT_Pod(&info), SPA_PARAM_ROUTE_info, SPA_POD_OPT_Pod(&pi->info),
SPA_PARAM_ROUTE_devices, SPA_POD_OPT_Pod(&devices), SPA_PARAM_ROUTE_devices, SPA_POD_OPT_Pod(&devices),
SPA_PARAM_ROUTE_profiles, SPA_POD_OPT_Pod(&profiles)) < 0) SPA_PARAM_ROUTE_profiles, SPA_POD_OPT_Pod(&profiles)) < 0)
continue; continue;
if (direction != dev_info->direction) if (devices)
pi->devices = spa_pod_get_array(devices, &pi->n_devices);
if (profiles)
pi->profiles = spa_pod_get_array(profiles, &pi->n_profiles);
if (dev_info->direction != SPA_ID_INVALID &&
pi->direction != dev_info->direction)
continue; continue;
if (!array_contains(profiles, dev_info->active_profile)) if (dev_info->active_profile != SPA_ID_INVALID &&
!array_contains(pi->profiles, pi->n_profiles, dev_info->active_profile))
continue; continue;
if (!array_contains(devices, dev_info->device)) if (dev_info->device != SPA_ID_INVALID &&
!array_contains(pi->devices, pi->n_devices, dev_info->device))
continue; continue;
if (id == dev_info->active_port) if (pi->id == dev_info->active_port)
dev_info->active_port_name = pi->name; dev_info->active_port_name = pi->name;
while (info != NULL) { while (pi->info != NULL) {
struct spa_pod_parser prs; struct spa_pod_parser prs;
struct spa_pod_frame f[1]; struct spa_pod_frame f[1];
int32_t n, n_items; uint32_t n;
const char *key, *value; const char *key, *value;
spa_pod_parser_pod(&prs, info); spa_pod_parser_pod(&prs, pi->info);
if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 || if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 ||
spa_pod_parser_get_int(&prs, &n_items) < 0) spa_pod_parser_get_int(&prs, &pi->n_props) < 0)
break; break;
for (n = 0; n < n_items; n++) { for (n = 0; n < pi->n_props; n++) {
if (spa_pod_parser_get(&prs, if (spa_pod_parser_get(&prs,
SPA_POD_String(&key), SPA_POD_String(&key),
SPA_POD_String(&value), SPA_POD_String(&value),
@ -3051,6 +2928,143 @@ static uint32_t collect_port_info(struct pw_manager_object *card, struct device_
return n; return n;
} }
static struct spa_dict *collect_props(struct spa_pod *info, struct spa_dict *dict)
{
struct spa_pod_parser prs;
struct spa_pod_frame f[1];
int32_t n, n_items;
spa_pod_parser_pod(&prs, info);
if (spa_pod_parser_push_struct(&prs, &f[0]) < 0 ||
spa_pod_parser_get_int(&prs, &n_items) < 0)
return NULL;
for (n = 0; n < n_items; n++) {
if (spa_pod_parser_get(&prs,
SPA_POD_String(&dict->items[n].key),
SPA_POD_String(&dict->items[n].value),
NULL) < 0)
break;
}
spa_pod_parser_pop(&prs, &f[0]);
dict->n_items = n;
return dict;
}
static int fill_card_info(struct client *client, struct message *m,
struct pw_manager_object *o)
{
struct pw_device_info *info = o->info;
const char *str;
uint32_t module_id = SPA_ID_INVALID, n_profiles, n;
struct device_info dev_info = DEVICE_INFO_INIT(-1);
struct profile_info *profile_info;
if (o == NULL || info == NULL || info->props == NULL || !is_card(o))
return ERR_NOENTITY;
if ((str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID)) != NULL)
module_id = (uint32_t)atoi(str);
message_put(m,
TAG_U32, o->id, /* card index */
TAG_STRING, spa_dict_lookup(info->props, PW_KEY_DEVICE_NAME),
TAG_U32, module_id,
TAG_STRING, spa_dict_lookup(info->props, PW_KEY_DEVICE_API),
TAG_INVALID);
collect_device_info(o, &dev_info);
message_put(m,
TAG_U32, dev_info.n_profiles, /* n_profiles */
TAG_INVALID);
profile_info = alloca(dev_info.n_profiles * sizeof(*profile_info));
n_profiles = collect_profile_info(o, &dev_info, profile_info);
for (n = 0; n < n_profiles; n++) {
struct profile_info *pi = &profile_info[n];
message_put(m,
TAG_STRING, pi->name, /* profile name */
TAG_STRING, pi->description, /* profile description */
TAG_U32, pi->n_sinks, /* n_sinks */
TAG_U32, pi->n_sources, /* n_sources */
TAG_U32, pi->priority, /* priority */
TAG_INVALID);
if (client->version >= 29) {
message_put(m,
TAG_U32, pi->available != SPA_PARAM_AVAILABILITY_no, /* available */
TAG_INVALID);
}
}
message_put(m,
TAG_STRING, dev_info.active_profile_name, /* active profile name */
TAG_PROPLIST, info->props,
TAG_INVALID);
if (client->version >= 26) {
uint32_t n_ports;
struct port_info *port_info, *pi;
port_info = alloca(dev_info.n_ports * sizeof(*port_info));
dev_info.active_profile = SPA_ID_INVALID;
n_ports = collect_port_info(o, &dev_info, port_info);
message_put(m,
TAG_U32, n_ports, /* n_ports */
TAG_INVALID);
for (n = 0; n < n_ports; n++) {
struct spa_dict_item *items;
struct spa_dict *pdict = NULL, dict;
uint32_t i;
pi = &port_info[n];
if (pi->info && pi->n_props > 0) {
items = alloca(pi->n_props * sizeof(*items));
dict.items = items;
pdict = collect_props(pi->info, &dict);
}
message_put(m,
TAG_STRING, pi->name, /* port name */
TAG_STRING, pi->description, /* port description */
TAG_U32, pi->priority, /* port priority */
TAG_U32, pi->available, /* port available */
TAG_U8, pi->direction == SPA_DIRECTION_INPUT ? 2 : 1, /* port direction */
TAG_PROPLIST, pdict, /* port proplist */
TAG_INVALID);
message_put(m,
TAG_U32, pi->n_profiles, /* n_profiles */
TAG_INVALID);
for (i = 0; i < pi->n_profiles; i++) {
uint32_t idx = pi->profiles[i];
message_put(m,
TAG_STRING, idx < n_profiles ?
profile_info[idx].name : NULL, /* profile name */
TAG_INVALID);
}
if (client->version >= 27) {
message_put(m,
TAG_S64, 0LL, /* port latency */
TAG_INVALID);
}
if (client->version >= 34) {
message_put(m,
TAG_STRING, pi->availability_group, /* available group */
TAG_U32, pi->type, /* port type */
TAG_INVALID);
}
}
}
return 0;
}
static int fill_sink_info(struct client *client, struct message *m, static int fill_sink_info(struct client *client, struct message *m,
struct pw_manager_object *o) struct pw_manager_object *o)
{ {
@ -3064,7 +3078,7 @@ static int fill_sink_info(struct client *client, struct message *m,
struct pw_manager_param *p; struct pw_manager_param *p;
struct pw_manager_object *card = NULL; struct pw_manager_object *card = NULL;
uint32_t flags; uint32_t flags;
struct device_info dev_info = CARD_INFO_INIT(PW_DIRECTION_OUTPUT); struct device_info dev_info = DEVICE_INFO_INIT(PW_DIRECTION_OUTPUT);
if (o == NULL || info == NULL || info->props == NULL || !is_sink(o)) if (o == NULL || info == NULL || info->props == NULL || !is_sink(o))
return ERR_NOENTITY; return ERR_NOENTITY;
@ -3154,7 +3168,7 @@ static int fill_sink_info(struct client *client, struct message *m,
uint32_t n_ports, n; uint32_t n_ports, n;
struct port_info *port_info, *pi; struct port_info *port_info, *pi;
port_info = alloca(dev_info.n_ports * sizeof(*p)); port_info = alloca(dev_info.n_ports * sizeof(*port_info));
n_ports = collect_port_info(card, &dev_info, port_info); n_ports = collect_port_info(card, &dev_info, port_info);
message_put(m, message_put(m,
@ -3210,7 +3224,7 @@ static int fill_source_info(struct client *client, struct message *m,
struct pw_manager_param *p; struct pw_manager_param *p;
struct pw_manager_object *card = NULL; struct pw_manager_object *card = NULL;
uint32_t flags; uint32_t flags;
struct device_info dev_info = CARD_INFO_INIT(PW_DIRECTION_INPUT); struct device_info dev_info = DEVICE_INFO_INIT(PW_DIRECTION_INPUT);
is_monitor = is_sink(o); is_monitor = is_sink(o);
if (o == NULL || info == NULL || info->props == NULL || if (o == NULL || info == NULL || info->props == NULL ||
@ -3307,7 +3321,7 @@ static int fill_source_info(struct client *client, struct message *m,
uint32_t n_ports, n; uint32_t n_ports, n;
struct port_info *port_info, *pi; struct port_info *port_info, *pi;
port_info = alloca(dev_info.n_ports * sizeof(*p)); port_info = alloca(dev_info.n_ports * sizeof(*port_info));
n_ports = collect_port_info(card, &dev_info, port_info); n_ports = collect_port_info(card, &dev_info, port_info);
message_put(m, message_put(m,