context: update the Profiles and Routes in the card

Update the Profile and Route information directly into the card
info. Use this info to fill the port info for the sink and sources.
This commit is contained in:
Wim Taymans 2020-06-25 16:46:47 +02:00
parent 42cc30e1ee
commit 1277e6cded
3 changed files with 376 additions and 194 deletions

View file

@ -44,8 +44,8 @@ static void global_free(pa_context *c, struct global *g)
{
spa_list_remove(&g->link);
if (g->destroy)
g->destroy(g);
if (g->ginfo && g->ginfo->destroy)
g->ginfo->destroy(g);
if (g->proxy) {
pw_proxy_destroy(g->proxy);
}
@ -255,6 +255,17 @@ static void emit_event(pa_context *c, struct global *g, pa_subscription_event_ty
}
}
static void remove_params(struct spa_list *params, uint32_t id)
{
struct param *p, *t;
spa_list_for_each_safe(p, t, params, link) {
if (id == SPA_ID_INVALID || p->id == id) {
spa_list_remove(&p->link);
free(p);
}
}
}
static void update_device_props(struct global *g)
{
pa_card_info *i = &g->card_info.info;
@ -298,6 +309,9 @@ static void device_event_info(void *object, const struct pw_device_info *info)
switch (info->params[n].id) {
case SPA_PARAM_EnumProfile:
remove_params(&g->card_info.profiles, SPA_PARAM_EnumProfile);
g->card_info.n_profiles = 0;
g->card_info.pending_profiles = true;
pw_device_enum_params((struct pw_device*)g->proxy,
0, SPA_PARAM_EnumProfile, 0, -1, NULL);
break;
@ -306,6 +320,9 @@ static void device_event_info(void *object, const struct pw_device_info *info)
0, SPA_PARAM_Profile, 0, -1, NULL);
break;
case SPA_PARAM_EnumRoute:
remove_params(&g->card_info.ports, SPA_PARAM_EnumRoute);
g->card_info.n_ports = 0;
g->card_info.pending_ports = true;
pw_device_enum_params((struct pw_device*)g->proxy,
0, SPA_PARAM_EnumRoute, 0, -1, NULL);
break;
@ -392,6 +409,226 @@ static void device_event_param(void *object, int seq,
}
}
static void device_clear_profiles(struct global *g)
{
pa_card_info *i = &g->card_info.info;
i->n_profiles = 0;
free(i->profiles);
i->profiles = NULL;
free(g->card_info.card_profiles);
g->card_info.card_profiles = NULL;
free(i->profiles2);
i->profiles2 = NULL;
}
static void device_sync_profiles(struct global *g)
{
pa_card_info *i = &g->card_info.info;
uint32_t n_profiles, j;
struct param *p;
device_clear_profiles(g);
n_profiles = g->card_info.n_profiles;
i->profiles = calloc(n_profiles, sizeof(pa_card_profile_info));
g->card_info.card_profiles = calloc(n_profiles, sizeof(pa_card_profile_info2));
i->profiles2 = calloc(n_profiles + 1, sizeof(pa_card_profile_info2 *));
i->n_profiles = 0;
pw_log_debug("context %p: info for %d", g->context, g->id);
j = 0;
spa_list_for_each(p, &g->card_info.profiles, link) {
uint32_t id, priority = 0, available = 0, n_cap = 0, n_play = 0;
const char *name = NULL;
const char *description = NULL;
struct spa_pod *classes = NULL, *info = NULL;
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_info, SPA_POD_OPT_Pod(&info),
SPA_PARAM_PROFILE_classes, SPA_POD_OPT_Pod(&classes)) < 0) {
pw_log_warn("device %d: can't parse profile", g->id);
continue;
}
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_play += count;
else if (strcmp(class, "Audio/Source") == 0)
n_cap += count;
spa_pod_parser_pop(&prs, &f[0]);
}
}
i->profiles[j].name = name;
i->profiles[j].description = description ? description : name;
i->profiles[j].n_sinks = n_play;
i->profiles[j].n_sources = n_cap;
i->profiles[j].priority = priority;
i->profiles2[j] = &g->card_info.card_profiles[j];
i->profiles2[j]->name = i->profiles[j].name;
i->profiles2[j]->description = i->profiles[j].description;
i->profiles2[j]->n_sinks = i->profiles[j].n_sinks;
i->profiles2[j]->n_sources = i->profiles[j].n_sources;
i->profiles2[j]->priority = i->profiles[j].priority;
i->profiles2[j]->available = available != SPA_PARAM_AVAILABILITY_no;
if (g->card_info.active_profile == id) {
i->active_profile = &i->profiles[j];
i->active_profile2 = i->profiles2[j];
}
j++;
}
i->profiles2[j] = NULL;
i->n_profiles = j;
}
static void device_clear_ports(struct global *g)
{
pa_card_info *i = &g->card_info.info;
uint32_t n;
for (n = 0; n < i->n_ports; i++)
free(i->ports[n]->profiles2);
i->n_ports = 0;
free(i->ports);
i->ports = NULL;
free(g->card_info.card_ports);
g->card_info.card_ports = NULL;
}
static void device_sync_ports(struct global *g)
{
pa_card_info *i = &g->card_info.info;
uint32_t n_ports, j;
struct param *p;
device_clear_ports(g);
n_ports = g->card_info.n_ports;
i->ports = calloc(n_ports+1, sizeof(pa_card_port_info *));
g->card_info.card_ports = calloc(n_ports, sizeof(pa_card_port_info));
i->n_ports = 0;
g->card_info.active_port_input = SPA_ID_INVALID;
g->card_info.active_port_output = SPA_ID_INVALID;
pw_log_debug("context %p: info for %d", g->context, g->id);
j = 0;
spa_list_for_each(p, &g->card_info.ports, link) {
uint32_t id, priority;
enum spa_direction direction;
const char *name = NULL, *description = NULL;
enum spa_param_availability available = SPA_PARAM_AVAILABILITY_unknown;
struct spa_pod *profiles = NULL, *info = NULL;
pa_card_port_info *pi;
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_profiles, SPA_POD_OPT_Pod(&profiles)) < 0) {
pw_log_warn("device %d: can't parse route", g->id);
continue;
}
pi = i->ports[j] = &g->card_info.card_ports[j];
spa_zero(*pi);
pi->name = name;
pi->description = description;
pi->priority = priority;
pi->available = available;
pi->direction = direction;
pi->proplist = pa_proplist_new();
while (info) {
struct spa_pod_parser prs;
struct spa_pod_frame f[1];
uint32_t n, n_items;
const char *key, *value;
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;
for (n = 0; n < n_items; n++) {
if (spa_pod_parser_get(&prs,
SPA_POD_String(&key),
SPA_POD_String(&value),
NULL) < 0)
break;
pa_proplist_sets(pi->proplist, key, value);
}
spa_pod_parser_pop(&prs, &f[0]);
break;
}
pi->n_profiles = 0;
pi->profiles = NULL;
pi->profiles2 = NULL;
while (profiles) {
uint32_t *pr, n, n_pr;
pr = spa_pod_get_array(profiles, &n_pr);
if (pr == NULL)
break;
pi->n_profiles = n_pr;
pi->profiles2 = calloc(n_pr + 1, sizeof(pa_card_profile_info2 *));
for (n = 0; n < n_pr; n++)
pi->profiles2[n] = i->profiles2[pr[n]];
pi->profiles2[n_pr] = NULL;
pi->profiles = (pa_card_profile_info **)pi->profiles2;
break;
}
j++;
}
i->ports[j] = NULL;
i->n_ports = j;
if (i->n_ports == 0)
i->ports = NULL;
}
static void device_sync(struct global *g)
{
if (g->card_info.pending_profiles) {
device_sync_profiles(g);
g->card_info.pending_profiles = false;
}
if (g->card_info.pending_ports) {
device_sync_ports(g);
g->card_info.pending_ports = false;
}
}
static const struct pw_device_events device_events = {
PW_VERSION_DEVICE_EVENTS,
.info = device_event_info,
@ -401,22 +638,27 @@ static const struct pw_device_events device_events = {
static void device_destroy(void *data)
{
struct global *global = data;
struct param *p;
if (global->card_info.info.proplist)
pa_proplist_free(global->card_info.info.proplist);
spa_list_consume(p, &global->card_info.profiles, link) {
spa_list_remove(&p->link);
free(p);
}
spa_list_consume(p, &global->card_info.ports, link) {
spa_list_remove(&p->link);
free(p);
}
device_clear_ports(global);
device_clear_profiles(global);
remove_params(&global->card_info.ports, SPA_ID_INVALID);
remove_params(&global->card_info.profiles, SPA_ID_INVALID);
if (global->info)
pw_device_info_free(global->info);
}
struct global_info device_info = {
.version = PW_VERSION_DEVICE,
.events = &device_events,
.destroy = device_destroy,
.sync = device_sync,
};
static void node_event_info(void *object, const struct pw_node_info *info)
{
struct global *g = object;
@ -513,6 +755,13 @@ static void node_destroy(void *data)
pw_node_info_free(global->info);
}
struct global_info node_info = {
.version = PW_VERSION_NODE,
.events = &node_events,
.destroy = node_destroy,
};
static void module_event_info(void *object, const struct pw_module_info *info)
{
struct global *g = object;
@ -551,6 +800,12 @@ static void module_destroy(void *data)
pw_module_info_free(global->info);
}
struct global_info module_info = {
.version = PW_VERSION_MODULE,
.events = &module_events,
.destroy = module_destroy,
};
static void client_event_info(void *object, const struct pw_client_info *info)
{
struct global *g = object;
@ -591,6 +846,12 @@ static void client_destroy(void *data)
pw_client_info_free(global->info);
}
struct global_info client_info = {
.version = PW_VERSION_CLIENT,
.events = &client_events,
.destroy = client_destroy,
};
static void proxy_removed(void *data)
{
struct global *g = data;
@ -611,6 +872,8 @@ static void proxy_done(void *data, int seq)
pa_subscription_event_type_t event;
if (g->pending_seq == seq) {
if (g->ginfo && g->ginfo->sync)
g->ginfo->sync(g);
if (g->init) {
g->init = false;
event = PA_SUBSCRIPTION_EVENT_NEW;
@ -633,9 +896,7 @@ static int set_mask(pa_context *c, struct global *g)
{
const char *str;
struct global *f;
const void *events = NULL;
pw_destroy_t destroy;
uint32_t client_version;
struct global_info *ginfo = NULL;
if (strcmp(g->type, PW_TYPE_INTERFACE_Device) == 0) {
if (g->props == NULL)
@ -648,12 +909,9 @@ static int set_mask(pa_context *c, struct global *g)
pw_log_debug("found card %d", g->id);
g->mask = PA_SUBSCRIPTION_MASK_CARD;
g->event = PA_SUBSCRIPTION_EVENT_CARD;
events = &device_events;
client_version = PW_VERSION_DEVICE;
destroy = device_destroy;
spa_list_init(&g->card_info.profiles);
spa_list_init(&g->card_info.ports);
ginfo = &device_info;
spa_list_init(&g->card_info.profiles);
spa_list_init(&g->card_info.ports);
} else if (strcmp(g->type, PW_TYPE_INTERFACE_Node) == 0) {
if (g->props == NULL)
return 0;
@ -693,9 +951,7 @@ static int set_mask(pa_context *c, struct global *g)
if ((str = pw_properties_get(g->props, PW_KEY_DEVICE_ID)) != NULL)
g->node_info.device_id = atoi(str);
events = &node_events;
client_version = PW_VERSION_NODE;
destroy = node_destroy;
ginfo = &node_info;
g->node_info.volume = 1.0;
g->node_info.mute = false;
} else if (strcmp(g->type, PW_TYPE_INTERFACE_Port) == 0) {
@ -712,16 +968,12 @@ static int set_mask(pa_context *c, struct global *g)
pw_log_debug("found module %d", g->id);
g->mask = PA_SUBSCRIPTION_MASK_MODULE;
g->event = PA_SUBSCRIPTION_EVENT_MODULE;
events = &module_events;
client_version = PW_VERSION_MODULE;
destroy = module_destroy;
ginfo = &module_info;
} else if (strcmp(g->type, PW_TYPE_INTERFACE_Client) == 0) {
pw_log_debug("found client %d", g->id);
g->mask = PA_SUBSCRIPTION_MASK_CLIENT;
g->event = PA_SUBSCRIPTION_EVENT_CLIENT;
events = &client_events;
client_version = PW_VERSION_CLIENT;
destroy = client_destroy;
ginfo = &client_info;
} else if (strcmp(g->type, PW_TYPE_INTERFACE_Link) == 0) {
if ((str = pw_properties_get(g->props, PW_KEY_LINK_OUTPUT_PORT)) == NULL)
return 0;
@ -752,17 +1004,17 @@ static int set_mask(pa_context *c, struct global *g)
pw_log_debug("global %p: id:%u mask %d/%d", g, g->id, g->mask, g->event);
if (events) {
if (ginfo) {
pw_log_debug("bind %d", g->id);
g->proxy = pw_registry_bind(c->registry, g->id, g->type,
client_version, 0);
ginfo->version, 0);
if (g->proxy == NULL)
return -ENOMEM;
pw_proxy_add_object_listener(g->proxy, &g->object_listener, events, g);
pw_proxy_add_object_listener(g->proxy, &g->object_listener, ginfo->events, g);
pw_proxy_add_listener(g->proxy, &g->proxy_listener, &proxy_events, g);
g->destroy = destroy;
g->ginfo = ginfo;
global_sync(g);
} else {
emit_event(c, g, PA_SUBSCRIPTION_EVENT_NEW);