spa-device: subclass from GObject

This commit is contained in:
George Kiagiadakis
2020-06-10 15:34:34 -04:00
parent c7982a8317
commit 8a0abd4fb8
4 changed files with 215 additions and 165 deletions

View File

@@ -208,14 +208,22 @@ wp_device_new_from_factory (WpCore * core,
} }
enum {
PROP_0,
PROP_CORE,
PROP_SPA_DEVICE_HANDLE,
};
struct _WpSpaDevice struct _WpSpaDevice
{ {
WpProxy parent; WpProxy parent;
GWeakRef core;
struct spa_handle *handle; struct spa_handle *handle;
struct spa_device *interface; struct spa_device *device;
struct spa_hook listener; struct spa_hook listener;
WpProperties *properties; WpProperties *properties;
gboolean ft_active_requested; struct pw_proxy *proxy;
struct spa_hook proxy_listener;
}; };
enum enum
@@ -226,11 +234,34 @@ enum
static guint spa_device_signals[SPA_DEVICE_LAST_SIGNAL] = { 0 }; static guint spa_device_signals[SPA_DEVICE_LAST_SIGNAL] = { 0 };
G_DEFINE_TYPE (WpSpaDevice, wp_spa_device, WP_TYPE_PROXY) G_DEFINE_TYPE (WpSpaDevice, wp_spa_device, G_TYPE_OBJECT)
static void static void
wp_spa_device_init (WpSpaDevice * self) wp_spa_device_init (WpSpaDevice * self)
{ {
g_weak_ref_init (&self->core, NULL);
self->properties = wp_properties_new_empty ();
}
static void
wp_spa_device_constructed (GObject *object)
{
WpSpaDevice *self = WP_SPA_DEVICE (object);
gint res;
g_return_if_fail (self->handle);
/* Get the handle interface */
res = spa_handle_get_interface (self->handle, SPA_TYPE_INTERFACE_Device,
(gpointer *) &self->device);
if (res < 0) {
wp_warning_object (self,
"Could not get device interface from SPA handle: %s",
spa_strerror (res));
return;
}
G_OBJECT_CLASS (wp_spa_device_parent_class)->constructed (object);
} }
static void static void
@@ -238,12 +269,52 @@ wp_spa_device_finalize (GObject * object)
{ {
WpSpaDevice *self = WP_SPA_DEVICE (object); WpSpaDevice *self = WP_SPA_DEVICE (object);
self->device = NULL;
g_clear_pointer (&self->handle, pw_unload_spa_handle); g_clear_pointer (&self->handle, pw_unload_spa_handle);
g_clear_pointer (&self->properties, wp_properties_unref); g_clear_pointer (&self->properties, wp_properties_unref);
g_weak_ref_clear (&self->core);
G_OBJECT_CLASS (wp_spa_device_parent_class)->finalize (object); G_OBJECT_CLASS (wp_spa_device_parent_class)->finalize (object);
} }
static void
wp_spa_device_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpSpaDevice *self = WP_SPA_DEVICE (object);
switch (property_id) {
case PROP_CORE:
g_weak_ref_set (&self->core, g_value_get_object (value));
break;
case PROP_SPA_DEVICE_HANDLE:
self->handle = g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_spa_device_get_property (GObject * object, guint property_id, GValue * value,
GParamSpec * pspec)
{
WpSpaDevice *self = WP_SPA_DEVICE (object);
switch (property_id) {
case PROP_CORE:
g_value_take_object (value, g_weak_ref_get (&self->core));
break;
case PROP_SPA_DEVICE_HANDLE:
g_value_set_pointer (value, self->handle);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void static void
spa_device_event_info (void *data, const struct spa_device_info *info) spa_device_event_info (void *data, const struct spa_device_info *info)
{ {
@@ -257,21 +328,6 @@ spa_device_event_info (void *data, const struct spa_device_info *info)
*/ */
if (info->change_mask & SPA_DEVICE_CHANGE_MASK_PROPS) if (info->change_mask & SPA_DEVICE_CHANGE_MASK_PROPS)
wp_properties_update_from_dict (self->properties, info->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 static void
@@ -298,110 +354,27 @@ spa_device_event_object_info (void *data, uint32_t id,
static const struct spa_device_events spa_device_events = { static const struct spa_device_events spa_device_events = {
SPA_VERSION_DEVICE_EVENTS, SPA_VERSION_DEVICE_EVENTS,
.info = spa_device_event_info, .info = spa_device_event_info,
.result = spa_device_event_result,
.object_info = spa_device_event_object_info .object_info = spa_device_event_object_info
}; };
static void
wp_spa_device_activate (WpSpaDevice *self)
{
gint res = spa_device_add_listener (self->interface, &self->listener,
&spa_device_events, self);
if (res < 0) {
wp_proxy_augment_error (WP_PROXY (self), g_error_new (WP_DOMAIN_LIBRARY,
WP_LIBRARY_ERROR_OPERATION_FAILED,
"failed to initialize device: %s", spa_strerror (res)));
}
}
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) {
/* if both BOUND and ACTIVE are requested,
delay the second until after we have a bound_id */
if (features & WP_PROXY_FEATURE_BOUND)
self->ft_active_requested = TRUE;
else
wp_spa_device_activate (self);
}
}
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, WpSpaPod * filter)
{
WpSpaDevice *self = WP_SPA_DEVICE (proxy);
return spa_device_enum_params (self->interface,
0, id, start, num, filter ? wp_spa_pod_get_spa_pod (filter) : NULL);
}
static gint
wp_spa_device_set_param (WpProxy * proxy, guint32 id, guint32 flags,
WpSpaPod *param)
{
WpSpaDevice *self = WP_SPA_DEVICE (proxy);
return spa_device_set_param (self->interface,
id, flags, wp_spa_pod_get_spa_pod (param));
}
static void
wp_spa_device_bound (WpProxy * proxy, guint32 id)
{
WpSpaDevice *self = WP_SPA_DEVICE (proxy);
if (self->ft_active_requested)
wp_spa_device_activate (self);
}
static void static void
wp_spa_device_class_init (WpSpaDeviceClass * klass) wp_spa_device_class_init (WpSpaDeviceClass * klass)
{ {
GObjectClass *object_class = (GObjectClass *) klass; GObjectClass *object_class = (GObjectClass *) klass;
WpProxyClass *proxy_class = (WpProxyClass *) klass;
object_class->constructed = wp_spa_device_constructed;
object_class->finalize = wp_spa_device_finalize; object_class->finalize = wp_spa_device_finalize;
object_class->set_property = wp_spa_device_set_property;
object_class->get_property = wp_spa_device_get_property;
proxy_class->augment = wp_spa_device_augment; g_object_class_install_property (object_class, PROP_CORE,
g_param_spec_object ("core", "core", "The WpCore", WP_TYPE_CORE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
proxy_class->get_properties = wp_spa_device_get_properties; g_object_class_install_property (object_class, PROP_SPA_DEVICE_HANDLE,
proxy_class->enum_params = wp_spa_device_enum_params; g_param_spec_pointer ("spa-device-handle", "spa-device-handle",
proxy_class->set_param = wp_spa_device_set_param; "The spa device handle",
proxy_class->bound = wp_spa_device_bound; G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
/** /**
* WpSpaDevice::object-info: * WpSpaDevice::object-info:
@@ -434,6 +407,22 @@ wp_spa_device_class_init (WpSpaDeviceClass * klass)
G_TYPE_STRING, WP_TYPE_PROPERTIES, WP_TYPE_PROPERTIES); G_TYPE_STRING, WP_TYPE_PROPERTIES, WP_TYPE_PROPERTIES);
} }
/**
* wp_spa_device_new_wrap:
* @core: the wireplumber core
* @spa_device_handle: the spa device handle
*
* Returns: (transfer full): A new #WpSpaDevice
*/
WpSpaDevice *
wp_spa_device_new_wrap (WpCore * core, gpointer spa_device_handle)
{
return g_object_new (WP_TYPE_SPA_DEVICE,
"core", core,
"spa-device-handle", spa_device_handle,
NULL);
}
/** /**
* wp_spa_device_new_from_spa_factory: * wp_spa_device_new_from_spa_factory:
* @core: the wireplumber core * @core: the wireplumber core
@@ -458,34 +447,85 @@ wp_spa_device_new_from_spa_factory (WpCore * core,
{ {
g_autoptr (WpProperties) props = properties; g_autoptr (WpProperties) props = properties;
struct pw_context *pw_context = wp_core_get_pw_context (core); struct pw_context *pw_context = wp_core_get_pw_context (core);
g_autoptr (WpSpaDevice) self = NULL; struct spa_handle *handle = NULL;
gint res;
g_return_val_if_fail (pw_context != NULL, NULL); g_return_val_if_fail (pw_context != NULL, NULL);
self = g_object_new (WP_TYPE_SPA_DEVICE, "core", core, NULL);
/* Load the monitor handle */ /* Load the monitor handle */
self->handle = pw_context_load_spa_handle (pw_context, handle = pw_context_load_spa_handle (pw_context, factory_name,
factory_name, props ? wp_properties_peek_dict (props) : NULL); props ? wp_properties_peek_dict (props) : NULL);
if (!self->handle) { if (!handle) {
wp_warning_object (self, "SPA handle '%s' could not be loaded; " wp_warning ("SPA handle '%s' could not be loaded; is it installed?",
"is it installed?", factory_name); factory_name);
return NULL; return NULL;
} }
/* Get the handle interface */ return wp_spa_device_new_wrap (core, handle);
res = spa_handle_get_interface (self->handle, SPA_TYPE_INTERFACE_Device, }
(gpointer *) &self->interface);
if (res < 0) { guint32
wp_warning_object (self, wp_spa_device_get_bound_id (WpSpaDevice * self)
"Could not get device interface from SPA handle: %s", {
spa_strerror (res)); g_return_val_if_fail (WP_IS_SPA_DEVICE (self), SPA_ID_INVALID);
return NULL; return self->proxy ? pw_proxy_get_bound_id (self->proxy) : SPA_ID_INVALID;
} }
self->properties = props ? static void
g_steal_pointer (&props) : wp_properties_new_empty (); proxy_event_bound (void *data, uint32_t global_id)
{
return g_steal_pointer (&self); GTask *task = G_TASK (data);
WpSpaDevice *self = g_task_get_source_object (task);
spa_hook_remove (&self->proxy_listener);
g_task_return_boolean (task, TRUE);
}
static const struct pw_proxy_events proxy_events = {
PW_VERSION_PROXY_EVENTS,
.bound = proxy_event_bound,
};
void
wp_spa_device_export (WpSpaDevice * self, GCancellable * cancellable,
GAsyncReadyCallback callback, gpointer user_data)
{
g_autoptr (GTask) task = NULL;
g_return_if_fail (WP_IS_SPA_DEVICE (self));
g_return_if_fail (!self->proxy);
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
struct pw_core *pw_core = wp_core_get_pw_core (core);
g_return_if_fail (pw_core);
task = g_task_new (self, cancellable, callback, user_data);
self->proxy = pw_core_export (pw_core,
SPA_TYPE_INTERFACE_Device,
wp_properties_peek_dict (self->properties),
self->device, 0);
pw_proxy_add_listener (self->proxy, &self->proxy_listener,
&proxy_events, task);
}
gboolean
wp_spa_device_export_finish (WpSpaDevice * self, GAsyncResult * res,
GError ** error)
{
g_return_val_if_fail (WP_IS_SPA_DEVICE (self), FALSE);
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
return g_task_propagate_boolean (G_TASK (res), error);
}
void
wp_spa_device_activate (WpSpaDevice * self)
{
g_return_if_fail (WP_IS_SPA_DEVICE (self));
gint res = spa_device_add_listener (self->device, &self->listener,
&spa_device_events, self);
if (res < 0)
wp_warning_object (self, "failed to activate device: %s",
spa_strerror (res));
} }

View File

@@ -30,17 +30,6 @@ WpDevice * wp_device_new_from_factory (WpCore * core,
/* WpSpaDevice */ /* WpSpaDevice */
/**
* WpSpaDeviceFeatures:
* @WP_SPA_DEVICE_FEATURE_ACTIVE: activates the device, making it query the
* hardware and emit the #WpSpaDevice::object-info signal
*
* An extension of #WpProxyFeatures
*/
typedef enum { /*< flags >*/
WP_SPA_DEVICE_FEATURE_ACTIVE = (WP_PROXY_FEATURE_LAST << 0),
} WpSpaDeviceFeatures;
/** /**
* WP_TYPE_SPA_DEVICE: * WP_TYPE_SPA_DEVICE:
* *
@@ -48,12 +37,30 @@ typedef enum { /*< flags >*/
*/ */
#define WP_TYPE_SPA_DEVICE (wp_spa_device_get_type ()) #define WP_TYPE_SPA_DEVICE (wp_spa_device_get_type ())
WP_API WP_API
G_DECLARE_FINAL_TYPE (WpSpaDevice, wp_spa_device, WP, SPA_DEVICE, WpProxy) G_DECLARE_FINAL_TYPE (WpSpaDevice, wp_spa_device, WP, SPA_DEVICE, GObject)
WP_API
WpSpaDevice * wp_spa_device_new_wrap (WpCore * core,
gpointer spa_device_handle);
WP_API WP_API
WpSpaDevice * wp_spa_device_new_from_spa_factory (WpCore * core, WpSpaDevice * wp_spa_device_new_from_spa_factory (WpCore * core,
const gchar * factory_name, WpProperties * properties); const gchar * factory_name, WpProperties * properties);
WP_API
guint32 wp_spa_device_get_bound_id (WpSpaDevice * self);
WP_API
void wp_spa_device_export (WpSpaDevice * self, GCancellable * cancellable,
GAsyncReadyCallback callback, gpointer user_data);
WP_API
gboolean wp_spa_device_export_finish (WpSpaDevice * self, GAsyncResult * res,
GError ** error);
WP_API
void wp_spa_device_activate (WpSpaDevice * self);
G_END_DECLS G_END_DECLS
#endif #endif

View File

@@ -92,7 +92,7 @@ on_node_added (WpObjectManager *om, WpProxy *proxy, gpointer d)
device_id = wp_proxy_get_property (proxy, PW_KEY_DEVICE_ID); device_id = wp_proxy_get_property (proxy, PW_KEY_DEVICE_ID);
if (!device_id) if (!device_id)
return; return;
device = wp_object_manager_lookup (self->spa_devices_om, WP_TYPE_SPA_DEVICE, device = wp_object_manager_lookup (self->spa_devices_om, WP_TYPE_DEVICE,
WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=i", atoi (device_id), NULL); WP_CONSTRAINT_TYPE_G_PROPERTY, "bound-id", "=i", atoi (device_id), NULL);
if (!device) { if (!device) {
wp_warning_object (self, "cannot find device for node reservation data"); wp_warning_object (self, "cannot find device for node reservation data");
@@ -185,10 +185,10 @@ activate_sync (WpCore *core, GAsyncResult *res, WpDeviceActivation *self)
/* Create the devices object manager and handle the device added signal */ /* Create the devices object manager and handle the device added signal */
self->spa_devices_om = wp_object_manager_new (); self->spa_devices_om = wp_object_manager_new ();
wp_object_manager_add_interest (self->spa_devices_om, WP_TYPE_SPA_DEVICE, wp_object_manager_add_interest (self->spa_devices_om, WP_TYPE_DEVICE,
NULL); NULL);
wp_object_manager_request_proxy_features (self->spa_devices_om, wp_object_manager_request_proxy_features (self->spa_devices_om,
WP_TYPE_SPA_DEVICE, WP_PROXY_FEATURE_BOUND); WP_TYPE_DEVICE, WP_PROXY_FEATURE_BOUND);
g_signal_connect_object (self->spa_devices_om, "object-added", g_signal_connect_object (self->spa_devices_om, "object-added",
G_CALLBACK (on_device_added), self, 0); G_CALLBACK (on_device_added), self, 0);
wp_core_install_object_manager (core, self->spa_devices_om); wp_core_install_object_manager (core, self->spa_devices_om);

View File

@@ -249,16 +249,18 @@ find_child (GObject * parent, guint32 id, GList ** children, GList ** link,
} }
static void static void
create_node (WpMonitor * self, WpProxy * parent, GList ** children, create_node (WpMonitor * self, WpSpaDevice * parent, GList ** children,
guint id, const gchar * spa_factory, WpProperties * props, guint id, const gchar * spa_factory, WpProperties * props,
WpProperties * parent_props) WpProperties * parent_props)
{ {
g_autoptr (WpCore) core = wp_proxy_get_core (parent); g_autoptr (WpCore) core = NULL;
GObject *node = NULL; GObject *node = NULL;
const gchar *pw_factory_name; const gchar *pw_factory_name;
wp_debug_object (self, "%s new node %u (%s)", self->factory, id, spa_factory); wp_debug_object (self, "%s new node %u (%s)", self->factory, id, spa_factory);
g_object_get (parent, "core", &core, NULL);
/* use the adapter instead of spa-node-factory if requested */ /* use the adapter instead of spa-node-factory if requested */
pw_factory_name = pw_factory_name =
(self->flags & FLAG_USE_ADAPTER) ? "adapter" : "spa-node-factory"; (self->flags & FLAG_USE_ADAPTER) ? "adapter" : "spa-node-factory";
@@ -267,8 +269,8 @@ create_node (WpMonitor * self, WpProxy * parent, GList ** children,
wp_properties_set (props, SPA_KEY_FACTORY_NAME, spa_factory); wp_properties_set (props, SPA_KEY_FACTORY_NAME, spa_factory);
/* add device id property */ /* add device id property */
if (wp_proxy_get_features (parent) & WP_PROXY_FEATURE_BOUND) { {
guint32 device_id = wp_proxy_get_bound_id (parent); guint32 device_id = wp_spa_device_get_bound_id (parent);
wp_properties_setf (props, PW_KEY_DEVICE_ID, "%u", device_id); wp_properties_setf (props, PW_KEY_DEVICE_ID, "%u", device_id);
} }
@@ -293,26 +295,29 @@ create_node (WpMonitor * self, WpProxy * parent, GList ** children,
} }
static void static void
device_created (GObject * proxy, GAsyncResult * res, gpointer user_data) device_created (GObject * device, GAsyncResult * res, gpointer user_data)
{ {
WpMonitor * self = user_data; WpMonitor * self = user_data;
g_autoptr (GError) error = NULL; g_autoptr (GError) error = NULL;
if (!wp_proxy_augment_finish (WP_PROXY (proxy), res, &error)) { if (!wp_spa_device_export_finish (WP_SPA_DEVICE (device), res, &error)) {
wp_warning_object (self, "%s", error->message); wp_warning_object (self, "%s", error->message);
return; return;
} }
wp_spa_device_activate (WP_SPA_DEVICE (device));
} }
static void static void
create_device (WpMonitor * self, WpProxy * parent, GList ** children, create_device (WpMonitor * self, WpSpaDevice * parent, GList ** children,
guint id, const gchar * spa_factory, WpProperties * props) guint id, const gchar * spa_factory, WpProperties * props)
{ {
g_autoptr (WpCore) core = wp_proxy_get_core (parent); g_autoptr (WpCore) core = NULL;
WpSpaDevice *device; WpSpaDevice *device;
wp_debug_object (self, "%s new device %u", self->factory, id); wp_debug_object (self, "%s new device %u", self->factory, id);
g_object_get (parent, "core", &core, NULL);
props = wp_properties_copy (props); props = wp_properties_copy (props);
setup_device_props (props); setup_device_props (props);
@@ -320,9 +325,8 @@ create_device (WpMonitor * self, WpProxy * parent, GList ** children,
return; return;
g_signal_connect (device, "object-info", (GCallback) on_object_info, self); g_signal_connect (device, "object-info", (GCallback) on_object_info, self);
wp_proxy_augment (WP_PROXY (device),
WP_PROXY_FEATURE_BOUND | WP_SPA_DEVICE_FEATURE_ACTIVE, wp_spa_device_export (device, NULL, device_created, self);
NULL, device_created, self);
g_object_set_qdata (G_OBJECT (device), id_quark (), GUINT_TO_POINTER (id)); g_object_set_qdata (G_OBJECT (device), id_quark (), GUINT_TO_POINTER (id));
*children = g_list_prepend (*children, device); *children = g_list_prepend (*children, device);
@@ -344,9 +348,9 @@ on_object_info (WpSpaDevice * device,
/* new object, construct... */ /* new object, construct... */
if (type != G_TYPE_NONE && !link) { if (type != G_TYPE_NONE && !link) {
if (type == WP_TYPE_DEVICE) { if (type == WP_TYPE_DEVICE) {
create_device (self, WP_PROXY (device), &children, id, spa_factory, props); create_device (self, device, &children, id, spa_factory, props);
} else if (type == WP_TYPE_NODE) { } else if (type == WP_TYPE_NODE) {
create_node (self, WP_PROXY (device), &children, id, spa_factory, props, create_node (self, device, &children, id, spa_factory, props,
parent_props); parent_props);
} else { } else {
wp_debug_object (self, "%s got device object-info for unknown object " wp_debug_object (self, "%s got device object-info for unknown object "
@@ -376,9 +380,8 @@ wp_monitor_activate (WpPlugin * plugin)
g_signal_connect (self->monitor, "object-info", (GCallback) on_object_info, g_signal_connect (self->monitor, "object-info", (GCallback) on_object_info,
self); self);
/* no FEATURE_BOUND here; exporting the monitor device is buggy */ /* activate directly; exporting the monitor device is buggy */
wp_proxy_augment (WP_PROXY (self->monitor), WP_SPA_DEVICE_FEATURE_ACTIVE, wp_spa_device_activate (self->monitor);
NULL, augment_done, self);
} }
static void static void