Merge pull request #559 from Rafostar/reactable

clapper: Add "Reactable" interface
This commit is contained in:
Rafał Dzięgiel
2025-06-09 20:52:15 +02:00
committed by GitHub
27 changed files with 1369 additions and 100 deletions

View File

@@ -86,9 +86,7 @@ typedef struct
gint64 last_tick;
} ClapperAppWindowResizeData;
#if CLAPPER_HAVE_MPRIS
static guint16 instance_count = 0;
#endif
static inline GQuark
clapper_app_window_extra_options_get_quark (void)
@@ -1252,16 +1250,28 @@ clapper_app_window_constructed (GObject *object)
#if (CLAPPER_HAVE_MPRIS || CLAPPER_HAVE_SERVER || CLAPPER_HAVE_DISCOVERER)
ClapperFeature *feature = NULL;
#endif
#if CLAPPER_HAVE_MPRIS
#if (!CLAPPER_HAVE_MPRIS || !CLAPPER_HAVE_SERVER || !CLAPPER_HAVE_DISCOVERER)
ClapperEnhancerProxyList *proxies = clapper_player_get_enhancer_proxies (player);
ClapperEnhancerProxy *proxy;
#endif
gchar mpris_name[45];
g_snprintf (mpris_name, sizeof (mpris_name),
"org.mpris.MediaPlayer2.Clapper.instance%" G_GUINT16_FORMAT, instance_count++);
#endif
self->settings = g_settings_new (CLAPPER_APP_ID);
self->last_volume = PERCENTAGE_ROUND (g_settings_get_double (self->settings, "volume"));
#if CLAPPER_HAVE_MPRIS
#if !CLAPPER_HAVE_MPRIS
if ((proxy = clapper_enhancer_proxy_list_get_proxy_by_module (proxies, "clapper-mpris"))) {
clapper_enhancer_proxy_set_locally (proxy,
"own-name", mpris_name,
"identity", CLAPPER_APP_NAME,
"desktop-entry", CLAPPER_APP_ID,
"queue-controllable", TRUE, NULL);
gst_object_unref (proxy);
}
#else
feature = CLAPPER_FEATURE (clapper_mpris_new (
mpris_name, CLAPPER_APP_NAME, CLAPPER_APP_ID));
clapper_mpris_set_queue_controllable (CLAPPER_MPRIS (feature), TRUE);

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

@@ -151,4 +151,23 @@ typedef enum
CLAPPER_ENHANCER_PARAM_DIRPATH = 1 << 20,
} ClapperEnhancerParamFlags;
/**
* ClapperReactableItemUpdatedFlags:
* @CLAPPER_REACTABLE_ITEM_UPDATED_TITLE: Media item title was updated.
* @CLAPPER_REACTABLE_ITEM_UPDATED_DURATION: Media item duration was updated.
* @CLAPPER_REACTABLE_ITEM_UPDATED_TIMELINE: Media item timeline was updated.
* @CLAPPER_REACTABLE_ITEM_UPDATED_TAGS: Media item tags were updated.
*
* Flags informing which properties were updated within [class@Clapper.MediaItem].
*
* Since: 0.10
*/
typedef enum
{
CLAPPER_REACTABLE_ITEM_UPDATED_TITLE = 1 << 0,
CLAPPER_REACTABLE_ITEM_UPDATED_DURATION = 1 << 1,
CLAPPER_REACTABLE_ITEM_UPDATED_TIMELINE = 1 << 2,
CLAPPER_REACTABLE_ITEM_UPDATED_TAGS = 1 << 3,
} ClapperReactableItemUpdatedFlags;
G_END_DECLS

View File

@@ -30,6 +30,8 @@
* virtual functions logic, while for controlling playback implementation
* may call [method@Gst.Object.get_parent] to acquire a weak reference on
* a parent [class@Clapper.Player] object feature was added to.
*
* Deprecated: 0.10: Use [iface@Clapper.Reactable] instead.
*/
#include "clapper-feature.h"

View File

@@ -37,7 +37,7 @@ G_BEGIN_DECLS
#define CLAPPER_TYPE_FEATURE (clapper_feature_get_type())
#define CLAPPER_FEATURE_CAST(obj) ((ClapperFeature *)(obj))
CLAPPER_API
CLAPPER_DEPRECATED_FOR(ClapperReactable)
G_DECLARE_DERIVABLE_TYPE (ClapperFeature, clapper_feature, CLAPPER, FEATURE, GstObject)
/**

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"
@@ -470,7 +471,7 @@ _tags_replace_func (const GstTagList *tags, const gchar *tag, ClapperMediaItemTa
static gboolean
clapper_media_item_insert_tags_internal (ClapperMediaItem *self, const GstTagList *tags,
ClapperAppBus *app_bus, gboolean from_user)
ClapperAppBus *app_bus, gboolean from_user, ClapperReactableItemUpdatedFlags *flags)
{
ClapperMediaItemTagIterData data;
gboolean title_changed = FALSE, cont_changed = FALSE;
@@ -485,8 +486,12 @@ clapper_media_item_insert_tags_internal (ClapperMediaItem *self, const GstTagLis
gst_tag_list_foreach (tags, (GstTagForeachFunc) _tags_replace_func, &data);
if (data.changed) {
title_changed = _refresh_tag_prop_unlocked (self, GST_TAG_TITLE,
from_user, &self->title);
*flags |= CLAPPER_REACTABLE_ITEM_UPDATED_TAGS;
if ((title_changed = _refresh_tag_prop_unlocked (self, GST_TAG_TITLE,
from_user, &self->title))) {
*flags |= CLAPPER_REACTABLE_ITEM_UPDATED_TITLE;
}
cont_changed = _refresh_tag_prop_unlocked (self, GST_TAG_CONTAINER_FORMAT,
from_user, &self->container_format);
}
@@ -547,6 +552,7 @@ clapper_media_item_populate_tags (ClapperMediaItem *self, const GstTagList *tags
{
ClapperPlayer *player;
ClapperAppBus *app_bus = NULL;
ClapperReactableItemUpdatedFlags flags = 0;
gboolean changed;
g_return_val_if_fail (CLAPPER_IS_MEDIA_ITEM (self), FALSE);
@@ -560,11 +566,13 @@ clapper_media_item_populate_tags (ClapperMediaItem *self, const GstTagList *tags
if ((player = clapper_player_get_from_ancestor (GST_OBJECT_CAST (self))))
app_bus = player->app_bus;
changed = clapper_media_item_insert_tags_internal (self, tags, app_bus, TRUE);
changed = clapper_media_item_insert_tags_internal (self, tags, app_bus, TRUE, &flags);
if (changed && player) {
ClapperFeaturesManager *features_manager;
if (player->reactables_manager)
clapper_reactables_manager_trigger_item_updated (player->reactables_manager, self, flags);
if ((features_manager = clapper_player_get_features_manager (player)))
clapper_features_manager_trigger_item_updated (features_manager, self);
}
@@ -597,11 +605,14 @@ clapper_media_item_update_from_tag_list (ClapperMediaItem *self, const GstTagLis
GstTagScope scope = gst_tag_list_get_scope (tags);
if (scope == GST_TAG_SCOPE_GLOBAL) {
gboolean changed = clapper_media_item_insert_tags_internal (self, tags, player->app_bus, FALSE);
ClapperReactableItemUpdatedFlags flags = 0;
gboolean changed = clapper_media_item_insert_tags_internal (self, tags, player->app_bus, FALSE, &flags);
if (changed) {
ClapperFeaturesManager *features_manager;
if (player->reactables_manager)
clapper_reactables_manager_trigger_item_updated (player->reactables_manager, self, flags);
if ((features_manager = clapper_player_get_features_manager (player)))
clapper_features_manager_trigger_item_updated (features_manager, self);
}
@@ -614,6 +625,7 @@ clapper_media_item_update_from_discoverer_info (ClapperMediaItem *self, GstDisco
ClapperPlayer *player;
GstDiscovererStreamInfo *sinfo;
GstClockTime duration;
ClapperReactableItemUpdatedFlags flags = 0;
gdouble val_dbl;
gboolean changed = FALSE;
@@ -629,7 +641,7 @@ clapper_media_item_update_from_discoverer_info (ClapperMediaItem *self, GstDisco
GstDiscovererContainerInfo *cinfo = (GstDiscovererContainerInfo *) sinfo;
if ((tags = gst_discoverer_container_info_get_tags (cinfo)))
changed |= clapper_media_item_insert_tags_internal (self, tags, player->app_bus, FALSE);
changed |= clapper_media_item_insert_tags_internal (self, tags, player->app_bus, FALSE, &flags);
}
gst_discoverer_stream_info_unref (sinfo);
}
@@ -640,11 +652,16 @@ clapper_media_item_update_from_discoverer_info (ClapperMediaItem *self, GstDisco
duration = 0;
val_dbl = (gdouble) duration / GST_SECOND;
changed |= clapper_media_item_set_duration (self, val_dbl, player->app_bus);
if (clapper_media_item_set_duration (self, val_dbl, player->app_bus)) {
changed = TRUE;
flags |= CLAPPER_REACTABLE_ITEM_UPDATED_DURATION;
}
if (changed) {
ClapperFeaturesManager *features_manager;
if (player->reactables_manager)
clapper_reactables_manager_trigger_item_updated (player->reactables_manager, self, flags);
if ((features_manager = clapper_player_get_features_manager (player)))
clapper_features_manager_trigger_item_updated (features_manager, self);
}

View File

@@ -173,6 +173,10 @@ _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,
CLAPPER_REACTABLE_ITEM_UPDATED_DURATION);
}
if ((features_manager = clapper_player_get_features_manager (player)))
clapper_features_manager_trigger_item_updated (features_manager, player->played_item);
}
@@ -1046,6 +1050,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,210 @@
/* 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.
*/
/**
* ClapperReactable:
*
* An interface for creating enhancers that react to the
* playback and/or events that should influence it.
*
* Since: 0.10
*/
#include "clapper-reactable.h"
#include "clapper-utils-private.h"
#define CLAPPER_REACTABLE_DO_WITH_QUEUE(reactable, _queue_dst, ...) { \
ClapperPlayer *_player = clapper_reactable_get_player (reactable); \
if (G_LIKELY (_player != NULL)) { \
*_queue_dst = clapper_player_get_queue (_player); \
__VA_ARGS__ \
gst_object_unref (_player); }}
G_DEFINE_INTERFACE (ClapperReactable, clapper_reactable, GST_TYPE_OBJECT);
static void
clapper_reactable_default_init (ClapperReactableInterface *iface)
{
}
/**
* clapper_reactable_get_player:
* @reactable: a #ClapperReactable
*
* Get the [class@Clapper.Player] that this reactable is reacting to.
*
* This is meant to be used in implementations where reaction goes the
* other way around (from enhancer plugin to the player). For example
* some external event needs to influence parent player object like
* changing its state, seeking, etc.
*
* Note that enhancers are working in a non-main application thread, thus
* if you need to do operations on a [class@Clapper.Queue] such as adding/removing
* items, you need to switch thread first. Otherwise this will not be thread safe
* for applications that use single threaded toolkits such as #GTK. You can do this
* manually or use provided reactable convenience functions.
*
* Due to the threaded nature, you should also avoid comparisons to the current
* properties values in the player or its queue. While these are thread safe, there
* is no guarantee that values/objects between threads are still the same in both
* (or still exist). For example, instead of using [property@Clapper.Queue:current_item],
* monitor it with implemented [vfunc@Clapper.Reactable.played_item_changed] instead,
* as these functions are all serialized into your implementation thread.
*
* Returns: (transfer full) (nullable): A reference to the parent #ClapperPlayer.
*
* Since: 0.10
*/
ClapperPlayer *
clapper_reactable_get_player (ClapperReactable *self)
{
g_return_val_if_fail (CLAPPER_IS_REACTABLE (self), NULL);
return CLAPPER_PLAYER_CAST (gst_object_get_parent (GST_OBJECT_CAST (self)));
}
/**
* clapper_reactable_queue_append_sync:
* @reactable: a #ClapperReactable
* @item: a #ClapperMediaItem
*
* A convenience function that within application main thread synchronously appends
* an @item to the playback queue of the player that @reactable belongs to.
*
* Reactable enhancers should only modify the queue from the application
* main thread, switching thread either themselves or using this convenience
* function that does so.
*
* Note that this function will do no operation if called when there is no player
* set yet (e.g. inside enhancer construction) or if enhancer outlived the parent
* instance somehow. Both cases are considered to be implementation bug.
*
* Since: 0.10
*/
void
clapper_reactable_queue_append_sync (ClapperReactable *self, ClapperMediaItem *item)
{
ClapperQueue *queue;
g_return_if_fail (CLAPPER_IS_REACTABLE (self));
g_return_if_fail (CLAPPER_IS_MEDIA_ITEM (item));
CLAPPER_REACTABLE_DO_WITH_QUEUE (self, &queue, {
clapper_utils_queue_append_on_main_sync (queue, item);
});
}
/**
* clapper_reactable_queue_insert_sync:
* @reactable: a #ClapperReactable
* @item: a #ClapperMediaItem
* @after_item: a #ClapperMediaItem after which to insert or %NULL to prepend
*
* A convenience function that within application main thread synchronously inserts
* an @item to the playback queue position after @after_item of the player that
* @reactable belongs to.
*
* This function uses @after_item instead of position index in order to ensure
* desired position does not change during thread switching.
*
* Reactable enhancers should only modify the queue from the application
* main thread, switching thread either themselves or using this convenience
* function that does so.
*
* Note that this function will do no operation if called when there is no player
* set yet (e.g. inside enhancer construction) or if enhancer outlived the parent
* instance somehow. Both cases are considered to be implementation bug.
*
* Since: 0.10
*/
void
clapper_reactable_queue_insert_sync (ClapperReactable *self,
ClapperMediaItem *item, ClapperMediaItem *after_item)
{
ClapperQueue *queue;
g_return_if_fail (CLAPPER_IS_REACTABLE (self));
g_return_if_fail (CLAPPER_IS_MEDIA_ITEM (item));
g_return_if_fail (after_item == NULL || CLAPPER_IS_MEDIA_ITEM (after_item));
CLAPPER_REACTABLE_DO_WITH_QUEUE (self, &queue, {
clapper_utils_queue_insert_on_main_sync (queue, item, after_item);
});
}
/**
* clapper_reactable_queue_remove_sync:
* @reactable: a #ClapperReactable
* @item: a #ClapperMediaItem
*
* A convenience function that within application main thread synchronously removes
* an @item from the playback queue of the player that @reactable belongs to.
*
* Reactable enhancers should only modify the queue from the application
* main thread, switching thread either themselves or using this convenience
* function that does so.
*
* Note that this function will do no operation if called when there is no player
* set yet (e.g. inside enhancer construction) or if enhancer outlived the parent
* instance somehow. Both cases are considered to be implementation bug.
*
* Since: 0.10
*/
void
clapper_reactable_queue_remove_sync (ClapperReactable *self, ClapperMediaItem *item)
{
ClapperQueue *queue;
g_return_if_fail (CLAPPER_IS_REACTABLE (self));
g_return_if_fail (CLAPPER_IS_MEDIA_ITEM (item));
CLAPPER_REACTABLE_DO_WITH_QUEUE (self, &queue, {
clapper_utils_queue_remove_on_main_sync (queue, item);
});
}
/**
* clapper_reactable_queue_clear_sync:
* @reactable: a #ClapperReactable
*
* A convenience function that within application main thread synchronously clears
* the playback queue of the player that @reactable belongs to.
*
* Reactable enhancers should only modify the queue from the application
* main thread, switching thread either themselves or using this convenience
* function that does so.
*
* Note that this function will do no operation if called when there is no player
* set yet (e.g. inside enhancer construction) or if enhancer outlived the parent
* instance somehow. Both cases are considered to be implementation bug.
*
* Since: 0.10
*/
void
clapper_reactable_queue_clear_sync (ClapperReactable *self)
{
ClapperQueue *queue;
g_return_if_fail (CLAPPER_IS_REACTABLE (self));
g_return_if_fail (CLAPPER_IS_MEDIA_ITEM (item));
CLAPPER_REACTABLE_DO_WITH_QUEUE (self, &queue, {
clapper_utils_queue_clear_on_main_sync (queue);
});
}

View File

@@ -0,0 +1,229 @@
/* 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
#if !defined(__CLAPPER_INSIDE__) && !defined(CLAPPER_COMPILATION)
#error "Only <clapper/clapper.h> can be included directly."
#endif
#include <glib.h>
#include <glib-object.h>
#include <gst/gst.h>
#include <clapper/clapper-visibility.h>
#include <clapper/clapper-player.h>
#include <clapper/clapper-enums.h>
G_BEGIN_DECLS
#define CLAPPER_TYPE_REACTABLE (clapper_reactable_get_type())
#define CLAPPER_REACTABLE_CAST(obj) ((ClapperReactable *)(obj))
CLAPPER_API
G_DECLARE_INTERFACE (ClapperReactable, clapper_reactable, CLAPPER, REACTABLE, GstObject)
/**
* ClapperReactableInterface:
* @parent_iface: The parent interface structure.
* @state_changed: Player state changed.
* @position_changed: Player position changed.
* @speed_changed: Player speed changed.
* @volume_changed: Player volume changed.
* @mute_changed: Player mute state changed.
* @played_item_changed: New media item started playing.
* @item_updated: An item in queue got updated.
* @queue_item_added: An item was added to the queue.
* @queue_item_removed: An item was removed from queue.
* @queue_item_repositioned: An item changed position within queue.
* @queue_cleared: All items were removed from queue.
* @queue_progression_changed: Progression mode of the queue was changed.
*/
struct _ClapperReactableInterface
{
GTypeInterface parent_iface;
/**
* ClapperReactableInterface::state_changed:
* @reactable: a #ClapperReactable
* @state: a #ClapperPlayerState
*
* Player state changed.
*
* Since: 0.10
*/
void (* state_changed) (ClapperReactable *reactable, ClapperPlayerState state);
/**
* ClapperReactableInterface::position_changed:
* @reactable: a #ClapperReactable
* @position: a decimal number with current position in seconds
*
* Player position changed.
*
* Since: 0.10
*/
void (* position_changed) (ClapperReactable *reactable, gdouble position);
/**
* ClapperReactableInterface::speed_changed:
* @reactable: a #ClapperReactable
* @speed: the playback speed multiplier
*
* Player speed changed.
*
* Since: 0.10
*/
void (* speed_changed) (ClapperReactable *reactable, gdouble speed);
/**
* ClapperReactableInterface::volume_changed:
* @reactable: a #ClapperReactable
* @volume: the volume level
*
* Player volume changed.
*
* Since: 0.10
*/
void (* volume_changed) (ClapperReactable *reactable, gdouble volume);
/**
* ClapperReactableInterface::mute_changed:
* @reactable: a #ClapperReactable
* @mute: %TRUE if player is muted, %FALSE otherwise
*
* Player mute state changed.
*
* Since: 0.10
*/
void (* mute_changed) (ClapperReactable *reactable, gboolean mute);
/**
* ClapperReactableInterface::played_item_changed:
* @reactable: a #ClapperReactable
* @item: a #ClapperMediaItem that is now playing
*
* New media item started playing. All following events (such as position changes)
* will be related to this @item from now on.
*
* Since: 0.10
*/
void (* played_item_changed) (ClapperReactable *reactable, ClapperMediaItem *item);
/**
* ClapperReactableInterface::item_updated:
* @reactable: a #ClapperReactable
* @item: a #ClapperMediaItem that was updated
* @flags: flags informing which properties were updated
*
* An item in queue got updated.
*
* This might be (or not) currently played item.
* Implementations can compare it against the last item from
* [vfunc@Clapper.Reactable.played_item_changed] if they
* need to know that.
*
* Since: 0.10
*/
void (* item_updated) (ClapperReactable *reactable, ClapperMediaItem *item, ClapperReactableItemUpdatedFlags flags);
/**
* ClapperReactableInterface::queue_item_added:
* @reactable: a #ClapperReactable
* @item: a #ClapperMediaItem that was added
* @index: position at which @item was placed in queue
*
* An item was added to the queue.
*
* Since: 0.10
*/
void (* queue_item_added) (ClapperReactable *reactable, ClapperMediaItem *item, guint index);
/**
* ClapperReactableInterface::queue_item_removed:
* @reactable: a #ClapperReactable
* @item: a #ClapperMediaItem that was removed
* @index: position from which @item was removed in queue
*
* An item was removed from queue.
*
* Implementations that are interested in queue items removal
* should also implement [vfunc@Clapper.Reactable.queue_cleared].
*
* Since: 0.10
*/
void (* queue_item_removed) (ClapperReactable *reactable, ClapperMediaItem *item, guint index);
/**
* ClapperReactableInterface::queue_item_repositioned:
* @reactable: a #ClapperReactable
* @before: position from which #ClapperMediaItem was removed
* @after: position at which #ClapperMediaItem was inserted after removal
*
* An item changed position within queue.
*
* Since: 0.10
*/
void (* queue_item_repositioned) (ClapperReactable *reactable, guint before, guint after);
/**
* ClapperReactableInterface::queue_cleared:
* @reactable: a #ClapperReactable
*
* All items were removed from queue.
*
* Note that in such event [vfunc@Clapper.Reactable.queue_item_removed]
* will NOT be called for each item for performance reasons. You probably
* want to implement this function if you also implemented item removal.
*
* Since: 0.10
*/
void (* queue_cleared) (ClapperReactable *reactable);
/**
* ClapperReactableInterface::queue_progression_changed:
* @reactable: a #ClapperReactable
* @mode: a #ClapperQueueProgressionMode
*
* Progression mode of the queue was changed.
*
* Since: 0.10
*/
void (* queue_progression_changed) (ClapperReactable *reactable, ClapperQueueProgressionMode mode);
/*< private >*/
gpointer padding[8];
};
CLAPPER_API
ClapperPlayer * clapper_reactable_get_player (ClapperReactable *reactable);
CLAPPER_API
void clapper_reactable_queue_append_sync (ClapperReactable *reactable, ClapperMediaItem *item);
CLAPPER_API
void clapper_reactable_queue_insert_sync (ClapperReactable *reactable, ClapperMediaItem *item, ClapperMediaItem *after_item);
CLAPPER_API
void clapper_reactable_queue_remove_sync (ClapperReactable *reactable, ClapperMediaItem *item);
CLAPPER_API
void clapper_reactable_queue_clear_sync (ClapperReactable *reactable);
G_END_DECLS

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, ClapperReactableItemUpdatedFlags flags);
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,534 @@
/* 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)),
g_value_get_flags (extra_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, ClapperReactableItemUpdatedFlags _flags)
{
_BUS_POST_EVENT_DUAL (_EVENT (ITEM_UPDATED), object, CLAPPER_TYPE_MEDIA_ITEM, item, flags, CLAPPER_TYPE_REACTABLE_ITEM_UPDATED_FLAGS, _flags);
}
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,19 @@ 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))))) {
clapper_features_manager_trigger_item_updated (features_manager, item);
gst_object_unref (item);
if (player->reactables_manager) {
clapper_reactables_manager_trigger_item_updated (player->reactables_manager, item,
CLAPPER_REACTABLE_ITEM_UPDATED_TIMELINE);
}
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 (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

@@ -45,6 +45,7 @@
#include <clapper/clapper-video-stream.h>
#include <clapper/clapper-extractable.h>
#include <clapper/clapper-reactable.h>
#include <clapper/clapper-functionalities-availability.h>
#include <clapper/features/clapper-features-availability.h>

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

@@ -120,6 +120,7 @@ clapper_headers = [
'clapper-media-item.h',
'clapper-player.h',
'clapper-queue.h',
'clapper-reactable.h',
'clapper-stream.h',
'clapper-stream-list.h',
'clapper-subtitle-stream.h',
@@ -147,6 +148,8 @@ clapper_sources = [
'clapper-playbin-bus.c',
'clapper-player.c',
'clapper-queue.c',
'clapper-reactable.c',
'clapper-reactables-manager.c',
'clapper-stream.c',
'clapper-stream-list.c',
'clapper-subtitle-stream.c',