pulse-server: add more introspection

Add default nodes support and subscription events when it changes.
Fix stream id.
Fix node lookup.
Fix node volume.
This commit is contained in:
Wim Taymans 2020-10-26 13:28:45 +01:00
parent f75acc31d3
commit a372e4e90e
4 changed files with 552 additions and 224 deletions

View file

@ -239,3 +239,37 @@ enum {
COMMAND_MAX
};
enum {
SUBSCRIPTION_MASK_NULL = 0x0000U,
SUBSCRIPTION_MASK_SINK = 0x0001U,
SUBSCRIPTION_MASK_SOURCE = 0x0002U,
SUBSCRIPTION_MASK_SINK_INPUT = 0x0004U,
SUBSCRIPTION_MASK_SOURCE_OUTPUT = 0x0008U,
SUBSCRIPTION_MASK_MODULE = 0x0010U,
SUBSCRIPTION_MASK_CLIENT = 0x0020U,
SUBSCRIPTION_MASK_SAMPLE_CACHE = 0x0040U,
SUBSCRIPTION_MASK_SERVER = 0x0080U,
SUBSCRIPTION_MASK_AUTOLOAD = 0x0100U,
SUBSCRIPTION_MASK_CARD = 0x0200U,
SUBSCRIPTION_MASK_ALL = 0x02ffU
};
enum {
SUBSCRIPTION_EVENT_SINK = 0x0000U,
SUBSCRIPTION_EVENT_SOURCE = 0x0001U,
SUBSCRIPTION_EVENT_SINK_INPUT = 0x0002U,
SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 0x0003U,
SUBSCRIPTION_EVENT_MODULE = 0x0004U,
SUBSCRIPTION_EVENT_CLIENT = 0x0005U,
SUBSCRIPTION_EVENT_SAMPLE_CACHE = 0x0006U,
SUBSCRIPTION_EVENT_SERVER = 0x0007U,
SUBSCRIPTION_EVENT_AUTOLOAD = 0x0008U,
SUBSCRIPTION_EVENT_CARD = 0x0009U,
SUBSCRIPTION_EVENT_FACILITY_MASK = 0x000FU,
SUBSCRIPTION_EVENT_NEW = 0x0000U,
SUBSCRIPTION_EVENT_CHANGE = 0x0010U,
SUBSCRIPTION_EVENT_REMOVE = 0x0020U,
SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U
};

View file

@ -23,24 +23,22 @@
*/
#include "manager.h"
#include <extensions/metadata.h>
#define manager_emit_sync(m) spa_hook_list_call(&m->hooks, struct pw_manager_events, sync, 0)
#define manager_emit_added(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, added, 0, o)
#define manager_emit_updated(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, updated, 0, o)
#define manager_emit_removed(m,o) spa_hook_list_call(&m->hooks, struct pw_manager_events, removed, 0, o)
#define manager_emit_metadata(m,s,k,t,v) spa_hook_list_call(&m->hooks, struct pw_manager_events, metadata,0,s,k,t,v)
struct manager {
struct pw_core *core;
struct pw_manager this;
struct spa_hook core_listener;
struct spa_hook registry_listener;
int sync_seq;
struct pw_registry *registry;
struct spa_hook registry_listener;
struct spa_hook_list hooks;
uint32_t n_objects;
struct spa_list object_list;
};
struct object_info {
@ -54,7 +52,6 @@ struct object {
struct pw_manager_object this;
struct manager *manager;
struct spa_list link;
const struct object_info *info;
@ -66,13 +63,13 @@ struct object {
static void core_sync(struct manager *m)
{
m->sync_seq = pw_core_sync(m->core, PW_ID_CORE, m->sync_seq);
m->sync_seq = pw_core_sync(m->this.core, PW_ID_CORE, m->sync_seq);
}
static struct object *find_object(struct manager *m, uint32_t id)
{
struct object *o;
spa_list_for_each(o, &m->object_list, link) {
spa_list_for_each(o, &m->this.object_list, this.link) {
if (o->this.id == id)
return o;
}
@ -87,8 +84,8 @@ static void object_destroy(struct object *o)
free(o->this.type);
if (o->this.props)
pw_properties_free(o->this.props);
spa_list_remove(&o->link);
m->n_objects--;
spa_list_remove(&o->this.link);
m->this.n_objects--;
free(o);
}
@ -112,7 +109,7 @@ static void client_destroy(void *data)
pw_client_info_free(o->this.info);
}
struct object_info client_info = {
static const struct object_info client_info = {
.type = PW_TYPE_INTERFACE_Client,
.version = PW_VERSION_CLIENT,
.events = &client_events,
@ -139,7 +136,7 @@ static void module_destroy(void *data)
pw_module_info_free(o->this.info);
}
struct object_info module_info = {
static const struct object_info module_info = {
.type = PW_TYPE_INTERFACE_Module,
.version = PW_VERSION_MODULE,
.events = &module_events,
@ -166,7 +163,7 @@ static void device_destroy(void *data)
pw_device_info_free(o->this.info);
}
struct object_info device_info = {
static const struct object_info device_info = {
.type = PW_TYPE_INTERFACE_Device,
.version = PW_VERSION_DEVICE,
.events = &device_events,
@ -176,9 +173,9 @@ struct object_info device_info = {
/* node */
static void node_event_info(void *object, const struct pw_node_info *info)
{
struct object *o = object;
pw_log_debug("object %p: id:%d change-mask:%"PRIu64, o, o->this.id, info->change_mask);
info = o->this.info = pw_node_info_update(o->this.info, info);
struct object *o = object;
pw_log_debug("object %p: id:%d change-mask:%"PRIu64, o, o->this.id, info->change_mask);
info = o->this.info = pw_node_info_update(o->this.info, info);
}
static const struct pw_node_events node_events = {
@ -193,19 +190,51 @@ static void node_destroy(void *data)
pw_node_info_free(o->this.info);
}
struct object_info node_info = {
static const struct object_info node_info = {
.type = PW_TYPE_INTERFACE_Node,
.version = PW_VERSION_NODE,
.events = &node_events,
.destroy = node_destroy,
};
/* link */
static const struct object_info link_info = {
.type = PW_TYPE_INTERFACE_Link,
.version = PW_VERSION_LINK,
};
/* metadata */
static int metadata_property(void *object,
uint32_t subject,
const char *key,
const char *type,
const char *value)
{
struct object *o = object;
struct manager *m = o->manager;
manager_emit_metadata(m, subject, key, type, value);
return 0;
}
static const struct pw_metadata_events metadata_events = {
PW_VERSION_METADATA_EVENTS,
.property = metadata_property,
};
static const struct object_info metadata_info = {
.type = PW_TYPE_INTERFACE_Metadata,
.version = PW_VERSION_METADATA,
.events = &metadata_events,
};
static const struct object_info *objects[] =
{
&module_info,
&client_info,
&device_info,
&node_info,
&link_info,
&metadata_info,
};
static const struct object_info *find_info(const char *type, uint32_t version)
@ -273,7 +302,7 @@ static void registry_event_global(void *data, uint32_t id,
if (info == NULL)
return;
proxy = pw_registry_bind(m->registry,
proxy = pw_registry_bind(m->this.registry,
id, type, info->version, 0);
if (proxy == NULL)
return;
@ -295,12 +324,13 @@ static void registry_event_global(void *data, uint32_t id,
o->manager = m;
o->info = info;
o->new = true;
spa_list_append(&m->object_list, &o->link);
m->n_objects++;
spa_list_append(&m->this.object_list, &o->this.link);
m->this.n_objects++;
pw_proxy_add_object_listener(proxy,
&o->object_listener,
o->info->events, o);
if (info->events)
pw_proxy_add_object_listener(proxy,
&o->object_listener,
o->info->events, o);
pw_proxy_add_listener(proxy,
&o->proxy_listener,
&proxy_events, o);
@ -331,9 +361,8 @@ static void on_core_done(void *data, uint32_t id, int seq)
{
struct manager *m = data;
if (id == PW_ID_CORE) {
if (m->sync_seq == seq) {
if (m->sync_seq == seq)
manager_emit_sync(m);
}
}
}
@ -344,44 +373,46 @@ static const struct pw_core_events core_events = {
struct pw_manager *pw_manager_new(struct pw_core *core)
{
struct manager *this;
struct manager *m;
this = calloc(1, sizeof(*this));
if (this == NULL)
m = calloc(1, sizeof(*m));
if (m == NULL)
return NULL;
this->core = core;
spa_hook_list_init(&this->hooks);
m->this.core = core;
spa_hook_list_init(&m->hooks);
spa_list_init(&this->object_list);
spa_list_init(&m->this.object_list);
pw_core_add_listener(this->core,
&this->core_listener,
&core_events, this);
this->registry = pw_core_get_registry(this->core,
pw_core_add_listener(m->this.core,
&m->core_listener,
&core_events, m);
m->this.registry = pw_core_get_registry(m->this.core,
PW_VERSION_REGISTRY, 0);
pw_registry_add_listener(this->registry,
&this->registry_listener,
&registry_events, this);
pw_registry_add_listener(m->this.registry,
&m->registry_listener,
&registry_events, m);
return (struct pw_manager*)this;
core_sync(m);
return &m->this;
}
void pw_manager_add_listener(struct pw_manager *manager,
struct spa_hook *listener,
const struct pw_manager_events *events, void *data)
{
struct manager *this = (struct manager*)manager;
spa_hook_list_append(&this->hooks, listener, events, data);
struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
spa_hook_list_append(&m->hooks, listener, events, data);
}
struct pw_manager_object *pw_manager_find_object(struct pw_manager *manager,
uint32_t id)
{
struct manager *this = (struct manager*)manager;
struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
struct object *o;
o = find_object(this, id);
o = find_object(m, id);
if (o == NULL)
return NULL;
return (struct pw_manager_object*)o;
@ -391,11 +422,11 @@ int pw_manager_for_each_object(struct pw_manager *manager,
int (*callback) (void *data, struct pw_manager_object *object),
void *data)
{
struct manager *this = (struct manager*)manager;
struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
struct object *o;
int res;
spa_list_for_each(o, &this->object_list, link) {
spa_list_for_each(o, &m->this.object_list, this.link) {
if ((res = callback(data, &o->this)) != 0)
return res;
}
@ -404,17 +435,17 @@ int pw_manager_for_each_object(struct pw_manager *manager,
void pw_manager_destroy(struct pw_manager *manager)
{
struct manager *this = (struct manager*)manager;
struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
struct object *o;
spa_hook_remove(&this->core_listener);
spa_hook_remove(&m->core_listener);
spa_list_consume(o, &this->object_list, link)
spa_list_consume(o, &m->this.object_list, this.link)
object_destroy(o);
if (this->registry) {
spa_hook_remove(&this->registry_listener);
pw_proxy_destroy((struct pw_proxy*)this->registry);
if (m->this.registry) {
spa_hook_remove(&m->registry_listener);
pw_proxy_destroy((struct pw_proxy*)m->this.registry);
}
free(this);
free(m);
}

View file

@ -34,7 +34,6 @@ extern "C" {
#include <pipewire/pipewire.h>
struct pw_manager;
struct pw_manager_object;
struct pw_manager_events {
@ -50,15 +49,27 @@ struct pw_manager_events {
void (*updated) (void *data, struct pw_manager_object *object);
void (*removed) (void *data, struct pw_manager_object *object);
void (*metadata) (void *data, uint32_t subject, const char *key,
const char *type, const char *value);
};
struct pw_manager {
struct pw_core *core;
struct pw_registry *registry;
uint32_t n_objects;
struct spa_list object_list;
};
struct pw_manager_param {
uint32_t id;
struct spa_list link; /**< link in param_list */
struct spa_list link; /**< link in manager_object param_list */
struct spa_pod *param;
};
struct pw_manager_object {
struct spa_list link; /**< link in manager object_list */
uint32_t id;
uint32_t permissions;
char *type;
@ -78,8 +89,6 @@ void pw_manager_add_listener(struct pw_manager *manager,
void pw_manager_destroy(struct pw_manager *manager);
struct pw_manager_object *pw_manager_find_object(struct pw_manager *manager,
uint32_t id);
int pw_manager_for_each_object(struct pw_manager *manager,
int (*callback) (void *data, struct pw_manager_object *object),
void *data);

View file

@ -87,6 +87,14 @@ struct client {
struct pw_core *core;
struct pw_manager *manager;
struct spa_hook manager_listener;
uint32_t cookie;
uint32_t default_rate;
uint32_t subscribed;
uint32_t default_sink;
uint32_t default_source;
uint32_t connect_tag;
uint32_t in_index;
@ -114,6 +122,7 @@ struct buffer_attr {
struct stream {
uint32_t create_tag;
uint32_t channel; /* index in map */
uint32_t id; /* id of global */
struct impl *impl;
struct client *client;
@ -354,6 +363,22 @@ static int send_underflow(struct stream *stream, int64_t offset)
return send_message(client, reply);
}
static int send_subscribe_event(struct client *client, uint32_t event, uint32_t id)
{
struct message *reply;
pw_log_info(NAME" %p: SUBSCRIBE event:%08x id:%u", client, event, id);
reply = message_alloc(client, -1, 0);
message_put(reply,
TAG_U32, COMMAND_SUBSCRIBE_EVENT,
TAG_U32, -1,
TAG_U32, event,
TAG_U32, id,
TAG_INVALID);
return send_message(client, reply);
}
static int send_overflow(struct stream *stream)
{
struct client *client = stream->client;
@ -466,11 +491,69 @@ static void manager_sync(void *data)
}
}
static void manager_added(void *data, struct pw_manager_object *o)
{
struct client *client = data;
const char *str;
if (strcmp(o->type, PW_TYPE_INTERFACE_Core) == 0 && o->info != NULL) {
struct pw_core_info *info = o->info;
if (info->props &&
(str = spa_dict_lookup(info->props, "default.clock.rate")) != NULL)
client->default_rate = atoi(str);
client->cookie = info->cookie;
}
}
static void manager_metadata(void *data, uint32_t subject, const char *key,
const char *type, const char *value)
{
struct client *client = data;
uint32_t val;
bool changed = false;
pw_log_debug("meta %d %s %s %s", subject, key, type, value);
if (subject == PW_ID_CORE) {
val = (key && value) ? (uint32_t)atoi(value) : SPA_ID_INVALID;
if (key == NULL || strcmp(key, "default.audio.sink") == 0) {
changed = client->default_sink != val;
client->default_sink = val;
}
if (key == NULL || strcmp(key, "default.audio.source") == 0) {
changed = client->default_source != val;
client->default_source = val;
}
}
if (changed) {
if (client->subscribed & SUBSCRIPTION_MASK_SERVER) {
send_subscribe_event(client,
SUBSCRIPTION_EVENT_CHANGE |
SUBSCRIPTION_EVENT_SERVER,
-1);
}
}
}
static const struct pw_manager_events manager_events = {
PW_VERSION_MANAGER_EVENTS,
.sync = manager_sync,
.added = manager_added,
.metadata = manager_metadata,
};
static struct stream *find_stream(struct client *client, uint32_t id)
{
union pw_map_item *item;
pw_array_for_each(item, &client->streams.items) {
struct stream *s = item->data;
if (!pw_map_item_is_free(item) &&
s->id == id)
return s;
}
return NULL;
}
static int do_set_client_name(struct client *client, uint32_t command, uint32_t tag, struct message *m)
{
struct impl *impl = client->impl;
@ -538,6 +621,7 @@ static int do_subscribe(struct client *client, uint32_t command, uint32_t tag, s
return res;
pw_log_info(NAME" %p: SUBSCRIBE mask:%08x", impl, mask);
client->subscribed = mask;
reply = reply_new(client, tag);
@ -752,9 +836,9 @@ static int reply_create_playback_stream(struct stream *stream)
reply = reply_new(client, stream->create_tag);
message_put(reply,
TAG_U32, stream->channel, /* stream index/channel */
TAG_U32, pw_stream_get_node_id(stream->stream), /* sink_input/stream index */
TAG_U32, size, /* missing/requested bytes */
TAG_U32, stream->channel, /* stream index/channel */
TAG_U32, stream->id, /* sink_input/stream index */
TAG_U32, size, /* missing/requested bytes */
TAG_INVALID);
stream->pending = size;
@ -841,7 +925,7 @@ static int reply_create_record_stream(struct stream *stream)
reply = reply_new(client, stream->create_tag);
message_put(reply,
TAG_U32, stream->channel, /* stream index/channel */
TAG_U32, pw_stream_get_node_id(stream->stream), /* source_output/stream index */
TAG_U32, stream->id, /* source_output/stream index */
TAG_INVALID);
if (client->version >= 9) {
@ -959,6 +1043,8 @@ static void stream_param_changed(void *data, uint32_t id, const struct spa_pod *
}
if (stream->create_tag != SPA_ID_INVALID) {
stream->id = pw_stream_get_node_id(stream->stream);
if (stream->volume_set) {
pw_stream_set_control(stream->stream,
SPA_PROP_channelVolumes, stream->volume.channels, stream->volume.values, 0);
@ -1146,56 +1232,96 @@ static const struct pw_stream_events stream_events =
.drained = stream_drained,
};
static struct pw_manager_object *find_node_by_name(struct impl *impl, const char *name)
static bool is_client(struct pw_manager_object *o)
{
return NULL;
return strcmp(o->type, PW_TYPE_INTERFACE_Client) == 0;
}
#if 0
static struct device *find_device_by_name(struct impl *impl, const char *name)
static bool is_module(struct pw_manager_object *o)
{
struct device *dev;
if (strcmp(name, impl->default_source.name) == 0 ||
strcmp(name, "@DEFAULT_SOURCE@") == 0 ||
(uint32_t)atoi(name) == impl->default_source.index)
dev = &impl->default_source;
else if (strcmp(name, impl->default_sink.name) == 0 ||
strcmp(name, "@DEFAULT_SINK@") == 0 ||
(uint32_t)atoi(name) == impl->default_sink.index)
dev = &impl->default_sink;
else if (strcmp(name, impl->default_monitor.name) == 0 ||
strcmp(name, "@DEFAULT_MONITOR@") == 0 ||
(uint32_t)atoi(name) == impl->default_monitor.index)
dev = &impl->default_monitor;
else
dev = NULL;
return dev;
return strcmp(o->type, PW_TYPE_INTERFACE_Module) == 0;
}
static struct device *find_device_by_index(struct impl *impl, uint32_t index)
static bool is_card(struct pw_manager_object *o)
{
struct device *dev;
if (impl->default_source.index == index)
dev = &impl->default_source;
else if (impl->default_sink.index == index)
dev = &impl->default_sink;
else if (impl->default_monitor.index == index)
dev = &impl->default_monitor;
else
dev = NULL;
return dev;
const char *str;
return
strcmp(o->type, PW_TYPE_INTERFACE_Device) == 0 &&
o->props != NULL &&
(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
strcmp(str, "Audio/Device") == 0;
}
static struct device *find_device(struct impl *impl, uint32_t idx, const char *name)
static bool is_sink(struct pw_manager_object *o)
{
struct device *dev;
if (idx != SPA_ID_INVALID)
dev = find_device_by_index(impl, idx);
else
dev = find_device_by_name(impl, name);
return dev;
const char *str;
return
strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 &&
o->props != NULL &&
(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
strcmp(str, "Audio/Sink") == 0;
}
static bool is_source(struct pw_manager_object *o)
{
const char *str;
return
strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 &&
o->props != NULL &&
(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
strcmp(str, "Audio/Source") == 0;
}
static bool is_sink_input(struct pw_manager_object *o)
{
const char *str;
return
strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 &&
o->props != NULL &&
(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
strcmp(str, "Stream/Output/Audio") == 0;
}
static bool is_source_output(struct pw_manager_object *o)
{
const char *str;
return
strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 &&
o->props != NULL &&
(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
strcmp(str, "Stream/Input/Audio") == 0;
}
struct selector {
bool (*type) (struct pw_manager_object *o);
uint32_t id;
const char *key;
const char *value;
void (*accumulate) (struct selector *sel, struct pw_manager_object *o);
int32_t score;
struct pw_manager_object *best;
};
static struct pw_manager_object *select_object(struct pw_manager *m,
struct selector *s)
{
struct pw_manager_object *o;
const char *str;
spa_list_for_each(o, &m->object_list, link) {
if (s->type != NULL && !s->type(o))
continue;
if (o->id == s->id)
return o;
if (s->accumulate)
s->accumulate(s, o);
if (o->props && s->key != NULL && s->value != NULL &&
(str = pw_properties_get(o->props, s->key)) != NULL &&
strcmp(str, s->value) == 0)
return o;
}
return s->best;
}
#endif
static void fix_stream_properties(struct stream *stream, struct pw_properties *props)
{
@ -1853,67 +1979,156 @@ static int do_error_access(struct client *client, uint32_t command, uint32_t tag
return reply_error(client, tag, ERR_ACCESS);
}
static int set_node_volume(struct pw_manager_object *o, struct volume *vol)
{
char buf[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
pw_node_set_param((struct pw_node*)o->proxy,
SPA_PARAM_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)));
return 0;
}
static int set_node_mute(struct pw_manager_object *o, bool mute)
{
char buf[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
pw_node_set_param((struct pw_node*)o->proxy,
SPA_PARAM_Props, 0,
spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
SPA_PROP_mute, SPA_POD_Bool(mute)));
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;
uint32_t channel;
uint32_t id;
struct stream *stream;
int res;
struct volume volume;
int res;
if ((res = message_get(m,
TAG_U32, &channel,
TAG_U32, &id,
TAG_CVOLUME, &volume,
TAG_INVALID)) < 0)
return res;
goto error_protocol;
pw_log_info(NAME" %p: DO_STREAM_VOLUME channel:%u",
impl, channel);
stream = pw_map_lookup(&client->streams, channel);
if (stream == NULL)
return -EINVAL;
pw_log_info(NAME" %p: DO_STREAM_VOLUME index:%u", impl, id);
stream->volume = volume;
stream->volume_set = true;
stream = find_stream(client, id);
if (stream != NULL) {
stream->volume = volume;
stream->volume_set = true;
pw_stream_set_control(stream->stream,
SPA_PROP_channelVolumes, volume.channels, volume.values,
0);
pw_stream_set_control(stream->stream,
SPA_PROP_channelVolumes, volume.channels, volume.values,
0);
} else {
struct selector sel;
struct pw_manager_object *o;
spa_zero(sel);
sel.id = id;
if (command == COMMAND_SET_SINK_INPUT_VOLUME)
sel.type = is_sink_input;
else
sel.type = is_source_output;
o = select_object(client->manager, &sel);
if (o == NULL)
goto error_noentity;
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_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, -1, res);
}
static int do_set_stream_mute(struct client *client, uint32_t command, uint32_t tag, struct message *m)
{
struct impl *impl = client->impl;
uint32_t channel;
uint32_t id;
struct stream *stream;
int res;
bool mute;
float val;
if ((res = message_get(m,
TAG_U32, &channel,
TAG_U32, &id,
TAG_BOOLEAN, &mute,
TAG_INVALID)) < 0)
return res;
goto error_protocol;
pw_log_info(NAME" %p: DO_SET_STREAM_MUTE channel:%u mute:%u",
impl, channel, mute);
pw_log_info(NAME" %p: DO_SET_STREAM_MUTE id:%u mute:%u",
impl, id, mute);
stream = pw_map_lookup(&client->streams, channel);
if (stream == NULL)
return -EINVAL;
stream = find_stream(client, id);
if (stream != NULL) {
float val;
stream->muted = mute;
stream->muted_set = true;
stream->muted = mute;
stream->muted_set = true;
val = mute ? 1.0f : 0.0f;
pw_stream_set_control(stream->stream,
SPA_PROP_mute, 1, &val,
0);
val = mute ? 1.0f : 0.0f;
pw_stream_set_control(stream->stream,
SPA_PROP_mute, 1, &val,
0);
} else {
struct selector sel;
struct pw_manager_object *o;
spa_zero(sel);
sel.id = id;
if (command == COMMAND_SET_SINK_INPUT_VOLUME)
sel.type = is_sink_input;
else
sel.type = is_source_output;
o = select_object(client->manager, &sel);
if (o == NULL)
goto error_noentity;
if (!SPA_FLAG_IS_SET(o->permissions, PW_PERM_W | PW_PERM_X))
goto error_access;
set_node_mute(o, mute);
}
return reply_simple_ack(client, tag);
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, -1, res);
}
static int do_set_stream_name(struct client *client, uint32_t command, uint32_t tag, struct message *m)
@ -2058,6 +2273,46 @@ 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_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(client->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)
{
struct impl *impl = client->impl;
@ -2072,7 +2327,7 @@ static int do_get_server_info(struct client *client, uint32_t command, uint32_t
spa_zero(ss);
ss.format = SAMPLE_FLOAT32LE;
ss.rate = 44100;
ss.rate = client->default_rate ? client->default_rate : 44100;
ss.channels = 2;
spa_zero(map);
@ -2087,9 +2342,9 @@ static int do_get_server_info(struct client *client, uint32_t command, uint32_t
TAG_STRING, pw_get_user_name(),
TAG_STRING, pw_get_host_name(),
TAG_SAMPLE_SPEC, &ss,
TAG_STRING, NULL, /* default sink name */
TAG_STRING, NULL /* default source name */,
TAG_U32, 0,
TAG_STRING, get_default(client, true), /* default sink name */
TAG_STRING, get_default(client, false), /* default source name */
TAG_U32, client->cookie, /* cookie */
TAG_INVALID);
if (client->version >= 15) {
@ -2122,25 +2377,40 @@ 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;
const char *name = NULL;
struct message *reply;
struct pw_manager_object *o;
struct selector sel;
int res;
spa_zero(sel);
sel.key = PW_KEY_NODE_NAME;
if ((res = message_get(m,
TAG_STRING, &name,
TAG_STRING, &sel.value,
TAG_INVALID)) < 0)
return res;
if (name == NULL)
if (sel.value == NULL)
goto error_invalid;
pw_log_info(NAME" %p: LOOKUP %s", impl, name);
if ((o = find_node_by_name(impl, name)) == NULL)
pw_log_info(NAME" %p: LOOKUP %s", impl, sel.value);
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(client->manager, &sel)) == NULL)
goto error_noentity;
reply = reply_new(client, tag);
message_put(reply,
TAG_U32, SPA_ID_INVALID,
TAG_U32, o->id,
TAG_INVALID);
return send_message(client, reply);
@ -2184,8 +2454,7 @@ static int fill_client_info(struct client *client, struct message *m,
{
struct pw_client_info *info = o->info;
if (o == NULL ||
strcmp(o->type, PW_TYPE_INTERFACE_Client) != 0)
if (o == NULL || !is_client(o))
return ERR_NOENTITY;
message_put(m,
@ -2207,14 +2476,13 @@ static int fill_module_info(struct client *client, struct message *m,
{
struct pw_module_info *info = o->info;
if (o == NULL ||
strcmp(o->type, PW_TYPE_INTERFACE_Module) != 0)
if (o == NULL || info == NULL || !is_module(o))
return ERR_NOENTITY;
message_put(m,
TAG_U32, o->id, /* module index */
TAG_STRING, info ? info->name : NULL,
TAG_STRING, info ? info->args : NULL,
TAG_STRING, info->name,
TAG_STRING, info->args,
TAG_U32, -1, /* n_used */
TAG_INVALID);
@ -2225,7 +2493,7 @@ static int fill_module_info(struct client *client, struct message *m,
}
if (client->version >= 15) {
message_put(m,
TAG_PROPLIST, info ? info->props : NULL,
TAG_PROPLIST, info->props,
TAG_INVALID);
}
return 0;
@ -2238,11 +2506,7 @@ static int fill_card_info(struct client *client, struct message *m,
const char *str;
uint32_t module_id = SPA_ID_INVALID;
if (o == NULL ||
strcmp(o->type, PW_TYPE_INTERFACE_Device) != 0 ||
o->props == NULL || info == NULL || info->props == NULL ||
(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) == NULL ||
strcmp(str, "Audio/Device") != 0)
if (o == NULL || info == NULL || info->props == NULL || !is_card(o))
return ERR_NOENTITY;
if ((str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID)) != NULL)
@ -2318,16 +2582,11 @@ static int fill_sink_info(struct client *client, struct message *m,
struct pw_manager_object *o)
{
struct pw_node_info *info = o->info;
const char *str;
struct sample_spec ss;
struct volume volume;
struct channel_map map;
if (o == NULL ||
strcmp(o->type, PW_TYPE_INTERFACE_Node) != 0 ||
o->props == NULL || info == NULL || info->props == NULL ||
(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) == NULL ||
strcmp(str, "Audio/Sink") != 0)
if (o == NULL || info == NULL || info->props == NULL || !is_sink(o))
return ERR_NOENTITY;
ss = (struct sample_spec) {
@ -2397,21 +2656,14 @@ static int fill_source_info(struct client *client, struct message *m,
struct pw_manager_object *o)
{
struct pw_node_info *info = o->info;
const char *str;
struct sample_spec ss;
struct volume volume;
struct channel_map map;
bool is_monitor;
if (o == NULL ||
strcmp(o->type, PW_TYPE_INTERFACE_Node) != 0 ||
o->props == NULL || info == NULL || info->props == NULL ||
(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) == NULL)
return ERR_NOENTITY;
is_monitor = strcmp(str, "Audio/Sink") == 0;
if (strcmp(str, "Audio/Source") != 0 &&
!is_monitor)
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) {
@ -2481,16 +2733,11 @@ static int fill_sink_input_info(struct client *client, struct message *m,
struct pw_manager_object *o)
{
struct pw_node_info *info = o->info;
const char *str;
struct sample_spec ss;
struct volume volume;
struct channel_map map;
if (o == NULL ||
strcmp(o->type, PW_TYPE_INTERFACE_Node) != 0 ||
o->props == NULL || info == NULL || info->props == NULL ||
(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) == NULL ||
strcmp(str, "Stream/Output/Audio") != 0)
if (o == NULL || info == NULL || info->props == NULL || !is_sink_input(o))
return ERR_NOENTITY;
ss = (struct sample_spec) {
@ -2552,16 +2799,11 @@ static int fill_source_output_info(struct client *client, struct message *m,
struct pw_manager_object *o)
{
struct pw_node_info *info = o->info;
const char *str;
struct sample_spec ss;
struct volume volume;
struct channel_map map;
if (o == NULL ||
strcmp(o->type, PW_TYPE_INTERFACE_Node) != 0 ||
o->props == NULL || info == NULL || info->props == NULL ||
(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) == NULL ||
strcmp(str, "Stream/Input/Audio") != 0)
if (o == NULL || info == NULL || info->props == NULL || !is_source_output(o))
return ERR_NOENTITY;
ss = (struct sample_spec) {
@ -2617,71 +2859,79 @@ static int do_get_info(struct client *client, uint32_t command, uint32_t tag, st
{
struct impl *impl = client->impl;
struct message *reply = NULL;
uint32_t idx;
const char *name = NULL;
int res, err;
struct pw_manager_object *o;
struct selector sel;
int (*fill_func) (struct client *client, struct message *m, struct pw_manager_object *o) = NULL;
spa_zero(sel);
if ((res = message_get(m,
TAG_U32, &idx,
TAG_U32, &sel.id,
TAG_INVALID)) < 0)
goto error_protocol;
switch (command) {
case COMMAND_GET_SINK_INFO:
case COMMAND_GET_SOURCE_INFO:
case COMMAND_GET_CLIENT_INFO:
sel.type = is_client;
fill_func = fill_client_info;
break;
case COMMAND_GET_MODULE_INFO:
sel.type = is_module;
fill_func = fill_module_info;
break;
case COMMAND_GET_CARD_INFO:
sel.type = is_card;
sel.key = PW_KEY_DEVICE_NAME;
fill_func = fill_card_info;
break;
case COMMAND_GET_SAMPLE_INFO:
if ((res = message_get(m,
TAG_STRING, &name,
TAG_INVALID)) < 0)
goto error_protocol;
sel.key = "";
break;
case COMMAND_GET_SINK_INFO:
sel.type = is_sink;
sel.key = PW_KEY_NODE_NAME;
fill_func = fill_sink_info;
break;
case COMMAND_GET_SOURCE_INFO:
sel.type = is_source;
sel.key = PW_KEY_NODE_NAME;
fill_func = fill_source_info;
break;
case COMMAND_GET_SINK_INPUT_INFO:
sel.type = is_sink_input;
fill_func = fill_sink_input_info;
break;
case COMMAND_GET_SOURCE_OUTPUT_INFO:
sel.type = is_source_output;
fill_func = fill_source_output_info;
break;
}
if (sel.key) {
if ((res = message_get(m,
TAG_STRING, &sel.value,
TAG_INVALID)) < 0)
goto error_protocol;
}
if ((idx == SPA_ID_INVALID && name == NULL) ||
(idx != SPA_ID_INVALID && name != NULL))
if ((sel.id == SPA_ID_INVALID && sel.value == NULL) ||
(sel.id != SPA_ID_INVALID && sel.value != NULL))
goto error_invalid;
pw_log_info(NAME" %p: %s idx:%u name:%s", impl,
commands[command].name, idx, name);
commands[command].name, sel.id, sel.value);
o = pw_manager_find_object(client->manager, idx);
o = select_object(client->manager, &sel);
if (o == NULL)
goto error_noentity;
reply = reply_new(client, tag);
switch (command) {
case COMMAND_GET_CLIENT_INFO:
err = fill_client_info(client, reply, o);
break;
case COMMAND_GET_MODULE_INFO:
err = fill_module_info(client, reply, o);
break;
if (fill_func)
err = fill_func(client, reply, o);
else
err = ERR_PROTOCOL;
case COMMAND_GET_CARD_INFO:
err = fill_card_info(client, reply, o);
break;
case COMMAND_GET_SAMPLE_INFO:
err = ERR_NOENTITY;
break;
case COMMAND_GET_SINK_INFO:
err = fill_sink_info(client, reply, o);
break;
case COMMAND_GET_SOURCE_INFO:
err = fill_source_info(client, reply, o);
break;
case COMMAND_GET_SINK_INPUT_INFO:
err = fill_sink_input_info(client, reply, o);
break;
case COMMAND_GET_SOURCE_OUTPUT_INFO:
err = fill_source_output_info(client, reply, o);
break;
default:
return -ENOTSUP;
}
if (err != 0)
goto error;
@ -2690,6 +2940,9 @@ static int do_get_info(struct client *client, uint32_t command, uint32_t tag, st
error_protocol:
err = ERR_PROTOCOL;
goto error;
error_noentity:
err = ERR_NOENTITY;
goto error;
error_invalid:
err = ERR_INVALID;
goto error;
@ -2719,6 +2972,7 @@ static int do_get_info_list(struct client *client, uint32_t command, uint32_t ta
pw_log_info(NAME" %p: %s", impl, commands[command].name);
spa_zero(info);
info.client = client;
info.reply = reply_new(client, tag);