diff --git a/src/lib/clapper/clapper-basic-functions.c b/src/lib/clapper/clapper-basic-functions.c index 32e58f45..f128b40e 100644 --- a/src/lib/clapper/clapper-basic-functions.c +++ b/src/lib/clapper/clapper-basic-functions.c @@ -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"); diff --git a/src/lib/clapper/clapper-cache.c b/src/lib/clapper/clapper-cache.c index 30774800..c15c9855 100644 --- a/src/lib/clapper/clapper-cache.c +++ b/src/lib/clapper/clapper-cache.c @@ -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; diff --git a/src/lib/clapper/clapper-enhancer-proxy-list-private.h b/src/lib/clapper/clapper-enhancer-proxy-list-private.h index c25b4024..a48162c2 100644 --- a/src/lib/clapper/clapper-enhancer-proxy-list-private.h +++ b/src/lib/clapper/clapper-enhancer-proxy-list-private.h @@ -20,6 +20,7 @@ #pragma once #include +#include #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 diff --git a/src/lib/clapper/clapper-enhancer-proxy-list.c b/src/lib/clapper/clapper-enhancer-proxy-list.c index 619436bd..228117b8 100644 --- a/src/lib/clapper/clapper-enhancer-proxy-list.c +++ b/src/lib/clapper/clapper-enhancer-proxy-list.c @@ -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 diff --git a/src/lib/clapper/clapper-enhancer-proxy-private.h b/src/lib/clapper/clapper-enhancer-proxy-private.h index fb1ff659..f7d20a42 100644 --- a/src/lib/clapper/clapper-enhancer-proxy-private.h +++ b/src/lib/clapper/clapper-enhancer-proxy-private.h @@ -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); diff --git a/src/lib/clapper/clapper-enhancer-proxy.c b/src/lib/clapper/clapper-enhancer-proxy.c index 2b78ed5e..367667d9 100644 --- a/src/lib/clapper/clapper-enhancer-proxy.c +++ b/src/lib/clapper/clapper-enhancer-proxy.c @@ -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 diff --git a/src/lib/clapper/clapper-enhancers-loader.c b/src/lib/clapper/clapper-enhancers-loader.c index 1969376b..aeb95dd7 100644 --- a/src/lib/clapper/clapper-enhancers-loader.c +++ b/src/lib/clapper/clapper-enhancers-loader.c @@ -33,6 +33,9 @@ static HMODULE _enhancers_dll_handle = NULL; // Supported interfaces #include "clapper-extractable.h" +#include "clapper-reactable.h" + +#include #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 */ diff --git a/src/lib/clapper/clapper-media-item-private.h b/src/lib/clapper/clapper-media-item-private.h index 2ddc5bea..ab218b6a 100644 --- a/src/lib/clapper/clapper-media-item-private.h +++ b/src/lib/clapper/clapper-media-item-private.h @@ -23,7 +23,7 @@ #include #include "clapper-media-item.h" -#include "clapper-player-private.h" +#include "clapper-player.h" #include "clapper-app-bus-private.h" G_BEGIN_DECLS diff --git a/src/lib/clapper/clapper-media-item.c b/src/lib/clapper/clapper-media-item.c index 2532c3cf..b4e925c7 100644 --- a/src/lib/clapper/clapper-media-item.c +++ b/src/lib/clapper/clapper-media-item.c @@ -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); } diff --git a/src/lib/clapper/clapper-playbin-bus.c b/src/lib/clapper/clapper-playbin-bus.c index faa815cc..0260455b 100644 --- a/src/lib/clapper/clapper-playbin-bus.c +++ b/src/lib/clapper/clapper-playbin-bus.c @@ -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); } diff --git a/src/lib/clapper/clapper-player-private.h b/src/lib/clapper/clapper-player-private.h index c042cbae..f201e34e 100644 --- a/src/lib/clapper/clapper-player-private.h +++ b/src/lib/clapper/clapper-player-private.h @@ -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 diff --git a/src/lib/clapper/clapper-player.c b/src/lib/clapper/clapper-player.c index de1efcc0..c4cb5eb6 100644 --- a/src/lib/clapper/clapper-player.c +++ b/src/lib/clapper/clapper-player.c @@ -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); diff --git a/src/lib/clapper/clapper-queue.c b/src/lib/clapper/clapper-queue.c index 98f1dac5..837ac4f6 100644 --- a/src/lib/clapper/clapper-queue.c +++ b/src/lib/clapper/clapper-queue.c @@ -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); diff --git a/src/lib/clapper/clapper-reactables-manager-private.h b/src/lib/clapper/clapper-reactables-manager-private.h new file mode 100644 index 00000000..80496648 --- /dev/null +++ b/src/lib/clapper/clapper-reactables-manager-private.h @@ -0,0 +1,82 @@ +/* Clapper Playback Library + * Copyright (C) 2025 Rafał Dzięgiel + * + * 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 diff --git a/src/lib/clapper/clapper-reactables-manager.c b/src/lib/clapper/clapper-reactables-manager.c new file mode 100644 index 00000000..8971deb6 --- /dev/null +++ b/src/lib/clapper/clapper-reactables-manager.c @@ -0,0 +1,533 @@ +/* Clapper Playback Library + * Copyright (C) 2025 Rafał Dzięgiel + * + * 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 + +#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; +} diff --git a/src/lib/clapper/clapper-timeline.c b/src/lib/clapper/clapper-timeline.c index 7bd911a7..2b4c9252 100644 --- a/src/lib/clapper/clapper-timeline.c +++ b/src/lib/clapper/clapper-timeline.c @@ -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); diff --git a/src/lib/clapper/clapper-utils-private.h b/src/lib/clapper/clapper-utils-private.h index 8e402a6a..fc59fec7 100644 --- a/src/lib/clapper/clapper-utils-private.h +++ b/src/lib/clapper/clapper-utils-private.h @@ -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 diff --git a/src/lib/clapper/clapper-utils.c b/src/lib/clapper/clapper-utils.c index 86db8e90..39979907 100644 --- a/src/lib/clapper/clapper-utils.c +++ b/src/lib/clapper/clapper-utils.c @@ -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; +} diff --git a/src/lib/clapper/gst/clapper-plugin.c b/src/lib/clapper/gst/clapper-plugin.c index 2bb2a7d9..ba7a1b72 100644 --- a/src/lib/clapper/gst/clapper-plugin.c +++ b/src/lib/clapper/gst/clapper-plugin.c @@ -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); diff --git a/src/lib/clapper/meson.build b/src/lib/clapper/meson.build index 522f00cd..655ea4df 100644 --- a/src/lib/clapper/meson.build +++ b/src/lib/clapper/meson.build @@ -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',