From 80628f38e7d8152f3f80ddbbaa13395f2899b80e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 2 Sep 2021 10:05:33 +0200 Subject: [PATCH] alsa: move codec config to acp-device Just like the latency, move the codecs to the device Route param. This way, it is easier for the session manager to save and restore the codecs as part of the Route settings. --- spa/plugins/alsa/acp/acp.c | 8 +- spa/plugins/alsa/acp/acp.h | 3 + spa/plugins/alsa/alsa-acp-device.c | 48 ++++++++ .../extensions/ext-device-restore.c | 112 +++++++++++++++--- 4 files changed, 151 insertions(+), 20 deletions(-) diff --git a/spa/plugins/alsa/acp/acp.c b/spa/plugins/alsa/acp/acp.c index 1ff1c5f95..9cf8859b0 100644 --- a/spa/plugins/alsa/acp/acp.c +++ b/spa/plugins/alsa/acp/acp.c @@ -205,6 +205,7 @@ static void init_device(pa_card *impl, pa_alsa_device *dev, pa_alsa_direction_t pa_alsa_mapping *m, uint32_t index) { uint32_t i; + char **d; dev->card = impl; dev->mapping = m; @@ -244,18 +245,21 @@ static void init_device(pa_card *impl, pa_alsa_device *dev, pa_alsa_direction_t if (m->ucm_context.ucm) { dev->ucm_context = &m->ucm_context; if (impl->ucm.alibpref != NULL) { - char **d; for (d = m->device_strings; *d; d++) { if (pa_startswith(*d, impl->ucm.alibpref)) { size_t plen = strlen(impl->ucm.alibpref); size_t len = strlen(*d); memmove(*d, (*d) + plen, len - plen + 1); dev->device.flags |= ACP_DEVICE_UCM_DEVICE; - break; } } } } + for (d = m->device_strings; *d; d++) { + if (pa_startswith(*d, "iec958") || + pa_startswith(*d, "hdmi")) + dev->device.flags |= ACP_DEVICE_IEC958; + } pa_dynarray_init(&dev->port_array, NULL); } diff --git a/spa/plugins/alsa/acp/acp.h b/spa/plugins/alsa/acp/acp.h index 22a4c5d98..8db9f8f27 100644 --- a/spa/plugins/alsa/acp/acp.h +++ b/spa/plugins/alsa/acp/acp.h @@ -209,6 +209,7 @@ struct acp_device { #define ACP_DEVICE_HW_VOLUME (1<<1) #define ACP_DEVICE_HW_MUTE (1<<2) #define ACP_DEVICE_UCM_DEVICE (1<<3) +#define ACP_DEVICE_IEC958 (1<<4) uint32_t flags; const char *name; @@ -227,6 +228,8 @@ struct acp_device { struct acp_port **ports; int64_t latency_ns; + uint32_t codecs[32]; + uint32_t n_codecs; }; struct acp_card_profile { diff --git a/spa/plugins/alsa/alsa-acp-device.c b/spa/plugins/alsa/alsa-acp-device.c index bcc1885a1..a295573a6 100644 --- a/spa/plugins/alsa/alsa-acp-device.c +++ b/spa/plugins/alsa/alsa-acp-device.c @@ -463,6 +463,12 @@ static struct spa_pod *build_route(struct spa_pod_builder *b, uint32_t id, spa_pod_builder_prop(b, SPA_PROP_latencyOffsetNsec, 0); spa_pod_builder_long(b, dev->latency_ns); + if (SPA_FLAG_IS_SET(dev->flags, ACP_DEVICE_IEC958)) { + spa_pod_builder_prop(b, SPA_PROP_iec958Codecs, 0); + spa_pod_builder_array(b, sizeof(uint32_t), SPA_TYPE_Id, + dev->n_codecs, dev->codecs); + } + spa_pod_builder_pop(b, &f[1]); } spa_pod_builder_prop(b, SPA_PARAM_ROUTE_devices, 0); @@ -603,6 +609,33 @@ static void on_latency_changed(void *data, struct acp_device *dev) spa_device_emit_event(&this->hooks, event); } +static void on_codecs_changed(void *data, struct acp_device *dev) +{ + struct impl *this = data; + struct spa_event *event; + uint8_t buffer[4096]; + struct spa_pod_builder b = { 0 }; + struct spa_pod_frame f[1]; + + spa_log_info(this->log, "device %s codecs changed", dev->name); + this->info.change_mask |= SPA_DEVICE_CHANGE_MASK_PARAMS; + this->params[IDX_Route].user++; + + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + spa_pod_builder_push_object(&b, &f[0], + SPA_TYPE_EVENT_Device, SPA_DEVICE_EVENT_ObjectConfig); + spa_pod_builder_prop(&b, SPA_EVENT_DEVICE_Object, 0); + spa_pod_builder_int(&b, dev->index); + spa_pod_builder_prop(&b, SPA_EVENT_DEVICE_Props, 0); + spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, SPA_EVENT_DEVICE_Props, + SPA_PROP_iec958Codecs, SPA_POD_Array(sizeof(uint32_t), + SPA_TYPE_Id, dev->n_codecs, dev->codecs)); + event = spa_pod_builder_pop(&b, &f[0]); + + spa_device_emit_event(&this->hooks, event); +} + static int apply_device_props(struct impl *this, struct acp_device *dev, struct spa_pod *props) { float volume = 0; @@ -655,6 +688,21 @@ static int apply_device_props(struct impl *this, struct acp_device *dev, struct } break; } + case SPA_PROP_iec958Codecs: + { + uint32_t codecs[32], n_codecs; + + n_codecs = spa_pod_copy_array(&prop->value, SPA_TYPE_Id, + codecs, SPA_N_ELEMENTS(codecs)); + if (n_codecs != dev->n_codecs || + memcmp(dev->codecs, codecs, n_codecs * sizeof(uint32_t)) != 0) { + memcpy(dev->codecs, codecs, n_codecs * sizeof(uint32_t)); + dev->n_codecs = n_codecs; + on_codecs_changed(this, dev); + changed++; + } + break; + } default: break; } diff --git a/src/modules/module-protocol-pulse/extensions/ext-device-restore.c b/src/modules/module-protocol-pulse/extensions/ext-device-restore.c index 03ba55ee8..920a5310f 100644 --- a/src/modules/module-protocol-pulse/extensions/ext-device-restore.c +++ b/src/modules/module-protocol-pulse/extensions/ext-device-restore.c @@ -165,21 +165,82 @@ static int do_extension_device_restore_read_formats(struct client *client, return client_queue_message(client, data.reply); } -static int do_extension_device_restore_save_formats(struct client *client, - uint32_t command, uint32_t tag, struct message *m) +static int set_card_codecs(struct pw_manager_object *o, uint32_t id, + uint32_t device_id, uint32_t n_codecs, uint32_t *codecs) +{ + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + struct spa_pod_frame f[2]; + struct spa_pod *param; + + if (!SPA_FLAG_IS_SET(o->permissions, PW_PERM_W | PW_PERM_X)) + return -EACCES; + + if (o->proxy == NULL) + return -ENOENT; + + spa_pod_builder_push_object(&b, &f[0], + SPA_TYPE_OBJECT_ParamRoute, SPA_PARAM_Route); + spa_pod_builder_add(&b, + SPA_PARAM_ROUTE_index, SPA_POD_Int(id), + SPA_PARAM_ROUTE_device, SPA_POD_Int(device_id), + 0); + spa_pod_builder_prop(&b, SPA_PARAM_ROUTE_props, 0); + spa_pod_builder_push_object(&b, &f[1], + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props); + spa_pod_builder_add(&b, + SPA_PROP_iec958Codecs, SPA_POD_Array(sizeof(uint32_t), + SPA_TYPE_Id, n_codecs, codecs), 0); + spa_pod_builder_pop(&b, &f[1]); + spa_pod_builder_prop(&b, SPA_PARAM_ROUTE_save, 0); + spa_pod_builder_bool(&b, true); + param = spa_pod_builder_pop(&b, &f[0]); + + pw_device_set_param((struct pw_device*)o->proxy, + SPA_PARAM_Route, 0, param); + return 0; +} + +static int set_node_codecs(struct pw_manager_object *o, uint32_t n_codecs, uint32_t *codecs) { - struct pw_manager *manager = client->manager; - struct selector sel; - struct pw_manager_object *o; - int res; - uint32_t type, sink_index; - uint8_t i, n_formats; - uint32_t codec, iec958codecs[32]; - uint32_t n_codecs = 0; char buf[1024]; struct spa_pod_builder b; struct spa_pod *param; + if (!SPA_FLAG_IS_SET(o->permissions, PW_PERM_W | PW_PERM_X)) + return -EACCES; + + if (o->proxy == NULL) + return -ENOENT; + + spa_pod_builder_init(&b, buf, sizeof(buf)); + param = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, + SPA_PROP_iec958Codecs, SPA_POD_Array(sizeof(uint32_t), + SPA_TYPE_Id, n_codecs, codecs)); + + pw_node_set_param((struct pw_node*)o->proxy, + SPA_PARAM_Props, 0, param); + + return 0; +} + + +static int do_extension_device_restore_save_formats(struct client *client, + uint32_t command, uint32_t tag, struct message *m) +{ + struct impl *impl = client->impl; + struct pw_manager *manager = client->manager; + struct selector sel; + struct pw_manager_object *o, *card = NULL; + struct pw_node_info *info; + int res; + uint32_t type, sink_index, card_id = SPA_ID_INVALID; + uint8_t i, n_formats; + uint32_t n_codecs = 0, codec, iec958codecs[32]; + struct device_info dev_info; + const char *str; + if ((res = message_get(m, TAG_U32, &type, TAG_U32, &sink_index, @@ -189,6 +250,9 @@ static int do_extension_device_restore_save_formats(struct client *client, if (n_formats < 1) return -EPROTO; + if (type != DEVICE_TYPE_SINK) + return -ENOTSUP; + for (i = 0; i < n_formats; ++i) { struct format_info format; if (message_get(m, @@ -208,17 +272,29 @@ static int do_extension_device_restore_save_formats(struct client *client, sel.type = pw_manager_object_is_sink; o = select_object(manager, &sel); - if (o == NULL) + if (o == NULL || (info = o->info) == NULL || info->props == NULL) return -ENOENT; - spa_pod_builder_init(&b, buf, sizeof(buf)); - param = spa_pod_builder_add_object(&b, - SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, - SPA_PROP_iec958Codecs, SPA_POD_Array(sizeof(uint32_t), - SPA_TYPE_Id, n_codecs, iec958codecs)); + dev_info = DEVICE_INFO_INIT(SPA_DIRECTION_INPUT); - pw_node_set_param((struct pw_node*)o->proxy, - SPA_PARAM_Props, 0, param); + if ((str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)) != NULL) + card_id = (uint32_t)atoi(str); + if ((str = spa_dict_lookup(info->props, "card.profile.device")) != NULL) + dev_info.device = (uint32_t)atoi(str); + if (card_id != SPA_ID_INVALID) { + struct selector sel = { .id = card_id, .type = pw_manager_object_is_card, }; + card = select_object(manager, &sel); + } + collect_device_info(o, card, &dev_info, false, &impl->defs); + + if (card != NULL && dev_info.active_port != SPA_ID_INVALID) { + res = set_card_codecs(card, dev_info.active_port, + dev_info.device, n_codecs, iec958codecs); + } else { + res = set_node_codecs(o, n_codecs, iec958codecs); + } + if (res < 0) + return res; return reply_simple_ack(client, tag); }