diff --git a/lib/wp/endpoint.c b/lib/wp/endpoint.c index a2f86350..860cad1b 100644 --- a/lib/wp/endpoint.c +++ b/lib/wp/endpoint.c @@ -733,13 +733,30 @@ wp_endpoint_get_links (WpEndpoint * self) return priv->links; } +/** + * wp_endpoint_unlink: + * @self: the endpoint + * + * Unlinks all the endpoints linked to this endpoint + */ +void +wp_endpoint_unlink (WpEndpoint * self) +{ + WpEndpointPrivate *priv; + gint i; + + g_return_if_fail (WP_IS_ENDPOINT (self)); + + priv = wp_endpoint_get_instance_private (self); + + for (i = priv->links->len - 1; i >= 0; i--) + wp_endpoint_link_destroy (g_ptr_array_index (priv->links, i)); +} + typedef struct _WpEndpointLinkPrivate WpEndpointLinkPrivate; struct _WpEndpointLinkPrivate { - /* The task to signal the endpoint link is initialized */ - GTask *init_task; - GWeakRef src; guint32 src_stream; GWeakRef sink; @@ -768,9 +785,6 @@ endpoint_link_finalize (GObject * object) WpEndpointLinkPrivate *priv = wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (object)); - /* Destroy the init task */ - g_clear_object(&priv->init_task); - /* Clear the endpoint weak reaferences */ g_weak_ref_clear(&priv->src); g_weak_ref_clear(&priv->sink); @@ -837,34 +851,27 @@ wp_endpoint_link_init_async (GAsyncInitable *initable, int io_priority, wp_endpoint_link_get_instance_private (WP_ENDPOINT_LINK (initable)); g_autoptr (WpEndpoint) src = g_weak_ref_get (&priv->src); g_autoptr (WpEndpoint) sink = g_weak_ref_get (&priv->sink); - g_autoptr (GError) error = NULL; g_autoptr (GVariant) src_props = NULL; g_autoptr (GVariant) sink_props = NULL; WpEndpointPrivate *endpoint_priv; - /* Create the async task */ - priv->init_task = g_task_new (initable, cancellable, callback, data); - /* Prepare the endpoints */ if (!WP_ENDPOINT_GET_CLASS (src)->prepare_link (src, priv->src_stream, link, - &src_props, &error)) { - g_task_return_error (priv->init_task, error); - g_clear_object(&priv->init_task); + &src_props, NULL)) { + g_critical ("Failed to prepare link on source endpoint"); return; } if (!WP_ENDPOINT_GET_CLASS (sink)->prepare_link (sink, priv->sink_stream, - link, &sink_props, &error)) { - g_task_return_error (priv->init_task, error); - g_clear_object(&priv->init_task); + link, &sink_props, NULL)) { + g_critical ("Failed to prepare link on sink endpoint"); return; } /* Create the link */ g_return_if_fail (WP_ENDPOINT_LINK_GET_CLASS (link)->create); if (!WP_ENDPOINT_LINK_GET_CLASS (link)->create (link, src_props, - sink_props, &error)) { - g_task_return_error (priv->init_task, error); - g_clear_object(&priv->init_task); + sink_props, NULL)) { + g_critical ("Failed to create link in src and sink endpoints"); return; } @@ -873,10 +880,6 @@ wp_endpoint_link_init_async (GAsyncInitable *initable, int io_priority, g_ptr_array_add (endpoint_priv->links, g_object_ref (link)); endpoint_priv = wp_endpoint_get_instance_private (sink); g_ptr_array_add (endpoint_priv->links, g_object_ref (link)); - - /* Finish the creation of the endpoint */ - g_task_return_boolean (priv->init_task, TRUE); - g_clear_object(&priv->init_task); } static gboolean @@ -1048,6 +1051,8 @@ wp_endpoint_link_destroy (WpEndpointLink * self) src = g_weak_ref_get (&priv->src); sink = g_weak_ref_get (&priv->sink); + WP_ENDPOINT_LINK_GET_CLASS (self)->destroy (self); + if (src && WP_ENDPOINT_GET_CLASS (src)->release_link) WP_ENDPOINT_GET_CLASS (src)->release_link (src, self); if (sink && WP_ENDPOINT_GET_CLASS (sink)->release_link) @@ -1061,6 +1066,4 @@ wp_endpoint_link_destroy (WpEndpointLink * self) endpoint_priv = wp_endpoint_get_instance_private (sink); g_ptr_array_remove_fast (endpoint_priv->links, self); } - - WP_ENDPOINT_LINK_GET_CLASS (self)->destroy (self); } diff --git a/lib/wp/endpoint.h b/lib/wp/endpoint.h index a6cdfa81..2ef688b6 100644 --- a/lib/wp/endpoint.h +++ b/lib/wp/endpoint.h @@ -68,6 +68,7 @@ void wp_endpoint_notify_control_value (WpEndpoint * self, guint32 control_id); gboolean wp_endpoint_is_linked (WpEndpoint * self); GPtrArray * wp_endpoint_get_links (WpEndpoint * self); +void wp_endpoint_unlink (WpEndpoint * self); struct _WpEndpointLinkClass { diff --git a/lib/wp/meson.build b/lib/wp/meson.build index fa6cdcf2..d81e2d89 100644 --- a/lib/wp/meson.build +++ b/lib/wp/meson.build @@ -8,6 +8,7 @@ wp_lib_sources = [ 'proxy.c', 'proxy-node.c', 'proxy-port.c', + 'proxy-link.c', 'remote.c', 'remote-pipewire.c', ] @@ -22,6 +23,7 @@ wp_lib_headers = [ 'proxy.h', 'proxy-node.h', 'proxy-port.h', + 'proxy-link.h', 'remote.h', 'remote-pipewire.h', 'wp.h', diff --git a/lib/wp/proxy-link.c b/lib/wp/proxy-link.c new file mode 100644 index 00000000..a0f6733f --- /dev/null +++ b/lib/wp/proxy-link.c @@ -0,0 +1,134 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author Julian Bouzas + * + * SPDX-License-Identifier: MIT + */ + +#include "proxy-link.h" +#include + +struct _WpProxyLink +{ + WpProxy parent; + + /* The task to signal the proxy is initialized */ + GTask *init_task; + + /* The link proxy listener */ + struct spa_hook listener; + + /* The link info */ + struct pw_link_info *info; +}; + +static void wp_proxy_link_async_initable_init (gpointer iface, + gpointer iface_data); + +G_DEFINE_TYPE_WITH_CODE (WpProxyLink, wp_proxy_link, WP_TYPE_PROXY, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, + wp_proxy_link_async_initable_init)) + +static void +link_event_info(void *data, const struct pw_link_info *info) +{ + WpProxyLink *self = data; + + /* Make sure the task is valid */ + if (!self->init_task) + return; + + /* Update the link info */ + self->info = pw_link_info_update(self->info, info); + + /* Finish the creation of the proxy */ + g_task_return_boolean (self->init_task, TRUE); + g_clear_object (&self->init_task); +} + +static const struct pw_link_proxy_events link_events = { + PW_VERSION_LINK_PROXY_EVENTS, + .info = link_event_info, +}; + +static void +wp_proxy_link_finalize (GObject * object) +{ + WpProxyLink *self = WP_PROXY_LINK(object); + + /* Destroy the init task */ + g_clear_object (&self->init_task); + + /* Clear the info */ + if (self->info) { + pw_link_info_free(self->info); + self->info = NULL; + } + + G_OBJECT_CLASS (wp_proxy_link_parent_class)->finalize (object); +} + +static void +wp_proxy_link_init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) +{ + WpProxyLink *self = WP_PROXY_LINK(initable); + WpProxy *wp_proxy = WP_PROXY(initable); + struct pw_link_proxy *proxy = NULL; + + /* Create the async task */ + self->init_task = g_task_new (initable, cancellable, callback, data); + + /* Get the proxy from the base class */ + proxy = wp_proxy_get_pw_proxy(wp_proxy); + + /* Add the link proxy listener */ + pw_link_proxy_add_listener(proxy, &self->listener, &link_events, self); +} + +static void +wp_proxy_link_async_initable_init (gpointer iface, gpointer iface_data) +{ + GAsyncInitableIface *ai_iface = iface; + + /* Only set the init_async */ + ai_iface->init_async = wp_proxy_link_init_async; +} + +static void +wp_proxy_link_init (WpProxyLink * self) +{ +} + +static void +wp_proxy_link_class_init (WpProxyLinkClass * klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + object_class->finalize = wp_proxy_link_finalize; +} + +void +wp_proxy_link_new (guint global_id, gpointer proxy, + GAsyncReadyCallback callback, gpointer user_data) +{ + g_async_initable_new_async ( + WP_TYPE_PROXY_LINK, G_PRIORITY_DEFAULT, NULL, callback, user_data, + "global-id", global_id, + "pw-proxy", proxy, + NULL); +} + +WpProxyLink * +wp_proxy_link_new_finish(GObject *initable, GAsyncResult *res, GError **error) +{ + GAsyncInitable *ai = G_ASYNC_INITABLE(initable); + return WP_PROXY_LINK(g_async_initable_new_finish(ai, res, error)); +} + +const struct pw_link_info * +wp_proxy_link_get_info (WpProxyLink * self) +{ + return self->info; +} diff --git a/lib/wp/proxy-link.h b/lib/wp/proxy-link.h new file mode 100644 index 00000000..cd965f50 --- /dev/null +++ b/lib/wp/proxy-link.h @@ -0,0 +1,29 @@ +/* WirePlumber + * + * Copyright © 2019 Collabora Ltd. + * @author Julian Bouzas + * + * SPDX-License-Identifier: MIT + */ + +#ifndef __WIREPLUMBER_PROXY_LINK_H__ +#define __WIREPLUMBER_PROXY_LINK_H__ + +#include "core.h" +#include "proxy.h" + +G_BEGIN_DECLS + +#define WP_TYPE_PROXY_LINK (wp_proxy_link_get_type ()) +G_DECLARE_FINAL_TYPE (WpProxyLink, wp_proxy_link, WP, PROXY_LINK, WpProxy) + +void wp_proxy_link_new (guint global_id, gpointer proxy, + GAsyncReadyCallback callback, gpointer user_data); +WpProxyLink *wp_proxy_link_new_finish(GObject *initable, GAsyncResult *res, + GError **error); + +const struct pw_link_info *wp_proxy_link_get_info (WpProxyLink * self); + +G_END_DECLS + +#endif diff --git a/lib/wp/proxy-node.c b/lib/wp/proxy-node.c index e39801a6..89ca19e5 100644 --- a/lib/wp/proxy-node.c +++ b/lib/wp/proxy-node.c @@ -12,6 +12,9 @@ struct _WpProxyNode { WpProxy parent; + + /* The task to signal the proxy is initialized */ + GTask *init_task; /* The node proxy listener */ struct spa_hook listener; @@ -20,7 +23,6 @@ struct _WpProxyNode struct pw_node_info *info; }; -static GAsyncInitableIface *proxy_node_parent_interface = NULL; static void wp_proxy_node_async_initable_init (gpointer iface, gpointer iface_data); @@ -33,8 +35,16 @@ node_event_info(void *data, const struct pw_node_info *info) { WpProxyNode *self = data; + /* Make sure the task is valid */ + if (!self->init_task) + return; + /* Update the node info */ self->info = pw_node_info_update(self->info, info); + + /* Finish the creation of the proxy */ + g_task_return_boolean (self->init_task, TRUE); + g_clear_object (&self->init_task); } static const struct pw_node_proxy_events node_events = { @@ -47,6 +57,9 @@ wp_proxy_node_finalize (GObject * object) { WpProxyNode *self = WP_PROXY_NODE(object); + /* Destroy the init task */ + g_clear_object (&self->init_task); + /* Clear the info */ if (self->info) { pw_node_info_free(self->info); @@ -64,24 +77,20 @@ wp_proxy_node_init_async (GAsyncInitable *initable, int io_priority, WpProxy *wp_proxy = WP_PROXY(initable); struct pw_node_proxy *proxy = NULL; + /* Create the async task */ + self->init_task = g_task_new (initable, cancellable, callback, data); + /* Get the proxy from the base class */ proxy = wp_proxy_get_pw_proxy(wp_proxy); /* Add the node proxy listener */ pw_node_proxy_add_listener(proxy, &self->listener, &node_events, self); - - /* Call the parent interface */ - proxy_node_parent_interface->init_async (initable, io_priority, cancellable, - callback, data); } static void wp_proxy_node_async_initable_init (gpointer iface, gpointer iface_data) { GAsyncInitableIface *ai_iface = iface; - - /* Set the parent interface */ - proxy_node_parent_interface = g_type_interface_peek_parent (iface); /* Only set the init_async */ ai_iface->init_async = wp_proxy_node_init_async; diff --git a/lib/wp/proxy-port.c b/lib/wp/proxy-port.c index f916a03e..4255381b 100644 --- a/lib/wp/proxy-port.c +++ b/lib/wp/proxy-port.c @@ -13,7 +13,10 @@ struct _WpProxyPort { WpProxy parent; - + + /* The task to signal the proxy is initialized */ + GTask *init_task; + /* The port proxy listener */ struct spa_hook listener; @@ -26,7 +29,6 @@ struct _WpProxyPort struct spa_audio_info_raw format; }; -static GAsyncInitableIface *proxy_port_parent_interface = NULL; static void wp_proxy_port_async_initable_init (gpointer iface, gpointer iface_data); @@ -49,6 +51,10 @@ port_event_param(void *data, int seq, uint32_t id, uint32_t index, { WpProxyPort *self = data; + /* Make sure the task is valid */ + if (!self->init_task) + return; + /* Only handle EnumFormat */ if (id != SPA_PARAM_EnumFormat) return; @@ -64,6 +70,10 @@ port_event_param(void *data, int seq, uint32_t id, uint32_t index, /* Parse the raw audio format */ spa_pod_fixate((struct spa_pod*)param); spa_format_audio_raw_parse(param, &self->format); + + /* Finish the creation of the proxy */ + g_task_return_boolean (self->init_task, TRUE); + g_clear_object (&self->init_task); } static const struct pw_port_proxy_events port_events = { @@ -77,6 +87,9 @@ wp_proxy_port_finalize (GObject * object) { WpProxyPort *self = WP_PROXY_PORT(object); + /* Destroy the init task */ + g_clear_object (&self->init_task); + /* Clear the indo */ if (self->info) { pw_port_info_free(self->info); @@ -94,6 +107,9 @@ wp_proxy_port_init_async (GAsyncInitable *initable, int io_priority, WpProxy *wp_proxy = WP_PROXY(initable); struct pw_port_proxy *proxy = NULL; + /* Create the async task */ + self->init_task = g_task_new (initable, cancellable, callback, data); + /* Get the proxy from the base class */ proxy = wp_proxy_get_pw_proxy(wp_proxy); @@ -103,19 +119,12 @@ wp_proxy_port_init_async (GAsyncInitable *initable, int io_priority, /* Emit the EnumFormat param */ pw_port_proxy_enum_params((struct pw_port_proxy*)proxy, 0, SPA_PARAM_EnumFormat, 0, -1, NULL); - - /* Call the parent interface */ - proxy_port_parent_interface->init_async (initable, io_priority, cancellable, - callback, data); } static void wp_proxy_port_async_initable_init (gpointer iface, gpointer iface_data) { GAsyncInitableIface *ai_iface = iface; - - /* Set the parent interface */ - proxy_port_parent_interface = g_type_interface_peek_parent (iface); /* Only set the init_async */ ai_iface->init_async = wp_proxy_port_init_async; diff --git a/lib/wp/proxy.c b/lib/wp/proxy.c index 54622d13..360b50df 100644 --- a/lib/wp/proxy.c +++ b/lib/wp/proxy.c @@ -21,9 +21,6 @@ struct _WpProxyPrivate /* The proxy listener */ struct spa_hook listener; - - /* The done info */ - GTask *done_task; }; enum { @@ -58,21 +55,8 @@ proxy_event_destroy (void *data) static void proxy_event_done (void *data, int seq) { - WpProxyPrivate *self = wp_proxy_get_instance_private (WP_PROXY(data)); - /* Emit the done signal */ g_signal_emit (data, wp_proxy_signals[SIGNAL_DONE], 0); - - /* Make sure the task is valid */ - if (!self->done_task) - return; - - /* Execute the task */ - g_task_return_boolean (self->done_task, TRUE); - - /* Clean up */ - g_object_unref (self->done_task); - self->done_task = NULL; } static const struct pw_proxy_events proxy_events = { @@ -81,6 +65,15 @@ static const struct pw_proxy_events proxy_events = { .done = proxy_event_done, }; +static void +wp_proxy_constructed (GObject * object) +{ + WpProxyPrivate *self = wp_proxy_get_instance_private (WP_PROXY(object)); + + /* Add the event listener */ + pw_proxy_add_listener (self->proxy, &self->listener, &proxy_events, object); +} + static void wp_proxy_finalize (GObject * object) { @@ -136,22 +129,6 @@ wp_proxy_get_property (GObject * object, guint property_id, GValue * value, } } -static void -wp_proxy_init_async (GAsyncInitable *initable, int io_priority, - GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) -{ - WpProxyPrivate *self = wp_proxy_get_instance_private (WP_PROXY(initable)); - - /* Create the async task */ - self->done_task = g_task_new (initable, cancellable, callback, data); - - /* Add the event listener */ - pw_proxy_add_listener (self->proxy, &self->listener, &proxy_events, initable); - - /* Trigger the done callback */ - wp_proxy_sync(WP_PROXY(initable)); -} - static gboolean wp_proxy_init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) @@ -166,7 +143,7 @@ wp_proxy_async_initable_init (gpointer iface, gpointer iface_data) { GAsyncInitableIface *ai_iface = iface; - ai_iface->init_async = wp_proxy_init_async; + /* The init_async must be implemented in the derived classes */ ai_iface->init_finish = wp_proxy_init_finish; } @@ -180,6 +157,7 @@ wp_proxy_class_init (WpProxyClass * klass) { GObjectClass *object_class = (GObjectClass *) klass; + object_class->constructed = wp_proxy_constructed; object_class->finalize = wp_proxy_finalize; object_class->get_property = wp_proxy_get_property; object_class->set_property = wp_proxy_set_property; diff --git a/lib/wp/wp.h b/lib/wp/wp.h index 35eabfa9..4f277323 100644 --- a/lib/wp/wp.h +++ b/lib/wp/wp.h @@ -13,6 +13,7 @@ #include "module.h" #include "policy.h" #include "proxy.h" +#include "proxy-link.h" #include "proxy-node.h" #include "proxy-port.h" #include "remote.h" diff --git a/modules/module-pipewire.c b/modules/module-pipewire.c index 5df31239..a87d66b5 100644 --- a/modules/module-pipewire.c +++ b/modules/module-pipewire.c @@ -80,8 +80,9 @@ on_node_added (WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p, /* Set the properties */ g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&b, "{sv}", - "name", name ? g_variant_new_string (name) : - g_variant_new_take_string (g_strdup_printf ("Stream %u", id))); + "name", name ? + g_variant_new_take_string (g_strdup_printf ("Stream %u (%s)", id, name)) : + g_variant_new_take_string (g_strdup_printf ("Stream %u", id))); g_variant_builder_add (&b, "{sv}", "media-class", g_variant_new_string (media_class)); g_variant_builder_add (&b, "{sv}", diff --git a/modules/module-pipewire/simple-endpoint-link.c b/modules/module-pipewire/simple-endpoint-link.c index dd029e86..895d8063 100644 --- a/modules/module-pipewire/simple-endpoint-link.c +++ b/modules/module-pipewire/simple-endpoint-link.c @@ -27,8 +27,18 @@ struct _WpPipewireSimpleEndpointLink { WpEndpointLink parent; - /* The wireplumber core */ + /* Props */ GWeakRef core; + guint link_count; + + /* The task to signal the simple endpoint link is initialized */ + GTask *init_task; + + /* Handler */ + gulong proxy_done_handler_id; + + /* The link proxies */ + GPtrArray *link_proxies; }; enum { @@ -36,17 +46,55 @@ enum { PROP_CORE, }; +static GAsyncInitableIface *wp_simple_endpoint_link_parent_interface = NULL; +static void wp_simple_endpoint_link_async_initable_init (gpointer iface, + gpointer iface_data); + G_DECLARE_FINAL_TYPE (WpPipewireSimpleEndpointLink, simple_endpoint_link, WP_PIPEWIRE, SIMPLE_ENDPOINT_LINK, WpEndpointLink) -G_DEFINE_TYPE (WpPipewireSimpleEndpointLink, - simple_endpoint_link, WP_TYPE_ENDPOINT_LINK) +G_DEFINE_TYPE_WITH_CODE (WpPipewireSimpleEndpointLink, simple_endpoint_link, + WP_TYPE_ENDPOINT_LINK, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, + wp_simple_endpoint_link_async_initable_init)) + +static void +wp_simple_endpoint_link_init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) +{ + WpPipewireSimpleEndpointLink *self = + WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK (initable); + + /* Create the async task */ + self->init_task = g_task_new (initable, cancellable, callback, data); + + /* Call the parent interface */ + wp_simple_endpoint_link_parent_interface->init_async (initable, + io_priority, cancellable, callback, data); +} + +static void +wp_simple_endpoint_link_async_initable_init (gpointer iface, + gpointer iface_data) +{ + GAsyncInitableIface *ai_iface = iface; + + /* Set the parent interface */ + wp_simple_endpoint_link_parent_interface = + g_type_interface_peek_parent (iface); + + /* Only set the init_async */ + ai_iface->init_async = wp_simple_endpoint_link_init_async; +} static void simple_endpoint_link_init (WpPipewireSimpleEndpointLink * self) { /* Init the core weak reference */ g_weak_ref_init (&self->core, NULL); + + /* Init the list of link proxies */ + self->link_proxies = g_ptr_array_new_full(2, (GDestroyNotify)g_object_unref); } static void @@ -54,6 +102,15 @@ simple_endpoint_link_finalize (GObject * object) { WpPipewireSimpleEndpointLink *self = WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK(object); + /* Destroy the init task */ + g_clear_object(&self->init_task); + + /* Destroy the proxies port */ + if (self->link_proxies) { + g_ptr_array_free(self->link_proxies, TRUE); + self->link_proxies = NULL; + } + /* Clear the core weak reference */ g_weak_ref_clear (&self->core); } @@ -92,6 +149,37 @@ simple_endpoint_link_get_property (GObject * object, guint property_id, } } +static void +finish_simple_endpoint_link_creation(WpPipewireSimpleEndpointLink *self) +{ + /* Don't do anything if the link has already been initialized */ + if (!self->init_task) + return; + + /* Finish the creation of the audio dsp */ + g_task_return_boolean (self->init_task, TRUE); + g_clear_object(&self->init_task); +} + +static void +on_proxy_link_created(GObject *initable, GAsyncResult *res, gpointer data) +{ + WpPipewireSimpleEndpointLink *self = data; + WpProxyLink *proxy_link = NULL; + + /* Get the link */ + proxy_link = wp_proxy_link_new_finish(initable, res, NULL); + g_return_if_fail (proxy_link); + + /* Add the proxy link to the array */ + g_ptr_array_add(self->link_proxies, proxy_link); + self->link_count--; + + /* Finish the simple endpoint link creation if all links have been created */ + if (self->link_count == 0) + finish_simple_endpoint_link_creation (self); +} + static gboolean simple_endpoint_link_create (WpEndpointLink * epl, GVariant * src_data, GVariant * sink_data, GError ** error) @@ -105,6 +193,7 @@ simple_endpoint_link_create (WpEndpointLink * epl, GVariant * src_data, GVariantIter *out_iter, *in_iter; guint64 out_ptr, in_ptr; GHashTable *linked_ports = NULL; + struct pw_proxy *proxy; /* Get the remote pipewire */ remote_pipewire = wp_core_get_global (core, WP_GLOBAL_REMOTE_PIPEWIRE); @@ -152,8 +241,12 @@ simple_endpoint_link_create (WpEndpointLink * epl, GVariant * src_data, pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", in_id); /* Create the link */ - wp_remote_pipewire_create_object(remote_pipewire, "link-factory", + proxy = wp_remote_pipewire_create_object(remote_pipewire, "link-factory", PW_TYPE_INTERFACE_Link, &props->dict); + g_return_val_if_fail (proxy, FALSE); + wp_proxy_link_new (pw_proxy_get_id(proxy), proxy, on_proxy_link_created, + self); + self->link_count++; /* Insert the port id in the hash table to know it is linked */ g_hash_table_insert (linked_ports, GUINT_TO_POINTER(in_id), NULL); @@ -172,9 +265,15 @@ simple_endpoint_link_create (WpEndpointLink * epl, GVariant * src_data, } static void -simple_endpoint_link_destroy (WpEndpointLink * self) +simple_endpoint_link_destroy (WpEndpointLink * epl) { - /* TODO destroy pw_links */ + WpPipewireSimpleEndpointLink *self = WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK(epl); + + /* Destroy the proxies port */ + if (self->link_proxies) { + g_ptr_array_free(self->link_proxies, TRUE); + self->link_proxies = NULL; + } } static void diff --git a/modules/module-simple-policy.c b/modules/module-simple-policy.c index 433819a5..e66da4f3 100644 --- a/modules/module-simple-policy.c +++ b/modules/module-simple-policy.c @@ -282,6 +282,10 @@ simple_policy_handle_endpoint (WpPolicy *policy, WpEndpoint *ep) return FALSE; } + /* Unlink the target if it is already linked */ + if (wp_endpoint_is_linked (target)) + wp_endpoint_unlink (target); + /* Link the client with the target */ if (is_sink) { wp_endpoint_link_new (core, target, 0, ep, stream_id,