Files
wireplumber/modules/module-pipewire/simple-endpoint-link.c
2019-06-28 12:33:00 -04:00

222 lines
6.7 KiB
C

/* WirePlumber
*
* Copyright © 2019 Collabora Ltd.
* @author George Kiagiadakis <george.kiagiadakis@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
/**
* 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;
/* The wireplumber core */
GWeakRef core;
};
enum {
PROP_0,
PROP_CORE,
};
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)
static void
simple_endpoint_link_init (WpPipewireSimpleEndpointLink * self)
{
/* Init the core weak reference */
g_weak_ref_init (&self->core, NULL);
}
static void
simple_endpoint_link_finalize (GObject * object)
{
WpPipewireSimpleEndpointLink *self = WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK(object);
/* Clear the core weak reference */
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;
}
}
static gboolean
simple_endpoint_link_create (WpEndpointLink * epl, GVariant * src_data,
GVariant * sink_data, GError ** error)
{
WpPipewireSimpleEndpointLink *self = WP_PIPEWIRE_SIMPLE_ENDPOINT_LINK(epl);
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
WpRemotePipewire *remote_pipewire;
struct pw_properties *props;
guint32 output_node_id, input_node_id;
GVariant *src_ports, *sink_ports;
GVariantIter *out_iter, *in_iter;
guint64 out_ptr, in_ptr;
/* Get the remote pipewire */
remote_pipewire = wp_core_get_global (core, WP_GLOBAL_REMOTE_PIPEWIRE);
g_return_val_if_fail (remote_pipewire, FALSE);
/* Get the node ids and port ids */
if (!g_variant_lookup (src_data, "node-id", "u", &output_node_id))
return FALSE;
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;
sink_ports = g_variant_lookup_value (sink_data, "ports", G_VARIANT_TYPE_ARRAY);
if (!sink_ports)
return FALSE;
/* 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 */
wp_remote_pipewire_create_object(remote_pipewire, "link-factory",
PW_TYPE_INTERFACE_Link, &props->dict);
/* Clean up */
pw_properties_free(props);
}
g_variant_iter_free (in_iter);
}
g_variant_iter_free (out_iter);
return TRUE;
}
static void
simple_endpoint_link_destroy (WpEndpointLink * self)
{
/* TODO destroy pw_links */
}
static void
simple_endpoint_link_class_init (WpPipewireSimpleEndpointLinkClass * klass)
{
GObjectClass *object_class = (GObjectClass *) klass;
WpEndpointLinkClass *link_class = (WpEndpointLinkClass *) klass;
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;
link_class->create = simple_endpoint_link_create;
link_class->destroy = simple_endpoint_link_destroy;
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));
}
void
simple_endpoint_link_factory (WpFactory * factory, GType type,
GVariant * properties, GAsyncReadyCallback ready, gpointer data)
{
g_autoptr(WpCore) core = NULL;
guint64 src, sink;
guint src_stream, sink_stream;
/* Make sure the type is an endpoint link */
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;
/* Create the endpoint link */
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);
}