mirror of
https://github.com/Rafostar/clapper.git
synced 2025-09-01 00:41:58 +02:00
Compare commits
18 Commits
harvest-ca
...
reactable
Author | SHA1 | Date | |
---|---|---|---|
|
c354d31436 | ||
|
c0b360dc0f | ||
|
a6ca0b726c | ||
|
976bcc338f | ||
|
6273446817 | ||
|
72ab32d4ef | ||
|
e9d0d8f345 | ||
|
0b8d359844 | ||
|
4a93bea203 | ||
|
5e2c1a8e30 | ||
|
72c8e4ab84 | ||
|
db61b9c773 | ||
|
682ad6c3c8 | ||
|
749796a12f | ||
|
c557c11e86 | ||
|
a2f67a9bc0 | ||
|
ddc0a4d8f9 | ||
|
92e3e686db |
Submodule pkgs/flatpak/flathub updated: f58477c356...b7c25acba8
@@ -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);
|
||||
|
@@ -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");
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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"
|
||||
@@ -213,7 +216,7 @@ clapper_enhancer_proxy_copy (ClapperEnhancerProxy *src_proxy, const gchar *copy_
|
||||
copy->pspecs = g_new (GParamSpec *, copy->n_pspecs);
|
||||
|
||||
for (i = 0; i < src_proxy->n_pspecs; ++i)
|
||||
copy->pspecs[i] = src_proxy->pspecs[i];
|
||||
copy->pspecs[i] = g_param_spec_ref (src_proxy->pspecs[i]);
|
||||
|
||||
copy->scope = CLAPPER_ENHANCER_PARAM_LOCAL;
|
||||
|
||||
@@ -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)) {
|
||||
GValue value = G_VALUE_INIT;
|
||||
|
||||
if (G_LIKELY (clapper_utils_set_value_from_variant (&value, val))) {
|
||||
if (!merged_config)
|
||||
merged_config = gst_structure_new_empty (CONFIG_STRUCTURE_NAME);
|
||||
|
||||
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;
|
||||
}
|
||||
gst_structure_take_value (merged_config, pspec->name, &value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -706,7 +691,7 @@ clapper_enhancer_proxy_get_version (ClapperEnhancerProxy *self)
|
||||
*
|
||||
* Get extra data from enhancer plugin info file specified by @key.
|
||||
*
|
||||
* External data in the plugin info file is prefixed with `X-`.
|
||||
* Extra data in the plugin info file is prefixed with `X-`.
|
||||
* For example `X-Schemes=https`.
|
||||
*
|
||||
* Returns: (nullable): extra data value of the proxied enhancer.
|
||||
@@ -740,6 +725,10 @@ clapper_enhancer_proxy_get_extra_data (ClapperEnhancerProxy *self, const gchar *
|
||||
* calling this function with "X-Schemes" as key and "http" as value will
|
||||
* return %TRUE.
|
||||
*
|
||||
* It is also safe to call this function when there is no such @key
|
||||
* in plugin info file. Use [method@Clapper.EnhancerProxy.get_extra_data]
|
||||
* if you need to know whether key exists.
|
||||
*
|
||||
* Returns: whether list named with @key existed and contained @value.
|
||||
*
|
||||
* Since: 0.10
|
||||
@@ -943,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
|
||||
@@ -1013,9 +1016,9 @@ 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) */
|
||||
|
||||
if (clapper_enhancer_proxy_target_has_interface (self, CLAPPER_TYPE_REACTABLE))
|
||||
_trigger_reactable_configure_take (self, structure);
|
||||
else
|
||||
gst_structure_free (structure);
|
||||
}
|
||||
|
||||
@@ -1080,9 +1083,9 @@ 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) */
|
||||
|
||||
if (clapper_enhancer_proxy_target_has_interface (self, CLAPPER_TYPE_REACTABLE))
|
||||
_trigger_reactable_configure_take (self, structure);
|
||||
else
|
||||
gst_structure_free (structure);
|
||||
}
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
@@ -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)
|
||||
|
||||
/**
|
||||
|
@@ -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
|
||||
|
@@ -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"
|
||||
|
||||
@@ -43,6 +44,7 @@ struct _ClapperMediaItem
|
||||
gchar *uri;
|
||||
gchar *suburi;
|
||||
|
||||
GstTagList *tags;
|
||||
ClapperTimeline *timeline;
|
||||
|
||||
guint id;
|
||||
@@ -56,6 +58,13 @@ struct _ClapperMediaItem
|
||||
gboolean used;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ClapperMediaItem *item;
|
||||
gboolean changed;
|
||||
gboolean from_user;
|
||||
} ClapperMediaItemTagIterData;
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
@@ -63,6 +72,7 @@ enum
|
||||
PROP_URI,
|
||||
PROP_SUBURI,
|
||||
PROP_CACHE_LOCATION,
|
||||
PROP_TAGS,
|
||||
PROP_TITLE,
|
||||
PROP_CONTAINER_FORMAT,
|
||||
PROP_DURATION,
|
||||
@@ -111,8 +121,8 @@ clapper_media_item_new (const gchar *uri)
|
||||
|
||||
/* FIXME: Set initial container format from file extension parsing */
|
||||
|
||||
GST_TRACE_OBJECT (item, "New media item, ID: %u, URI: %s, title: %s",
|
||||
item->id, item->uri, item->title);
|
||||
GST_TRACE_OBJECT (item, "New media item, ID: %u, URI: \"%s\", title: \"%s\"",
|
||||
item->id, item->uri, GST_STR_NULL (item->title));
|
||||
|
||||
return item;
|
||||
}
|
||||
@@ -258,27 +268,6 @@ clapper_media_item_get_suburi (ClapperMediaItem *self)
|
||||
return suburi;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_media_item_take_title (ClapperMediaItem *self, gchar *title,
|
||||
ClapperAppBus *app_bus)
|
||||
{
|
||||
gboolean changed;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
if ((changed = g_strcmp0 (self->title, title) != 0)) {
|
||||
g_free (self->title);
|
||||
self->title = title;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (changed)
|
||||
clapper_app_bus_post_prop_notify (app_bus, GST_OBJECT_CAST (self), param_specs[PROP_TITLE]);
|
||||
else
|
||||
g_free (title);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_media_item_get_title:
|
||||
* @item: a #ClapperMediaItem
|
||||
@@ -305,25 +294,24 @@ clapper_media_item_get_title (ClapperMediaItem *self)
|
||||
return title;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_media_item_take_container_format (ClapperMediaItem *self, gchar *container_format,
|
||||
ClapperAppBus *app_bus)
|
||||
static inline gboolean
|
||||
_refresh_tag_prop_unlocked (ClapperMediaItem *self, const gchar *tag,
|
||||
gboolean from_user, gchar **tag_ptr)
|
||||
{
|
||||
gboolean changed;
|
||||
const gchar *string;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
if ((changed = g_strcmp0 (self->container_format, container_format) != 0)) {
|
||||
g_free (self->container_format);
|
||||
self->container_format = container_format;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
if ((*tag_ptr && from_user) // if already set, user cannot modify it
|
||||
|| !gst_tag_list_peek_string_index (self->tags, tag, 0, &string) // guarantees non-empty string
|
||||
|| (g_strcmp0 (*tag_ptr, string) == 0))
|
||||
return FALSE;
|
||||
|
||||
if (changed)
|
||||
clapper_app_bus_post_prop_notify (app_bus, GST_OBJECT_CAST (self), param_specs[PROP_CONTAINER_FORMAT]);
|
||||
else
|
||||
g_free (container_format);
|
||||
GST_LOG_OBJECT (self, "Tag prop \"%s\" update: \"%s\" -> \"%s\"",
|
||||
tag, GST_STR_NULL (*tag_ptr), string);
|
||||
|
||||
return changed;
|
||||
g_free (*tag_ptr);
|
||||
*tag_ptr = g_strdup (string);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,6 +321,8 @@ clapper_media_item_take_container_format (ClapperMediaItem *self, gchar *contain
|
||||
* Get media item container format.
|
||||
*
|
||||
* Returns: (transfer full) (nullable): media container format.
|
||||
*
|
||||
* Deprecated: 0.10: Get `container-format` from [property@Clapper.MediaItem:tags] instead.
|
||||
*/
|
||||
gchar *
|
||||
clapper_media_item_get_container_format (ClapperMediaItem *self)
|
||||
@@ -389,11 +379,214 @@ clapper_media_item_get_duration (ClapperMediaItem *self)
|
||||
return duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_media_item_get_tags:
|
||||
* @item: a #ClapperMediaItem
|
||||
*
|
||||
* Get readable list of tags stored in media item.
|
||||
*
|
||||
* Returns: (transfer full): a #GstTagList.
|
||||
*
|
||||
* Since: 0.10
|
||||
*/
|
||||
GstTagList *
|
||||
clapper_media_item_get_tags (ClapperMediaItem *self)
|
||||
{
|
||||
GstTagList *tags = NULL;
|
||||
|
||||
g_return_val_if_fail (CLAPPER_IS_MEDIA_ITEM (self), NULL);
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
tags = gst_tag_list_ref (self->tags);
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
return tags;
|
||||
}
|
||||
|
||||
static void
|
||||
_tags_replace_func (const GstTagList *tags, const gchar *tag, ClapperMediaItemTagIterData *data)
|
||||
{
|
||||
ClapperMediaItem *self = data->item;
|
||||
guint index = 0;
|
||||
gboolean replace = FALSE;
|
||||
|
||||
while (TRUE) {
|
||||
const GValue *old_value = gst_tag_list_get_value_index (self->tags, tag, index);
|
||||
const GValue *new_value = gst_tag_list_get_value_index (tags, tag, index);
|
||||
|
||||
/* Number of old values is the same or greater and
|
||||
* all values until this iteration were the same */
|
||||
if (!new_value)
|
||||
break;
|
||||
|
||||
/* A wild new tag appeared */
|
||||
if (!old_value) {
|
||||
replace = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Users can only set non-existing tags */
|
||||
if (data->from_user)
|
||||
break;
|
||||
|
||||
/* Check with tolerance for doubles */
|
||||
if (G_VALUE_TYPE (old_value) == G_TYPE_DOUBLE
|
||||
&& G_VALUE_TYPE (new_value) == G_TYPE_DOUBLE) {
|
||||
gdouble old_dbl, new_dbl;
|
||||
|
||||
old_dbl = g_value_get_double (old_value);
|
||||
new_dbl = g_value_get_double (new_value);
|
||||
|
||||
if ((replace = !G_APPROX_VALUE (old_dbl, new_dbl, FLT_EPSILON)))
|
||||
break;
|
||||
} else if (gst_value_compare (old_value, new_value) != GST_VALUE_EQUAL) {
|
||||
replace = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
if (replace) {
|
||||
const GValue *value;
|
||||
index = 0;
|
||||
|
||||
GST_LOG_OBJECT (self, "Replacing \"%s\" tag value", tag);
|
||||
|
||||
/* Ensure writable, but only when replacing something */
|
||||
if (!data->changed) {
|
||||
self->tags = gst_tag_list_make_writable (self->tags);
|
||||
data->changed = TRUE;
|
||||
}
|
||||
|
||||
/* Replace first tag value (so it becomes sole member) */
|
||||
value = gst_tag_list_get_value_index (tags, tag, index);
|
||||
gst_tag_list_add_value (self->tags, GST_TAG_MERGE_REPLACE, tag, value);
|
||||
|
||||
/* Append any remaining tags (so next time we iterate indexes will match) */
|
||||
while ((value = gst_tag_list_get_value_index (tags, tag, ++index)))
|
||||
gst_tag_list_add_value (self->tags, GST_TAG_MERGE_APPEND, tag, value);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_media_item_insert_tags_internal (ClapperMediaItem *self, const GstTagList *tags,
|
||||
ClapperAppBus *app_bus, gboolean from_user, ClapperReactableItemUpdatedFlags *flags)
|
||||
{
|
||||
ClapperMediaItemTagIterData data;
|
||||
gboolean title_changed = FALSE, cont_changed = FALSE;
|
||||
|
||||
GST_OBJECT_LOCK (self);
|
||||
|
||||
data.item = self;
|
||||
data.changed = FALSE;
|
||||
data.from_user = from_user;
|
||||
|
||||
if (G_LIKELY (tags != self->tags))
|
||||
gst_tag_list_foreach (tags, (GstTagForeachFunc) _tags_replace_func, &data);
|
||||
|
||||
if (data.changed) {
|
||||
*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);
|
||||
}
|
||||
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
||||
if (!data.changed)
|
||||
return FALSE;
|
||||
|
||||
if (app_bus) {
|
||||
GstObject *src = GST_OBJECT_CAST (self);
|
||||
|
||||
clapper_app_bus_post_prop_notify (app_bus, src, param_specs[PROP_TAGS]);
|
||||
|
||||
if (title_changed)
|
||||
clapper_app_bus_post_prop_notify (app_bus, src, param_specs[PROP_TITLE]);
|
||||
if (cont_changed)
|
||||
clapper_app_bus_post_prop_notify (app_bus, src, param_specs[PROP_CONTAINER_FORMAT]);
|
||||
} else {
|
||||
GObject *src = G_OBJECT (self);
|
||||
|
||||
clapper_utils_prop_notify_on_main_sync (src, param_specs[PROP_TAGS]);
|
||||
|
||||
if (title_changed)
|
||||
clapper_utils_prop_notify_on_main_sync (src, param_specs[PROP_TITLE]);
|
||||
if (cont_changed)
|
||||
clapper_utils_prop_notify_on_main_sync (src, param_specs[PROP_CONTAINER_FORMAT]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_media_item_populate_tags:
|
||||
* @item: a #ClapperMediaItem
|
||||
* @tags: a #GstTagList of GLOBAL scope
|
||||
*
|
||||
* Populate non-existing tags in @item tag list.
|
||||
*
|
||||
* Passed @tags must use [enum@Gst.TagScope.GLOBAL] scope.
|
||||
*
|
||||
* Note that tags are automatically determined during media playback
|
||||
* and those take precedence. This function can be useful if an app can
|
||||
* determine some tags that are not in media metadata or for filling
|
||||
* item with some initial/cached tags to display in UI before playback.
|
||||
*
|
||||
* When a tag already exists in the tag list (was populated) this
|
||||
* function will not overwrite it. If you really need to permanently
|
||||
* override some tags in media, you can use `taginject` element as
|
||||
* player video/audio filter instead.
|
||||
*
|
||||
* Returns: whether at least one tag got updated.
|
||||
*
|
||||
* Since: 0.10
|
||||
*/
|
||||
gboolean
|
||||
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);
|
||||
g_return_val_if_fail (tags != NULL, FALSE);
|
||||
|
||||
if (G_UNLIKELY (gst_tag_list_get_scope (tags) != GST_TAG_SCOPE_GLOBAL)) {
|
||||
g_warning ("Cannot populate media item tags using a list with non-global tag scope");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
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, &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);
|
||||
}
|
||||
|
||||
gst_clear_object (&player);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
/**
|
||||
* clapper_media_item_get_timeline:
|
||||
* @item: a #ClapperMediaItem
|
||||
*
|
||||
* Get the [class@Clapper.Timeline] assosiated with @item.
|
||||
* Get the [class@Clapper.Timeline] associated with @item.
|
||||
*
|
||||
* Returns: (transfer none): a #ClapperTimeline of item.
|
||||
*/
|
||||
@@ -405,21 +598,6 @@ clapper_media_item_get_timeline (ClapperMediaItem *self)
|
||||
return self->timeline;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clapper_media_item_update_from_container_tags (ClapperMediaItem *self, const GstTagList *tags,
|
||||
ClapperAppBus *app_bus)
|
||||
{
|
||||
gchar *string = NULL;
|
||||
gboolean changed = FALSE;
|
||||
|
||||
if (gst_tag_list_get_string (tags, GST_TAG_CONTAINER_FORMAT, &string))
|
||||
changed |= clapper_media_item_take_container_format (self, string, app_bus);
|
||||
if (gst_tag_list_get_string (tags, GST_TAG_TITLE, &string))
|
||||
changed |= clapper_media_item_take_title (self, string, app_bus);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void
|
||||
clapper_media_item_update_from_tag_list (ClapperMediaItem *self, const GstTagList *tags,
|
||||
ClapperPlayer *player)
|
||||
@@ -427,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_update_from_container_tags (self, tags, player->app_bus);
|
||||
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);
|
||||
}
|
||||
@@ -444,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;
|
||||
|
||||
@@ -459,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_update_from_container_tags (self, tags, player->app_bus);
|
||||
changed |= clapper_media_item_insert_tags_internal (self, tags, player->app_bus, FALSE, &flags);
|
||||
}
|
||||
gst_discoverer_stream_info_unref (sinfo);
|
||||
}
|
||||
@@ -470,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);
|
||||
}
|
||||
@@ -542,6 +729,9 @@ clapper_media_item_get_used (ClapperMediaItem *self)
|
||||
static void
|
||||
clapper_media_item_init (ClapperMediaItem *self)
|
||||
{
|
||||
self->tags = gst_tag_list_new_empty ();
|
||||
gst_tag_list_set_scope (self->tags, GST_TAG_SCOPE_GLOBAL);
|
||||
|
||||
self->timeline = clapper_timeline_new ();
|
||||
gst_object_set_parent (GST_OBJECT_CAST (self->timeline), GST_OBJECT_CAST (self));
|
||||
}
|
||||
@@ -571,6 +761,8 @@ clapper_media_item_finalize (GObject *object)
|
||||
g_free (self->title);
|
||||
g_free (self->container_format);
|
||||
|
||||
gst_tag_list_unref (self->tags);
|
||||
|
||||
gst_object_unparent (GST_OBJECT_CAST (self->timeline));
|
||||
gst_object_unref (self->timeline);
|
||||
|
||||
@@ -617,6 +809,9 @@ clapper_media_item_get_property (GObject *object, guint prop_id,
|
||||
case PROP_SUBURI:
|
||||
g_value_take_string (value, clapper_media_item_get_suburi (self));
|
||||
break;
|
||||
case PROP_TAGS:
|
||||
g_value_take_boxed (value, clapper_media_item_get_tags (self));
|
||||
break;
|
||||
case PROP_TITLE:
|
||||
g_value_take_string (value, clapper_media_item_get_title (self));
|
||||
break;
|
||||
@@ -686,10 +881,27 @@ clapper_media_item_class_init (ClapperMediaItemClass *klass)
|
||||
NULL, NULL, NULL,
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* ClapperMediaItem:tags:
|
||||
*
|
||||
* A readable list of tags stored in media item.
|
||||
*
|
||||
* Since: 0.10
|
||||
*/
|
||||
param_specs[PROP_TAGS] = g_param_spec_boxed ("tags",
|
||||
NULL, NULL, GST_TYPE_TAG_LIST,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/* FIXME: 1.0: Consider rename to e.g. "(menu/display)-title"
|
||||
* and also make it non-nullable (return URI as final fallback) */
|
||||
/**
|
||||
* ClapperMediaItem:title:
|
||||
*
|
||||
* Media title.
|
||||
*
|
||||
* This might be a different string compared to `title` from
|
||||
* [property@Clapper.MediaItem:tags], as this gives parsed
|
||||
* title from file name/URI as fallback when no `title` tag.
|
||||
*/
|
||||
param_specs[PROP_TITLE] = g_param_spec_string ("title",
|
||||
NULL, NULL, NULL,
|
||||
@@ -699,15 +911,21 @@ clapper_media_item_class_init (ClapperMediaItemClass *klass)
|
||||
* ClapperMediaItem:container-format:
|
||||
*
|
||||
* Media container format.
|
||||
*
|
||||
* Deprecated: 0.10: Get `container-format` from [property@Clapper.MediaItem:tags] instead.
|
||||
*/
|
||||
param_specs[PROP_CONTAINER_FORMAT] = g_param_spec_string ("container-format",
|
||||
NULL, NULL, NULL,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED);
|
||||
|
||||
/**
|
||||
* ClapperMediaItem:duration:
|
||||
*
|
||||
* Media duration as a decimal number in seconds.
|
||||
*
|
||||
* This might be a different value compared to `duration` from
|
||||
* [property@Clapper.MediaItem:tags], as this value is updated
|
||||
* during decoding instead of being a fixed value from metadata.
|
||||
*/
|
||||
param_specs[PROP_DURATION] = g_param_spec_double ("duration",
|
||||
NULL, NULL, 0, G_MAXDOUBLE, 0,
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include <glib-object.h>
|
||||
#include <gio/gio.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/tag/tag.h>
|
||||
|
||||
#include <clapper/clapper-visibility.h>
|
||||
#include <clapper/clapper-timeline.h>
|
||||
@@ -63,12 +64,18 @@ gchar * clapper_media_item_get_suburi (ClapperMediaItem *item);
|
||||
CLAPPER_API
|
||||
gchar * clapper_media_item_get_title (ClapperMediaItem *item);
|
||||
|
||||
CLAPPER_API
|
||||
CLAPPER_DEPRECATED_FOR(clapper_media_item_get_tags)
|
||||
gchar * clapper_media_item_get_container_format (ClapperMediaItem *item);
|
||||
|
||||
CLAPPER_API
|
||||
gdouble clapper_media_item_get_duration (ClapperMediaItem *item);
|
||||
|
||||
CLAPPER_API
|
||||
GstTagList * clapper_media_item_get_tags (ClapperMediaItem *item);
|
||||
|
||||
CLAPPER_API
|
||||
gboolean clapper_media_item_populate_tags (ClapperMediaItem *item, const GstTagList *tags);
|
||||
|
||||
CLAPPER_API
|
||||
ClapperTimeline * clapper_media_item_get_timeline (ClapperMediaItem *item);
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
||||
@@ -2833,7 +2856,7 @@ clapper_player_class_init (ClapperPlayerClass *klass)
|
||||
* An initial bitrate (bits/s) to select during
|
||||
* starting adaptive streaming such as DASH or HLS.
|
||||
*
|
||||
* If value is higher than lowest available bitrate in streaming
|
||||
* If value is lower than the lowest available bitrate in streaming
|
||||
* manifest, then lowest possible bitrate will be selected.
|
||||
*
|
||||
* Since: 0.8
|
||||
|
@@ -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,16 +134,28 @@ _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
|
||||
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
|
||||
} 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
|
||||
} 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
|
||||
} 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);
|
||||
|
||||
|
210
src/lib/clapper/clapper-reactable.c
Normal file
210
src/lib/clapper/clapper-reactable.c
Normal 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);
|
||||
});
|
||||
}
|
229
src/lib/clapper/clapper-reactable.h
Normal file
229
src/lib/clapper/clapper-reactable.h
Normal 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
|
82
src/lib/clapper/clapper-reactables-manager-private.h
Normal file
82
src/lib/clapper/clapper-reactables-manager-private.h
Normal 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
|
534
src/lib/clapper/clapper-reactables-manager.c
Normal file
534
src/lib/clapper/clapper-reactables-manager.c
Normal 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;
|
||||
}
|
@@ -52,7 +52,7 @@ G_DEFINE_TYPE_WITH_PRIVATE (ClapperThreadedObject, clapper_threaded_object, GST_
|
||||
* Useful when you want to invoke object thread to do some
|
||||
* action in it from a different thread.
|
||||
*
|
||||
* Returns: a #GMainContext of the object used thread.
|
||||
* Returns: (transfer none): a #GMainContext of the object used thread.
|
||||
*/
|
||||
GMainContext *
|
||||
clapper_threaded_object_get_context (ClapperThreadedObject *self)
|
||||
|
@@ -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;
|
||||
|
||||
if ((features_manager = clapper_player_get_features_manager (player))) {
|
||||
ClapperMediaItem *item;
|
||||
|
||||
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 ((item = CLAPPER_MEDIA_ITEM_CAST (gst_object_get_parent (GST_OBJECT_CAST (self))))) {
|
||||
ClapperFeaturesManager *features_manager;
|
||||
|
||||
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);
|
||||
|
@@ -45,10 +45,16 @@ void clapper_utils_queue_remove_on_main_sync (ClapperQueue *queue, ClapperMediaI
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_utils_queue_clear_on_main_sync (ClapperQueue *queue);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
void clapper_utils_prop_notify_on_main_sync (GObject *object, GParamSpec *pspec);
|
||||
|
||||
G_GNUC_INTERNAL
|
||||
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
|
||||
|
@@ -39,6 +39,12 @@ typedef struct
|
||||
ClapperUtilsQueueAlterMethod method;
|
||||
} ClapperUtilsQueueAlterData;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GObject *object;
|
||||
GParamSpec *pspec;
|
||||
} ClapperUtilsPropNotifyData;
|
||||
|
||||
void
|
||||
clapper_utils_initialize (void)
|
||||
{
|
||||
@@ -71,6 +77,27 @@ clapper_utils_queue_alter_data_free (ClapperUtilsQueueAlterData *data)
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static ClapperUtilsPropNotifyData *
|
||||
clapper_utils_prop_notify_data_new (GObject *object, GParamSpec *pspec)
|
||||
{
|
||||
ClapperUtilsPropNotifyData *data = g_new (ClapperUtilsPropNotifyData, 1);
|
||||
|
||||
data->object = object;
|
||||
data->pspec = pspec;
|
||||
|
||||
GST_TRACE ("Created prop notify data: %p", data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void
|
||||
clapper_utils_prop_notify_data_free (ClapperUtilsPropNotifyData *data)
|
||||
{
|
||||
GST_TRACE ("Freeing prop notify data: %p", data);
|
||||
|
||||
g_free (data);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
clapper_utils_queue_alter_on_main (ClapperUtilsQueueAlterData *data)
|
||||
{
|
||||
@@ -110,6 +137,15 @@ clapper_utils_queue_alter_on_main (ClapperUtilsQueueAlterData *data)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gpointer
|
||||
clapper_utils_prop_notify_on_main (ClapperUtilsPropNotifyData *data)
|
||||
{
|
||||
GST_DEBUG ("Prop notify invoked");
|
||||
g_object_notify_by_pspec (data->object, data->pspec);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
clapper_utils_queue_alter_invoke_on_main_sync_take (ClapperUtilsQueueAlterData *data)
|
||||
{
|
||||
@@ -155,6 +191,27 @@ clapper_utils_queue_clear_on_main_sync (ClapperQueue *queue)
|
||||
clapper_utils_queue_alter_invoke_on_main_sync_take (data);
|
||||
}
|
||||
|
||||
void
|
||||
clapper_utils_prop_notify_on_main_sync (GObject *object, GParamSpec *pspec)
|
||||
{
|
||||
ClapperUtilsPropNotifyData *data;
|
||||
|
||||
if (g_main_context_is_owner (g_main_context_default ())) { // already in main thread
|
||||
g_object_notify_by_pspec (object, pspec);
|
||||
return;
|
||||
}
|
||||
|
||||
data = clapper_utils_prop_notify_data_new (object, pspec);
|
||||
|
||||
GST_DEBUG ("Invoking prop notify on main...");
|
||||
|
||||
clapper_shared_utils_context_invoke_sync_full (g_main_context_default (),
|
||||
(GThreadFunc) clapper_utils_prop_notify_on_main, data,
|
||||
(GDestroyNotify) clapper_utils_prop_notify_data_free);
|
||||
|
||||
GST_DEBUG ("Prop notify invoke finished");
|
||||
}
|
||||
|
||||
gchar *
|
||||
clapper_utils_uri_from_file (GFile *file)
|
||||
{
|
||||
@@ -214,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;
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -95,6 +95,7 @@ clapper_enhancer_director_extract_in_thread (ClapperEnhancerDirectorData *data)
|
||||
#endif
|
||||
|
||||
if (G_LIKELY (extractable != NULL)) {
|
||||
if (config)
|
||||
clapper_enhancer_proxy_apply_config_to_enhancer (proxy, config, (GObject *) extractable);
|
||||
|
||||
success = clapper_extractable_extract (extractable, data->uri,
|
||||
|
@@ -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);
|
||||
|
@@ -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',
|
||||
@@ -235,6 +238,7 @@ if build_gir
|
||||
clapper_enums,
|
||||
],
|
||||
extra_args: [
|
||||
gir_init_section,
|
||||
'--quiet',
|
||||
'--warn-all',
|
||||
'-DCLAPPER_COMPILATION',
|
||||
|
@@ -1,6 +1,6 @@
|
||||
// Skipped by GI, but Vala can handle it fine
|
||||
//init_get_option_group skip=false
|
||||
*_FORMAT skip=false
|
||||
*.peek_* skip=false
|
||||
|
||||
// Init func compatibility
|
||||
init.argv unowned
|
||||
|
@@ -594,11 +594,14 @@ static gboolean
|
||||
gst_clapper_sink_start (GstBaseSink *bsink)
|
||||
{
|
||||
GstClapperSink *self = GST_CLAPPER_SINK_CAST (bsink);
|
||||
gboolean with_clapper_gtk;
|
||||
|
||||
GST_INFO_OBJECT (self, "Start");
|
||||
|
||||
if (G_UNLIKELY (!(! !gst_gtk_invoke_on_main ((GThreadFunc) (GCallback)
|
||||
gst_clapper_sink_start_on_main, self)))) {
|
||||
with_clapper_gtk = g_type_from_name ("ClapperGtkVideo");
|
||||
|
||||
if (G_UNLIKELY (!with_clapper_gtk && !(! !gst_gtk_invoke_on_main (
|
||||
(GThreadFunc) (GCallback) gst_clapper_sink_start_on_main, self)))) {
|
||||
GST_ELEMENT_ERROR (self, RESOURCE, NOT_FOUND,
|
||||
("GtkWidget could not be created"), (NULL));
|
||||
|
||||
|
@@ -4,6 +4,13 @@ build_gir = (gir.found() and not get_option('introspection').disabled())
|
||||
vapigen = find_program('vapigen', required: get_option('vapi'))
|
||||
build_vapi = (vapigen.found() and not get_option('vapi').disabled())
|
||||
|
||||
gir_init_section = '--add-init-section=extern void gst_init(gint*,gchar**);' + \
|
||||
'g_setenv("GST_REGISTRY_DISABLE", "yes", TRUE);' + \
|
||||
'g_setenv("GST_REGISTRY_1_0", "@0@", TRUE);'.format(meson.current_build_dir() + '/gir_empty_registry.reg') + \
|
||||
'g_setenv("GST_PLUGIN_PATH_1_0", "", TRUE);' + \
|
||||
'g_setenv("GST_PLUGIN_SYSTEM_PATH_1_0", "", TRUE);' + \
|
||||
'gst_init(NULL,NULL);'
|
||||
|
||||
subdir('gst')
|
||||
subdir('clapper')
|
||||
subdir('clapper-gtk')
|
||||
|
@@ -41,5 +41,11 @@
|
||||
#endif
|
||||
|
||||
#define @CLAPPER_API@_API _@CLAPPER_API@_VISIBILITY
|
||||
|
||||
#if !defined(@CLAPPER_API@_COMPILATION)
|
||||
#define @CLAPPER_API@_DEPRECATED G_DEPRECATED _@CLAPPER_API@_VISIBILITY
|
||||
#define @CLAPPER_API@_DEPRECATED_FOR(f) G_DEPRECATED_FOR(f) _@CLAPPER_API@_VISIBILITY
|
||||
#else
|
||||
#define @CLAPPER_API@_DEPRECATED _@CLAPPER_API@_VISIBILITY
|
||||
#define @CLAPPER_API@_DEPRECATED_FOR(f) _@CLAPPER_API@_VISIBILITY
|
||||
#endif
|
||||
|
Reference in New Issue
Block a user