pulse-server: fill volumes and state

This commit is contained in:
Wim Taymans 2020-10-27 10:12:27 +01:00
parent 3c2b58d192
commit 2bf5cfa2f7
4 changed files with 198 additions and 95 deletions

View file

@ -274,6 +274,32 @@ enum {
SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U
};
enum {
STATE_INVALID = -1,
STATE_RUNNING = 0,
STATE_IDLE = 1,
STATE_SUSPENDED = 2,
STATE_INIT = -2,
STATE_UNLINKED = -3
};
static inline int node_state(enum pw_node_state state)
{
switch (state) {
case PW_NODE_STATE_ERROR:
return STATE_UNLINKED;
case PW_NODE_STATE_CREATING:
return STATE_INIT;
case PW_NODE_STATE_SUSPENDED:
return STATE_SUSPENDED;
case PW_NODE_STATE_IDLE:
return STATE_IDLE;
case PW_NODE_STATE_RUNNING:
return STATE_RUNNING;
}
return STATE_INVALID;
}
static inline bool pw_endswith(const char *s, const char *sfx)
{
size_t l1, l2;

View file

@ -97,6 +97,11 @@ struct sample_spec {
uint32_t rate;
uint8_t channels;
};
#define SAMPLE_SPEC_INIT (struct sample_spec) { \
.format = SAMPLE_FLOAT32LE, \
.rate = 44100, \
.channels = 2, \
};
static inline uint32_t sample_spec_frame_size(const struct sample_spec *ss)
{
@ -247,6 +252,12 @@ struct channel_map {
enum channel_position map[CHANNELS_MAX];
};
#define CHANNEL_MAP_INIT (struct channel_map) { \
.channels = 2, \
.map[0] = CHANNEL_POSITION_FRONT_LEFT, \
.map[1] = CHANNEL_POSITION_FRONT_RIGHT, \
}
static inline uint32_t channel_pa2id(enum channel_position channel)
{
if (channel < 0 || (size_t)channel >= SPA_N_ELEMENTS(audio_channels))
@ -283,11 +294,6 @@ static void channel_map_to_positions(const struct channel_map *map, uint32_t *po
pos[i] = channel_pa2id(map->map[i]);
}
struct volume {
uint8_t channels;
float values[CHANNELS_MAX];
};
enum encoding {
ENCODING_ANY,
ENCODING_PCM,

View file

@ -57,6 +57,7 @@
#include "defs.h"
#include "format.c"
#include "volume.c"
#include "message.c"
#include "manager.h"
@ -2800,9 +2801,9 @@ static int fill_sink_info(struct client *client, struct message *m,
struct pw_manager_object *o)
{
struct pw_node_info *info = o->info;
struct sample_spec ss;
struct volume volume;
struct channel_map map;
struct volume_info volume_info = VOLUME_INFO_INIT;
struct sample_spec ss = SAMPLE_SPEC_INIT;
struct channel_map map = CHANNEL_MAP_INIT;
const char *name, *str;
char *monitor_name = NULL;
uint32_t module_id = SPA_ID_INVALID;
@ -2812,27 +2813,6 @@ static int fill_sink_info(struct client *client, struct message *m,
if (o == NULL || info == NULL || info->props == NULL || !is_sink(o))
return ERR_NOENTITY;
ss = (struct sample_spec) {
.format = SAMPLE_FLOAT32LE,
.rate = 44100,
.channels = 2, };
map = (struct channel_map) {
.channels = 2,
.map[0] = 1,
.map[1] = 2, };
volume = (struct volume) {
.channels = 2,
.values[0] = 1.0f,
.values[1] = 1.0f, };
spa_list_for_each(p, &o->param_list, link) {
switch (p->id) {
case SPA_PARAM_Format:
format_parse_param(p->param, &ss, &map);
break;
}
}
if ((name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) != NULL) {
size_t size = strlen(name) + 10;
monitor_name = alloca(size);
@ -2843,6 +2823,16 @@ static int fill_sink_info(struct client *client, struct message *m,
if ((str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)) != NULL)
card_id = (uint32_t)atoi(str);
spa_list_for_each(p, &o->param_list, link) {
switch (p->id) {
case SPA_PARAM_Format:
format_parse_param(p->param, &ss, &map);
break;
}
}
if (volume_info.volume.channels != map.channels)
volume_info.volume.channels = map.channels;
message_put(m,
TAG_U32, o->id, /* sink index */
TAG_STRING, spa_dict_lookup(info->props, PW_KEY_NODE_NAME),
@ -2850,8 +2840,8 @@ static int fill_sink_info(struct client *client, struct message *m,
TAG_SAMPLE_SPEC, &ss,
TAG_CHANNEL_MAP, &map,
TAG_U32, module_id, /* module index */
TAG_CVOLUME, &volume,
TAG_BOOLEAN, false,
TAG_CVOLUME, &volume_info.volume,
TAG_BOOLEAN, volume_info.mute,
TAG_U32, o->id | 0x10000U, /* monitor source */
TAG_STRING, monitor_name, /* monitor source name */
TAG_USEC, 0LL, /* latency */
@ -2867,9 +2857,9 @@ static int fill_sink_info(struct client *client, struct message *m,
}
if (client->version >= 15) {
message_put(m,
TAG_VOLUME, 1.0f, /* base volume */
TAG_U32, 0, /* state */
TAG_U32, 256, /* n_volume_steps */
TAG_VOLUME, volume_info.base, /* base volume */
TAG_U32, node_state(info->state), /* state */
TAG_U32, volume_info.steps, /* n_volume_steps */
TAG_U32, card_id, /* card index */
TAG_INVALID);
}
@ -2897,34 +2887,22 @@ static int fill_source_info(struct client *client, struct message *m,
struct pw_manager_object *o)
{
struct pw_node_info *info = o->info;
struct sample_spec ss;
struct volume volume;
struct channel_map map;
struct volume_info volume_info = VOLUME_INFO_INIT;
struct sample_spec ss = SAMPLE_SPEC_INIT;
struct channel_map map = CHANNEL_MAP_INIT;
bool is_monitor;
const char *name, *desc, *str;
char *monitor_name = NULL;
char *monitor_desc = NULL;
uint32_t module_id = SPA_ID_INVALID;
uint32_t card_id = SPA_ID_INVALID;
struct pw_manager_param *p;
is_monitor = is_sink(o);
if (o == NULL || info == NULL || info->props == NULL ||
(!is_source(o) && !is_monitor))
return ERR_NOENTITY;
ss = (struct sample_spec) {
.format = SAMPLE_FLOAT32LE,
.rate = 44100,
.channels = 2, };
volume = (struct volume) {
.channels = 2,
.values[0] = 1.0f,
.values[1] = 1.0f, };
map = (struct channel_map) {
.channels = 2,
.map[0] = 1,
.map[1] = 2, };
if ((name = spa_dict_lookup(info->props, PW_KEY_NODE_NAME)) != NULL) {
size_t size = strlen(name) + 10;
monitor_name = alloca(size);
@ -2940,6 +2918,16 @@ static int fill_source_info(struct client *client, struct message *m,
if ((str = spa_dict_lookup(info->props, PW_KEY_DEVICE_ID)) != NULL)
card_id = (uint32_t)atoi(str);
spa_list_for_each(p, &o->param_list, link) {
switch (p->id) {
case SPA_PARAM_Format:
format_parse_param(p->param, &ss, &map);
break;
}
}
if (volume_info.volume.channels != map.channels)
volume_info.volume.channels = map.channels;
message_put(m,
TAG_U32, is_monitor ? o->id | 0x10000 : o->id, /* source index */
TAG_STRING, is_monitor ? monitor_name : name,
@ -2947,8 +2935,8 @@ static int fill_source_info(struct client *client, struct message *m,
TAG_SAMPLE_SPEC, &ss,
TAG_CHANNEL_MAP, &map,
TAG_U32, module_id, /* module index */
TAG_CVOLUME, &volume,
TAG_BOOLEAN, false,
TAG_CVOLUME, &volume_info.volume,
TAG_BOOLEAN, volume_info.mute,
TAG_U32, is_monitor ? o->id : SPA_ID_INVALID, /* monitor of sink */
TAG_STRING, is_monitor ? name : NULL, /* monitor of sink name */
TAG_USEC, 0LL, /* latency */
@ -2964,9 +2952,9 @@ static int fill_source_info(struct client *client, struct message *m,
}
if (client->version >= 15) {
message_put(m,
TAG_VOLUME, 1.0f, /* base volume */
TAG_U32, 0, /* state */
TAG_U32, 256, /* n_volume_steps */
TAG_VOLUME, volume_info.base, /* base volume */
TAG_U32, node_state(info->state), /* state */
TAG_U32, volume_info.steps, /* n_volume_steps */
TAG_U32, card_id, /* card index */
TAG_INVALID);
}
@ -2994,34 +2982,33 @@ static int fill_sink_input_info(struct client *client, struct message *m,
struct pw_manager_object *o)
{
struct pw_node_info *info = o->info;
struct sample_spec ss;
struct volume volume;
struct channel_map map;
struct volume_info volume_info = VOLUME_INFO_INIT;
struct sample_spec ss = SAMPLE_SPEC_INIT;
struct channel_map map = CHANNEL_MAP_INIT;
struct pw_manager_object *peer;
const char *str;
uint32_t module_id = SPA_ID_INVALID, client_id = SPA_ID_INVALID;
struct pw_manager_param *p;
if (o == NULL || info == NULL || info->props == NULL || !is_sink_input(o))
return ERR_NOENTITY;
ss = (struct sample_spec) {
.format = SAMPLE_FLOAT32LE,
.rate = 44100,
.channels = 2, };
volume = (struct volume) {
.channels = 2,
.values[0] = 1.0f,
.values[1] = 1.0f, };
map = (struct channel_map) {
.channels = 2,
.map[0] = 1,
.map[1] = 2, };
if ((str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID)) != NULL)
module_id = (uint32_t)atoi(str);
if ((str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID)) != NULL)
client_id = (uint32_t)atoi(str);
spa_list_for_each(p, &o->param_list, link) {
switch (p->id) {
case SPA_PARAM_Format:
format_parse_param(p->param, &ss, &map);
break;
case SPA_PARAM_Props:
volume_parse_param(p->param, &volume_info);
break;
}
}
peer = find_linked(client, o->id, PW_DIRECTION_OUTPUT);
message_put(m,
@ -3032,7 +3019,7 @@ static int fill_sink_input_info(struct client *client, struct message *m,
TAG_U32, peer ? peer->id : SPA_ID_INVALID, /* sink index */
TAG_SAMPLE_SPEC, &ss,
TAG_CHANNEL_MAP, &map,
TAG_CVOLUME, &volume,
TAG_CVOLUME, &volume_info.volume,
TAG_USEC, 0LL, /* latency */
TAG_USEC, 0LL, /* sink latency */
TAG_STRING, "PipeWire", /* resample method */
@ -3040,7 +3027,7 @@ static int fill_sink_input_info(struct client *client, struct message *m,
TAG_INVALID);
if (client->version >= 11)
message_put(m,
TAG_BOOLEAN, false, /* muted */
TAG_BOOLEAN, volume_info.mute, /* muted */
TAG_INVALID);
if (client->version >= 13)
message_put(m,
@ -3048,7 +3035,7 @@ static int fill_sink_input_info(struct client *client, struct message *m,
TAG_INVALID);
if (client->version >= 19)
message_put(m,
TAG_BOOLEAN, false, /* corked */
TAG_BOOLEAN, info->state != PW_NODE_STATE_RUNNING, /* corked */
TAG_INVALID);
if (client->version >= 20)
message_put(m,
@ -3070,35 +3057,34 @@ static int fill_source_output_info(struct client *client, struct message *m,
struct pw_manager_object *o)
{
struct pw_node_info *info = o->info;
struct sample_spec ss;
struct volume volume;
struct channel_map map;
struct volume_info volume_info = VOLUME_INFO_INIT;
struct sample_spec ss = SAMPLE_SPEC_INIT;
struct channel_map map = CHANNEL_MAP_INIT;
struct pw_manager_object *peer;
const char *str;
uint32_t module_id = SPA_ID_INVALID, client_id = SPA_ID_INVALID;
uint32_t peer_id;
struct pw_manager_param *p;
if (o == NULL || info == NULL || info->props == NULL || !is_source_output(o))
return ERR_NOENTITY;
ss = (struct sample_spec) {
.format = SAMPLE_FLOAT32LE,
.rate = 44100,
.channels = 2, };
volume = (struct volume) {
.channels = 2,
.values[0] = 1.0f,
.values[1] = 1.0f, };
map = (struct channel_map) {
.channels = 2,
.map[0] = 1,
.map[1] = 2, };
if ((str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID)) != NULL)
module_id = (uint32_t)atoi(str);
if ((str = spa_dict_lookup(info->props, PW_KEY_CLIENT_ID)) != NULL)
client_id = (uint32_t)atoi(str);
spa_list_for_each(p, &o->param_list, link) {
switch (p->id) {
case SPA_PARAM_Format:
format_parse_param(p->param, &ss, &map);
break;
case SPA_PARAM_Props:
volume_parse_param(p->param, &volume_info);
break;
}
}
peer = find_linked(client, o->id, PW_DIRECTION_INPUT);
if (peer) {
peer_id = peer->id;
@ -3127,15 +3113,15 @@ static int fill_source_output_info(struct client *client, struct message *m,
TAG_INVALID);
if (client->version >= 19)
message_put(m,
TAG_BOOLEAN, false, /* corked */
TAG_BOOLEAN, info->state != PW_NODE_STATE_RUNNING, /* corked */
TAG_INVALID);
if (client->version >= 22) {
struct format_info fi;
spa_zero(fi);
fi.encoding = ENCODING_PCM;
message_put(m,
TAG_CVOLUME, &volume,
TAG_BOOLEAN, false, /* muted */
TAG_CVOLUME, &volume_info.volume,
TAG_BOOLEAN, volume_info.mute, /* muted */
TAG_BOOLEAN, true, /* has_volume */
TAG_BOOLEAN, true, /* volume writable */
TAG_FORMAT_INFO, &fi,

View file

@ -0,0 +1,85 @@
/* PipeWire
*
* Copyright © 2020 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
struct volume {
uint8_t channels;
float values[CHANNELS_MAX];
};
#define VOLUME_INIT (struct volume) { \
.channels = 2, \
.values[0] = 1.0f, \
.values[1] = 1.0f, \
}
struct volume_info {
struct volume volume;
bool mute;
float level;
float base;
uint32_t steps;
};
#define VOLUME_INFO_INIT (struct volume_info) { \
.volume = VOLUME_INIT, \
.mute = false, \
.level = 1.0, \
.base = 1.0, \
.steps = 256, \
}
static int volume_parse_param(const struct spa_pod *param, struct volume_info *info)
{
struct spa_pod_object *obj = (struct spa_pod_object *) param;
struct spa_pod_prop *prop;
SPA_POD_OBJECT_FOREACH(obj, prop) {
switch (prop->key) {
case SPA_PROP_volume:
spa_pod_get_float(&prop->value, &info->level);
break;
case SPA_PROP_mute:
spa_pod_get_bool(&prop->value, &info->mute);
break;
case SPA_PROP_channelVolumes:
info->volume.channels = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
info->volume.values, SPA_AUDIO_MAX_CHANNELS);
break;
case SPA_PROP_volumeBase:
spa_pod_get_float(&prop->value, &info->base);
break;
case SPA_PROP_volumeStep:
{
float step;
if (spa_pod_get_float(&prop->value, &step) >= 0)
info->steps = 0x10000u * step;
break;
}
default:
break;
}
}
return 0;
}