mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-06 01:18:22 +02:00
lib: introduce WpObjectManager
* rework how global objects are stored in the core * rework how users get notified about global objects and proxies of remote global objects The purpose of this change is to have a class that can manage objects that are registered in the core or signalled through the registry. This object can declare interest on certain types of global objects and only keep & signal those objects that it is interested in. Additionally, it can prepare proxy features and asynchronously deliver an 'objects-changed' signal, which is basically telling us that the list of objects has changed. This is useful to simplify port proxies management in WpAudioStream. Now the stream object can declare that it is interested in ports that have "node.id" == X and the object manager will only maintain a list of those. Additionally, it will emit the 'objects-changed' signal when the list of ports is complete, so there is no reason to do complex operations and core syncs in the WpAudioStream class in order to figure out when the list of ports is ready. As a side effect, this also reduces resource management. Now we don't construct a WpProxy for every global that pipewire reports; we only construct proxies when there is interest in them! Another interesting side effect is that we can now register an object manager at any point in time and get immediately notified about remote globals that already exist. i.e. when you register an object manager that is interested in nodes, it will be immediately notified about all the existing nodes in the graph. This is useful to avoid race conditions between connecting the signal and objects beting created in pipewire
This commit is contained in:
parent
a93cbdf8f0
commit
e7e5c66853
19 changed files with 520 additions and 488 deletions
353
lib/wp/core.c
353
lib/wp/core.c
|
|
@ -7,12 +7,14 @@
|
|||
*/
|
||||
|
||||
#include "core.h"
|
||||
#include "object-manager.h"
|
||||
#include "proxy.h"
|
||||
#include "wpenums.h"
|
||||
#include "private.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <spa/utils/result.h>
|
||||
#include <spa/debug/types.h>
|
||||
|
||||
/*
|
||||
* Integration between the PipeWire main loop and GMainLoop
|
||||
|
|
@ -94,19 +96,9 @@ struct _WpCore
|
|||
struct pw_registry_proxy *registry_proxy;
|
||||
struct spa_hook registry_listener;
|
||||
|
||||
/* local proxies */
|
||||
GHashTable *proxies;
|
||||
GHashTable *default_features;
|
||||
|
||||
/* local global objects */
|
||||
GPtrArray *global_objects;
|
||||
};
|
||||
|
||||
struct global_object
|
||||
{
|
||||
GQuark key;
|
||||
gpointer object;
|
||||
GDestroyNotify destroy;
|
||||
GPtrArray *globals; // elementy-type: WpGlobal*
|
||||
GPtrArray *objects; // element-type: GObject*
|
||||
GPtrArray *object_managers; // element-type: WpObjectManager*
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
@ -120,10 +112,6 @@ enum {
|
|||
|
||||
enum {
|
||||
SIGNAL_REMOTE_STATE_CHANGED,
|
||||
SIGNAL_GLOBAL_ADDED,
|
||||
SIGNAL_GLOBAL_REMOVED,
|
||||
SIGNAL_REMOTE_GLOBAL_ADDED,
|
||||
SIGNAL_REMOTE_GLOBAL_REMOVED,
|
||||
NUM_SIGNALS
|
||||
};
|
||||
|
||||
|
|
@ -131,61 +119,59 @@ static guint32 signals[NUM_SIGNALS];
|
|||
|
||||
G_DEFINE_TYPE (WpCore, wp_core, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
on_proxy_ready (GObject * obj, GAsyncResult * res, gpointer data)
|
||||
{
|
||||
WpCore *self = WP_CORE (data);
|
||||
WpProxy *proxy = WP_PROXY (obj);
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
if (!wp_proxy_augment_finish (proxy, res, &error)) {
|
||||
g_warning ("Failed to augment WpProxy (%p): %s", obj, error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
g_signal_emit (self, signals[SIGNAL_REMOTE_GLOBAL_ADDED],
|
||||
wp_proxy_get_interface_quark (proxy), proxy);
|
||||
}
|
||||
|
||||
static void
|
||||
registry_global (void *data, uint32_t id, uint32_t permissions,
|
||||
uint32_t type, uint32_t version, const struct spa_dict *props)
|
||||
{
|
||||
WpCore *self = WP_CORE (data);
|
||||
WpProxy *proxy;
|
||||
WpProxyFeatures features;
|
||||
g_autoptr (WpProperties) properties = wp_properties_new_copy_dict (props);
|
||||
WpGlobal *global = NULL;
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (!g_hash_table_contains (self->proxies, GUINT_TO_POINTER (id)));
|
||||
g_return_if_fail (self->globals->len <= id ||
|
||||
g_ptr_array_index (self->globals, id) == NULL);
|
||||
|
||||
/* construct & store WpProxy */
|
||||
proxy = wp_proxy_new_global (self, id, permissions, properties,
|
||||
type, version);
|
||||
g_hash_table_insert (self->proxies, GUINT_TO_POINTER (id), proxy);
|
||||
g_debug ("registry global:%u perm:0x%x type:%u/%u (%s)",
|
||||
id, permissions, type, version,
|
||||
spa_debug_type_find_name (pw_type_info (), type));
|
||||
|
||||
g_debug ("registry global:%u perm:0x%x type:%u/%u -> %s:%p",
|
||||
id, permissions, type, version, G_OBJECT_TYPE_NAME (proxy), proxy);
|
||||
/* construct & store the global */
|
||||
global = wp_global_new ();
|
||||
global->id = id;
|
||||
global->type = type;
|
||||
global->version = version;
|
||||
global->permissions = permissions;
|
||||
global->properties = wp_properties_new_copy_dict (props);
|
||||
|
||||
/* augment */
|
||||
features = GPOINTER_TO_UINT (g_hash_table_lookup (self->default_features,
|
||||
GUINT_TO_POINTER (G_TYPE_FROM_INSTANCE (proxy))));
|
||||
wp_proxy_augment (proxy, features, NULL, on_proxy_ready, self);
|
||||
if (self->globals->len <= id)
|
||||
g_ptr_array_set_size (self->globals, id + 1);
|
||||
|
||||
g_ptr_array_index (self->globals, id) = global;
|
||||
|
||||
/* notify object managers */
|
||||
for (i = 0; i < self->object_managers->len; i++) {
|
||||
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
|
||||
wp_object_manager_add_global (om, global);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
registry_global_remove (void *data, uint32_t id)
|
||||
{
|
||||
WpCore *self = WP_CORE (data);
|
||||
g_autoptr (WpProxy) proxy = NULL;
|
||||
g_autoptr (WpGlobal) global = NULL;
|
||||
guint i;
|
||||
|
||||
g_hash_table_steal_extended (self->proxies, GUINT_TO_POINTER (id), NULL,
|
||||
(gpointer *) &proxy);
|
||||
global = g_steal_pointer (&g_ptr_array_index (self->globals, id));
|
||||
|
||||
g_debug ("registry global removed: %u (%p)", id, proxy);
|
||||
g_debug ("registry global removed:%u type:%u/%u (%s)", id,
|
||||
global->type, global->version,
|
||||
spa_debug_type_find_name (pw_type_info (), global->type));
|
||||
|
||||
if (proxy)
|
||||
g_signal_emit (data, signals[SIGNAL_REMOTE_GLOBAL_REMOVED],
|
||||
wp_proxy_get_interface_quark (proxy), proxy);
|
||||
/* notify object managers */
|
||||
for (i = 0; i < self->object_managers->len; i++) {
|
||||
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
|
||||
wp_object_manager_rm_global (om, id);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct pw_registry_proxy_events registry_proxy_events = {
|
||||
|
|
@ -232,25 +218,21 @@ static const struct pw_remote_events remote_events = {
|
|||
.state_changed = on_remote_state_changed,
|
||||
};
|
||||
|
||||
static void
|
||||
free_global_object (gpointer p)
|
||||
/* wrapper around wp_global_unref because
|
||||
the WpGlobal pointers in self->globals can be NULL */
|
||||
static inline void
|
||||
free_global (WpGlobal * g)
|
||||
{
|
||||
struct global_object *g = p;
|
||||
|
||||
/* Destroy the object */
|
||||
if (g->destroy)
|
||||
g->destroy(g->object);
|
||||
|
||||
g_slice_free (struct global_object, p);
|
||||
if (g)
|
||||
wp_global_unref (g);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_core_init (WpCore * self)
|
||||
{
|
||||
self->proxies = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
|
||||
g_object_unref);
|
||||
self->default_features = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||
self->global_objects = g_ptr_array_new_with_free_func (free_global_object);
|
||||
self->globals = g_ptr_array_new_with_free_func ((GDestroyNotify) free_global);
|
||||
self->objects = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
self->object_managers = g_ptr_array_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -274,22 +256,62 @@ wp_core_constructed (GObject *object)
|
|||
G_OBJECT_CLASS (wp_core_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void object_manager_destroyed (gpointer data, GObject * om);
|
||||
|
||||
static void
|
||||
wp_core_dispose (GObject * obj)
|
||||
{
|
||||
WpCore *self = WP_CORE (obj);
|
||||
g_autoptr (GPtrArray) global_objects;
|
||||
struct global_object *global;
|
||||
|
||||
global_objects = g_steal_pointer (&self->global_objects);
|
||||
/* remove pipewire globals */
|
||||
{
|
||||
g_autoptr (GPtrArray) objlist = g_steal_pointer (&self->globals);
|
||||
|
||||
/* Remove and emit the removed signal for all globals */
|
||||
while (global_objects->len > 0) {
|
||||
global = g_ptr_array_steal_index_fast (global_objects,
|
||||
global_objects->len - 1);
|
||||
g_signal_emit (self, signals[SIGNAL_GLOBAL_REMOVED], global->key,
|
||||
global->key, global->object);
|
||||
free_global_object (global);
|
||||
while (objlist->len > 0) {
|
||||
guint i;
|
||||
g_autoptr (WpGlobal) global = g_ptr_array_steal_index_fast (objlist,
|
||||
objlist->len - 1);
|
||||
|
||||
if (!global)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < self->object_managers->len; i++) {
|
||||
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
|
||||
wp_object_manager_rm_global (om, global->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* remove all the registered objects
|
||||
this will normally also destroy the object managers, eventually, since
|
||||
they are normally ref'ed by modules, which are registered objects */
|
||||
{
|
||||
g_autoptr (GPtrArray) objlist = g_steal_pointer (&self->objects);
|
||||
|
||||
while (objlist->len > 0) {
|
||||
guint i;
|
||||
g_autoptr (GObject) object = g_ptr_array_steal_index_fast (objlist,
|
||||
objlist->len - 1);
|
||||
|
||||
for (i = 0; i < self->object_managers->len; i++) {
|
||||
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
|
||||
wp_object_manager_rm_object (om, object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* in case there are any object managers left,
|
||||
remove the weak ref on them and let them be... */
|
||||
{
|
||||
g_autoptr (GPtrArray) object_mgrs;
|
||||
GObject *om;
|
||||
|
||||
object_mgrs = g_steal_pointer (&self->object_managers);
|
||||
|
||||
while (object_mgrs->len > 0) {
|
||||
om = g_ptr_array_steal_index_fast (object_mgrs, object_mgrs->len - 1);
|
||||
g_object_weak_unref (om, object_manager_destroyed, self);
|
||||
}
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (wp_core_parent_class)->dispose (obj);
|
||||
|
|
@ -300,8 +322,6 @@ wp_core_finalize (GObject * obj)
|
|||
{
|
||||
WpCore *self = WP_CORE (obj);
|
||||
|
||||
g_clear_pointer (&self->proxies, g_hash_table_unref);
|
||||
g_clear_pointer (&self->default_features, g_hash_table_unref);
|
||||
g_clear_pointer (&self->pw_remote, pw_remote_destroy);
|
||||
self->core_proxy= NULL;
|
||||
self->registry_proxy = NULL;
|
||||
|
|
@ -402,22 +422,6 @@ wp_core_class_init (WpCoreClass * klass)
|
|||
signals[SIGNAL_REMOTE_STATE_CHANGED] = g_signal_new ("remote-state-changed",
|
||||
G_TYPE_FROM_CLASS (klass), G_SIGNAL_DETAILED | G_SIGNAL_RUN_LAST,
|
||||
0, NULL, NULL, NULL, G_TYPE_NONE, 1, WP_TYPE_REMOTE_STATE);
|
||||
|
||||
signals[SIGNAL_GLOBAL_ADDED] = g_signal_new ("global-added",
|
||||
G_TYPE_FROM_CLASS (klass), G_SIGNAL_DETAILED | G_SIGNAL_RUN_LAST, 0, NULL,
|
||||
NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);
|
||||
|
||||
signals[SIGNAL_GLOBAL_REMOVED] = g_signal_new ("global-removed",
|
||||
G_TYPE_FROM_CLASS (klass), G_SIGNAL_DETAILED | G_SIGNAL_RUN_LAST, 0, NULL,
|
||||
NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_POINTER);
|
||||
|
||||
signals[SIGNAL_REMOTE_GLOBAL_ADDED] = g_signal_new ("remote-global-added",
|
||||
G_TYPE_FROM_CLASS (klass), G_SIGNAL_DETAILED | G_SIGNAL_RUN_LAST,
|
||||
0, NULL, NULL, NULL, G_TYPE_NONE, 1, WP_TYPE_PROXY);
|
||||
|
||||
signals[SIGNAL_REMOTE_GLOBAL_REMOVED] = g_signal_new ("remote-global-removed",
|
||||
G_TYPE_FROM_CLASS (klass), G_SIGNAL_DETAILED | G_SIGNAL_RUN_LAST,
|
||||
0, NULL, NULL, NULL, G_TYPE_NONE, 1, WP_TYPE_PROXY);
|
||||
}
|
||||
|
||||
WpCore *
|
||||
|
|
@ -485,16 +489,6 @@ wp_core_get_remote_state (WpCore * self, const gchar ** error)
|
|||
return (WpRemoteState) pw_remote_get_state (self->pw_remote, error);
|
||||
}
|
||||
|
||||
void
|
||||
wp_core_set_default_proxy_features (WpCore * self,
|
||||
GType proxy_type, WpProxyFeatures features)
|
||||
{
|
||||
g_return_if_fail (WP_IS_CORE (self));
|
||||
|
||||
g_hash_table_insert (self->default_features, GUINT_TO_POINTER (proxy_type),
|
||||
GUINT_TO_POINTER (features));
|
||||
}
|
||||
|
||||
WpProxy *
|
||||
wp_core_create_remote_object (WpCore *self,
|
||||
const gchar *factory_name, guint32 interface_type,
|
||||
|
|
@ -527,130 +521,137 @@ wp_core_get_pw_registry_proxy (WpCore * self)
|
|||
}
|
||||
|
||||
/**
|
||||
* wp_core_get_global: (method)
|
||||
* wp_core_find_object: (skip)
|
||||
* @self: the core
|
||||
* @key: the key of the global
|
||||
* @func: (scope call): a function that takes the object being searched
|
||||
* as the first argument and @data as the second. it should return TRUE if
|
||||
* the object is found or FALSE otherwise
|
||||
* @data: the second argument to @func
|
||||
*
|
||||
* Returns: (type GObject*) (nullable) (transfer none): the global object
|
||||
* associated with @key; if multiple globals with the same key exist, the
|
||||
* first one found is returned
|
||||
* Finds a registered object
|
||||
*
|
||||
* Returns: (transfer full) (type GObject *) (nullable): the registered object
|
||||
* or NULL if not found
|
||||
*/
|
||||
gpointer
|
||||
wp_core_get_global (WpCore * self, GQuark key)
|
||||
wp_core_find_object (WpCore * self, GEqualFunc func, gconstpointer data)
|
||||
{
|
||||
gint i;
|
||||
struct global_object *global;
|
||||
GObject *object;
|
||||
guint i;
|
||||
|
||||
g_return_val_if_fail (WP_IS_CORE (self), NULL);
|
||||
|
||||
if (G_UNLIKELY (!self->global_objects))
|
||||
/* prevent bad things when called from within _dispose() */
|
||||
if (G_UNLIKELY (!self->objects))
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < self->global_objects->len; i++) {
|
||||
global = g_ptr_array_index (self->global_objects, i);
|
||||
if (global->key == key)
|
||||
return global->object;
|
||||
for (i = 0; i < self->objects->len; i++) {
|
||||
object = g_ptr_array_index (self->objects, i);
|
||||
if (func (object, data))
|
||||
return g_object_ref (object);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_core_foreach_global: (method)
|
||||
* wp_core_register_object: (skip)
|
||||
* @self: the core
|
||||
* @callback: (scope call): the function to call for each global object
|
||||
* @user_data: data to passs to @callback
|
||||
* @obj: (transfer full) (type GObject*): the object to register
|
||||
*
|
||||
* Calls @callback for every global object registered
|
||||
* Registers @obj with the core, making it appear on #WpObjectManager
|
||||
* instances as well. The core will also maintain a ref to that object
|
||||
* until it is removed.
|
||||
*/
|
||||
void
|
||||
wp_core_foreach_global (WpCore * self, WpCoreForeachGlobalFunc callback,
|
||||
gpointer user_data)
|
||||
wp_core_register_object (WpCore * self, gpointer obj)
|
||||
{
|
||||
gint i;
|
||||
struct global_object *global;
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (WP_IS_CORE (self));
|
||||
g_return_if_fail (G_IS_OBJECT (obj));
|
||||
|
||||
if (G_UNLIKELY (!self->global_objects))
|
||||
/* prevent bad things when called from within _dispose() */
|
||||
if (G_UNLIKELY (!self->objects)) {
|
||||
g_object_unref (obj);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < self->global_objects->len; i++) {
|
||||
global = g_ptr_array_index (self->global_objects, i);
|
||||
if (!callback (global->key, global->object, user_data))
|
||||
break;
|
||||
g_ptr_array_add (self->objects, obj);
|
||||
|
||||
/* notify object managers */
|
||||
for (i = 0; i < self->object_managers->len; i++) {
|
||||
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
|
||||
wp_object_manager_add_object (om, obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_core_register_global: (method)
|
||||
* wp_core_remove_object: (skip)
|
||||
* @self: the core
|
||||
* @key: the key for this global
|
||||
* @obj: (transfer full): the global object to attach
|
||||
* @destroy_obj: the destroy function for @obj
|
||||
* @obj: (transfer none) (type GObject*): a pointer to the object to remove
|
||||
*
|
||||
* Registers @obj as a global object associated with @key
|
||||
* Detaches and unrefs the specified object from this core
|
||||
*/
|
||||
void
|
||||
wp_core_register_global (WpCore * self, GQuark key, gpointer obj,
|
||||
GDestroyNotify destroy_obj)
|
||||
wp_core_remove_object (WpCore * self, gpointer obj)
|
||||
{
|
||||
struct global_object *global;
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (WP_IS_CORE(self));
|
||||
g_return_if_fail (WP_IS_CORE (self));
|
||||
g_return_if_fail (G_IS_OBJECT (obj));
|
||||
|
||||
if (G_UNLIKELY (!self->global_objects)) {
|
||||
if (destroy_obj)
|
||||
destroy_obj (obj);
|
||||
/* prevent bad things when called from within _dispose() */
|
||||
if (G_UNLIKELY (!self->objects))
|
||||
return;
|
||||
|
||||
/* notify object managers */
|
||||
for (i = 0; i < self->object_managers->len; i++) {
|
||||
WpObjectManager *om = g_ptr_array_index (self->object_managers, i);
|
||||
wp_object_manager_rm_object (om, obj);
|
||||
}
|
||||
|
||||
global = g_slice_new0 (struct global_object);
|
||||
global->key = key;
|
||||
global->object = obj;
|
||||
global->destroy = destroy_obj;
|
||||
g_ptr_array_add (self->global_objects, global);
|
||||
g_ptr_array_remove_fast (self->objects, obj);
|
||||
}
|
||||
|
||||
g_signal_emit (self, signals[SIGNAL_GLOBAL_ADDED], key, key, obj);
|
||||
static void
|
||||
object_manager_destroyed (gpointer data, GObject * om)
|
||||
{
|
||||
WpCore *self = WP_CORE (data);
|
||||
g_ptr_array_remove_fast (self->object_managers, om);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_core_remove_global: (method)
|
||||
* wp_core_install_object_manager: (method)
|
||||
* @self: the core
|
||||
* @key: the key for this global
|
||||
* @obj: (nullable): a pointer to the global object to match, if there are
|
||||
* multiple ones with the same key
|
||||
* @om: (transfer none): a #WpObjectManager
|
||||
*
|
||||
* Detaches and unrefs the specified global from this core
|
||||
* Installs the object manager on this core, activating its internal management
|
||||
* engine. This will immediately emit signals about objects added on @om
|
||||
* if objects that the @om is interested in were in existence already.
|
||||
*/
|
||||
void
|
||||
wp_core_remove_global (WpCore * self, GQuark key, gpointer obj)
|
||||
wp_core_install_object_manager (WpCore * self, WpObjectManager * om)
|
||||
{
|
||||
gint i;
|
||||
struct global_object *global;
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (WP_IS_CORE (self));
|
||||
g_return_if_fail (WP_IS_OBJECT_MANAGER (om));
|
||||
|
||||
if (G_UNLIKELY (!self->global_objects))
|
||||
return;
|
||||
g_object_weak_ref (G_OBJECT (om), object_manager_destroyed, self);
|
||||
g_ptr_array_add (self->object_managers, om);
|
||||
g_object_set (om, "core", self, NULL);
|
||||
|
||||
for (i = 0; i < self->global_objects->len; i++) {
|
||||
global = g_ptr_array_index (self->global_objects, i);
|
||||
if (global->key == key && (!obj || global->object == obj))
|
||||
break;
|
||||
/* add pre-existing objects to the object manager,
|
||||
in case it's interested in them */
|
||||
for (i = 0; i < self->globals->len; i++) {
|
||||
WpGlobal *g = g_ptr_array_index (self->globals, i);
|
||||
/* check if null because the globals array can have gaps */
|
||||
if (g)
|
||||
wp_object_manager_add_global (om, g);
|
||||
}
|
||||
|
||||
if (i < self->global_objects->len) {
|
||||
global = g_ptr_array_steal_index_fast (self->global_objects, i);
|
||||
|
||||
g_signal_emit (self, signals[SIGNAL_GLOBAL_REMOVED], key,
|
||||
key, global->object);
|
||||
|
||||
free_global_object (global);
|
||||
for (i = 0; i < self->objects->len; i++) {
|
||||
GObject *o = g_ptr_array_index (self->objects, i);
|
||||
wp_object_manager_add_object (om, o);
|
||||
}
|
||||
}
|
||||
|
||||
G_DEFINE_QUARK (endpoint, wp_global_endpoint)
|
||||
G_DEFINE_QUARK (factory, wp_global_factory)
|
||||
G_DEFINE_QUARK (module, wp_global_module)
|
||||
G_DEFINE_QUARK (policy-manager, wp_global_policy_manager)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#define __WIREPLUMBER_CORE_H__
|
||||
|
||||
#include <glib-object.h>
|
||||
#include "object-manager.h"
|
||||
#include "proxy.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
|
@ -45,13 +46,12 @@ struct pw_remote * wp_core_get_pw_remote (WpCore * self);
|
|||
gboolean wp_core_connect (WpCore * self);
|
||||
WpRemoteState wp_core_get_remote_state (WpCore * self, const gchar ** error);
|
||||
|
||||
void wp_core_set_default_proxy_features (
|
||||
WpCore * self, GType proxy_type, WpProxyFeatures features);
|
||||
|
||||
WpProxy * wp_core_create_remote_object (WpCore * self,
|
||||
const gchar * factory_name, guint32 interface_type,
|
||||
guint32 interface_version, WpProperties * properties);
|
||||
|
||||
void wp_core_install_object_manager (WpCore * self, WpObjectManager * om);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -356,8 +356,7 @@ wp_endpoint_register (WpEndpoint * self)
|
|||
g_info ("WpEndpoint:%p registering '%s' (%s)", self, priv->name,
|
||||
priv->media_class);
|
||||
|
||||
wp_core_register_global (core, WP_GLOBAL_ENDPOINT, g_object_ref (self),
|
||||
g_object_unref);
|
||||
wp_core_register_object (core, g_object_ref (self));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -387,82 +386,10 @@ wp_endpoint_unregister (WpEndpoint * self)
|
|||
g_info ("WpEndpoint:%p unregistering '%s' (%s)", self, priv->name,
|
||||
priv->media_class);
|
||||
|
||||
wp_core_remove_global (core, WP_GLOBAL_ENDPOINT, self);
|
||||
wp_core_remove_object (core, self);
|
||||
}
|
||||
}
|
||||
|
||||
struct endpoints_foreach_data
|
||||
{
|
||||
GPtrArray *result;
|
||||
const gchar *lookup;
|
||||
};
|
||||
|
||||
static inline gboolean
|
||||
media_class_matches (const gchar * media_class, const gchar * lookup)
|
||||
{
|
||||
const gchar *c1 = media_class, *c2 = lookup;
|
||||
|
||||
/* empty lookup matches all classes */
|
||||
if (!lookup)
|
||||
return TRUE;
|
||||
|
||||
/* compare until we reach the end of the lookup string */
|
||||
for (; *c2 != '\0'; c1++, c2++) {
|
||||
if (*c1 != *c2)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* the lookup may not end in a slash, however it must match up
|
||||
* to the end of a submedia_class. i.e.:
|
||||
* match: media_class: Audio/Source/Virtual
|
||||
* lookup: Audio/Source
|
||||
*
|
||||
* NO match: media_class: Audio/Source/Virtual
|
||||
* lookup: Audio/Sou
|
||||
*
|
||||
* if *c1 is not /, also check the previous char, because the lookup
|
||||
* may actually end in a slash:
|
||||
*
|
||||
* match: media_class: Audio/Source/Virtual
|
||||
* lookup: Audio/Source/
|
||||
*/
|
||||
if (!(*c1 == '/' || *c1 == '\0' || *(c1 - 1) == '/'))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
find_endpoints (GQuark key, gpointer global, gpointer user_data)
|
||||
{
|
||||
struct endpoints_foreach_data * data = user_data;
|
||||
|
||||
if (key == WP_GLOBAL_ENDPOINT &&
|
||||
media_class_matches (wp_endpoint_get_media_class (WP_ENDPOINT (global)),
|
||||
data->lookup))
|
||||
g_ptr_array_add (data->result, g_object_ref (global));
|
||||
|
||||
return WP_CORE_FOREACH_GLOBAL_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_endpoint_find:
|
||||
* @core: the core
|
||||
* @media_class_lookup: the media class lookup string
|
||||
*
|
||||
* Returns: (element-type WpEndpoint) (transfer full): an array with all the
|
||||
* endpoints matching the media class lookup string
|
||||
*/
|
||||
GPtrArray *
|
||||
wp_endpoint_find (WpCore * core, const gchar * media_class_lookup)
|
||||
{
|
||||
struct endpoints_foreach_data data;
|
||||
data.result = g_ptr_array_new_with_free_func (g_object_unref);
|
||||
data.lookup = media_class_lookup;
|
||||
wp_core_foreach_global (core, find_endpoints, &data);
|
||||
return data.result;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_endpoint_get_core:
|
||||
* @self: the endpoint
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ WpEndpoint * wp_endpoint_new_finish (GObject *initable, GAsyncResult *res,
|
|||
GError **error);
|
||||
void wp_endpoint_register (WpEndpoint * self);
|
||||
void wp_endpoint_unregister (WpEndpoint * self);
|
||||
GPtrArray * wp_endpoint_find (WpCore * core, const gchar * media_class_lookup);
|
||||
|
||||
WpCore *wp_endpoint_get_core (WpEndpoint * self);
|
||||
const gchar * wp_endpoint_get_name (WpEndpoint * self);
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@ wp_factory_class_init (WpFactoryClass * klass)
|
|||
* @name: the name of the factory
|
||||
* @func: the create object callback
|
||||
*
|
||||
* Returns: (transfer full): the newly created factory
|
||||
* Returns: (transfer none): the newly created factory. No reference
|
||||
* is passed to the caller, since the reference is held by the core.
|
||||
* The caller is free to ignore the return value
|
||||
*/
|
||||
WpFactory *
|
||||
wp_factory_new (WpCore * core, const gchar * name, WpFactoryFunc func)
|
||||
|
|
@ -70,7 +72,7 @@ wp_factory_new (WpCore * core, const gchar * name, WpFactoryFunc func)
|
|||
|
||||
g_info ("WpFactory:%p new factory: %s", f, name);
|
||||
|
||||
wp_core_register_global (core, WP_GLOBAL_FACTORY, f, g_object_unref);
|
||||
wp_core_register_object (core, f);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
|
@ -103,23 +105,11 @@ wp_factory_create_object (WpFactory * self, GType type,
|
|||
self->create_object (self, type, properties, ready, user_data);
|
||||
}
|
||||
|
||||
struct find_factory_data
|
||||
{
|
||||
GQuark name_quark;
|
||||
WpFactory *ret;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
find_factory_func (GQuark key, gpointer global, gpointer user_data)
|
||||
find_factory_func (gpointer factory, gpointer name_quark)
|
||||
{
|
||||
struct find_factory_data *d = user_data;
|
||||
|
||||
if (key != WP_GLOBAL_FACTORY ||
|
||||
WP_FACTORY (global)->name_quark != d->name_quark)
|
||||
return WP_CORE_FOREACH_GLOBAL_CONTINUE;
|
||||
|
||||
d->ret = WP_FACTORY (global);
|
||||
return WP_CORE_FOREACH_GLOBAL_DONE;
|
||||
return WP_IS_FACTORY (factory) &&
|
||||
WP_FACTORY (factory)->name_quark == GPOINTER_TO_UINT (name_quark);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -127,21 +117,23 @@ find_factory_func (GQuark key, gpointer global, gpointer user_data)
|
|||
* @core: the core
|
||||
* @name: the lookup name
|
||||
*
|
||||
* Returns: (transfer none): the factory matching the lookup name
|
||||
* Returns: (transfer full): the factory matching the lookup name
|
||||
*/
|
||||
WpFactory *
|
||||
wp_factory_find (WpCore * core, const gchar * name)
|
||||
{
|
||||
struct find_factory_data d = { g_quark_from_string (name), NULL };
|
||||
wp_core_foreach_global (core, find_factory_func, &d);
|
||||
return d.ret;
|
||||
GObject *f;
|
||||
GQuark q = g_quark_from_string (name);
|
||||
f = wp_core_find_object (core, (GEqualFunc) find_factory_func,
|
||||
GUINT_TO_POINTER (q));
|
||||
return f ? WP_FACTORY (f) : NULL;
|
||||
}
|
||||
|
||||
void
|
||||
wp_factory_make (WpCore * core, const gchar * name, GType type,
|
||||
GVariant * properties, GAsyncReadyCallback ready, gpointer user_data)
|
||||
{
|
||||
WpFactory *f = wp_factory_find (core, name);
|
||||
g_autoptr (WpFactory) f = wp_factory_find (core, name);
|
||||
if (!f) return;
|
||||
wp_factory_create_object (f, type, properties, ready, user_data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ wp_lib_sources = [
|
|||
'factory.c',
|
||||
'module.c',
|
||||
'monitor.c',
|
||||
'object-manager.c',
|
||||
'policy.c',
|
||||
'properties.c',
|
||||
'proxy.c',
|
||||
|
|
@ -21,6 +22,7 @@ wp_lib_headers = [
|
|||
'factory.h',
|
||||
'module.h',
|
||||
'monitor.h',
|
||||
'object-manager.h',
|
||||
'policy.h',
|
||||
'properties.h',
|
||||
'proxy.h',
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ wp_module_load_c (WpModule * self, WpCore * core,
|
|||
* @args: the args passed to the module
|
||||
* @error: return location for errors, or NULL to ignore
|
||||
*
|
||||
* Returns: (transfer full): the loaded module
|
||||
* Returns: (transfer none): the loaded module
|
||||
*/
|
||||
WpModule *
|
||||
wp_module_load (WpCore * core, const gchar * abi, const gchar * module_name,
|
||||
|
|
@ -139,8 +139,7 @@ wp_module_load (WpCore * core, const gchar * abi, const gchar * module_name,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
wp_core_register_global (core, WP_GLOBAL_MODULE, g_object_ref (module),
|
||||
g_object_unref);
|
||||
wp_core_register_object (core, g_object_ref (module));
|
||||
|
||||
return module;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ struct _WpPolicyManager
|
|||
{
|
||||
GObject parent;
|
||||
GList *policies;
|
||||
WpObjectManager *endpoints_om;
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
@ -29,6 +30,7 @@ G_DEFINE_TYPE (WpPolicyManager, wp_policy_manager, G_TYPE_OBJECT)
|
|||
static void
|
||||
wp_policy_manager_init (WpPolicyManager *self)
|
||||
{
|
||||
self->endpoints_om = wp_object_manager_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -38,6 +40,7 @@ wp_policy_manager_finalize (GObject *object)
|
|||
|
||||
g_debug ("WpPolicyManager destroyed");
|
||||
|
||||
g_clear_object (&self->endpoints_om);
|
||||
g_list_free_full (self->policies, g_object_unref);
|
||||
|
||||
G_OBJECT_CLASS (wp_policy_manager_parent_class)->finalize (object);
|
||||
|
|
@ -56,7 +59,7 @@ wp_policy_manager_class_init (WpPolicyManagerClass *klass)
|
|||
}
|
||||
|
||||
static void
|
||||
policy_mgr_endpoint_added (WpCore *core, GQuark key, WpEndpoint *ep,
|
||||
policy_mgr_endpoint_added (WpObjectManager *om, WpEndpoint *ep,
|
||||
WpPolicyManager *self)
|
||||
{
|
||||
GList *l;
|
||||
|
|
@ -75,7 +78,7 @@ policy_mgr_endpoint_added (WpCore *core, GQuark key, WpEndpoint *ep,
|
|||
}
|
||||
|
||||
static void
|
||||
policy_mgr_endpoint_removed (WpCore *core, GQuark key, WpEndpoint *ep,
|
||||
policy_mgr_endpoint_removed (WpObjectManager *om, WpEndpoint *ep,
|
||||
WpPolicyManager *self)
|
||||
{
|
||||
GList *l;
|
||||
|
|
@ -103,20 +106,85 @@ wp_policy_manager_get_instance (WpCore *core)
|
|||
|
||||
g_return_val_if_fail (WP_IS_CORE (core), NULL);
|
||||
|
||||
mgr = wp_core_get_global (core, WP_GLOBAL_POLICY_MANAGER);
|
||||
mgr = wp_core_find_object (core, (GEqualFunc) WP_IS_POLICY_MANAGER,
|
||||
NULL);
|
||||
if (G_UNLIKELY (!mgr)) {
|
||||
mgr = g_object_new (WP_TYPE_POLICY_MANAGER, NULL);
|
||||
|
||||
g_signal_connect_object (core, "global-added::endpoint",
|
||||
/* install the object manager to listen to added/removed endpoints */
|
||||
wp_object_manager_add_object_interest (mgr->endpoints_om,
|
||||
WP_TYPE_ENDPOINT, NULL);
|
||||
g_signal_connect_object (mgr->endpoints_om, "object-added",
|
||||
(GCallback) policy_mgr_endpoint_added, mgr, 0);
|
||||
g_signal_connect_object (core, "global-removed::endpoint",
|
||||
g_signal_connect_object (mgr->endpoints_om, "object-removed",
|
||||
(GCallback) policy_mgr_endpoint_removed, mgr, 0);
|
||||
wp_core_install_object_manager (core, mgr->endpoints_om);
|
||||
|
||||
wp_core_register_global (core, WP_GLOBAL_POLICY_MANAGER, mgr,
|
||||
g_object_unref);
|
||||
wp_core_register_object (core, g_object_ref (mgr));
|
||||
}
|
||||
|
||||
return g_object_ref (mgr);
|
||||
return mgr;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
media_class_matches (const gchar * media_class, const gchar * lookup)
|
||||
{
|
||||
const gchar *c1 = media_class, *c2 = lookup;
|
||||
|
||||
/* empty lookup matches all classes */
|
||||
if (!lookup)
|
||||
return TRUE;
|
||||
|
||||
/* compare until we reach the end of the lookup string */
|
||||
for (; *c2 != '\0'; c1++, c2++) {
|
||||
if (*c1 != *c2)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* the lookup may not end in a slash, however it must match up
|
||||
* to the end of a submedia_class. i.e.:
|
||||
* match: media_class: Audio/Source/Virtual
|
||||
* lookup: Audio/Source
|
||||
*
|
||||
* NO match: media_class: Audio/Source/Virtual
|
||||
* lookup: Audio/Sou
|
||||
*
|
||||
* if *c1 is not /, also check the previous char, because the lookup
|
||||
* may actually end in a slash:
|
||||
*
|
||||
* match: media_class: Audio/Source/Virtual
|
||||
* lookup: Audio/Source/
|
||||
*/
|
||||
if (!(*c1 == '/' || *c1 == '\0' || *(c1 - 1) == '/'))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_policy_manager_list_endpoints:
|
||||
* @self: the policy manager
|
||||
* @media_class: the media class lookup string
|
||||
*
|
||||
* Returns: (transfer full) (element-type WpEndpoint*): an array with all the
|
||||
* endpoints matching the media class lookup string
|
||||
*/
|
||||
GPtrArray *
|
||||
wp_policy_manager_list_endpoints (WpPolicyManager * self,
|
||||
const gchar * media_class)
|
||||
{
|
||||
GPtrArray * ret;
|
||||
guint i;
|
||||
|
||||
g_return_val_if_fail (WP_IS_POLICY_MANAGER (self), NULL);
|
||||
|
||||
ret = wp_object_manager_get_objects (self->endpoints_om, 0);
|
||||
for (i = ret->len; i > 0; i--) {
|
||||
WpEndpoint *ep = g_ptr_array_index (ret, i-1);
|
||||
if (!media_class_matches (wp_endpoint_get_media_class (ep), media_class))
|
||||
g_ptr_array_remove_index_fast (ret, i-1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* WpPolicy */
|
||||
|
|
@ -309,7 +377,7 @@ wp_policy_register (WpPolicy *self, WpCore *core)
|
|||
void
|
||||
wp_policy_unregister (WpPolicy *self)
|
||||
{
|
||||
WpPolicyManager *mgr;
|
||||
g_autoptr (WpPolicyManager) mgr = NULL;
|
||||
WpPolicyPrivate *priv;
|
||||
|
||||
g_return_if_fail (WP_IS_POLICY (self));
|
||||
|
|
@ -317,7 +385,8 @@ wp_policy_unregister (WpPolicy *self)
|
|||
priv = wp_policy_get_instance_private (self);
|
||||
|
||||
if (priv->core) {
|
||||
mgr = wp_core_get_global (priv->core, WP_GLOBAL_POLICY_MANAGER);
|
||||
mgr = wp_core_find_object (priv->core, (GEqualFunc) WP_IS_POLICY_MANAGER,
|
||||
NULL);
|
||||
if (G_UNLIKELY (!mgr)) {
|
||||
g_critical ("WpPolicy:%p seems registered, but the policy manager "
|
||||
"is absent", self);
|
||||
|
|
@ -333,14 +402,15 @@ wp_policy_unregister (WpPolicy *self)
|
|||
void
|
||||
wp_policy_notify_changed (WpPolicy *self)
|
||||
{
|
||||
WpPolicyManager *mgr;
|
||||
g_autoptr (WpPolicyManager) mgr = NULL;
|
||||
WpPolicyPrivate *priv;
|
||||
|
||||
g_return_if_fail (WP_IS_POLICY (self));
|
||||
|
||||
priv = wp_policy_get_instance_private (self);
|
||||
if (priv->core) {
|
||||
mgr = wp_core_get_global (priv->core, WP_GLOBAL_POLICY_MANAGER);
|
||||
mgr = wp_core_find_object (priv->core, (GEqualFunc) WP_IS_POLICY_MANAGER,
|
||||
NULL);
|
||||
if (G_UNLIKELY (!mgr)) {
|
||||
g_critical ("WpPolicy:%p seems registered, but the policy manager "
|
||||
"is absent", self);
|
||||
|
|
@ -366,7 +436,7 @@ WpEndpoint *
|
|||
wp_policy_find_endpoint (WpCore *core, GVariant *props,
|
||||
guint32 *stream_id)
|
||||
{
|
||||
WpPolicyManager *mgr;
|
||||
g_autoptr (WpPolicyManager) mgr = NULL;
|
||||
GList *l;
|
||||
WpPolicy *p;
|
||||
WpEndpoint * ret;
|
||||
|
|
@ -375,7 +445,8 @@ wp_policy_find_endpoint (WpCore *core, GVariant *props,
|
|||
g_return_val_if_fail (g_variant_is_of_type (props, G_VARIANT_TYPE_VARDICT), NULL);
|
||||
g_return_val_if_fail (stream_id != NULL, NULL);
|
||||
|
||||
mgr = wp_core_get_global (core, WP_GLOBAL_POLICY_MANAGER);
|
||||
mgr = wp_core_find_object (core,
|
||||
(GEqualFunc) WP_IS_POLICY_MANAGER, NULL);
|
||||
if (mgr) {
|
||||
for (l = g_list_first (mgr->policies); l; l = g_list_next (l)) {
|
||||
p = WP_POLICY (l->data);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ struct _WpPolicyClass
|
|||
};
|
||||
|
||||
WpPolicyManager * wp_policy_manager_get_instance (WpCore *core);
|
||||
GPtrArray * wp_policy_manager_list_endpoints (WpPolicyManager * self,
|
||||
const gchar * media_class);
|
||||
|
||||
guint32 wp_policy_get_rank (WpPolicy *self);
|
||||
WpCore *wp_policy_get_core (WpPolicy *self);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@
|
|||
#ifndef __WIREPLUMBER_PRIVATE_H__
|
||||
#define __WIREPLUMBER_PRIVATE_H__
|
||||
|
||||
#include "core.h"
|
||||
#include "object-manager.h"
|
||||
#include "proxy.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* core */
|
||||
|
|
@ -19,36 +23,65 @@ struct pw_registry_proxy;
|
|||
struct pw_core_proxy * wp_core_get_pw_core_proxy (WpCore * self);
|
||||
struct pw_registry_proxy * wp_core_get_pw_registry_proxy (WpCore * self);
|
||||
|
||||
enum {
|
||||
WP_CORE_FOREACH_GLOBAL_DONE = FALSE,
|
||||
WP_CORE_FOREACH_GLOBAL_CONTINUE = TRUE,
|
||||
gpointer wp_core_find_object (WpCore * self, GEqualFunc func,
|
||||
gconstpointer data);
|
||||
void wp_core_register_object (WpCore * self, gpointer obj);
|
||||
void wp_core_remove_object (WpCore * self, gpointer obj);
|
||||
|
||||
/* global */
|
||||
|
||||
typedef struct _WpGlobal WpGlobal;
|
||||
struct _WpGlobal
|
||||
{
|
||||
guint32 id;
|
||||
guint32 type;
|
||||
guint32 version;
|
||||
guint32 permissions;
|
||||
WpProperties *properties;
|
||||
GWeakRef proxy;
|
||||
};
|
||||
|
||||
typedef gboolean (*WpCoreForeachGlobalFunc) (GQuark key, gpointer global,
|
||||
gpointer user_data);
|
||||
static inline WpGlobal *
|
||||
wp_global_new (void)
|
||||
{
|
||||
WpGlobal *self = g_rc_box_new0 (WpGlobal);
|
||||
g_weak_ref_init (&self->proxy, NULL);
|
||||
return self;
|
||||
}
|
||||
|
||||
gpointer wp_core_get_global (WpCore * self, GQuark key);
|
||||
void wp_core_foreach_global (WpCore * self, WpCoreForeachGlobalFunc callback,
|
||||
gpointer user_data);
|
||||
static inline void
|
||||
wp_global_clear (WpGlobal * self)
|
||||
{
|
||||
g_clear_pointer (&self->properties, wp_properties_unref);
|
||||
g_weak_ref_clear (&self->proxy);
|
||||
}
|
||||
|
||||
void wp_core_register_global (WpCore * self, GQuark key, gpointer obj,
|
||||
GDestroyNotify destroy_obj);
|
||||
void wp_core_remove_global (WpCore * self, GQuark key, gpointer obj);
|
||||
static inline WpGlobal *
|
||||
wp_global_ref (WpGlobal * self)
|
||||
{
|
||||
return g_rc_box_acquire (self);
|
||||
}
|
||||
|
||||
#define WP_GLOBAL_ENDPOINT (wp_global_endpoint_quark ())
|
||||
GQuark wp_global_endpoint_quark (void);
|
||||
static inline void
|
||||
wp_global_unref (WpGlobal * self)
|
||||
{
|
||||
g_rc_box_release_full (self, (GDestroyNotify) wp_global_clear);
|
||||
}
|
||||
|
||||
#define WP_GLOBAL_FACTORY (wp_global_factory_quark ())
|
||||
GQuark wp_global_factory_quark (void);
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (WpGlobal, wp_global_unref)
|
||||
|
||||
#define WP_GLOBAL_MODULE (wp_global_module_quark ())
|
||||
GQuark wp_global_module_quark (void);
|
||||
/* object manager */
|
||||
|
||||
#define WP_GLOBAL_POLICY_MANAGER (wp_global_policy_manager_quark ())
|
||||
GQuark wp_global_policy_manager_quark (void);
|
||||
void wp_object_manager_add_global (WpObjectManager * self, WpGlobal * global);
|
||||
void wp_object_manager_rm_global (WpObjectManager * self, guint32 id);
|
||||
|
||||
void wp_object_manager_add_object (WpObjectManager * self, GObject * object);
|
||||
void wp_object_manager_rm_object (WpObjectManager * self, GObject * object);
|
||||
|
||||
/* proxy */
|
||||
|
||||
WpProxy * wp_proxy_new_global (WpCore * core, WpGlobal * global);
|
||||
|
||||
void wp_proxy_set_feature_ready (WpProxy * self, WpProxyFeatures feature);
|
||||
void wp_proxy_augment_error (WpProxy * self, GError * error);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,9 +27,7 @@ struct _WpProxyPrivate
|
|||
/* properties */
|
||||
GWeakRef core;
|
||||
|
||||
guint32 global_id;
|
||||
guint32 global_perm;
|
||||
WpProperties *global_props;
|
||||
WpGlobal *global;
|
||||
|
||||
guint32 iface_type;
|
||||
guint32 iface_version;
|
||||
|
|
@ -49,6 +47,7 @@ struct _WpProxyPrivate
|
|||
enum {
|
||||
PROP_0,
|
||||
PROP_CORE,
|
||||
PROP_GLOBAL,
|
||||
PROP_GLOBAL_ID,
|
||||
PROP_GLOBAL_PERMISSIONS,
|
||||
PROP_GLOBAL_PROPERTIES,
|
||||
|
|
@ -69,6 +68,7 @@ enum
|
|||
|
||||
static guint wp_proxy_signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_BOXED_TYPE (WpGlobal, wp_global, wp_global_ref, wp_global_unref)
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (WpProxy, wp_proxy, G_TYPE_OBJECT)
|
||||
|
||||
G_DEFINE_QUARK (core, wp_proxy_core)
|
||||
|
|
@ -137,7 +137,11 @@ proxy_event_destroy (void *data)
|
|||
GHashTableIter iter;
|
||||
GTask *task;
|
||||
|
||||
g_debug ("destroyed pw proxy %p for global %u", priv->pw_proxy, priv->global_id);
|
||||
g_debug ("%s:%p destroyed pw_proxy %p (%s; %s; %u)",
|
||||
G_OBJECT_TYPE_NAME (self), self, priv->pw_proxy,
|
||||
spa_debug_type_find_name (pw_type_info(), priv->iface_type),
|
||||
priv->global ? "global" : "not global",
|
||||
priv->global ? priv->global->id : 0);
|
||||
priv->pw_proxy = NULL;
|
||||
|
||||
g_signal_emit (self, wp_proxy_signals[SIGNAL_PW_PROXY_DESTROYED], 0);
|
||||
|
|
@ -232,10 +236,12 @@ wp_proxy_finalize (GObject * object)
|
|||
WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object));
|
||||
|
||||
g_debug ("%s:%p destroyed (global %u; pw_proxy %p)",
|
||||
G_OBJECT_TYPE_NAME (object), object, priv->global_id, priv->pw_proxy);
|
||||
G_OBJECT_TYPE_NAME (object), object,
|
||||
priv->global ? priv->global->id : 0,
|
||||
priv->pw_proxy);
|
||||
|
||||
g_clear_pointer (&priv->augment_tasks, g_ptr_array_unref);
|
||||
g_clear_pointer (&priv->global_props, wp_properties_unref);
|
||||
g_clear_pointer (&priv->global, wp_global_unref);
|
||||
g_weak_ref_clear (&priv->core);
|
||||
g_clear_pointer (&priv->async_tasks, g_hash_table_unref);
|
||||
|
||||
|
|
@ -252,14 +258,8 @@ wp_proxy_set_property (GObject * object, guint property_id,
|
|||
case PROP_CORE:
|
||||
g_weak_ref_set (&priv->core, g_value_get_object (value));
|
||||
break;
|
||||
case PROP_GLOBAL_ID:
|
||||
priv->global_id = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_GLOBAL_PERMISSIONS:
|
||||
priv->global_perm = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_GLOBAL_PROPERTIES:
|
||||
priv->global_props = g_value_dup_boxed (value);
|
||||
case PROP_GLOBAL:
|
||||
priv->global = g_value_dup_boxed (value);
|
||||
break;
|
||||
case PROP_INTERFACE_TYPE:
|
||||
priv->iface_type = g_value_get_uint (value);
|
||||
|
|
@ -287,13 +287,13 @@ wp_proxy_get_property (GObject * object, guint property_id, GValue * value,
|
|||
g_value_take_object (value, g_weak_ref_get (&priv->core));
|
||||
break;
|
||||
case PROP_GLOBAL_ID:
|
||||
g_value_set_uint (value, priv->global_id);
|
||||
g_value_set_uint (value, priv->global ? priv->global->id : 0);
|
||||
break;
|
||||
case PROP_GLOBAL_PERMISSIONS:
|
||||
g_value_set_uint (value, priv->global_perm);
|
||||
g_value_set_uint (value, priv->global ? priv->global->permissions : 0);
|
||||
break;
|
||||
case PROP_GLOBAL_PROPERTIES:
|
||||
g_value_set_boxed (value, priv->global_props);
|
||||
g_value_set_boxed (value, priv->global ? priv->global->properties : NULL);
|
||||
break;
|
||||
case PROP_INTERFACE_TYPE:
|
||||
g_value_set_uint (value, priv->iface_type);
|
||||
|
|
@ -347,7 +347,7 @@ wp_proxy_default_augment (WpProxy * self, WpProxyFeatures features)
|
|||
|
||||
/* bind */
|
||||
priv->pw_proxy = pw_registry_proxy_bind (
|
||||
wp_core_get_pw_registry_proxy (core), priv->global_id,
|
||||
wp_core_get_pw_registry_proxy (core), priv->global->id,
|
||||
priv->iface_type, priv->iface_version, 0);
|
||||
wp_proxy_got_pw_proxy (self);
|
||||
}
|
||||
|
|
@ -372,20 +372,25 @@ wp_proxy_class_init (WpProxyClass * klass)
|
|||
g_param_spec_object ("core", "core", "The WpCore", WP_TYPE_CORE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_GLOBAL,
|
||||
g_param_spec_boxed ("global", "global", "Internal WpGlobal object",
|
||||
wp_global_get_type (),
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_GLOBAL_ID,
|
||||
g_param_spec_uint ("global-id", "global-id",
|
||||
"The pipewire global id", 0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_GLOBAL_PERMISSIONS,
|
||||
g_param_spec_uint ("global-permissions", "global-permissions",
|
||||
"The pipewire global permissions", 0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_GLOBAL_PROPERTIES,
|
||||
g_param_spec_boxed ("global-properties", "global-properties",
|
||||
"The pipewire global properties", WP_TYPE_PROPERTIES,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_INTERFACE_TYPE,
|
||||
g_param_spec_uint ("interface-type", "interface-type",
|
||||
|
|
@ -429,18 +434,14 @@ wp_proxy_class_init (WpProxyClass * klass)
|
|||
}
|
||||
|
||||
WpProxy *
|
||||
wp_proxy_new_global (WpCore * core,
|
||||
guint32 id, guint32 permissions, WpProperties * properties,
|
||||
guint32 type, guint32 version)
|
||||
wp_proxy_new_global (WpCore * core, WpGlobal * global)
|
||||
{
|
||||
GType gtype = wp_proxy_find_instance_type (type, version);
|
||||
GType gtype = wp_proxy_find_instance_type (global->type, global->version);
|
||||
return g_object_new (gtype,
|
||||
"core", core,
|
||||
"global-id", id,
|
||||
"global-permissions", permissions,
|
||||
"global-properties", properties,
|
||||
"interface-type", type,
|
||||
"interface-version", version,
|
||||
"global", global,
|
||||
"interface-type", global->type,
|
||||
"interface-version", global->version,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
|
@ -585,7 +586,12 @@ wp_proxy_get_core (WpProxy * self)
|
|||
gboolean
|
||||
wp_proxy_is_global (WpProxy * self)
|
||||
{
|
||||
return wp_proxy_get_global_id (self) != 0;
|
||||
WpProxyPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (WP_IS_PROXY (self), FALSE);
|
||||
|
||||
priv = wp_proxy_get_instance_private (self);
|
||||
return priv->global != NULL;
|
||||
}
|
||||
|
||||
guint32
|
||||
|
|
@ -596,7 +602,7 @@ wp_proxy_get_global_id (WpProxy * self)
|
|||
g_return_val_if_fail (WP_IS_PROXY (self), 0);
|
||||
|
||||
priv = wp_proxy_get_instance_private (self);
|
||||
return priv->global_id;
|
||||
return priv->global ? priv->global->id : 0;
|
||||
}
|
||||
|
||||
guint32
|
||||
|
|
@ -607,7 +613,7 @@ wp_proxy_get_global_permissions (WpProxy * self)
|
|||
g_return_val_if_fail (WP_IS_PROXY (self), 0);
|
||||
|
||||
priv = wp_proxy_get_instance_private (self);
|
||||
return priv->global_perm;
|
||||
return priv->global ? priv->global->permissions : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -623,7 +629,9 @@ wp_proxy_get_global_properties (WpProxy * self)
|
|||
g_return_val_if_fail (WP_IS_PROXY (self), NULL);
|
||||
|
||||
priv = wp_proxy_get_instance_private (self);
|
||||
return priv->global_props ? wp_properties_ref (priv->global_props) : NULL;
|
||||
if (!priv->global || !priv->global->properties)
|
||||
return NULL;
|
||||
return wp_properties_ref (priv->global->properties);
|
||||
}
|
||||
|
||||
guint32
|
||||
|
|
|
|||
|
|
@ -40,9 +40,6 @@ struct _WpProxyClass
|
|||
void (*pw_proxy_destroyed) (WpProxy * self);
|
||||
};
|
||||
|
||||
WpProxy * wp_proxy_new_global (WpCore * core,
|
||||
guint32 id, guint32 permissions, WpProperties * properties,
|
||||
guint32 type, guint32 version);
|
||||
WpProxy * wp_proxy_new_wrap (WpCore * core,
|
||||
struct pw_proxy * proxy, guint32 type, guint32 version);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include "factory.h"
|
||||
#include "module.h"
|
||||
#include "monitor.h"
|
||||
#include "object-manager.h"
|
||||
#include "policy.h"
|
||||
#include "properties.h"
|
||||
#include "proxy.h"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#include <pipewire/pipewire.h>
|
||||
|
||||
static void
|
||||
client_added (WpCore * core, WpProxyClient *client, gpointer data)
|
||||
client_added (WpObjectManager * om, WpProxyClient *client, gpointer data)
|
||||
{
|
||||
g_autoptr (WpProperties) properties = NULL;
|
||||
const char *access;
|
||||
|
|
@ -30,9 +30,14 @@ client_added (WpCore * core, WpProxyClient *client, gpointer data)
|
|||
void
|
||||
wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
|
||||
{
|
||||
wp_core_set_default_proxy_features (core, WP_TYPE_PROXY_CLIENT,
|
||||
WpObjectManager *om;
|
||||
|
||||
om = wp_object_manager_new ();
|
||||
wp_object_manager_add_proxy_interest (om, PW_TYPE_INTERFACE_Client, NULL,
|
||||
WP_PROXY_FEATURE_PW_PROXY | WP_PROXY_FEATURE_INFO);
|
||||
|
||||
g_signal_connect(core, "remote-global-added::client",
|
||||
(GCallback) client_added, NULL);
|
||||
g_signal_connect (om, "object-added", (GCallback) client_added, NULL);
|
||||
|
||||
wp_core_install_object_manager (core, om);
|
||||
wp_module_set_destroy_callback (module, g_object_unref, om);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ struct _WpAudioStreamPrivate
|
|||
/* Stream Proxy */
|
||||
WpProxyNode *proxy;
|
||||
|
||||
/* Stream Port Proxies */
|
||||
GPtrArray *port_proxies;
|
||||
WpObjectManager *ports_om;
|
||||
GVariantBuilder port_vb;
|
||||
gboolean port_config_done;
|
||||
|
||||
/* Stream Controls */
|
||||
gfloat volume;
|
||||
|
|
@ -94,98 +94,6 @@ wp_audio_stream_id_decode (guint id, guint *stream_id, guint *control_id)
|
|||
*control_id = c_id;
|
||||
}
|
||||
|
||||
/* called once after all the ports are augmented with INFO */
|
||||
static void
|
||||
on_all_ports_augmented (WpProxy *proxy, GAsyncResult *res, WpAudioStream *self)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
wp_proxy_sync_finish (proxy, res, &error);
|
||||
if (error) {
|
||||
g_warning ("WpAudioStream:%p second sync failed: %s", self,
|
||||
error->message);
|
||||
wp_audio_stream_init_task_finish (self, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
|
||||
g_debug ("%s:%p second sync done", G_OBJECT_TYPE_NAME (self), self);
|
||||
|
||||
wp_audio_stream_init_task_finish (self, NULL);
|
||||
}
|
||||
|
||||
/* called multiple times after on_port_config_done */
|
||||
static void
|
||||
on_audio_stream_port_augmented (WpProxy *port_proxy, GAsyncResult *res,
|
||||
WpAudioStream *self)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
wp_proxy_augment_finish (port_proxy, res, &error);
|
||||
if (error) {
|
||||
g_warning ("WpAudioStream:%p Stream port failed to augment: %s", self,
|
||||
error->message);
|
||||
wp_audio_stream_init_task_finish (self, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* called once after we have all the ports added */
|
||||
static void
|
||||
on_port_config_done (WpProxy *proxy, GAsyncResult *res, WpAudioStream *self)
|
||||
{
|
||||
WpAudioStreamPrivate *priv = wp_audio_stream_get_instance_private (self);
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
wp_proxy_sync_finish (proxy, res, &error);
|
||||
if (error) {
|
||||
g_warning ("WpAudioStream:%p port config sync failed: %s", self,
|
||||
error->message);
|
||||
wp_audio_stream_init_task_finish (self, g_steal_pointer (&error));
|
||||
return;
|
||||
}
|
||||
|
||||
g_debug ("%s:%p port config done", G_OBJECT_TYPE_NAME (self), self);
|
||||
|
||||
wp_proxy_sync (WP_PROXY (priv->proxy), NULL,
|
||||
(GAsyncReadyCallback) on_all_ports_augmented, self);
|
||||
}
|
||||
|
||||
/* called multiple times after we set the PortConfig */
|
||||
static void
|
||||
on_audio_stream_port_added (WpCore *core, WpProxy *proxy, WpAudioStream *self)
|
||||
{
|
||||
WpAudioStreamPrivate *priv = wp_audio_stream_get_instance_private (self);
|
||||
g_autoptr (WpProperties) props = wp_proxy_get_global_properties (proxy);
|
||||
const struct pw_node_info *info = NULL;
|
||||
const char *s;
|
||||
guint node_id = 0;
|
||||
|
||||
/* Get the node id */
|
||||
info = wp_proxy_node_get_info (priv->proxy);
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
if ((s = wp_properties_get (props, PW_KEY_NODE_ID)))
|
||||
node_id = atoi(s);
|
||||
|
||||
/* Skip ports that are not owned by this stream */
|
||||
if (info->id != node_id)
|
||||
return;
|
||||
|
||||
/* Add the proxy port to the array */
|
||||
g_ptr_array_add(priv->port_proxies, g_object_ref (proxy));
|
||||
|
||||
wp_proxy_augment (proxy, WP_PROXY_FEATURE_PW_PROXY | WP_PROXY_FEATURE_INFO,
|
||||
NULL, (GAsyncReadyCallback) on_audio_stream_port_augmented, self);
|
||||
}
|
||||
|
||||
static void
|
||||
on_audio_stream_port_removed (WpCore *core, WpProxy *proxy, WpAudioStream *self)
|
||||
{
|
||||
WpAudioStreamPrivate *priv = wp_audio_stream_get_instance_private (self);
|
||||
g_ptr_array_remove (priv->port_proxies, proxy);
|
||||
}
|
||||
|
||||
static void
|
||||
audio_stream_event_param (WpProxy *proxy, int seq, uint32_t id,
|
||||
uint32_t index, uint32_t next, const struct spa_pod *param,
|
||||
|
|
@ -232,11 +140,29 @@ audio_stream_event_param (WpProxy *proxy, int seq, uint32_t id,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_ports_changed (WpObjectManager *om, WpAudioStream *self)
|
||||
{
|
||||
WpAudioStreamPrivate *priv = wp_audio_stream_get_instance_private (self);
|
||||
|
||||
if (priv->port_config_done) {
|
||||
g_debug ("%s:%p port config done", G_OBJECT_TYPE_NAME (self), self);
|
||||
wp_audio_stream_init_task_finish (self, NULL);
|
||||
g_signal_handlers_disconnect_by_func (priv->ports_om, on_ports_changed,
|
||||
self);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
on_node_proxy_augmented (WpProxy * proxy, GAsyncResult * res,
|
||||
WpAudioStream * self)
|
||||
{
|
||||
WpAudioStreamPrivate *priv = wp_audio_stream_get_instance_private (self);
|
||||
g_autoptr (GError) error = NULL;
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
GVariantBuilder b;
|
||||
const struct pw_node_info *info = NULL;
|
||||
g_autofree gchar *node_id = NULL;
|
||||
|
||||
wp_proxy_augment_finish (proxy, res, &error);
|
||||
if (error) {
|
||||
|
|
@ -249,6 +175,36 @@ on_node_proxy_augmented (WpProxy * proxy, GAsyncResult * res,
|
|||
g_signal_connect_object (proxy, "param",
|
||||
(GCallback) audio_stream_event_param, self, 0);
|
||||
wp_proxy_node_subscribe_params (WP_PROXY_NODE (proxy), 1, SPA_PARAM_Props);
|
||||
|
||||
priv->ports_om = wp_object_manager_new ();
|
||||
|
||||
/* Get the node id */
|
||||
info = wp_proxy_node_get_info (WP_PROXY_NODE (proxy));
|
||||
node_id = g_strdup_printf ("%u", info->id);
|
||||
|
||||
/* set a constraint: the port's "node.id" must match
|
||||
the stream's underlying node id */
|
||||
g_variant_builder_init (&b, G_VARIANT_TYPE ("aa{sv}"));
|
||||
g_variant_builder_open (&b, G_VARIANT_TYPE_VARDICT);
|
||||
g_variant_builder_add (&b, "{sv}", "type",
|
||||
g_variant_new_int32 (WP_OBJECT_MANAGER_CONSTRAINT_PW_GLOBAL_PROPERTY));
|
||||
g_variant_builder_add (&b, "{sv}", "name",
|
||||
g_variant_new_string (PW_KEY_NODE_ID));
|
||||
g_variant_builder_add (&b, "{sv}", "value",
|
||||
g_variant_new_string (node_id));
|
||||
g_variant_builder_close (&b);
|
||||
|
||||
/* declare interest on ports with this constraint */
|
||||
wp_object_manager_add_proxy_interest (priv->ports_om, PW_TYPE_INTERFACE_Port,
|
||||
g_variant_builder_end (&b),
|
||||
WP_PROXY_FEATURE_PW_PROXY | WP_PROXY_FEATURE_INFO);
|
||||
|
||||
g_signal_connect (priv->ports_om, "objects-changed",
|
||||
(GCallback) on_ports_changed, self);
|
||||
|
||||
/* install the object manager */
|
||||
g_object_get (proxy, "core", &core, NULL);
|
||||
wp_core_install_object_manager (core, priv->ports_om);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -257,15 +213,14 @@ wp_audio_stream_finalize (GObject * object)
|
|||
WpAudioStreamPrivate *priv =
|
||||
wp_audio_stream_get_instance_private (WP_AUDIO_STREAM (object));
|
||||
|
||||
g_clear_object (&priv->ports_om);
|
||||
|
||||
/* Clear the endpoint weak reference */
|
||||
g_weak_ref_clear (&priv->endpoint);
|
||||
|
||||
/* Clear the name */
|
||||
g_clear_pointer (&priv->name, g_free);
|
||||
|
||||
/* Clear the port proxies */
|
||||
g_clear_pointer (&priv->port_proxies, g_ptr_array_unref);
|
||||
|
||||
g_clear_object (&priv->init_task);
|
||||
|
||||
G_OBJECT_CLASS (wp_audio_stream_parent_class)->finalize (object);
|
||||
|
|
@ -371,12 +326,6 @@ wp_audio_stream_init_async (GAsyncInitable *initable, int io_priority,
|
|||
wp_proxy_augment (WP_PROXY (priv->proxy),
|
||||
WP_PROXY_FEATURE_PW_PROXY | WP_PROXY_FEATURE_INFO, NULL,
|
||||
(GAsyncReadyCallback) on_node_proxy_augmented, self);
|
||||
|
||||
/* Register a port_added & removed callback */
|
||||
g_signal_connect_object(core, "remote-global-added::port",
|
||||
(GCallback) on_audio_stream_port_added, self, 0);
|
||||
g_signal_connect_object(core, "remote-global-removed::port",
|
||||
(GCallback) on_audio_stream_port_removed, self, 0);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
|
@ -405,7 +354,6 @@ wp_audio_stream_init (WpAudioStream * self)
|
|||
/* Controls */
|
||||
priv->volume = 1.0;
|
||||
priv->mute = FALSE;
|
||||
priv->port_proxies = g_ptr_array_new_full(4, (GDestroyNotify)g_object_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -524,10 +472,12 @@ wp_audio_stream_prepare_link (WpAudioStream * self, GVariant ** properties,
|
|||
GError ** error)
|
||||
{
|
||||
WpAudioStreamPrivate *priv = wp_audio_stream_get_instance_private (self);
|
||||
g_autoptr (GPtrArray) port_proxies =
|
||||
wp_object_manager_get_objects (priv->ports_om, 0);
|
||||
|
||||
/* Create a variant array with all the ports */
|
||||
g_variant_builder_init (&priv->port_vb, G_VARIANT_TYPE ("a(uuuy)"));
|
||||
g_ptr_array_foreach(priv->port_proxies, port_proxies_foreach_func, self);
|
||||
g_ptr_array_foreach (port_proxies, port_proxies_foreach_func, self);
|
||||
*properties = g_variant_builder_end (&priv->port_vb);
|
||||
|
||||
return TRUE;
|
||||
|
|
@ -635,7 +585,5 @@ void
|
|||
wp_audio_stream_finish_port_config (WpAudioStream * self)
|
||||
{
|
||||
WpAudioStreamPrivate *priv = wp_audio_stream_get_instance_private (self);
|
||||
|
||||
wp_proxy_sync (WP_PROXY (priv->proxy), NULL,
|
||||
(GAsyncReadyCallback) on_port_config_done, self);
|
||||
priv->port_config_done = TRUE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
struct impl
|
||||
{
|
||||
WpModule *module;
|
||||
WpObjectManager *om;
|
||||
GHashTable *registered_endpoints;
|
||||
GVariant *streams;
|
||||
};
|
||||
|
|
@ -84,13 +85,14 @@ is_alsa_node (WpProperties * props)
|
|||
}
|
||||
|
||||
static void
|
||||
on_node_added(WpCore *core, WpProxy *proxy, struct impl *impl)
|
||||
on_node_added (WpObjectManager *om, WpProxy *proxy, struct impl *impl)
|
||||
{
|
||||
const gchar *media_class, *name;
|
||||
enum pw_direction direction;
|
||||
GVariantBuilder b;
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
g_autoptr (GVariant) endpoint_props = NULL;
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
|
||||
props = wp_proxy_get_global_properties (proxy);
|
||||
g_return_if_fail(props);
|
||||
|
|
@ -121,12 +123,13 @@ on_node_added(WpCore *core, WpProxy *proxy, struct impl *impl)
|
|||
endpoint_props = g_variant_builder_end (&b);
|
||||
|
||||
/* Create the endpoint async */
|
||||
g_object_get (om, "core", &core, NULL);
|
||||
wp_factory_make (core, "pw-audio-softdsp-endpoint", WP_TYPE_ENDPOINT,
|
||||
endpoint_props, on_endpoint_created, impl);
|
||||
}
|
||||
|
||||
static void
|
||||
on_node_removed (WpCore *core, WpProxy *proxy, struct impl *impl)
|
||||
on_node_removed (WpObjectManager *om, WpProxy *proxy, struct impl *impl)
|
||||
{
|
||||
WpEndpoint *endpoint = NULL;
|
||||
guint32 id = wp_proxy_get_global_id (proxy);
|
||||
|
|
@ -150,6 +153,8 @@ module_destroy (gpointer data)
|
|||
/* Set to NULL as we don't own the reference */
|
||||
impl->module = NULL;
|
||||
|
||||
g_clear_object (&impl->om);
|
||||
|
||||
/* Destroy the registered endpoints table */
|
||||
g_hash_table_unref(impl->registered_endpoints);
|
||||
impl->registered_endpoints = NULL;
|
||||
|
|
@ -168,6 +173,7 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
|
|||
/* Create the module data */
|
||||
impl = g_slice_new0(struct impl);
|
||||
impl->module = module;
|
||||
impl->om = wp_object_manager_new ();
|
||||
impl->registered_endpoints = g_hash_table_new_full (g_direct_hash,
|
||||
g_direct_equal, NULL, (GDestroyNotify)g_object_unref);
|
||||
impl->streams = g_variant_lookup_value (args, "streams",
|
||||
|
|
@ -177,8 +183,13 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
|
|||
wp_module_set_destroy_callback (module, module_destroy, impl);
|
||||
|
||||
/* Register the global addded/removed callbacks */
|
||||
g_signal_connect(core, "remote-global-added::node",
|
||||
g_signal_connect(impl->om, "object-added",
|
||||
(GCallback) on_node_added, impl);
|
||||
g_signal_connect(core, "remote-global-removed::node",
|
||||
g_signal_connect(impl->om, "object-removed",
|
||||
(GCallback) on_node_removed, impl);
|
||||
|
||||
//TODO add constraints & features
|
||||
wp_object_manager_add_proxy_interest (impl->om, PW_TYPE_INTERFACE_Node, NULL,
|
||||
0);
|
||||
wp_core_install_object_manager (core, impl->om);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
struct module_data
|
||||
{
|
||||
WpObjectManager *om;
|
||||
GHashTable *registered_endpoints;
|
||||
};
|
||||
|
||||
|
|
@ -48,7 +49,7 @@ on_endpoint_created(GObject *initable, GAsyncResult *res, gpointer d)
|
|||
}
|
||||
|
||||
static void
|
||||
on_node_added (WpCore *core, WpProxy *proxy, gpointer d)
|
||||
on_node_added (WpObjectManager *om, WpProxy *proxy, gpointer d)
|
||||
{
|
||||
struct module_data *data = d;
|
||||
const gchar *name, *media_class;
|
||||
|
|
@ -57,6 +58,7 @@ on_node_added (WpCore *core, WpProxy *proxy, gpointer d)
|
|||
g_autoptr (GVariant) endpoint_props = NULL;
|
||||
guint32 id = wp_proxy_get_global_id (proxy);
|
||||
g_autoptr (WpProperties) props = wp_proxy_get_global_properties (proxy);
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
|
||||
/* Get the media_class */
|
||||
media_class = wp_properties_get (props, PW_KEY_MEDIA_CLASS);
|
||||
|
|
@ -95,12 +97,13 @@ on_node_added (WpCore *core, WpProxy *proxy, gpointer d)
|
|||
endpoint_props = g_variant_builder_end (&b);
|
||||
|
||||
/* Create the endpoint async */
|
||||
g_object_get (om, "core", &core, NULL);
|
||||
wp_factory_make (core, "pw-audio-softdsp-endpoint", WP_TYPE_ENDPOINT,
|
||||
endpoint_props, on_endpoint_created, data);
|
||||
}
|
||||
|
||||
static void
|
||||
on_node_removed (WpCore *core, WpProxy *proxy, gpointer d)
|
||||
on_node_removed (WpObjectManager *om, WpProxy *proxy, gpointer d)
|
||||
{
|
||||
struct module_data *data = d;
|
||||
WpEndpoint *endpoint = NULL;
|
||||
|
|
@ -122,6 +125,8 @@ module_destroy (gpointer d)
|
|||
{
|
||||
struct module_data *data = d;
|
||||
|
||||
g_clear_object (&data->om);
|
||||
|
||||
/* Destroy the registered endpoints table */
|
||||
g_clear_pointer (&data->registered_endpoints, g_hash_table_unref);
|
||||
|
||||
|
|
@ -136,15 +141,21 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
|
|||
|
||||
/* Create the module data */
|
||||
data = g_slice_new0 (struct module_data);
|
||||
data->om = wp_object_manager_new ();
|
||||
data->registered_endpoints = g_hash_table_new_full (g_direct_hash,
|
||||
g_direct_equal, NULL, (GDestroyNotify)g_object_unref);
|
||||
g_direct_equal, NULL, (GDestroyNotify) g_object_unref);
|
||||
|
||||
/* Set the module destroy callback */
|
||||
wp_module_set_destroy_callback (module, module_destroy, data);
|
||||
|
||||
/* Register the global added/removed callbacks */
|
||||
g_signal_connect(core, "remote-global-added::node",
|
||||
g_signal_connect(data->om, "object-added",
|
||||
(GCallback) on_node_added, data);
|
||||
g_signal_connect(core, "remote-global-removed::node",
|
||||
g_signal_connect(data->om, "object-removed",
|
||||
(GCallback) on_node_removed, data);
|
||||
|
||||
//TODO add constraints & features
|
||||
wp_object_manager_add_proxy_interest (data->om, PW_TYPE_INTERFACE_Node, NULL,
|
||||
0);
|
||||
wp_core_install_object_manager (core, data->om);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ static gboolean
|
|||
select_new_endpoint (WpSimplePolicy *self)
|
||||
{
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
g_autoptr (WpPolicyManager) pmgr = NULL;
|
||||
g_autoptr (GPtrArray) ptr_array = NULL;
|
||||
const gchar *media_class = NULL;
|
||||
WpEndpoint *other;
|
||||
|
|
@ -144,9 +145,10 @@ select_new_endpoint (WpSimplePolicy *self)
|
|||
return G_SOURCE_REMOVE;
|
||||
|
||||
core = wp_policy_get_core (WP_POLICY (self));
|
||||
pmgr = wp_policy_manager_get_instance (core);
|
||||
|
||||
/* Get all the endpoints with the same media class */
|
||||
ptr_array = wp_endpoint_find (core, media_class);
|
||||
ptr_array = wp_policy_manager_list_endpoints (pmgr, media_class);
|
||||
|
||||
/* select the first available that has the "selected" control */
|
||||
for (i = 0; i < (ptr_array ? ptr_array->len : 0); i++) {
|
||||
|
|
@ -392,13 +394,15 @@ static gboolean
|
|||
simple_policy_rescan_in_idle (WpSimplePolicy *self)
|
||||
{
|
||||
g_autoptr (WpCore) core = wp_policy_get_core (WP_POLICY (self));
|
||||
g_autoptr (WpPolicyManager) pmgr = wp_policy_manager_get_instance (core);
|
||||
g_autoptr (GPtrArray) endpoints = NULL;
|
||||
WpEndpoint *ep;
|
||||
gint i;
|
||||
|
||||
g_debug ("rescanning for clients that need linking");
|
||||
|
||||
endpoints = wp_endpoint_find (core, "Stream/Input/Audio");
|
||||
endpoints = wp_policy_manager_list_endpoints (pmgr,
|
||||
"Stream/Input/Audio");
|
||||
if (endpoints) {
|
||||
/* link all capture clients */
|
||||
for (i = 0; i < endpoints->len; i++) {
|
||||
|
|
@ -408,7 +412,8 @@ simple_policy_rescan_in_idle (WpSimplePolicy *self)
|
|||
}
|
||||
g_clear_pointer (&endpoints, g_ptr_array_unref);
|
||||
|
||||
endpoints = wp_endpoint_find (core, "Persistent/Stream/Input/Audio");
|
||||
endpoints = wp_policy_manager_list_endpoints (pmgr,
|
||||
"Persistent/Stream/Input/Audio");
|
||||
if (endpoints) {
|
||||
/* link all persistent capture clients */
|
||||
for (i = 0; i < endpoints->len; i++) {
|
||||
|
|
@ -418,7 +423,8 @@ simple_policy_rescan_in_idle (WpSimplePolicy *self)
|
|||
}
|
||||
g_clear_pointer (&endpoints, g_ptr_array_unref);
|
||||
|
||||
endpoints = wp_endpoint_find (core, "Stream/Output/Audio");
|
||||
endpoints = wp_policy_manager_list_endpoints (pmgr,
|
||||
"Stream/Output/Audio");
|
||||
if (endpoints && endpoints->len > 0) {
|
||||
/* sort based on role priorities */
|
||||
g_ptr_array_sort_with_data (endpoints, compare_client_priority,
|
||||
|
|
@ -430,7 +436,8 @@ simple_policy_rescan_in_idle (WpSimplePolicy *self)
|
|||
}
|
||||
g_clear_pointer (&endpoints, g_ptr_array_unref);
|
||||
|
||||
endpoints = wp_endpoint_find (core, "Persistent/Stream/Output/Audio");
|
||||
endpoints = wp_policy_manager_list_endpoints (pmgr,
|
||||
"Persistent/Stream/Output/Audio");
|
||||
if (endpoints) {
|
||||
/* link all persistent output clients */
|
||||
for (i = 0; i < endpoints->len; i++) {
|
||||
|
|
@ -474,6 +481,7 @@ simple_policy_find_endpoint (WpPolicy *policy, GVariant *props,
|
|||
guint32 *stream_id)
|
||||
{
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
g_autoptr (WpPolicyManager) pmgr = NULL;
|
||||
g_autoptr (GPtrArray) ptr_array = NULL;
|
||||
const char *action = NULL;
|
||||
const char *media_class = NULL;
|
||||
|
|
@ -482,12 +490,13 @@ simple_policy_find_endpoint (WpPolicy *policy, GVariant *props,
|
|||
int i;
|
||||
|
||||
core = wp_policy_get_core (policy);
|
||||
pmgr = wp_policy_manager_get_instance (core);
|
||||
|
||||
g_variant_lookup (props, "action", "&s", &action);
|
||||
|
||||
/* Get all the endpoints with the specific media class*/
|
||||
g_variant_lookup (props, "media.class", "&s", &media_class);
|
||||
ptr_array = wp_endpoint_find (core, media_class);
|
||||
ptr_array = wp_policy_manager_list_endpoints (pmgr, media_class);
|
||||
if (!ptr_array)
|
||||
return NULL;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ typedef struct {
|
|||
/* the client wireplumber core */
|
||||
WpCore *core;
|
||||
|
||||
/* the object manager that listens for proxies */
|
||||
WpObjectManager *om;
|
||||
|
||||
} TestProxyFixture;
|
||||
|
||||
static gboolean
|
||||
|
|
@ -65,6 +68,7 @@ test_proxy_setup (TestProxyFixture *self, gconstpointer user_data)
|
|||
self->context = g_main_context_new ();
|
||||
self->loop = g_main_loop_new (self->context, FALSE);
|
||||
self->core = wp_core_new (self->context, props);
|
||||
self->om = wp_object_manager_new ();
|
||||
|
||||
g_main_context_push_thread_default (self->context);
|
||||
|
||||
|
|
@ -83,6 +87,7 @@ test_proxy_teardown (TestProxyFixture *self, gconstpointer user_data)
|
|||
{
|
||||
g_main_context_pop_thread_default (self->context);
|
||||
|
||||
g_clear_object (&self->om);
|
||||
g_clear_object (&self->core);
|
||||
g_clear_pointer (&self->timeout_source, g_source_unref);
|
||||
g_clear_pointer (&self->loop, g_main_loop_unref);
|
||||
|
|
@ -117,14 +122,18 @@ test_proxy_basic_augmented (WpProxy *proxy, GAsyncResult *res,
|
|||
}
|
||||
|
||||
static void
|
||||
test_proxy_basic_remote_global_added (WpCore *core, WpProxy *proxy,
|
||||
test_proxy_basic_object_added (WpObjectManager *om, WpProxy *proxy,
|
||||
TestProxyFixture *fixture)
|
||||
{
|
||||
g_assert_nonnull (proxy);
|
||||
{
|
||||
g_autoptr (WpCore) pcore = wp_proxy_get_core (proxy);
|
||||
g_autoptr (WpCore) pcore = NULL;
|
||||
g_autoptr (WpCore) omcore = NULL;
|
||||
g_object_get (proxy, "core", &pcore, NULL);
|
||||
g_object_get (om, "core", &omcore, NULL);
|
||||
g_assert_nonnull (pcore);
|
||||
g_assert_true (pcore == core);
|
||||
g_assert_nonnull (omcore);
|
||||
g_assert_true (pcore == omcore);
|
||||
}
|
||||
g_assert_cmpuint (wp_proxy_get_global_id (proxy), !=, 0);
|
||||
g_assert_true (wp_proxy_is_global (proxy));
|
||||
|
|
@ -156,8 +165,12 @@ test_proxy_basic (TestProxyFixture *fixture, gconstpointer data)
|
|||
{
|
||||
/* our test server should advertise exactly one
|
||||
* client: our WpRemote; use this to test WpProxy */
|
||||
g_signal_connect (fixture->core, "remote-global-added::client",
|
||||
(GCallback) test_proxy_basic_remote_global_added, fixture);
|
||||
g_signal_connect (fixture->om, "object-added",
|
||||
(GCallback) test_proxy_basic_object_added, fixture);
|
||||
|
||||
wp_object_manager_add_proxy_interest (fixture->om,
|
||||
PW_TYPE_INTERFACE_Client, NULL, 0);
|
||||
wp_core_install_object_manager (fixture->core, fixture->om);
|
||||
|
||||
g_assert_true (wp_core_connect (fixture->core));
|
||||
g_main_loop_run (fixture->loop);
|
||||
|
|
@ -200,7 +213,7 @@ test_proxy_node_enum_params_done (WpProxyNode *node, GAsyncResult *res,
|
|||
}
|
||||
|
||||
static void
|
||||
test_proxy_node_remote_global_added (WpCore *core, WpProxy *proxy,
|
||||
test_proxy_node_object_added (WpObjectManager *om, WpProxy *proxy,
|
||||
TestProxyFixture *fixture)
|
||||
{
|
||||
const struct pw_node_info *info;
|
||||
|
|
@ -257,12 +270,15 @@ test_proxy_node (TestProxyFixture *fixture, gconstpointer data)
|
|||
pw_thread_loop_unlock (fixture->server.thread_loop);
|
||||
|
||||
/* we should be able to see this exported audiotestsrc node on the client */
|
||||
g_signal_connect (fixture->core, "remote-global-added::node",
|
||||
(GCallback) test_proxy_node_remote_global_added, fixture);
|
||||
g_signal_connect (fixture->om, "object-added",
|
||||
(GCallback) test_proxy_node_object_added, fixture);
|
||||
|
||||
/* tell the remote to call global-added only when these features are ready */
|
||||
wp_core_set_default_proxy_features (fixture->core,
|
||||
WP_TYPE_PROXY_NODE, WP_PROXY_FEATURE_PW_PROXY | WP_PROXY_FEATURE_INFO);
|
||||
/* declare interest and set default features to be ready
|
||||
when the signal is fired */
|
||||
wp_object_manager_add_proxy_interest (fixture->om,
|
||||
PW_TYPE_INTERFACE_Node, NULL,
|
||||
WP_PROXY_FEATURE_PW_PROXY | WP_PROXY_FEATURE_INFO);
|
||||
wp_core_install_object_manager (fixture->core, fixture->om);
|
||||
|
||||
g_assert_true (wp_core_connect (fixture->core));
|
||||
g_main_loop_run (fixture->loop);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue