mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-04 13:30:12 -05:00
pulse-server: implement volume on sink/source
This commit is contained in:
parent
5862ce606c
commit
4d42a83fb9
2 changed files with 185 additions and 65 deletions
|
|
@ -2045,6 +2045,34 @@ static int set_node_volume(struct pw_manager_object *o, struct volume *vol)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int set_card_volume(struct pw_manager_object *o, uint32_t id,
|
||||
uint32_t device_id, struct volume *vol)
|
||||
{
|
||||
char buf[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
||||
struct spa_pod_frame f[1];
|
||||
struct spa_pod *param;
|
||||
|
||||
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_add_object(&b,
|
||||
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
|
||||
SPA_PROP_channelVolumes, SPA_POD_Array(sizeof(float),
|
||||
SPA_TYPE_Float,
|
||||
vol->channels,
|
||||
vol->values));
|
||||
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_mute(struct pw_manager_object *o, bool mute)
|
||||
{
|
||||
char buf[1024];
|
||||
|
|
@ -2183,6 +2211,139 @@ error:
|
|||
return reply_error(client, tag, res);
|
||||
}
|
||||
|
||||
static const char *get_default(struct client *client, bool sink)
|
||||
{
|
||||
struct selector sel;
|
||||
struct pw_manager *manager = client->manager;
|
||||
struct pw_manager_object *o;
|
||||
const char *def, *str;
|
||||
|
||||
spa_zero(sel);
|
||||
if (sink) {
|
||||
sel.type = is_sink;
|
||||
sel.id = client->default_sink;
|
||||
def = "@DEFAULT_SINK@";
|
||||
} else {
|
||||
sel.type = is_source;
|
||||
sel.id = client->default_source;
|
||||
def = "@DEFAULT_SOURCE@";
|
||||
}
|
||||
sel.accumulate = select_best;
|
||||
|
||||
o = select_object(manager, &sel);
|
||||
if (o == NULL || o->props == NULL ||
|
||||
((str = pw_properties_get(o->props, PW_KEY_NODE_NAME)) == NULL))
|
||||
return def;
|
||||
return str;
|
||||
}
|
||||
|
||||
static struct pw_manager_object *find_device(struct client *client,
|
||||
uint32_t id, const char *name, bool sink)
|
||||
{
|
||||
struct selector sel;
|
||||
const char *def;
|
||||
|
||||
spa_zero(sel);
|
||||
sel.id = id;
|
||||
sel.key = PW_KEY_NODE_NAME;
|
||||
sel.value = name;
|
||||
|
||||
if (sink) {
|
||||
sel.type = is_sink;
|
||||
def = "@DEFAULT_SINK@";
|
||||
} else {
|
||||
sel.type = is_source;
|
||||
def = "@DEFAULT_SOURCE@";
|
||||
}
|
||||
if (sel.value != NULL && strcmp(sel.value, def) == 0)
|
||||
sel.value = get_default(client, sink);
|
||||
|
||||
return select_object(client->manager, &sel);
|
||||
}
|
||||
|
||||
static int do_set_volume(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;
|
||||
const char *name, *str;
|
||||
struct volume volume;
|
||||
struct pw_manager_object *o, *card = NULL;
|
||||
int res;
|
||||
struct device_info dev_info;
|
||||
enum pw_direction direction;
|
||||
|
||||
if ((res = message_get(m,
|
||||
TAG_U32, &id,
|
||||
TAG_STRING, &name,
|
||||
TAG_CVOLUME, &volume,
|
||||
TAG_INVALID)) < 0)
|
||||
goto error_protocol;
|
||||
|
||||
pw_log_info(NAME" %p: %s index:%u name:%s", impl, commands[command].name, id, name);
|
||||
|
||||
if (id == SPA_ID_INVALID && name == NULL)
|
||||
goto error_invalid;
|
||||
|
||||
if (command == COMMAND_SET_SINK_VOLUME)
|
||||
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;
|
||||
|
||||
dev_info = DEVICE_INFO_INIT(direction);
|
||||
|
||||
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 = is_card, };
|
||||
card = select_object(manager, &sel);
|
||||
}
|
||||
collect_device_info(o, card, &dev_info);
|
||||
|
||||
if (card != NULL && dev_info.active_port) {
|
||||
if (!SPA_FLAG_IS_SET(card->permissions, PW_PERM_W | PW_PERM_X))
|
||||
goto error_access;
|
||||
|
||||
set_card_volume(card, dev_info.active_port, dev_info.device, &volume);
|
||||
} else {
|
||||
if (!SPA_FLAG_IS_SET(o->permissions, PW_PERM_W | PW_PERM_X))
|
||||
goto error_access;
|
||||
|
||||
set_node_volume(o, &volume);
|
||||
}
|
||||
return reply_simple_ack(client, tag);
|
||||
|
||||
error_invalid:
|
||||
res = ERR_INVALID;
|
||||
goto error;
|
||||
error_access:
|
||||
res = ERR_ACCESS;
|
||||
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_mute(struct client *client, uint32_t command, uint32_t tag, struct message *m)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_set_stream_name(struct client *client, uint32_t command, uint32_t tag, struct message *m)
|
||||
{
|
||||
struct impl *impl = client->impl;
|
||||
|
|
@ -2325,46 +2486,6 @@ exit:
|
|||
return res;
|
||||
}
|
||||
|
||||
static void select_best(struct selector *s, struct pw_manager_object *o)
|
||||
{
|
||||
const char *str;
|
||||
int32_t prio = 0;
|
||||
|
||||
if (o->props &&
|
||||
(str = pw_properties_get(o->props, PW_KEY_PRIORITY_DRIVER)) != NULL) {
|
||||
prio = pw_properties_parse_int(str);
|
||||
if (prio > s->score) {
|
||||
s->best = o;
|
||||
s->score = prio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const char *get_default(struct client *client, bool sink)
|
||||
{
|
||||
struct selector sel;
|
||||
struct pw_manager *manager = client->manager;
|
||||
struct pw_manager_object *o;
|
||||
const char *def, *str;
|
||||
|
||||
spa_zero(sel);
|
||||
if (sink) {
|
||||
sel.type = is_sink;
|
||||
sel.id = client->default_sink;
|
||||
def = "@DEFAULT_SINK@";
|
||||
} else {
|
||||
sel.type = is_source;
|
||||
sel.id = client->default_source;
|
||||
def = "@DEFAULT_SOURCE@";
|
||||
}
|
||||
sel.accumulate = select_best;
|
||||
|
||||
o = select_object(manager, &sel);
|
||||
if (o == NULL || o->props == NULL ||
|
||||
((str = pw_properties_get(o->props, PW_KEY_NODE_NAME)) == NULL))
|
||||
return def;
|
||||
return str;
|
||||
}
|
||||
|
||||
static int do_get_server_info(struct client *client, uint32_t command, uint32_t tag, struct message *m)
|
||||
{
|
||||
|
|
@ -2430,36 +2551,21 @@ static int do_stat(struct client *client, uint32_t command, uint32_t tag, struct
|
|||
static int do_lookup(struct client *client, uint32_t command, uint32_t tag, struct message *m)
|
||||
{
|
||||
struct impl *impl = client->impl;
|
||||
struct pw_manager *manager = client->manager;
|
||||
struct message *reply;
|
||||
struct pw_manager_object *o;
|
||||
struct selector sel;
|
||||
const char *name;
|
||||
int res;
|
||||
|
||||
spa_zero(sel);
|
||||
sel.key = PW_KEY_NODE_NAME;
|
||||
|
||||
if ((res = message_get(m,
|
||||
TAG_STRING, &sel.value,
|
||||
TAG_STRING, &name,
|
||||
TAG_INVALID)) < 0)
|
||||
return res;
|
||||
if (sel.value == NULL)
|
||||
if (name == NULL)
|
||||
goto error_invalid;
|
||||
|
||||
pw_log_info(NAME" %p: LOOKUP %s", impl, sel.value);
|
||||
pw_log_info(NAME" %p: LOOKUP %s", impl, name);
|
||||
|
||||
if (command == COMMAND_LOOKUP_SINK) {
|
||||
if (strcmp(sel.value, "@DEFAULT_SINK@"))
|
||||
sel.value = get_default(client, true);
|
||||
sel.type = is_sink;
|
||||
}
|
||||
else {
|
||||
if (strcmp(sel.value, "@DEFAULT_SOURCE@"))
|
||||
sel.value = get_default(client, false);
|
||||
sel.type = is_source;
|
||||
}
|
||||
|
||||
if ((o = select_object(manager, &sel)) == NULL)
|
||||
if ((o = find_device(client, SPA_ID_INVALID, name, command == COMMAND_LOOKUP_SINK)) == NULL)
|
||||
goto error_noentity;
|
||||
|
||||
reply = reply_new(client, tag);
|
||||
|
|
@ -2700,7 +2806,6 @@ static int fill_sink_info(struct client *client, struct message *m,
|
|||
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 = is_card, };
|
||||
card = select_object(manager, &sel);
|
||||
|
|
@ -3368,12 +3473,12 @@ static const struct command commands[COMMAND_MAX] =
|
|||
[COMMAND_GET_SAMPLE_INFO_LIST] = { "GET_SAMPLE_INFO_LIST", do_get_info_list, },
|
||||
[COMMAND_GET_CARD_INFO_LIST] = { "GET_CARD_INFO_LIST", do_get_info_list, },
|
||||
|
||||
[COMMAND_SET_SINK_VOLUME] = { "SET_SINK_VOLUME", do_error_not_implemented, },
|
||||
[COMMAND_SET_SINK_VOLUME] = { "SET_SINK_VOLUME", do_set_volume, },
|
||||
[COMMAND_SET_SINK_INPUT_VOLUME] = { "SET_SINK_INPUT_VOLUME", do_set_stream_volume, },
|
||||
[COMMAND_SET_SOURCE_VOLUME] = { "SET_SOURCE_VOLUME", do_error_not_implemented, },
|
||||
[COMMAND_SET_SOURCE_VOLUME] = { "SET_SOURCE_VOLUME", do_set_volume, },
|
||||
|
||||
[COMMAND_SET_SINK_MUTE] = { "SET_SINK_MUTE", do_error_not_implemented, },
|
||||
[COMMAND_SET_SOURCE_MUTE] = { "SET_SOURCE_MUTE", do_error_not_implemented, },
|
||||
[COMMAND_SET_SINK_MUTE] = { "SET_SINK_MUTE", do_set_mute, },
|
||||
[COMMAND_SET_SOURCE_MUTE] = { "SET_SOURCE_MUTE", do_set_mute, },
|
||||
|
||||
[COMMAND_CORK_PLAYBACK_STREAM] = { "CORK_PLAYBACK_STREAM", do_cork_stream, },
|
||||
[COMMAND_FLUSH_PLAYBACK_STREAM] = { "FLUSH_PLAYBACK_STREAM", do_flush_trigger_prebuf_stream, },
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue