mirror of
https://gitlab.freedesktop.org/pipewire/wireplumber.git
synced 2026-05-05 08:58:01 +02:00
modules: add config policy module
This commit is contained in:
parent
c8417f8826
commit
664db8a200
14 changed files with 1262 additions and 0 deletions
|
|
@ -64,3 +64,17 @@ shared_library(
|
|||
install_dir : wireplumber_module_dir,
|
||||
dependencies : [wp_dep, pipewire_dep],
|
||||
)
|
||||
|
||||
shared_library(
|
||||
'wireplumber-module-config-policy',
|
||||
[
|
||||
'module-config-policy/parser-endpoint-link.c',
|
||||
'module-config-policy/parser-streams.c',
|
||||
'module-config-policy/config-policy.c',
|
||||
'module-config-policy.c',
|
||||
],
|
||||
c_args : [common_c_args, '-DG_LOG_DOMAIN="m-config-policy"'],
|
||||
install : true,
|
||||
install_dir : wireplumber_module_dir,
|
||||
dependencies : [wp_dep, wptoml_dep, pipewire_dep],
|
||||
)
|
||||
|
|
|
|||
21
modules/module-config-policy.c
Normal file
21
modules/module-config-policy.c
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <wp/wp.h>
|
||||
|
||||
#include "module-config-policy/config-policy.h"
|
||||
|
||||
void
|
||||
wireplumber__module_init (WpModule * module, WpCore * core, GVariant * args)
|
||||
{
|
||||
g_autoptr (WpConfiguration) config = wp_configuration_get_instance (core);
|
||||
|
||||
/* Create and register the config policy */
|
||||
WpConfigPolicy *cp = wp_config_policy_new (config);
|
||||
wp_policy_register (WP_POLICY (cp), core);
|
||||
}
|
||||
473
modules/module-config-policy/config-policy.c
Normal file
473
modules/module-config-policy/config-policy.c
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <spa/utils/keys.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include <wp/wp.h>
|
||||
|
||||
#include "config-policy.h"
|
||||
#include "parser-endpoint-link.h"
|
||||
#include "parser-streams.h"
|
||||
|
||||
struct _WpConfigPolicy
|
||||
{
|
||||
WpPolicy parent;
|
||||
|
||||
WpConfiguration *config;
|
||||
|
||||
gboolean pending_rescan;
|
||||
WpEndpoint *pending_endpoint;
|
||||
WpEndpoint *pending_target;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_CONFIG,
|
||||
};
|
||||
|
||||
enum {
|
||||
SIGNAL_DONE,
|
||||
N_SIGNALS
|
||||
};
|
||||
|
||||
static guint signals[N_SIGNALS];
|
||||
|
||||
G_DEFINE_TYPE (WpConfigPolicy, wp_config_policy, WP_TYPE_POLICY)
|
||||
|
||||
static void
|
||||
on_endpoint_link_created (GObject *initable, GAsyncResult *res, gpointer p)
|
||||
{
|
||||
WpConfigPolicy *self = p;
|
||||
g_autoptr (WpEndpointLink) link = NULL;
|
||||
g_autoptr (GError) error = NULL;
|
||||
g_autoptr (WpEndpoint) src_ep = NULL;
|
||||
g_autoptr (WpEndpoint) sink_ep = NULL;
|
||||
|
||||
/* Get the link */
|
||||
link = wp_endpoint_link_new_finish(initable, res, &error);
|
||||
|
||||
/* Log linking info */
|
||||
if (error) {
|
||||
g_warning ("Could not link endpoints: %s\n", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
g_return_if_fail (link);
|
||||
src_ep = wp_endpoint_link_get_source_endpoint (link);
|
||||
sink_ep = wp_endpoint_link_get_sink_endpoint (link);
|
||||
g_info ("Sucessfully linked '%s' to '%s'\n", wp_endpoint_get_name (src_ep),
|
||||
wp_endpoint_get_name (sink_ep));
|
||||
|
||||
/* Clear the pending target */
|
||||
g_clear_object (&self->pending_target);
|
||||
|
||||
/* Emit the done signal */
|
||||
if (self->pending_endpoint) {
|
||||
gboolean is_capture =
|
||||
wp_endpoint_get_direction (self->pending_endpoint) == PW_DIRECTION_INPUT;
|
||||
if (self->pending_endpoint == (is_capture ? sink_ep : src_ep)) {
|
||||
g_signal_emit (self, signals[SIGNAL_DONE], 0, self->pending_endpoint, link);
|
||||
g_clear_object (&self->pending_endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wp_config_policy_can_link_stream (WpConfigPolicy *self, WpEndpoint *ep,
|
||||
const struct WpParserEndpointLinkData *data)
|
||||
{
|
||||
g_autoptr (WpConfigParser) parser = NULL;
|
||||
const struct WpParserStreamsData *streams_data = NULL;
|
||||
|
||||
/* If no streams data is specified, we can link */
|
||||
if (!data->te.streams)
|
||||
return TRUE;
|
||||
|
||||
/* If the endpoint is not linked, we can link */
|
||||
if (!wp_endpoint_is_linked (ep))
|
||||
return TRUE;
|
||||
|
||||
/* Get the linked stream */
|
||||
gboolean is_capture = wp_endpoint_get_direction (ep) == PW_DIRECTION_INPUT;
|
||||
GPtrArray *links = wp_endpoint_get_links (ep);
|
||||
WpEndpointLink *l = g_ptr_array_index (links, 0);
|
||||
guint32 linked_stream = is_capture ?
|
||||
wp_endpoint_link_get_sink_stream (l) :
|
||||
wp_endpoint_link_get_source_stream (l);
|
||||
|
||||
/* Check if linked stream is the same as ep stream. Last one wins */
|
||||
if (data->te.stream &&
|
||||
linked_stream == wp_endpoint_find_stream (ep, data->te.stream))
|
||||
return TRUE;
|
||||
|
||||
/* Get the linked stream name */
|
||||
g_autoptr (GVariant) s = wp_endpoint_get_stream (ep, linked_stream);
|
||||
if (!s)
|
||||
return TRUE;
|
||||
const gchar *linked_stream_name;
|
||||
if (!g_variant_lookup (s, "name", "&s", &linked_stream_name))
|
||||
return TRUE;
|
||||
|
||||
/* Get the linked and ep streams data */
|
||||
parser = wp_configuration_get_parser (self->config,
|
||||
WP_PARSER_STREAMS_EXTENSION);
|
||||
streams_data = wp_config_parser_get_matched_data (parser, data->te.streams);
|
||||
if (!data)
|
||||
return TRUE;
|
||||
const struct WpParserStreamsStreamData *linked_stream_data =
|
||||
wp_parser_streams_find_stream (streams_data, linked_stream_name);
|
||||
const struct WpParserStreamsStreamData *ep_stream_data =
|
||||
wp_parser_streams_find_stream (streams_data, data->te.stream);
|
||||
|
||||
/* Return false if linked stream has higher priority than ep stream */
|
||||
if (linked_stream_data && ep_stream_data) {
|
||||
if (linked_stream_data->priority > ep_stream_data->priority)
|
||||
return FALSE;
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
if (linked_stream_data && !ep_stream_data)
|
||||
return FALSE;
|
||||
if (!linked_stream_data && ep_stream_data)
|
||||
return TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wp_config_policy_link_endpoint_with_target (WpConfigPolicy *policy,
|
||||
WpEndpoint *ep, guint32 ep_stream, WpEndpoint *target,
|
||||
guint32 target_stream, const struct WpParserEndpointLinkData *data)
|
||||
{
|
||||
WpConfigPolicy *self = WP_CONFIG_POLICY (policy);
|
||||
g_autoptr (WpCore) core = wp_policy_get_core (WP_POLICY (self));
|
||||
gboolean is_capture = wp_endpoint_get_direction (ep) == PW_DIRECTION_INPUT;
|
||||
|
||||
/* Check if the endpoint is already linked with the proper target */
|
||||
if (wp_endpoint_is_linked (ep)) {
|
||||
GPtrArray *links = wp_endpoint_get_links (ep);
|
||||
WpEndpointLink *l = g_ptr_array_index (links, 0);
|
||||
g_autoptr (WpEndpoint) src_ep = wp_endpoint_link_get_source_endpoint (l);
|
||||
g_autoptr (WpEndpoint) sink_ep = wp_endpoint_link_get_sink_endpoint (l);
|
||||
WpEndpoint *existing_target = is_capture ? src_ep : sink_ep;
|
||||
|
||||
if (existing_target == target) {
|
||||
/* linked to correct target so do nothing */
|
||||
g_debug ("Endpoint '%s' is already linked correctly",
|
||||
wp_endpoint_get_name (ep));
|
||||
return FALSE;
|
||||
} else {
|
||||
/* linked to the wrong target so unlink and continue */
|
||||
g_debug ("Unlinking endpoint '%s' from its previous target",
|
||||
wp_endpoint_get_name (ep));
|
||||
wp_endpoint_link_destroy (l);
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure the target is not going to be linked with another endpoint */
|
||||
if (self->pending_target == target)
|
||||
return FALSE;
|
||||
g_clear_object (&self->pending_target);
|
||||
self->pending_target = g_object_ref (target);
|
||||
|
||||
/* Unlink the target links that are not kept if endpoint is capture */
|
||||
if (!is_capture && wp_endpoint_is_linked (target)) {
|
||||
GPtrArray *links = wp_endpoint_get_links (target);
|
||||
for (guint i = 0; i < links->len; i++) {
|
||||
WpEndpointLink *l = g_ptr_array_index (links, i);
|
||||
if (!wp_endpoint_link_is_kept (l))
|
||||
wp_endpoint_link_destroy (l);
|
||||
}
|
||||
}
|
||||
|
||||
/* Link the client with the target */
|
||||
if (is_capture) {
|
||||
wp_endpoint_link_new (core, target, target_stream, ep, ep_stream,
|
||||
data->el.keep, on_endpoint_link_created, self);
|
||||
} else {
|
||||
wp_endpoint_link_new (core, ep, ep_stream, target, target_stream,
|
||||
data->el.keep, on_endpoint_link_created, self);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wp_config_policy_handle_endpoint (WpPolicy *policy, WpEndpoint *ep)
|
||||
{
|
||||
WpConfigPolicy *self = WP_CONFIG_POLICY (policy);
|
||||
g_autoptr (WpCore) core = wp_policy_get_core (policy);
|
||||
g_autoptr (WpConfigParser) parser = NULL;
|
||||
const struct WpParserEndpointLinkData *data;
|
||||
GVariantBuilder b;
|
||||
GVariant *target_data = NULL;
|
||||
g_autoptr (WpEndpoint) target = NULL;
|
||||
guint32 stream_id;
|
||||
const char *role = NULL;
|
||||
|
||||
/* Get the parser for the endpoint-link extension */
|
||||
parser = wp_configuration_get_parser (self->config,
|
||||
WP_PARSER_ENDPOINT_LINK_EXTENSION);
|
||||
|
||||
/* Get the matched endpoint data from the parser */
|
||||
data = wp_config_parser_get_matched_data (parser, G_OBJECT (ep));
|
||||
if (!data)
|
||||
return FALSE;
|
||||
|
||||
/* Create the target gvariant */
|
||||
g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
|
||||
g_variant_builder_add (&b, "{sv}",
|
||||
"data", g_variant_new_uint64 ((guint64) data));
|
||||
role = wp_endpoint_get_role (ep);
|
||||
if (role)
|
||||
g_variant_builder_add (&b, "{sv}", "role", g_variant_new_string (role));
|
||||
target_data = g_variant_builder_end (&b);
|
||||
|
||||
/* Find the target endpoint */
|
||||
target = wp_policy_find_endpoint (core, target_data, &stream_id);
|
||||
if (!target) {
|
||||
g_info ("Target not found for endpoint '%s'", wp_endpoint_get_name (ep));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Don't link if the target is linked with a higher priority stream */
|
||||
if (!wp_config_policy_can_link_stream (self, target, data))
|
||||
return FALSE;
|
||||
|
||||
/* Link the endpoint with its target */
|
||||
return wp_config_policy_link_endpoint_with_target (self, ep,
|
||||
WP_STREAM_ID_NONE, target, stream_id, data);
|
||||
}
|
||||
|
||||
static WpEndpoint *
|
||||
wp_config_policy_find_endpoint (WpPolicy *policy, GVariant *props,
|
||||
guint32 *stream_id)
|
||||
{
|
||||
g_autoptr (WpCore) core = NULL;
|
||||
g_autoptr (WpPolicyManager) pmgr = NULL;
|
||||
const struct WpParserEndpointLinkData *data = NULL;
|
||||
g_autoptr (GPtrArray) endpoints = NULL;
|
||||
guint i;
|
||||
WpEndpoint *target = NULL;
|
||||
g_autoptr (WpProxy) proxy = NULL;
|
||||
g_autoptr (WpProperties) p = NULL;
|
||||
const char *role = NULL, *target_role = NULL;
|
||||
|
||||
/* Get the data from props */
|
||||
g_variant_lookup (props, "data", "t", &data);
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
/* Get all the endpoints matching the media class */
|
||||
core = wp_policy_get_core (policy);
|
||||
pmgr = wp_policy_manager_get_instance (core);
|
||||
endpoints = wp_policy_manager_list_endpoints (pmgr,
|
||||
data->te.endpoint_data.media_class);
|
||||
if (!endpoints)
|
||||
return NULL;
|
||||
|
||||
/* Get the first endpoint that matches target data */
|
||||
for (i = 0; i < endpoints->len; i++) {
|
||||
target = g_ptr_array_index (endpoints, i);
|
||||
if (wp_parser_endpoint_link_matches_endpoint_data (target,
|
||||
&data->te.endpoint_data))
|
||||
break;
|
||||
}
|
||||
|
||||
/* If target did not match any data, return NULL */
|
||||
if (i >= endpoints->len)
|
||||
return NULL;
|
||||
|
||||
/* Set the stream id */
|
||||
if (stream_id) {
|
||||
g_variant_lookup (props, "role", "&s", &role);
|
||||
target_role = role ? role : data->te.stream;
|
||||
*stream_id = target && target_role ?
|
||||
wp_endpoint_find_stream (target, target_role) :
|
||||
WP_CONTROL_ID_NONE;
|
||||
}
|
||||
|
||||
return g_object_ref (target);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_policy_sync_rescan (WpCore *core, GAsyncResult *res, gpointer data)
|
||||
{
|
||||
WpConfigPolicy *self = WP_CONFIG_POLICY (data);
|
||||
g_autoptr (WpPolicyManager) pmgr = wp_policy_manager_get_instance (core);
|
||||
g_autoptr (GPtrArray) endpoints = NULL;
|
||||
WpEndpoint *ep;
|
||||
gboolean handled = FALSE;
|
||||
|
||||
/* Handle all endpoints when rescanning */
|
||||
endpoints = wp_policy_manager_list_endpoints (pmgr, NULL);
|
||||
if (endpoints) {
|
||||
for (guint i = 0; i < endpoints->len; i++) {
|
||||
ep = g_ptr_array_index (endpoints, i);
|
||||
if (wp_config_policy_handle_endpoint (WP_POLICY (self), ep))
|
||||
handled = ep == self->pending_endpoint;
|
||||
}
|
||||
}
|
||||
|
||||
/* If endpoint was not handled, we are done */
|
||||
if (!handled) {
|
||||
g_signal_emit (self, signals[SIGNAL_DONE], 0, self->pending_endpoint,
|
||||
NULL);
|
||||
g_clear_object (&self->pending_endpoint);
|
||||
}
|
||||
|
||||
self->pending_rescan = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_policy_rescan (WpConfigPolicy *self, WpEndpoint *ep)
|
||||
{
|
||||
if (self->pending_rescan)
|
||||
return;
|
||||
|
||||
/* Check if there is a pending link while a new endpoint is added/removed */
|
||||
if (self->pending_endpoint) {
|
||||
g_warning ("Not handling endpoint '%s' beacause of pending link",
|
||||
wp_endpoint_get_name (ep));
|
||||
return;
|
||||
}
|
||||
|
||||
g_autoptr (WpCore) core = wp_policy_get_core (WP_POLICY (self));
|
||||
if (!core)
|
||||
return;
|
||||
|
||||
self->pending_endpoint = g_object_ref (ep);
|
||||
wp_core_sync (core, NULL, (GAsyncReadyCallback)wp_config_policy_sync_rescan,
|
||||
self);
|
||||
self->pending_rescan = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_policy_endpoint_added (WpPolicy *policy, WpEndpoint *ep)
|
||||
{
|
||||
WpConfigPolicy *self = WP_CONFIG_POLICY (policy);
|
||||
wp_config_policy_rescan (self, ep);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_policy_endpoint_removed (WpPolicy *policy, WpEndpoint *ep)
|
||||
{
|
||||
WpConfigPolicy *self = WP_CONFIG_POLICY (policy);
|
||||
wp_config_policy_rescan (self, ep);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_policy_set_property (GObject * object, guint property_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpConfigPolicy *self = WP_CONFIG_POLICY (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_CONFIG:
|
||||
self->config = g_value_dup_object (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_policy_get_property (GObject * object, guint property_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
WpConfigPolicy *self = WP_CONFIG_POLICY (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_CONFIG:
|
||||
g_value_take_object (value, g_object_ref (self->config));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_policy_constructed (GObject * object)
|
||||
{
|
||||
WpConfigPolicy *self = WP_CONFIG_POLICY (object);
|
||||
|
||||
/* Add the parsers */
|
||||
wp_configuration_add_extension (self->config,
|
||||
WP_PARSER_ENDPOINT_LINK_EXTENSION, WP_TYPE_PARSER_ENDPOINT_LINK);
|
||||
wp_configuration_add_extension (self->config,
|
||||
WP_PARSER_STREAMS_EXTENSION, WP_TYPE_PARSER_STREAMS);
|
||||
|
||||
/* Parse the file */
|
||||
wp_configuration_reload (self->config, WP_PARSER_ENDPOINT_LINK_EXTENSION);
|
||||
wp_configuration_reload (self->config, WP_PARSER_STREAMS_EXTENSION);
|
||||
|
||||
G_OBJECT_CLASS (wp_config_policy_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_policy_finalize (GObject *object)
|
||||
{
|
||||
WpConfigPolicy *self = WP_CONFIG_POLICY (object);
|
||||
|
||||
/* Remove the extensions from the configuration */
|
||||
wp_configuration_remove_extension (self->config,
|
||||
WP_PARSER_ENDPOINT_LINK_EXTENSION);
|
||||
wp_configuration_remove_extension (self->config,
|
||||
WP_PARSER_STREAMS_EXTENSION);
|
||||
|
||||
/* Clear the configuration */
|
||||
g_clear_object (&self->config);
|
||||
|
||||
G_OBJECT_CLASS (wp_config_policy_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_policy_init (WpConfigPolicy *self)
|
||||
{
|
||||
self->pending_rescan = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_config_policy_class_init (WpConfigPolicyClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
WpPolicyClass *policy_class = (WpPolicyClass *) klass;
|
||||
|
||||
object_class->constructed = wp_config_policy_constructed;
|
||||
object_class->finalize = wp_config_policy_finalize;
|
||||
object_class->set_property = wp_config_policy_set_property;
|
||||
object_class->get_property = wp_config_policy_get_property;
|
||||
|
||||
policy_class->endpoint_added = wp_config_policy_endpoint_added;
|
||||
policy_class->endpoint_removed = wp_config_policy_endpoint_removed;
|
||||
policy_class->find_endpoint = wp_config_policy_find_endpoint;
|
||||
|
||||
/* Properties */
|
||||
g_object_class_install_property (object_class, PROP_CONFIG,
|
||||
g_param_spec_object ("configuration", "configuration",
|
||||
"The configuration this policy is based on", WP_TYPE_CONFIGURATION,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/* Signals */
|
||||
signals[SIGNAL_DONE] = g_signal_new ("done",
|
||||
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 2, WP_TYPE_ENDPOINT, WP_TYPE_ENDPOINT_LINK);
|
||||
}
|
||||
|
||||
WpConfigPolicy *
|
||||
wp_config_policy_new (WpConfiguration *config)
|
||||
{
|
||||
return g_object_new (wp_config_policy_get_type (),
|
||||
"rank", WP_POLICY_RANK_UPSTREAM,
|
||||
"configuration", config,
|
||||
NULL);
|
||||
}
|
||||
22
modules/module-config-policy/config-policy.h
Normal file
22
modules/module-config-policy/config-policy.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef __WIREPLUMBER_CONFIG_POLICY_H__
|
||||
#define __WIREPLUMBER_CONFIG_POLICY_H__
|
||||
|
||||
#include <wp/wp.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
G_DECLARE_FINAL_TYPE (WpConfigPolicy, wp_config_policy, WP, CONFIG_POLICY, WpPolicy)
|
||||
|
||||
WpConfigPolicy *wp_config_policy_new (WpConfiguration *config);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
347
modules/module-config-policy/parser-endpoint-link.c
Normal file
347
modules/module-config-policy/parser-endpoint-link.c
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <wptoml/wptoml.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "parser-endpoint-link.h"
|
||||
|
||||
static gboolean
|
||||
wildcard_match (const char *pattern, const char *str)
|
||||
{
|
||||
if(*pattern == '\0' && *str == '\0')
|
||||
return TRUE;
|
||||
if(*pattern == '?' || *pattern == *str)
|
||||
return wildcard_match (pattern+1, str + 1);
|
||||
if(*pattern == '*')
|
||||
return wildcard_match (pattern + 1, str) ||
|
||||
wildcard_match (pattern, str + 1);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
wp_parser_endpoint_link_matches_endpoint_data (WpEndpoint *ep,
|
||||
const struct WpParserEndpointLinkEndpointData *data)
|
||||
{
|
||||
g_autoptr (WpProperties) props = NULL;
|
||||
|
||||
g_return_val_if_fail (ep, FALSE);
|
||||
g_return_val_if_fail (data, FALSE);
|
||||
|
||||
props = wp_endpoint_get_properties (ep);
|
||||
g_return_val_if_fail (props, FALSE);
|
||||
|
||||
/* Name */
|
||||
if (data->name &&
|
||||
!wildcard_match (data->name, wp_endpoint_get_name (ep)))
|
||||
return FALSE;
|
||||
|
||||
/* Media Class */
|
||||
if (data->media_class &&
|
||||
g_strcmp0 (wp_endpoint_get_media_class (ep), data->media_class) != 0)
|
||||
return FALSE;
|
||||
|
||||
/* Direction */
|
||||
if (wp_endpoint_get_direction (ep) != data->direction)
|
||||
return FALSE;
|
||||
|
||||
/* Properties */
|
||||
if (!wp_properties_matches (props, data->props))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
struct _WpParserEndpointLink
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GPtrArray *datas;
|
||||
};
|
||||
|
||||
static void wp_parser_endpoint_link_config_parser_init (gpointer iface,
|
||||
gpointer iface_data);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (WpParserEndpointLink, wp_parser_endpoint_link,
|
||||
G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_CONFIG_PARSER,
|
||||
wp_parser_endpoint_link_config_parser_init))
|
||||
|
||||
static void
|
||||
wp_parser_endpoint_link_data_destroy (gpointer p)
|
||||
{
|
||||
struct WpParserEndpointLinkData *data = p;
|
||||
|
||||
/* Free the strings */
|
||||
g_clear_pointer (&data->me.endpoint_data.name, g_free);
|
||||
g_clear_pointer (&data->me.endpoint_data.media_class, g_free);
|
||||
g_clear_pointer (&data->me.endpoint_data.props, wp_properties_unref);
|
||||
g_clear_pointer (&data->te.endpoint_data.name, g_free);
|
||||
g_clear_pointer (&data->te.endpoint_data.media_class, g_free);
|
||||
g_clear_pointer (&data->te.endpoint_data.props, wp_properties_unref);
|
||||
g_clear_pointer (&data->te.streams, g_free);
|
||||
g_clear_pointer (&data->te.stream, g_free);
|
||||
g_clear_pointer (&data->el.state, g_free);
|
||||
|
||||
g_slice_free (struct WpParserEndpointLinkData, data);
|
||||
}
|
||||
|
||||
static void
|
||||
parse_properties_for_each (const WpTomlTable *table, gpointer user_data)
|
||||
{
|
||||
WpProperties *props = user_data;
|
||||
g_return_if_fail (props);
|
||||
|
||||
/* Skip unparsed tables */
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
/* Parse the name and value */
|
||||
g_autofree gchar *name = wp_toml_table_get_string (table, "name");
|
||||
g_autofree gchar *value = wp_toml_table_get_string (table, "value");
|
||||
|
||||
/* Set the property */
|
||||
if (name && value)
|
||||
wp_properties_set (props, name, value);
|
||||
}
|
||||
|
||||
static WpProperties *
|
||||
parse_properties (WpTomlTable *table, const char *name)
|
||||
{
|
||||
WpProperties *props = wp_properties_new_empty ();
|
||||
|
||||
g_autoptr (WpTomlTableArray) properties = NULL;
|
||||
properties = wp_toml_table_get_array_table (table, name);
|
||||
if (properties)
|
||||
wp_toml_table_array_for_each (properties, parse_properties_for_each, props);
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
static guint
|
||||
parse_endpoint_direction (const char *direction)
|
||||
{
|
||||
if (g_strcmp0 (direction, "input") == 0)
|
||||
return PW_DIRECTION_INPUT;
|
||||
else if (g_strcmp0 (direction, "output") == 0)
|
||||
return PW_DIRECTION_OUTPUT;
|
||||
|
||||
g_return_val_if_reached (PW_DIRECTION_INPUT);
|
||||
}
|
||||
|
||||
static struct WpParserEndpointLinkData *
|
||||
wp_parser_endpoint_link_data_new (const gchar *location)
|
||||
{
|
||||
g_autoptr (WpTomlFile) file = NULL;
|
||||
g_autoptr (WpTomlTable) table = NULL, me = NULL, te = NULL, el = NULL;
|
||||
struct WpParserEndpointLinkData *res = NULL;
|
||||
g_autofree char *direction = NULL;
|
||||
|
||||
/* File format:
|
||||
* ------------
|
||||
* [match-endpoint]
|
||||
* priority (uint32)
|
||||
* name (string)
|
||||
* media_class (string)
|
||||
* direction (string)
|
||||
* properties (WpProperties)
|
||||
*
|
||||
* [target-endpoint]
|
||||
* name (string)
|
||||
* media_class (string)
|
||||
* direction (string)
|
||||
* properties (WpProperties)
|
||||
* stream (string)
|
||||
*
|
||||
* [endpoint-link]
|
||||
* state (string)
|
||||
* keep (bool)
|
||||
*/
|
||||
|
||||
/* Get the TOML file */
|
||||
file = wp_toml_file_new (location);
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
/* Get the file table */
|
||||
table = wp_toml_file_get_table (file);
|
||||
if (!table)
|
||||
return NULL;
|
||||
|
||||
/* Create the link data */
|
||||
res = g_slice_new0(struct WpParserEndpointLinkData);
|
||||
|
||||
/* Get the match-node table */
|
||||
me = wp_toml_table_get_table (table, "match-endpoint");
|
||||
if (!me)
|
||||
goto error;
|
||||
|
||||
/* Get the priority from the endpoint table */
|
||||
res->me.priority = 0;
|
||||
wp_toml_table_get_uint32 (me, "priority", &res->me.priority);
|
||||
|
||||
/* Get the name from the match endpoint table (Optional) */
|
||||
res->me.endpoint_data.name = wp_toml_table_get_string (me, "name");
|
||||
|
||||
/* Get the media class from the match endpoint table (Optional) */
|
||||
res->me.endpoint_data.media_class =
|
||||
wp_toml_table_get_string (me, "media_class");
|
||||
|
||||
/* Get the direction from the match endpoint table */
|
||||
direction = wp_toml_table_get_string (me, "direction");
|
||||
if (!direction)
|
||||
goto error;
|
||||
res->me.endpoint_data.direction = parse_endpoint_direction (direction);
|
||||
|
||||
/* Get the match endpoint properties (Optional) */
|
||||
res->me.endpoint_data.props =
|
||||
parse_properties (table, "match-endpoint-properties");
|
||||
|
||||
/* Get the target-endpoint table */
|
||||
te = wp_toml_table_get_table (table, "target-endpoint");
|
||||
if (!te)
|
||||
goto error;
|
||||
|
||||
/* Get the name from the match endpoint table (Optional) */
|
||||
res->te.endpoint_data.name = wp_toml_table_get_string (te, "name");
|
||||
|
||||
/* Get the media class from the match endpoint table (Optional) */
|
||||
res->te.endpoint_data.media_class =
|
||||
wp_toml_table_get_string (te, "media_class");
|
||||
|
||||
/* Set the direction to the match endpoint's reverse one */
|
||||
res->te.endpoint_data.direction =
|
||||
pw_direction_reverse (res->me.endpoint_data.direction);
|
||||
|
||||
/* Get the target endpoint properties (Optional) */
|
||||
res->te.endpoint_data.props =
|
||||
parse_properties (table, "target-endpoint-properties");
|
||||
|
||||
/* Get the target endpoint streams */
|
||||
res->te.streams = wp_toml_table_get_string (te, "streams");
|
||||
|
||||
/* Get the target endpoint stream */
|
||||
res->te.stream = wp_toml_table_get_string (te, "stream");
|
||||
|
||||
/* Get the target-endpoint table */
|
||||
el = wp_toml_table_get_table (table, "endpoint-link");
|
||||
if (!el)
|
||||
goto error;
|
||||
|
||||
/* Get the endpoint link state */
|
||||
res->el.state = wp_toml_table_get_string (el, "state");
|
||||
if (!res->el.state)
|
||||
res->el.state = g_strdup ("active");
|
||||
|
||||
/* Get the endpoint link keep */
|
||||
res->el.keep = FALSE;
|
||||
wp_toml_table_get_boolean (el, "keep", &res->el.keep);
|
||||
|
||||
return res;
|
||||
|
||||
error:
|
||||
g_clear_pointer (&res, wp_parser_endpoint_link_data_destroy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gint
|
||||
compare_datas_func (gconstpointer a, gconstpointer b)
|
||||
{
|
||||
struct WpParserEndpointLinkData *da =
|
||||
*(struct WpParserEndpointLinkData *const *)a;
|
||||
struct WpParserEndpointLinkData *db =
|
||||
*(struct WpParserEndpointLinkData *const *)b;
|
||||
|
||||
return da->me.priority - db->me.priority;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wp_parser_endpoint_link_add_file (WpConfigParser *parser,
|
||||
const gchar *name)
|
||||
{
|
||||
WpParserEndpointLink *self = WP_PARSER_ENDPOINT_LINK (parser);
|
||||
struct WpParserEndpointLinkData *data;
|
||||
|
||||
/* Parse the file */
|
||||
data = wp_parser_endpoint_link_data_new (name);
|
||||
if (!data) {
|
||||
g_warning ("Failed to parse configuration file '%s'", name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Add the data to the array */
|
||||
g_ptr_array_add(self->datas, data);
|
||||
|
||||
/* Sort the array by priority */
|
||||
g_ptr_array_sort(self->datas, compare_datas_func);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_parser_endpoint_link_get_matched_data (WpConfigParser *parser, gpointer data)
|
||||
{
|
||||
WpParserEndpointLink *self = WP_PARSER_ENDPOINT_LINK (parser);
|
||||
WpEndpoint *ep = WP_ENDPOINT (data);
|
||||
const struct WpParserEndpointLinkData *d = NULL;
|
||||
|
||||
/* Find the first data that matches endpoint */
|
||||
for (guint i = 0; i < self->datas->len; i++) {
|
||||
d = g_ptr_array_index(self->datas, i);
|
||||
if (wp_parser_endpoint_link_matches_endpoint_data (ep,
|
||||
&d->me.endpoint_data))
|
||||
return d;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_endpoint_link_reset (WpConfigParser *parser)
|
||||
{
|
||||
WpParserEndpointLink *self = WP_PARSER_ENDPOINT_LINK (parser);
|
||||
|
||||
g_ptr_array_set_size (self->datas, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_endpoint_link_config_parser_init (gpointer iface,
|
||||
gpointer iface_data)
|
||||
{
|
||||
WpConfigParserInterface *cp_iface = iface;
|
||||
|
||||
cp_iface->add_file = wp_parser_endpoint_link_add_file;
|
||||
cp_iface->get_matched_data = wp_parser_endpoint_link_get_matched_data;
|
||||
cp_iface->reset = wp_parser_endpoint_link_reset;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_endpoint_link_finalize (GObject * object)
|
||||
{
|
||||
WpParserEndpointLink *self = WP_PARSER_ENDPOINT_LINK (object);
|
||||
|
||||
g_clear_pointer (&self->datas, g_ptr_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (wp_parser_endpoint_link_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_endpoint_link_init (WpParserEndpointLink * self)
|
||||
{
|
||||
self->datas = g_ptr_array_new_with_free_func(
|
||||
wp_parser_endpoint_link_data_destroy);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_endpoint_link_class_init (WpParserEndpointLinkClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
|
||||
object_class->finalize = wp_parser_endpoint_link_finalize;
|
||||
}
|
||||
51
modules/module-config-policy/parser-endpoint-link.h
Normal file
51
modules/module-config-policy/parser-endpoint-link.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef __WIREPLUMBER_PARSER_ENDPOINT_LINK_H__
|
||||
#define __WIREPLUMBER_PARSER_ENDPOINT_LINK_H__
|
||||
|
||||
#include <wp/wp.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define WP_PARSER_ENDPOINT_LINK_EXTENSION "endpoint-link"
|
||||
|
||||
struct WpParserEndpointLinkEndpointData {
|
||||
char *name;
|
||||
char *media_class;
|
||||
guint direction;
|
||||
WpProperties *props;
|
||||
};
|
||||
|
||||
struct WpParserEndpointLinkData {
|
||||
struct MatchEndpoint {
|
||||
guint priority;
|
||||
struct WpParserEndpointLinkEndpointData endpoint_data;
|
||||
} me;
|
||||
struct TargetEndpoint {
|
||||
struct WpParserEndpointLinkEndpointData endpoint_data;
|
||||
char *streams;
|
||||
char *stream;
|
||||
} te;
|
||||
struct EndpointLink {
|
||||
char *state;
|
||||
gboolean keep;
|
||||
} el;
|
||||
};
|
||||
|
||||
/* Helpers */
|
||||
gboolean wp_parser_endpoint_link_matches_endpoint_data (WpEndpoint *ep,
|
||||
const struct WpParserEndpointLinkEndpointData *data);
|
||||
|
||||
#define WP_TYPE_PARSER_ENDPOINT_LINK (wp_parser_endpoint_link_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (WpParserEndpointLink, wp_parser_endpoint_link,
|
||||
WP, PARSER_ENDPOINT_LINK, GObject)
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
208
modules/module-config-policy/parser-streams.c
Normal file
208
modules/module-config-policy/parser-streams.c
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <wptoml/wptoml.h>
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
#include "parser-streams.h"
|
||||
|
||||
struct _WpParserStreams
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GPtrArray *datas;
|
||||
};
|
||||
|
||||
const struct WpParserStreamsStreamData *
|
||||
wp_parser_streams_find_stream (const struct WpParserStreamsData *data,
|
||||
const char *name)
|
||||
{
|
||||
for (guint i = 0; i < data->n_streams; i++) {
|
||||
const struct WpParserStreamsStreamData *s = data->streams + i;
|
||||
if (g_strcmp0 (s->name, name) == 0)
|
||||
return s;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void wp_parser_streams_config_parser_init (gpointer iface,
|
||||
gpointer iface_data);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (WpParserStreams, wp_parser_streams,
|
||||
G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE (WP_TYPE_CONFIG_PARSER,
|
||||
wp_parser_streams_config_parser_init))
|
||||
|
||||
static void
|
||||
wp_parser_streams_data_destroy (gpointer p)
|
||||
{
|
||||
struct WpParserStreamsData *data = p;
|
||||
|
||||
/* Clear the location */
|
||||
g_clear_pointer (&data->location, g_free);
|
||||
|
||||
/* Clear the streams */
|
||||
for (guint i = 0; i < data->n_streams; i++) {
|
||||
struct WpParserStreamsStreamData *s = data->streams + i;
|
||||
g_clear_pointer (&s->name, g_free);
|
||||
}
|
||||
data->n_streams = 0;
|
||||
|
||||
g_slice_free (struct WpParserStreamsData, data);
|
||||
}
|
||||
|
||||
static void
|
||||
streams_for_each (const WpTomlTable *table, gpointer user_data)
|
||||
{
|
||||
struct WpParserStreamsData *data = user_data;
|
||||
struct WpParserStreamsStreamData *stream = NULL;
|
||||
g_return_if_fail (data);
|
||||
|
||||
/* Make sure we don't parse more MAX_STREAMS streams */
|
||||
if (data->n_streams >= MAX_STREAMS)
|
||||
return;
|
||||
|
||||
/* Skip unparsed tables */
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
/* Parse the mandatory name */
|
||||
stream = data->streams + data->n_streams;
|
||||
stream->name = wp_toml_table_get_string (table, "name");
|
||||
if (!stream->name)
|
||||
return;
|
||||
|
||||
/* Parse the optional priority */
|
||||
stream->priority = 0;
|
||||
wp_toml_table_get_uint32 (table, "priority", &stream->priority);
|
||||
|
||||
/* Increment the number of streams */
|
||||
data->n_streams++;
|
||||
}
|
||||
|
||||
|
||||
static struct WpParserStreamsData *
|
||||
wp_parser_streams_data_new (const gchar *location)
|
||||
{
|
||||
g_autoptr (WpTomlFile) file = NULL;
|
||||
g_autoptr (WpTomlTable) table = NULL;
|
||||
g_autoptr (WpTomlTableArray) streams = NULL;
|
||||
struct WpParserStreamsData *res = NULL;
|
||||
|
||||
/* File format:
|
||||
* ------------
|
||||
* [[streams]]
|
||||
* name (string)
|
||||
* priority (uint32)
|
||||
*/
|
||||
|
||||
/* Get the TOML file */
|
||||
file = wp_toml_file_new (location);
|
||||
if (!file)
|
||||
return NULL;
|
||||
|
||||
/* Get the file table */
|
||||
table = wp_toml_file_get_table (file);
|
||||
if (!table)
|
||||
return NULL;
|
||||
|
||||
/* Create the streams data */
|
||||
res = g_slice_new0 (struct WpParserStreamsData);
|
||||
|
||||
/* Set the location */
|
||||
res->location = g_strdup (location);
|
||||
|
||||
/* Parse the streams */
|
||||
res->n_streams = 0;
|
||||
streams = wp_toml_table_get_array_table (table, "streams");
|
||||
if (streams)
|
||||
wp_toml_table_array_for_each (streams, streams_for_each, res);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wp_parser_streams_add_file (WpConfigParser *parser,
|
||||
const gchar *name)
|
||||
{
|
||||
WpParserStreams *self = WP_PARSER_STREAMS (parser);
|
||||
struct WpParserStreamsData *data;
|
||||
|
||||
/* Parse the file */
|
||||
data = wp_parser_streams_data_new (name);
|
||||
if (!data) {
|
||||
g_warning ("Failed to parse configuration file '%s'", name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Add the data to the array */
|
||||
g_ptr_array_add(self->datas, data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gconstpointer
|
||||
wp_parser_streams_get_matched_data (WpConfigParser *parser, gpointer data)
|
||||
{
|
||||
WpParserStreams *self = WP_PARSER_STREAMS (parser);
|
||||
const char *location = data;
|
||||
const struct WpParserStreamsData *d = NULL;
|
||||
|
||||
/* Find the first data that matches location */
|
||||
for (guint i = 0; i < self->datas->len; i++) {
|
||||
d = g_ptr_array_index(self->datas, i);
|
||||
if (g_strrstr (d->location, location))
|
||||
return d;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_streams_reset (WpConfigParser *parser)
|
||||
{
|
||||
WpParserStreams *self = WP_PARSER_STREAMS (parser);
|
||||
|
||||
g_ptr_array_set_size (self->datas, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_streams_config_parser_init (gpointer iface,
|
||||
gpointer iface_data)
|
||||
{
|
||||
WpConfigParserInterface *cp_iface = iface;
|
||||
|
||||
cp_iface->add_file = wp_parser_streams_add_file;
|
||||
cp_iface->get_matched_data = wp_parser_streams_get_matched_data;
|
||||
cp_iface->reset = wp_parser_streams_reset;
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_streams_finalize (GObject * object)
|
||||
{
|
||||
WpParserStreams *self = WP_PARSER_STREAMS (object);
|
||||
|
||||
g_clear_pointer (&self->datas, g_ptr_array_unref);
|
||||
|
||||
G_OBJECT_CLASS (wp_parser_streams_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_streams_init (WpParserStreams * self)
|
||||
{
|
||||
self->datas = g_ptr_array_new_with_free_func (wp_parser_streams_data_destroy);
|
||||
}
|
||||
|
||||
static void
|
||||
wp_parser_streams_class_init (WpParserStreamsClass * klass)
|
||||
{
|
||||
GObjectClass *object_class = (GObjectClass *) klass;
|
||||
|
||||
object_class->finalize = wp_parser_streams_finalize;
|
||||
}
|
||||
42
modules/module-config-policy/parser-streams.h
Normal file
42
modules/module-config-policy/parser-streams.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/* WirePlumber
|
||||
*
|
||||
* Copyright © 2019 Collabora Ltd.
|
||||
* @author Julian Bouzas <julian.bouzas@collabora.com>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#ifndef __WIREPLUMBER_PARSER_STREAMS_H__
|
||||
#define __WIREPLUMBER_PARSER_STREAMS_H__
|
||||
|
||||
#include <wp/wp.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define WP_PARSER_STREAMS_EXTENSION "streams"
|
||||
|
||||
/* For simplicity, we limit the number of streams */
|
||||
#define MAX_STREAMS 32
|
||||
|
||||
struct WpParserStreamsStreamData {
|
||||
char *name;
|
||||
guint priority;
|
||||
};
|
||||
|
||||
struct WpParserStreamsData {
|
||||
char *location;
|
||||
struct WpParserStreamsStreamData streams[MAX_STREAMS];
|
||||
guint n_streams;
|
||||
};
|
||||
|
||||
/* Helpers */
|
||||
const struct WpParserStreamsStreamData *wp_parser_streams_find_stream (
|
||||
const struct WpParserStreamsData *data, const char *name);
|
||||
|
||||
#define WP_TYPE_PARSER_STREAMS (wp_parser_streams_get_type ())
|
||||
G_DECLARE_FINAL_TYPE (WpParserStreams, wp_parser_streams,
|
||||
WP, PARSER_STREAMS, GObject);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -40,3 +40,6 @@ load-module C libwireplumber-module-pw-alsa-udev {
|
|||
# Monitors the Audio clients that are discovered via pipewire
|
||||
# and creates simple-endpoints for each one of them
|
||||
load-module C libwireplumber-module-pw-audio-client
|
||||
|
||||
# Implements linking clients to devices based on TOML configuration files
|
||||
load-module C libwireplumber-module-config-policy
|
||||
|
|
|
|||
17
src/wireplumber/bluealsa-input-audio.endpoint-link
Normal file
17
src/wireplumber/bluealsa-input-audio.endpoint-link
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
[match-endpoint]
|
||||
priority = 75
|
||||
direction = "input"
|
||||
name = "bluealsa*"
|
||||
media_class = "Stream/Input/Audio"
|
||||
|
||||
[target-endpoint]
|
||||
media_class = "Audio/Source"
|
||||
streams = "default.streams"
|
||||
stream = "Multimedia"
|
||||
|
||||
[[target-endpoint-properties]]
|
||||
name = "api.alsa.path"
|
||||
value = "hw:0,0"
|
||||
|
||||
[endpoint-link]
|
||||
keep = true
|
||||
17
src/wireplumber/bluealsa-output-audio.endpoint-link
Normal file
17
src/wireplumber/bluealsa-output-audio.endpoint-link
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
[match-endpoint]
|
||||
priority = 75
|
||||
direction = "output"
|
||||
name = "bluealsa*"
|
||||
media_class = "Stream/Output/Audio"
|
||||
|
||||
[target-endpoint]
|
||||
media_class = "Audio/Sink"
|
||||
streams = "default.streams"
|
||||
stream = "Multimedia"
|
||||
|
||||
[[target-endpoint-properties]]
|
||||
name = "api.alsa.path"
|
||||
value = "hw:0,0"
|
||||
|
||||
[endpoint-link]
|
||||
keep = true
|
||||
16
src/wireplumber/default-input-audio.endpoint-link
Normal file
16
src/wireplumber/default-input-audio.endpoint-link
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
[match-endpoint]
|
||||
priority = 50
|
||||
direction = "input"
|
||||
media_class = "Stream/Input/Audio"
|
||||
|
||||
[target-endpoint]
|
||||
media_class = "Audio/Source"
|
||||
streams = "default.streams"
|
||||
stream = "Multimedia"
|
||||
|
||||
[[target-endpoint-properties]]
|
||||
name = "api.alsa.path"
|
||||
value = "hw:0,0"
|
||||
|
||||
[endpoint-link]
|
||||
keep = false
|
||||
16
src/wireplumber/default-output-audio.endpoint-link
Normal file
16
src/wireplumber/default-output-audio.endpoint-link
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
[match-endpoint]
|
||||
priority = 50
|
||||
direction = "output"
|
||||
media_class = "Stream/Output/Audio"
|
||||
|
||||
[target-endpoint]
|
||||
media_class = "Audio/Sink"
|
||||
streams = "default.streams"
|
||||
stream = "Multimedia"
|
||||
|
||||
[[target-endpoint-properties]]
|
||||
name = "api.alsa.path"
|
||||
value = "hw:0,0"
|
||||
|
||||
[endpoint-link]
|
||||
keep = false
|
||||
15
src/wireplumber/default.streams
Normal file
15
src/wireplumber/default.streams
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
[[streams]]
|
||||
name = "Multimedia"
|
||||
priority = 25
|
||||
|
||||
[[streams]]
|
||||
name = "Navigation"
|
||||
priority = 50
|
||||
|
||||
[[streams]]
|
||||
name = "Communiction"
|
||||
priority = 75
|
||||
|
||||
[[streams]]
|
||||
name = "Emergency"
|
||||
priority = 99
|
||||
Loading…
Add table
Reference in a new issue