mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-05 21:48:31 +02:00
proxy/core: refactor object creation
* core no longer exposes create_remote/local_object * node, device & link have constructor methods to enable the create_remote_object functionality * added WpImplNode to wrap pw_impl_node and allow creating "local" node instances * added WpSpaDevice to wrap spa_device and allow creating "local" device instances * exporting objects in all cases now happens by requesting FEATURE_BOUND from the proxy, eliminating the need for WpExported * replaced WpMonitor by new, simpler code directly in module-monitor * the proxy type lookup table in WpProxy is gone, we now use a field on the class structure of every WpProxy subclass and iterate through all the class structures instead; this is more flexible and extensible
This commit is contained in:
parent
d8ae151aba
commit
9330208ada
26 changed files with 1040 additions and 992 deletions
|
|
@ -55,12 +55,12 @@ client_event_info(void *data, const struct pw_client_info *info)
|
|||
WpClient *self = WP_CLIENT (data);
|
||||
|
||||
self->info = pw_client_info_update (self->info, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
|
||||
if (info->change_mask & PW_CLIENT_CHANGE_MASK_PROPS)
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
}
|
||||
|
||||
static const struct pw_client_events client_events = {
|
||||
|
|
@ -84,6 +84,9 @@ wp_client_class_init (WpClientClass * klass)
|
|||
|
||||
object_class->finalize = wp_client_finalize;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Client;
|
||||
proxy_class->pw_iface_version = PW_VERSION_CLIENT;
|
||||
|
||||
proxy_class->get_info = wp_client_get_info;
|
||||
proxy_class->get_properties = wp_client_get_properties;
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,7 @@
|
|||
*/
|
||||
|
||||
#include "core.h"
|
||||
#include "error.h"
|
||||
#include "object-manager.h"
|
||||
#include "proxy.h"
|
||||
#include "wpenums.h"
|
||||
#include "wp.h"
|
||||
#include "private.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
|
@ -411,6 +408,16 @@ wp_core_class_init (WpCoreClass * klass)
|
|||
signals[SIGNAL_DISCONNECTED] = g_signal_new ("disconnected",
|
||||
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
/* ensure WpProxy subclasses are loaded, which is needed to be able
|
||||
to autodetect the GType of proxies created through wp_proxy_new_global() */
|
||||
g_type_ensure (WP_TYPE_CLIENT);
|
||||
g_type_ensure (WP_TYPE_DEVICE);
|
||||
g_type_ensure (WP_TYPE_PROXY_ENDPOINT);
|
||||
g_type_ensure (WP_TYPE_LINK);
|
||||
g_type_ensure (WP_TYPE_NODE);
|
||||
g_type_ensure (WP_TYPE_PORT);
|
||||
g_type_ensure (WP_TYPE_PROXY_SESSION);
|
||||
}
|
||||
|
||||
WpCore *
|
||||
|
|
@ -436,6 +443,13 @@ wp_core_get_pw_context (WpCore * self)
|
|||
return self->pw_context;
|
||||
}
|
||||
|
||||
struct pw_core *
|
||||
wp_core_get_pw_core (WpCore * self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_CORE (self), NULL);
|
||||
return self->pw_core;
|
||||
}
|
||||
|
||||
struct pw_registry *
|
||||
wp_core_get_pw_registry (WpCore * self)
|
||||
{
|
||||
|
|
@ -554,83 +568,6 @@ wp_core_sync_finish (WpCore * self, GAsyncResult * res, GError ** error)
|
|||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
WpProxy *
|
||||
wp_core_export_object (WpCore * self, const gchar * interface_type,
|
||||
gpointer local_object, WpProperties * properties)
|
||||
{
|
||||
struct pw_proxy *proxy = NULL;
|
||||
const char *type;
|
||||
guint32 version;
|
||||
|
||||
g_return_val_if_fail (WP_IS_CORE (self), NULL);
|
||||
g_return_val_if_fail (self->pw_core, NULL);
|
||||
|
||||
proxy = pw_core_export (self->pw_core, interface_type,
|
||||
properties ? wp_properties_peek_dict (properties) : NULL,
|
||||
local_object, 0);
|
||||
if (!proxy)
|
||||
return NULL;
|
||||
|
||||
type = pw_proxy_get_type (proxy, &version);
|
||||
return wp_proxy_new_wrap (self, proxy, type, version, NULL);
|
||||
}
|
||||
|
||||
WpProxy *
|
||||
wp_core_create_local_object (WpCore * self, const gchar * factory_name,
|
||||
const gchar *interface_type, guint32 interface_version,
|
||||
WpProperties * properties)
|
||||
{
|
||||
struct pw_proxy *pw_proxy = NULL;
|
||||
struct pw_impl_factory *factory = NULL;
|
||||
gpointer local_object = NULL;
|
||||
|
||||
g_return_val_if_fail (WP_IS_CORE (self), NULL);
|
||||
g_return_val_if_fail (self->pw_core, NULL);
|
||||
|
||||
factory = pw_context_find_factory (self->pw_context, factory_name);
|
||||
if (!factory)
|
||||
return NULL;
|
||||
|
||||
local_object = pw_impl_factory_create_object (factory,
|
||||
NULL,
|
||||
interface_type,
|
||||
interface_version,
|
||||
properties ? wp_properties_to_pw_properties (properties) : NULL,
|
||||
0);
|
||||
if (!local_object)
|
||||
return NULL;
|
||||
|
||||
pw_proxy = pw_core_export (self->pw_core,
|
||||
interface_type,
|
||||
properties ? wp_properties_peek_dict (properties) : NULL,
|
||||
local_object,
|
||||
0);
|
||||
if (!pw_proxy) {
|
||||
wp_proxy_local_object_destroy_for_type (interface_type, local_object);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return wp_proxy_new_wrap (self, pw_proxy, interface_type, interface_version,
|
||||
local_object);
|
||||
}
|
||||
|
||||
WpProxy *
|
||||
wp_core_create_remote_object (WpCore *self,
|
||||
const gchar *factory_name, const gchar * interface_type,
|
||||
guint32 interface_version, WpProperties * properties)
|
||||
{
|
||||
struct pw_proxy *pw_proxy;
|
||||
|
||||
g_return_val_if_fail (WP_IS_CORE (self), NULL);
|
||||
g_return_val_if_fail (self->pw_core, NULL);
|
||||
|
||||
pw_proxy = pw_core_create_object (self->pw_core, factory_name,
|
||||
interface_type, interface_version,
|
||||
properties ? wp_properties_peek_dict (properties) : NULL, 0);
|
||||
return wp_proxy_new_wrap (self, pw_proxy, interface_type, interface_version,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_core_find_object: (skip)
|
||||
* @self: the core
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
G_BEGIN_DECLS
|
||||
|
||||
struct pw_context;
|
||||
struct pw_core;
|
||||
|
||||
#define WP_TYPE_CORE (wp_core_get_type ())
|
||||
WP_API
|
||||
|
|
@ -32,6 +33,9 @@ GMainContext * wp_core_get_context (WpCore * self);
|
|||
WP_API
|
||||
struct pw_context * wp_core_get_pw_context (WpCore * self);
|
||||
|
||||
WP_API
|
||||
struct pw_core * wp_core_get_pw_core (WpCore * self);
|
||||
|
||||
/* Connection */
|
||||
|
||||
WP_API
|
||||
|
|
@ -57,22 +61,6 @@ WP_API
|
|||
gboolean wp_core_sync_finish (WpCore * self, GAsyncResult * res,
|
||||
GError ** error);
|
||||
|
||||
/* Object */
|
||||
|
||||
WP_API
|
||||
WpProxy * wp_core_export_object (WpCore * self, const gchar * interface_type,
|
||||
gpointer local_object, WpProperties * properties);
|
||||
|
||||
WP_API
|
||||
WpProxy * wp_core_create_local_object (WpCore * self,
|
||||
const gchar *factory_name, const gchar * interface_type,
|
||||
guint32 interface_version, WpProperties * properties);
|
||||
|
||||
WP_API
|
||||
WpProxy * wp_core_create_remote_object (WpCore * self,
|
||||
const gchar * factory_name, const gchar * interface_type,
|
||||
guint32 interface_version, WpProperties * properties);
|
||||
|
||||
/* Object Manager */
|
||||
|
||||
WP_API
|
||||
|
|
|
|||
355
lib/wp/device.c
355
lib/wp/device.c
|
|
@ -1,26 +1,34 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* Copyright © 2019-2020 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "device.h"
|
||||
#include "node.h"
|
||||
#include "error.h"
|
||||
#include "private.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <pipewire/impl.h>
|
||||
#include <spa/monitor/device.h>
|
||||
#include <spa/utils/result.h>
|
||||
|
||||
struct _WpDevice
|
||||
{
|
||||
WpProxy parent;
|
||||
struct pw_device_info *info;
|
||||
};
|
||||
|
||||
/* The device proxy listener */
|
||||
typedef struct _WpDevicePrivate WpDevicePrivate;
|
||||
struct _WpDevicePrivate
|
||||
{
|
||||
struct pw_device_info *info;
|
||||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (WpDevice, wp_device, WP_TYPE_PROXY)
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (WpDevice, wp_device, WP_TYPE_PROXY)
|
||||
|
||||
static void
|
||||
wp_device_init (WpDevice * self)
|
||||
|
|
@ -31,8 +39,9 @@ static void
|
|||
wp_device_finalize (GObject * object)
|
||||
{
|
||||
WpDevice *self = WP_DEVICE (object);
|
||||
WpDevicePrivate *priv = wp_device_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&self->info, pw_device_info_free);
|
||||
g_clear_pointer (&priv->info, pw_device_info_free);
|
||||
|
||||
G_OBJECT_CLASS (wp_device_parent_class)->finalize (object);
|
||||
}
|
||||
|
|
@ -40,13 +49,15 @@ wp_device_finalize (GObject * object)
|
|||
static gconstpointer
|
||||
wp_device_get_info (WpProxy * self)
|
||||
{
|
||||
return WP_DEVICE (self)->info;
|
||||
WpDevicePrivate *priv = wp_device_get_instance_private (WP_DEVICE (self));
|
||||
return priv->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_device_get_properties (WpProxy * self)
|
||||
{
|
||||
return wp_properties_new_wrap_dict (WP_DEVICE (self)->info->props);
|
||||
WpDevicePrivate *priv = wp_device_get_instance_private (WP_DEVICE (self));
|
||||
return wp_properties_new_wrap_dict (priv->info->props);
|
||||
}
|
||||
|
||||
static gint
|
||||
|
|
@ -82,14 +93,15 @@ static void
|
|||
device_event_info(void *data, const struct pw_device_info *info)
|
||||
{
|
||||
WpDevice *self = WP_DEVICE (data);
|
||||
WpDevicePrivate *priv = wp_device_get_instance_private (self);
|
||||
|
||||
priv->info = pw_device_info_update (priv->info, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
|
||||
self->info = pw_device_info_update (self->info, info);
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
|
||||
if (info->change_mask & PW_DEVICE_CHANGE_MASK_PROPS)
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
}
|
||||
|
||||
static const struct pw_device_events device_events = {
|
||||
|
|
@ -102,8 +114,9 @@ static void
|
|||
wp_device_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
||||
{
|
||||
WpDevice *self = WP_DEVICE (proxy);
|
||||
WpDevicePrivate *priv = wp_device_get_instance_private (self);
|
||||
pw_device_add_listener ((struct pw_device *) pw_proxy,
|
||||
&self->listener, &device_events, self);
|
||||
&priv->listener, &device_events, self);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -114,6 +127,9 @@ wp_device_class_init (WpDeviceClass * klass)
|
|||
|
||||
object_class->finalize = wp_device_finalize;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Device;
|
||||
proxy_class->pw_iface_version = PW_VERSION_DEVICE;
|
||||
|
||||
proxy_class->get_info = wp_device_get_info;
|
||||
proxy_class->get_properties = wp_device_get_properties;
|
||||
proxy_class->enum_params = wp_device_enum_params;
|
||||
|
|
@ -121,3 +137,320 @@ wp_device_class_init (WpDeviceClass * klass)
|
|||
|
||||
proxy_class->pw_proxy_created = wp_device_pw_proxy_created;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_device_new_from_factory:
|
||||
* @core: the wireplumber core
|
||||
* @factory_name: the pipewire factory name to construct the device
|
||||
* @properties: (nullable) (transfer full): the properties to pass to the factory
|
||||
*
|
||||
* Constructs a device on the PipeWire server by asking the remote factory
|
||||
* @factory_name to create it.
|
||||
*
|
||||
* Because of the nature of the PipeWire protocol, this operation completes
|
||||
* asynchronously at some point in the future. In order to find out when
|
||||
* this is done, you should call wp_proxy_augment(), requesting at least
|
||||
* %WP_PROXY_FEATURE_BOUND. When this feature is ready, the device is ready for
|
||||
* use on the server. If the device cannot be created, this augment operation
|
||||
* will fail.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): the new device or %NULL if the core
|
||||
* is not connected and therefore the device cannot be created
|
||||
*/
|
||||
WpDevice *
|
||||
wp_device_new_from_factory (WpCore * core,
|
||||
const gchar * factory_name, WpProperties * properties)
|
||||
{
|
||||
g_autoptr (WpProperties) props = properties;
|
||||
WpDevice *self = NULL;
|
||||
struct pw_core *pw_core = wp_core_get_pw_core (core);
|
||||
|
||||
if (!pw_core) {
|
||||
g_warning ("The WirePlumber core is not connected; device cannot be created");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self = g_object_new (WP_TYPE_DEVICE, "core", core, NULL);
|
||||
wp_proxy_set_pw_proxy (WP_PROXY (self), pw_core_create_object (pw_core,
|
||||
factory_name, PW_TYPE_INTERFACE_Device, PW_VERSION_DEVICE,
|
||||
props ? wp_properties_peek_dict (props) : NULL, 0));
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
struct _WpSpaDevice
|
||||
{
|
||||
WpDevice parent;
|
||||
struct spa_handle *handle;
|
||||
struct spa_device *interface;
|
||||
struct spa_hook listener;
|
||||
WpProperties *properties;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SIGNAL_OBJECT_INFO,
|
||||
SPA_DEVICE_LAST_SIGNAL,
|
||||
};
|
||||
|
||||
static guint spa_device_signals[SPA_DEVICE_LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE (WpSpaDevice, wp_spa_device, WP_TYPE_PROXY)
|
||||
|
||||
static void
|
||||
wp_spa_device_init (WpSpaDevice * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
wp_spa_device_finalize (GObject * object)
|
||||
{
|
||||
WpSpaDevice *self = WP_SPA_DEVICE (object);
|
||||
|
||||
g_clear_pointer (&self->handle, pw_unload_spa_handle);
|
||||
g_clear_pointer (&self->properties, wp_properties_unref);
|
||||
|
||||
G_OBJECT_CLASS (wp_spa_device_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
spa_device_event_info (void *data, const struct spa_device_info *info)
|
||||
{
|
||||
WpSpaDevice *self = WP_SPA_DEVICE (data);
|
||||
|
||||
/*
|
||||
* This is emited syncrhonously at the time we add the listener and
|
||||
* before object_info is emited. It gives us additional properties
|
||||
* about the device, like the "api.alsa.card.*" ones that are not
|
||||
* set by the monitor
|
||||
*/
|
||||
if (info->change_mask & SPA_DEVICE_CHANGE_MASK_PROPS)
|
||||
wp_properties_update_from_dict (self->properties, info->props);
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_SPA_DEVICE_FEATURE_ACTIVE);
|
||||
}
|
||||
|
||||
static void
|
||||
spa_device_event_result (void *data, int seq, int res, uint32_t type,
|
||||
const void *result)
|
||||
{
|
||||
if (type != SPA_RESULT_TYPE_DEVICE_PARAMS)
|
||||
return;
|
||||
|
||||
const struct spa_result_device_params *srdp = result;
|
||||
wp_proxy_handle_event_param (WP_PROXY (data), seq, srdp->id, srdp->index,
|
||||
srdp->next, srdp->param);
|
||||
}
|
||||
|
||||
static void
|
||||
spa_device_event_object_info (void *data, uint32_t id,
|
||||
const struct spa_device_object_info *info)
|
||||
{
|
||||
WpSpaDevice *self = WP_SPA_DEVICE (data);
|
||||
GType type = G_TYPE_NONE;
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
|
||||
if (info) {
|
||||
if (!g_strcmp0 (info->type, SPA_TYPE_INTERFACE_Device))
|
||||
type = WP_TYPE_DEVICE;
|
||||
else if (!g_strcmp0 (info->type, SPA_TYPE_INTERFACE_Node))
|
||||
type = WP_TYPE_NODE;
|
||||
|
||||
props = wp_properties_new_wrap_dict (info->props);
|
||||
}
|
||||
|
||||
g_signal_emit (self, spa_device_signals[SIGNAL_OBJECT_INFO], 0, id, type,
|
||||
info ? info->factory_name : NULL, props, self->properties);
|
||||
}
|
||||
|
||||
static const struct spa_device_events spa_device_events = {
|
||||
SPA_VERSION_DEVICE_EVENTS,
|
||||
.info = spa_device_event_info,
|
||||
.result = spa_device_event_result,
|
||||
.object_info = spa_device_event_object_info
|
||||
};
|
||||
|
||||
static void
|
||||
wp_spa_device_augment (WpProxy * proxy, WpProxyFeatures features)
|
||||
{
|
||||
WpSpaDevice *self = WP_SPA_DEVICE (proxy);
|
||||
|
||||
/* if any of the standard features is requested, make sure BOUND
|
||||
is also requested, as they all depend on binding the pw_spa_device */
|
||||
if (features & WP_PROXY_FEATURES_STANDARD)
|
||||
features |= WP_PROXY_FEATURE_BOUND;
|
||||
|
||||
if (features & WP_PROXY_FEATURE_BOUND) {
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (proxy);
|
||||
struct pw_core *pw_core = wp_core_get_pw_core (core);
|
||||
|
||||
/* no pw_core -> we are not connected */
|
||||
if (!pw_core) {
|
||||
wp_proxy_augment_error (proxy, g_error_new (WP_DOMAIN_LIBRARY,
|
||||
WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"The WirePlumber core is not connected; "
|
||||
"object cannot be exported to PipeWire"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* export to get a proxy; feature will complete
|
||||
when the pw_proxy.bound event will be called. */
|
||||
wp_proxy_set_pw_proxy (proxy, pw_core_export (pw_core,
|
||||
SPA_TYPE_INTERFACE_Device,
|
||||
wp_properties_peek_dict (self->properties),
|
||||
self->interface, 0));
|
||||
}
|
||||
|
||||
if (features & WP_SPA_DEVICE_FEATURE_ACTIVE) {
|
||||
gint res = spa_device_add_listener (self->interface, &self->listener,
|
||||
&spa_device_events, self);
|
||||
if (res < 0) {
|
||||
wp_proxy_augment_error (proxy, g_error_new (WP_DOMAIN_LIBRARY,
|
||||
WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"failed to initialize device: %s", spa_strerror (res)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_spa_device_get_info (WpProxy * proxy)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_spa_device_get_properties (WpProxy * proxy)
|
||||
{
|
||||
WpSpaDevice *self = WP_SPA_DEVICE (proxy);
|
||||
return wp_properties_ref (self->properties);
|
||||
}
|
||||
|
||||
static gint
|
||||
wp_spa_device_enum_params (WpProxy * proxy, guint32 id, guint32 start,
|
||||
guint32 num, const struct spa_pod *filter)
|
||||
{
|
||||
WpSpaDevice *self = WP_SPA_DEVICE (proxy);
|
||||
int device_enum_params_result;
|
||||
|
||||
device_enum_params_result = spa_device_enum_params (self->interface,
|
||||
0, id, start, num, filter);
|
||||
g_warn_if_fail (device_enum_params_result >= 0);
|
||||
|
||||
return device_enum_params_result;
|
||||
}
|
||||
|
||||
static gint
|
||||
wp_spa_device_set_param (WpProxy * proxy, guint32 id, guint32 flags,
|
||||
const struct spa_pod *param)
|
||||
{
|
||||
WpSpaDevice *self = WP_SPA_DEVICE (proxy);
|
||||
int device_set_param_result;
|
||||
|
||||
device_set_param_result = spa_device_set_param (self->interface,
|
||||
id, flags, param);
|
||||
g_warn_if_fail (device_set_param_result >= 0);
|
||||
|
||||
return device_set_param_result;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_spa_device_class_init (WpSpaDeviceClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_spa_device_finalize;
|
||||
|
||||
proxy_class->augment = wp_spa_device_augment;
|
||||
|
||||
proxy_class->get_info = wp_spa_device_get_info;
|
||||
proxy_class->get_properties = wp_spa_device_get_properties;
|
||||
proxy_class->enum_params = wp_spa_device_enum_params;
|
||||
proxy_class->set_param = wp_spa_device_set_param;
|
||||
|
||||
/**
|
||||
* WpSpaDevice::object-info:
|
||||
* @self: the #WpSpaDevice
|
||||
* @id: the id of the managed object
|
||||
* @type: the #WpProxy subclass type that the managed object should have,
|
||||
* or %G_TYPE_NONE if the object is being destroyed
|
||||
* @factory: (nullable): the name of the SPA factory to use to construct
|
||||
* the managed object, or %NULL if the object is being destroyed
|
||||
* @properties: (nullable): additional properties that the managed object
|
||||
* should have, or %NULL if the object is being destroyed
|
||||
* @parent_props: the properties of the device itself
|
||||
*
|
||||
* This signal is emitted when the device is creating or destroying a managed
|
||||
* object. The handler is expected to actually construct or destroy the
|
||||
* object using the requested SPA @factory and with the given @properties.
|
||||
*
|
||||
* The handler may also use @parent_props to enrich the properties set
|
||||
* that will be assigned on the object. @parent_props contains all the
|
||||
* properties that this device object has.
|
||||
*
|
||||
* When the object is being created, @type can either be %WP_TYPE_DEVICE
|
||||
* or %WP_TYPE_NODE. The handler is free to create a substitute of those,
|
||||
* like %WP_TYPE_SPA_DEVICE instead of %WP_TYPE_DEVICE, depending on the
|
||||
* use case.
|
||||
*/
|
||||
spa_device_signals[SIGNAL_OBJECT_INFO] = g_signal_new (
|
||||
"object-info", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
|
||||
0, NULL, NULL, NULL, G_TYPE_NONE, 5, G_TYPE_UINT, G_TYPE_GTYPE,
|
||||
G_TYPE_STRING, WP_TYPE_PROPERTIES, WP_TYPE_PROPERTIES);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_spa_device_new_from_spa_factory:
|
||||
* @core: the wireplumber core
|
||||
* @factory_name: the name of the SPA factory
|
||||
* @properties: (nullable) (transfer full): properties to be passed to device
|
||||
* constructor
|
||||
*
|
||||
* Constructs a `SPA_TYPE_INTERFACE_Device` by loading the given SPA
|
||||
* @factory_name.
|
||||
*
|
||||
* To export this device to the PipeWire server, you need to call
|
||||
* wp_proxy_augment() requesting %WP_PROXY_FEATURE_BOUND and
|
||||
* wait for the operation to complete.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): A new #WpSpaDevice wrapping the
|
||||
* device that was constructed by the factory, or %NULL if the factory
|
||||
* does not exist or was unable to construct the device
|
||||
*/
|
||||
WpSpaDevice *
|
||||
wp_spa_device_new_from_spa_factory (WpCore * core,
|
||||
const gchar * factory_name, WpProperties * properties)
|
||||
{
|
||||
g_autoptr (WpProperties) props = properties;
|
||||
struct pw_context *pw_context = wp_core_get_pw_context (core);
|
||||
g_autoptr (WpSpaDevice) self = NULL;
|
||||
gint res;
|
||||
|
||||
g_return_val_if_fail (pw_context != NULL, NULL);
|
||||
|
||||
self = g_object_new (WP_TYPE_SPA_DEVICE, "core", core, NULL);
|
||||
|
||||
/* Load the monitor handle */
|
||||
self->handle = pw_context_load_spa_handle (pw_context,
|
||||
factory_name, props ? wp_properties_peek_dict (props) : NULL);
|
||||
if (!self->handle) {
|
||||
g_warning ("SPA handle '%s' could not be loaded; is it installed?",
|
||||
factory_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the handle interface */
|
||||
res = spa_handle_get_interface (self->handle, SPA_TYPE_INTERFACE_Device,
|
||||
(gpointer *) &self->interface);
|
||||
if (res < 0) {
|
||||
g_warning ("Could not get device interface from SPA handle: %s",
|
||||
spa_strerror (res));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->properties = props ?
|
||||
g_steal_pointer (&props) : wp_properties_new_empty ();
|
||||
|
||||
return g_steal_pointer (&self);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,30 @@
|
|||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* WpDevice */
|
||||
|
||||
#define WP_TYPE_DEVICE (wp_device_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_FINAL_TYPE (WpDevice, wp_device, WP, DEVICE, WpProxy)
|
||||
|
||||
WP_API
|
||||
WpDevice * wp_device_new_from_factory (WpCore * core,
|
||||
const gchar * factory_name, WpProperties * properties);
|
||||
|
||||
/* WpSpaDevice */
|
||||
|
||||
typedef enum { /*< flags >*/
|
||||
WP_SPA_DEVICE_FEATURE_ACTIVE = WP_PROXY_FEATURE_LAST,
|
||||
} WpSpaDeviceFeatures;
|
||||
|
||||
#define WP_TYPE_SPA_DEVICE (wp_spa_device_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_FINAL_TYPE (WpSpaDevice, wp_spa_device, WP, SPA_DEVICE, WpProxy)
|
||||
|
||||
WP_API
|
||||
WpSpaDevice * wp_spa_device_new_from_spa_factory (WpCore * core,
|
||||
const gchar * factory_name, WpProperties * properties);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -315,12 +315,12 @@ endpoint_event_info (void *data, const struct pw_endpoint_info *info)
|
|||
WpProxyEndpoint *self = WP_PROXY_ENDPOINT (data);
|
||||
|
||||
self->info = endpoint_info_update (self->info, &self->properties, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
|
||||
if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS)
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
}
|
||||
|
||||
static const struct pw_endpoint_events endpoint_events = {
|
||||
|
|
@ -425,6 +425,9 @@ wp_proxy_endpoint_class_init (WpProxyEndpointClass * klass)
|
|||
|
||||
object_class->finalize = wp_proxy_endpoint_finalize;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Endpoint;
|
||||
proxy_class->pw_iface_version = PW_VERSION_ENDPOINT;
|
||||
|
||||
proxy_class->augment = wp_proxy_endpoint_augment;
|
||||
proxy_class->get_info = wp_proxy_endpoint_get_info;
|
||||
proxy_class->get_properties = wp_proxy_endpoint_get_properties;
|
||||
|
|
@ -604,6 +607,7 @@ wp_exported_endpoint_export (WpExported * self)
|
|||
wp_exported_endpoint_get_instance_private (WP_EXPORTED_ENDPOINT (self));
|
||||
g_autoptr (WpCore) core = wp_exported_get_core (self);
|
||||
struct pw_client_endpoint *pw_proxy = NULL;
|
||||
struct pw_core *pw_core = wp_core_get_pw_core (core);
|
||||
|
||||
/* make sure these props are not present; they are added by the server */
|
||||
wp_properties_set (priv->properties, PW_KEY_OBJECT_ID, NULL);
|
||||
|
|
@ -614,12 +618,11 @@ wp_exported_endpoint_export (WpExported * self)
|
|||
wp_properties_set (priv->properties, PW_KEY_ENDPOINT_NAME, priv->info.name);
|
||||
wp_properties_set (priv->properties, PW_KEY_MEDIA_CLASS, priv->info.media_class);
|
||||
|
||||
priv->client_ep = wp_core_create_remote_object (core, "client-endpoint",
|
||||
pw_proxy = pw_core_create_object (pw_core, "client-endpoint",
|
||||
PW_TYPE_INTERFACE_ClientEndpoint, PW_VERSION_CLIENT_ENDPOINT,
|
||||
priv->properties);
|
||||
|
||||
pw_proxy = (struct pw_client_endpoint *) wp_proxy_get_pw_proxy (
|
||||
priv->client_ep);
|
||||
wp_properties_peek_dict (priv->properties), 0);
|
||||
priv->client_ep = g_object_new (WP_TYPE_PROXY, "core", core,
|
||||
"pw-proxy", pw_proxy, NULL);
|
||||
|
||||
pw_client_endpoint_add_listener (pw_proxy, &priv->listener,
|
||||
&client_endpoint_events, self);
|
||||
|
|
|
|||
|
|
@ -55,12 +55,12 @@ link_event_info(void *data, const struct pw_link_info *info)
|
|||
WpLink *self = WP_LINK (data);
|
||||
|
||||
self->info = pw_link_info_update (self->info, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
|
||||
if (info->change_mask & PW_LINK_CHANGE_MASK_PROPS)
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
}
|
||||
|
||||
static const struct pw_link_events link_events = {
|
||||
|
|
@ -84,8 +84,50 @@ wp_link_class_init (WpLinkClass * klass)
|
|||
|
||||
object_class->finalize = wp_link_finalize;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Link;
|
||||
proxy_class->pw_iface_version = PW_VERSION_LINK;
|
||||
|
||||
proxy_class->get_info = wp_link_get_info;
|
||||
proxy_class->get_properties = wp_link_get_properties;
|
||||
|
||||
proxy_class->pw_proxy_created = wp_link_pw_proxy_created;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_link_new_from_factory:
|
||||
* @core: the wireplumber core
|
||||
* @factory_name: the pipewire factory name to construct the link
|
||||
* @properties: (nullable) (transfer full): the properties to pass to the factory
|
||||
*
|
||||
* Constructs a link on the PipeWire server by asking the remote factory
|
||||
* @factory_name to create it.
|
||||
*
|
||||
* Because of the nature of the PipeWire protocol, this operation completes
|
||||
* asynchronously at some point in the future. In order to find out when
|
||||
* this is done, you should call wp_proxy_augment(), requesting at least
|
||||
* %WP_PROXY_FEATURE_BOUND. When this feature is ready, the link is ready for
|
||||
* use on the server. If the link cannot be created, this augment operation
|
||||
* will fail.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): the new link or %NULL if the core
|
||||
* is not connected and therefore the link cannot be created
|
||||
*/
|
||||
WpLink *
|
||||
wp_link_new_from_factory (WpCore * core,
|
||||
const gchar * factory_name, WpProperties * properties)
|
||||
{
|
||||
g_autoptr (WpProperties) props = properties;
|
||||
WpLink *self = NULL;
|
||||
struct pw_core *pw_core = wp_core_get_pw_core (core);
|
||||
|
||||
if (!pw_core) {
|
||||
g_warning ("The WirePlumber core is not connected; link cannot be created");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self = g_object_new (WP_TYPE_LINK, "core", core, NULL);
|
||||
wp_proxy_set_pw_proxy (WP_PROXY (self), pw_core_create_object (pw_core,
|
||||
factory_name, PW_TYPE_INTERFACE_Link, PW_VERSION_LINK,
|
||||
props ? wp_properties_peek_dict (props) : NULL, 0));
|
||||
return self;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ G_BEGIN_DECLS
|
|||
WP_API
|
||||
G_DECLARE_FINAL_TYPE (WpLink, wp_link, WP, LINK, WpProxy)
|
||||
|
||||
WP_API
|
||||
WpLink * wp_link_new_from_factory (WpCore * core,
|
||||
const gchar * factory_name, WpProperties * properties);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ wp_lib_sources = files(
|
|||
'factory.c',
|
||||
'link.c',
|
||||
'module.c',
|
||||
'monitor.c',
|
||||
'node.c',
|
||||
'object-manager.c',
|
||||
'policy.c',
|
||||
|
|
@ -34,7 +33,6 @@ wp_lib_headers = files(
|
|||
'factory.h',
|
||||
'link.h',
|
||||
'module.h',
|
||||
'monitor.h',
|
||||
'node.h',
|
||||
'object-manager.h',
|
||||
'policy.h',
|
||||
|
|
|
|||
556
lib/wp/monitor.c
556
lib/wp/monitor.c
|
|
@ -1,556 +0,0 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <spa/monitor/device.h>
|
||||
#include <spa/pod/builder.h>
|
||||
#include <spa/pod/iter.h>
|
||||
#include <spa/utils/result.h>
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "node.h"
|
||||
#include "device.h"
|
||||
#include "monitor.h"
|
||||
#include "error.h"
|
||||
#include "wpenums.h"
|
||||
#include "private.h"
|
||||
|
||||
typedef struct {
|
||||
struct spa_handle *handle;
|
||||
struct spa_device *interface;
|
||||
struct spa_hook listener;
|
||||
} WpSpaObject;
|
||||
|
||||
struct _WpMonitor
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
/* Props */
|
||||
GWeakRef core;
|
||||
gchar *factory_name;
|
||||
WpProperties *properties;
|
||||
WpMonitorFlags flags;
|
||||
|
||||
struct object *device;
|
||||
};
|
||||
|
||||
struct object
|
||||
{
|
||||
guint32 id;
|
||||
GType type;
|
||||
|
||||
WpProxy *proxy;
|
||||
WpProperties *properties;
|
||||
|
||||
GList *children; /* element-type: struct object* */
|
||||
|
||||
WpMonitor *self;
|
||||
WpSpaObject *spa_obj;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_CORE,
|
||||
PROP_FACTORY_NAME,
|
||||
PROP_PROPERTIES,
|
||||
PROP_FLAGS,
|
||||
};
|
||||
|
||||
enum {
|
||||
SIG_SETUP_NODE_PROPS,
|
||||
SIG_SETUP_DEVICE_PROPS,
|
||||
N_SIGNALS
|
||||
};
|
||||
|
||||
static guint32 signals[N_SIGNALS] = {0};
|
||||
|
||||
G_DEFINE_TYPE (WpMonitor, wp_monitor, G_TYPE_OBJECT)
|
||||
|
||||
static gpointer find_object (GList *list, guint32 id, GList **link);
|
||||
static struct object * node_new (struct object *dev, uint32_t id,
|
||||
const struct spa_device_object_info *info);
|
||||
static struct object * device_new (WpMonitor *self, uint32_t id,
|
||||
const gchar *factory_name, WpProperties *properties, GError **error);
|
||||
static void object_free (struct object *obj);
|
||||
|
||||
/* device events */
|
||||
|
||||
static void
|
||||
device_info (void *data, const struct spa_device_info *info)
|
||||
{
|
||||
struct object *obj = data;
|
||||
|
||||
/*
|
||||
* This is emited syncrhonously at the time we add the listener and
|
||||
* before object_info is emited. It gives us additional properties
|
||||
* about the device, like the "api.alsa.card.*" ones that are not
|
||||
* set by the monitor
|
||||
*/
|
||||
if (info->change_mask & SPA_DEVICE_CHANGE_MASK_PROPS && obj->properties)
|
||||
wp_properties_update_from_dict (obj->properties, info->props);
|
||||
}
|
||||
|
||||
static void
|
||||
device_object_info (void *data, uint32_t id,
|
||||
const struct spa_device_object_info *info)
|
||||
{
|
||||
struct object *obj = data;
|
||||
struct object *child = NULL;
|
||||
WpMonitor *self = obj->self;
|
||||
GList *link = NULL;
|
||||
g_autoptr (GError) err = NULL;
|
||||
|
||||
/* Find the child */
|
||||
child = find_object (obj->children, id, &link);
|
||||
|
||||
/* new object, construct... */
|
||||
if (info && !child) {
|
||||
/* Device */
|
||||
if (g_strcmp0 (info->type, SPA_TYPE_INTERFACE_Device) == 0) {
|
||||
if (!(child = device_new (self, id, info->factory_name,
|
||||
wp_properties_new_wrap_dict (info->props), &err)))
|
||||
g_debug ("WpMonitor:%p:%s %s", self, self->factory_name, err->message);
|
||||
return;
|
||||
}
|
||||
/* Node */
|
||||
else if (g_strcmp0 (info->type, SPA_TYPE_INTERFACE_Node) == 0) {
|
||||
if (!(child = node_new (obj, id, info)))
|
||||
return;
|
||||
}
|
||||
/* Default */
|
||||
else {
|
||||
g_debug ("WpMonitor:%p:%s got device_object_info for unknown object "
|
||||
"type %s", self, self->factory_name, info->type);
|
||||
return;
|
||||
}
|
||||
obj->children = g_list_append (obj->children, child);
|
||||
}
|
||||
/* object removed, delete... */
|
||||
else if (!info && child) {
|
||||
object_free (child);
|
||||
obj->children = g_list_delete_link (obj->children, link);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct spa_device_events device_events = {
|
||||
SPA_VERSION_DEVICE_EVENTS,
|
||||
.info = device_info,
|
||||
.object_info = device_object_info
|
||||
};
|
||||
|
||||
/* WpSpaObject */
|
||||
|
||||
static void
|
||||
wp_spa_object_free (WpSpaObject *self)
|
||||
{
|
||||
spa_hook_remove (&self->listener);
|
||||
pw_unload_spa_handle (self->handle);
|
||||
}
|
||||
|
||||
static inline WpSpaObject *
|
||||
wp_spa_object_ref (WpSpaObject *self)
|
||||
{
|
||||
return g_rc_box_acquire (self);
|
||||
}
|
||||
|
||||
static inline void
|
||||
wp_spa_object_unref (WpSpaObject *self)
|
||||
{
|
||||
g_rc_box_release_full (self, (GDestroyNotify) wp_spa_object_free);
|
||||
}
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC (WpSpaObject, wp_spa_object_unref)
|
||||
|
||||
static WpSpaObject *
|
||||
load_spa_object (WpCore *core, const gchar *factory, const char * iface_type,
|
||||
WpProperties *props, GError **error)
|
||||
{
|
||||
g_autoptr (WpSpaObject) self = g_rc_box_new0 (WpSpaObject);
|
||||
gint res;
|
||||
|
||||
/* Load the monitor handle */
|
||||
self->handle = pw_context_load_spa_handle (wp_core_get_pw_context (core),
|
||||
factory, props ? wp_properties_peek_dict (props) : NULL);
|
||||
if (!self->handle) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"SPA handle '%s' could not be loaded; is it installed?",
|
||||
factory);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Get the handle interface */
|
||||
res = spa_handle_get_interface (self->handle, iface_type,
|
||||
(gpointer *)&self->interface);
|
||||
if (res < 0) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"Could not get interface %s from SPA handle", iface_type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return g_steal_pointer (&self);
|
||||
}
|
||||
|
||||
/* struct object */
|
||||
|
||||
static gpointer
|
||||
find_object (GList *list, guint32 id, GList **link)
|
||||
{
|
||||
/*
|
||||
* The first element of struct object is the guint32 containing the id,
|
||||
* so we can directly cast the list data to guint32, no matter what the
|
||||
* actual structure is
|
||||
*/
|
||||
for (; list; list = g_list_next (list)) {
|
||||
if (id == *((guint32 *) list->data)) {
|
||||
*link = list;
|
||||
return list->data;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct object *
|
||||
node_new (struct object *dev, uint32_t id,
|
||||
const struct spa_device_object_info *info)
|
||||
{
|
||||
WpMonitor *self = dev->self;
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
g_autoptr (WpProxy) proxy = NULL;
|
||||
struct object *node = NULL;
|
||||
const gchar *pw_factory_name = "spa-node-factory";
|
||||
|
||||
g_return_val_if_fail (g_strcmp0 (info->type, SPA_TYPE_INTERFACE_Node) == 0, NULL);
|
||||
|
||||
g_debug ("WpMonitor:%p:%s new node %u", self, self->factory_name, id);
|
||||
|
||||
/* use the adapter instead of spa-node-factory if requested */
|
||||
if (self->flags & WP_MONITOR_FLAG_USE_ADAPTER)
|
||||
pw_factory_name = "adapter";
|
||||
|
||||
core = g_weak_ref_get (&self->core);
|
||||
props = wp_properties_new_copy_dict (info->props);
|
||||
|
||||
/* pass down the id to the setup function */
|
||||
wp_properties_setf (props, WP_MONITOR_KEY_OBJECT_ID, "%u", id);
|
||||
|
||||
/* the SPA factory name must be set as a property
|
||||
for the spa-node-factory / adapter */
|
||||
wp_properties_set (props, PW_KEY_FACTORY_NAME, info->factory_name);
|
||||
|
||||
/* the rest is up to the user */
|
||||
g_signal_emit (self, signals[SIG_SETUP_NODE_PROPS], 0, dev->properties,
|
||||
props);
|
||||
|
||||
/* and delete the id - it should not appear on the proxy */
|
||||
wp_properties_set (props, WP_MONITOR_KEY_OBJECT_ID, NULL);
|
||||
|
||||
/* create the node locally or remotely */
|
||||
proxy = (self->flags & WP_MONITOR_FLAG_LOCAL_NODES) ?
|
||||
wp_core_create_local_object (core, pw_factory_name,
|
||||
PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, props) :
|
||||
wp_core_create_remote_object (core, pw_factory_name,
|
||||
PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, props);
|
||||
if (!proxy) {
|
||||
g_warning ("WpMonitor:%p: failed to create node: %s", self,
|
||||
g_strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = g_slice_new0 (struct object);
|
||||
node->self = self;
|
||||
node->id = id;
|
||||
node->type = WP_TYPE_NODE;
|
||||
node->proxy = g_steal_pointer (&proxy);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static void
|
||||
set_profile(struct spa_device * dev, int index)
|
||||
{
|
||||
char buf[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
||||
spa_device_set_param (dev,
|
||||
SPA_PARAM_Profile, 0,
|
||||
spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_ParamProfile, 0,
|
||||
SPA_PARAM_PROFILE_index, SPA_POD_Int(index)));
|
||||
}
|
||||
|
||||
static struct object *
|
||||
device_new (WpMonitor *self, uint32_t id, const gchar *factory_name,
|
||||
WpProperties *properties, GError **error)
|
||||
{
|
||||
g_autoptr (GError) err = NULL;
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
g_autoptr (WpSpaObject) spa_dev = NULL;
|
||||
g_autoptr (WpProxy) proxy = NULL;
|
||||
struct object *dev = NULL;
|
||||
gint ret = 0;
|
||||
|
||||
g_debug ("WpMonitor:%p:%s new device %d", self, self->factory_name, (gint) id);
|
||||
|
||||
core = g_weak_ref_get (&self->core);
|
||||
props = properties ? wp_properties_copy (properties) : wp_properties_new_empty ();
|
||||
|
||||
/* pass down the id to the setup function */
|
||||
wp_properties_setf (props, WP_MONITOR_KEY_OBJECT_ID, "%d", (gint) id);
|
||||
|
||||
/* let the handler setup the properties accordingly */
|
||||
g_signal_emit (self, signals[SIG_SETUP_DEVICE_PROPS], 0, props);
|
||||
|
||||
/* and delete the id - it should not appear on the proxy */
|
||||
wp_properties_set (props, WP_MONITOR_KEY_OBJECT_ID, NULL);
|
||||
|
||||
/* load the spa device */
|
||||
spa_dev = load_spa_object (core, factory_name, SPA_TYPE_INTERFACE_Device,
|
||||
props, &err);
|
||||
if (!spa_dev) {
|
||||
g_propagate_error (error, g_steal_pointer (&err));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* check for id != -1 to avoid exporting the "monitor" device itself;
|
||||
exporting it is buggy, but we should revise this in the future; FIXME */
|
||||
if (id != -1 && !(proxy = wp_core_export_object (core,
|
||||
SPA_TYPE_INTERFACE_Device, spa_dev->interface, props))) {
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"failed to export device: %s", g_strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create the device */
|
||||
dev = g_slice_new0 (struct object);
|
||||
dev->self = self;
|
||||
dev->id = id;
|
||||
dev->type = WP_TYPE_DEVICE;
|
||||
dev->spa_obj = g_steal_pointer (&spa_dev);
|
||||
dev->properties = g_steal_pointer (&props);
|
||||
dev->proxy = g_steal_pointer (&proxy);
|
||||
|
||||
/* Add device listener for events */
|
||||
ret = spa_device_add_listener (dev->spa_obj->interface,
|
||||
&dev->spa_obj->listener, &device_events, dev);
|
||||
if (ret < 0) {
|
||||
object_free (dev);
|
||||
g_set_error (error, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"failed to initialize device: %s", spa_strerror (ret));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* HACK this is very specific to the current alsa pcm profiles */
|
||||
if (self->flags & WP_MONITOR_FLAG_ACTIVATE_DEVICES)
|
||||
set_profile (dev->spa_obj->interface, 1);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static void
|
||||
object_free (struct object *obj)
|
||||
{
|
||||
g_debug ("WpMonitor:%p:%s free %s %u", obj->self, obj->self->factory_name,
|
||||
g_type_name (obj->type), obj->id);
|
||||
|
||||
g_list_free_full (obj->children, (GDestroyNotify) object_free);
|
||||
g_clear_object (&obj->proxy);
|
||||
|
||||
g_clear_pointer (&obj->spa_obj, wp_spa_object_unref);
|
||||
|
||||
g_clear_pointer (&obj->properties, wp_properties_unref);
|
||||
|
||||
g_slice_free (struct object, obj);
|
||||
}
|
||||
|
||||
/* WpMonitor */
|
||||
|
||||
static void
|
||||
wp_monitor_init (WpMonitor * self)
|
||||
{
|
||||
g_weak_ref_init (&self->core, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_monitor_finalize (GObject * object)
|
||||
{
|
||||
WpMonitor * self = WP_MONITOR (object);
|
||||
|
||||
wp_monitor_stop (self);
|
||||
|
||||
g_clear_pointer (&self->properties, wp_properties_unref);
|
||||
g_weak_ref_clear (&self->core);
|
||||
g_free (self->factory_name);
|
||||
|
||||
G_OBJECT_CLASS (wp_monitor_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_monitor_set_property (GObject * object, guint property_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpMonitor * self = WP_MONITOR (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_CORE:
|
||||
g_weak_ref_set (&self->core, g_value_get_object (value));
|
||||
break;
|
||||
case PROP_FACTORY_NAME:
|
||||
self->factory_name = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_PROPERTIES:
|
||||
self->properties = g_value_dup_boxed (value);
|
||||
break;
|
||||
case PROP_FLAGS:
|
||||
self->flags = (WpMonitorFlags) g_value_get_flags (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_monitor_get_property (GObject * object, guint property_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpMonitor * self = WP_MONITOR (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_CORE:
|
||||
g_value_take_object (value, g_weak_ref_get (&self->core));
|
||||
break;
|
||||
case PROP_FACTORY_NAME:
|
||||
g_value_set_string (value, self->factory_name);
|
||||
break;
|
||||
case PROP_PROPERTIES:
|
||||
g_value_set_boxed (value, self->properties);
|
||||
break;
|
||||
case PROP_FLAGS:
|
||||
g_value_set_flags (value, (guint) self->flags);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_monitor_class_init (WpMonitorClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
|
||||
object_class->finalize = wp_monitor_finalize;
|
||||
object_class->set_property = wp_monitor_set_property;
|
||||
object_class->get_property = wp_monitor_get_property;
|
||||
|
||||
/* Install the properties */
|
||||
g_object_class_install_property (object_class, PROP_CORE,
|
||||
g_param_spec_object ("core", "core", "The wireplumber core",
|
||||
WP_TYPE_CORE,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class, PROP_FACTORY_NAME,
|
||||
g_param_spec_string ("factory-name", "factory-name",
|
||||
"The factory name of the spa device", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class, PROP_PROPERTIES,
|
||||
g_param_spec_boxed ("properties", "properties",
|
||||
"Properties for the spa device", WP_TYPE_PROPERTIES,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
g_object_class_install_property (object_class, PROP_FLAGS,
|
||||
g_param_spec_flags ("flags", "flags",
|
||||
"Additional feature flags", WP_TYPE_MONITOR_FLAGS, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/**
|
||||
* WpMonitor::setup-device-props:
|
||||
* @self: the #WpMonitor
|
||||
* @device_props: the properties of the device to be created
|
||||
*
|
||||
* This signal allows the handler to modify the properties of a device
|
||||
* object before it is created.
|
||||
*/
|
||||
signals[SIG_SETUP_DEVICE_PROPS] = g_signal_new (
|
||||
"setup-device-props", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, 0,
|
||||
NULL, NULL, NULL, G_TYPE_NONE, 1, WP_TYPE_PROPERTIES);
|
||||
|
||||
/**
|
||||
* WpMonitor::setup-node-props:
|
||||
* @self: the #WpMonitor
|
||||
* @device_props: the properties of the parent device
|
||||
* @node_props: the properties of the node to be created
|
||||
*
|
||||
* This signal allows the handler to modify the properties of a node
|
||||
* object before it is created.
|
||||
*/
|
||||
signals[SIG_SETUP_NODE_PROPS] = g_signal_new (
|
||||
"setup-node-props", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, 0,
|
||||
NULL, NULL, NULL, G_TYPE_NONE, 2, WP_TYPE_PROPERTIES, WP_TYPE_PROPERTIES);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_monitor_new:
|
||||
* @core: the wireplumber core
|
||||
* @factory_name: the factory name of the spa device
|
||||
* @props: properties to pass to the spa device
|
||||
* @flags: additional feature flags
|
||||
*
|
||||
* Returns: (transfer full): the newly created monitor
|
||||
*/
|
||||
WpMonitor *
|
||||
wp_monitor_new (WpCore * core, const gchar * factory_name, WpProperties *props,
|
||||
WpMonitorFlags flags)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_CORE (core), NULL);
|
||||
g_return_val_if_fail (factory_name != NULL && *factory_name != '\0', NULL);
|
||||
|
||||
return g_object_new (WP_TYPE_MONITOR,
|
||||
"core", core,
|
||||
"factory-name", factory_name,
|
||||
"properties", props,
|
||||
"flags", flags,
|
||||
NULL);
|
||||
}
|
||||
|
||||
const gchar *
|
||||
wp_monitor_get_factory_name (WpMonitor *self)
|
||||
{
|
||||
g_return_val_if_fail (WP_IS_MONITOR (self), NULL);
|
||||
return self->factory_name;
|
||||
}
|
||||
|
||||
gboolean
|
||||
wp_monitor_start (WpMonitor *self, GError **error)
|
||||
{
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
g_autoptr (GError) err = NULL;
|
||||
|
||||
g_return_val_if_fail (WP_IS_MONITOR (self), FALSE);
|
||||
|
||||
core = g_weak_ref_get (&self->core);
|
||||
|
||||
g_debug ("WpMonitor:%p:%s starting monitor, flags 0x%x", self,
|
||||
self->factory_name, self->flags);
|
||||
|
||||
self->device = device_new (self, -1, self->factory_name, self->properties,
|
||||
&err);
|
||||
if (!self->device) {
|
||||
g_propagate_error (error, g_steal_pointer (&err));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
wp_monitor_stop (WpMonitor *self)
|
||||
{
|
||||
g_return_if_fail (WP_IS_MONITOR (self));
|
||||
|
||||
g_debug ("WpMonitor:%p:%s stopping monitor", self, self->factory_name);
|
||||
g_clear_pointer (&self->device, object_free);
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef __WIREPLUMBER_MONITOR_H__
|
||||
#define __WIREPLUMBER_MONITOR_H__
|
||||
|
||||
#include "core.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum { /*< flags, prefix=WP_MONITOR_FLAG_ >*/
|
||||
WP_MONITOR_FLAG_LOCAL_NODES = (1 << 0),
|
||||
WP_MONITOR_FLAG_USE_ADAPTER = (1 << 1),
|
||||
WP_MONITOR_FLAG_ACTIVATE_DEVICES = (1 << 2),
|
||||
} WpMonitorFlags;
|
||||
|
||||
#define WP_MONITOR_KEY_OBJECT_ID "wp.monitor.object.id"
|
||||
|
||||
#define WP_TYPE_MONITOR (wp_monitor_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_FINAL_TYPE (WpMonitor, wp_monitor, WP, MONITOR, GObject)
|
||||
|
||||
WP_API
|
||||
WpMonitor * wp_monitor_new (WpCore * core, const gchar * factory_name,
|
||||
WpProperties *props, WpMonitorFlags flags);
|
||||
|
||||
WP_API
|
||||
const gchar * wp_monitor_get_factory_name (WpMonitor *self);
|
||||
|
||||
WP_API
|
||||
WpMonitorFlags wp_monitor_get_flags (WpMonitor *self);
|
||||
|
||||
WP_API
|
||||
gboolean wp_monitor_start (WpMonitor *self, GError **error);
|
||||
|
||||
WP_API
|
||||
void wp_monitor_stop (WpMonitor *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
245
lib/wp/node.c
245
lib/wp/node.c
|
|
@ -1,26 +1,26 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* Copyright © 2019-2020 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include "node.h"
|
||||
#include "error.h"
|
||||
#include "private.h"
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <pipewire/impl.h>
|
||||
|
||||
struct _WpNode
|
||||
typedef struct _WpNodePrivate WpNodePrivate;
|
||||
struct _WpNodePrivate
|
||||
{
|
||||
WpProxy parent;
|
||||
struct pw_node_info *info;
|
||||
|
||||
/* The node proxy listener */
|
||||
struct spa_hook listener;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (WpNode, wp_node, WP_TYPE_PROXY)
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (WpNode, wp_node, WP_TYPE_PROXY)
|
||||
|
||||
static void
|
||||
wp_node_init (WpNode * self)
|
||||
|
|
@ -31,8 +31,9 @@ static void
|
|||
wp_node_finalize (GObject * object)
|
||||
{
|
||||
WpNode *self = WP_NODE (object);
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
|
||||
g_clear_pointer (&self->info, pw_node_info_free);
|
||||
g_clear_pointer (&priv->info, pw_node_info_free);
|
||||
|
||||
G_OBJECT_CLASS (wp_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
|
@ -40,13 +41,15 @@ wp_node_finalize (GObject * object)
|
|||
static gconstpointer
|
||||
wp_node_get_info (WpProxy * self)
|
||||
{
|
||||
return WP_NODE (self)->info;
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (WP_NODE (self));
|
||||
return priv->info;
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
wp_node_get_properties (WpProxy * self)
|
||||
{
|
||||
return wp_properties_new_wrap_dict (WP_NODE (self)->info->props);
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (WP_NODE (self));
|
||||
return wp_properties_new_wrap_dict (priv->info->props);
|
||||
}
|
||||
|
||||
static gint
|
||||
|
|
@ -94,14 +97,15 @@ static void
|
|||
node_event_info(void *data, const struct pw_node_info *info)
|
||||
{
|
||||
WpNode *self = WP_NODE (data);
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
|
||||
priv->info = pw_node_info_update (priv->info, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
|
||||
self->info = pw_node_info_update (self->info, info);
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
|
||||
if (info->change_mask & PW_NODE_CHANGE_MASK_PROPS)
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
}
|
||||
|
||||
static const struct pw_node_events node_events = {
|
||||
|
|
@ -114,8 +118,9 @@ static void
|
|||
wp_node_pw_proxy_created (WpProxy * proxy, struct pw_proxy * pw_proxy)
|
||||
{
|
||||
WpNode *self = WP_NODE (proxy);
|
||||
WpNodePrivate *priv = wp_node_get_instance_private (self);
|
||||
pw_node_add_listener ((struct pw_node *) pw_proxy,
|
||||
&self->listener, &node_events, self);
|
||||
&priv->listener, &node_events, self);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -126,6 +131,9 @@ wp_node_class_init (WpNodeClass * klass)
|
|||
|
||||
object_class->finalize = wp_node_finalize;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Node;
|
||||
proxy_class->pw_iface_version = PW_VERSION_NODE;
|
||||
|
||||
proxy_class->get_info = wp_node_get_info;
|
||||
proxy_class->get_properties = wp_node_get_properties;
|
||||
proxy_class->enum_params = wp_node_enum_params;
|
||||
|
|
@ -134,3 +142,214 @@ wp_node_class_init (WpNodeClass * klass)
|
|||
|
||||
proxy_class->pw_proxy_created = wp_node_pw_proxy_created;
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_node_new_from_factory:
|
||||
* @core: the wireplumber core
|
||||
* @factory_name: the pipewire factory name to construct the node
|
||||
* @properties: (nullable) (transfer full): the properties to pass to the factory
|
||||
*
|
||||
* Constructs a node on the PipeWire server by asking the remote factory
|
||||
* @factory_name to create it.
|
||||
*
|
||||
* Because of the nature of the PipeWire protocol, this operation completes
|
||||
* asynchronously at some point in the future. In order to find out when
|
||||
* this is done, you should call wp_proxy_augment(), requesting at least
|
||||
* %WP_PROXY_FEATURE_BOUND. When this feature is ready, the node is ready for
|
||||
* use on the server. If the node cannot be created, this augment operation
|
||||
* will fail.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): the new node or %NULL if the core
|
||||
* is not connected and therefore the node cannot be created
|
||||
*/
|
||||
WpNode *
|
||||
wp_node_new_from_factory (WpCore * core,
|
||||
const gchar * factory_name, WpProperties * properties)
|
||||
{
|
||||
g_autoptr (WpProperties) props = properties;
|
||||
WpNode *self = NULL;
|
||||
struct pw_core *pw_core = wp_core_get_pw_core (core);
|
||||
|
||||
if (!pw_core) {
|
||||
g_warning ("The WirePlumber core is not connected; node cannot be created");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self = g_object_new (WP_TYPE_NODE, "core", core, NULL);
|
||||
wp_proxy_set_pw_proxy (WP_PROXY (self), pw_core_create_object (pw_core,
|
||||
factory_name, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE,
|
||||
props ? wp_properties_peek_dict (props) : NULL, 0));
|
||||
return self;
|
||||
}
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_PW_IMPL_NODE,
|
||||
};
|
||||
|
||||
struct _WpImplNode
|
||||
{
|
||||
WpNode parent;
|
||||
struct pw_impl_node *pw_impl_node;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (WpImplNode, wp_impl_node, WP_TYPE_NODE)
|
||||
|
||||
static void
|
||||
wp_impl_node_init (WpImplNode * self)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_node_finalize (GObject * object)
|
||||
{
|
||||
WpImplNode *self = WP_IMPL_NODE (object);
|
||||
|
||||
g_clear_pointer (&self->pw_impl_node, pw_impl_node_destroy);
|
||||
|
||||
G_OBJECT_CLASS (wp_impl_node_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_node_set_property (GObject * object, guint property_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpImplNode *self = WP_IMPL_NODE (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_PW_IMPL_NODE:
|
||||
self->pw_impl_node = g_value_get_pointer (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_node_get_property (GObject * object, guint property_id, GValue * value,
|
||||
GParamSpec * pspec)
|
||||
{
|
||||
WpImplNode *self = WP_IMPL_NODE (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_PW_IMPL_NODE:
|
||||
g_value_set_pointer (value, self->pw_impl_node);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_node_augment (WpProxy * proxy, WpProxyFeatures features)
|
||||
{
|
||||
WpImplNode *self = WP_IMPL_NODE (proxy);
|
||||
|
||||
/* if any of the default features is requested, make sure BOUND
|
||||
is also requested, as they all depend on binding the pw_impl_node */
|
||||
if (features & WP_PROXY_FEATURES_STANDARD)
|
||||
features |= WP_PROXY_FEATURE_BOUND;
|
||||
|
||||
if (features & WP_PROXY_FEATURE_BOUND) {
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (proxy);
|
||||
struct pw_core *pw_core = wp_core_get_pw_core (core);
|
||||
|
||||
/* no pw_core -> we are not connected */
|
||||
if (!pw_core) {
|
||||
wp_proxy_augment_error (proxy, g_error_new (WP_DOMAIN_LIBRARY,
|
||||
WP_LIBRARY_ERROR_OPERATION_FAILED,
|
||||
"The WirePlumber core is not connected; "
|
||||
"object cannot be exported to PipeWire"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* export to get a proxy; feature will complete
|
||||
when the pw_proxy.bound event will be called.
|
||||
properties are NULL because they are not needed;
|
||||
remote-node uses the properties of the pw_impl_node */
|
||||
wp_proxy_set_pw_proxy (proxy, pw_core_export (pw_core,
|
||||
PW_TYPE_INTERFACE_Node, NULL, self->pw_impl_node, 0));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_impl_node_class_init (WpImplNodeClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpProxyClass *proxy_class = (WpProxyClass *) klass;
|
||||
|
||||
object_class->finalize = wp_impl_node_finalize;
|
||||
object_class->set_property = wp_impl_node_set_property;
|
||||
object_class->get_property = wp_impl_node_get_property;
|
||||
|
||||
proxy_class->augment = wp_impl_node_augment;
|
||||
|
||||
g_object_class_install_property (object_class, PROP_PW_IMPL_NODE,
|
||||
g_param_spec_pointer ("pw-impl-node", "pw-impl-node",
|
||||
"The actual node implementation, struct pw_impl_node *",
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_impl_node_new_wrap:
|
||||
* @core: the wireplumber core
|
||||
* @node: an existing pw_impl_node to wrap
|
||||
*
|
||||
* Returns: (transfer full): A new #WpImplNode wrapping @node
|
||||
*/
|
||||
WpImplNode *
|
||||
wp_impl_node_new_wrap (WpCore * core, struct pw_impl_node * node)
|
||||
{
|
||||
return g_object_new (WP_TYPE_IMPL_NODE,
|
||||
"core", core,
|
||||
"pw-impl-node", node,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* wp_impl_node_new_from_pw_factory:
|
||||
* @core: the wireplumber core
|
||||
* @factory_name: the name of the pipewire factory
|
||||
* @properties: (nullable) (transfer full): properties to be passed to node
|
||||
* constructor
|
||||
*
|
||||
* Constructs a new node, locally on this process, using the specified
|
||||
* @factory_name.
|
||||
*
|
||||
* To export this node to the PipeWire server, you need to call
|
||||
* wp_proxy_augment() requesting %WP_PROXY_FEATURE_BOUND and
|
||||
* wait for the operation to complete.
|
||||
*
|
||||
* Returns: (nullable) (transfer full): A new #WpImplNode wrapping the
|
||||
* node that was constructed by the factory, or %NULL if the factory
|
||||
* does not exist or was unable to construct the node
|
||||
*/
|
||||
WpImplNode *
|
||||
wp_impl_node_new_from_pw_factory (WpCore * core,
|
||||
const gchar * factory_name, WpProperties * properties)
|
||||
{
|
||||
g_autoptr (WpProperties) props = properties;
|
||||
struct pw_context *pw_context = wp_core_get_pw_context (core);
|
||||
struct pw_impl_factory *factory = NULL;
|
||||
struct pw_impl_node *node = NULL;
|
||||
|
||||
g_return_val_if_fail (pw_context != NULL, NULL);
|
||||
|
||||
factory = pw_context_find_factory (pw_context, factory_name);
|
||||
if (!factory) {
|
||||
g_warning ("pipewire factory '%s' not found", factory_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = pw_impl_factory_create_object (factory,
|
||||
NULL, PW_TYPE_INTERFACE_Node, PW_VERSION_NODE,
|
||||
props ? wp_properties_to_pw_properties (props) : NULL, 0);
|
||||
if (!node) {
|
||||
g_warning ("failed to create node from factory '%s'", factory_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return wp_impl_node_new_wrap (core, node);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,32 @@
|
|||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
struct pw_impl_node;
|
||||
|
||||
#define WP_TYPE_NODE (wp_node_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_FINAL_TYPE (WpNode, wp_node, WP, NODE, WpProxy)
|
||||
G_DECLARE_DERIVABLE_TYPE (WpNode, wp_node, WP, NODE, WpProxy)
|
||||
|
||||
struct _WpNodeClass
|
||||
{
|
||||
WpProxyClass parent_class;
|
||||
};
|
||||
|
||||
WP_API
|
||||
WpNode * wp_node_new_from_factory (WpCore * core,
|
||||
const gchar * factory_name, WpProperties * properties);
|
||||
|
||||
|
||||
#define WP_TYPE_IMPL_NODE (wp_impl_node_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_FINAL_TYPE (WpImplNode, wp_impl_node, WP, IMPL_NODE, WpNode)
|
||||
|
||||
WP_API
|
||||
WpImplNode * wp_impl_node_new_wrap (WpCore * core, struct pw_impl_node * node);
|
||||
|
||||
WP_API
|
||||
WpImplNode * wp_impl_node_new_from_pw_factory (WpCore * core,
|
||||
const gchar * factory_name, WpProperties * properties);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
|
|
|||
|
|
@ -82,12 +82,12 @@ port_event_info(void *data, const struct pw_port_info *info)
|
|||
WpPort *self = WP_PORT (data);
|
||||
|
||||
self->info = pw_port_info_update (self->info, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
|
||||
if (info->change_mask & PW_PORT_CHANGE_MASK_PROPS)
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
}
|
||||
|
||||
static const struct pw_port_events port_events = {
|
||||
|
|
@ -112,6 +112,9 @@ wp_port_class_init (WpPortClass * klass)
|
|||
|
||||
object_class->finalize = wp_port_finalize;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Port;
|
||||
proxy_class->pw_iface_version = PW_VERSION_PORT;
|
||||
|
||||
proxy_class->get_info = wp_port_get_info;
|
||||
proxy_class->get_properties = wp_port_get_properties;
|
||||
proxy_class->enum_params = wp_port_enum_params;
|
||||
|
|
|
|||
|
|
@ -86,11 +86,10 @@ void wp_object_manager_rm_object (WpObjectManager * self, GObject * object);
|
|||
|
||||
/* proxy */
|
||||
|
||||
void wp_proxy_local_object_destroy_for_type (const char * type,
|
||||
gpointer local_object);
|
||||
|
||||
WpProxy * wp_proxy_new_global (WpCore * core, WpGlobal * global);
|
||||
|
||||
void wp_proxy_set_pw_proxy (WpProxy * self, struct pw_proxy * proxy);
|
||||
|
||||
void wp_proxy_set_feature_ready (WpProxy * self, WpProxyFeatures feature);
|
||||
void wp_proxy_augment_error (WpProxy * self, GError * error);
|
||||
|
||||
|
|
|
|||
193
lib/wp/proxy.c
193
lib/wp/proxy.c
|
|
@ -36,13 +36,7 @@ struct _WpProxyPrivate
|
|||
{
|
||||
/* properties */
|
||||
GWeakRef core;
|
||||
|
||||
WpGlobal *global;
|
||||
|
||||
char *iface_type;
|
||||
guint32 iface_version;
|
||||
gpointer local_object;
|
||||
|
||||
struct pw_proxy *pw_proxy;
|
||||
|
||||
/* The proxy listener */
|
||||
|
|
@ -61,9 +55,6 @@ enum {
|
|||
PROP_GLOBAL,
|
||||
PROP_GLOBAL_PERMISSIONS,
|
||||
PROP_GLOBAL_PROPERTIES,
|
||||
PROP_INTERFACE_TYPE,
|
||||
PROP_INTERFACE_VERSION,
|
||||
PROP_LOCAL_OBJECT,
|
||||
PROP_FEATURES,
|
||||
PROP_PW_PROXY,
|
||||
PROP_INFO,
|
||||
|
|
@ -84,61 +75,30 @@ 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)
|
||||
|
||||
static struct {
|
||||
/* the pipewire interface type */
|
||||
const char * pw_type;
|
||||
/* the minimum interface version that the remote object must support */
|
||||
guint32 req_version;
|
||||
/* the _get_type() function of the subclass */
|
||||
GType (*get_type) (void);
|
||||
/* the destroy function of the local object, if any */
|
||||
void (*local_object_destroy) (gpointer);
|
||||
} types_assoc[] = {
|
||||
{ PW_TYPE_INTERFACE_Core, 0, wp_proxy_get_type, NULL },
|
||||
{ PW_TYPE_INTERFACE_Registry, 0, wp_proxy_get_type, NULL },
|
||||
{ PW_TYPE_INTERFACE_Node, 0, wp_node_get_type, (GDestroyNotify)pw_impl_node_destroy },
|
||||
{ PW_TYPE_INTERFACE_Port, 0, wp_port_get_type, NULL },
|
||||
{ PW_TYPE_INTERFACE_Factory, 0, wp_proxy_get_type, (GDestroyNotify)pw_impl_factory_destroy },
|
||||
{ PW_TYPE_INTERFACE_Link, 0, wp_link_get_type, (GDestroyNotify)pw_impl_link_destroy },
|
||||
{ PW_TYPE_INTERFACE_Client, 0, wp_client_get_type, (GDestroyNotify)pw_impl_client_destroy },
|
||||
{ PW_TYPE_INTERFACE_Module, 0, wp_proxy_get_type, (GDestroyNotify)pw_impl_module_destroy },
|
||||
{ PW_TYPE_INTERFACE_Device, 0, wp_device_get_type, (GDestroyNotify)pw_impl_device_destroy },
|
||||
{ PW_TYPE_INTERFACE_Metadata, 0, wp_proxy_get_type, NULL },
|
||||
{ PW_TYPE_INTERFACE_Session, 0, wp_proxy_session_get_type, NULL },
|
||||
{ PW_TYPE_INTERFACE_Endpoint, 0, wp_proxy_endpoint_get_type, NULL },
|
||||
{ PW_TYPE_INTERFACE_EndpointStream, 0, wp_proxy_get_type, NULL },
|
||||
{ PW_TYPE_INTERFACE_EndpointLink, 0, wp_proxy_get_type, NULL },
|
||||
{ PW_TYPE_INTERFACE_ClientNode, 0, wp_proxy_get_type, NULL },
|
||||
{ PW_TYPE_INTERFACE_ClientSession, 0, wp_proxy_get_type, NULL },
|
||||
{ PW_TYPE_INTERFACE_ClientEndpoint, 0, wp_proxy_get_type, NULL },
|
||||
};
|
||||
|
||||
/* find the subclass of WpProxy that can handle
|
||||
the given pipewire interface type of the given version */
|
||||
static inline GType
|
||||
wp_proxy_find_instance_type (const char * type, guint32 version)
|
||||
{
|
||||
for (gint i = 0; i < SPA_N_ELEMENTS (types_assoc); i++) {
|
||||
if (g_strcmp0 (types_assoc[i].pw_type, type) == 0 &&
|
||||
types_assoc[i].req_version <= version)
|
||||
return types_assoc[i].get_type ();
|
||||
g_autofree GType *children;
|
||||
guint n_children;
|
||||
|
||||
children = g_type_children (WP_TYPE_PROXY, &n_children);
|
||||
|
||||
for (gint i = 0; i < n_children; i++) {
|
||||
WpProxyClass *klass = (WpProxyClass *) g_type_class_ref (children[i]);
|
||||
if (g_strcmp0 (klass->pw_iface_type, type) == 0 &&
|
||||
klass->pw_iface_version == version) {
|
||||
g_type_class_unref (klass);
|
||||
return children[i];
|
||||
}
|
||||
|
||||
g_type_class_unref (klass);
|
||||
}
|
||||
|
||||
return WP_TYPE_PROXY;
|
||||
}
|
||||
|
||||
void
|
||||
wp_proxy_local_object_destroy_for_type (const char *type, gpointer local_object)
|
||||
{
|
||||
g_return_if_fail (local_object);
|
||||
|
||||
for (gint i = 0; i < SPA_N_ELEMENTS (types_assoc); i++) {
|
||||
if (g_strcmp0 (types_assoc[i].pw_type, type) == 0) {
|
||||
if (types_assoc[i].local_object_destroy)
|
||||
types_assoc[i].local_object_destroy (local_object);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
proxy_event_destroy (void *data)
|
||||
{
|
||||
|
|
@ -149,10 +109,10 @@ proxy_event_destroy (void *data)
|
|||
GHashTableIter iter;
|
||||
GTask *task;
|
||||
|
||||
g_debug ("%s:%p destroyed pw_proxy %p (%s; %s; %u)",
|
||||
G_OBJECT_TYPE_NAME (self), self, priv->pw_proxy, priv->iface_type,
|
||||
g_debug ("%s:%p destroyed pw_proxy %p (%s; %u)",
|
||||
G_OBJECT_TYPE_NAME (self), self, priv->pw_proxy,
|
||||
priv->global ? "global" : "not global",
|
||||
priv->global ? priv->global->id : 0);
|
||||
wp_proxy_get_bound_id (self));
|
||||
priv->pw_proxy = NULL;
|
||||
|
||||
g_signal_emit (self, wp_proxy_signals[SIGNAL_PW_PROXY_DESTROYED], 0);
|
||||
|
|
@ -196,11 +156,17 @@ static const struct pw_proxy_events proxy_events = {
|
|||
.bound = proxy_event_bound,
|
||||
};
|
||||
|
||||
static void
|
||||
wp_proxy_got_pw_proxy (WpProxy * self)
|
||||
void
|
||||
wp_proxy_set_pw_proxy (WpProxy * self, struct pw_proxy * proxy)
|
||||
{
|
||||
WpProxyPrivate *priv = wp_proxy_get_instance_private (self);
|
||||
|
||||
if (!proxy)
|
||||
return;
|
||||
|
||||
g_return_if_fail (priv->pw_proxy == NULL);
|
||||
priv->pw_proxy = proxy;
|
||||
|
||||
pw_proxy_add_listener (priv->pw_proxy, &priv->listener, &proxy_events,
|
||||
self);
|
||||
|
||||
|
|
@ -223,17 +189,6 @@ wp_proxy_init (WpProxy * self)
|
|||
NULL, g_object_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_constructed (GObject * object)
|
||||
{
|
||||
WpProxy *self = WP_PROXY (object);
|
||||
WpProxyPrivate *priv = wp_proxy_get_instance_private (self);
|
||||
|
||||
/* native proxy was passed in the constructor, declare it as ready */
|
||||
if (priv->pw_proxy)
|
||||
wp_proxy_got_pw_proxy (self);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_proxy_dispose (GObject * object)
|
||||
{
|
||||
|
|
@ -257,14 +212,6 @@ wp_proxy_finalize (GObject * object)
|
|||
WpProxy *self = WP_PROXY (object);
|
||||
WpProxyPrivate *priv = wp_proxy_get_instance_private (self);
|
||||
|
||||
/* Clear the local object */
|
||||
if (priv->local_object) {
|
||||
wp_proxy_local_object_destroy_for_type (priv->iface_type,
|
||||
priv->local_object);
|
||||
priv->local_object = NULL;
|
||||
}
|
||||
|
||||
g_clear_pointer (&priv->iface_type, g_free);
|
||||
g_clear_pointer (&priv->augment_tasks, g_ptr_array_unref);
|
||||
g_clear_pointer (&priv->global, wp_global_unref);
|
||||
g_weak_ref_clear (&priv->core);
|
||||
|
|
@ -277,7 +224,8 @@ static void
|
|||
wp_proxy_set_property (GObject * object, guint property_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpProxyPrivate *priv = wp_proxy_get_instance_private (WP_PROXY(object));
|
||||
WpProxy *self = WP_PROXY (object);
|
||||
WpProxyPrivate *priv = wp_proxy_get_instance_private (self);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_CORE:
|
||||
|
|
@ -286,17 +234,8 @@ wp_proxy_set_property (GObject * object, guint property_id,
|
|||
case PROP_GLOBAL:
|
||||
priv->global = g_value_dup_boxed (value);
|
||||
break;
|
||||
case PROP_INTERFACE_TYPE:
|
||||
priv->iface_type = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_INTERFACE_VERSION:
|
||||
priv->iface_version = g_value_get_uint (value);
|
||||
break;
|
||||
case PROP_LOCAL_OBJECT:
|
||||
priv->local_object = g_value_get_pointer (value);
|
||||
break;
|
||||
case PROP_PW_PROXY:
|
||||
priv->pw_proxy = g_value_get_pointer (value);
|
||||
wp_proxy_set_pw_proxy (self, g_value_get_pointer (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
|
|
@ -321,15 +260,6 @@ wp_proxy_get_property (GObject * object, guint property_id, GValue * value,
|
|||
case PROP_GLOBAL_PROPERTIES:
|
||||
g_value_set_boxed (value, priv->global ? priv->global->properties : NULL);
|
||||
break;
|
||||
case PROP_INTERFACE_TYPE:
|
||||
g_value_set_string (value, priv->iface_type);
|
||||
break;
|
||||
case PROP_INTERFACE_VERSION:
|
||||
g_value_set_uint (value, priv->iface_version);
|
||||
break;
|
||||
case PROP_LOCAL_OBJECT:
|
||||
g_value_set_pointer (value, priv->local_object);
|
||||
break;
|
||||
case PROP_FEATURES:
|
||||
g_value_set_flags (value, priv->ft_ready);
|
||||
break;
|
||||
|
|
@ -377,10 +307,9 @@ wp_proxy_default_augment (WpProxy * self, WpProxyFeatures features)
|
|||
g_return_if_fail (core);
|
||||
|
||||
/* bind */
|
||||
priv->pw_proxy = pw_registry_bind (
|
||||
wp_core_get_pw_registry (core), priv->global->id,
|
||||
priv->iface_type, priv->iface_version, 0);
|
||||
wp_proxy_got_pw_proxy (self);
|
||||
wp_proxy_set_pw_proxy (self, pw_registry_bind (
|
||||
wp_core_get_pw_registry (core), priv->global->id,
|
||||
priv->global->type, priv->global->version, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -389,7 +318,6 @@ wp_proxy_class_init (WpProxyClass * klass)
|
|||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
|
||||
object_class->constructed = wp_proxy_constructed;
|
||||
object_class->dispose = wp_proxy_dispose;
|
||||
object_class->finalize = wp_proxy_finalize;
|
||||
object_class->get_property = wp_proxy_get_property;
|
||||
|
|
@ -418,21 +346,6 @@ wp_proxy_class_init (WpProxyClass * klass)
|
|||
"The pipewire global properties", WP_TYPE_PROPERTIES,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_INTERFACE_TYPE,
|
||||
g_param_spec_string ("interface-type", "interface-type",
|
||||
"The pipewire interface type", NULL,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_INTERFACE_VERSION,
|
||||
g_param_spec_uint ("interface-version", "interface-version",
|
||||
"The pipewire interface version", 0, G_MAXUINT, 0,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_LOCAL_OBJECT,
|
||||
g_param_spec_pointer ("local-object", "local-object",
|
||||
"The local object this proxy refers to, if any",
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_FEATURES,
|
||||
g_param_spec_flags ("features", "features",
|
||||
"The ready WpProxyFeatures on this proxy", WP_TYPE_PROXY_FEATURES, 0,
|
||||
|
|
@ -440,7 +353,7 @@ wp_proxy_class_init (WpProxyClass * klass)
|
|||
|
||||
g_object_class_install_property (object_class, PROP_PW_PROXY,
|
||||
g_param_spec_pointer ("pw-proxy", "pw-proxy", "The struct pw_proxy *",
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (object_class, PROP_INFO,
|
||||
g_param_spec_pointer ("info", "info", "The native info structure",
|
||||
|
|
@ -480,22 +393,6 @@ wp_proxy_new_global (WpCore * core, WpGlobal * global)
|
|||
return g_object_new (gtype,
|
||||
"core", core,
|
||||
"global", global,
|
||||
"interface-type", global->type,
|
||||
"interface-version", global->version,
|
||||
NULL);
|
||||
}
|
||||
|
||||
WpProxy *
|
||||
wp_proxy_new_wrap (WpCore * core, struct pw_proxy * proxy, const char *type,
|
||||
guint32 version, gpointer local_object)
|
||||
{
|
||||
GType gtype = wp_proxy_find_instance_type (type, version);
|
||||
return g_object_new (gtype,
|
||||
"core", core,
|
||||
"pw-proxy", proxy,
|
||||
"interface-type", type,
|
||||
"interface-version", version,
|
||||
"local-object", local_object,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
|
@ -653,28 +550,6 @@ wp_proxy_get_global_properties (WpProxy * self)
|
|||
return wp_properties_ref (priv->global->properties);
|
||||
}
|
||||
|
||||
const char *
|
||||
wp_proxy_get_interface_type (WpProxy * self)
|
||||
{
|
||||
WpProxyPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (WP_IS_PROXY (self), 0);
|
||||
|
||||
priv = wp_proxy_get_instance_private (self);
|
||||
return priv->iface_type;
|
||||
}
|
||||
|
||||
guint32
|
||||
wp_proxy_get_interface_version (WpProxy * self)
|
||||
{
|
||||
WpProxyPrivate *priv;
|
||||
|
||||
g_return_val_if_fail (WP_IS_PROXY (self), 0);
|
||||
|
||||
priv = wp_proxy_get_instance_private (self);
|
||||
return priv->iface_version;
|
||||
}
|
||||
|
||||
struct pw_proxy *
|
||||
wp_proxy_get_pw_proxy (WpProxy * self)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,6 +20,16 @@ struct pw_proxy;
|
|||
struct spa_pod;
|
||||
typedef struct _WpCore WpCore;
|
||||
|
||||
/**
|
||||
* WpProxyFeatures:
|
||||
*
|
||||
* Flags that specify functionality that is available on this class.
|
||||
* Use wp_proxy_augment() to enable more features and wp_proxy_get_features()
|
||||
* to find out which features are already enabled.
|
||||
*
|
||||
* Subclasses may also specify additional features that can be ORed with these
|
||||
* ones and they can also be enabled with wp_proxy_augment().
|
||||
*/
|
||||
typedef enum { /*< flags >*/
|
||||
WP_PROXY_FEATURE_PW_PROXY = (1 << 0),
|
||||
WP_PROXY_FEATURE_INFO = (1 << 1),
|
||||
|
|
@ -28,6 +38,19 @@ typedef enum { /*< flags >*/
|
|||
WP_PROXY_FEATURE_LAST = (1 << 5), /*< skip >*/
|
||||
} WpProxyFeatures;
|
||||
|
||||
/**
|
||||
* WP_PROXY_FEATURES_STANDARD:
|
||||
*
|
||||
* A constant set of features that contains the standard features that are
|
||||
* available in the #WpProxy class. The standard features are usually all
|
||||
* enabled at once, even if not requested explicitly. It is a good practice,
|
||||
* though, to enable only the features that you actually need. This leaves
|
||||
* room for optimizations in the #WpProxy class.
|
||||
*/
|
||||
#define WP_PROXY_FEATURES_STANDARD \
|
||||
(WP_PROXY_FEATURE_PW_PROXY | WP_PROXY_FEATURE_INFO | WP_PROXY_FEATURE_BOUND)
|
||||
|
||||
|
||||
#define WP_TYPE_PROXY (wp_proxy_get_type ())
|
||||
WP_API
|
||||
G_DECLARE_DERIVABLE_TYPE (WpProxy, wp_proxy, WP, PROXY, GObject)
|
||||
|
|
@ -37,6 +60,9 @@ struct _WpProxyClass
|
|||
{
|
||||
GObjectClass parent_class;
|
||||
|
||||
const gchar * pw_iface_type;
|
||||
guint32 pw_iface_version;
|
||||
|
||||
void (*augment) (WpProxy *self, WpProxyFeatures features);
|
||||
|
||||
gconstpointer (*get_info) (WpProxy * self);
|
||||
|
|
@ -56,10 +82,6 @@ struct _WpProxyClass
|
|||
guint32 next, const struct spa_pod *param);
|
||||
};
|
||||
|
||||
WP_API
|
||||
WpProxy * wp_proxy_new_wrap (WpCore * core, struct pw_proxy * proxy,
|
||||
const char *type, guint32 version, gpointer local_object);
|
||||
|
||||
/* features API */
|
||||
|
||||
WP_API
|
||||
|
|
@ -87,14 +109,6 @@ guint32 wp_proxy_get_global_permissions (WpProxy * self);
|
|||
WP_API
|
||||
WpProperties * wp_proxy_get_global_properties (WpProxy * self);
|
||||
|
||||
/* the pipewire type & version */
|
||||
|
||||
WP_API
|
||||
const char * wp_proxy_get_interface_type (WpProxy * self);
|
||||
|
||||
WP_API
|
||||
guint32 wp_proxy_get_interface_version (WpProxy * self);
|
||||
|
||||
/* native pw_proxy object getter (requires FEATURE_PW_PROXY) */
|
||||
|
||||
WP_API
|
||||
|
|
|
|||
|
|
@ -203,12 +203,12 @@ session_event_info (void *data, const struct pw_session_info *info)
|
|||
WpProxySession *self = WP_PROXY_SESSION (data);
|
||||
|
||||
self->info = session_info_update (self->info, &self->properties, info);
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
|
||||
g_object_notify (G_OBJECT (self), "info");
|
||||
|
||||
if (info->change_mask & PW_SESSION_CHANGE_MASK_PROPS)
|
||||
g_object_notify (G_OBJECT (self), "properties");
|
||||
|
||||
wp_proxy_set_feature_ready (WP_PROXY (self), WP_PROXY_FEATURE_INFO);
|
||||
}
|
||||
|
||||
static const struct pw_session_events session_events = {
|
||||
|
|
@ -318,6 +318,9 @@ wp_proxy_session_class_init (WpProxySessionClass * klass)
|
|||
|
||||
object_class->finalize = wp_proxy_session_finalize;
|
||||
|
||||
proxy_class->pw_iface_type = PW_TYPE_INTERFACE_Session;
|
||||
proxy_class->pw_iface_version = PW_VERSION_SESSION;
|
||||
|
||||
proxy_class->augment = wp_proxy_session_augment;
|
||||
proxy_class->get_info = wp_proxy_session_get_info;
|
||||
proxy_class->get_properties = wp_proxy_session_get_properties;
|
||||
|
|
@ -504,18 +507,18 @@ wp_exported_session_export (WpExported * self)
|
|||
wp_exported_session_get_instance_private (WP_EXPORTED_SESSION (self));
|
||||
g_autoptr (WpCore) core = wp_exported_get_core (self);
|
||||
struct pw_client_session *pw_proxy = NULL;
|
||||
struct pw_core *pw_core = wp_core_get_pw_core (core);
|
||||
|
||||
/* make sure these props are not present; they are added by the server */
|
||||
wp_properties_set (priv->properties, PW_KEY_OBJECT_ID, NULL);
|
||||
wp_properties_set (priv->properties, PW_KEY_CLIENT_ID, NULL);
|
||||
wp_properties_set (priv->properties, PW_KEY_FACTORY_ID, NULL);
|
||||
|
||||
priv->client_sess = wp_core_create_remote_object (core, "client-session",
|
||||
pw_proxy = pw_core_create_object (pw_core, "client-session",
|
||||
PW_TYPE_INTERFACE_ClientSession, PW_VERSION_CLIENT_SESSION,
|
||||
priv->properties);
|
||||
|
||||
pw_proxy = (struct pw_client_session *) wp_proxy_get_pw_proxy (
|
||||
priv->client_sess);
|
||||
wp_properties_peek_dict (priv->properties), 0);
|
||||
priv->client_sess = g_object_new (WP_TYPE_PROXY, "core", core,
|
||||
"pw-proxy", pw_proxy, NULL);
|
||||
|
||||
pw_client_session_add_listener (pw_proxy, &priv->listener,
|
||||
&client_session_events, self);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
#include "factory.h"
|
||||
#include "link.h"
|
||||
#include "module.h"
|
||||
#include "monitor.h"
|
||||
#include "node.h"
|
||||
#include "object-manager.h"
|
||||
#include "policy.h"
|
||||
|
|
|
|||
|
|
@ -39,33 +39,45 @@ static guint signals[N_SIGNALS];
|
|||
G_DEFINE_TYPE (WpConfigStaticNodesContext, wp_config_static_nodes_context,
|
||||
G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
on_node_created (GObject * proxy, GAsyncResult * res, gpointer user_data)
|
||||
{
|
||||
WpConfigStaticNodesContext *self = user_data;
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
if (!wp_proxy_augment_finish (WP_PROXY (proxy), res, &error)) {
|
||||
g_warning ("WpConfigStaticNodesContext:%p: failed to export node: %s",
|
||||
self, error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
g_ptr_array_add (self->static_nodes, g_object_ref (proxy));
|
||||
|
||||
/* Emit the node-created signal */
|
||||
g_signal_emit (self, signals[SIGNAL_NODE_CREATED], 0, proxy);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_static_nodes_context_create_node (WpConfigStaticNodesContext *self,
|
||||
const struct WpParserNodeData *node_data)
|
||||
{
|
||||
g_autoptr (WpProxy) node_proxy = NULL;
|
||||
g_autoptr (WpProxy) node = NULL;
|
||||
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
|
||||
g_return_if_fail (core);
|
||||
|
||||
/* Create the node */
|
||||
node_proxy = node_data->n.local ?
|
||||
wp_core_create_local_object (core, node_data->n.factory,
|
||||
PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, node_data->n.props) :
|
||||
wp_core_create_remote_object (core, node_data->n.factory,
|
||||
PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, node_data->n.props);
|
||||
if (!node_proxy) {
|
||||
g_warning ("WpConfigStaticNodesContext:%p: failed to create node: %s", self,
|
||||
g_strerror (errno));
|
||||
node = node_data->n.local ?
|
||||
(WpProxy *) wp_impl_node_new_from_pw_factory (core, node_data->n.factory,
|
||||
wp_properties_ref (node_data->n.props)) :
|
||||
(WpProxy *) wp_node_new_from_factory (core, node_data->n.factory,
|
||||
wp_properties_ref (node_data->n.props));
|
||||
if (!node) {
|
||||
g_warning ("WpConfigStaticNodesContext:%p: failed to create node", self);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Add the node to the array */
|
||||
g_ptr_array_add (self->static_nodes, g_object_ref (node_proxy));
|
||||
g_debug ("WpConfigStaticNodesContext:%p: added static node: %s", self,
|
||||
node_data->n.factory);
|
||||
|
||||
/* Emit the node-created signal */
|
||||
g_signal_emit (self, signals[SIGNAL_NODE_CREATED], 0, node_proxy);
|
||||
/* export to pipewire by requesting FEATURE_BOUND */
|
||||
wp_proxy_augment (node, WP_PROXY_FEATURE_BOUND, NULL, on_node_created, self);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -12,9 +12,40 @@
|
|||
#include <pipewire/pipewire.h>
|
||||
#include <spa/utils/keys.h>
|
||||
#include <spa/monitor/device.h>
|
||||
#include <spa/pod/builder.h>
|
||||
|
||||
G_DEFINE_QUARK (wp-module-monitor-id, id);
|
||||
G_DEFINE_QUARK (wp-module-monitor-children, children);
|
||||
|
||||
typedef enum {
|
||||
FLAG_LOCAL_NODES = (1 << 0),
|
||||
FLAG_USE_ADAPTER = (1 << 1),
|
||||
FLAG_ACTIVATE_DEVICES = (1 << 2),
|
||||
} MonitorFlags;
|
||||
|
||||
static const struct {
|
||||
MonitorFlags flag;
|
||||
const gchar *name;
|
||||
} flag_names[] = {
|
||||
{ FLAG_LOCAL_NODES, "local-nodes" },
|
||||
{ FLAG_USE_ADAPTER, "use-adapter" },
|
||||
{ FLAG_ACTIVATE_DEVICES, "activate-devices" }
|
||||
};
|
||||
|
||||
struct module_data
|
||||
{
|
||||
WpSpaDevice *monitor;
|
||||
gchar *factory;
|
||||
MonitorFlags flags;
|
||||
};
|
||||
|
||||
static void on_object_info (WpSpaDevice * device,
|
||||
guint id, GType type, const gchar * spa_factory,
|
||||
WpProperties * props, WpProperties * parent_props,
|
||||
struct module_data * data);
|
||||
|
||||
static void
|
||||
setup_device_props (WpMonitor *self, WpProperties *p, WpModule *module)
|
||||
setup_device_props (WpProperties *p)
|
||||
{
|
||||
const gchar *s, *d, *api;
|
||||
|
||||
|
|
@ -24,7 +55,7 @@ setup_device_props (WpMonitor *self, WpProperties *p, WpModule *module)
|
|||
if (!wp_properties_get (p, SPA_KEY_DEVICE_NAME)) {
|
||||
if ((s = wp_properties_get (p, SPA_KEY_DEVICE_BUS_ID)) == NULL) {
|
||||
if ((s = wp_properties_get (p, SPA_KEY_DEVICE_BUS_PATH)) == NULL) {
|
||||
s = wp_properties_get (p, WP_MONITOR_KEY_OBJECT_ID);
|
||||
s = "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,8 +133,7 @@ setup_device_props (WpMonitor *self, WpProperties *p, WpModule *module)
|
|||
}
|
||||
|
||||
static void
|
||||
setup_node_props (WpMonitor *self, WpProperties *dev_props,
|
||||
WpProperties *node_props, WpModule *module)
|
||||
setup_node_props (WpProperties *dev_props, WpProperties *node_props)
|
||||
{
|
||||
const gchar *api, *name, *description, *factory;
|
||||
|
||||
|
|
@ -135,7 +165,7 @@ setup_node_props (WpMonitor *self, WpProperties *dev_props,
|
|||
|
||||
/* get some strings that we are going to need below */
|
||||
api = wp_properties_get (node_props, SPA_KEY_DEVICE_API);
|
||||
factory = wp_properties_get (node_props, PW_KEY_FACTORY_NAME);
|
||||
factory = wp_properties_get (node_props, SPA_KEY_FACTORY_NAME);
|
||||
|
||||
name = wp_properties_get (node_props, SPA_KEY_DEVICE_NAME);
|
||||
if (G_UNLIKELY (!name))
|
||||
|
|
@ -200,51 +230,201 @@ setup_node_props (WpMonitor *self, WpProperties *dev_props,
|
|||
}
|
||||
|
||||
static void
|
||||
start_monitor (WpMonitor *monitor)
|
||||
augment_done (GObject * proxy, GAsyncResult * res, gpointer user_data)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
if (!wp_monitor_start (monitor, &error)) {
|
||||
g_message ("Failed to start monitor: %s", error->message);
|
||||
if (!wp_proxy_augment_finish (WP_PROXY (proxy), res, &error)) {
|
||||
g_warning ("%s", error->message);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
free_children (GList * children)
|
||||
{
|
||||
g_list_free_full (children, g_object_unref);
|
||||
}
|
||||
|
||||
static void
|
||||
find_child (GObject * parent, guint32 id, GList ** children, GList ** link,
|
||||
GObject ** child)
|
||||
{
|
||||
*children = g_object_steal_qdata (parent, children_quark ());
|
||||
|
||||
/* Find the child */
|
||||
for (*link = *children; *link != NULL; *link = g_list_next (*link)) {
|
||||
*child = G_OBJECT ((*link)->data);
|
||||
guint32 child_id = GPOINTER_TO_UINT (g_object_get_qdata (*child, id_quark ()));
|
||||
if (id == child_id)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
create_node (struct module_data * data, WpProxy * parent, GList ** children,
|
||||
guint id, const gchar * spa_factory, WpProperties * props,
|
||||
WpProperties * parent_props)
|
||||
{
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (parent);
|
||||
WpProxy *node = NULL;
|
||||
const gchar *pw_factory_name;
|
||||
|
||||
g_debug ("module-monitor:%p:%s new node %u (%s)", data, data->factory, id,
|
||||
spa_factory);
|
||||
|
||||
/* use the adapter instead of spa-node-factory if requested */
|
||||
pw_factory_name =
|
||||
(data->flags & FLAG_USE_ADAPTER) ? "adapter" : "spa-node-factory";
|
||||
|
||||
props = wp_properties_copy (props);
|
||||
wp_properties_set (props, SPA_KEY_FACTORY_NAME, spa_factory);
|
||||
setup_node_props (parent_props, props);
|
||||
|
||||
/* create the node */
|
||||
node = (data->flags & FLAG_LOCAL_NODES) ?
|
||||
(WpProxy *) wp_impl_node_new_from_pw_factory (core, pw_factory_name, props) :
|
||||
(WpProxy *) wp_node_new_from_factory (core, pw_factory_name, props);
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
/* export to pipewire by requesting FEATURE_BOUND */
|
||||
wp_proxy_augment (node, WP_PROXY_FEATURE_BOUND, NULL, augment_done, NULL);
|
||||
|
||||
g_object_set_qdata (G_OBJECT (node), id_quark (), GUINT_TO_POINTER (id));
|
||||
*children = g_list_prepend (*children, node);
|
||||
}
|
||||
|
||||
static void
|
||||
device_created (GObject * proxy, GAsyncResult * res, gpointer user_data)
|
||||
{
|
||||
struct module_data * data = user_data;
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
if (!wp_proxy_augment_finish (WP_PROXY (proxy), res, &error)) {
|
||||
g_warning ("%s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data->flags & FLAG_ACTIVATE_DEVICES) {
|
||||
char buf[1024];
|
||||
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
||||
|
||||
wp_proxy_set_param (WP_PROXY (proxy),
|
||||
SPA_PARAM_Profile, 0,
|
||||
spa_pod_builder_add_object (&b,
|
||||
SPA_TYPE_OBJECT_ParamProfile, 0,
|
||||
SPA_PARAM_PROFILE_index, SPA_POD_Int (1)));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
create_device (struct module_data * data, WpProxy * parent, GList ** children,
|
||||
guint id, const gchar * spa_factory, WpProperties * props)
|
||||
{
|
||||
g_autoptr (WpCore) core = wp_proxy_get_core (parent);
|
||||
WpSpaDevice *device;
|
||||
|
||||
g_debug ("module-monitor:%p:%s new device %u", data, data->factory, id);
|
||||
|
||||
props = wp_properties_copy (props);
|
||||
setup_device_props (props);
|
||||
|
||||
if (!(device = wp_spa_device_new_from_spa_factory (core, spa_factory, props)))
|
||||
return;
|
||||
|
||||
g_signal_connect (device, "object-info", (GCallback) on_object_info, data);
|
||||
wp_proxy_augment (WP_PROXY (device),
|
||||
WP_PROXY_FEATURE_BOUND | WP_SPA_DEVICE_FEATURE_ACTIVE,
|
||||
NULL, device_created, data);
|
||||
|
||||
g_object_set_qdata (G_OBJECT (device), id_quark (), GUINT_TO_POINTER (id));
|
||||
*children = g_list_prepend (*children, device);
|
||||
}
|
||||
|
||||
static void
|
||||
on_object_info (WpSpaDevice * device,
|
||||
guint id, GType type, const gchar * spa_factory,
|
||||
WpProperties * props, WpProperties * parent_props,
|
||||
struct module_data * data)
|
||||
{
|
||||
GList *children = NULL;
|
||||
GList *link = NULL;
|
||||
GObject *child = NULL;
|
||||
|
||||
/* Find the child */
|
||||
find_child (G_OBJECT (device), id, &children, &link, &child);
|
||||
|
||||
/* new object, construct... */
|
||||
if (type != G_TYPE_NONE && !link) {
|
||||
if (type == WP_TYPE_DEVICE) {
|
||||
create_device (data, WP_PROXY (device), &children, id, spa_factory, props);
|
||||
} else if (type == WP_TYPE_NODE) {
|
||||
create_node (data, WP_PROXY (device), &children, id, spa_factory, props,
|
||||
parent_props);
|
||||
} else {
|
||||
g_debug ("module-monitor:%p:%s got device object-info for unknown object "
|
||||
"type %s", data, data->factory, g_type_name (type));
|
||||
}
|
||||
}
|
||||
/* object removed, delete... */
|
||||
else if (type == G_TYPE_NONE && link) {
|
||||
g_object_unref (child);
|
||||
children = g_list_delete_link (children, link);
|
||||
}
|
||||
|
||||
/* put back the children */
|
||||
g_object_set_qdata_full (G_OBJECT (device), children_quark (), children,
|
||||
(GDestroyNotify) free_children);
|
||||
}
|
||||
|
||||
static void
|
||||
start_monitor (WpSpaDevice * monitor)
|
||||
{
|
||||
/* no FEATURE_BOUND here; exporting the monitor device is buggy */
|
||||
wp_proxy_augment (WP_PROXY (monitor),
|
||||
WP_SPA_DEVICE_FEATURE_ACTIVE,
|
||||
NULL, augment_done, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
module_destroy (gpointer d)
|
||||
{
|
||||
struct module_data *data = d;
|
||||
|
||||
g_clear_object (&data->monitor);
|
||||
g_free (data->factory);
|
||||
|
||||
g_slice_free (struct module_data, data);
|
||||
}
|
||||
|
||||
WP_PLUGIN_EXPORT void
|
||||
wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
|
||||
{
|
||||
WpMonitor *monitor;
|
||||
const gchar *factory = NULL;
|
||||
WpMonitorFlags flags = 0;
|
||||
GVariantIter *iter;
|
||||
struct module_data *data = g_slice_new0 (struct module_data);
|
||||
wp_module_set_destroy_callback (module, module_destroy, data);
|
||||
|
||||
if (!g_variant_lookup (args, "factory", "&s", &factory)) {
|
||||
if (!g_variant_lookup (args, "factory", "s", &data->factory)) {
|
||||
g_message ("Failed to load monitor: no 'factory' key specified");
|
||||
return;
|
||||
}
|
||||
|
||||
GVariantIter *iter;
|
||||
if (g_variant_lookup (args, "flags", "as", &iter)) {
|
||||
gchar *flag_str = NULL;
|
||||
GFlagsValue *flag_val = NULL;
|
||||
GFlagsClass *flag_class = g_type_class_ref (WP_TYPE_MONITOR_FLAGS);
|
||||
|
||||
while (g_variant_iter_loop (iter, "s", &flag_str)) {
|
||||
flag_val = g_flags_get_value_by_nick (flag_class, flag_str);
|
||||
if (flag_val)
|
||||
flags |= flag_val->value;
|
||||
for (gint i = 0; i < SPA_N_ELEMENTS (flag_names); i++) {
|
||||
if (!g_strcmp0 (flag_str, flag_names[i].name))
|
||||
data->flags |= flag_names[i].flag;
|
||||
}
|
||||
}
|
||||
g_variant_iter_free (iter);
|
||||
}
|
||||
|
||||
monitor = wp_monitor_new (core, factory, NULL, flags);
|
||||
|
||||
g_signal_connect (monitor, "setup-device-props",
|
||||
(GCallback) setup_device_props, module);
|
||||
g_signal_connect (monitor, "setup-node-props",
|
||||
(GCallback) setup_node_props, module);
|
||||
|
||||
wp_module_set_destroy_callback (module, g_object_unref, monitor);
|
||||
data->monitor = wp_spa_device_new_from_spa_factory (core, data->factory,
|
||||
NULL);
|
||||
g_signal_connect (data->monitor, "object-info", (GCallback) on_object_info,
|
||||
data);
|
||||
|
||||
/* Start the monitor when the connected callback is triggered */
|
||||
g_signal_connect_object (core, "connected",
|
||||
(GCallback) start_monitor, monitor, G_CONNECT_SWAPPED);
|
||||
g_signal_connect_object (core, "connected", (GCallback) start_monitor,
|
||||
data->monitor, G_CONNECT_SWAPPED);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ create_link_cb (WpProperties *props, gpointer user_data)
|
|||
{
|
||||
WpAudioConvert *self = WP_AUDIO_CONVERT (user_data);
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
WpProxy *proxy;
|
||||
WpLink *link;
|
||||
|
||||
core = wp_audio_stream_get_core (WP_AUDIO_STREAM (self));
|
||||
g_return_if_fail (core);
|
||||
|
|
@ -57,10 +57,10 @@ create_link_cb (WpProperties *props, gpointer user_data)
|
|||
wp_properties_set (props, PW_KEY_LINK_PASSIVE, "1");
|
||||
|
||||
/* Create the link */
|
||||
proxy = wp_core_create_remote_object(core, "link-factory",
|
||||
PW_TYPE_INTERFACE_Link, PW_VERSION_LINK, props);
|
||||
g_return_if_fail (proxy);
|
||||
g_ptr_array_add(self->link_proxies, proxy);
|
||||
link = wp_link_new_from_factory (core, "link-factory",
|
||||
wp_properties_ref (props));
|
||||
g_return_if_fail (link);
|
||||
g_ptr_array_add(self->link_proxies, link);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -177,8 +177,8 @@ wp_audio_convert_init_async (GAsyncInitable *initable, int io_priority,
|
|||
wp_properties_set (props, "factory.name", SPA_NAME_AUDIO_CONVERT);
|
||||
|
||||
/* Create the proxy */
|
||||
proxy = wp_core_create_remote_object (core, "spa-node-factory",
|
||||
PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, props);
|
||||
proxy = (WpProxy *) wp_node_new_from_factory (core, "spa-node-factory",
|
||||
g_steal_pointer (&props));
|
||||
g_return_if_fail (proxy);
|
||||
|
||||
g_object_set (self, "node", proxy, NULL);
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ wp_audio_stream_init_async (GAsyncInitable *initable, int io_priority,
|
|||
|
||||
g_return_if_fail (priv->proxy);
|
||||
wp_proxy_augment (WP_PROXY (priv->proxy),
|
||||
WP_PROXY_FEATURE_PW_PROXY | WP_PROXY_FEATURE_INFO, NULL,
|
||||
WP_PROXY_FEATURES_STANDARD, cancellable,
|
||||
(GAsyncReadyCallback) on_node_proxy_augmented, self);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -170,21 +170,20 @@ create_link_cb (WpProperties *props, gpointer user_data)
|
|||
{
|
||||
WpPipewireSimpleEndpointLink *self = WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK(user_data);
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
WpProxy *proxy;
|
||||
WpLink *link;
|
||||
|
||||
core = g_weak_ref_get (&self->core);
|
||||
g_return_if_fail (core);
|
||||
|
||||
/* Create the link */
|
||||
proxy = wp_core_create_remote_object(core, "link-factory",
|
||||
PW_TYPE_INTERFACE_Link, PW_VERSION_LINK, props);
|
||||
g_return_if_fail (proxy);
|
||||
g_ptr_array_add(self->link_proxies, proxy);
|
||||
link = wp_link_new_from_factory (core, "link-factory",
|
||||
wp_properties_ref (props));
|
||||
g_return_if_fail (link);
|
||||
g_ptr_array_add (self->link_proxies, link);
|
||||
|
||||
/* Wait for the link to be created on the server side
|
||||
by waiting for the info event, which will be signaled anyway */
|
||||
/* Wait for the link to be created on the server side */
|
||||
self->link_count++;
|
||||
wp_proxy_augment (proxy, WP_PROXY_FEATURE_INFO, NULL,
|
||||
wp_proxy_augment (WP_PROXY (link), WP_PROXY_FEATURE_BOUND, NULL,
|
||||
(GAsyncReadyCallback) on_link_augmented, self);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,9 +56,11 @@ loop_thread_start (void *d)
|
|||
/* Create the server */
|
||||
wp_test_server_setup (&self->server);
|
||||
|
||||
/* Add the audioconvert SPA library */
|
||||
/* Load needed plugins on the server side */
|
||||
pw_context_add_spa_lib (self->server.context, "audio.convert*",
|
||||
"audioconvert/libspa-audioconvert");
|
||||
pw_context_load_module (self->server.context,
|
||||
"libpipewire-module-spa-node-factory", NULL, NULL);
|
||||
|
||||
/* Create the core and connect to the server */
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
|
|
|
|||
|
|
@ -113,8 +113,6 @@ test_proxy_basic_object_added (WpObjectManager *om, WpProxy *proxy,
|
|||
g_assert_nonnull (omcore);
|
||||
g_assert_true (pcore == omcore);
|
||||
}
|
||||
g_assert_cmpstr (wp_proxy_get_interface_type (proxy), ==,
|
||||
PW_TYPE_INTERFACE_Client);
|
||||
g_assert_cmphex (wp_proxy_get_global_permissions (proxy), ==, PW_PERM_RWX);
|
||||
g_assert_true (WP_IS_CLIENT (proxy));
|
||||
|
||||
|
|
@ -192,10 +190,7 @@ test_node_object_added (WpObjectManager *om, WpProxy *proxy,
|
|||
TestNodeParamData *param_data;
|
||||
|
||||
g_assert_nonnull (proxy);
|
||||
g_assert_cmpstr (wp_proxy_get_interface_type (proxy), ==,
|
||||
PW_TYPE_INTERFACE_Node);
|
||||
g_assert_cmphex (wp_proxy_get_features (proxy), ==,
|
||||
WP_PROXY_FEATURE_PW_PROXY | WP_PROXY_FEATURE_INFO | WP_PROXY_FEATURE_BOUND);
|
||||
g_assert_cmphex (wp_proxy_get_features (proxy), ==, WP_PROXY_FEATURES_STANDARD);
|
||||
g_assert_nonnull (wp_proxy_get_pw_proxy (proxy));
|
||||
|
||||
g_assert_true (WP_IS_NODE (proxy));
|
||||
|
|
@ -246,8 +241,7 @@ test_node (TestProxyFixture *fixture, gconstpointer data)
|
|||
/* 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_PROXY_FEATURE_BOUND);
|
||||
PW_TYPE_INTERFACE_Node, NULL, WP_PROXY_FEATURES_STANDARD);
|
||||
wp_core_install_object_manager (fixture->core, fixture->om);
|
||||
|
||||
g_assert_true (wp_core_connect (fixture->core));
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue