2019-05-17 13:08:45 +03:00
|
|
|
/* WirePlumber
|
|
|
|
|
*
|
|
|
|
|
* Copyright © 2019 Collabora Ltd.
|
|
|
|
|
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
|
|
|
|
|
*
|
2019-05-31 12:13:01 +03:00
|
|
|
* SPDX-License-Identifier: MIT
|
2019-05-17 13:08:45 +03:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The simple endpoint link is an implementation of WpEndpointLink that
|
|
|
|
|
* expects the two linked endpoints to have nodes in the pipewire graph.
|
|
|
|
|
* When asked to create a link, it creates pw_link objects that will link
|
|
|
|
|
* the ports of the source node to the ports of the sink node.
|
|
|
|
|
*
|
|
|
|
|
* The GVariant data that is passed in create must be of type (uau),
|
|
|
|
|
* which means a tuple with the following fields:
|
|
|
|
|
* - u: a uint32 that is the ID of a node
|
|
|
|
|
* - au: an array of uint32 that are the IDs of the ports on this node
|
|
|
|
|
*
|
|
|
|
|
* Linking endpoints with multiple nodes is not supported by this implementation.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <wp/wp.h>
|
|
|
|
|
#include <pipewire/pipewire.h>
|
|
|
|
|
|
|
|
|
|
struct _WpPipewireSimpleEndpointLink
|
|
|
|
|
{
|
|
|
|
|
WpEndpointLink parent;
|
2019-06-05 13:41:41 -04:00
|
|
|
|
2019-07-05 11:00:28 -04:00
|
|
|
/* Props */
|
2019-06-28 12:33:00 -04:00
|
|
|
GWeakRef core;
|
2019-07-05 11:47:45 -04:00
|
|
|
guint link_count;
|
2019-07-05 11:00:28 -04:00
|
|
|
|
|
|
|
|
/* The task to signal the simple endpoint link is initialized */
|
|
|
|
|
GTask *init_task;
|
2019-07-05 11:47:45 -04:00
|
|
|
|
|
|
|
|
/* Handler */
|
|
|
|
|
gulong proxy_done_handler_id;
|
|
|
|
|
|
|
|
|
|
/* The link proxies */
|
|
|
|
|
GPtrArray *link_proxies;
|
2019-06-28 12:33:00 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
PROP_0,
|
|
|
|
|
PROP_CORE,
|
2019-05-17 13:08:45 +03:00
|
|
|
};
|
|
|
|
|
|
2019-07-05 11:00:28 -04:00
|
|
|
static GAsyncInitableIface *wp_simple_endpoint_link_parent_interface = NULL;
|
|
|
|
|
static void wp_simple_endpoint_link_async_initable_init (gpointer iface,
|
|
|
|
|
gpointer iface_data);
|
|
|
|
|
|
2019-05-17 13:08:45 +03:00
|
|
|
G_DECLARE_FINAL_TYPE (WpPipewireSimpleEndpointLink,
|
|
|
|
|
simple_endpoint_link, WP_PIPEWIRE, SIMPLE_ENDPOINT_LINK, WpEndpointLink)
|
|
|
|
|
|
2019-07-05 11:00:28 -04:00
|
|
|
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;
|
|
|
|
|
}
|
2019-05-17 13:08:45 +03:00
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
simple_endpoint_link_init (WpPipewireSimpleEndpointLink * self)
|
|
|
|
|
{
|
2019-06-28 12:33:00 -04:00
|
|
|
/* Init the core weak reference */
|
|
|
|
|
g_weak_ref_init (&self->core, NULL);
|
2019-07-05 11:47:45 -04:00
|
|
|
|
|
|
|
|
/* Init the list of link proxies */
|
|
|
|
|
self->link_proxies = g_ptr_array_new_full(2, (GDestroyNotify)g_object_unref);
|
2019-06-28 12:33:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
simple_endpoint_link_finalize (GObject * object)
|
|
|
|
|
{
|
|
|
|
|
WpPipewireSimpleEndpointLink *self = WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK(object);
|
|
|
|
|
|
2019-08-29 21:21:33 +03:00
|
|
|
g_clear_object (&self->init_task);
|
|
|
|
|
g_clear_pointer (&self->link_proxies, g_ptr_array_unref);
|
2019-06-28 12:33:00 -04:00
|
|
|
g_weak_ref_clear (&self->core);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
simple_endpoint_link_set_property (GObject * object, guint property_id,
|
|
|
|
|
const GValue * value, GParamSpec * pspec)
|
|
|
|
|
{
|
|
|
|
|
WpPipewireSimpleEndpointLink *self =
|
|
|
|
|
WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK (object);
|
|
|
|
|
|
|
|
|
|
switch (property_id) {
|
|
|
|
|
case PROP_CORE:
|
|
|
|
|
g_weak_ref_set (&self->core, g_value_get_object (value));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
simple_endpoint_link_get_property (GObject * object, guint property_id,
|
|
|
|
|
GValue * value, GParamSpec * pspec)
|
|
|
|
|
{
|
|
|
|
|
WpPipewireSimpleEndpointLink *self =
|
|
|
|
|
WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK (object);
|
|
|
|
|
|
|
|
|
|
switch (property_id) {
|
|
|
|
|
case PROP_CORE:
|
|
|
|
|
g_value_take_object (value, g_weak_ref_get (&self->core));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-05-17 13:08:45 +03:00
|
|
|
}
|
|
|
|
|
|
2019-07-05 11:47:45 -04:00
|
|
|
static void
|
2019-08-29 21:21:33 +03:00
|
|
|
on_proxy_link_augmented (WpProxy *proxy, GAsyncResult *res, gpointer data)
|
2019-07-05 11:47:45 -04:00
|
|
|
{
|
|
|
|
|
WpPipewireSimpleEndpointLink *self = data;
|
2019-08-29 21:21:33 +03:00
|
|
|
g_autoptr (GError) error = NULL;
|
2019-07-05 11:47:45 -04:00
|
|
|
|
2019-08-29 21:21:33 +03:00
|
|
|
wp_proxy_augment_finish (proxy, res, &error);
|
|
|
|
|
if (error && self->init_task) {
|
|
|
|
|
g_task_return_error (self->init_task, g_steal_pointer (&error));
|
|
|
|
|
g_clear_object (&self->init_task);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-07-05 11:47:45 -04:00
|
|
|
|
|
|
|
|
/* Finish the simple endpoint link creation if all links have been created */
|
2019-08-29 21:21:33 +03:00
|
|
|
if (--self->link_count == 0 && self->init_task) {
|
|
|
|
|
g_task_return_boolean (self->init_task, TRUE);
|
|
|
|
|
g_clear_object(&self->init_task);
|
|
|
|
|
}
|
2019-07-05 11:47:45 -04:00
|
|
|
}
|
|
|
|
|
|
2019-05-17 13:08:45 +03:00
|
|
|
static gboolean
|
2019-06-05 13:41:41 -04:00
|
|
|
simple_endpoint_link_create (WpEndpointLink * epl, GVariant * src_data,
|
2019-05-17 13:08:45 +03:00
|
|
|
GVariant * sink_data, GError ** error)
|
|
|
|
|
{
|
2019-06-05 13:41:41 -04:00
|
|
|
WpPipewireSimpleEndpointLink *self = WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK(epl);
|
2019-08-29 21:21:33 +03:00
|
|
|
g_autoptr (WpCore) core = NULL;
|
2019-06-28 12:33:00 -04:00
|
|
|
WpRemotePipewire *remote_pipewire;
|
2019-06-24 14:27:52 -04:00
|
|
|
guint32 output_node_id, input_node_id;
|
|
|
|
|
GVariant *src_ports, *sink_ports;
|
|
|
|
|
GVariantIter *out_iter, *in_iter;
|
|
|
|
|
guint64 out_ptr, in_ptr;
|
2019-08-29 21:21:33 +03:00
|
|
|
g_autoptr (GHashTable) linked_ports = NULL;
|
|
|
|
|
g_autoptr (WpProperties) props = NULL;
|
|
|
|
|
WpProxy *proxy = NULL;
|
|
|
|
|
|
|
|
|
|
core = g_weak_ref_get (&self->core);
|
|
|
|
|
g_return_val_if_fail (core, FALSE);
|
2019-06-05 13:41:41 -04:00
|
|
|
|
2019-06-28 12:33:00 -04:00
|
|
|
/* Get the remote pipewire */
|
|
|
|
|
remote_pipewire = wp_core_get_global (core, WP_GLOBAL_REMOTE_PIPEWIRE);
|
|
|
|
|
g_return_val_if_fail (remote_pipewire, FALSE);
|
|
|
|
|
|
2019-06-05 13:41:41 -04:00
|
|
|
/* Get the node ids and port ids */
|
|
|
|
|
if (!g_variant_lookup (src_data, "node-id", "u", &output_node_id))
|
|
|
|
|
return FALSE;
|
2019-06-24 14:27:52 -04:00
|
|
|
src_ports = g_variant_lookup_value (src_data, "ports", G_VARIANT_TYPE_ARRAY);
|
|
|
|
|
if (!src_ports)
|
2019-06-05 13:41:41 -04:00
|
|
|
return FALSE;
|
|
|
|
|
if (!g_variant_lookup (sink_data, "node-id", "u", &input_node_id))
|
|
|
|
|
return FALSE;
|
2019-06-24 14:27:52 -04:00
|
|
|
sink_ports = g_variant_lookup_value (sink_data, "ports", G_VARIANT_TYPE_ARRAY);
|
|
|
|
|
if (!sink_ports)
|
2019-06-05 13:41:41 -04:00
|
|
|
return FALSE;
|
|
|
|
|
|
2019-06-24 14:27:52 -04:00
|
|
|
/* Link all the output ports with the input ports */
|
2019-07-05 12:21:57 -04:00
|
|
|
linked_ports = g_hash_table_new (g_direct_hash, g_direct_equal);
|
2019-06-24 14:27:52 -04:00
|
|
|
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;
|
|
|
|
|
|
2019-07-18 11:03:19 -04:00
|
|
|
/* Skip the ports if they are already linked */
|
2019-08-29 21:21:33 +03:00
|
|
|
if (g_hash_table_contains (linked_ports, GUINT_TO_POINTER(in_id)) ||
|
|
|
|
|
g_hash_table_contains (linked_ports, GUINT_TO_POINTER(out_id)))
|
2019-07-18 11:03:19 -04:00
|
|
|
continue;
|
2019-07-05 12:21:57 -04:00
|
|
|
|
2019-06-24 14:27:52 -04:00
|
|
|
/* Create the properties */
|
2019-08-29 21:21:33 +03:00
|
|
|
props = wp_properties_new_empty ();
|
|
|
|
|
wp_properties_setf(props, PW_KEY_LINK_OUTPUT_NODE, "%d", output_node_id);
|
|
|
|
|
wp_properties_setf(props, PW_KEY_LINK_OUTPUT_PORT, "%d", out_id);
|
|
|
|
|
wp_properties_setf(props, PW_KEY_LINK_INPUT_NODE, "%d", input_node_id);
|
|
|
|
|
wp_properties_setf(props, PW_KEY_LINK_INPUT_PORT, "%d", in_id);
|
2019-06-24 14:27:52 -04:00
|
|
|
|
|
|
|
|
/* Create the link */
|
2019-07-05 11:47:45 -04:00
|
|
|
proxy = wp_remote_pipewire_create_object(remote_pipewire, "link-factory",
|
2019-08-29 21:21:33 +03:00
|
|
|
PW_TYPE_INTERFACE_Link, PW_VERSION_LINK_PROXY, props);
|
2019-07-05 11:47:45 -04:00
|
|
|
g_return_val_if_fail (proxy, FALSE);
|
2019-08-29 21:21:33 +03:00
|
|
|
g_ptr_array_add(self->link_proxies, proxy);
|
|
|
|
|
|
|
|
|
|
/* Wait for the link to be created on the server side
|
|
|
|
|
by waiting for the info event, which will be signaled anyway */
|
2019-07-05 11:47:45 -04:00
|
|
|
self->link_count++;
|
2019-08-29 21:21:33 +03:00
|
|
|
wp_proxy_augment (proxy, WP_PROXY_FEATURE_INFO, NULL,
|
|
|
|
|
(GAsyncReadyCallback) on_proxy_link_augmented, self);
|
2019-06-24 14:27:52 -04:00
|
|
|
|
2019-07-18 11:03:19 -04:00
|
|
|
/* Insert the port ids in the hash tables to know they are linked */
|
2019-08-29 21:21:33 +03:00
|
|
|
g_hash_table_add (linked_ports, GUINT_TO_POINTER(in_id));
|
|
|
|
|
g_hash_table_add (linked_ports, GUINT_TO_POINTER(out_id));
|
2019-06-24 14:27:52 -04:00
|
|
|
}
|
|
|
|
|
g_variant_iter_free (in_iter);
|
|
|
|
|
}
|
|
|
|
|
g_variant_iter_free (out_iter);
|
2019-05-22 09:49:09 -04:00
|
|
|
|
|
|
|
|
return TRUE;
|
2019-05-17 13:08:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2019-07-05 11:47:45 -04:00
|
|
|
simple_endpoint_link_destroy (WpEndpointLink * epl)
|
2019-05-17 13:08:45 +03:00
|
|
|
{
|
2019-07-05 11:47:45 -04:00
|
|
|
WpPipewireSimpleEndpointLink *self = WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK(epl);
|
|
|
|
|
|
2019-08-29 21:21:33 +03:00
|
|
|
g_clear_pointer (&self->link_proxies, g_ptr_array_unref);
|
2019-05-17 13:08:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
simple_endpoint_link_class_init (WpPipewireSimpleEndpointLinkClass * klass)
|
|
|
|
|
{
|
2019-06-28 12:33:00 -04:00
|
|
|
GObjectClass *object_class = (GObjectClass *) klass;
|
2019-05-17 13:08:45 +03:00
|
|
|
WpEndpointLinkClass *link_class = (WpEndpointLinkClass *) klass;
|
|
|
|
|
|
2019-06-28 12:33:00 -04:00
|
|
|
object_class->finalize = simple_endpoint_link_finalize;
|
|
|
|
|
object_class->set_property = simple_endpoint_link_set_property;
|
|
|
|
|
object_class->get_property = simple_endpoint_link_get_property;
|
|
|
|
|
|
2019-05-17 13:08:45 +03:00
|
|
|
link_class->create = simple_endpoint_link_create;
|
|
|
|
|
link_class->destroy = simple_endpoint_link_destroy;
|
2019-06-28 12:33:00 -04:00
|
|
|
|
|
|
|
|
g_object_class_install_property (object_class, PROP_CORE,
|
|
|
|
|
g_param_spec_object ("core", "core",
|
|
|
|
|
"The wireplumber core object this links belongs to", WP_TYPE_CORE,
|
|
|
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
2019-05-17 13:08:45 +03:00
|
|
|
}
|
|
|
|
|
|
2019-06-28 12:33:00 -04:00
|
|
|
void
|
2019-05-17 13:08:45 +03:00
|
|
|
simple_endpoint_link_factory (WpFactory * factory, GType type,
|
2019-06-28 12:33:00 -04:00
|
|
|
GVariant * properties, GAsyncReadyCallback ready, gpointer data)
|
2019-05-17 13:08:45 +03:00
|
|
|
{
|
2019-06-28 12:33:00 -04:00
|
|
|
g_autoptr(WpCore) core = NULL;
|
|
|
|
|
guint64 src, sink;
|
|
|
|
|
guint src_stream, sink_stream;
|
2019-06-05 13:41:41 -04:00
|
|
|
|
|
|
|
|
/* Make sure the type is an endpoint link */
|
2019-06-28 12:33:00 -04:00
|
|
|
g_return_if_fail (type == WP_TYPE_ENDPOINT_LINK);
|
|
|
|
|
|
|
|
|
|
/* Get the Core */
|
|
|
|
|
core = wp_factory_get_core (factory);
|
|
|
|
|
g_return_if_fail (core);
|
|
|
|
|
|
|
|
|
|
/* Get the properties */
|
|
|
|
|
if (!g_variant_lookup (properties, "src", "t", &src))
|
|
|
|
|
return;
|
|
|
|
|
if (!g_variant_lookup (properties, "src-stream", "u", &src_stream))
|
|
|
|
|
return;
|
|
|
|
|
if (!g_variant_lookup (properties, "sink", "t", &sink))
|
|
|
|
|
return;
|
|
|
|
|
if (!g_variant_lookup (properties, "sink-stream", "u", &sink_stream))
|
|
|
|
|
return;
|
2019-06-05 13:41:41 -04:00
|
|
|
|
|
|
|
|
/* Create the endpoint link */
|
2019-06-28 12:33:00 -04:00
|
|
|
g_async_initable_new_async (
|
|
|
|
|
simple_endpoint_link_get_type (), G_PRIORITY_DEFAULT, NULL, ready, data,
|
|
|
|
|
"src", (gpointer)src,
|
|
|
|
|
"src-stream", src_stream,
|
|
|
|
|
"sink", (gpointer)sink,
|
|
|
|
|
"sink-stream", sink_stream,
|
|
|
|
|
"core", core,
|
|
|
|
|
NULL);
|
2019-05-17 13:08:45 +03:00
|
|
|
}
|