clapper: Implement reactables manager

An object for managing instances of reactable type of enhancers.

Based on/similar to features manager which along with Clapper
features objects gets deprecated in favour of reactables.
This commit is contained in:
Rafał Dzięgiel
2025-06-05 20:16:43 +02:00
parent 976bcc338f
commit a6ca0b726c
20 changed files with 868 additions and 87 deletions

View File

@@ -29,6 +29,7 @@
#include "clapper-app-bus-private.h"
#include "clapper-features-bus-private.h"
#include "clapper-enhancer-proxy-list-private.h"
#include "clapper-reactables-manager-private.h"
#include "gst/clapper-plugin-private.h"
#include "clapper-functionalities-availability.h"
@@ -56,6 +57,7 @@ clapper_init_check_internal (int *argc, char **argv[])
clapper_playbin_bus_initialize ();
clapper_app_bus_initialize ();
clapper_features_bus_initialize ();
clapper_reactables_manager_initialize ();
_proxies = clapper_enhancer_proxy_list_new_named ("global-proxy-list");

View File

@@ -20,12 +20,14 @@
#include "clapper-cache-private.h"
#include "clapper-version.h"
#include "clapper-extractable.h"
#include "clapper-reactable.h"
#define CLAPPER_CACHE_HEADER "CLAPPER"
typedef enum
{
CLAPPER_CACHE_IFACE_EXTRACTABLE = 1,
CLAPPER_CACHE_IFACE_REACTABLE,
} ClapperCacheIfaces;
static GArray *enum_registry = NULL;
@@ -243,6 +245,8 @@ clapper_cache_read_iface (const gchar **data)
switch (iface_id) {
case CLAPPER_CACHE_IFACE_EXTRACTABLE:
return CLAPPER_TYPE_EXTRACTABLE;
case CLAPPER_CACHE_IFACE_REACTABLE:
return CLAPPER_TYPE_REACTABLE;
default:
return 0;
}
@@ -433,6 +437,8 @@ clapper_cache_store_iface (GByteArray *bytes, GType iface)
if (iface == CLAPPER_TYPE_EXTRACTABLE)
iface_id = CLAPPER_CACHE_IFACE_EXTRACTABLE;
else if (iface == CLAPPER_TYPE_REACTABLE)
iface_id = CLAPPER_CACHE_IFACE_REACTABLE;
else
return FALSE;

View File

@@ -20,6 +20,7 @@
#pragma once
#include <glib.h>
#include <glib-object.h>
#include "clapper-enhancer-proxy-list.h"
#include "clapper-enhancer-proxy.h"
@@ -38,4 +39,7 @@ void clapper_enhancer_proxy_list_fill_from_global_proxies (ClapperEnhancerProxyL
G_GNUC_INTERNAL
void clapper_enhancer_proxy_list_sort (ClapperEnhancerProxyList *list);
G_GNUC_INTERNAL
gboolean clapper_enhancer_proxy_list_has_proxy_with_interface (ClapperEnhancerProxyList *list, GType iface_type);
G_END_DECLS

View File

@@ -161,6 +161,29 @@ clapper_enhancer_proxy_list_sort (ClapperEnhancerProxyList *self)
g_ptr_array_sort_values (self->proxies, (GCompareFunc) _sort_values_by_name);
}
/*
* clapper_enhancer_proxy_list_has_proxy_with_interface:
* @iface_type: an interface #GType
*
* Check if any enhancer implementing given interface type is available.
*
* Returns: whether any enhancer proxy was found.
*/
gboolean
clapper_enhancer_proxy_list_has_proxy_with_interface (ClapperEnhancerProxyList *self, GType iface_type)
{
guint i;
for (i = 0; i < self->proxies->len; ++i) {
ClapperEnhancerProxy *proxy = clapper_enhancer_proxy_list_peek_proxy (self, i);
if (clapper_enhancer_proxy_target_has_interface (proxy, iface_type))
return TRUE;
}
return FALSE;
}
/**
* clapper_enhancer_proxy_list_get_proxy:
* @list: a #ClapperEnhancerProxyList

View File

@@ -45,6 +45,9 @@ void clapper_enhancer_proxy_export_to_cache (ClapperEnhancerProxy *proxy);
G_GNUC_INTERNAL
GObject * clapper_enhancer_proxy_get_peas_info (ClapperEnhancerProxy *proxy);
G_GNUC_INTERNAL
gboolean clapper_enhancer_proxy_has_locally_set (ClapperEnhancerProxy *proxy, const gchar *property_name);
G_GNUC_INTERNAL
GstStructure * clapper_enhancer_proxy_make_current_config (ClapperEnhancerProxy *proxy);

View File

@@ -48,6 +48,9 @@
#include "clapper-basic-functions.h"
#include "clapper-cache-private.h"
#include "clapper-extractable.h"
#include "clapper-reactable.h"
#include "clapper-player-private.h"
#include "clapper-utils-private.h"
#include "clapper-enums.h"
#include "clapper-functionalities-availability.h"
@@ -454,7 +457,7 @@ clapper_enhancer_proxy_export_to_cache (ClapperEnhancerProxy *self)
gboolean
clapper_enhancer_proxy_fill_from_instance (ClapperEnhancerProxy *self, GObject *enhancer)
{
GType enhancer_types[1] = { CLAPPER_TYPE_EXTRACTABLE };
const GType enhancer_types[] = { CLAPPER_TYPE_EXTRACTABLE, CLAPPER_TYPE_REACTABLE };
GType *ifaces;
GParamSpec **pspecs;
GParamFlags enhancer_flags;
@@ -500,6 +503,18 @@ clapper_enhancer_proxy_get_peas_info (ClapperEnhancerProxy *self)
return self->peas_info;
}
gboolean
clapper_enhancer_proxy_has_locally_set (ClapperEnhancerProxy *self, const gchar *property_name)
{
gboolean has_field;
GST_OBJECT_LOCK (self);
has_field = (self->local_config && gst_structure_has_field (self->local_config, property_name));
GST_OBJECT_UNLOCK (self);
return has_field;
}
static gboolean
_apply_config_cb (GQuark field_id, const GValue *value, GObject *enhancer)
{
@@ -527,6 +542,7 @@ clapper_enhancer_proxy_make_current_config (ClapperEnhancerProxy *self)
/* Using "has_field", as set value might be %NULL */
if ((pspec->flags & CLAPPER_ENHANCER_PARAM_LOCAL)
&& self->local_config
&& gst_structure_has_field (self->local_config, pspec->name)) {
if (!merged_config)
merged_config = gst_structure_new_empty (CONFIG_STRUCTURE_NAME);
@@ -540,44 +556,13 @@ clapper_enhancer_proxy_make_current_config (ClapperEnhancerProxy *self)
GVariant *def = g_settings_get_default_value (settings, pspec->name);
if (!g_variant_equal (val, def)) {
if (!merged_config)
merged_config = gst_structure_new_empty (CONFIG_STRUCTURE_NAME);
GValue value = G_VALUE_INIT;
switch (pspec->value_type) {
case G_TYPE_BOOLEAN:
gst_structure_set (merged_config, pspec->name,
pspec->value_type, g_variant_get_boolean (val), NULL);
break;
case G_TYPE_INT:
gst_structure_set (merged_config, pspec->name,
pspec->value_type, g_variant_get_int32 (val), NULL);
break;
case G_TYPE_UINT:
gst_structure_set (merged_config, pspec->name,
pspec->value_type, g_variant_get_uint32 (val), NULL);
break;
case G_TYPE_DOUBLE:
gst_structure_set (merged_config, pspec->name,
pspec->value_type, g_variant_get_double (val), NULL);
break;
case G_TYPE_STRING:
gst_structure_set (merged_config, pspec->name,
pspec->value_type, g_variant_get_string (val, NULL), NULL);
break;
default:{
if (G_IS_PARAM_SPEC_ENUM (pspec)) {
gst_structure_set (merged_config, pspec->name,
G_TYPE_INT, g_variant_get_int32 (val), NULL);
break;
} else if (G_IS_PARAM_SPEC_FLAGS (pspec)) {
gst_structure_set (merged_config, pspec->name,
G_TYPE_UINT, g_variant_get_uint32 (val), NULL);
break;
}
GST_ERROR_OBJECT (self, "Unsupported enhancer \"%s\" setting type: %s",
pspec->name, g_type_name (pspec->value_type));
break;
}
if (G_LIKELY (clapper_utils_set_value_from_variant (&value, val))) {
if (!merged_config)
merged_config = gst_structure_new_empty (CONFIG_STRUCTURE_NAME);
gst_structure_take_value (merged_config, pspec->name, &value);
}
}
@@ -947,6 +932,20 @@ _structure_take_value_by_pspec (ClapperEnhancerProxy *self,
return FALSE;
}
static void
_trigger_reactable_configure_take (ClapperEnhancerProxy *self, GstStructure *structure)
{
ClapperPlayer *player = clapper_player_get_from_ancestor (GST_OBJECT_CAST (self));
if (G_LIKELY (player != NULL)) {
clapper_reactables_manager_trigger_configure_take_config (
player->reactables_manager, self, structure);
gst_object_unref (player);
} else {
gst_structure_free (structure);
}
}
/**
* clapper_enhancer_proxy_set_locally:
* @proxy: a #ClapperEnhancerProxy
@@ -1017,10 +1016,10 @@ clapper_enhancer_proxy_set_locally (ClapperEnhancerProxy *self, const gchar *fir
_update_local_config_from_structure (self, structure);
/* TODO: _post_local_config instead of free if managed
* (for when managed interfaces are implemented) */
gst_structure_free (structure);
if (clapper_enhancer_proxy_target_has_interface (self, CLAPPER_TYPE_REACTABLE))
_trigger_reactable_configure_take (self, structure);
else
gst_structure_free (structure);
}
/**
@@ -1084,10 +1083,10 @@ clapper_enhancer_proxy_set_locally_with_table (ClapperEnhancerProxy *self, GHash
_update_local_config_from_structure (self, structure);
/* TODO: _post_local_config instead of free if managed
* (for when managed interfaces are implemented) */
gst_structure_free (structure);
if (clapper_enhancer_proxy_target_has_interface (self, CLAPPER_TYPE_REACTABLE))
_trigger_reactable_configure_take (self, structure);
else
gst_structure_free (structure);
}
static void

View File

@@ -33,6 +33,9 @@ static HMODULE _enhancers_dll_handle = NULL;
// Supported interfaces
#include "clapper-extractable.h"
#include "clapper-reactable.h"
#include <clapper-functionalities-availability.h>
#define GST_CAT_DEFAULT clapper_enhancers_loader_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@@ -110,6 +113,36 @@ clapper_enhancers_loader_initialize (ClapperEnhancerProxyList *proxies)
ClapperEnhancerProxy *proxy;
gboolean filled;
/* FIXME: 1.0: Remove together with features code and manager.
* These would clash with each other, so avoid loading these
* as enhancers when also compiled as part of the library. */
#if (CLAPPER_HAVE_MPRIS || CLAPPER_HAVE_DISCOVERER || CLAPPER_HAVE_SERVER)
guint f_index;
const gchar *module_name = peas_plugin_info_get_module_name (info);
const gchar *ported_features[] = {
#if CLAPPER_HAVE_MPRIS
"clapper-mpris",
#endif
#if CLAPPER_HAVE_DISCOVERER
"clapper-discoverer",
#endif
#if CLAPPER_HAVE_SERVER
"clapper-server",
#endif
};
for (f_index = 0; f_index < G_N_ELEMENTS (ported_features); ++f_index) {
if (strcmp (module_name, ported_features[f_index]) == 0) {
GST_INFO ("Skipped \"%s\" enhancer module, since its"
" loaded from deprecated feature object", module_name);
g_clear_object (&info);
}
}
if (!info) // cleared when exists as feature
continue;
#endif
/* Clapper supports only 1 proxy per plugin. Each plugin can
* ship 1 class, but it can implement more than 1 interface. */
proxy = clapper_enhancer_proxy_new_global_take ((GObject *) info);
@@ -118,7 +151,7 @@ clapper_enhancers_loader_initialize (ClapperEnhancerProxyList *proxies)
* Otherwise make an instance and fill missing data from it (slow). */
if (!(filled = clapper_enhancer_proxy_fill_from_cache (proxy))) {
GObject *enhancer;
GType main_types[1] = { CLAPPER_TYPE_EXTRACTABLE };
const GType main_types[] = { CLAPPER_TYPE_EXTRACTABLE, CLAPPER_TYPE_REACTABLE };
guint j;
/* We cannot ask libpeas for "any" of our main interfaces, so try each one until found */

View File

@@ -23,7 +23,7 @@
#include <gst/pbutils/pbutils.h>
#include "clapper-media-item.h"
#include "clapper-player-private.h"
#include "clapper-player.h"
#include "clapper-app-bus-private.h"
G_BEGIN_DECLS

View File

@@ -30,6 +30,7 @@
#include "clapper-timeline-private.h"
#include "clapper-player-private.h"
#include "clapper-playbin-bus-private.h"
#include "clapper-reactables-manager-private.h"
#include "clapper-features-manager-private.h"
#include "clapper-utils-private.h"
@@ -565,6 +566,8 @@ clapper_media_item_populate_tags (ClapperMediaItem *self, const GstTagList *tags
if (changed && player) {
ClapperFeaturesManager *features_manager;
if (player->reactables_manager)
clapper_reactables_manager_trigger_item_updated (player->reactables_manager, self);
if ((features_manager = clapper_player_get_features_manager (player)))
clapper_features_manager_trigger_item_updated (features_manager, self);
}
@@ -602,6 +605,8 @@ clapper_media_item_update_from_tag_list (ClapperMediaItem *self, const GstTagLis
if (changed) {
ClapperFeaturesManager *features_manager;
if (player->reactables_manager)
clapper_reactables_manager_trigger_item_updated (player->reactables_manager, self);
if ((features_manager = clapper_player_get_features_manager (player)))
clapper_features_manager_trigger_item_updated (features_manager, self);
}
@@ -645,6 +650,8 @@ clapper_media_item_update_from_discoverer_info (ClapperMediaItem *self, GstDisco
if (changed) {
ClapperFeaturesManager *features_manager;
if (player->reactables_manager)
clapper_reactables_manager_trigger_item_updated (player->reactables_manager, self);
if ((features_manager = clapper_player_get_features_manager (player)))
clapper_features_manager_trigger_item_updated (features_manager, self);
}

View File

@@ -173,6 +173,8 @@ _update_current_duration (ClapperPlayer *player)
if (clapper_media_item_set_duration (player->played_item, duration_dbl, player->app_bus)) {
ClapperFeaturesManager *features_manager;
if (player->reactables_manager)
clapper_reactables_manager_trigger_item_updated (player->reactables_manager, player->played_item);
if ((features_manager = clapper_player_get_features_manager (player)))
clapper_features_manager_trigger_item_updated (features_manager, player->played_item);
}
@@ -1046,6 +1048,8 @@ _handle_stream_start_msg (GstMessage *msg, ClapperPlayer *player)
if (G_LIKELY (changed)) {
clapper_queue_handle_played_item_changed (player->queue, player->played_item, player->app_bus);
if (player->reactables_manager)
clapper_reactables_manager_trigger_played_item_changed (player->reactables_manager, player->played_item);
if (clapper_player_get_have_features (player))
clapper_features_manager_trigger_played_item_changed (player->features_manager, player->played_item);
}

View File

@@ -27,6 +27,7 @@
#include "clapper-app-bus-private.h"
#include "clapper-features-manager-private.h"
#include "clapper-reactables-manager-private.h"
G_BEGIN_DECLS
@@ -47,6 +48,8 @@ struct _ClapperPlayer
ClapperFeaturesManager *features_manager;
gint have_features; // atomic integer
ClapperReactablesManager *reactables_manager;
ClapperEnhancerProxyList *enhancer_proxies;
/* This is different from queue current item as it is used/changed only

View File

@@ -50,6 +50,7 @@
#include "clapper-audio-stream-private.h"
#include "clapper-subtitle-stream-private.h"
#include "clapper-enhancer-proxy-list-private.h"
#include "clapper-reactable.h"
#include "clapper-enums-private.h"
#include "clapper-utils-private.h"
#include "../shared/clapper-shared-utils-private.h"
@@ -159,6 +160,8 @@ clapper_player_refresh_position (ClapperPlayer *self)
clapper_app_bus_post_prop_notify (self->app_bus,
GST_OBJECT_CAST (self), param_specs[PROP_POSITION]);
if (self->reactables_manager)
clapper_reactables_manager_trigger_position_changed (self->reactables_manager, position_dbl);
if (clapper_player_get_have_features (self))
clapper_features_manager_trigger_position_changed (self->features_manager, position_dbl);
}
@@ -225,6 +228,8 @@ clapper_player_handle_playbin_state_changed (ClapperPlayer *self)
clapper_app_bus_post_prop_notify (self->app_bus,
GST_OBJECT_CAST (self), param_specs[PROP_STATE]);
if (self->reactables_manager)
clapper_reactables_manager_trigger_state_changed (self->reactables_manager, state);
if (clapper_player_get_have_features (self))
clapper_features_manager_trigger_state_changed (self->features_manager, state);
}
@@ -256,6 +261,8 @@ clapper_player_handle_playbin_volume_changed (ClapperPlayer *self, const GValue
clapper_app_bus_post_prop_notify (self->app_bus,
GST_OBJECT_CAST (self), param_specs[PROP_VOLUME]);
if (self->reactables_manager)
clapper_reactables_manager_trigger_volume_changed (self->reactables_manager, volume);
if (clapper_player_get_have_features (self))
clapper_features_manager_trigger_volume_changed (self->features_manager, volume);
}
@@ -280,6 +287,8 @@ clapper_player_handle_playbin_mute_changed (ClapperPlayer *self, const GValue *v
clapper_app_bus_post_prop_notify (self->app_bus,
GST_OBJECT_CAST (self), param_specs[PROP_MUTE]);
if (self->reactables_manager)
clapper_reactables_manager_trigger_mute_changed (self->reactables_manager, mute);
if (clapper_player_get_have_features (self))
clapper_features_manager_trigger_mute_changed (self->features_manager, mute);
}
@@ -400,6 +409,8 @@ clapper_player_handle_playbin_rate_changed (ClapperPlayer *self, gdouble speed)
clapper_app_bus_post_prop_notify (self->app_bus,
GST_OBJECT_CAST (self), param_specs[PROP_SPEED]);
if (self->reactables_manager)
clapper_reactables_manager_trigger_speed_changed (self->reactables_manager, speed);
if (clapper_player_get_have_features (self))
clapper_features_manager_trigger_speed_changed (self->features_manager, speed);
}
@@ -2326,6 +2337,13 @@ clapper_player_init (ClapperPlayer *self)
clapper_enhancer_proxy_list_fill_from_global_proxies (self->enhancer_proxies);
if (clapper_enhancer_proxy_list_has_proxy_with_interface (self->enhancer_proxies, CLAPPER_TYPE_REACTABLE)) {
self->reactables_manager = clapper_reactables_manager_new ();
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->current_state = GST_STATE_NULL;
@@ -2392,6 +2410,11 @@ clapper_player_finalize (GObject *object)
gst_object_unparent (GST_OBJECT_CAST (self->subtitle_streams));
gst_object_unref (self->subtitle_streams);
if (self->reactables_manager) {
gst_object_unparent (GST_OBJECT_CAST (self->reactables_manager));
gst_object_unref (self->reactables_manager);
}
gst_object_unparent (GST_OBJECT_CAST (self->enhancer_proxies));
gst_object_unref (self->enhancer_proxies);

View File

@@ -29,6 +29,8 @@
#include "clapper-media-item-private.h"
#include "clapper-player-private.h"
#include "clapper-playbin-bus-private.h"
#include "clapper-reactables-manager-private.h"
#include "clapper-features-manager-private.h"
#define CLAPPER_QUEUE_GET_REC_LOCK(obj) (&CLAPPER_QUEUE_CAST(obj)->rec_lock)
#define CLAPPER_QUEUE_REC_LOCK(obj) g_rec_mutex_lock (CLAPPER_QUEUE_GET_REC_LOCK(obj))
@@ -132,15 +134,27 @@ _announce_model_update (ClapperQueue *self, guint index, guint removed, guint ad
if (removed != added) {
ClapperPlayer *player = clapper_player_get_from_ancestor (GST_OBJECT_CAST (self));
if (player && clapper_player_get_have_features (player)) {
if (added == 1) // addition
clapper_features_manager_trigger_queue_item_added (player->features_manager, changed_item, index);
else if (removed == 1) // removal
clapper_features_manager_trigger_queue_item_removed (player->features_manager, changed_item, index);
else if (removed > 1 && added == 0) // queue cleared
clapper_features_manager_trigger_queue_cleared (player->features_manager);
else
if (player) {
gboolean have_features = clapper_player_get_have_features (player);
if (added == 1) { // addition
if (player->reactables_manager)
clapper_reactables_manager_trigger_queue_item_added (player->reactables_manager, changed_item, index);
if (have_features)
clapper_features_manager_trigger_queue_item_added (player->features_manager, changed_item, index);
} else if (removed == 1) { // removal
if (player->reactables_manager)
clapper_reactables_manager_trigger_queue_item_removed (player->reactables_manager, changed_item, index);
if (have_features)
clapper_features_manager_trigger_queue_item_removed (player->features_manager, changed_item, index);
} else if (removed > 1 && added == 0) { // queue cleared
if (player->reactables_manager)
clapper_reactables_manager_trigger_queue_cleared (player->reactables_manager);
if (have_features)
clapper_features_manager_trigger_queue_cleared (player->features_manager);
} else {
g_assert_not_reached ();
}
}
gst_clear_object (&player);
@@ -160,6 +174,8 @@ _announce_reposition (ClapperQueue *self, guint before, guint after)
GST_DEBUG_OBJECT (self, "Announcing item reposition: %u -> %u", before, after);
if ((player = clapper_player_get_from_ancestor (GST_OBJECT_CAST (self)))) {
if (player->reactables_manager)
clapper_reactables_manager_trigger_queue_item_repositioned (player->reactables_manager, before, after);
if (clapper_player_get_have_features (player))
clapper_features_manager_trigger_queue_item_repositioned (player->features_manager, before, after);
@@ -989,6 +1005,8 @@ clapper_queue_set_progression_mode (ClapperQueue *self, ClapperQueueProgressionM
clapper_app_bus_post_prop_notify (player->app_bus,
GST_OBJECT_CAST (self), param_specs[PROP_PROGRESSION_MODE]);
if (player->reactables_manager)
clapper_reactables_manager_trigger_queue_progression_changed (player->reactables_manager, mode);
if (clapper_player_get_have_features (player))
clapper_features_manager_trigger_queue_progression_changed (player->features_manager, mode);

View File

@@ -0,0 +1,82 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include "clapper-enums.h"
#include "clapper-threaded-object.h"
#include "clapper-enhancer-proxy.h"
#include "clapper-media-item.h"
G_BEGIN_DECLS
#define CLAPPER_TYPE_REACTABLES_MANAGER (clapper_reactables_manager_get_type())
#define CLAPPER_REACTABLES_MANAGER_CAST(obj) ((ClapperReactablesManager *)(obj))
G_DECLARE_FINAL_TYPE (ClapperReactablesManager, clapper_reactables_manager, CLAPPER, REACTABLES_MANAGER, ClapperThreadedObject)
G_GNUC_INTERNAL
void clapper_reactables_manager_initialize (void);
G_GNUC_INTERNAL
ClapperReactablesManager * clapper_reactables_manager_new (void);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_prepare (ClapperReactablesManager *manager);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_configure_take_config (ClapperReactablesManager *manager, ClapperEnhancerProxy *proxy, GstStructure *config);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_state_changed (ClapperReactablesManager *manager, ClapperPlayerState state);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_position_changed (ClapperReactablesManager *manager, gdouble position);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_speed_changed (ClapperReactablesManager *manager, gdouble speed);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_volume_changed (ClapperReactablesManager *manager, gdouble volume);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_mute_changed (ClapperReactablesManager *manager, gboolean mute);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_played_item_changed (ClapperReactablesManager *manager, ClapperMediaItem *item);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_item_updated (ClapperReactablesManager *manager, ClapperMediaItem *item);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_queue_item_added (ClapperReactablesManager *manager, ClapperMediaItem *item, guint index);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_queue_item_removed (ClapperReactablesManager *manager, ClapperMediaItem *item, guint index);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_queue_item_repositioned (ClapperReactablesManager *manager, guint before, guint after);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_queue_cleared (ClapperReactablesManager *manager);
G_GNUC_INTERNAL
void clapper_reactables_manager_trigger_queue_progression_changed (ClapperReactablesManager *manager, ClapperQueueProgressionMode mode);
G_END_DECLS

View File

@@ -0,0 +1,533 @@
/* Clapper Playback Library
* Copyright (C) 2025 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <gst/gst.h>
#include "clapper-reactables-manager-private.h"
#include "clapper-reactable.h"
#include "clapper-bus-private.h"
#include "clapper-player.h"
#include "clapper-enhancer-proxy-list.h"
#include "clapper-enhancer-proxy-private.h"
#include "clapper-utils-private.h"
#include "clapper-functionalities-availability.h"
#if CLAPPER_WITH_ENHANCERS_LOADER
#include "clapper-enhancers-loader-private.h"
#endif
#define CONFIG_STRUCTURE_NAME "config"
#define GST_CAT_DEFAULT clapper_reactables_manager_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperReactablesManager
{
ClapperThreadedObject parent;
GstBus *bus;
GPtrArray *array;
};
#define parent_class clapper_reactables_manager_parent_class
G_DEFINE_TYPE (ClapperReactablesManager, clapper_reactables_manager, CLAPPER_TYPE_THREADED_OBJECT);
typedef struct
{
ClapperReactable *reactable;
ClapperEnhancerProxy *proxy;
GSettings *settings;
} ClapperReactableManagerData;
enum
{
CLAPPER_REACTABLES_MANAGER_EVENT_INVALID = 0,
CLAPPER_REACTABLES_MANAGER_EVENT_STATE_CHANGED,
CLAPPER_REACTABLES_MANAGER_EVENT_POSITION_CHANGED,
CLAPPER_REACTABLES_MANAGER_EVENT_SPEED_CHANGED,
CLAPPER_REACTABLES_MANAGER_EVENT_VOLUME_CHANGED,
CLAPPER_REACTABLES_MANAGER_EVENT_MUTE_CHANGED,
CLAPPER_REACTABLES_MANAGER_EVENT_PLAYED_ITEM_CHANGED,
CLAPPER_REACTABLES_MANAGER_EVENT_ITEM_UPDATED,
CLAPPER_REACTABLES_MANAGER_EVENT_QUEUE_ITEM_ADDED,
CLAPPER_REACTABLES_MANAGER_EVENT_QUEUE_ITEM_REMOVED,
CLAPPER_REACTABLES_MANAGER_EVENT_QUEUE_ITEM_REPOSITIONED,
CLAPPER_REACTABLES_MANAGER_EVENT_QUEUE_CLEARED,
CLAPPER_REACTABLES_MANAGER_EVENT_QUEUE_PROGRESSION_CHANGED
};
enum
{
CLAPPER_REACTABLES_MANAGER_QUARK_PREPARE = 0,
CLAPPER_REACTABLES_MANAGER_QUARK_CONFIGURE,
CLAPPER_REACTABLES_MANAGER_QUARK_EVENT,
CLAPPER_REACTABLES_MANAGER_QUARK_VALUE,
CLAPPER_REACTABLES_MANAGER_QUARK_EXTRA_VALUE
};
static ClapperBusQuark _quarks[] = {
{"prepare", 0},
{"configure", 0},
{"event", 0},
{"value", 0},
{"extra-value", 0},
{NULL, 0}
};
#define _EVENT(e) G_PASTE(CLAPPER_REACTABLES_MANAGER_EVENT_, e)
#define _QUARK(q) (_quarks[CLAPPER_REACTABLES_MANAGER_QUARK_##q].quark)
#define _BUS_POST_EVENT_SINGLE(event_id,lower,type,val) { \
GValue _value = G_VALUE_INIT; \
g_value_init (&_value, type); \
g_value_set_##lower (&_value, val); \
_bus_post_event (self, event_id, &_value, NULL); }
#define _BUS_POST_EVENT_DUAL(event_id,lower1,type1,val1,lower2,type2,val2) { \
GValue _value1 = G_VALUE_INIT; \
GValue _value2 = G_VALUE_INIT; \
g_value_init (&_value1, type1); \
g_value_init (&_value2, type2); \
g_value_set_##lower1 (&_value1, val1); \
g_value_set_##lower2 (&_value2, val2); \
_bus_post_event (self, event_id, &_value1, &_value2); }
void
clapper_reactables_manager_initialize (void)
{
gint i;
for (i = 0; _quarks[i].name; ++i)
_quarks[i].quark = g_quark_from_static_string (_quarks[i].name);
}
static void
_settings_changed_cb (GSettings *settings, const gchar *key, ClapperReactableManagerData *data)
{
GST_DEBUG_OBJECT (data->reactable, "Global setting \"%s\" changed", key);
/* Local settings are applied through bus events, so all that is
* needed here is a check to not overwrite locally set setting */
if (!clapper_enhancer_proxy_has_locally_set (data->proxy, key)) {
GVariant *variant = g_settings_get_value (settings, key);
GValue value = G_VALUE_INIT;
if (G_LIKELY (clapper_utils_set_value_from_variant (&value, variant))) {
g_object_set_property (G_OBJECT (data->reactable), key, &value);
g_value_unset (&value);
}
g_variant_unref (variant);
}
}
static inline void
clapper_reactables_manager_handle_prepare (ClapperReactablesManager *self)
{
ClapperPlayer *player;
GST_INFO_OBJECT (self, "Preparing reactable enhancers");
player = CLAPPER_PLAYER_CAST (gst_object_get_parent (GST_OBJECT_CAST (self)));
if (G_LIKELY (player != NULL)) {
ClapperEnhancerProxyList *proxies = clapper_player_get_enhancer_proxies (player);
guint i, n_proxies = clapper_enhancer_proxy_list_get_n_proxies (proxies);
for (i = 0; i < n_proxies; ++i) {
ClapperEnhancerProxy *proxy = clapper_enhancer_proxy_list_peek_proxy (proxies, i);
ClapperReactable *reactable = NULL;
if (!clapper_enhancer_proxy_target_has_interface (proxy, CLAPPER_TYPE_REACTABLE))
continue;
#if CLAPPER_WITH_ENHANCERS_LOADER
reactable = CLAPPER_REACTABLE_CAST (
clapper_enhancers_loader_create_enhancer (proxy, CLAPPER_TYPE_REACTABLE));
#endif
if (G_LIKELY (reactable != NULL)) {
ClapperReactableManagerData *data;
GstStructure *config;
if (g_object_is_floating (reactable))
gst_object_ref_sink (reactable);
data = g_new (ClapperReactableManagerData, 1);
data->reactable = reactable;
data->proxy = gst_object_ref (proxy);
data->settings = clapper_enhancer_proxy_get_settings (proxy);
GST_TRACE_OBJECT (self, "Created data for reactable: %" GST_PTR_FORMAT, data->reactable);
/* Settings are stored in data in order for this signal to keep working */
if (data->settings)
g_signal_connect (data->settings, "changed", G_CALLBACK (_settings_changed_cb), data);
if ((config = clapper_enhancer_proxy_make_current_config (proxy))) {
clapper_enhancer_proxy_apply_config_to_enhancer (proxy, config, (GObject *) reactable);
gst_structure_free (config);
}
g_ptr_array_add (self->array, data);
gst_object_set_parent (GST_OBJECT_CAST (data->reactable), GST_OBJECT_CAST (player));
}
}
GST_INFO_OBJECT (self, "Prepared %i reactable enhancers", self->array->len);
gst_object_unref (player);
} else {
GST_ERROR_OBJECT (self, "Could not prepare reactable enhancers!");
}
}
static inline void
clapper_reactables_manager_handle_configure (ClapperReactablesManager *self, const GstStructure *structure)
{
const GValue *proxy_val, *config_val;
ClapperEnhancerProxy *proxy;
const GstStructure *config;
guint i;
proxy_val = gst_structure_id_get_value (structure, _QUARK (VALUE));
config_val = gst_structure_id_get_value (structure, _QUARK (EXTRA_VALUE));
proxy = CLAPPER_ENHANCER_PROXY_CAST (g_value_get_object (proxy_val));
config = gst_value_get_structure (config_val);
for (i = 0; i < self->array->len; ++i) {
ClapperReactableManagerData *data = g_ptr_array_index (self->array, i);
if (data->proxy == proxy) {
clapper_enhancer_proxy_apply_config_to_enhancer (data->proxy,
config, (GObject *) data->reactable);
return;
}
}
GST_ERROR_OBJECT (self, "Triggered configure, but no matching enhancer proxy found");
}
static inline void
clapper_reactables_manager_handle_event (ClapperReactablesManager *self, const GstStructure *structure)
{
const GValue *value = gst_structure_id_get_value (structure, _QUARK (VALUE));
const GValue *extra_value = gst_structure_id_get_value (structure, _QUARK (EXTRA_VALUE));
guint i, event_id;
if (G_UNLIKELY (!gst_structure_id_get (structure,
_QUARK (EVENT), G_TYPE_ENUM, &event_id, NULL))) {
GST_ERROR_OBJECT (self, "Could not read event ID");
return;
}
for (i = 0; i < self->array->len; ++i) {
ClapperReactableManagerData *data = g_ptr_array_index (self->array, i);
ClapperReactableInterface *reactable_iface = CLAPPER_REACTABLE_GET_IFACE (data->reactable);
switch (event_id) {
case _EVENT (STATE_CHANGED):
if (reactable_iface->state_changed)
reactable_iface->state_changed (data->reactable, g_value_get_int (value));
break;
case _EVENT (POSITION_CHANGED):
if (reactable_iface->position_changed)
reactable_iface->position_changed (data->reactable, g_value_get_double (value));
break;
case _EVENT (SPEED_CHANGED):
if (reactable_iface->speed_changed)
reactable_iface->speed_changed (data->reactable, g_value_get_double (value));
break;
case _EVENT (VOLUME_CHANGED):
if (reactable_iface->volume_changed)
reactable_iface->volume_changed (data->reactable, g_value_get_double (value));
break;
case _EVENT (MUTE_CHANGED):
if (reactable_iface->mute_changed)
reactable_iface->mute_changed (data->reactable, g_value_get_boolean (value));
break;
case _EVENT (PLAYED_ITEM_CHANGED):
if (reactable_iface->played_item_changed) {
reactable_iface->played_item_changed (data->reactable,
CLAPPER_MEDIA_ITEM_CAST (g_value_get_object (value)));
}
break;
case _EVENT (ITEM_UPDATED):
if (reactable_iface->item_updated) {
reactable_iface->item_updated (data->reactable,
CLAPPER_MEDIA_ITEM_CAST (g_value_get_object (value)));
}
break;
case _EVENT (QUEUE_ITEM_ADDED):
if (reactable_iface->queue_item_added) {
reactable_iface->queue_item_added (data->reactable,
CLAPPER_MEDIA_ITEM_CAST (g_value_get_object (value)),
g_value_get_uint (extra_value));
}
break;
case _EVENT (QUEUE_ITEM_REMOVED):
if (reactable_iface->queue_item_removed) {
reactable_iface->queue_item_removed (data->reactable,
CLAPPER_MEDIA_ITEM_CAST (g_value_get_object (value)),
g_value_get_uint (extra_value));
}
break;
case _EVENT (QUEUE_ITEM_REPOSITIONED):
if (reactable_iface->queue_item_repositioned) {
reactable_iface->queue_item_repositioned (data->reactable,
g_value_get_uint (value),
g_value_get_uint (extra_value));
}
break;
case _EVENT (QUEUE_CLEARED):
if (reactable_iface->queue_cleared)
reactable_iface->queue_cleared (data->reactable);
break;
case _EVENT (QUEUE_PROGRESSION_CHANGED):
if (reactable_iface->queue_progression_changed)
reactable_iface->queue_progression_changed (data->reactable, g_value_get_int (value));
break;
default:
GST_ERROR_OBJECT (self, "Invalid event ID on reactables bus: %u", event_id);
break;
}
}
}
static gboolean
_bus_message_func (GstBus *bus, GstMessage *msg, gpointer user_data G_GNUC_UNUSED)
{
if (G_LIKELY (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_APPLICATION)) {
ClapperReactablesManager *self = CLAPPER_REACTABLES_MANAGER_CAST (GST_MESSAGE_SRC (msg));
const GstStructure *structure = gst_message_get_structure (msg);
GQuark quark = gst_structure_get_name_id (structure);
if (quark == _QUARK (EVENT)) {
clapper_reactables_manager_handle_event (self, structure);
} else if (quark == _QUARK (PREPARE)) {
clapper_reactables_manager_handle_prepare (self);
} else if (quark == _QUARK (CONFIGURE)) {
clapper_reactables_manager_handle_configure (self, structure);
} else {
GST_ERROR_OBJECT (self, "Received invalid quark on reactables bus!");
}
}
return G_SOURCE_CONTINUE;
}
static void
_bus_post_event (ClapperReactablesManager *self, guint event_id,
GValue *value, GValue *extra_value)
{
GstStructure *structure = gst_structure_new_id (_QUARK (EVENT),
_QUARK (EVENT), G_TYPE_ENUM, event_id,
NULL);
if (value)
gst_structure_id_take_value (structure, _QUARK (VALUE), value);
if (extra_value)
gst_structure_id_take_value (structure, _QUARK (EXTRA_VALUE), extra_value);
gst_bus_post (self->bus, gst_message_new_application (
GST_OBJECT_CAST (self), structure));
}
/*
* clapper_reactables_manager_new:
*
* Returns: (transfer full): a new #ClapperReactablesManager instance.
*/
ClapperReactablesManager *
clapper_reactables_manager_new (void)
{
ClapperReactablesManager *reactables_manager;
reactables_manager = g_object_new (CLAPPER_TYPE_REACTABLES_MANAGER, NULL);
gst_object_ref_sink (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
clapper_reactables_manager_trigger_configure_take_config (ClapperReactablesManager *self,
ClapperEnhancerProxy *proxy, GstStructure *config)
{
GstStructure *structure = gst_structure_new_id (_QUARK (CONFIGURE),
_QUARK (VALUE), G_TYPE_OBJECT, proxy, NULL);
GValue extra_value = G_VALUE_INIT;
g_value_init (&extra_value, GST_TYPE_STRUCTURE);
g_value_take_boxed (&extra_value, config);
gst_structure_id_take_value (structure, _QUARK (EXTRA_VALUE), &extra_value);
gst_bus_post (self->bus, gst_message_new_application (
GST_OBJECT_CAST (self), structure));
}
void
clapper_reactables_manager_trigger_state_changed (ClapperReactablesManager *self, ClapperPlayerState state)
{
_BUS_POST_EVENT_SINGLE (_EVENT (STATE_CHANGED), int, G_TYPE_INT, state);
}
void
clapper_reactables_manager_trigger_position_changed (ClapperReactablesManager *self, gdouble position)
{
_BUS_POST_EVENT_SINGLE (_EVENT (POSITION_CHANGED), double, G_TYPE_DOUBLE, position);
}
void
clapper_reactables_manager_trigger_speed_changed (ClapperReactablesManager *self, gdouble speed)
{
_BUS_POST_EVENT_SINGLE (_EVENT (SPEED_CHANGED), double, G_TYPE_DOUBLE, speed);
}
void
clapper_reactables_manager_trigger_volume_changed (ClapperReactablesManager *self, gdouble volume)
{
_BUS_POST_EVENT_SINGLE (_EVENT (VOLUME_CHANGED), double, G_TYPE_DOUBLE, volume);
}
void
clapper_reactables_manager_trigger_mute_changed (ClapperReactablesManager *self, gboolean mute)
{
_BUS_POST_EVENT_SINGLE (_EVENT (MUTE_CHANGED), boolean, G_TYPE_BOOLEAN, mute);
}
void
clapper_reactables_manager_trigger_played_item_changed (ClapperReactablesManager *self, ClapperMediaItem *item)
{
_BUS_POST_EVENT_SINGLE (_EVENT (PLAYED_ITEM_CHANGED), object, CLAPPER_TYPE_MEDIA_ITEM, item);
}
void
clapper_reactables_manager_trigger_item_updated (ClapperReactablesManager *self, ClapperMediaItem *item)
{
_BUS_POST_EVENT_SINGLE (_EVENT (ITEM_UPDATED), object, CLAPPER_TYPE_MEDIA_ITEM, item);
}
void
clapper_reactables_manager_trigger_queue_item_added (ClapperReactablesManager *self, ClapperMediaItem *item, guint index)
{
_BUS_POST_EVENT_DUAL (_EVENT (QUEUE_ITEM_ADDED), object, CLAPPER_TYPE_MEDIA_ITEM, item, uint, G_TYPE_UINT, index);
}
void
clapper_reactables_manager_trigger_queue_item_removed (ClapperReactablesManager *self, ClapperMediaItem *item, guint index)
{
_BUS_POST_EVENT_DUAL (_EVENT (QUEUE_ITEM_REMOVED), object, CLAPPER_TYPE_MEDIA_ITEM, item, uint, G_TYPE_UINT, index);
}
void
clapper_reactables_manager_trigger_queue_item_repositioned (ClapperReactablesManager *self, guint before, guint after)
{
_BUS_POST_EVENT_DUAL (_EVENT (QUEUE_ITEM_REPOSITIONED), uint, G_TYPE_UINT, before, uint, G_TYPE_UINT, after);
}
void
clapper_reactables_manager_trigger_queue_cleared (ClapperReactablesManager *self)
{
_bus_post_event (self, _EVENT (QUEUE_CLEARED), NULL, NULL);
}
void
clapper_reactables_manager_trigger_queue_progression_changed (ClapperReactablesManager *self, ClapperQueueProgressionMode mode)
{
_BUS_POST_EVENT_SINGLE (_EVENT (QUEUE_PROGRESSION_CHANGED), int, G_TYPE_INT, mode);
}
static void
_data_remove_func (ClapperReactableManagerData *data)
{
GST_TRACE ("Removing data for reactable: %" GST_PTR_FORMAT, data->reactable);
g_clear_object (&data->settings);
gst_object_unparent (GST_OBJECT_CAST (data->reactable));
gst_object_unref (data->reactable);
gst_object_unref (data->proxy);
g_free (data);
}
static void
clapper_reactables_manager_thread_start (ClapperThreadedObject *threaded_object)
{
ClapperReactablesManager *self = CLAPPER_REACTABLES_MANAGER_CAST (threaded_object);
GST_TRACE_OBJECT (threaded_object, "Reactables manager thread start");
self->array = g_ptr_array_new_with_free_func (
(GDestroyNotify) _data_remove_func);
self->bus = gst_bus_new ();
gst_bus_add_watch (self->bus, (GstBusFunc) _bus_message_func, NULL);
}
static void
clapper_reactables_manager_thread_stop (ClapperThreadedObject *threaded_object)
{
ClapperReactablesManager *self = CLAPPER_REACTABLES_MANAGER_CAST (threaded_object);
GST_TRACE_OBJECT (self, "Reactables manager thread stop");
gst_bus_set_flushing (self->bus, TRUE);
gst_bus_remove_watch (self->bus);
gst_clear_object (&self->bus);
g_ptr_array_unref (self->array);
}
static void
clapper_reactables_manager_init (ClapperReactablesManager *self)
{
}
static void
clapper_reactables_manager_finalize (GObject *object)
{
GST_TRACE_OBJECT (object, "Finalize");
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_reactables_manager_class_init (ClapperReactablesManagerClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
ClapperThreadedObjectClass *threaded_object = (ClapperThreadedObjectClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperreactablesmanager", 0,
"Clapper Reactables Manager");
gobject_class->finalize = clapper_reactables_manager_finalize;
threaded_object->thread_start = clapper_reactables_manager_thread_start;
threaded_object->thread_stop = clapper_reactables_manager_thread_stop;
}

View File

@@ -29,6 +29,8 @@
#include "clapper-timeline-private.h"
#include "clapper-marker-private.h"
#include "clapper-player-private.h"
#include "clapper-reactables-manager-private.h"
#include "clapper-features-manager-private.h"
#define GST_CAT_DEFAULT clapper_timeline_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@@ -106,15 +108,17 @@ clapper_timeline_post_item_updated (ClapperTimeline *self)
ClapperPlayer *player;
if ((player = clapper_player_get_from_ancestor (GST_OBJECT_CAST (self)))) {
ClapperFeaturesManager *features_manager;
ClapperMediaItem *item;
if ((features_manager = clapper_player_get_features_manager (player))) {
ClapperMediaItem *item;
if ((item = CLAPPER_MEDIA_ITEM_CAST (gst_object_get_parent (GST_OBJECT_CAST (self))))) {
ClapperFeaturesManager *features_manager;
if ((item = CLAPPER_MEDIA_ITEM (gst_object_get_parent (GST_OBJECT_CAST (self))))) {
if (player->reactables_manager)
clapper_reactables_manager_trigger_item_updated (player->reactables_manager, item);
if ((features_manager = clapper_player_get_features_manager (player)))
clapper_features_manager_trigger_item_updated (features_manager, item);
gst_object_unref (item);
}
gst_object_unref (item);
}
gst_object_unref (player);

View File

@@ -54,4 +54,7 @@ gchar * clapper_utils_uri_from_file (GFile *file);
G_GNUC_INTERNAL
gchar * clapper_utils_title_from_uri (const gchar *uri);
G_GNUC_INTERNAL
gboolean clapper_utils_set_value_from_variant (GValue *value, GVariant *variant);
G_END_DECLS

View File

@@ -271,3 +271,59 @@ clapper_utils_title_from_uri (const gchar *uri)
return title;
}
gboolean
clapper_utils_set_value_from_variant (GValue *value, GVariant *variant)
{
const gchar *var_type = g_variant_get_type_string (variant);
GType val_type;
switch (var_type[0]) {
case 'b':
val_type = G_TYPE_BOOLEAN;
break;
case 'i':
val_type = G_TYPE_INT;
break;
case 'u':
val_type = G_TYPE_UINT;
break;
case 'd':
val_type = G_TYPE_DOUBLE;
break;
case 's':
val_type = G_TYPE_STRING;
break;
default:
goto error;
}
g_value_init (value, val_type);
switch (val_type) {
case G_TYPE_BOOLEAN:
g_value_set_boolean (value, g_variant_get_boolean (variant));
break;
case G_TYPE_INT:
g_value_set_int (value, g_variant_get_int32 (variant));
break;
case G_TYPE_UINT:
g_value_set_uint (value, g_variant_get_uint32 (variant));
break;
case G_TYPE_DOUBLE:
g_value_set_double (value, g_variant_get_double (variant));
break;
case G_TYPE_STRING:
g_value_set_string (value, g_variant_get_string (variant, NULL));
break;
default:
g_assert_not_reached ();
break;
}
return TRUE;
error:
GST_ERROR ("Unsupported conversion for variant type: %s", var_type);
return FALSE;
}

View File

@@ -23,36 +23,13 @@
#include "../clapper-basic-functions.h"
#include "../clapper-enhancer-proxy.h"
#include "../clapper-enhancer-proxy-list.h"
#include "../clapper-enhancer-proxy-list-private.h"
#include "../clapper-extractable.h"
#include "clapper-plugin-private.h"
#include "clapper-extractable-src-private.h"
#include "clapper-uri-list-demux-private.h"
/*
* clapper_gst_plugin_has_enhancers:
* @iface_type: an interface #GType
*
* Check if any enhancer implementing given interface type is available.
*
* Returns: whether any enhancer was found.
*/
static gboolean
clapper_gst_plugin_has_enhancers (ClapperEnhancerProxyList *proxies, GType iface_type)
{
guint i, n_proxies = clapper_enhancer_proxy_list_get_n_proxies (proxies);
for (i = 0; i < n_proxies; ++i) {
ClapperEnhancerProxy *proxy = clapper_enhancer_proxy_list_peek_proxy (proxies, i);
if (clapper_enhancer_proxy_target_has_interface (proxy, iface_type))
return TRUE;
}
return FALSE;
}
gboolean
clapper_gst_plugin_init (GstPlugin *plugin)
{
@@ -66,7 +43,7 @@ clapper_gst_plugin_init (GstPlugin *plugin)
global_proxies = clapper_get_global_enhancer_proxies ();
/* Avoid registering an URI handler without schemes */
if (clapper_gst_plugin_has_enhancers (global_proxies, CLAPPER_TYPE_EXTRACTABLE))
if (clapper_enhancer_proxy_list_has_proxy_with_interface (global_proxies, CLAPPER_TYPE_EXTRACTABLE))
res |= GST_ELEMENT_REGISTER (clapperextractablesrc, plugin);
res |= GST_ELEMENT_REGISTER (clapperurilistdemux, plugin);

View File

@@ -149,6 +149,7 @@ clapper_sources = [
'clapper-player.c',
'clapper-queue.c',
'clapper-reactable.c',
'clapper-reactables-manager.c',
'clapper-stream.c',
'clapper-stream-list.c',
'clapper-subtitle-stream.c',