309 lines
9.4 KiB
C
309 lines
9.4 KiB
C
/* WirePlumber
|
|
*
|
|
* Copyright © 2019 Collabora Ltd.
|
|
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include <pipewire/pipewire.h>
|
|
|
|
#include <wp/wp.h>
|
|
|
|
#include "parser-endpoint.h"
|
|
#include "parser-streams.h"
|
|
#include "context.h"
|
|
|
|
struct _WpConfigEndpointContext
|
|
{
|
|
GObject parent;
|
|
|
|
/* Props */
|
|
GWeakRef core;
|
|
|
|
WpObjectManager *om;
|
|
GHashTable *registered_endpoints;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_CORE,
|
|
};
|
|
|
|
enum {
|
|
SIGNAL_ENDPOINT_CREATED,
|
|
N_SIGNALS
|
|
};
|
|
|
|
static guint signals[N_SIGNALS];
|
|
|
|
G_DEFINE_TYPE (WpConfigEndpointContext, wp_config_endpoint_context,
|
|
G_TYPE_OBJECT)
|
|
|
|
static void
|
|
on_endpoint_created (GObject *initable, GAsyncResult *res, gpointer d)
|
|
{
|
|
WpConfigEndpointContext *self = d;
|
|
g_autoptr (WpBaseEndpoint) endpoint = NULL;
|
|
g_autoptr (WpProxy) proxy = NULL;
|
|
guint global_id = 0;
|
|
GError *error = NULL;
|
|
|
|
/* Get the endpoint */
|
|
endpoint = wp_base_endpoint_new_finish (initable, res, &error);
|
|
if (error) {
|
|
g_warning ("Failed to create endpoint: %s", error->message);
|
|
return;
|
|
}
|
|
|
|
/* Get the endpoint global id */
|
|
g_object_get (endpoint, "node", &proxy, NULL);
|
|
global_id = wp_proxy_get_bound_id (proxy);
|
|
|
|
/* Register the endpoint and add it to the table */
|
|
wp_base_endpoint_register (endpoint);
|
|
g_hash_table_insert (self->registered_endpoints, GUINT_TO_POINTER (global_id),
|
|
g_object_ref (endpoint));
|
|
|
|
/* Emit the endpoint-created signal */
|
|
g_signal_emit (self, signals[SIGNAL_ENDPOINT_CREATED], 0, endpoint);
|
|
}
|
|
|
|
static GVariant *
|
|
create_streams_variant (WpConfiguration *config, const char *streams)
|
|
{
|
|
g_autoptr (WpConfigParser) parser = NULL;
|
|
const struct WpParserStreamsData *streams_data = NULL;
|
|
g_autoptr (GVariantBuilder) ba = NULL;
|
|
|
|
if (!streams || !config)
|
|
return NULL;
|
|
|
|
/* Get the streams parser */
|
|
parser = wp_configuration_get_parser (config, WP_PARSER_STREAMS_EXTENSION);
|
|
if (!parser)
|
|
return NULL;
|
|
|
|
/* Get the streams data */
|
|
streams_data = wp_config_parser_get_matched_data (parser, (gpointer)streams);
|
|
if (!streams_data || streams_data->n_streams <= 0)
|
|
return NULL;
|
|
|
|
/* Build the variant array with the stream name and priority */
|
|
ba = g_variant_builder_new (G_VARIANT_TYPE ("a(su)"));
|
|
g_variant_builder_init (ba, G_VARIANT_TYPE_ARRAY);
|
|
for (guint i = 0; i < streams_data->n_streams; i++)
|
|
g_variant_builder_add (ba, "(su)", streams_data->streams[i].name,
|
|
streams_data->streams[i].priority);
|
|
|
|
return g_variant_new ("a(su)", ba);
|
|
}
|
|
|
|
static void
|
|
on_node_added (WpObjectManager *om, WpProxy *proxy, gpointer d)
|
|
{
|
|
WpConfigEndpointContext *self = d;
|
|
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
|
|
g_autoptr (WpConfiguration) config = wp_configuration_get_instance (core);
|
|
g_autoptr (WpProperties) props = wp_proxy_get_properties (proxy);
|
|
g_autoptr (WpConfigParser) parser = NULL;
|
|
const struct WpParserEndpointData *endpoint_data = NULL;
|
|
GVariantBuilder b;
|
|
g_autoptr (GVariant) endpoint_props = NULL;
|
|
const char *media_class = NULL, *name = NULL;
|
|
g_autoptr (GVariant) streams_variant = NULL;
|
|
|
|
/* Skip nodes with no media class (JACK Clients) */
|
|
media_class = wp_properties_get (props, PW_KEY_MEDIA_CLASS);
|
|
if (!media_class)
|
|
return;
|
|
|
|
/* Get the linked and ep streams data */
|
|
parser = wp_configuration_get_parser (config, WP_PARSER_ENDPOINT_EXTENSION);
|
|
endpoint_data = wp_config_parser_get_matched_data (parser, proxy);
|
|
if (!endpoint_data)
|
|
return;
|
|
|
|
/* Set the name if it is null */
|
|
name = endpoint_data->e.name;
|
|
if (!name)
|
|
name = wp_properties_get (props, PW_KEY_NODE_NAME);
|
|
|
|
/* Set the media class if it is null */
|
|
if (endpoint_data->e.media_class)
|
|
media_class = endpoint_data->e.media_class;
|
|
|
|
/* Create the streams variant */
|
|
streams_variant = create_streams_variant (config, endpoint_data->e.streams);
|
|
|
|
/* 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 ("%s", name)));
|
|
g_variant_builder_add (&b, "{sv}",
|
|
"media-class", g_variant_new_string (media_class));
|
|
g_variant_builder_add (&b, "{sv}",
|
|
"direction", g_variant_new_uint32 (endpoint_data->e.direction));
|
|
g_variant_builder_add (&b, "{sv}",
|
|
"priority", g_variant_new_uint32 (endpoint_data->e.priority));
|
|
g_variant_builder_add (&b, "{sv}",
|
|
"node", g_variant_new_uint64 ((guint64) proxy));
|
|
if (streams_variant)
|
|
g_variant_builder_add (&b, "{sv}", "streams",
|
|
g_steal_pointer (&streams_variant));
|
|
endpoint_props = g_variant_builder_end (&b);
|
|
|
|
/* Create the endpoint async */
|
|
wp_factory_make (core, endpoint_data->e.type, WP_TYPE_BASE_ENDPOINT,
|
|
endpoint_props, on_endpoint_created, self);
|
|
}
|
|
|
|
static void
|
|
on_node_removed (WpObjectManager *om, WpProxy *proxy, gpointer d)
|
|
{
|
|
WpConfigEndpointContext *self = d;
|
|
WpBaseEndpoint *endpoint = NULL;
|
|
guint32 id = wp_proxy_get_bound_id (proxy);
|
|
|
|
/* Get the endpoint */
|
|
endpoint = g_hash_table_lookup (self->registered_endpoints,
|
|
GUINT_TO_POINTER(id));
|
|
if (!endpoint)
|
|
return;
|
|
|
|
/* Unregister the endpoint and remove it from the table */
|
|
wp_base_endpoint_unregister (endpoint);
|
|
g_hash_table_remove (self->registered_endpoints, GUINT_TO_POINTER(id));
|
|
}
|
|
|
|
static void
|
|
wp_config_endpoint_context_constructed (GObject * object)
|
|
{
|
|
WpConfigEndpointContext *self = WP_CONFIG_ENDPOINT_CONTEXT (object);
|
|
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
|
|
g_return_if_fail (core);
|
|
g_autoptr (WpConfiguration) config = wp_configuration_get_instance (core);
|
|
g_return_if_fail (config);
|
|
|
|
/* Add the endpoint and streams parsers */
|
|
wp_configuration_add_extension (config, WP_PARSER_ENDPOINT_EXTENSION,
|
|
WP_TYPE_PARSER_ENDPOINT);
|
|
wp_configuration_add_extension (config, WP_PARSER_STREAMS_EXTENSION,
|
|
WP_TYPE_PARSER_STREAMS);
|
|
|
|
/* Parse the files */
|
|
wp_configuration_reload (config, WP_PARSER_ENDPOINT_EXTENSION);
|
|
wp_configuration_reload (config, WP_PARSER_STREAMS_EXTENSION);
|
|
|
|
/* Install the object manager */
|
|
wp_core_install_object_manager (core, self->om);
|
|
|
|
G_OBJECT_CLASS (wp_config_endpoint_context_parent_class)->constructed (object);
|
|
}
|
|
|
|
static void
|
|
wp_config_endpoint_context_set_property (GObject * object, guint property_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
WpConfigEndpointContext *self = WP_CONFIG_ENDPOINT_CONTEXT (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
|
|
wp_config_endpoint_context_get_property (GObject * object, guint property_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
WpConfigEndpointContext *self = WP_CONFIG_ENDPOINT_CONTEXT (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 void
|
|
wp_config_endpoint_context_finalize (GObject *object)
|
|
{
|
|
WpConfigEndpointContext *self = WP_CONFIG_ENDPOINT_CONTEXT (object);
|
|
|
|
g_autoptr (WpCore) core = g_weak_ref_get (&self->core);
|
|
if (core) {
|
|
g_autoptr (WpConfiguration) config = wp_configuration_get_instance (core);
|
|
wp_configuration_remove_extension (config, WP_PARSER_ENDPOINT_EXTENSION);
|
|
wp_configuration_remove_extension (config, WP_PARSER_STREAMS_EXTENSION);
|
|
}
|
|
g_weak_ref_clear (&self->core);
|
|
|
|
g_clear_object (&self->om);
|
|
g_clear_pointer (&self->registered_endpoints, g_hash_table_unref);
|
|
|
|
G_OBJECT_CLASS (wp_config_endpoint_context_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
wp_config_endpoint_context_init (WpConfigEndpointContext *self)
|
|
{
|
|
self->om = wp_object_manager_new ();
|
|
self->registered_endpoints = g_hash_table_new_full (g_direct_hash,
|
|
g_direct_equal, NULL, (GDestroyNotify) g_object_unref);
|
|
|
|
/* Only handle augmented nodes with info set */
|
|
wp_object_manager_add_interest (self->om, WP_TYPE_NODE, NULL,
|
|
WP_PROXY_FEATURES_STANDARD);
|
|
|
|
/* Register the global added/removed callbacks */
|
|
g_signal_connect(self->om, "object-added",
|
|
(GCallback) on_node_added, self);
|
|
g_signal_connect(self->om, "object-removed",
|
|
(GCallback) on_node_removed, self);
|
|
}
|
|
|
|
static void
|
|
wp_config_endpoint_context_class_init (WpConfigEndpointContextClass *klass)
|
|
{
|
|
GObjectClass *object_class = (GObjectClass *) klass;
|
|
|
|
object_class->constructed = wp_config_endpoint_context_constructed;
|
|
object_class->finalize = wp_config_endpoint_context_finalize;
|
|
object_class->set_property = wp_config_endpoint_context_set_property;
|
|
object_class->get_property = wp_config_endpoint_context_get_property;
|
|
|
|
/* Properties */
|
|
g_object_class_install_property (object_class, PROP_CORE,
|
|
g_param_spec_object ("core", "core", "The wireplumber core",
|
|
WP_TYPE_CORE,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
|
|
|
/* Signals */
|
|
signals[SIGNAL_ENDPOINT_CREATED] = g_signal_new ("endpoint-created",
|
|
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
|
|
G_TYPE_NONE, 1, WP_TYPE_ENDPOINT);
|
|
}
|
|
|
|
WpConfigEndpointContext *
|
|
wp_config_endpoint_context_new (WpCore *core)
|
|
{
|
|
return g_object_new (wp_config_endpoint_context_get_type (),
|
|
"core", core,
|
|
NULL);
|
|
}
|
|
|
|
guint
|
|
wp_config_endpoint_context_get_length (WpConfigEndpointContext *self)
|
|
{
|
|
g_return_val_if_fail (WP_IS_CONFIG_ENDPOINT_CONTEXT (self), 0);
|
|
return g_hash_table_size (self->registered_endpoints);
|
|
}
|