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); }