diff --git a/modules/meson.build b/modules/meson.build index e12e01ba707ecb23e1c473df440a614f5ed308a0..3b3fabd3ec66a2aa8514690ccea2100dc3551f70 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -24,6 +24,7 @@ shared_library( 'wireplumber-module-monitor', [ 'module-monitor.c', + 'module-monitor/reservation-data.c', 'module-monitor/dbus-device-reservation.c', reserve_device_interface_src, ], diff --git a/modules/module-monitor/reservation-data.c b/modules/module-monitor/reservation-data.c new file mode 100644 index 0000000000000000000000000000000000000000..2688390929fac4632dfb1ac47293588a4d982520 --- /dev/null +++ b/modules/module-monitor/reservation-data.c @@ -0,0 +1,450 @@ +/* WirePlumber + * + * Copyright © 2020 Collabora Ltd. + * @author Julian Bouzas <julian.bouzas@collabora.com> + * + * SPDX-License-Identifier: MIT + */ + +#include "reservation-data.h" + +#include <spa/pod/builder.h> +#include <pipewire/pipewire.h> + +struct _WpMonitorDeviceReservationData +{ + GObject parent; + + /* Props */ + GWeakRef device; + WpMonitorDbusDeviceReservation *reservation; + + guint n_acquired; +}; + +enum { + DEVICE_PROP_0, + DEVICE_PROP_DEVICE, + DEVICE_PROP_RESERVATION, +}; + +G_DEFINE_TYPE (WpMonitorDeviceReservationData, + wp_monitor_device_reservation_data, G_TYPE_OBJECT) + +static void +on_device_done (WpCore *core, GAsyncResult *res, + WpMonitorDeviceReservationData *self) +{ + if (self->reservation) + wp_monitor_dbus_device_reservation_complete_release (self->reservation, TRUE); +} + +static void +on_reservation_acquired (GObject *obj, GAsyncResult *res, gpointer user_data) +{ + WpMonitorDeviceReservationData *self = user_data; + WpMonitorDbusDeviceReservation *reserv = + WP_MONITOR_DBUS_DEVICE_RESERVATION (obj); + g_autoptr (GError) error = NULL; + g_autoptr (WpProxy) device = NULL; + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof(buf)); + + /* Finish */ + if (!wp_monitor_dbus_device_reservation_async_finish (reserv, res, &error)) { + g_warning ("%s", error->message); + return; + } + + /* Get the device */ + device = g_weak_ref_get (&self->device); + if (!device) + return; + + /* Set profile 1 */ + wp_proxy_set_param (device, SPA_PARAM_Profile, 0, + spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, + SPA_PARAM_PROFILE_index, SPA_POD_Int(1))); +} + +static void +on_reservation_release (WpMonitorDbusDeviceReservation *reservation, int forced, + WpMonitorDeviceReservationData *self) +{ + g_autoptr (WpProxy) device = NULL; + g_autoptr (WpCore) core = NULL; + char buf[1024]; + struct spa_pod_builder b = SPA_POD_BUILDER_INIT (buf, sizeof(buf)); + + /* Get the device and core */ + device = g_weak_ref_get (&self->device); + if (!device) + return; + core = wp_proxy_get_core (device); + if (!core) + return; + + /* Set profile 0 */ + wp_proxy_set_param (device, SPA_PARAM_Profile, 0, + spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, + SPA_PARAM_PROFILE_index, SPA_POD_Int(0))); + + /* Complete release on done */ + wp_core_sync (core, NULL, (GAsyncReadyCallback)on_device_done, self); +} + +static void +on_device_destroyed (WpProxy *device, WpMonitorDeviceReservationData *self) +{ + wp_monitor_dbus_device_reservation_release (self->reservation); +} + +static void +wp_monitor_device_reservation_data_constructed (GObject * object) +{ + WpMonitorDeviceReservationData *self = + WP_MONITOR_DEVICE_RESERVATION_DATA (object); + g_autoptr (WpProxy) device = g_weak_ref_get (&self->device); + + /* Make sure the device is released when the pw proxy device is destroyed */ + g_return_if_fail (device); + g_signal_connect_object (device, "pw-proxy-destroyed", + (GCallback) on_device_destroyed, self, 0); + + /* Handle the reservation signals */ + g_return_if_fail (self->reservation); + g_signal_connect_object (self->reservation, "release", + (GCallback) on_reservation_release, self, 0); + + /* Try to acquire the device */ + wp_monitor_dbus_device_reservation_acquire (self->reservation, NULL, + on_reservation_acquired, self); + + G_OBJECT_CLASS (wp_monitor_device_reservation_data_parent_class)->constructed (object); +} + +static void +wp_monitor_device_reservation_data_get_property (GObject * object, + guint property_id, GValue * value, GParamSpec * pspec) +{ + WpMonitorDeviceReservationData *self = + WP_MONITOR_DEVICE_RESERVATION_DATA (object); + + switch (property_id) { + case DEVICE_PROP_DEVICE: + g_value_take_object (value, g_weak_ref_get (&self->device)); + break; + case DEVICE_PROP_RESERVATION: + g_value_set_object (value, self->reservation); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +wp_monitor_device_reservation_data_set_property (GObject * object, + guint property_id, const GValue * value, GParamSpec * pspec) +{ + WpMonitorDeviceReservationData *self = + WP_MONITOR_DEVICE_RESERVATION_DATA (object); + + switch (property_id) { + case DEVICE_PROP_DEVICE: + g_weak_ref_set (&self->device, g_value_get_object (value)); + break; + case DEVICE_PROP_RESERVATION: + self->reservation = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +wp_monitor_device_reservation_data_finalize (GObject * object) +{ + WpMonitorDeviceReservationData *self = + WP_MONITOR_DEVICE_RESERVATION_DATA (object); + + wp_monitor_dbus_device_reservation_release (self->reservation); + + /* Props */ + g_weak_ref_clear (&self->device); + g_clear_object (&self->reservation); + + G_OBJECT_CLASS (wp_monitor_device_reservation_data_parent_class)->finalize (object); +} + +static void +wp_monitor_device_reservation_data_init (WpMonitorDeviceReservationData * self) +{ + /* Props */ + g_weak_ref_init (&self->device, NULL); + + self->n_acquired = 0; +} + +static void +wp_monitor_device_reservation_data_class_init ( + WpMonitorDeviceReservationDataClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + object_class->constructed = wp_monitor_device_reservation_data_constructed; + object_class->get_property = wp_monitor_device_reservation_data_get_property; + object_class->set_property = wp_monitor_device_reservation_data_set_property; + object_class->finalize = wp_monitor_device_reservation_data_finalize; + + /* Props */ + g_object_class_install_property (object_class, DEVICE_PROP_DEVICE, + g_param_spec_object ("device", "device", "The device", WP_TYPE_PROXY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, DEVICE_PROP_RESERVATION, + g_param_spec_object ("reservation", "reservation", + "The dbus device reservation", WP_TYPE_MONITOR_DBUS_DEVICE_RESERVATION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} + +WpMonitorDeviceReservationData * +wp_monitor_device_reservation_data_new (WpProxy *device, + WpMonitorDbusDeviceReservation *reservation) +{ + return g_object_new (WP_TYPE_MONITOR_DEVICE_RESERVATION_DATA, + "device", device, + "reservation", reservation, + NULL); +} + +void +wp_monitor_device_reservation_data_acquire ( + WpMonitorDeviceReservationData *self) +{ + g_return_if_fail (WP_IS_MONITOR_DEVICE_RESERVATION_DATA (self)); + g_return_if_fail (self->reservation); + + if (self->n_acquired == 0) + wp_monitor_dbus_device_reservation_acquire (self->reservation, NULL, + on_reservation_acquired, self); + + self->n_acquired++; +} + +void +wp_monitor_device_reservation_data_release ( + WpMonitorDeviceReservationData *self) +{ + g_return_if_fail (WP_IS_MONITOR_DEVICE_RESERVATION_DATA (self)); + g_return_if_fail (self->reservation); + + if (self->n_acquired == 1) + wp_monitor_dbus_device_reservation_release (self->reservation); + + self->n_acquired--; +} + + +struct _WpMonitorNodeReservationData +{ + GObject parent; + + /* Props */ + GWeakRef node; + WpMonitorDeviceReservationData *device_data; + + gboolean acquired; + GSource *timeout_source; +}; + +enum { + NODE_PROP_0, + NODE_PROP_NODE, + NODE_PROP_DEVICE_DATA, +}; + +G_DEFINE_TYPE (WpMonitorNodeReservationData, + wp_monitor_node_reservation_data, G_TYPE_OBJECT) + +static void +on_node_destroyed (WpProxy *node, WpMonitorNodeReservationData *self) +{ + if (self->acquired) + wp_monitor_device_reservation_data_release (self->device_data); +} + +static void +wp_monitor_node_reservation_data_constructed (GObject * object) +{ + WpMonitorNodeReservationData *self = + WP_MONITOR_NODE_RESERVATION_DATA (object); + WpProxy *node = g_weak_ref_get (&self->node); + + g_return_if_fail (node); + + /* Make sure the device is released when the pw proxy node is destroyed */ + g_signal_connect_object (node, "pw-proxy-destroyed", + (GCallback) on_node_destroyed, self, 0); + + G_OBJECT_CLASS (wp_monitor_node_reservation_data_parent_class)->constructed (object); +} + +static void +wp_monitor_node_reservation_data_get_property (GObject * object, + guint property_id, GValue * value, GParamSpec * pspec) +{ + WpMonitorNodeReservationData *self = + WP_MONITOR_NODE_RESERVATION_DATA (object); + + switch (property_id) { + case NODE_PROP_NODE: + g_value_take_object (value, g_weak_ref_get (&self->node)); + break; + case NODE_PROP_DEVICE_DATA: + g_value_set_object (value, self->device_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +wp_monitor_node_reservation_data_set_property (GObject * object, + guint property_id, const GValue * value, GParamSpec * pspec) +{ + WpMonitorNodeReservationData *self = + WP_MONITOR_NODE_RESERVATION_DATA (object); + + switch (property_id) { + case NODE_PROP_NODE: + g_weak_ref_set (&self->node, g_value_get_object (value)); + break; + case NODE_PROP_DEVICE_DATA: + self->device_data = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +wp_monitor_node_reservation_data_finalize (GObject * object) +{ + WpMonitorNodeReservationData *self = + WP_MONITOR_NODE_RESERVATION_DATA (object); + + /* Clear the current timeout release callback */ + if (self->timeout_source) + g_source_destroy (self->timeout_source); + g_clear_pointer (&self->timeout_source, g_source_unref); + + /* Release device if acquired */ + if (self->acquired) + wp_monitor_device_reservation_data_release (self->device_data); + + /* Props */ + g_weak_ref_clear (&self->node); + g_clear_object (&self->device_data); + + G_OBJECT_CLASS (wp_monitor_node_reservation_data_parent_class)->finalize (object); +} + +static void +wp_monitor_node_reservation_data_init (WpMonitorNodeReservationData * self) +{ + /* Props */ + g_weak_ref_init (&self->node, NULL); + + self->acquired = FALSE; + self->timeout_source = NULL; +} + +static void +wp_monitor_node_reservation_data_class_init ( + WpMonitorNodeReservationDataClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + object_class->constructed = wp_monitor_node_reservation_data_constructed; + object_class->get_property = wp_monitor_node_reservation_data_get_property; + object_class->set_property = wp_monitor_node_reservation_data_set_property; + object_class->finalize = wp_monitor_node_reservation_data_finalize; + + /* Props */ + g_object_class_install_property (object_class, NODE_PROP_NODE, + g_param_spec_object ("node", "node", "The node", WP_TYPE_PROXY, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, NODE_PROP_DEVICE_DATA, + g_param_spec_object ("device-data", "device-data", + "The monitor device reservation data", WP_TYPE_MONITOR_DEVICE_RESERVATION_DATA, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} + +WpMonitorNodeReservationData * +wp_monitor_node_reservation_data_new (WpProxy *node, + WpMonitorDeviceReservationData *device_data) +{ + return g_object_new (WP_TYPE_MONITOR_NODE_RESERVATION_DATA, + "node", node, + "device-data", device_data, + NULL); +} + +static gboolean +timeout_release_callback (gpointer data) +{ + WpMonitorNodeReservationData *self = data; + g_return_val_if_fail (self, G_SOURCE_REMOVE); + + wp_monitor_device_reservation_data_release (self->device_data); + self->acquired = FALSE; + return G_SOURCE_REMOVE; +} + +void +wp_monitor_node_reservation_data_timeout_release ( + WpMonitorNodeReservationData *self, guint64 timeout_ms) +{ + g_autoptr (WpProxy) node = NULL; + g_autoptr (WpCore) core = NULL; + g_return_if_fail (WP_IS_MONITOR_NODE_RESERVATION_DATA (self)); + + node = g_weak_ref_get (&self->node); + g_return_if_fail (node); + core = wp_proxy_get_core (node); + g_return_if_fail (core); + + /* Clear the current timeout release callback */ + if (self->timeout_source) + g_source_destroy (self->timeout_source); + g_clear_pointer (&self->timeout_source, g_source_unref); + + /* Add new timeout release callback */ + wp_core_timeout_add (core, &self->timeout_source, timeout_ms, + timeout_release_callback, g_object_ref (self), g_object_unref); +} + +void +wp_monitor_node_reservation_data_acquire (WpMonitorNodeReservationData *self) +{ + g_return_if_fail (WP_IS_MONITOR_NODE_RESERVATION_DATA (self)); + + /* Clear the current timeout release callback */ + if (self->timeout_source) + g_source_destroy (self->timeout_source); + g_clear_pointer (&self->timeout_source, g_source_unref); + + /* Don't do anything if already acquired */ + if (self->acquired) + return; + + /* Acquire the device */ + wp_monitor_device_reservation_data_acquire (self->device_data); + self->acquired = TRUE; +} + diff --git a/modules/module-monitor/reservation-data.h b/modules/module-monitor/reservation-data.h new file mode 100644 index 0000000000000000000000000000000000000000..fc70e37fdb0cc811f6642c47e95f6d39128718ac --- /dev/null +++ b/modules/module-monitor/reservation-data.h @@ -0,0 +1,54 @@ +/* WirePlumber + * + * Copyright © 2020 Collabora Ltd. + * @author Julian Bouzas <julian.bouzas@collabora.com> + * + * SPDX-License-Identifier: MIT + */ + +#ifndef __WIREPLUMBER_MONITOR_RESERVATION_DATA_H__ +#define __WIREPLUMBER_MONITOR_RESERVATION_DATA_H__ + +#include <wp/wp.h> + +#include "dbus-device-reservation.h" + +G_BEGIN_DECLS + +#define WP_TYPE_MONITOR_DEVICE_RESERVATION_DATA (wp_monitor_device_reservation_data_get_type ()) + +G_DECLARE_FINAL_TYPE (WpMonitorDeviceReservationData, + wp_monitor_device_reservation_data, WP, MONITOR_DEVICE_RESERVATION_DATA, + GObject) + +WpMonitorDeviceReservationData * wp_monitor_device_reservation_data_new ( + WpProxy *device, WpMonitorDbusDeviceReservation *reservation); + +void +wp_monitor_device_reservation_data_acquire ( + WpMonitorDeviceReservationData *self); + +void +wp_monitor_device_reservation_data_release ( + WpMonitorDeviceReservationData *self); + + +#define WP_TYPE_MONITOR_NODE_RESERVATION_DATA (wp_monitor_node_reservation_data_get_type ()) + +G_DECLARE_FINAL_TYPE (WpMonitorNodeReservationData, + wp_monitor_node_reservation_data, WP, MONITOR_NODE_RESERVATION_DATA, + GObject) + +WpMonitorNodeReservationData * wp_monitor_node_reservation_data_new ( + WpProxy *node, WpMonitorDeviceReservationData *device_data); + +void +wp_monitor_node_reservation_data_timeout_release ( + WpMonitorNodeReservationData *self, guint64 timeout_ms); + +void +wp_monitor_node_reservation_data_acquire (WpMonitorNodeReservationData *self); + +G_END_DECLS + +#endif