config: make it possible to have feature profiles

A profile is a list of features set to required/optional/disabled
which governs which components are getting loaded, given a static
components list with well-defined dependencies
This commit is contained in:
George Kiagiadakis
2023-06-23 20:15:26 +03:00
parent 840a2304b4
commit 84d3382292
6 changed files with 54 additions and 45 deletions

View File

@@ -476,14 +476,13 @@ wp_core_activate_execute_step (WpObject * object,
return;
}
else {
g_autoptr (WpConf) conf = wp_conf_get_instance (self);
g_autoptr (WpSpaJson) json_comps = NULL;
const gchar *profile = wp_properties_get (props, "wireplumber.profile");
wp_info_object (self, "parsing & loading components...");
wp_info_object (self,
"parsing & loading components for profile [%s]...", profile);
/* Load components that are defined in the configuration section */
json_comps = wp_conf_get_section (conf, "wireplumber.components", NULL);
wp_core_load_component (self, NULL, "array", json_comps, NULL, NULL,
wp_core_load_component (self, profile, "profile", NULL, NULL, NULL,
(GAsyncReadyCallback) on_components_loaded, transition);
}
break;

View File

@@ -169,6 +169,8 @@ struct _WpComponentArrayLoadTask
WpTransition parent;
/* the input json object */
WpSpaJson *json;
/* the features profile */
WpProperties *profile;
/* all components that provide a feature; key: comp->provides, value: comp */
GHashTable *feat_components;
/* the final sorted list of components to load */
@@ -286,25 +288,11 @@ add_component (ComponentData * comp, gboolean strongly_required,
return TRUE;
}
static WpProperties *
conf_get_features_section (WpComponentArrayLoadTask * self)
{
WpProperties *props = wp_properties_new_empty ();
WpCore *core = wp_transition_get_data (WP_TRANSITION (self));
g_autoptr (WpConf) conf = wp_conf_get_instance (core);
g_autoptr (WpSpaJson) json =
wp_conf_get_section (conf, "wireplumber.features", NULL);
if (json)
wp_properties_update_from_json (props, json);
return props;
}
static gboolean
parse_components (WpComponentArrayLoadTask * self, GError ** error)
{
/* all the parsed components that are explicitly required */
g_autoptr (GPtrArray) required_components = NULL;
g_autoptr (WpProperties) conf = conf_get_features_section (self);
g_autoptr (WpIterator) it = NULL;
g_auto (GValue) item = G_VALUE_INIT;
@@ -329,7 +317,7 @@ parse_components (WpComponentArrayLoadTask * self, GError ** error)
GError *e = NULL;
g_autoptr (ComponentData) comp = NULL;
if (!(comp = component_data_new_from_json (cjson, conf, &e))) {
if (!(comp = component_data_new_from_json (cjson, self->profile, &e))) {
g_propagate_error (error, e);
return FALSE;
}
@@ -470,6 +458,7 @@ wp_component_array_load_task_finalize (GObject * object)
g_clear_pointer (&self->feat_components, g_hash_table_unref);
g_clear_pointer (&self->components, g_ptr_array_unref);
g_clear_pointer (&self->profile, wp_properties_unref);
g_clear_pointer (&self->json, wp_spa_json_unref);
G_OBJECT_CLASS (wp_component_array_load_task_parent_class)->finalize (object);
@@ -488,7 +477,7 @@ wp_component_array_load_task_class_init (WpComponentArrayLoadTaskClass * klass)
}
static WpTransition *
wp_component_array_load_task_new (WpSpaJson *json,
wp_component_array_load_task_new (WpSpaJson * json, WpProperties * profile,
gpointer source_object, GCancellable * cancellable,
GAsyncReadyCallback callback, gpointer callback_data)
{
@@ -496,6 +485,7 @@ wp_component_array_load_task_new (WpSpaJson *json,
source_object, cancellable, callback, callback_data);
WpComponentArrayLoadTask *task = WP_COMPONENT_ARRAY_LOAD_TASK (t);
task->json = wp_spa_json_ref (json);
task->profile = wp_properties_ref (profile);
return t;
}
@@ -637,9 +627,10 @@ wp_internal_comp_loader_supports_type (WpComponentLoader * cl,
const gchar * type)
{
return g_str_equal (type, "module") ||
g_str_equal (type, "array") ||
g_str_equal (type, "virtual") ||
g_str_equal (type, "built-in");
g_str_equal (type, "built-in") ||
g_str_equal (type, "profile") ||
g_str_equal (type, "array");
}
static void
@@ -647,8 +638,26 @@ wp_internal_comp_loader_load (WpComponentLoader * self, WpCore * core,
const gchar * component, const gchar * type, WpSpaJson * args,
GCancellable * cancellable, GAsyncReadyCallback callback, gpointer data)
{
if (g_str_equal (type, "array")) {
WpTransition *task = wp_component_array_load_task_new (args, self,
if (g_str_equal (type, "profile") || g_str_equal (type, "array")) {
WpTransition *task = NULL;
g_autoptr (WpSpaJson) components = NULL;
g_autoptr (WpProperties) profile = wp_properties_new_empty ();
if (g_str_equal (type, "profile")) {
/* component name is the profile name;
component list and profile features are loaded from config */
g_autoptr (WpConf) conf = wp_conf_get_instance (core);
g_autoptr (WpSpaJson) profile_json =
wp_conf_get_value (conf, "wireplumber.profiles", component, NULL);
if (profile_json)
wp_properties_update_from_json (profile, profile_json);
components = wp_conf_get_section (conf, "wireplumber.components", NULL);
} else {
/* component list is retrieved from args; profile features are empty */
components = wp_spa_json_ref (args);
}
task = wp_component_array_load_task_new (components, profile, self,
cancellable, callback, data);
wp_transition_set_data (task, g_object_ref (core), g_object_unref);
wp_transition_set_source_tag (task, wp_internal_comp_loader_load);

View File

@@ -77,11 +77,15 @@ context.modules = [
{ name = libpipewire-module-spa-node-factory }
]
wireplumber.features = {
wireplumber.profiles = {
## Syntax:
## <profile> = {
## # optional is the default
## <feature name> = [ required | optional | disabled ]
## optional is the default
## ...
## }
main = {
check.no-media-session = required
support.settings = required
hardware.audio = required
@@ -90,6 +94,7 @@ wireplumber.features = {
policy.standard = required
#policy.role-priority-system = optional
}
}
wireplumber.components = [
## WirePlumber components to load

View File

@@ -161,6 +161,7 @@ main (gint argc, gchar **argv)
PW_KEY_CONFIG_NAME, config_file,
PW_KEY_APP_NAME, "WirePlumber",
"wireplumber.daemon", "true",
"wireplumber.profile", "main",
NULL);
/* init wireplumber daemon */

View File

@@ -200,14 +200,7 @@ test_dependencies_setup (TestFixture *f, gconstpointer data)
static void
test_dependencies (TestFixture *f, gconstpointer data)
{
g_autoptr (WpConf) conf = wp_conf_get_instance (f->base.core);
g_assert_nonnull (conf);
g_autoptr (WpSpaJson) components = wp_conf_get_section (conf,
"wireplumber.components", NULL);
g_assert_nonnull (components);
wp_core_load_component (f->base.core, NULL, "array", components,
wp_core_load_component (f->base.core, "test", "profile", NULL,
NULL, NULL, (GAsyncReadyCallback) on_component_loaded, f);
g_main_loop_run (f->base.loop);

View File

@@ -2,9 +2,11 @@ context.modules = [
{ name = libpipewire-module-protocol-native }
]
wireplumber.features = {
wireplumber.profiles = {
test = {
virtual.four = required
}
}
wireplumber.components = [
# expected load order: