/* WirePlumber * * Copyright © 2019 Collabora Ltd. * @author Julian Bouzas * * SPDX-License-Identifier: MIT */ #include #include #include "parser-endpoint.h" #include "parser-streams.h" #include "context.h" G_DEFINE_QUARK (wp-module-config-endpoint-context-session, session); struct _WpConfigEndpointContext { GObject parent; WpObjectManager *sessions_om; WpObjectManager *nodes_om; GHashTable *endpoints; }; enum { SIGNAL_ENDPOINT_CREATED, N_SIGNALS }; static guint signals[N_SIGNALS]; G_DEFINE_TYPE (WpConfigEndpointContext, wp_config_endpoint_context, WP_TYPE_PLUGIN) static const struct WpParserStreamsData * get_streams_data (WpConfiguration *config, const char *file_name) { g_autoptr (WpConfigParser) parser = NULL; g_return_val_if_fail (config, 0); g_return_val_if_fail (file_name, 0); /* Get the streams parser */ parser = wp_configuration_get_parser (config, WP_PARSER_STREAMS_EXTENSION); if (!parser) return 0; /* Get the streams data */ return wp_config_parser_get_matched_data (parser, (gpointer)file_name); } static void endpoint_export_finish_cb (WpSessionItem * ep, GAsyncResult * res, WpConfigEndpointContext * self) { g_autoptr (GError) error = NULL; gboolean export_ret = wp_session_item_export_finish (ep, res, &error); g_return_if_fail (error == NULL); g_return_if_fail (export_ret); /* Emit the signal */ g_signal_emit (self, signals[SIGNAL_ENDPOINT_CREATED], 0, ep); } static void endpoint_activate_finish_cb (WpSessionItem * ep, GAsyncResult * res, WpConfigEndpointContext * self) { WpSession * session = NULL; g_autoptr (GError) error = NULL; gboolean activate_ret = wp_session_item_activate_finish (ep, res, &error); g_return_if_fail (error == NULL); g_return_if_fail (activate_ret); /* Get the session */ session = g_object_get_qdata (G_OBJECT (ep), session_quark ()); g_return_if_fail (session); wp_session_item_export (ep, WP_SESSION (session), (GAsyncReadyCallback) endpoint_export_finish_cb, self); } static void on_node_added (WpObjectManager *om, WpProxy *proxy, gpointer d) { WpConfigEndpointContext *self = d; g_autoptr (WpCore) core = wp_plugin_get_core (WP_PLUGIN (self)); g_autoptr (WpConfiguration) config = wp_configuration_get_instance (core); g_autoptr (WpProperties) props = wp_proxy_get_properties (proxy); g_autoptr (WpSessionItem) ep = NULL; g_autoptr (WpSessionItem) streams_ep = NULL; g_autoptr (WpSession) session = NULL; g_autoptr (WpConfigParser) parser = NULL; const struct WpParserEndpointData *endpoint_data = NULL; const struct WpParserStreamsData *streams_data = NULL; /* Skip nodes with no media class (JACK Clients) */ if (!wp_properties_get (props, PW_KEY_MEDIA_CLASS)) return; /* Get the endpoint configuration 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; /* Get the session */ session = wp_object_manager_lookup (self->sessions_om, WP_TYPE_SESSION, WP_CONSTRAINT_TYPE_PW_PROPERTY, "session.name", "=s", endpoint_data->e.session, NULL); if (!session) { wp_warning_object (self, "could not find session for endpoint"); return; } /* Get the streams data */ streams_data = endpoint_data->e.streams ? get_streams_data (config, endpoint_data->e.streams) : NULL; /* Create the endpoint */ ep = wp_session_item_make (core, endpoint_data->e.type); if (!ep) { wp_warning_object (self, "could not create endpoint of type %s", endpoint_data->e.type); return; } /* Configure the endpoint */ { g_auto (GVariantBuilder) b = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&b, "{sv}", "node", g_variant_new_uint64 ((guint64) proxy)); if (endpoint_data->e.c.name) g_variant_builder_add (&b, "{sv}", "name", g_variant_new_string (endpoint_data->e.c.name)); if (endpoint_data->e.c.media_class) g_variant_builder_add (&b, "{sv}", "media-class", g_variant_new_string (endpoint_data->e.c.media_class)); if (endpoint_data->e.c.role) g_variant_builder_add (&b, "{sv}", "role", g_variant_new_string (endpoint_data->e.c.role)); g_variant_builder_add (&b, "{sv}", "priority", g_variant_new_uint32 (endpoint_data->e.c.priority)); g_variant_builder_add (&b, "{sv}", "enable-control-port", g_variant_new_boolean (endpoint_data->e.c.enable_control_port)); g_variant_builder_add (&b, "{sv}", "enable-monitor", g_variant_new_boolean (endpoint_data->e.c.enable_monitor)); wp_session_item_configure (ep, g_variant_builder_end (&b)); } /* TODO: for now we always create softdsp audio endpoints if streams data is * valid. However, this will need to change once we have video endpoints. */ if (streams_data) { /* Create the steams endpoint */ streams_ep = wp_session_item_make (core, "si-audio-softdsp-endpoint"); /* Configure the streams endpoint */ { g_auto (GVariantBuilder) b = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&b, "{sv}", "adapter", g_variant_new_uint64 ((guint64) ep)); wp_session_item_configure (streams_ep, g_variant_builder_end (&b)); } /* Add the streams */ for (guint i = 0; i < streams_data->n_streams; i++) { const struct WpParserStreamsStreamData *sd = streams_data->streams + i; g_autoptr (WpSessionItem) stream = wp_session_item_make (core, "si-convert"); { g_auto (GVariantBuilder) b = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT); g_variant_builder_add (&b, "{sv}", "target", g_variant_new_uint64 ((guint64) ep)); g_variant_builder_add (&b, "{sv}", "name", g_variant_new_string (sd->name)); g_variant_builder_add (&b, "{sv}", "enable-control-port", g_variant_new_boolean (sd->enable_control_port)); wp_session_item_configure (stream, g_variant_builder_end (&b)); } wp_session_bin_add (WP_SESSION_BIN (streams_ep), g_steal_pointer (&stream)); } } /* Activate endpoint */ g_object_set_qdata_full ( G_OBJECT (streams_data ? streams_ep : ep), session_quark (), g_steal_pointer (&session), g_object_unref); wp_session_item_activate (streams_data ? streams_ep : ep, (GAsyncReadyCallback) endpoint_activate_finish_cb, self); /* Insert the endpoint */ g_hash_table_insert (self->endpoints, proxy, streams_data ? g_steal_pointer (&streams_ep) : g_steal_pointer (&ep)); } static void on_node_removed (WpObjectManager *om, WpProxy *proxy, gpointer d) { WpConfigEndpointContext *self = d; /* Remove the endpoint */ g_hash_table_remove (self->endpoints, proxy); } static void wp_config_endpoint_context_activate (WpPlugin * plugin) { WpConfigEndpointContext *self = WP_CONFIG_ENDPOINT_CONTEXT (plugin); g_autoptr (WpCore) core = wp_plugin_get_core (plugin); 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); self->endpoints = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_object_unref); /* Install the session object manager */ self->sessions_om = wp_object_manager_new (); wp_object_manager_add_interest (self->sessions_om, WP_TYPE_SESSION, NULL); wp_object_manager_request_proxy_features (self->sessions_om, WP_TYPE_SESSION, WP_SESSION_FEATURES_STANDARD); wp_core_install_object_manager (core, self->sessions_om); /* Handle node-added signal and install the nodes object manager */ self->nodes_om = wp_object_manager_new (); wp_object_manager_add_interest (self->nodes_om, WP_TYPE_NODE, NULL); wp_object_manager_request_proxy_features (self->nodes_om, WP_TYPE_NODE, WP_PROXY_FEATURES_STANDARD); g_signal_connect_object (self->nodes_om, "object-added", G_CALLBACK (on_node_added), self, 0); g_signal_connect_object (self->nodes_om, "object-removed", G_CALLBACK (on_node_removed), self, 0); wp_core_install_object_manager (core, self->nodes_om); } static void wp_config_endpoint_context_deactivate (WpPlugin *plugin) { WpConfigEndpointContext *self = WP_CONFIG_ENDPOINT_CONTEXT (plugin); g_autoptr (WpCore) core = wp_plugin_get_core (plugin); 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_clear_pointer (&self->endpoints, g_hash_table_unref); g_clear_object (&self->sessions_om); g_clear_object (&self->nodes_om); } static void wp_config_endpoint_context_init (WpConfigEndpointContext *self) { } static void wp_config_endpoint_context_class_init (WpConfigEndpointContextClass *klass) { WpPluginClass *plugin_class = (WpPluginClass *) klass; plugin_class->activate = wp_config_endpoint_context_activate; plugin_class->deactivate = wp_config_endpoint_context_deactivate; /* 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_SESSION_ITEM); } WpConfigEndpointContext * wp_config_endpoint_context_new (WpModule * module) { return g_object_new (wp_config_endpoint_context_get_type (), "module", module, NULL); }