From 686128a5dd92fbf790f050fe6691c71e1c5ea56f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Wed, 28 Oct 2020 20:05:13 +0100 Subject: [PATCH] pulse-server: implement set_sink/source_port --- src/modules/module-protocol-pulse/collect.c | 26 +++++ .../module-protocol-pulse/pulse-server.c | 94 ++++++++++++++++++- 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/modules/module-protocol-pulse/collect.c b/src/modules/module-protocol-pulse/collect.c index adc6101ea..130af355c 100644 --- a/src/modules/module-protocol-pulse/collect.c +++ b/src/modules/module-protocol-pulse/collect.c @@ -489,6 +489,32 @@ static uint32_t collect_port_info(struct pw_manager_object *card, struct card_in return n; } +static uint32_t find_port_id(struct pw_manager_object *card, uint32_t direction, const char *port_name) +{ + struct pw_manager_param *p; + + spa_list_for_each(p, &card->param_list, link) { + uint32_t id, dir; + const char *name; + + 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(&dir), + SPA_PARAM_ROUTE_name, SPA_POD_Id(&name)) < 0) + continue; + if (dir != direction) + continue; + if (strcmp(name, port_name) == 0) + return id; + + } + return SPA_ID_INVALID; +} + static struct spa_dict *collect_props(struct spa_pod *info, struct spa_dict *dict) { struct spa_pod_parser prs; diff --git a/src/modules/module-protocol-pulse/pulse-server.c b/src/modules/module-protocol-pulse/pulse-server.c index 11ed4a9aa..b26fc3c3e 100644 --- a/src/modules/module-protocol-pulse/pulse-server.c +++ b/src/modules/module-protocol-pulse/pulse-server.c @@ -333,7 +333,7 @@ static int reply_error(struct client *client, uint32_t tag, uint32_t error) { struct message *reply; - pw_log_debug(NAME" %p: ERROR tag:%u error:%u", client, tag, error); + pw_log_warn(NAME" %p: ERROR tag:%u error:%u", client, tag, error); reply = message_alloc(client, -1, 0); message_put(reply, @@ -2123,6 +2123,25 @@ static int set_card_mute(struct pw_manager_object *o, uint32_t id, return 0; } +static int set_card_port(struct pw_manager_object *o, uint32_t id, + uint32_t device_id) +{ + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + + if (!SPA_FLAG_IS_SET(o->permissions, PW_PERM_W | PW_PERM_X)) + return ERR_ACCESS; + + pw_device_set_param((struct pw_device*)o->proxy, + SPA_PARAM_Route, 0, + spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamRoute, SPA_PARAM_Route, + SPA_PARAM_ROUTE_index, SPA_POD_Int(id), + SPA_PARAM_ROUTE_device, SPA_POD_Int(device_id))); + + return 0; +} + static int do_set_stream_volume(struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct impl *impl = client->impl; @@ -2433,6 +2452,75 @@ error: return reply_error(client, tag, res); } +static int do_set_port(struct client *client, uint32_t command, uint32_t tag, struct message *m) +{ + struct impl *impl = client->impl; + struct pw_manager *manager = client->manager; + struct pw_node_info *info; + uint32_t id, card_id = SPA_ID_INVALID, device_id = SPA_ID_INVALID; + uint32_t port_id = SPA_ID_INVALID; + const char *name, *str, *port_name; + struct pw_manager_object *o, *card = NULL; + int res; + enum pw_direction direction; + + if ((res = message_get(m, + TAG_U32, &id, + TAG_STRING, &name, + TAG_STRING, &port_name, + TAG_INVALID)) < 0) + goto error_protocol; + + pw_log_info(NAME" %p: %s index:%u name:%s port:%s", impl, + commands[command].name, id, name, port_name); + + if ((id == SPA_ID_INVALID && name == NULL) || + (id != SPA_ID_INVALID && name != NULL)) + goto error_invalid; + + if (command == COMMAND_SET_SINK_PORT) + direction = PW_DIRECTION_OUTPUT; + else + direction = PW_DIRECTION_INPUT; + + o = find_device(client, id, name, direction == PW_DIRECTION_OUTPUT); + if (o == NULL || o->info == NULL) + goto error_noentity; + info = o->info; + if (info == NULL) + goto error_noentity; + + 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) + device_id = (uint32_t)atoi(str); + if (card_id != SPA_ID_INVALID) { + struct selector sel = { .id = card_id, .type = is_card, }; + card = select_object(manager, &sel); + } + if (card == NULL || device_id == SPA_ID_INVALID) + goto error_noentity; + + port_id = find_port_id(card, direction, port_name); + + if ((res = set_card_port(card, device_id, port_id)) != 0) + goto error; + + return reply_simple_ack(client, tag); + +error_invalid: + res = ERR_INVALID; + goto error; +error_noentity: + res = ERR_NOENTITY; + goto error; +error_protocol: + res = ERR_PROTOCOL; + goto error; +error: + return reply_error(client, tag, res); +} + static int do_set_stream_name(struct client *client, uint32_t command, uint32_t tag, struct message *m) { struct impl *impl = client->impl; @@ -3722,8 +3810,8 @@ static const struct command commands[COMMAND_MAX] = [COMMAND_RECORD_BUFFER_ATTR_CHANGED] = { "RECORD_BUFFER_ATTR_CHANGED", }, /* Supported since protocol v16 (0.9.16) */ - [COMMAND_SET_SINK_PORT] = { "SET_SINK_PORT", do_error_not_implemented, }, - [COMMAND_SET_SOURCE_PORT] = { "SET_SOURCE_PORT", do_error_not_implemented, }, + [COMMAND_SET_SINK_PORT] = { "SET_SINK_PORT", do_set_port, }, + [COMMAND_SET_SOURCE_PORT] = { "SET_SOURCE_PORT", do_set_port, }, /* Supported since protocol v22 (1.0) */ [COMMAND_SET_SOURCE_OUTPUT_VOLUME] = { "SET_SOURCE_OUTPUT_VOLUME", do_set_stream_volume, },