diff --git a/lib/wp/endpoint.c b/lib/wp/endpoint.c index 8a30c5e816bd53f1cf93ebd7169d12ca144a4f3c..12853e0b7cd34eb818b10e581900457efca0122c 100644 --- a/lib/wp/endpoint.c +++ b/lib/wp/endpoint.c @@ -113,7 +113,37 @@ enum { static guint32 signals[NUM_SIGNALS]; -G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (WpEndpoint, wp_endpoint, G_TYPE_OBJECT) +static void wp_endpoint_async_initable_init (gpointer iface, + gpointer iface_data); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (WpEndpoint, wp_endpoint, G_TYPE_OBJECT, + G_ADD_PRIVATE (WpEndpoint) + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, + wp_endpoint_async_initable_init)) + +static void +wp_endpoint_init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) +{ +} + +static gboolean +wp_endpoint_init_finish (GAsyncInitable *initable, GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_task_is_valid (result, initable), FALSE); + + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +wp_endpoint_async_initable_init (gpointer iface, gpointer iface_data) +{ + GAsyncInitableIface *ai_iface = iface; + + ai_iface->init_async = wp_endpoint_init_async; + ai_iface->init_finish = wp_endpoint_init_finish; +} static void wp_endpoint_init (WpEndpoint * self) @@ -253,6 +283,22 @@ wp_endpoint_class_init (WpEndpointClass * klass) G_TYPE_NONE, 1, G_TYPE_UINT); } +/** + * wp_endpoint_new_finish: + * @initable: the #GAsyncInitable from the callback + * @res: the #GAsyncResult from the callback + * @error: return location for errors, or NULL to ignore + * + * Finishes the async construction of #WpEndpoint. + */ +WpEndpoint * +wp_endpoint_new_finish (GObject *initable, GAsyncResult *res, + GError **error) +{ + GAsyncInitable *ai = G_ASYNC_INITABLE(initable); + return WP_ENDPOINT(g_async_initable_new_finish(ai, res, error)); +} + /** * wp_endpoint_register: * @self: the endpoint diff --git a/lib/wp/endpoint.h b/lib/wp/endpoint.h index 1364b776ccdfe83b3027cbff8a759722b4724486..4c5c4b5ad9cd717b82dfa7add8b8babb3bd4f69c 100644 --- a/lib/wp/endpoint.h +++ b/lib/wp/endpoint.h @@ -9,6 +9,8 @@ #ifndef __WIREPLUMBER_ENDPOINT_H__ #define __WIREPLUMBER_ENDPOINT_H__ +#include <gio/gio.h> + #include "core.h" G_BEGIN_DECLS @@ -37,6 +39,8 @@ struct _WpEndpointClass const gchar * (*get_endpoint_link_factory) (WpEndpoint * self); }; +WpEndpoint * wp_endpoint_new_finish (GObject *initable, GAsyncResult *res, + GError **error); void wp_endpoint_register (WpEndpoint * self); void wp_endpoint_unregister (WpEndpoint * self); GPtrArray * wp_endpoint_find (WpCore * core, const gchar * media_class_lookup); diff --git a/lib/wp/factory.c b/lib/wp/factory.c index 861f86de793721fc8d345ca880afd1342587da13..82cb6562e7b8f657bef8d25cc657365d84a98b0f 100644 --- a/lib/wp/factory.c +++ b/lib/wp/factory.c @@ -15,7 +15,10 @@ struct _WpFactory GWeakRef core; gchar *name; GQuark name_quark; - WpFactoryFunc create_object; + union { + WpFactoryFunc sync; + WpFactoryAsyncFunc async; + } create_object; }; G_DEFINE_TYPE (WpFactory, wp_factory, G_TYPE_OBJECT) @@ -45,19 +48,29 @@ wp_factory_class_init (WpFactoryClass * klass) object_class->finalize = wp_factory_finalize; } -WpFactory * -wp_factory_new (WpCore * core, const gchar * name, WpFactoryFunc func) +static +WpFactory * create_factory (WpCore * core, const gchar * name) { WpFactory *f = NULL; g_return_val_if_fail (name != NULL && *name != '\0', NULL); - g_return_val_if_fail (func != NULL, NULL); f = g_object_new (WP_TYPE_FACTORY, NULL); g_weak_ref_init (&f->core, core); f->name = g_strdup (name); f->name_quark = g_quark_from_string (f->name); - f->create_object = func; + + return f; +} + +WpFactory * +wp_factory_new (WpCore * core, const gchar * name, WpFactoryFunc func) +{ + WpFactory *f = NULL; + g_return_val_if_fail (func, NULL); + + f = create_factory(core, name); + f->create_object.sync = func; g_info ("WpFactory:%p new factory: %s", f, name); @@ -66,6 +79,23 @@ wp_factory_new (WpCore * core, const gchar * name, WpFactoryFunc func) return f; } +WpFactory * +wp_factory_new_async (WpCore * core, const gchar * name, + WpFactoryAsyncFunc func) +{ + WpFactory *f = NULL; + g_return_val_if_fail (func, NULL); + + f = create_factory(core, name); + f->create_object.async = func; + + g_info ("WpFactory:%p new async factory: %s", f, name); + + wp_core_register_global (core, WP_GLOBAL_FACTORY, f, g_object_unref); + + return f; +} + const gchar * wp_factory_get_name (WpFactory * self) { @@ -89,7 +119,18 @@ wp_factory_create_object (WpFactory * self, GType type, GVariant * properties) { g_debug ("WpFactory:%p (%s) create object of type %s", self, self->name, g_type_name (type)); - return self->create_object (self, type, properties); + + return self->create_object.sync (self, type, properties); +} + +void +wp_factory_create_object_async (WpFactory * self, GType type, + GVariant * properties, GAsyncReadyCallback ready, gpointer user_data) +{ + g_debug ("WpFactory:%p (%s) create object async of type %s", self, self->name, + g_type_name (type)); + + self->create_object.async (self, type, properties, ready, user_data); } struct find_factory_data @@ -127,3 +168,12 @@ wp_factory_make (WpCore * core, const gchar * name, GType type, if (!f) return NULL; return wp_factory_create_object (f, type, properties); } + +void +wp_factory_make_async (WpCore * core, const gchar * name, GType type, + GVariant * properties, GAsyncReadyCallback ready, gpointer user_data) +{ + WpFactory *f = wp_factory_find (core, name); + if (!f) return; + wp_factory_create_object_async (f, type, properties, ready, user_data); +} diff --git a/lib/wp/factory.h b/lib/wp/factory.h index 6ab9613e1e10e8c84a36720876694f4ddd4967ff..9f74c22c593b3a5e4db3694d5275c2e7cb2f47c4 100644 --- a/lib/wp/factory.h +++ b/lib/wp/factory.h @@ -9,6 +9,8 @@ #ifndef __WIREPLUMBER_FACTORY_H__ #define __WIREPLUMBER_FACTORY_H__ +#include <gio/gio.h> + #include "core.h" G_BEGIN_DECLS @@ -18,18 +20,26 @@ G_DECLARE_FINAL_TYPE (WpFactory, wp_factory, WP, FACTORY, GObject) typedef gpointer (*WpFactoryFunc) (WpFactory * self, GType type, GVariant * properties); +typedef void (*WpFactoryAsyncFunc) (WpFactory * self, GType type, + GVariant * properties, GAsyncReadyCallback ready, gpointer user_data); WpFactory * wp_factory_new (WpCore * core, const gchar * name, WpFactoryFunc func); +WpFactory * wp_factory_new_async (WpCore * core, const gchar * name, + WpFactoryAsyncFunc func); const gchar * wp_factory_get_name (WpFactory * self); WpCore * wp_factory_get_core (WpFactory * self); gpointer wp_factory_create_object (WpFactory * self, GType type, GVariant * properties); +void wp_factory_create_object_async (WpFactory * self, GType type, + GVariant * properties, GAsyncReadyCallback ready, gpointer user_data); WpFactory * wp_factory_find (WpCore * core, const gchar * name); gpointer wp_factory_make (WpCore * core, const gchar * name, GType type, GVariant * properties); +void wp_factory_make_async (WpCore * core, const gchar * name, GType type, + GVariant * properties, GAsyncReadyCallback ready, gpointer user_data); G_END_DECLS diff --git a/modules/meson.build b/modules/meson.build index 53ad1eefe1e6b5e565618571c9a0150b72e0e8f8..9b7b2d5ba6e6dbd7c185914032689bd922931328 100644 --- a/modules/meson.build +++ b/modules/meson.build @@ -36,7 +36,7 @@ shared_library( c_args : [common_c_args, '-DG_LOG_DOMAIN="m-pipewire"'], install : true, install_dir : wireplumber_module_dir, - dependencies : [wp_dep, pipewire_dep], + dependencies : [gio_dep, wp_dep, pipewire_dep], ) shared_library( @@ -58,7 +58,7 @@ shared_library( c_args : [common_c_args, '-DG_LOG_DOMAIN="m-pw-audio-softdsp-endpoint"'], install : true, install_dir : wireplumber_module_dir, - dependencies : [wp_dep, pipewire_dep], + dependencies : [gio_dep, wp_dep, pipewire_dep], ) shared_library( diff --git a/modules/module-pipewire.c b/modules/module-pipewire.c index a329fb8b9d13d8fa4107e34d6a80e08bafc51cf8..831e4f966bebd6354569b94f883e6f31eeca2bc7 100644 --- a/modules/module-pipewire.c +++ b/modules/module-pipewire.c @@ -14,12 +14,11 @@ #include <wp/wp.h> #include <pipewire/pipewire.h> -#include <spa/param/audio/format-utils.h> void remote_endpoint_init (WpCore * core, struct pw_core * pw_core, struct pw_remote * remote); -gpointer simple_endpoint_factory (WpFactory * factory, GType type, - GVariant * properties); +void simple_endpoint_factory (WpFactory * factory, GType type, + GVariant * properties, GAsyncReadyCallback ready, gpointer user_data); gpointer simple_endpoint_link_factory (WpFactory * factory, GType type, GVariant * properties); @@ -27,158 +26,40 @@ struct module_data { WpModule *module; WpRemotePipewire *remote_pipewire; - GHashTable *client_nodes_info; -}; - -struct endpoint_info -{ - gchar *name; - gchar *media_class; - struct pw_proxy *proxy; -}; - -struct proxy_info -{ - const struct module_data *data; - uint32_t node_id; - WpProxyPort *proxy_port; }; static void -endpoint_info_destroy(gpointer p) +on_endpoint_created(GObject *initable, GAsyncResult *res, gpointer d) { - struct endpoint_info *ei = p; - - /* Free the name */ - g_free (ei->name); - - /* Free the media class */ - g_free (ei->media_class); - - /* Clean up */ - g_slice_free (struct endpoint_info, p); -} - -static void -proxy_info_destroy(gpointer p) -{ - struct proxy_info *pi = p; - - /* Unref the proxy port */ - g_clear_object (&pi->proxy_port); - - /* Clean up */ - g_slice_free (struct proxy_info, p); -} - -static void -unregister_endpoint (WpProxy* wp_proxy, WpEndpoint *endpoint) -{ - g_return_if_fail(WP_IS_PROXY(wp_proxy)); - g_return_if_fail(WP_IS_ENDPOINT(endpoint)); - - /* Unregister the endpoint */ - wp_endpoint_unregister(endpoint); -} - -static void -proxy_node_created(GObject *initable, GAsyncResult *res, gpointer d) -{ - struct proxy_info *pi = d; - const struct module_data *data = pi->data; - g_autoptr (WpCore) core = wp_module_get_core (data->module); - g_autoptr (WpProxyNode) proxy_node = NULL; - struct endpoint_info *ei = NULL; g_autoptr (WpEndpoint) endpoint = NULL; - g_autoptr (GVariant) endpoint_props = NULL; - GVariantBuilder b; - - /* Get the proxy */ - proxy_node = wp_proxy_node_new_finish(initable, res, NULL); - if (!proxy_node) - return; + guint global_id = 0; - /* Get the client node info */ - ei = g_hash_table_lookup(data->client_nodes_info, - GINT_TO_POINTER(pi->node_id)); - if (!ei) + /* Get the endpoint */ + endpoint = wp_endpoint_new_finish(initable, res, NULL); + if (!endpoint) return; - /* Set the properties */ - g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&b, "{sv}", - "name", ei->name ? g_variant_new_string (ei->name) : - g_variant_new_take_string ( - g_strdup_printf ("Stream %u", pi->node_id))); - g_variant_builder_add (&b, "{sv}", - "media-class", g_variant_new_string (ei->media_class)); - g_variant_builder_add (&b, "{sv}", - "proxy-node", g_variant_new_uint64 ((guint64) proxy_node)); - g_variant_builder_add (&b, "{sv}", - "proxy-port", g_variant_new_uint64 ((guint64) pi->proxy_port)); - endpoint_props = g_variant_builder_end (&b); - - /* Create the endpoint */ - endpoint = wp_factory_make (core, "pipewire-simple-endpoint", - WP_TYPE_ENDPOINT, endpoint_props); + /* Get the endpoint global id */ + g_object_get (endpoint, "global-id", &global_id, NULL); + g_debug ("Created client endpoint for global id %d", global_id); /* Register the endpoint */ wp_endpoint_register (endpoint); - - /* Set destroy handler to unregister endpoint when the proxy is detroyed */ - g_signal_connect (proxy_node, "destroyed", G_CALLBACK(unregister_endpoint), - endpoint); - - /* Clean up */ - proxy_info_destroy (pi); } static void -proxy_port_created(GObject *initable, GAsyncResult *res, gpointer d) -{ - struct proxy_info *pi = d; - const struct module_data *data = pi->data; - struct endpoint_info *ei = NULL; - WpProxyPort *proxy_port = NULL; - - /* Get the proxy port */ - proxy_port = wp_proxy_port_new_finish(initable, res, NULL); - if (!proxy_port) - return; - - /* Forward the proxy port */ - pi->proxy_port = proxy_port; - - /* Get the node proxy */ - ei = g_hash_table_lookup(data->client_nodes_info, - GINT_TO_POINTER(pi->node_id)); - if (!ei) - return; - - /* Create the proxy node asynchronically */ - wp_proxy_node_new(pi->node_id, ei->proxy, proxy_node_created, pi); -} - -static void -handle_node (WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p, +on_node_added (WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p, gpointer d) { struct module_data *data = d; const struct spa_dict *props = p; - struct endpoint_info *ei = NULL; - const gchar *name; - const gchar *media_class; - struct pw_proxy *proxy; - struct spa_audio_info_raw format = { 0, }; - struct spa_pod *param; - struct spa_pod_builder pod_builder = { 0, }; - char buf[1024]; + g_autoptr (WpCore) core = wp_module_get_core (data->module); + const gchar *name, *media_class; + GVariantBuilder b; + g_autoptr (GVariant) endpoint_props = NULL; /* Make sure the node has properties */ - if (!props) { - g_warning("node has no properties, skipping..."); - return; - } + g_return_if_fail(props); /* Get the media_class */ media_class = spa_dict_lookup(props, "media.class"); @@ -192,67 +73,20 @@ handle_node (WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p, if (!name) name = spa_dict_lookup (props, "node.name"); - g_debug ("found stream node: id:%u ; name:%s ; media_class:%s", id, name, - media_class); - - /* Bind the proxy */ - proxy = wp_remote_pipewire_proxy_bind(rp, id, PW_TYPE_INTERFACE_Node); - if (!proxy) - return; - - /* TODO: Assume all clients have this format for now */ - format.format = SPA_AUDIO_FORMAT_F32P; - format.flags = 1; - format.rate = 48000; - format.channels = 1; - format.position[0] = 0; - - /* Set the profile */ - spa_pod_builder_init(&pod_builder, buf, sizeof(buf)); - param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format); - param = spa_pod_builder_add_object(&pod_builder, - SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, - SPA_PARAM_PROFILE_direction, SPA_POD_Id(PW_DIRECTION_OUTPUT), - SPA_PARAM_PROFILE_format, SPA_POD_Pod(param)); - pw_node_proxy_set_param((struct pw_node_proxy*)proxy, - SPA_PARAM_Profile, 0, param); - - /* Create the endpoint info */ - ei = g_slice_new0 (struct endpoint_info); - ei->name = g_strdup(name); - ei->media_class = g_strdup(media_class); - ei->proxy = proxy; - - /* Insert the client node info in the hash table */ - g_hash_table_insert(data->client_nodes_info, GINT_TO_POINTER (id), ei); -} - -static void -handle_port(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p, - gpointer d) -{ - struct module_data *data = d; - struct proxy_info *pi = NULL; - struct pw_proxy *proxy = NULL; - - /* Only handle ports whose parent is an alsa node */ - if (!g_hash_table_contains(data->client_nodes_info, - GINT_TO_POINTER (parent_id))) - return; - - /* Bind the proxy */ - proxy = wp_remote_pipewire_proxy_bind (rp, id, PW_TYPE_INTERFACE_Port); - if (!proxy) - return; - - /* Create the port info */ - pi = g_slice_new0 (struct proxy_info); - pi->data = data; - pi->node_id = parent_id; - pi->proxy_port = NULL; + /* 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))); + g_variant_builder_add (&b, "{sv}", + "media-class", g_variant_new_string (media_class)); + g_variant_builder_add (&b, "{sv}", + "global-id", g_variant_new_uint32 (id)); + endpoint_props = g_variant_builder_end (&b); - /* Create the proxy port asynchronically */ - wp_proxy_port_new(id, proxy, proxy_port_created, pi); + /* Create the endpoint async */ + wp_factory_make_async (core, "pipewire-simple-endpoint", WP_TYPE_ENDPOINT, + endpoint_props, on_endpoint_created, data); } static void @@ -264,10 +98,6 @@ module_destroy (gpointer d) data->module = NULL; data->remote_pipewire = NULL; - /* Destroy the hash table */ - g_hash_table_unref (data->client_nodes_info); - data->client_nodes_info = NULL; - /* Clean up */ g_slice_free (struct module_data, data); } @@ -292,22 +122,20 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args) data = g_slice_new0 (struct module_data); data->module = module; data->remote_pipewire = rp; - data->client_nodes_info = g_hash_table_new_full (g_direct_hash, - g_direct_equal, NULL, endpoint_info_destroy); /* Set the module destroy callback */ wp_module_set_destroy_callback (module, module_destroy, data); - /* Register the global addded callbacks */ - g_signal_connect(rp, "global-added::node", (GCallback)handle_node, data); - g_signal_connect(rp, "global-added::port", (GCallback)handle_port, data); + /* Register the global added/removed callbacks */ + g_signal_connect(rp, "global-added::node", (GCallback)on_node_added, data); /* Init remoted endpoint */ g_object_get (rp, "pw-core", &pw_core, "pw-remote", &pw_remote, NULL); remote_endpoint_init (core, pw_core, pw_remote); /* Register simple-endpoint and simple-endpoint-link */ - wp_factory_new (core, "pipewire-simple-endpoint", simple_endpoint_factory); + wp_factory_new_async (core, "pipewire-simple-endpoint", + simple_endpoint_factory); wp_factory_new (core, "pipewire-simple-endpoint-link", simple_endpoint_link_factory); } diff --git a/modules/module-pipewire/simple-endpoint-link.c b/modules/module-pipewire/simple-endpoint-link.c index 9b8993694f9b33c9c6e21d51ae7fbc3204c2442e..a7e8edc71090c2cfa32fadd1d7a9c6c9356459b0 100644 --- a/modules/module-pipewire/simple-endpoint-link.c +++ b/modules/module-pipewire/simple-endpoint-link.c @@ -48,31 +48,57 @@ simple_endpoint_link_create (WpEndpointLink * epl, GVariant * src_data, { WpPipewireSimpleEndpointLink *self = WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK(epl); struct pw_properties *props; - guint32 output_node_id, input_node_id, output_port_id, input_port_id; + guint32 output_node_id, input_node_id; + GVariant *src_ports, *sink_ports; + GVariantIter *out_iter, *in_iter; + guint64 out_ptr, in_ptr; /* Get the node ids and port ids */ if (!g_variant_lookup (src_data, "node-id", "u", &output_node_id)) return FALSE; - if (!g_variant_lookup (src_data, "node-port-id", "u", &output_port_id)) + src_ports = g_variant_lookup_value (src_data, "ports", G_VARIANT_TYPE_ARRAY); + if (!src_ports) return FALSE; if (!g_variant_lookup (sink_data, "node-id", "u", &input_node_id)) return FALSE; - if (!g_variant_lookup (sink_data, "node-port-id", "u", &input_port_id)) + sink_ports = g_variant_lookup_value (sink_data, "ports", G_VARIANT_TYPE_ARRAY); + if (!sink_ports) return FALSE; - /* Create the properties */ - props = pw_properties_new(NULL, NULL); - pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", output_node_id); - pw_properties_setf(props, PW_LINK_OUTPUT_PORT_ID, "%d", output_port_id); - pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", input_node_id); - pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", input_port_id); - - /* Create the link */ - pw_core_proxy_create_object(self->core_proxy, "link-factory", - PW_TYPE_INTERFACE_Link, PW_VERSION_LINK, &props->dict, 0); - - /* Clean up */ - pw_properties_free(props); + /* Link all the output ports with the input ports */ + g_variant_get (src_ports, "at", &out_iter); + while (g_variant_iter_loop (out_iter, "t", &out_ptr)) { + WpProxyPort *out_p = (gpointer)out_ptr; + enum pw_direction out_direction = wp_proxy_port_get_info(out_p)->direction; + guint out_id = wp_proxy_get_global_id(WP_PROXY(out_p)); + if (out_direction == PW_DIRECTION_INPUT) + continue; + + g_variant_get (sink_ports, "at", &in_iter); + while (g_variant_iter_loop (in_iter, "t", &in_ptr)) { + WpProxyPort *in_p = (gpointer)in_ptr; + enum pw_direction in_direction = wp_proxy_port_get_info(in_p)->direction; + guint in_id = wp_proxy_get_global_id(WP_PROXY(in_p)); + if (in_direction == PW_DIRECTION_OUTPUT) + continue; + + /* Create the properties */ + props = pw_properties_new(NULL, NULL); + pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", output_node_id); + pw_properties_setf(props, PW_LINK_OUTPUT_PORT_ID, "%d", out_id); + pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", input_node_id); + pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", in_id); + + /* Create the link */ + pw_core_proxy_create_object(self->core_proxy, "link-factory", + PW_TYPE_INTERFACE_Link, PW_VERSION_LINK, &props->dict, 0); + + /* Clean up */ + pw_properties_free(props); + } + g_variant_iter_free (in_iter); + } + g_variant_iter_free (out_iter); return TRUE; } diff --git a/modules/module-pipewire/simple-endpoint.c b/modules/module-pipewire/simple-endpoint.c index 6f9aa6937ccf98941396b3a048f926b0b55827e2..6f617d50e70bb143032f89e65cf304147d335517 100644 --- a/modules/module-pipewire/simple-endpoint.c +++ b/modules/module-pipewire/simple-endpoint.c @@ -13,6 +13,8 @@ * other arbitrary node that does not need any kind of internal management. */ +#include <spa/param/audio/format-utils.h> + #include <wp/wp.h> #include <pipewire/pipewire.h> #include <spa/pod/parser.h> @@ -22,10 +24,19 @@ struct _WpPipewireSimpleEndpoint { WpEndpoint parent; - /* Proxy */ + /* The global-id this endpoint refers to */ + guint global_id; + + /* The task to signal the endpoint is initialized */ + GTask *init_task; + + /* The remote pipewire */ + WpRemotePipewire *remote_pipewire; + + /* Proxies */ WpProxyNode *proxy_node; - WpProxyPort *proxy_port; struct spa_hook node_proxy_listener; + GPtrArray *proxies_port; /* controls cache */ gfloat volume; @@ -34,8 +45,7 @@ struct _WpPipewireSimpleEndpoint enum { PROP_0, - PROP_NODE_PROXY, - PROP_PORT_PROXY + PROP_GLOBAL_ID, }; enum { @@ -43,10 +53,17 @@ enum { CONTROL_MUTE, }; +static GAsyncInitableIface *wp_simple_endpoint_parent_interface = NULL; +static void wp_simple_endpoint_async_initable_init (gpointer iface, + gpointer iface_data); + G_DECLARE_FINAL_TYPE (WpPipewireSimpleEndpoint, simple_endpoint, WP_PIPEWIRE, SIMPLE_ENDPOINT, WpEndpoint) -G_DEFINE_TYPE (WpPipewireSimpleEndpoint, simple_endpoint, WP_TYPE_ENDPOINT) +G_DEFINE_TYPE_WITH_CODE (WpPipewireSimpleEndpoint, simple_endpoint, + WP_TYPE_ENDPOINT, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, + wp_simple_endpoint_async_initable_init)) static void node_proxy_param (void *object, int seq, uint32_t id, @@ -100,21 +117,122 @@ static const struct pw_node_proxy_events node_node_proxy_events = { }; static void -simple_endpoint_init (WpPipewireSimpleEndpoint * self) +on_proxy_node_destroyed (WpProxy* wp_proxy, WpEndpoint *endpoint) { + g_return_if_fail(WP_IS_ENDPOINT(endpoint)); + + /* Unregister the endpoint */ + wp_endpoint_unregister(endpoint); } static void -simple_endpoint_constructed (GObject * object) +on_all_ports_done(WpProxy *proxy, gpointer data) { - WpPipewireSimpleEndpoint *self = WP_PIPEWIRE_SIMPLE_ENDPOINT (object); + WpPipewireSimpleEndpoint *self = data; + + /* Don't do anything if the endpoint has already been initialized */ + if (!self->init_task) + return; + + /* Set destroy handler to unregister endpoint on proxy_node destruction */ + g_signal_connect (self->proxy_node, "destroyed", + G_CALLBACK(on_proxy_node_destroyed), WP_ENDPOINT(self)); + + /* Finish the creation of the endpoint */ + g_task_return_boolean (self->init_task, TRUE); + g_clear_object(&self->init_task); +} + +static void +on_proxy_port_created(GObject *initable, GAsyncResult *res, gpointer data) +{ + WpPipewireSimpleEndpoint *self = data; + WpProxyPort *proxy_port = NULL; + + /* Get the proxy port */ + proxy_port = wp_proxy_port_new_finish(initable, res, NULL); + g_return_if_fail (proxy_port); + + /* Add the proxy port to the array */ + g_return_if_fail (self->proxies_port); + g_ptr_array_add(self->proxies_port, proxy_port); + + /* Register the done callback */ + g_signal_connect(self->proxy_node, "done", (GCallback)on_all_ports_done, + self); + wp_proxy_sync (WP_PROXY(self->proxy_node)); +} + +static void +on_port_added(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p, + gpointer d) +{ + WpPipewireSimpleEndpoint *self = d; + struct pw_port_proxy *port_proxy = NULL; + + /* Only handle ports owned by this endpoint */ + if (parent_id != self->global_id) + return; + + /* Create the proxy port async */ + port_proxy = wp_remote_pipewire_proxy_bind (self->remote_pipewire, id, + PW_TYPE_INTERFACE_Port); + g_return_if_fail(port_proxy); + wp_proxy_port_new(id, port_proxy, on_proxy_port_created, self); +} + +static void +emit_endpoint_ports(WpPipewireSimpleEndpoint *self) +{ + struct pw_node_proxy* node_proxy = NULL; + struct spa_audio_info_raw format = { 0, }; + struct spa_pod *param; + struct spa_pod_builder pod_builder = { 0, }; + char buf[1024]; + + /* Get the pipewire node proxy */ + node_proxy = wp_proxy_get_pw_proxy(WP_PROXY(self->proxy_node)); + g_return_if_fail (node_proxy); + + /* TODO: Assume all clients have this format for now */ + format.format = SPA_AUDIO_FORMAT_F32P; + format.flags = 1; + format.rate = 48000; + format.channels = 2; + format.position[0] = 0; + format.position[1] = 0; + + /* Build the param profile */ + spa_pod_builder_init(&pod_builder, buf, sizeof(buf)); + param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format); + param = spa_pod_builder_add_object(&pod_builder, + SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, + SPA_PARAM_PROFILE_direction, SPA_POD_Id(PW_DIRECTION_OUTPUT), + SPA_PARAM_PROFILE_format, SPA_POD_Pod(param)); + + /* Set the param profile to emit the ports */ + pw_node_proxy_set_param(node_proxy, SPA_PARAM_Profile, 0, param); +} + +static void +on_proxy_node_created(GObject *initable, GAsyncResult *res, gpointer data) +{ + WpPipewireSimpleEndpoint *self = data; GVariantDict d; uint32_t ids[1] = { SPA_PARAM_Props }; uint32_t n_ids = 1; struct pw_node_proxy *node_proxy = NULL; + /* Get the proxy node */ + self->proxy_node = wp_proxy_node_new_finish(initable, res, NULL); + g_return_if_fail (self->proxy_node); + + /* Emit the ports */ + emit_endpoint_ports(self); + /* Add a custom node proxy event listener */ node_proxy = wp_proxy_get_pw_proxy(WP_PROXY(self->proxy_node)); + g_return_if_fail (node_proxy); pw_node_proxy_add_listener (node_proxy, &self->node_proxy_listener, &node_node_proxy_events, self); pw_node_proxy_subscribe_params (node_proxy, ids, n_ids); @@ -143,8 +261,54 @@ simple_endpoint_constructed (GObject * object) g_variant_dict_insert (&d, "default-value", "b", FALSE); wp_endpoint_register_control (WP_ENDPOINT (self), g_variant_dict_end (&d)); } +} + +static void +wp_simple_endpoint_init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) +{ + WpPipewireSimpleEndpoint *self = WP_PIPEWIRE_SIMPLE_ENDPOINT (initable); + g_autoptr (WpCore) core = wp_endpoint_get_core(WP_ENDPOINT(self)); + struct pw_node_proxy *node_proxy = NULL; + + /* Create the async task */ + self->init_task = g_task_new (initable, cancellable, callback, data); + + /* Init the proxies_port array */ + self->proxies_port = g_ptr_array_new_full(2, (GDestroyNotify)g_object_unref); + + /* Register a port_added callback */ + self->remote_pipewire = wp_core_get_global (core, WP_GLOBAL_REMOTE_PIPEWIRE); + g_return_if_fail(self->remote_pipewire); + g_signal_connect(self->remote_pipewire, "global-added::port", + (GCallback)on_port_added, self); + + /* Create the proxy node async */ + node_proxy = wp_remote_pipewire_proxy_bind (self->remote_pipewire, + self->global_id, PW_TYPE_INTERFACE_Node); + g_return_if_fail(node_proxy); + wp_proxy_node_new(self->global_id, node_proxy, on_proxy_node_created, self); + + /* Call the parent interface */ + wp_simple_endpoint_parent_interface->init_async (initable, io_priority, + cancellable, callback, data); +} + +static void +wp_simple_endpoint_async_initable_init (gpointer iface, gpointer iface_data) +{ + GAsyncInitableIface *ai_iface = iface; + + /* Set the parent interface */ + wp_simple_endpoint_parent_interface = g_type_interface_peek_parent (iface); + + /* Only set the init_async */ + ai_iface->init_async = wp_simple_endpoint_init_async; +} - G_OBJECT_CLASS (simple_endpoint_parent_class)->constructed (object); +static void +simple_endpoint_init (WpPipewireSimpleEndpoint * self) +{ } static void @@ -152,12 +316,18 @@ simple_endpoint_finalize (GObject * object) { WpPipewireSimpleEndpoint *self = WP_PIPEWIRE_SIMPLE_ENDPOINT (object); - /* Unref the proxy node */ - if (self->proxy_node) { - g_object_unref(self->proxy_node); - self->proxy_node = NULL; + /* Destroy the proxies port */ + if (self->proxies_port) { + g_ptr_array_free(self->proxies_port, TRUE); + self->proxies_port = NULL; } + /* Destroy the proxy node */ + g_clear_object(&self->proxy_node); + + /* Destroy the done task */ + g_clear_object(&self->init_task); + G_OBJECT_CLASS (simple_endpoint_parent_class)->finalize (object); } @@ -168,13 +338,8 @@ simple_endpoint_set_property (GObject * object, guint property_id, WpPipewireSimpleEndpoint *self = WP_PIPEWIRE_SIMPLE_ENDPOINT (object); switch (property_id) { - case PROP_NODE_PROXY: - g_clear_object(&self->proxy_node); - self->proxy_node = g_value_dup_object(value); - break; - case PROP_PORT_PROXY: - g_clear_object(&self->proxy_port); - self->proxy_port = g_value_dup_object(value); + case PROP_GLOBAL_ID: + self->global_id = g_value_get_uint(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -189,11 +354,8 @@ simple_endpoint_get_property (GObject * object, guint property_id, WpPipewireSimpleEndpoint *self = WP_PIPEWIRE_SIMPLE_ENDPOINT (object); switch (property_id) { - case PROP_NODE_PROXY: - g_value_set_object (value, self->proxy_node); - break; - case PROP_PORT_PROXY: - g_value_set_object (value, self->proxy_port); + case PROP_GLOBAL_ID: + g_value_set_uint (value, self->global_id); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -201,21 +363,31 @@ simple_endpoint_get_property (GObject * object, guint property_id, } } +static void +proxies_port_foreach_func(gpointer data, gpointer user_data) +{ + GVariantBuilder *b = user_data; + g_variant_builder_add (b, "t", data); +} + static gboolean simple_endpoint_prepare_link (WpEndpoint * ep, guint32 stream_id, WpEndpointLink * link, GVariant ** properties, GError ** error) { WpPipewireSimpleEndpoint *self = WP_PIPEWIRE_SIMPLE_ENDPOINT (ep); - uint32_t node_id = wp_proxy_get_global_id(WP_PROXY(self->proxy_node)); - uint32_t port_id = wp_proxy_get_global_id(WP_PROXY(self->proxy_port)); - GVariantBuilder b; + GVariantBuilder b, *b_ports; + GVariant *v_ports; + + /* Create a variant array with all the ports */ + b_ports = g_variant_builder_new (G_VARIANT_TYPE ("at")); + g_ptr_array_foreach(self->proxies_port, proxies_port_foreach_func, b_ports); + v_ports = g_variant_builder_end (b_ports); /* Set the properties */ g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&b, "{sv}", "node-id", - g_variant_new_uint32 (node_id)); - g_variant_builder_add (&b, "{sv}", "node-port-id", - g_variant_new_uint32 (port_id)); + g_variant_new_uint32 (self->global_id)); + g_variant_builder_add (&b, "{sv}", "ports", v_ports); *properties = g_variant_builder_end (&b); return TRUE; @@ -294,7 +466,6 @@ simple_endpoint_class_init (WpPipewireSimpleEndpointClass * klass) GObjectClass *object_class = (GObjectClass *) klass; WpEndpointClass *endpoint_class = (WpEndpointClass *) klass; - object_class->constructed = simple_endpoint_constructed; object_class->finalize = simple_endpoint_finalize; object_class->set_property = simple_endpoint_set_property; object_class->get_property = simple_endpoint_get_property; @@ -303,48 +474,40 @@ simple_endpoint_class_init (WpPipewireSimpleEndpointClass * klass) endpoint_class->get_control_value = simple_endpoint_get_control_value; endpoint_class->set_control_value = simple_endpoint_set_control_value; - g_object_class_install_property (object_class, PROP_NODE_PROXY, - g_param_spec_object ("node-proxy", "node-proxy", - "Pointer to the node proxy of the client", WP_TYPE_PROXY_NODE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, PROP_PORT_PROXY, - g_param_spec_object ("port-proxy", "port-proxy", - "Pointer to the port proxy of the client", WP_TYPE_PROXY_PORT, + g_object_class_install_property (object_class, PROP_GLOBAL_ID, + g_param_spec_uint ("global-id", "global-id", + "The global Id this endpoint refers to", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } -gpointer +void simple_endpoint_factory (WpFactory * factory, GType type, - GVariant * properties) + GVariant * properties, GAsyncReadyCallback ready, gpointer user_data) { g_autoptr (WpCore) core = NULL; - guint64 proxy_node; - guint64 proxy_port; - const gchar *name; - const gchar *media_class; + const gchar *name, *media_class; + guint global_id; - g_return_val_if_fail (type == WP_TYPE_ENDPOINT, NULL); - g_return_val_if_fail (properties != NULL, NULL); - g_return_val_if_fail (g_variant_is_of_type (properties, - G_VARIANT_TYPE_VARDICT), NULL); + /* Make sure the type is correct */ + g_return_if_fail (type == WP_TYPE_ENDPOINT); + /* Get the Core */ core = wp_factory_get_core (factory); - g_return_val_if_fail (core != NULL, NULL); + g_return_if_fail (core); + /* Get the properties */ if (!g_variant_lookup (properties, "name", "&s", &name)) - return NULL; + return; if (!g_variant_lookup (properties, "media-class", "&s", &media_class)) - return NULL; - if (!g_variant_lookup (properties, "proxy-node", "t", &proxy_node)) - return NULL; - if (!g_variant_lookup (properties, "proxy-port", "t", &proxy_port)) - return NULL; + return; + if (!g_variant_lookup (properties, "global-id", "u", &global_id)) + return; - return g_object_new (simple_endpoint_get_type (), + g_async_initable_new_async ( + simple_endpoint_get_type (), G_PRIORITY_DEFAULT, NULL, ready, user_data, "core", core, "name", name, "media-class", media_class, - "node-proxy", (gpointer) proxy_node, - "port-proxy", (gpointer) proxy_port, + "global-id", global_id, NULL); } diff --git a/modules/module-pw-alsa-udev.c b/modules/module-pw-alsa-udev.c index c98cd76cf713d263b30236bdd813e1bbb8cc141a..576e1e2cd141b38a86dad3d99c14f520cc9b5fe5 100644 --- a/modules/module-pw-alsa-udev.c +++ b/modules/module-pw-alsa-udev.c @@ -19,143 +19,37 @@ struct impl { WpModule *module; WpRemotePipewire *remote_pipewire; - GHashTable *alsa_nodes_info; }; -struct endpoint_info -{ - gchar *name; - gchar *media_class; -}; - -struct proxy_info -{ - const struct impl *impl; - uint32_t node_id; - WpProxyPort *proxy_port; -}; - -static void -endpoint_info_destroy(gpointer p) -{ - struct endpoint_info *ei = p; - - /* Free the name */ - g_free (ei->name); - - /* Free the media class */ - g_free (ei->media_class); - - /* Clean up */ - g_slice_free (struct endpoint_info, p); -} - static void -proxy_info_destroy(gpointer p) +on_endpoint_created(GObject *initable, GAsyncResult *res, gpointer d) { - struct proxy_info *pi = p; - - /* Unref the proxy port */ - g_clear_object (&pi->proxy_port); - - /* Clean up */ - g_slice_free (struct proxy_info, p); -} - -static void -unregister_endpoint (WpProxy* wp_proxy, WpEndpoint *endpoint) -{ - g_return_if_fail(WP_IS_PROXY(wp_proxy)); - g_return_if_fail(WP_IS_ENDPOINT(endpoint)); - - /* Unregister the endpoint */ - wp_endpoint_unregister(endpoint); -} - -static void -proxy_node_created(GObject *initable, GAsyncResult *res, gpointer data) -{ - struct proxy_info *pi = data; - const struct impl *impl = pi->impl; - g_autoptr (WpCore) core = wp_module_get_core (impl->module); - g_autoptr(WpProxyNode) proxy_node = NULL; - struct endpoint_info *ei = NULL; - GVariantBuilder b; - g_autoptr (GVariant) endpoint_props = NULL; g_autoptr (WpEndpoint) endpoint = NULL; + guint global_id = 0; - /* Get the proxy */ - proxy_node = wp_proxy_node_new_finish(initable, res, NULL); - if (!proxy_node) - return; - - /* Get the alsa node info */ - ei = g_hash_table_lookup(impl->alsa_nodes_info, GINT_TO_POINTER(pi->node_id)); - if (!ei) + /* Get the endpoint */ + endpoint = wp_endpoint_new_finish(initable, res, NULL); + if (!endpoint) return; - /* Build the properties for the endpoint */ - g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT); - g_variant_builder_add (&b, "{sv}", "name", - g_variant_new_take_string (g_strdup_printf ("Endpoint %u: %s", - pi->node_id, ei->name))); - g_variant_builder_add (&b, "{sv}", - "media-class", g_variant_new_string (ei->media_class)); - g_variant_builder_add (&b, "{sv}", - "proxy-node", g_variant_new_uint64 ((guint64) proxy_node)); - g_variant_builder_add (&b, "{sv}", - "proxy-port", g_variant_new_uint64 ((guint64) pi->proxy_port)); - endpoint_props = g_variant_builder_end (&b); - - /* Create and register the endpoint */ - endpoint = wp_factory_make (core, "pw-audio-softdsp-endpoint", - WP_TYPE_ENDPOINT, endpoint_props); + /* Get the endpoint global id */ + g_object_get (endpoint, "global-id", &global_id, NULL); + g_debug ("Created alsa endpoint for global id %d", global_id); /* Register the endpoint */ wp_endpoint_register (endpoint); - - /* Set destroy handler to unregister endpoint when the proxy is detroyed */ - g_signal_connect (proxy_node, "destroyed", G_CALLBACK(unregister_endpoint), - endpoint); - - /* Clean up */ - proxy_info_destroy (pi); -} - -static void -proxy_port_created(GObject *initable, GAsyncResult *res, gpointer data) -{ - struct proxy_info *pi = data; - const struct impl *impl = pi->impl; - WpProxyPort *proxy_port = NULL; - struct pw_proxy *proxy = NULL; - - /* Get the proxy port */ - proxy_port = wp_proxy_port_new_finish(initable, res, NULL); - if (!proxy_port) - return; - - /* Forward the proxy port */ - pi->proxy_port = proxy_port; - - /* Get the node proxy */ - proxy = wp_remote_pipewire_proxy_bind (impl->remote_pipewire, pi->node_id, - PW_TYPE_INTERFACE_Node); - if (!proxy) - return; - - /* Create the proxy node asynchronically */ - wp_proxy_node_new(pi->node_id, proxy, proxy_node_created, pi); } static void -handle_node(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p, +on_node_added(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p, gpointer d) { struct impl *impl = d; const struct spa_dict *props = p; - const gchar *media_class = NULL, *name = NULL; - struct endpoint_info *ei = NULL; + g_autoptr (WpCore) core = wp_module_get_core (impl->module); + const gchar *name, *media_class; + GVariantBuilder b; + g_autoptr (GVariant) endpoint_props = NULL; /* Make sure the node has properties */ g_return_if_fail(props); @@ -170,40 +64,20 @@ handle_node(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p, if (g_str_has_prefix (media_class, "Audio/DSP")) return; - /* Create the endpoint info */ - ei = g_slice_new0 (struct endpoint_info); - ei->name = g_strdup(name); - ei->media_class = g_strdup(media_class); - - /* Insert the alsa node info in the hash table */ - g_hash_table_insert(impl->alsa_nodes_info, GINT_TO_POINTER (id), ei); -} - -static void -handle_port(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p, - gpointer d) -{ - struct impl *impl = d; - struct proxy_info *pi = NULL; - struct pw_proxy *proxy = NULL; - - /* Only handle ports whose parent is an alsa node */ - if (!g_hash_table_contains(impl->alsa_nodes_info, GINT_TO_POINTER (parent_id))) - return; - - /* Get the port proxy */ - proxy = wp_remote_pipewire_proxy_bind (rp, id, PW_TYPE_INTERFACE_Port); - if (!proxy) - return; - - /* Create the port info */ - pi = g_slice_new0 (struct proxy_info); - pi->impl = impl; - pi->node_id = parent_id; - pi->proxy_port = NULL; + /* Set the properties */ + g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT); + g_variant_builder_add (&b, "{sv}", + "name", g_variant_new_take_string (g_strdup_printf ( + "Endpoint %u: %s", id, name))); + g_variant_builder_add (&b, "{sv}", + "media-class", g_variant_new_string (media_class)); + g_variant_builder_add (&b, "{sv}", + "global-id", g_variant_new_uint32 (id)); + endpoint_props = g_variant_builder_end (&b); - /* Create the proxy port asynchronically */ - wp_proxy_port_new(id, proxy, proxy_port_created, pi); + /* Create the endpoint async */ + wp_factory_make_async (core, "pw-audio-softdsp-endpoint", WP_TYPE_ENDPOINT, + endpoint_props, on_endpoint_created, impl); } static void @@ -215,10 +89,6 @@ module_destroy (gpointer data) impl->module = NULL; impl->remote_pipewire = NULL; - /* Destroy the hash table */ - g_hash_table_unref (impl->alsa_nodes_info); - impl->alsa_nodes_info = NULL; - /* Clean up */ g_slice_free (struct impl, impl); } @@ -241,13 +111,10 @@ wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args) impl = g_slice_new0(struct impl); impl->module = module; impl->remote_pipewire = rp; - impl->alsa_nodes_info = g_hash_table_new_full (g_direct_hash, - g_direct_equal, NULL, endpoint_info_destroy); /* Set destroy callback for impl */ wp_module_set_destroy_callback (module, module_destroy, impl); /* Register the global addded callbacks */ - g_signal_connect(rp, "global-added::node", (GCallback)handle_node, impl); - g_signal_connect(rp, "global-added::port", (GCallback)handle_port, impl); + g_signal_connect(rp, "global-added::node", (GCallback)on_node_added, impl); } diff --git a/modules/module-pw-audio-softdsp-endpoint.c b/modules/module-pw-audio-softdsp-endpoint.c index c728dce55b3193b51f6720b35751f89fd5879886..d7da449d82cf67f06a491339e752e60a2b1b1d92 100644 --- a/modules/module-pw-audio-softdsp-endpoint.c +++ b/modules/module-pw-audio-softdsp-endpoint.c @@ -26,6 +26,12 @@ struct _WpPwAudioSoftdspEndpoint { WpEndpoint parent; + /* The global-id this endpoint refers to */ + guint global_id; + + /* The task to signal the endpoint is initialized */ + GTask *init_task; + /* The remote pipewire */ WpRemotePipewire *remote_pipewire; @@ -36,28 +42,24 @@ struct _WpPwAudioSoftdspEndpoint /* Direction */ enum pw_direction direction; - /* Proxy */ + /* Proxies */ WpProxyNode *proxy_node; WpProxyPort *proxy_port; - - /* DSP port id */ - uint32_t dsp_port_id; + WpProxyNode *proxy_dsp; + GPtrArray *proxies_dsp_port; /* Volume */ gfloat master_volume; gboolean master_mute; - /* TODO: This needs to use the new proxy API */ - struct pw_node_proxy *dsp_proxy; + /* DSP */ struct spa_hook dsp_listener; - struct pw_node_info *dsp_info; struct pw_proxy *link_proxy; }; enum { PROP_0, - PROP_NODE_PROXY, - PROP_PORT_PROXY, + PROP_GLOBAL_ID, }; enum { @@ -66,28 +68,48 @@ enum { CONTROL_SELECTED, }; +static GAsyncInitableIface *wp_endpoint_parent_interface = NULL; +static void wp_endpoint_async_initable_init (gpointer iface, + gpointer iface_data); + G_DECLARE_FINAL_TYPE (WpPwAudioSoftdspEndpoint, endpoint, WP_PW, AUDIO_SOFTDSP_ENDPOINT, WpEndpoint) -G_DEFINE_TYPE (WpPwAudioSoftdspEndpoint, endpoint, WP_TYPE_ENDPOINT) +G_DEFINE_TYPE_WITH_CODE (WpPwAudioSoftdspEndpoint, endpoint, WP_TYPE_ENDPOINT, + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, + wp_endpoint_async_initable_init)) + +static void +proxies_dsp_port_foreach_func(gpointer data, gpointer user_data) +{ + GVariantBuilder *b = user_data; + g_variant_builder_add (b, "t", data); +} static gboolean endpoint_prepare_link (WpEndpoint * ep, guint32 stream_id, WpEndpointLink * link, GVariant ** properties, GError ** error) { WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (ep); - GVariantBuilder b; + const struct pw_node_info *dsp_info = NULL; + GVariantBuilder b, *b_ports; + GVariant *v_ports; + + /* Get the dsp info */ + dsp_info = wp_proxy_node_get_info(self->proxy_dsp); + g_return_val_if_fail (dsp_info, FALSE); - /* Make sure dsp info is valid */ - if (!self->dsp_info) - return FALSE; + /* Create a variant array with all the ports */ + b_ports = g_variant_builder_new (G_VARIANT_TYPE ("at")); + g_ptr_array_foreach(self->proxies_dsp_port, proxies_dsp_port_foreach_func, + b_ports); + v_ports = g_variant_builder_end (b_ports); /* Set the properties */ g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&b, "{sv}", "node-id", - g_variant_new_uint32 (self->dsp_info->id)); - g_variant_builder_add (&b, "{sv}", "node-port-id", - g_variant_new_uint32 (self->dsp_port_id)); + g_variant_new_uint32 (dsp_info->id)); + g_variant_builder_add (&b, "{sv}", "ports", v_ports); *properties = g_variant_builder_end (&b); return TRUE; @@ -98,15 +120,18 @@ on_dsp_running(WpPwAudioSoftdspEndpoint *self) { struct pw_properties *props; const struct pw_node_info *node_info = NULL; + const struct pw_node_info *dsp_info = NULL; /* Return if the node has already been linked */ - if (self->link_proxy) - return; + g_return_if_fail (!self->link_proxy); /* Get the node info */ node_info = wp_proxy_node_get_info(self->proxy_node); - if (!node_info) - return; + g_return_if_fail (node_info); + + /* Get the dsp info */ + dsp_info = wp_proxy_node_get_info(self->proxy_dsp); + g_return_if_fail (dsp_info); /* Create new properties */ props = pw_properties_new(NULL, NULL); @@ -114,14 +139,14 @@ on_dsp_running(WpPwAudioSoftdspEndpoint *self) /* Set the new properties */ pw_properties_set(props, PW_LINK_PROP_PASSIVE, "true"); if (self->direction == PW_DIRECTION_OUTPUT) { - pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", self->dsp_info->id); + pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", dsp_info->id); pw_properties_setf(props, PW_LINK_OUTPUT_PORT_ID, "%d", -1); pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", node_info->id); pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", -1); } else { pw_properties_setf(props, PW_LINK_OUTPUT_NODE_ID, "%d", node_info->id); pw_properties_setf(props, PW_LINK_OUTPUT_PORT_ID, "%d", -1); - pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", self->dsp_info->id); + pw_properties_setf(props, PW_LINK_INPUT_NODE_ID, "%d", dsp_info->id); pw_properties_setf(props, PW_LINK_INPUT_PORT_ID, "%d", -1); } @@ -150,9 +175,6 @@ dsp_node_event_info (void *data, const struct pw_node_info *info) { WpPwAudioSoftdspEndpoint *self = data; - /* Set dsp info */ - self->dsp_info = pw_node_info_update(self->dsp_info, info); - /* Handle the different states */ switch (info->state) { case PW_NODE_STATE_IDLE: @@ -221,32 +243,88 @@ static const struct pw_node_proxy_events dsp_node_events = { }; static void -emit_audio_dsp_node (WpPwAudioSoftdspEndpoint *self) +on_proxy_node_destroyed (WpProxy* wp_proxy, WpEndpoint *endpoint) { - struct pw_properties *props; - const char *dsp_name = NULL; + g_return_if_fail(WP_IS_ENDPOINT(endpoint)); + + /* Unregister the endpoint */ + wp_endpoint_unregister(endpoint); +} + +static void +on_proxy_dsp_done(WpProxy *proxy, gpointer data) +{ + WpPwAudioSoftdspEndpoint *self = data; + + /* Don't do anything if the endpoint has already been initialized */ + if (!self->init_task) + return; + + /* Set destroy handler to unregister endpoint on proxy_node destruction */ + g_signal_connect (self->proxy_node, "destroyed", + G_CALLBACK(on_proxy_node_destroyed), WP_ENDPOINT(self)); + + /* Finish the creation of the endpoint */ + g_task_return_boolean (self->init_task, TRUE); + g_clear_object(&self->init_task); +} + +static void +on_proxy_dsp_created(GObject *initable, GAsyncResult *res, gpointer data) +{ + WpPwAudioSoftdspEndpoint *self = data; + struct pw_node_proxy *dsp_proxy = NULL; + const struct spa_audio_info_raw *port_format; + struct spa_audio_info_raw format; uint8_t buf[1024]; struct spa_pod_builder pod_builder = { 0, }; struct spa_pod *param; - const struct pw_node_info *node_info; - const struct spa_audio_info_raw *port_format; - struct spa_audio_info_raw format; - /* Get the node info */ - node_info = wp_proxy_node_get_info(self->proxy_node); - if (!node_info) - return; + /* Get the proxy dsp */ + self->proxy_dsp = wp_proxy_node_new_finish(initable, res, NULL); + g_return_if_fail (self->proxy_dsp); + + /* Add a custom dsp listener */ + dsp_proxy = wp_proxy_get_pw_proxy(WP_PROXY(self->proxy_dsp)); + g_return_if_fail (dsp_proxy); + pw_node_proxy_add_listener(dsp_proxy, &self->dsp_listener, + &dsp_node_events, self); + + /* Emit the props param */ + pw_node_proxy_enum_params (dsp_proxy, 0, SPA_PARAM_Props, 0, -1, NULL); /* Get the port format */ port_format = wp_proxy_port_get_format(self->proxy_port); - if (!port_format) - return; + g_return_if_fail (port_format); format = *port_format; + /* Build the param profile */ + spa_pod_builder_init(&pod_builder, buf, sizeof(buf)); + param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format); + param = spa_pod_builder_add_object(&pod_builder, + SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, + SPA_PARAM_PROFILE_direction, SPA_POD_Id(pw_direction_reverse(self->direction)), + SPA_PARAM_PROFILE_format, SPA_POD_Pod(param)); + + /* Set the param profile to emit the dsp ports */ + pw_node_proxy_set_param(dsp_proxy, SPA_PARAM_Profile, 0, param); +} + +static void +emit_audio_dsp_node (WpPwAudioSoftdspEndpoint *self) +{ + struct pw_properties *props; + const char *dsp_name = NULL; + struct pw_node_proxy *dsp_proxy = NULL; + const struct pw_node_info *node_info; + + /* Get the node info */ + node_info = wp_proxy_node_get_info(self->proxy_node); + g_return_if_fail (node_info); + /* Get the properties */ props = pw_properties_new_dict(node_info->props); - if (!props) - return; + g_return_if_fail (props); /* Get the DSP name */ dsp_name = pw_properties_get(props, "device.nick"); @@ -259,87 +337,25 @@ emit_audio_dsp_node (WpPwAudioSoftdspEndpoint *self) pw_properties_setf(props, "audio-dsp.maxbuffer", "%ld", MAX_QUANTUM_SIZE * sizeof(float)); - /* Set the DSP proxy and listener */ - self->dsp_proxy = wp_remote_pipewire_create_object(self->remote_pipewire, + /* Create the proxy dsp async */ + dsp_proxy = wp_remote_pipewire_create_object(self->remote_pipewire, "audio-dsp", PW_TYPE_INTERFACE_Node, &props->dict); - pw_node_proxy_add_listener(self->dsp_proxy, &self->dsp_listener, - &dsp_node_events, self); - pw_node_proxy_enum_params (self->dsp_proxy, 0, SPA_PARAM_Props, 0, -1, NULL); - - /* Set DSP proxy params */ - spa_pod_builder_init(&pod_builder, buf, sizeof(buf)); - param = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_Format, &format); - param = spa_pod_builder_add_object(&pod_builder, - SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, - SPA_PARAM_PROFILE_direction, SPA_POD_Id(pw_direction_reverse(self->direction)), - SPA_PARAM_PROFILE_format, SPA_POD_Pod(param)); - pw_node_proxy_set_param((struct pw_node_proxy*)self->dsp_proxy, - SPA_PARAM_Profile, 0, param); + wp_proxy_node_new(pw_proxy_get_id((struct pw_proxy *)dsp_proxy), dsp_proxy, + on_proxy_dsp_created, self); /* Clean up */ pw_properties_free(props); } static void -handle_port(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p, - gpointer d) -{ - WpPwAudioSoftdspEndpoint *self = d; - const struct spa_dict *props = p; - const char *direction_prop = NULL; - enum pw_direction direction; - - /* Make sure the dsp port is not already set*/ - if (self->dsp_port_id != 0) - return; - - /* Make sure the port has porperties */ - if (!props) - return; - - /* Only handle ports owned by this endpoint */ - if (!self->dsp_info || self->dsp_info->id != parent_id) - return; - - /* Get the direction property */ - direction_prop = spa_dict_lookup(props, "port.direction"); - if (!direction_prop) - return; - direction = - !strcmp(direction_prop, "out") ? PW_DIRECTION_OUTPUT : PW_DIRECTION_INPUT; - - /* Only handle ports with the oposit direction of the endpoint */ - if (self->direction == direction) - return; - - /* Set the dsp port id */ - self->dsp_port_id = id; -} - -static void -endpoint_constructed (GObject * object) +on_proxy_node_created(GObject *initable, GAsyncResult *res, gpointer data) { - WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object); - g_autoptr (WpCore) core = wp_endpoint_get_core(WP_ENDPOINT(self)); - const gchar *media_class = wp_endpoint_get_media_class (WP_ENDPOINT (self)); + WpPwAudioSoftdspEndpoint *self = data; GVariantDict d; - /* Set Remote Pipewire */ - self->remote_pipewire = wp_core_get_global (core, WP_GLOBAL_REMOTE_PIPEWIRE); - if (!self->remote_pipewire) - g_critical ("failed to get remote pipewire "); - - /* Register the global-added::port callback in remote pipewire */ - g_signal_connect(self->remote_pipewire, "global-added::port", - (GCallback)handle_port, self); - - /* Set the direction */ - if (g_str_has_suffix (media_class, "Source")) - self->direction = PW_DIRECTION_INPUT; - else if (g_str_has_suffix (media_class, "Sink")) - self->direction = PW_DIRECTION_OUTPUT; - else - g_critical ("failed to parse direction"); + /* Get the proxy node */ + self->proxy_node = wp_proxy_node_new_finish(initable, res, NULL); + g_return_if_fail (self->proxy_node); /* Emit the audio DSP node */ emit_audio_dsp_node(self); @@ -373,37 +389,120 @@ endpoint_constructed (GObject * object) g_variant_dict_insert (&d, "type", "s", "b"); g_variant_dict_insert (&d, "default-value", "b", self->selected); wp_endpoint_register_control (WP_ENDPOINT (self), g_variant_dict_end (&d)); +} - G_OBJECT_CLASS (endpoint_parent_class)->constructed (object); +static void +on_proxy_port_created(GObject *initable, GAsyncResult *res, gpointer data) +{ + WpPwAudioSoftdspEndpoint *self = data; + struct pw_node_proxy *node_proxy = NULL; + + /* Get the proxy port */ + self->proxy_port = wp_proxy_port_new_finish(initable, res, NULL); + g_return_if_fail (self->proxy_port); + + /* Create the proxy node async */ + node_proxy = wp_remote_pipewire_proxy_bind (self->remote_pipewire, + self->global_id, PW_TYPE_INTERFACE_Node); + g_return_if_fail(node_proxy); + wp_proxy_node_new(self->global_id, node_proxy, on_proxy_node_created, self); } static void -endpoint_finalize (GObject * object) +handle_node_port(WpPwAudioSoftdspEndpoint *self, guint id, guint parent_id, + const struct spa_dict *props) { - WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object); + struct pw_port_proxy *port_proxy = NULL; + + /* Alsa nodes should have 1 port only, so make sure proxy_port is not set */ + if (self->proxy_port != 0) + return; + + /* Create the proxy port async */ + port_proxy = wp_remote_pipewire_proxy_bind (self->remote_pipewire, id, + PW_TYPE_INTERFACE_Port); + g_return_if_fail(port_proxy); + wp_proxy_port_new(id, port_proxy, on_proxy_port_created, self); +} + +static void +on_proxy_dsp_port_created(GObject *initable, GAsyncResult *res, gpointer data) +{ + WpPwAudioSoftdspEndpoint *self = data; + WpProxyPort *proxy_dsp_port = NULL; + + /* Get the proxy dsp port */ + proxy_dsp_port = wp_proxy_port_new_finish(initable, res, NULL); + g_return_if_fail (proxy_dsp_port); - /* Set to NULL remote pipewire as we don't own the reference */ - self->remote_pipewire = NULL; + /* Add the proxy dsp port to the array */ + g_return_if_fail (self->proxies_dsp_port); + g_ptr_array_add(self->proxies_dsp_port, proxy_dsp_port); - /* Unref the proxy node */ - g_clear_object (&self->proxy_node); + /* Register a callback to know when all the dsp ports have been emitted */ + g_signal_connect(self->proxy_dsp, "done", (GCallback)on_proxy_dsp_done, self); + wp_proxy_sync (WP_PROXY(self->proxy_dsp)); +} - /* Unref the proxy port */ - g_clear_object (&self->proxy_port); +static void +handle_dsp_port(WpPwAudioSoftdspEndpoint *self, guint id, guint parent_id, + const struct spa_dict *props) +{ + struct pw_port_proxy *port_proxy = NULL; - /* Clear the dsp info */ - if (self->dsp_info) { - pw_node_info_free(self->dsp_info); - self->dsp_info = NULL; + /* Create the proxy dsp port async */ + port_proxy = wp_remote_pipewire_proxy_bind (self->remote_pipewire, id, + PW_TYPE_INTERFACE_Port); + g_return_if_fail(port_proxy); + wp_proxy_port_new(id, port_proxy, on_proxy_dsp_port_created, self); +} + +static void +on_port_added(WpRemotePipewire *rp, guint id, guint parent_id, gconstpointer p, + gpointer d) +{ + WpPwAudioSoftdspEndpoint *self = d; + const struct spa_dict *props = p; + const struct pw_node_info *dsp_info = NULL; + + /* Check if it is a node port and handle it */ + if (self->global_id == parent_id) { + handle_node_port(self, id, parent_id, props); + return; } - /* Destroy the dsp_proxy */ - if (self->dsp_proxy) { - spa_hook_remove (&self->dsp_listener); - pw_proxy_destroy ((struct pw_proxy *) self->dsp_proxy); - self->dsp_proxy = NULL; + /* Otherwise, check if it is a dsp port and handle it */ + if (!self->proxy_dsp) + return; + dsp_info = wp_proxy_node_get_info (self->proxy_dsp); + if (!dsp_info || dsp_info->id != parent_id) + return; + handle_dsp_port(self, id, parent_id, props); +} + +static void +endpoint_finalize (GObject * object) +{ + WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object); + + /* Destroy the proxies port */ + if (self->proxies_dsp_port) { + g_ptr_array_free(self->proxies_dsp_port, TRUE); + self->proxies_dsp_port = NULL; } + /* Destroy the proxy node */ + g_clear_object(&self->proxy_node); + + /* Destroy the proxy port */ + g_clear_object(&self->proxy_port); + + /* Destroy the proxy dsp */ + g_clear_object(&self->proxy_dsp); + + /* Destroy the done task */ + g_clear_object(&self->init_task); + G_OBJECT_CLASS (endpoint_parent_class)->finalize (object); } @@ -414,13 +513,8 @@ endpoint_set_property (GObject * object, guint property_id, WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object); switch (property_id) { - case PROP_NODE_PROXY: - g_clear_object(&self->proxy_node); - self->proxy_node = g_value_dup_object(value); - break; - case PROP_PORT_PROXY: - g_clear_object(&self->proxy_port); - self->proxy_port = g_value_dup_object(value); + case PROP_GLOBAL_ID: + self->global_id = g_value_get_uint(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -435,11 +529,8 @@ endpoint_get_property (GObject * object, guint property_id, WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (object); switch (property_id) { - case PROP_NODE_PROXY: - g_value_set_object (value, self->proxy_node); - break; - case PROP_PORT_PROXY: - g_value_set_object (value, self->proxy_port); + case PROP_GLOBAL_ID: + g_value_set_uint (value, self->global_id); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); @@ -472,14 +563,14 @@ endpoint_set_control_value (WpEndpoint * ep, guint32 control_id, WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (ep); char buf[1024]; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf)); + struct pw_node_proxy *dsp_proxy = NULL; float volume; bool mute; - if (!self->dsp_proxy) { - g_debug("WpEndpoint:%p too early to set control, dsp is not created yet", - self); - return FALSE; - } + /* Get the pipewire dsp proxy */ + g_return_val_if_fail (self->proxy_dsp, FALSE); + dsp_proxy = wp_proxy_get_pw_proxy (WP_PROXY(self->proxy_dsp)); + g_return_val_if_fail (dsp_proxy, FALSE); switch (control_id) { case CONTROL_VOLUME: @@ -488,13 +579,13 @@ endpoint_set_control_value (WpEndpoint * ep, guint32 control_id, g_debug("WpEndpoint:%p set volume control (%u) value, vol:%f", self, control_id, volume); - pw_node_proxy_set_param (self->dsp_proxy, + pw_node_proxy_set_param (dsp_proxy, SPA_PARAM_Props, 0, spa_pod_builder_add_object (&b, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, SPA_PROP_volume, SPA_POD_Float(volume), NULL)); - pw_node_proxy_enum_params (self->dsp_proxy, 0, SPA_PARAM_Props, 0, -1, + pw_node_proxy_enum_params (dsp_proxy, 0, SPA_PARAM_Props, 0, -1, NULL); break; @@ -504,13 +595,13 @@ endpoint_set_control_value (WpEndpoint * ep, guint32 control_id, g_debug("WpEndpoint:%p set mute control (%u) value, mute:%d", self, control_id, mute); - pw_node_proxy_set_param (self->dsp_proxy, + pw_node_proxy_set_param (dsp_proxy, SPA_PARAM_Props, 0, spa_pod_builder_add_object (&b, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props, SPA_PROP_mute, SPA_POD_Bool(mute), NULL)); - pw_node_proxy_enum_params (self->dsp_proxy, 0, SPA_PARAM_Props, 0, -1, + pw_node_proxy_enum_params (dsp_proxy, 0, SPA_PARAM_Props, 0, -1, NULL); break; @@ -527,6 +618,51 @@ endpoint_set_control_value (WpEndpoint * ep, guint32 control_id, return TRUE; } +static void +wp_endpoint_init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, gpointer data) +{ + WpPwAudioSoftdspEndpoint *self = WP_PW_AUDIO_SOFTDSP_ENDPOINT (initable); + g_autoptr (WpCore) core = wp_endpoint_get_core(WP_ENDPOINT(self)); + const gchar *media_class = wp_endpoint_get_media_class (WP_ENDPOINT (self)); + + /* Create the async task */ + self->init_task = g_task_new (initable, cancellable, callback, data); + + /* Init the proxies_dsp_port array */ + self->proxies_dsp_port = g_ptr_array_new_full(4, (GDestroyNotify)g_object_unref); + + /* Set the direction */ + if (g_str_has_suffix (media_class, "Source")) + self->direction = PW_DIRECTION_INPUT; + else if (g_str_has_suffix (media_class, "Sink")) + self->direction = PW_DIRECTION_OUTPUT; + else + g_critical ("failed to parse direction"); + + /* Register a port_added callback */ + self->remote_pipewire = wp_core_get_global (core, WP_GLOBAL_REMOTE_PIPEWIRE); + g_return_if_fail(self->remote_pipewire); + g_signal_connect(self->remote_pipewire, "global-added::port", + (GCallback)on_port_added, self); + + /* Call the parent interface */ + wp_endpoint_parent_interface->init_async (initable, io_priority, cancellable, + callback, data); +} + +static void +wp_endpoint_async_initable_init (gpointer iface, gpointer iface_data) +{ + GAsyncInitableIface *ai_iface = iface; + + /* Set the parent interface */ + wp_endpoint_parent_interface = g_type_interface_peek_parent (iface); + + /* Only set the init_async */ + ai_iface->init_async = wp_endpoint_init_async; +} + static void endpoint_init (WpPwAudioSoftdspEndpoint * self) { @@ -538,7 +674,6 @@ endpoint_class_init (WpPwAudioSoftdspEndpointClass * klass) GObjectClass *object_class = (GObjectClass *) klass; WpEndpointClass *endpoint_class = (WpEndpointClass *) klass; - object_class->constructed = endpoint_constructed; object_class->finalize = endpoint_finalize; object_class->set_property = endpoint_set_property; object_class->get_property = endpoint_get_property; @@ -548,48 +683,42 @@ endpoint_class_init (WpPwAudioSoftdspEndpointClass * klass) endpoint_class->set_control_value = endpoint_set_control_value; /* Instal the properties */ - g_object_class_install_property (object_class, PROP_NODE_PROXY, - g_param_spec_object ("node-proxy", "node-proxy", - "Pointer to the node proxy of the device", WP_TYPE_PROXY_NODE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, PROP_PORT_PROXY, - g_param_spec_object ("port-proxy", "port-proxy", - "Pointer to the port proxy of the device", WP_TYPE_PROXY_PORT, + g_object_class_install_property (object_class, PROP_GLOBAL_ID, + g_param_spec_uint ("global-id", "global-id", + "The global Id this endpoint refers to", 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } -static gpointer -endpoint_factory (WpFactory * factory, GType type, GVariant * properties) +void +endpoint_factory (WpFactory * factory, GType type, GVariant * properties, + GAsyncReadyCallback ready, gpointer user_data) { g_autoptr (WpCore) core = NULL; - const gchar *name = NULL; - const gchar *media_class = NULL; - guint64 proxy_node, proxy_port; + const gchar *name, *media_class; + guint global_id; /* Make sure the type is correct */ - g_return_val_if_fail(type == WP_TYPE_ENDPOINT, NULL); + g_return_if_fail(type == WP_TYPE_ENDPOINT); /* Get the Core */ core = wp_factory_get_core(factory); - g_return_val_if_fail (core, NULL); + g_return_if_fail (core); /* Get the properties */ if (!g_variant_lookup (properties, "name", "&s", &name)) - return NULL; + return; if (!g_variant_lookup (properties, "media-class", "&s", &media_class)) - return NULL; - if (!g_variant_lookup (properties, "proxy-node", "t", &proxy_node)) - return NULL; - if (!g_variant_lookup (properties, "proxy-port", "t", &proxy_port)) - return NULL; + return; + if (!g_variant_lookup (properties, "global-id", "u", &global_id)) + return; /* Create and return the softdsp endpoint object */ - return g_object_new (endpoint_get_type (), + g_async_initable_new_async ( + endpoint_get_type (), G_PRIORITY_DEFAULT, NULL, ready, user_data, "core", core, "name", name, "media-class", media_class, - "node-proxy", (gpointer) proxy_node, - "port-proxy", (gpointer) proxy_port, + "global-id", global_id, NULL); } @@ -597,5 +726,5 @@ void wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args) { /* Register the softdsp endpoint */ - wp_factory_new (core, "pw-audio-softdsp-endpoint", endpoint_factory); + wp_factory_new_async (core, "pw-audio-softdsp-endpoint", endpoint_factory); }