modules: Add new collection-manager module

This module is in charge of creating all the collection metadatas.
This commit is contained in:
Julian Bouzas 2025-12-07 10:59:41 -05:00
parent 6efc077a5a
commit 511d85177d
4 changed files with 264 additions and 0 deletions

View file

@ -780,6 +780,20 @@ load_settings_instance (GTask * task, WpCore * core, WpSpaJson * args)
g_task_return_pointer (task, settings, g_object_unref);
}
static void
load_collection_manager_instance (GTask * task, WpCore * core, WpSpaJson * args)
{
g_autofree gchar *metadata_name = NULL;
if (args)
wp_spa_json_object_get (args, "metadata.name", "s", &metadata_name, NULL);
wp_info_object (core, "loading collection manager instance '%s'...",
metadata_name ? metadata_name : "(default: sm-collection-manager)");
WpCollectionManager *cm = wp_collection_manager_new (core, metadata_name);
g_task_return_pointer (task, cm, g_object_unref);
}
static const struct {
const gchar * name;
void (*load) (GTask *, WpCore *, WpSpaJson *);
@ -787,6 +801,7 @@ static const struct {
{ "ensure-no-media-session", ensure_no_media_session },
{ "export-core", load_export_core },
{ "settings-instance", load_settings_instance },
{ "collection-manager-instance", load_collection_manager_instance },
};
/*** WpInternalCompLoader ***/

View file

@ -191,3 +191,13 @@ shared_library(
install_dir : wireplumber_module_dir,
dependencies : [wp_dep],
)
shared_library(
'wireplumber-module-collection-manager',
[
'module-collection-manager.c',
],
install : true,
install_dir : wireplumber_module_dir,
dependencies : [wp_dep, pipewire_dep],
)

View file

@ -0,0 +1,217 @@
/* WirePlumber
*
* Copyright © 2025 Collabora Ltd.
* @author Julian Bouzas <julian.bouzas@collabora.com>
*
* SPDX-License-Identifier: MIT
*/
#include <wp/wp.h>
#include <spa/utils/defs.h>
WP_DEFINE_LOCAL_LOG_TOPIC ("m-collection-manager")
/*
* This module creates the "sm-collection-manager" metadata and also each
* individual collection metadata.
*/
struct _WpCollectionManagerPlugin
{
WpPlugin parent;
/* Props */
gchar *metadata_name;
WpImplMetadata *impl_metadata;
GHashTable *collection_metadatas;
};
enum {
PROP_0,
PROP_METADATA_NAME,
};
G_DECLARE_FINAL_TYPE (WpCollectionManagerPlugin, wp_collection_manager_plugin,
WP, COLLECTION_MANAGER_PLUGIN, WpPlugin)
G_DEFINE_TYPE (WpCollectionManagerPlugin, wp_collection_manager_plugin,
WP_TYPE_PLUGIN)
static void
wp_collection_manager_plugin_init (WpCollectionManagerPlugin * self)
{
}
static void
on_collection_metadata_activated (GObject * o, GAsyncResult * res, gpointer d)
{
WpCollectionManagerPlugin *self = WP_COLLECTION_MANAGER_PLUGIN (d);
g_autoptr (WpImplMetadata) m = WP_IMPL_METADATA (o);
g_autoptr (WpProperties) props = NULL;
const gchar *name = NULL;
g_autoptr (GError) error = NULL;
/* Get the metadata name */
props = wp_global_proxy_get_global_properties (WP_GLOBAL_PROXY (m));
g_return_if_fail (props);
name = wp_properties_get (props, "metadata.name");
g_return_if_fail (name);
/* make sure activation was successful */
if (!wp_object_activate_finish (WP_OBJECT (m), res, &error)) {
wp_warning_object (self, "Failed to activate collection metadata: %s",
name);
return;
}
/* Add the metadata */
g_hash_table_insert (self->collection_metadatas, g_strdup (name),
g_object_ref (m));
wp_info_object (self, "Added collection metadata: %s", name);
}
static void
on_metadata_changed (WpMetadata *m, guint32 subject, const gchar *key,
const gchar *type, const gchar *value, gpointer d)
{
WpCollectionManagerPlugin *self = WP_COLLECTION_MANAGER_PLUGIN (d);
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self));
if (key) {
WpImplMetadata *collection_m = g_hash_table_lookup (
self->collection_metadatas, key);
if (collection_m && !value) {
/* Remove collection metadata */
g_hash_table_remove (self->collection_metadatas, key);
wp_info_object (self, "Removed collection metadata: %s", key);
} else if (!collection_m && value) {
/* Create new collection metadata */
wp_info_object (self, "Creating collection metadata: %s", key);
WpImplMetadata *new_collection_m = wp_impl_metadata_new_full (core, key,
wp_properties_new ("wireplumber.collection", "true", NULL));
wp_object_activate (WP_OBJECT (new_collection_m), WP_OBJECT_FEATURES_ALL,
NULL, on_collection_metadata_activated, self);
}
} else {
/* Remove all collection metadatas */
g_hash_table_remove_all (self->collection_metadatas);
wp_info_object (self, "Removed all collection metadatas");
}
}
static void
on_metadata_activated (WpMetadata * m, GAsyncResult * res,
gpointer user_data)
{
WpTransition *trans = WP_TRANSITION (user_data);
WpCollectionManagerPlugin *self = wp_transition_get_source_object (trans);
g_autoptr (GError) error = NULL;
if (!wp_object_activate_finish (WP_OBJECT (m), res, &error)) {
wp_transition_return_error (trans, g_error_new (WP_DOMAIN_LIBRARY,
WP_LIBRARY_ERROR_OPERATION_FAILED,
"Failed to activate metadata object %s", self->metadata_name));
return;
}
/* monitor changes in metadata */
g_signal_connect_object (m, "changed", G_CALLBACK (on_metadata_changed), self,
0);
wp_object_update_features (WP_OBJECT (self), WP_PLUGIN_FEATURE_ENABLED, 0);
}
static void
wp_collection_manager_plugin_enable (WpPlugin * plugin,
WpTransition * transition)
{
WpCollectionManagerPlugin * self = WP_COLLECTION_MANAGER_PLUGIN (plugin);
g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (plugin));
/* Create the collection metadatas table */
self->collection_metadatas = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_object_unref);
/* Create the collection manager metadata */
self->impl_metadata = wp_impl_metadata_new_full (core, self->metadata_name,
wp_properties_new ("wireplumber.collection-manager", "true", NULL));
wp_object_activate (WP_OBJECT (self->impl_metadata), WP_OBJECT_FEATURES_ALL,
NULL,
(GAsyncReadyCallback)on_metadata_activated, transition);
}
static void
wp_collection_manager_plugin_disable (WpPlugin * plugin)
{
WpCollectionManagerPlugin * self = WP_COLLECTION_MANAGER_PLUGIN (plugin);
g_clear_object (&self->impl_metadata);
g_clear_pointer (&self->collection_metadatas, g_hash_table_unref);
g_clear_pointer (&self->metadata_name, g_free);
}
static void
wp_collection_manager_plugin_set_property (GObject * object, guint property_id,
const GValue * value, GParamSpec * pspec)
{
WpCollectionManagerPlugin *self = WP_COLLECTION_MANAGER_PLUGIN (object);
switch (property_id) {
case PROP_METADATA_NAME:
g_clear_pointer (&self->metadata_name, g_free);
self->metadata_name = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_collection_manager_plugin_get_property (GObject * object, guint property_id,
GValue * value, GParamSpec * pspec)
{
WpCollectionManagerPlugin *self = WP_COLLECTION_MANAGER_PLUGIN (object);
switch (property_id) {
case PROP_METADATA_NAME:
g_value_set_string (value, self->metadata_name);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
wp_collection_manager_plugin_class_init (WpCollectionManagerPluginClass * klass)
{
WpPluginClass *plugin_class = (WpPluginClass *) klass;
GObjectClass *object_class = (GObjectClass *) klass;
plugin_class->enable = wp_collection_manager_plugin_enable;
plugin_class->disable = wp_collection_manager_plugin_disable;
object_class->set_property = wp_collection_manager_plugin_set_property;
object_class->get_property = wp_collection_manager_plugin_get_property;
g_object_class_install_property (object_class, PROP_METADATA_NAME,
g_param_spec_string ("metadata-name", "metadata-name",
"The metadata object to look after", NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
}
WP_PLUGIN_EXPORT GObject *
wireplumber__module_init (WpCore * core, WpSpaJson * args, GError ** error)
{
g_autofree gchar *metadata_name = NULL;
if (args)
wp_spa_json_object_get (args, "metadata.name", "s", &metadata_name, NULL);
return G_OBJECT (g_object_new (wp_collection_manager_plugin_get_type (),
"name", "collection-manager",
"core", core,
"metadata-name", metadata_name ? metadata_name : "sm-collection-manager",
NULL));
}

View file

@ -68,6 +68,7 @@ wireplumber.profiles = {
inherits = [ base ]
metadata.sm-settings = required
metadata.sm-collection-manager = required
metadata.sm-objects = required
policy.standard = required
@ -101,6 +102,7 @@ wireplumber.profiles = {
policy = {
inherits = [ base ]
metadata.sm-settings = required
metadata.sm-collection-manager = required
metadata.sm-objects = required
policy.standard = required
}
@ -128,6 +130,7 @@ wireplumber.profiles = {
base = {
check.no-media-session = required
support.settings = required
support.collection-manager = required
support.log-settings = required
support.session-services = required
}
@ -233,6 +236,13 @@ wireplumber.components = [
provides = metadata.sm-settings
}
## Provides the "sm-collection-manager" metadata object
{
name = libwireplumber-module-collection-manager, type = module
arguments = { metadata.name = sm-collection-manager }
provides = metadata.sm-collection-manager
}
## Activates a global WpSettings instance, providing settings from
## the sm-settings metadata object. Note that this blocks and waits for the
## sm-settings metadata object to become available, so one instance must
@ -244,6 +254,18 @@ wireplumber.components = [
after = [ metadata.sm-settings ]
}
## Activates a global WpCollectionManager instance, allowing creating
## collections for global objects. Note that this blocks and waits for the
## sm-collection-manager metadata object to become available, so one instance
## must provide that, while others should only load this to access the
## collection manager.
{
name = collection-manager-instance, type = built-in
arguments = { metadata.name = sm-collection-manager }
provides = support.collection-manager
after = [ metadata.sm-collection-manager ]
}
## Log level settings
{
name = libwireplumber-module-log-settings, type = module