clapper: Add ability to enable/disable creation of given enhancer

Allow apps to enable or disable given enhancer instances from being created.
Also as a safely measure, by default only enable enhancers that work
on-demand (extractables) and disable others (reactables).
This commit is contained in:
Rafał Dzięgiel
2025-06-18 13:03:26 +02:00
parent 7b4a19659b
commit 9f1102bafd
7 changed files with 150 additions and 42 deletions

View File

@@ -86,6 +86,8 @@ struct _ClapperEnhancerProxy
ClapperEnhancerParamFlags scope; ClapperEnhancerParamFlags scope;
GstStructure *local_config; GstStructure *local_config;
gboolean allowed;
/* GSettings are not thread-safe, /* GSettings are not thread-safe,
* so store schema instead */ * so store schema instead */
GSettingsSchema *schema; GSettingsSchema *schema;
@@ -100,6 +102,7 @@ enum
PROP_MODULE_DIR, PROP_MODULE_DIR,
PROP_DESCRIPTION, PROP_DESCRIPTION,
PROP_VERSION, PROP_VERSION,
PROP_TARGET_CREATION_ALLOWED,
PROP_LAST PROP_LAST
}; };
@@ -229,6 +232,8 @@ clapper_enhancer_proxy_copy (ClapperEnhancerProxy *src_proxy, const gchar *copy_
if (src_proxy->local_config) if (src_proxy->local_config)
copy->local_config = gst_structure_copy (src_proxy->local_config); copy->local_config = gst_structure_copy (src_proxy->local_config);
copy->allowed = src_proxy->allowed;
GST_OBJECT_UNLOCK (src_proxy); GST_OBJECT_UNLOCK (src_proxy);
gst_object_ref_sink (copy); gst_object_ref_sink (copy);
@@ -359,6 +364,8 @@ clapper_enhancer_proxy_fill_from_cache (ClapperEnhancerProxy *self)
if (G_UNLIKELY ((self->ifaces[i] = clapper_cache_read_iface (&data)) == 0)) if (G_UNLIKELY ((self->ifaces[i] = clapper_cache_read_iface (&data)) == 0))
goto abort_reading; goto abort_reading;
} }
/* Reactable type is always last */
self->allowed = (self->ifaces[self->n_ifaces - 1] != CLAPPER_TYPE_REACTABLE);
} }
/* Restore ParamSpecs */ /* Restore ParamSpecs */
@@ -456,6 +463,7 @@ clapper_enhancer_proxy_export_to_cache (ClapperEnhancerProxy *self)
gboolean gboolean
clapper_enhancer_proxy_fill_from_instance (ClapperEnhancerProxy *self, GObject *enhancer) clapper_enhancer_proxy_fill_from_instance (ClapperEnhancerProxy *self, GObject *enhancer)
{ {
/* NOTE: REACTABLE must be last for "allowed" to work as expected */
const GType enhancer_types[] = { CLAPPER_TYPE_EXTRACTABLE, CLAPPER_TYPE_REACTABLE }; const GType enhancer_types[] = { CLAPPER_TYPE_EXTRACTABLE, CLAPPER_TYPE_REACTABLE };
GType *ifaces; GType *ifaces;
GParamSpec **pspecs; GParamSpec **pspecs;
@@ -465,9 +473,12 @@ clapper_enhancer_proxy_fill_from_instance (ClapperEnhancerProxy *self, GObject *
/* Filter to only Clapper interfaces */ /* Filter to only Clapper interfaces */
ifaces = g_type_interfaces (G_OBJECT_TYPE (enhancer), &n); ifaces = g_type_interfaces (G_OBJECT_TYPE (enhancer), &n);
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
for (j = 0; j < G_N_ELEMENTS (enhancer_types); ++j) { const guint n_types = G_N_ELEMENTS (enhancer_types);
for (j = 0; j < n_types; ++j) {
if (ifaces[i] == enhancer_types[j]) { if (ifaces[i] == enhancer_types[j]) {
ifaces[write_index++] = ifaces[i]; ifaces[write_index++] = ifaces[i];
self->allowed = (j < n_types - 1);
break; // match found, do next iface break; // match found, do next iface
} }
} }
@@ -1088,6 +1099,58 @@ clapper_enhancer_proxy_set_locally_with_table (ClapperEnhancerProxy *self, GHash
gst_structure_free (structure); gst_structure_free (structure);
} }
/**
* clapper_enhancer_proxy_set_target_creation_allowed:
* @proxy: a #ClapperEnhancerProxy
* @allowed: whether allowed
*
* Set whether to allow instances of proxy target to be created.
*
* See [property@Clapper.EnhancerProxy:target-creation-allowed] for
* detailed descripton what this does.
*
* Since: 0.10
*/
void
clapper_enhancer_proxy_set_target_creation_allowed (ClapperEnhancerProxy *self, gboolean allowed)
{
gboolean changed;
g_return_if_fail (CLAPPER_IS_ENHANCER_PROXY (self));
GST_OBJECT_LOCK (self);
if ((changed = self->allowed != allowed))
self->allowed = allowed;
GST_OBJECT_UNLOCK (self);
if (changed)
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_TARGET_CREATION_ALLOWED]);
}
/**
* clapper_enhancer_proxy_get_target_creation_allowed:
* @proxy: a #ClapperEnhancerProxy
*
* Get whether it is allowed to create instances of enhancer that this proxy targets.
*
* Returns: whether target creation is allowed.
*
* Since: 0.10
*/
gboolean
clapper_enhancer_proxy_get_target_creation_allowed (ClapperEnhancerProxy *self)
{
gboolean allowed;
g_return_val_if_fail (CLAPPER_IS_ENHANCER_PROXY (self), FALSE);
GST_OBJECT_LOCK (self);
allowed = self->allowed;
GST_OBJECT_UNLOCK (self);
return allowed;
}
static void static void
clapper_enhancer_proxy_init (ClapperEnhancerProxy *self) clapper_enhancer_proxy_init (ClapperEnhancerProxy *self)
{ {
@@ -1137,6 +1200,25 @@ clapper_enhancer_proxy_get_property (GObject *object, guint prop_id,
case PROP_VERSION: case PROP_VERSION:
g_value_set_string (value, clapper_enhancer_proxy_get_version (self)); g_value_set_string (value, clapper_enhancer_proxy_get_version (self));
break; break;
case PROP_TARGET_CREATION_ALLOWED:
g_value_set_boolean (value, clapper_enhancer_proxy_get_target_creation_allowed (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_enhancer_proxy_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperEnhancerProxy *self = CLAPPER_ENHANCER_PROXY_CAST (object);
switch (prop_id) {
case PROP_TARGET_CREATION_ALLOWED:
clapper_enhancer_proxy_set_target_creation_allowed (self, g_value_get_boolean (value));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@@ -1152,6 +1234,7 @@ clapper_enhancer_proxy_class_init (ClapperEnhancerProxyClass *klass)
"Clapper Enhancer Proxy"); "Clapper Enhancer Proxy");
gobject_class->get_property = clapper_enhancer_proxy_get_property; gobject_class->get_property = clapper_enhancer_proxy_get_property;
gobject_class->set_property = clapper_enhancer_proxy_set_property;
gobject_class->finalize = clapper_enhancer_proxy_finalize; gobject_class->finalize = clapper_enhancer_proxy_finalize;
/** /**
@@ -1209,5 +1292,29 @@ clapper_enhancer_proxy_class_init (ClapperEnhancerProxyClass *klass)
NULL, NULL, NULL, NULL, NULL, NULL,
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperEnhancerProxy:target-creation-allowed:
*
* Whether to allow instances of proxy target to be created.
*
* This effectively means whether the given enhancer can be used.
*
* By default all enhancers that work on-demand such as [iface@Clapper.Extractable]
* are allowed while enhancers implementing [iface@Clapper.Reactable] are not.
*
* Value of this property from a `GLOBAL` [class@Clapper.EnhancerProxyList] will carry
* over to all newly created [class@Clapper.Player] objects, while altering this on
* `LOCAL` proxy list will only influence given player instance that list belongs to.
*
* Changing this property will not remove already created enhancer instances, thus
* it is usually best practice to allow/disallow creation of given enhancer plugin
* right after [class@Clapper.Player] is created (before it or its queue are used).
*
* Since: 0.10
*/
param_specs[PROP_TARGET_CREATION_ALLOWED] = g_param_spec_boolean ("target-creation-allowed",
NULL, NULL, FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs); g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
} }

View File

@@ -76,4 +76,10 @@ void clapper_enhancer_proxy_set_locally (ClapperEnhancerProxy *proxy, const gcha
CLAPPER_API CLAPPER_API
void clapper_enhancer_proxy_set_locally_with_table (ClapperEnhancerProxy *proxy, GHashTable *table); void clapper_enhancer_proxy_set_locally_with_table (ClapperEnhancerProxy *proxy, GHashTable *table);
CLAPPER_API
void clapper_enhancer_proxy_set_target_creation_allowed (ClapperEnhancerProxy *proxy, gboolean allowed);
CLAPPER_API
gboolean clapper_enhancer_proxy_get_target_creation_allowed (ClapperEnhancerProxy *proxy);
G_END_DECLS G_END_DECLS

View File

@@ -54,6 +54,28 @@ _import_enhancers (const gchar *enhancers_path)
g_strfreev (dir_paths); g_strfreev (dir_paths);
} }
static GObject *
_force_create_enhancer (ClapperEnhancerProxy *proxy, GType iface_type)
{
GObject *enhancer = NULL;
PeasPluginInfo *info = (PeasPluginInfo *) clapper_enhancer_proxy_get_peas_info (proxy);
g_mutex_lock (&load_lock);
if (!peas_plugin_info_is_loaded (info) && !peas_engine_load_plugin (_engine, info)) {
GST_ERROR ("Could not load enhancer: %s", peas_plugin_info_get_module_name (info));
} else if (!peas_engine_provides_extension (_engine, info, iface_type)) {
GST_LOG ("No \"%s\" enhancer in module: %s", g_type_name (iface_type),
peas_plugin_info_get_module_name (info));
} else {
enhancer = peas_engine_create_extension (_engine, info, iface_type, NULL);
}
g_mutex_unlock (&load_lock);
return enhancer;
}
/* /*
* clapper_enhancers_loader_initialize: * clapper_enhancers_loader_initialize:
* *
@@ -155,7 +177,7 @@ clapper_enhancers_loader_initialize (ClapperEnhancerProxyList *proxies)
/* We cannot ask libpeas for "any" of our main interfaces, so try each one until found */ /* We cannot ask libpeas for "any" of our main interfaces, so try each one until found */
for (j = 0; j < G_N_ELEMENTS (main_types); ++j) { for (j = 0; j < G_N_ELEMENTS (main_types); ++j) {
if ((enhancer = clapper_enhancers_loader_create_enhancer (proxy, main_types[j]))) { if ((enhancer = _force_create_enhancer (proxy, main_types[j]))) {
filled = clapper_enhancer_proxy_fill_from_instance (proxy, enhancer); filled = clapper_enhancer_proxy_fill_from_instance (proxy, enhancer);
g_object_unref (enhancer); g_object_unref (enhancer);
@@ -200,21 +222,8 @@ clapper_enhancers_loader_initialize (ClapperEnhancerProxyList *proxies)
GObject * GObject *
clapper_enhancers_loader_create_enhancer (ClapperEnhancerProxy *proxy, GType iface_type) clapper_enhancers_loader_create_enhancer (ClapperEnhancerProxy *proxy, GType iface_type)
{ {
GObject *enhancer = NULL; if (!clapper_enhancer_proxy_get_target_creation_allowed (proxy))
PeasPluginInfo *info = (PeasPluginInfo *) clapper_enhancer_proxy_get_peas_info (proxy); return NULL;
g_mutex_lock (&load_lock); return _force_create_enhancer (proxy, iface_type);
if (!peas_plugin_info_is_loaded (info) && !peas_engine_load_plugin (_engine, info)) {
GST_ERROR ("Could not load enhancer: %s", peas_plugin_info_get_module_name (info));
} else if (!peas_engine_provides_extension (_engine, info, iface_type)) {
GST_LOG ("No \"%s\" enhancer in module: %s", g_type_name (iface_type),
peas_plugin_info_get_module_name (info));
} else {
enhancer = peas_engine_create_extension (_engine, info, iface_type, NULL);
}
g_mutex_unlock (&load_lock);
return enhancer;
} }

View File

@@ -2339,8 +2339,6 @@ clapper_player_init (ClapperPlayer *self)
if (clapper_enhancer_proxy_list_has_proxy_with_interface (self->enhancer_proxies, CLAPPER_TYPE_REACTABLE)) { if (clapper_enhancer_proxy_list_has_proxy_with_interface (self->enhancer_proxies, CLAPPER_TYPE_REACTABLE)) {
self->reactables_manager = clapper_reactables_manager_new (); self->reactables_manager = clapper_reactables_manager_new ();
gst_object_set_parent (GST_OBJECT_CAST (self->reactables_manager), GST_OBJECT_CAST (self)); gst_object_set_parent (GST_OBJECT_CAST (self->reactables_manager), GST_OBJECT_CAST (self));
clapper_reactables_manager_trigger_prepare (self->reactables_manager);
} }
self->position_query = gst_query_new_position (GST_FORMAT_TIME); self->position_query = gst_query_new_position (GST_FORMAT_TIME);

View File

@@ -36,9 +36,6 @@ void clapper_reactables_manager_initialize (void);
G_GNUC_INTERNAL G_GNUC_INTERNAL
ClapperReactablesManager * clapper_reactables_manager_new (void); ClapperReactablesManager * clapper_reactables_manager_new (void);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_prepare (ClapperReactablesManager *manager);
G_GNUC_INTERNAL G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_configure_take_config (ClapperReactablesManager *manager, ClapperEnhancerProxy *proxy, GstStructure *config); void clapper_reactables_manager_trigger_configure_take_config (ClapperReactablesManager *manager, ClapperEnhancerProxy *proxy, GstStructure *config);

View File

@@ -43,6 +43,8 @@ struct _ClapperReactablesManager
GstBus *bus; GstBus *bus;
GPtrArray *array; GPtrArray *array;
gboolean prepare_called;
}; };
#define parent_class clapper_reactables_manager_parent_class #define parent_class clapper_reactables_manager_parent_class
@@ -74,15 +76,13 @@ enum
enum enum
{ {
CLAPPER_REACTABLES_MANAGER_QUARK_PREPARE = 0, CLAPPER_REACTABLES_MANAGER_QUARK_CONFIGURE = 0,
CLAPPER_REACTABLES_MANAGER_QUARK_CONFIGURE,
CLAPPER_REACTABLES_MANAGER_QUARK_EVENT, CLAPPER_REACTABLES_MANAGER_QUARK_EVENT,
CLAPPER_REACTABLES_MANAGER_QUARK_VALUE, CLAPPER_REACTABLES_MANAGER_QUARK_VALUE,
CLAPPER_REACTABLES_MANAGER_QUARK_EXTRA_VALUE CLAPPER_REACTABLES_MANAGER_QUARK_EXTRA_VALUE
}; };
static ClapperBusQuark _quarks[] = { static ClapperBusQuark _quarks[] = {
{"prepare", 0},
{"configure", 0}, {"configure", 0},
{"event", 0}, {"event", 0},
{"value", 0}, {"value", 0},
@@ -161,7 +161,7 @@ clapper_reactables_manager_handle_prepare (ClapperReactablesManager *self)
clapper_enhancers_loader_create_enhancer (proxy, CLAPPER_TYPE_REACTABLE)); clapper_enhancers_loader_create_enhancer (proxy, CLAPPER_TYPE_REACTABLE));
#endif #endif
if (G_LIKELY (reactable != NULL)) { if (reactable) {
ClapperReactableManagerData *data; ClapperReactableManagerData *data;
GstStructure *config; GstStructure *config;
@@ -216,11 +216,9 @@ clapper_reactables_manager_handle_configure (ClapperReactablesManager *self, con
if (data->proxy == proxy) { if (data->proxy == proxy) {
clapper_enhancer_proxy_apply_config_to_enhancer (data->proxy, clapper_enhancer_proxy_apply_config_to_enhancer (data->proxy,
config, (GObject *) data->reactable); config, (GObject *) data->reactable);
return; break;
} }
} }
GST_ERROR_OBJECT (self, "Triggered configure, but no matching enhancer proxy found");
} }
static inline void static inline void
@@ -319,9 +317,11 @@ _bus_message_func (GstBus *bus, GstMessage *msg, gpointer user_data G_GNUC_UNUSE
GQuark quark = gst_structure_get_name_id (structure); GQuark quark = gst_structure_get_name_id (structure);
if (quark == _QUARK (EVENT)) { if (quark == _QUARK (EVENT)) {
if (G_UNLIKELY (!self->prepare_called)) {
clapper_reactables_manager_handle_prepare (self);
self->prepare_called = TRUE;
}
clapper_reactables_manager_handle_event (self, structure); clapper_reactables_manager_handle_event (self, structure);
} else if (quark == _QUARK (PREPARE)) {
clapper_reactables_manager_handle_prepare (self);
} else if (quark == _QUARK (CONFIGURE)) { } else if (quark == _QUARK (CONFIGURE)) {
clapper_reactables_manager_handle_configure (self, structure); clapper_reactables_manager_handle_configure (self, structure);
} else { } else {
@@ -365,15 +365,6 @@ clapper_reactables_manager_new (void)
return reactables_manager; return reactables_manager;
} }
void
clapper_reactables_manager_trigger_prepare (ClapperReactablesManager *self)
{
GstStructure *structure = gst_structure_new_id_empty (_QUARK (PREPARE));
gst_bus_post (self->bus, gst_message_new_application (
GST_OBJECT_CAST (self), structure));
}
void void
clapper_reactables_manager_trigger_configure_take_config (ClapperReactablesManager *self, clapper_reactables_manager_trigger_configure_take_config (ClapperReactablesManager *self,
ClapperEnhancerProxy *proxy, GstStructure *config) ClapperEnhancerProxy *proxy, GstStructure *config)

View File

@@ -93,7 +93,7 @@ clapper_enhancer_director_extract_in_thread (ClapperEnhancerDirectorData *data)
clapper_enhancers_loader_create_enhancer (proxy, CLAPPER_TYPE_EXTRACTABLE)); clapper_enhancers_loader_create_enhancer (proxy, CLAPPER_TYPE_EXTRACTABLE));
#endif #endif
if (G_LIKELY (extractable != NULL)) { if (extractable) {
if (config) if (config)
clapper_enhancer_proxy_apply_config_to_enhancer (proxy, config, (GObject *) extractable); clapper_enhancer_proxy_apply_config_to_enhancer (proxy, config, (GObject *) extractable);