mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-02 09:01:50 -05:00
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:
parent
f75acc31d3
commit
a372e4e90e
4 changed files with 552 additions and 224 deletions
|
|
@ -239,3 +239,37 @@ enum {
|
||||||
|
|
||||||
COMMAND_MAX
|
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
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -23,24 +23,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "manager.h"
|
#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_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_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_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_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 manager {
|
||||||
struct pw_core *core;
|
struct pw_manager this;
|
||||||
|
|
||||||
struct spa_hook core_listener;
|
struct spa_hook core_listener;
|
||||||
|
struct spa_hook registry_listener;
|
||||||
int sync_seq;
|
int sync_seq;
|
||||||
|
|
||||||
struct pw_registry *registry;
|
|
||||||
struct spa_hook registry_listener;
|
|
||||||
|
|
||||||
struct spa_hook_list hooks;
|
struct spa_hook_list hooks;
|
||||||
|
|
||||||
uint32_t n_objects;
|
|
||||||
struct spa_list object_list;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct object_info {
|
struct object_info {
|
||||||
|
|
@ -54,7 +52,6 @@ struct object {
|
||||||
struct pw_manager_object this;
|
struct pw_manager_object this;
|
||||||
|
|
||||||
struct manager *manager;
|
struct manager *manager;
|
||||||
struct spa_list link;
|
|
||||||
|
|
||||||
const struct object_info *info;
|
const struct object_info *info;
|
||||||
|
|
||||||
|
|
@ -66,13 +63,13 @@ struct object {
|
||||||
|
|
||||||
static void core_sync(struct manager *m)
|
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)
|
static struct object *find_object(struct manager *m, uint32_t id)
|
||||||
{
|
{
|
||||||
struct object *o;
|
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)
|
if (o->this.id == id)
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
@ -87,8 +84,8 @@ static void object_destroy(struct object *o)
|
||||||
free(o->this.type);
|
free(o->this.type);
|
||||||
if (o->this.props)
|
if (o->this.props)
|
||||||
pw_properties_free(o->this.props);
|
pw_properties_free(o->this.props);
|
||||||
spa_list_remove(&o->link);
|
spa_list_remove(&o->this.link);
|
||||||
m->n_objects--;
|
m->this.n_objects--;
|
||||||
free(o);
|
free(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,7 +109,7 @@ static void client_destroy(void *data)
|
||||||
pw_client_info_free(o->this.info);
|
pw_client_info_free(o->this.info);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct object_info client_info = {
|
static const struct object_info client_info = {
|
||||||
.type = PW_TYPE_INTERFACE_Client,
|
.type = PW_TYPE_INTERFACE_Client,
|
||||||
.version = PW_VERSION_CLIENT,
|
.version = PW_VERSION_CLIENT,
|
||||||
.events = &client_events,
|
.events = &client_events,
|
||||||
|
|
@ -139,7 +136,7 @@ static void module_destroy(void *data)
|
||||||
pw_module_info_free(o->this.info);
|
pw_module_info_free(o->this.info);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct object_info module_info = {
|
static const struct object_info module_info = {
|
||||||
.type = PW_TYPE_INTERFACE_Module,
|
.type = PW_TYPE_INTERFACE_Module,
|
||||||
.version = PW_VERSION_MODULE,
|
.version = PW_VERSION_MODULE,
|
||||||
.events = &module_events,
|
.events = &module_events,
|
||||||
|
|
@ -166,7 +163,7 @@ static void device_destroy(void *data)
|
||||||
pw_device_info_free(o->this.info);
|
pw_device_info_free(o->this.info);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct object_info device_info = {
|
static const struct object_info device_info = {
|
||||||
.type = PW_TYPE_INTERFACE_Device,
|
.type = PW_TYPE_INTERFACE_Device,
|
||||||
.version = PW_VERSION_DEVICE,
|
.version = PW_VERSION_DEVICE,
|
||||||
.events = &device_events,
|
.events = &device_events,
|
||||||
|
|
@ -176,9 +173,9 @@ struct object_info device_info = {
|
||||||
/* node */
|
/* node */
|
||||||
static void node_event_info(void *object, const struct pw_node_info *info)
|
static void node_event_info(void *object, const struct pw_node_info *info)
|
||||||
{
|
{
|
||||||
struct object *o = object;
|
struct object *o = object;
|
||||||
pw_log_debug("object %p: id:%d change-mask:%"PRIu64, o, o->this.id, info->change_mask);
|
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);
|
info = o->this.info = pw_node_info_update(o->this.info, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct pw_node_events node_events = {
|
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);
|
pw_node_info_free(o->this.info);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct object_info node_info = {
|
static const struct object_info node_info = {
|
||||||
.type = PW_TYPE_INTERFACE_Node,
|
.type = PW_TYPE_INTERFACE_Node,
|
||||||
.version = PW_VERSION_NODE,
|
.version = PW_VERSION_NODE,
|
||||||
.events = &node_events,
|
.events = &node_events,
|
||||||
.destroy = node_destroy,
|
.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[] =
|
static const struct object_info *objects[] =
|
||||||
{
|
{
|
||||||
&module_info,
|
&module_info,
|
||||||
&client_info,
|
&client_info,
|
||||||
&device_info,
|
&device_info,
|
||||||
&node_info,
|
&node_info,
|
||||||
|
&link_info,
|
||||||
|
&metadata_info,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct object_info *find_info(const char *type, uint32_t version)
|
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)
|
if (info == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
proxy = pw_registry_bind(m->registry,
|
proxy = pw_registry_bind(m->this.registry,
|
||||||
id, type, info->version, 0);
|
id, type, info->version, 0);
|
||||||
if (proxy == NULL)
|
if (proxy == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
@ -295,12 +324,13 @@ static void registry_event_global(void *data, uint32_t id,
|
||||||
o->manager = m;
|
o->manager = m;
|
||||||
o->info = info;
|
o->info = info;
|
||||||
o->new = true;
|
o->new = true;
|
||||||
spa_list_append(&m->object_list, &o->link);
|
spa_list_append(&m->this.object_list, &o->this.link);
|
||||||
m->n_objects++;
|
m->this.n_objects++;
|
||||||
|
|
||||||
pw_proxy_add_object_listener(proxy,
|
if (info->events)
|
||||||
&o->object_listener,
|
pw_proxy_add_object_listener(proxy,
|
||||||
o->info->events, o);
|
&o->object_listener,
|
||||||
|
o->info->events, o);
|
||||||
pw_proxy_add_listener(proxy,
|
pw_proxy_add_listener(proxy,
|
||||||
&o->proxy_listener,
|
&o->proxy_listener,
|
||||||
&proxy_events, o);
|
&proxy_events, o);
|
||||||
|
|
@ -331,9 +361,8 @@ static void on_core_done(void *data, uint32_t id, int seq)
|
||||||
{
|
{
|
||||||
struct manager *m = data;
|
struct manager *m = data;
|
||||||
if (id == PW_ID_CORE) {
|
if (id == PW_ID_CORE) {
|
||||||
if (m->sync_seq == seq) {
|
if (m->sync_seq == seq)
|
||||||
manager_emit_sync(m);
|
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 pw_manager *pw_manager_new(struct pw_core *core)
|
||||||
{
|
{
|
||||||
struct manager *this;
|
struct manager *m;
|
||||||
|
|
||||||
this = calloc(1, sizeof(*this));
|
m = calloc(1, sizeof(*m));
|
||||||
if (this == NULL)
|
if (m == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
this->core = core;
|
m->this.core = core;
|
||||||
spa_hook_list_init(&this->hooks);
|
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,
|
pw_core_add_listener(m->this.core,
|
||||||
&this->core_listener,
|
&m->core_listener,
|
||||||
&core_events, this);
|
&core_events, m);
|
||||||
this->registry = pw_core_get_registry(this->core,
|
m->this.registry = pw_core_get_registry(m->this.core,
|
||||||
PW_VERSION_REGISTRY, 0);
|
PW_VERSION_REGISTRY, 0);
|
||||||
pw_registry_add_listener(this->registry,
|
pw_registry_add_listener(m->this.registry,
|
||||||
&this->registry_listener,
|
&m->registry_listener,
|
||||||
®istry_events, this);
|
®istry_events, m);
|
||||||
|
|
||||||
return (struct pw_manager*)this;
|
core_sync(m);
|
||||||
|
|
||||||
|
return &m->this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pw_manager_add_listener(struct pw_manager *manager,
|
void pw_manager_add_listener(struct pw_manager *manager,
|
||||||
struct spa_hook *listener,
|
struct spa_hook *listener,
|
||||||
const struct pw_manager_events *events, void *data)
|
const struct pw_manager_events *events, void *data)
|
||||||
{
|
{
|
||||||
struct manager *this = (struct manager*)manager;
|
struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
|
||||||
spa_hook_list_append(&this->hooks, listener, events, data);
|
spa_hook_list_append(&m->hooks, listener, events, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct pw_manager_object *pw_manager_find_object(struct pw_manager *manager,
|
struct pw_manager_object *pw_manager_find_object(struct pw_manager *manager,
|
||||||
uint32_t id)
|
uint32_t id)
|
||||||
{
|
{
|
||||||
struct manager *this = (struct manager*)manager;
|
struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
|
||||||
struct object *o;
|
struct object *o;
|
||||||
|
|
||||||
o = find_object(this, id);
|
o = find_object(m, id);
|
||||||
if (o == NULL)
|
if (o == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
return (struct pw_manager_object*)o;
|
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),
|
int (*callback) (void *data, struct pw_manager_object *object),
|
||||||
void *data)
|
void *data)
|
||||||
{
|
{
|
||||||
struct manager *this = (struct manager*)manager;
|
struct manager *m = SPA_CONTAINER_OF(manager, struct manager, this);
|
||||||
struct object *o;
|
struct object *o;
|
||||||
int res;
|
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)
|
if ((res = callback(data, &o->this)) != 0)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
@ -404,17 +435,17 @@ int pw_manager_for_each_object(struct pw_manager *manager,
|
||||||
|
|
||||||
void pw_manager_destroy(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;
|
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);
|
object_destroy(o);
|
||||||
|
|
||||||
if (this->registry) {
|
if (m->this.registry) {
|
||||||
spa_hook_remove(&this->registry_listener);
|
spa_hook_remove(&m->registry_listener);
|
||||||
pw_proxy_destroy((struct pw_proxy*)this->registry);
|
pw_proxy_destroy((struct pw_proxy*)m->this.registry);
|
||||||
}
|
}
|
||||||
free(this);
|
free(m);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ extern "C" {
|
||||||
|
|
||||||
#include <pipewire/pipewire.h>
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
struct pw_manager;
|
|
||||||
struct pw_manager_object;
|
struct pw_manager_object;
|
||||||
|
|
||||||
struct pw_manager_events {
|
struct pw_manager_events {
|
||||||
|
|
@ -50,15 +49,27 @@ struct pw_manager_events {
|
||||||
void (*updated) (void *data, struct pw_manager_object *object);
|
void (*updated) (void *data, struct pw_manager_object *object);
|
||||||
|
|
||||||
void (*removed) (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 {
|
struct pw_manager_param {
|
||||||
uint32_t id;
|
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 spa_pod *param;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pw_manager_object {
|
struct pw_manager_object {
|
||||||
|
struct spa_list link; /**< link in manager object_list */
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint32_t permissions;
|
uint32_t permissions;
|
||||||
char *type;
|
char *type;
|
||||||
|
|
@ -78,8 +89,6 @@ void pw_manager_add_listener(struct pw_manager *manager,
|
||||||
|
|
||||||
void pw_manager_destroy(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 pw_manager_for_each_object(struct pw_manager *manager,
|
||||||
int (*callback) (void *data, struct pw_manager_object *object),
|
int (*callback) (void *data, struct pw_manager_object *object),
|
||||||
void *data);
|
void *data);
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,14 @@ struct client {
|
||||||
struct pw_core *core;
|
struct pw_core *core;
|
||||||
struct pw_manager *manager;
|
struct pw_manager *manager;
|
||||||
struct spa_hook manager_listener;
|
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 connect_tag;
|
||||||
|
|
||||||
uint32_t in_index;
|
uint32_t in_index;
|
||||||
|
|
@ -114,6 +122,7 @@ struct buffer_attr {
|
||||||
struct stream {
|
struct stream {
|
||||||
uint32_t create_tag;
|
uint32_t create_tag;
|
||||||
uint32_t channel; /* index in map */
|
uint32_t channel; /* index in map */
|
||||||
|
uint32_t id; /* id of global */
|
||||||
|
|
||||||
struct impl *impl;
|
struct impl *impl;
|
||||||
struct client *client;
|
struct client *client;
|
||||||
|
|
@ -354,6 +363,22 @@ static int send_underflow(struct stream *stream, int64_t offset)
|
||||||
return send_message(client, reply);
|
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)
|
static int send_overflow(struct stream *stream)
|
||||||
{
|
{
|
||||||
struct client *client = stream->client;
|
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 = {
|
static const struct pw_manager_events manager_events = {
|
||||||
PW_VERSION_MANAGER_EVENTS,
|
PW_VERSION_MANAGER_EVENTS,
|
||||||
.sync = manager_sync,
|
.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)
|
static int do_set_client_name(struct client *client, uint32_t command, uint32_t tag, struct message *m)
|
||||||
{
|
{
|
||||||
struct impl *impl = client->impl;
|
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;
|
return res;
|
||||||
|
|
||||||
pw_log_info(NAME" %p: SUBSCRIBE mask:%08x", impl, mask);
|
pw_log_info(NAME" %p: SUBSCRIBE mask:%08x", impl, mask);
|
||||||
|
client->subscribed = mask;
|
||||||
|
|
||||||
reply = reply_new(client, tag);
|
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);
|
reply = reply_new(client, stream->create_tag);
|
||||||
message_put(reply,
|
message_put(reply,
|
||||||
TAG_U32, stream->channel, /* stream index/channel */
|
TAG_U32, stream->channel, /* stream index/channel */
|
||||||
TAG_U32, pw_stream_get_node_id(stream->stream), /* sink_input/stream index */
|
TAG_U32, stream->id, /* sink_input/stream index */
|
||||||
TAG_U32, size, /* missing/requested bytes */
|
TAG_U32, size, /* missing/requested bytes */
|
||||||
TAG_INVALID);
|
TAG_INVALID);
|
||||||
|
|
||||||
stream->pending = size;
|
stream->pending = size;
|
||||||
|
|
@ -841,7 +925,7 @@ static int reply_create_record_stream(struct stream *stream)
|
||||||
reply = reply_new(client, stream->create_tag);
|
reply = reply_new(client, stream->create_tag);
|
||||||
message_put(reply,
|
message_put(reply,
|
||||||
TAG_U32, stream->channel, /* stream index/channel */
|
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);
|
TAG_INVALID);
|
||||||
|
|
||||||
if (client->version >= 9) {
|
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) {
|
if (stream->create_tag != SPA_ID_INVALID) {
|
||||||
|
stream->id = pw_stream_get_node_id(stream->stream);
|
||||||
|
|
||||||
if (stream->volume_set) {
|
if (stream->volume_set) {
|
||||||
pw_stream_set_control(stream->stream,
|
pw_stream_set_control(stream->stream,
|
||||||
SPA_PROP_channelVolumes, stream->volume.channels, stream->volume.values, 0);
|
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,
|
.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 bool is_module(struct pw_manager_object *o)
|
||||||
static struct device *find_device_by_name(struct impl *impl, const char *name)
|
|
||||||
{
|
{
|
||||||
struct device *dev;
|
return strcmp(o->type, PW_TYPE_INTERFACE_Module) == 0;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
const char *str;
|
||||||
if (impl->default_source.index == index)
|
return
|
||||||
dev = &impl->default_source;
|
strcmp(o->type, PW_TYPE_INTERFACE_Device) == 0 &&
|
||||||
else if (impl->default_sink.index == index)
|
o->props != NULL &&
|
||||||
dev = &impl->default_sink;
|
(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
|
||||||
else if (impl->default_monitor.index == index)
|
strcmp(str, "Audio/Device") == 0;
|
||||||
dev = &impl->default_monitor;
|
|
||||||
else
|
|
||||||
dev = NULL;
|
|
||||||
return dev;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
const char *str;
|
||||||
if (idx != SPA_ID_INVALID)
|
return
|
||||||
dev = find_device_by_index(impl, idx);
|
strcmp(o->type, PW_TYPE_INTERFACE_Node) == 0 &&
|
||||||
else
|
o->props != NULL &&
|
||||||
dev = find_device_by_name(impl, name);
|
(str = pw_properties_get(o->props, PW_KEY_MEDIA_CLASS)) != NULL &&
|
||||||
return dev;
|
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)
|
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);
|
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)
|
static int do_set_stream_volume(struct client *client, uint32_t command, uint32_t tag, struct message *m)
|
||||||
{
|
{
|
||||||
struct impl *impl = client->impl;
|
struct impl *impl = client->impl;
|
||||||
uint32_t channel;
|
uint32_t id;
|
||||||
struct stream *stream;
|
struct stream *stream;
|
||||||
int res;
|
|
||||||
struct volume volume;
|
struct volume volume;
|
||||||
|
int res;
|
||||||
|
|
||||||
if ((res = message_get(m,
|
if ((res = message_get(m,
|
||||||
TAG_U32, &channel,
|
TAG_U32, &id,
|
||||||
TAG_CVOLUME, &volume,
|
TAG_CVOLUME, &volume,
|
||||||
TAG_INVALID)) < 0)
|
TAG_INVALID)) < 0)
|
||||||
return res;
|
goto error_protocol;
|
||||||
|
|
||||||
pw_log_info(NAME" %p: DO_STREAM_VOLUME channel:%u",
|
pw_log_info(NAME" %p: DO_STREAM_VOLUME index:%u", impl, id);
|
||||||
impl, channel);
|
|
||||||
stream = pw_map_lookup(&client->streams, channel);
|
|
||||||
if (stream == NULL)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
stream->volume = volume;
|
stream = find_stream(client, id);
|
||||||
stream->volume_set = true;
|
if (stream != NULL) {
|
||||||
|
stream->volume = volume;
|
||||||
|
stream->volume_set = true;
|
||||||
|
|
||||||
pw_stream_set_control(stream->stream,
|
pw_stream_set_control(stream->stream,
|
||||||
SPA_PROP_channelVolumes, volume.channels, volume.values,
|
SPA_PROP_channelVolumes, volume.channels, volume.values,
|
||||||
0);
|
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);
|
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)
|
static int do_set_stream_mute(struct client *client, uint32_t command, uint32_t tag, struct message *m)
|
||||||
{
|
{
|
||||||
struct impl *impl = client->impl;
|
struct impl *impl = client->impl;
|
||||||
uint32_t channel;
|
uint32_t id;
|
||||||
struct stream *stream;
|
struct stream *stream;
|
||||||
int res;
|
int res;
|
||||||
bool mute;
|
bool mute;
|
||||||
float val;
|
|
||||||
|
|
||||||
if ((res = message_get(m,
|
if ((res = message_get(m,
|
||||||
TAG_U32, &channel,
|
TAG_U32, &id,
|
||||||
TAG_BOOLEAN, &mute,
|
TAG_BOOLEAN, &mute,
|
||||||
TAG_INVALID)) < 0)
|
TAG_INVALID)) < 0)
|
||||||
return res;
|
goto error_protocol;
|
||||||
|
|
||||||
pw_log_info(NAME" %p: DO_SET_STREAM_MUTE channel:%u mute:%u",
|
pw_log_info(NAME" %p: DO_SET_STREAM_MUTE id:%u mute:%u",
|
||||||
impl, channel, mute);
|
impl, id, mute);
|
||||||
|
|
||||||
stream = pw_map_lookup(&client->streams, channel);
|
stream = find_stream(client, id);
|
||||||
if (stream == NULL)
|
if (stream != NULL) {
|
||||||
return -EINVAL;
|
float val;
|
||||||
|
|
||||||
stream->muted = mute;
|
stream->muted = mute;
|
||||||
stream->muted_set = true;
|
stream->muted_set = true;
|
||||||
|
|
||||||
val = mute ? 1.0f : 0.0f;
|
val = mute ? 1.0f : 0.0f;
|
||||||
pw_stream_set_control(stream->stream,
|
|
||||||
SPA_PROP_mute, 1, &val,
|
|
||||||
0);
|
|
||||||
|
|
||||||
|
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);
|
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)
|
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;
|
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)
|
static int do_get_server_info(struct client *client, uint32_t command, uint32_t tag, struct message *m)
|
||||||
{
|
{
|
||||||
struct impl *impl = client->impl;
|
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);
|
spa_zero(ss);
|
||||||
ss.format = SAMPLE_FLOAT32LE;
|
ss.format = SAMPLE_FLOAT32LE;
|
||||||
ss.rate = 44100;
|
ss.rate = client->default_rate ? client->default_rate : 44100;
|
||||||
ss.channels = 2;
|
ss.channels = 2;
|
||||||
|
|
||||||
spa_zero(map);
|
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_user_name(),
|
||||||
TAG_STRING, pw_get_host_name(),
|
TAG_STRING, pw_get_host_name(),
|
||||||
TAG_SAMPLE_SPEC, &ss,
|
TAG_SAMPLE_SPEC, &ss,
|
||||||
TAG_STRING, NULL, /* default sink name */
|
TAG_STRING, get_default(client, true), /* default sink name */
|
||||||
TAG_STRING, NULL /* default source name */,
|
TAG_STRING, get_default(client, false), /* default source name */
|
||||||
TAG_U32, 0,
|
TAG_U32, client->cookie, /* cookie */
|
||||||
TAG_INVALID);
|
TAG_INVALID);
|
||||||
|
|
||||||
if (client->version >= 15) {
|
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)
|
static int do_lookup(struct client *client, uint32_t command, uint32_t tag, struct message *m)
|
||||||
{
|
{
|
||||||
struct impl *impl = client->impl;
|
struct impl *impl = client->impl;
|
||||||
const char *name = NULL;
|
|
||||||
struct message *reply;
|
struct message *reply;
|
||||||
struct pw_manager_object *o;
|
struct pw_manager_object *o;
|
||||||
|
struct selector sel;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
|
spa_zero(sel);
|
||||||
|
sel.key = PW_KEY_NODE_NAME;
|
||||||
|
|
||||||
if ((res = message_get(m,
|
if ((res = message_get(m,
|
||||||
TAG_STRING, &name,
|
TAG_STRING, &sel.value,
|
||||||
TAG_INVALID)) < 0)
|
TAG_INVALID)) < 0)
|
||||||
return res;
|
return res;
|
||||||
if (name == NULL)
|
if (sel.value == NULL)
|
||||||
goto error_invalid;
|
goto error_invalid;
|
||||||
|
|
||||||
pw_log_info(NAME" %p: LOOKUP %s", impl, name);
|
pw_log_info(NAME" %p: LOOKUP %s", impl, sel.value);
|
||||||
if ((o = find_node_by_name(impl, name)) == NULL)
|
|
||||||
|
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;
|
goto error_noentity;
|
||||||
|
|
||||||
reply = reply_new(client, tag);
|
reply = reply_new(client, tag);
|
||||||
message_put(reply,
|
message_put(reply,
|
||||||
TAG_U32, SPA_ID_INVALID,
|
TAG_U32, o->id,
|
||||||
TAG_INVALID);
|
TAG_INVALID);
|
||||||
|
|
||||||
return send_message(client, reply);
|
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;
|
struct pw_client_info *info = o->info;
|
||||||
|
|
||||||
if (o == NULL ||
|
if (o == NULL || !is_client(o))
|
||||||
strcmp(o->type, PW_TYPE_INTERFACE_Client) != 0)
|
|
||||||
return ERR_NOENTITY;
|
return ERR_NOENTITY;
|
||||||
|
|
||||||
message_put(m,
|
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;
|
struct pw_module_info *info = o->info;
|
||||||
|
|
||||||
if (o == NULL ||
|
if (o == NULL || info == NULL || !is_module(o))
|
||||||
strcmp(o->type, PW_TYPE_INTERFACE_Module) != 0)
|
|
||||||
return ERR_NOENTITY;
|
return ERR_NOENTITY;
|
||||||
|
|
||||||
message_put(m,
|
message_put(m,
|
||||||
TAG_U32, o->id, /* module index */
|
TAG_U32, o->id, /* module index */
|
||||||
TAG_STRING, info ? info->name : NULL,
|
TAG_STRING, info->name,
|
||||||
TAG_STRING, info ? info->args : NULL,
|
TAG_STRING, info->args,
|
||||||
TAG_U32, -1, /* n_used */
|
TAG_U32, -1, /* n_used */
|
||||||
TAG_INVALID);
|
TAG_INVALID);
|
||||||
|
|
||||||
|
|
@ -2225,7 +2493,7 @@ static int fill_module_info(struct client *client, struct message *m,
|
||||||
}
|
}
|
||||||
if (client->version >= 15) {
|
if (client->version >= 15) {
|
||||||
message_put(m,
|
message_put(m,
|
||||||
TAG_PROPLIST, info ? info->props : NULL,
|
TAG_PROPLIST, info->props,
|
||||||
TAG_INVALID);
|
TAG_INVALID);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -2238,11 +2506,7 @@ static int fill_card_info(struct client *client, struct message *m,
|
||||||
const char *str;
|
const char *str;
|
||||||
uint32_t module_id = SPA_ID_INVALID;
|
uint32_t module_id = SPA_ID_INVALID;
|
||||||
|
|
||||||
if (o == NULL ||
|
if (o == NULL || info == NULL || info->props == NULL || !is_card(o))
|
||||||
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)
|
|
||||||
return ERR_NOENTITY;
|
return ERR_NOENTITY;
|
||||||
|
|
||||||
if ((str = spa_dict_lookup(info->props, PW_KEY_MODULE_ID)) != NULL)
|
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_manager_object *o)
|
||||||
{
|
{
|
||||||
struct pw_node_info *info = o->info;
|
struct pw_node_info *info = o->info;
|
||||||
const char *str;
|
|
||||||
struct sample_spec ss;
|
struct sample_spec ss;
|
||||||
struct volume volume;
|
struct volume volume;
|
||||||
struct channel_map map;
|
struct channel_map map;
|
||||||
|
|
||||||
if (o == NULL ||
|
if (o == NULL || info == NULL || info->props == NULL || !is_sink(o))
|
||||||
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)
|
|
||||||
return ERR_NOENTITY;
|
return ERR_NOENTITY;
|
||||||
|
|
||||||
ss = (struct sample_spec) {
|
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_manager_object *o)
|
||||||
{
|
{
|
||||||
struct pw_node_info *info = o->info;
|
struct pw_node_info *info = o->info;
|
||||||
const char *str;
|
|
||||||
struct sample_spec ss;
|
struct sample_spec ss;
|
||||||
struct volume volume;
|
struct volume volume;
|
||||||
struct channel_map map;
|
struct channel_map map;
|
||||||
bool is_monitor;
|
bool is_monitor;
|
||||||
|
|
||||||
if (o == NULL ||
|
is_monitor = is_sink(o);
|
||||||
strcmp(o->type, PW_TYPE_INTERFACE_Node) != 0 ||
|
if (o == NULL || info == NULL || info->props == NULL ||
|
||||||
o->props == NULL || info == NULL || info->props == NULL ||
|
(!is_source(o) && !is_monitor))
|
||||||
(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)
|
|
||||||
return ERR_NOENTITY;
|
return ERR_NOENTITY;
|
||||||
|
|
||||||
ss = (struct sample_spec) {
|
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_manager_object *o)
|
||||||
{
|
{
|
||||||
struct pw_node_info *info = o->info;
|
struct pw_node_info *info = o->info;
|
||||||
const char *str;
|
|
||||||
struct sample_spec ss;
|
struct sample_spec ss;
|
||||||
struct volume volume;
|
struct volume volume;
|
||||||
struct channel_map map;
|
struct channel_map map;
|
||||||
|
|
||||||
if (o == NULL ||
|
if (o == NULL || info == NULL || info->props == NULL || !is_sink_input(o))
|
||||||
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)
|
|
||||||
return ERR_NOENTITY;
|
return ERR_NOENTITY;
|
||||||
|
|
||||||
ss = (struct sample_spec) {
|
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_manager_object *o)
|
||||||
{
|
{
|
||||||
struct pw_node_info *info = o->info;
|
struct pw_node_info *info = o->info;
|
||||||
const char *str;
|
|
||||||
struct sample_spec ss;
|
struct sample_spec ss;
|
||||||
struct volume volume;
|
struct volume volume;
|
||||||
struct channel_map map;
|
struct channel_map map;
|
||||||
|
|
||||||
if (o == NULL ||
|
if (o == NULL || info == NULL || info->props == NULL || !is_source_output(o))
|
||||||
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)
|
|
||||||
return ERR_NOENTITY;
|
return ERR_NOENTITY;
|
||||||
|
|
||||||
ss = (struct sample_spec) {
|
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 impl *impl = client->impl;
|
||||||
struct message *reply = NULL;
|
struct message *reply = NULL;
|
||||||
uint32_t idx;
|
|
||||||
const char *name = NULL;
|
|
||||||
int res, err;
|
int res, err;
|
||||||
struct pw_manager_object *o;
|
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,
|
if ((res = message_get(m,
|
||||||
TAG_U32, &idx,
|
TAG_U32, &sel.id,
|
||||||
TAG_INVALID)) < 0)
|
TAG_INVALID)) < 0)
|
||||||
goto error_protocol;
|
goto error_protocol;
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case COMMAND_GET_SINK_INFO:
|
case COMMAND_GET_CLIENT_INFO:
|
||||||
case COMMAND_GET_SOURCE_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:
|
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:
|
case COMMAND_GET_SAMPLE_INFO:
|
||||||
if ((res = message_get(m,
|
sel.key = "";
|
||||||
TAG_STRING, &name,
|
break;
|
||||||
TAG_INVALID)) < 0)
|
case COMMAND_GET_SINK_INFO:
|
||||||
goto error_protocol;
|
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;
|
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) ||
|
if ((sel.id == SPA_ID_INVALID && sel.value == NULL) ||
|
||||||
(idx != SPA_ID_INVALID && name != NULL))
|
(sel.id != SPA_ID_INVALID && sel.value != NULL))
|
||||||
goto error_invalid;
|
goto error_invalid;
|
||||||
|
|
||||||
pw_log_info(NAME" %p: %s idx:%u name:%s", impl,
|
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);
|
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:
|
if (fill_func)
|
||||||
err = fill_module_info(client, reply, o);
|
err = fill_func(client, reply, o);
|
||||||
break;
|
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)
|
if (err != 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
|
@ -2690,6 +2940,9 @@ static int do_get_info(struct client *client, uint32_t command, uint32_t tag, st
|
||||||
error_protocol:
|
error_protocol:
|
||||||
err = ERR_PROTOCOL;
|
err = ERR_PROTOCOL;
|
||||||
goto error;
|
goto error;
|
||||||
|
error_noentity:
|
||||||
|
err = ERR_NOENTITY;
|
||||||
|
goto error;
|
||||||
error_invalid:
|
error_invalid:
|
||||||
err = ERR_INVALID;
|
err = ERR_INVALID;
|
||||||
goto error;
|
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);
|
pw_log_info(NAME" %p: %s", impl, commands[command].name);
|
||||||
|
|
||||||
|
spa_zero(info);
|
||||||
info.client = client;
|
info.client = client;
|
||||||
info.reply = reply_new(client, tag);
|
info.reply = reply_new(client, tag);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue