mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-28 00:46:37 +00:00
[Keyframes] Adds API to set a control binding on a track element, and the serialization code.
This commit is contained in:
parent
811e68811c
commit
e655a75605
7 changed files with 486 additions and 1 deletions
|
@ -96,6 +96,9 @@ struct _GESBaseXmlFormatterPrivate
|
||||||
|
|
||||||
/* List of asset waited to be created */
|
/* List of asset waited to be created */
|
||||||
GList *pending_assets;
|
GList *pending_assets;
|
||||||
|
|
||||||
|
/* current track element */
|
||||||
|
GESTrackElement *current_track_element;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -339,6 +342,7 @@ ges_base_xml_formatter_init (GESBaseXmlFormatter * self)
|
||||||
g_str_equal, g_free, gst_object_unref);
|
g_str_equal, g_free, gst_object_unref);
|
||||||
priv->layers = g_hash_table_new_full (g_direct_hash,
|
priv->layers = g_hash_table_new_full (g_direct_hash,
|
||||||
g_direct_equal, NULL, (GDestroyNotify) _free_layer_entry);
|
g_direct_equal, NULL, (GDestroyNotify) _free_layer_entry);
|
||||||
|
priv->current_track_element = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -828,6 +832,35 @@ ges_base_xml_formatter_add_track (GESBaseXmlFormatter * self,
|
||||||
metadatas);
|
metadatas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ges_base_xml_formatter_add_control_binding (GESBaseXmlFormatter * self,
|
||||||
|
const gchar * binding_type, const gchar * source_type,
|
||||||
|
const gchar * property_name, gint mode, GSList * timed_values)
|
||||||
|
{
|
||||||
|
GESBaseXmlFormatterPrivate *priv = _GET_PRIV (self);
|
||||||
|
GESTrackElement *element;
|
||||||
|
|
||||||
|
element = priv->current_track_element;
|
||||||
|
if (element == NULL) {
|
||||||
|
GST_WARNING ("No current track element to which we can append a binding");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_strcmp0 (source_type, "interpolation")) {
|
||||||
|
GstControlSource *source;
|
||||||
|
|
||||||
|
source = gst_interpolation_control_source_new ();
|
||||||
|
ges_track_element_set_property_controlling_parameters (element, source,
|
||||||
|
property_name, binding_type);
|
||||||
|
|
||||||
|
g_object_set (source, "mode", mode, NULL);
|
||||||
|
|
||||||
|
gst_timed_value_control_source_set_from_list (GST_TIMED_VALUE_CONTROL_SOURCE
|
||||||
|
(source), timed_values);
|
||||||
|
} else
|
||||||
|
GST_WARNING ("This interpolation type is not supported\n");
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ges_base_xml_formatter_add_track_element (GESBaseXmlFormatter * self,
|
ges_base_xml_formatter_add_track_element (GESBaseXmlFormatter * self,
|
||||||
GType track_element_type, const gchar * asset_id, const gchar * track_id,
|
GType track_element_type, const gchar * asset_id, const gchar * track_id,
|
||||||
|
@ -895,6 +928,7 @@ ges_base_xml_formatter_add_track_element (GESBaseXmlFormatter * self,
|
||||||
|
|
||||||
pend->effects = g_list_append (pend->effects, peffect);
|
pend->effects = g_list_append (pend->effects, peffect);
|
||||||
}
|
}
|
||||||
|
priv->current_track_element = trackelement;
|
||||||
}
|
}
|
||||||
|
|
||||||
ges_project_add_asset (GES_FORMATTER (self)->project, asset);
|
ges_project_add_asset (GES_FORMATTER (self)->project, asset);
|
||||||
|
|
|
@ -89,6 +89,9 @@ ges_asset_cache_put (GESAsset * asset, GSimpleAsyncResult *res);
|
||||||
G_GNUC_INTERNAL gboolean
|
G_GNUC_INTERNAL gboolean
|
||||||
ges_asset_cache_set_loaded(GType extractable_type, const gchar * id, GError *error);
|
ges_asset_cache_set_loaded(GType extractable_type, const gchar * id, GError *error);
|
||||||
|
|
||||||
|
G_GNUC_INTERNAL GHashTable *
|
||||||
|
ges_track_element_get_bindings_hashtable(GESTrackElement *element);
|
||||||
|
|
||||||
GESAsset*
|
GESAsset*
|
||||||
ges_asset_cache_lookup(GType extractable_type, const gchar * id);
|
ges_asset_cache_lookup(GType extractable_type, const gchar * id);
|
||||||
|
|
||||||
|
@ -214,6 +217,14 @@ G_GNUC_INTERNAL void ges_base_xml_formatter_add_track_element (GESBaseXmlForm
|
||||||
GstStructure *properties,
|
GstStructure *properties,
|
||||||
const gchar *metadatas,
|
const gchar *metadatas,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
|
G_GNUC_INTERNAL void ges_base_xml_formatter_add_control_binding(GESBaseXmlFormatter * self,
|
||||||
|
const gchar * binding_type,
|
||||||
|
const gchar * source_type,
|
||||||
|
const gchar * property_name,
|
||||||
|
gint mode,
|
||||||
|
GSList * timed_values);
|
||||||
|
|
||||||
G_GNUC_INTERNAL void set_property_foreach (GQuark field_id,
|
G_GNUC_INTERNAL void set_property_foreach (GQuark field_id,
|
||||||
const GValue * value,
|
const GValue * value,
|
||||||
GObject * object);;
|
GObject * object);;
|
||||||
|
|
|
@ -66,6 +66,9 @@ struct _GESTrackElementPrivate
|
||||||
|
|
||||||
gboolean locked; /* If TRUE, then moves in sync with its controlling
|
gboolean locked; /* If TRUE, then moves in sync with its controlling
|
||||||
* GESClip */
|
* GESClip */
|
||||||
|
|
||||||
|
GHashTable *bindings_hashtable; /* We need this if we want to be able to serialize
|
||||||
|
and deserialize keyframes */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
|
@ -181,6 +184,9 @@ ges_track_element_dispose (GObject * object)
|
||||||
if (priv->properties_hashtable)
|
if (priv->properties_hashtable)
|
||||||
g_hash_table_destroy (priv->properties_hashtable);
|
g_hash_table_destroy (priv->properties_hashtable);
|
||||||
|
|
||||||
|
if (priv->bindings_hashtable)
|
||||||
|
g_hash_table_destroy (priv->bindings_hashtable);
|
||||||
|
|
||||||
if (priv->gnlobject) {
|
if (priv->gnlobject) {
|
||||||
GstState cstate;
|
GstState cstate;
|
||||||
|
|
||||||
|
@ -301,6 +307,8 @@ ges_track_element_init (GESTrackElement * self)
|
||||||
priv->pending_active = TRUE;
|
priv->pending_active = TRUE;
|
||||||
priv->locked = TRUE;
|
priv->locked = TRUE;
|
||||||
priv->properties_hashtable = NULL;
|
priv->properties_hashtable = NULL;
|
||||||
|
priv->bindings_hashtable = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||||
|
g_free, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -748,6 +756,14 @@ ges_track_element_set_track (GESTrackElement * object, GESTrack * track)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GHashTable *
|
||||||
|
ges_track_element_get_bindings_hashtable (GESTrackElement * trackelement)
|
||||||
|
{
|
||||||
|
GESTrackElementPrivate *priv = GES_TRACK_ELEMENT (trackelement)->priv;
|
||||||
|
|
||||||
|
return priv->bindings_hashtable;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ges_track_element_get_track:
|
* ges_track_element_get_track:
|
||||||
* @object: a #GESTrackElement
|
* @object: a #GESTrackElement
|
||||||
|
@ -1449,3 +1465,99 @@ ges_track_element_edit (GESTrackElement * object,
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ges_track_element_set_property_controlling_parameters:
|
||||||
|
* @object: the #GESTrackElement on which to set a control binding
|
||||||
|
* @source: (element-type GstControlSource): the #GstControlSource to set on the binding.
|
||||||
|
* @property_name: The name of the property to control.
|
||||||
|
* @binding_type: The type of binding to create. Only "direct" is available for now.
|
||||||
|
*
|
||||||
|
* Creates a #GstControlBinding and adds it to the #GstElement concerned by the
|
||||||
|
* property. Use the same syntax as #ges_track_element_lookup_child for
|
||||||
|
* the property name.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the binding could be created and added, %FALSE if an error
|
||||||
|
* occured
|
||||||
|
*
|
||||||
|
* Since: 1.0.XX
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
ges_track_element_set_property_controlling_parameters (GESTrackElement * object,
|
||||||
|
GstControlSource * source,
|
||||||
|
const gchar * property_name, const gchar * binding_type)
|
||||||
|
{
|
||||||
|
GESTrackElementPrivate *priv;
|
||||||
|
GstElement *element;
|
||||||
|
GParamSpec *pspec;
|
||||||
|
GstControlBinding *binding;
|
||||||
|
|
||||||
|
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
|
||||||
|
priv = GES_TRACK_ELEMENT (object)->priv;
|
||||||
|
|
||||||
|
if (G_UNLIKELY (!(GST_IS_CONTROL_SOURCE (source)))) {
|
||||||
|
GST_WARNING
|
||||||
|
("You need to provide a non-null control source to build a new control binding");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ges_track_element_lookup_child (object, property_name, &element, &pspec)) {
|
||||||
|
GST_WARNING ("You need to provide a valid and controllable property name");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO : update this according to new types of bindings */
|
||||||
|
if (!g_strcmp0 (binding_type, "direct")) {
|
||||||
|
/* First remove existing binding */
|
||||||
|
binding =
|
||||||
|
(GstControlBinding *) g_hash_table_lookup (priv->bindings_hashtable,
|
||||||
|
property_name);
|
||||||
|
if (binding) {
|
||||||
|
GST_LOG ("Removing old binding %p for property %s", binding,
|
||||||
|
property_name);
|
||||||
|
gst_object_remove_control_binding (GST_OBJECT (element), binding);
|
||||||
|
}
|
||||||
|
binding =
|
||||||
|
gst_direct_control_binding_new (GST_OBJECT (element), property_name,
|
||||||
|
source);
|
||||||
|
gst_object_add_control_binding (GST_OBJECT (element), binding);
|
||||||
|
g_hash_table_insert (priv->bindings_hashtable, g_strdup (property_name),
|
||||||
|
binding);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_WARNING ("Binding type must be in [direct]");
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ges_track_element_get_control_binding:
|
||||||
|
* @object: the #GESTrackElement in which to lookup the bindings.
|
||||||
|
* @property_name: The property_name to which the binding is associated.
|
||||||
|
*
|
||||||
|
* Looks up the various controlled properties for that #GESTrackElement,
|
||||||
|
* and returns the #GstControlBinding which controls @property_name.
|
||||||
|
*
|
||||||
|
* Returns: the #GstControlBinding associated with @property_name, or %NULL
|
||||||
|
* if that property is not controlled.
|
||||||
|
*
|
||||||
|
* Since: 1.0.XX
|
||||||
|
*/
|
||||||
|
GstControlBinding *
|
||||||
|
ges_track_element_get_control_binding (GESTrackElement * object,
|
||||||
|
const gchar * property_name)
|
||||||
|
{
|
||||||
|
GESTrackElementPrivate *priv;
|
||||||
|
GstControlBinding *binding;
|
||||||
|
|
||||||
|
g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
|
||||||
|
|
||||||
|
priv = GES_TRACK_ELEMENT (object)->priv;
|
||||||
|
|
||||||
|
binding =
|
||||||
|
(GstControlBinding *) g_hash_table_lookup (priv->bindings_hashtable,
|
||||||
|
property_name);
|
||||||
|
return binding;
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
#include <ges/ges-types.h>
|
#include <ges/ges-types.h>
|
||||||
#include <ges/ges-clip.h>
|
#include <ges/ges-clip.h>
|
||||||
#include <ges/ges-track.h>
|
#include <ges/ges-track.h>
|
||||||
|
#include <gst/controller/gstdirectcontrolbinding.h>
|
||||||
|
#include <gst/controller/gstinterpolationcontrolsource.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
@ -184,5 +186,15 @@ ges_track_element_edit (GESTrackElement * object,
|
||||||
GList *layers, GESEditMode mode,
|
GList *layers, GESEditMode mode,
|
||||||
GESEdge edge, guint64 position);
|
GESEdge edge, guint64 position);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
ges_track_element_set_property_controlling_parameters(GESTrackElement *object,
|
||||||
|
GstControlSource *source,
|
||||||
|
const gchar *property_name,
|
||||||
|
const gchar *binding_type);
|
||||||
|
|
||||||
|
GstControlBinding *
|
||||||
|
ges_track_element_get_control_binding (GESTrackElement *object,
|
||||||
|
const gchar *property_name);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
#endif /* _GES_TRACK_ELEMENT */
|
#endif /* _GES_TRACK_ELEMENT */
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
/* TODO Determine error codes numbers */
|
/* TODO Determine error codes numbers */
|
||||||
|
|
||||||
#include "ges.h"
|
#include "ges.h"
|
||||||
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include "ges-internal.h"
|
#include "ges-internal.h"
|
||||||
|
|
||||||
|
@ -513,6 +514,54 @@ wrong_type:
|
||||||
"element '%s', %s not a GESClip'", element_name, strtype);
|
"element '%s', %s not a GESClip'", element_name, strtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
_parse_binding (GMarkupParseContext * context, const gchar * element_name,
|
||||||
|
const gchar ** attribute_names, const gchar ** attribute_values,
|
||||||
|
GESXmlFormatter * self, GError ** error)
|
||||||
|
{
|
||||||
|
const gchar *type = NULL, *source_type = NULL, *timed_values =
|
||||||
|
NULL, *property_name = NULL, *mode = NULL;
|
||||||
|
gchar **pairs, **tmp;
|
||||||
|
gchar *pair;
|
||||||
|
GSList *list = NULL;
|
||||||
|
|
||||||
|
if (!g_markup_collect_attributes (element_name, attribute_names,
|
||||||
|
attribute_values, error,
|
||||||
|
G_MARKUP_COLLECT_STRING, "type", &type,
|
||||||
|
G_MARKUP_COLLECT_STRING, "source_type", &source_type,
|
||||||
|
G_MARKUP_COLLECT_STRING, "property", &property_name,
|
||||||
|
G_MARKUP_COLLECT_STRING, "mode", &mode,
|
||||||
|
G_MARKUP_COLLECT_STRING, "values", &timed_values,
|
||||||
|
G_MARKUP_COLLECT_INVALID)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pairs = g_strsplit (timed_values, " ", 0);
|
||||||
|
for (tmp = pairs; tmp != NULL; tmp += 1) {
|
||||||
|
gchar **value_pair;
|
||||||
|
|
||||||
|
pair = *tmp;
|
||||||
|
if (pair == NULL)
|
||||||
|
break;
|
||||||
|
if (strlen (pair)) {
|
||||||
|
GstTimedValue *value;
|
||||||
|
|
||||||
|
value = g_slice_new (GstTimedValue);
|
||||||
|
value_pair = g_strsplit (pair, ":", 0);
|
||||||
|
value->timestamp = g_ascii_strtoull (value_pair[0], NULL, 10);
|
||||||
|
value->value = g_ascii_strtod (value_pair[1], NULL);
|
||||||
|
list = g_slist_append (list, value);
|
||||||
|
g_strfreev (value_pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev (pairs);
|
||||||
|
ges_base_xml_formatter_add_control_binding (GES_BASE_XML_FORMATTER (self),
|
||||||
|
type,
|
||||||
|
source_type,
|
||||||
|
property_name, (gint) g_ascii_strtoll (mode, NULL, 10), list);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
_parse_effect (GMarkupParseContext * context, const gchar * element_name,
|
_parse_effect (GMarkupParseContext * context, const gchar * element_name,
|
||||||
const gchar ** attribute_names, const gchar ** attribute_values,
|
const gchar ** attribute_names, const gchar ** attribute_values,
|
||||||
|
@ -623,6 +672,9 @@ _parse_element_start (GMarkupParseContext * context, const gchar * element_name,
|
||||||
else if (g_strcmp0 (element_name, "effect") == 0)
|
else if (g_strcmp0 (element_name, "effect") == 0)
|
||||||
_parse_effect (context, element_name, attribute_names,
|
_parse_effect (context, element_name, attribute_names,
|
||||||
attribute_values, self, error);
|
attribute_values, self, error);
|
||||||
|
else if (g_strcmp0 (element_name, "binding") == 0)
|
||||||
|
_parse_binding (context, element_name, attribute_names,
|
||||||
|
attribute_values, self, error);
|
||||||
else
|
else
|
||||||
GST_LOG_OBJECT (self, "Element %s not handled", element_name);
|
GST_LOG_OBJECT (self, "Element %s not handled", element_name);
|
||||||
}
|
}
|
||||||
|
@ -767,6 +819,56 @@ _save_tracks (GString * str, GESTimeline * timeline)
|
||||||
g_list_free_full (tracks, gst_object_unref);
|
g_list_free_full (tracks, gst_object_unref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO : Use this function for every track element with controllable properties */
|
||||||
|
static inline void
|
||||||
|
_save_keyframes (GString * str, GESTrackElement * trackelement)
|
||||||
|
{
|
||||||
|
GHashTable *bindings_hashtable;
|
||||||
|
GHashTableIter iter;
|
||||||
|
gpointer key, value;
|
||||||
|
|
||||||
|
bindings_hashtable = ges_track_element_get_bindings_hashtable (trackelement);
|
||||||
|
|
||||||
|
g_hash_table_iter_init (&iter, bindings_hashtable);
|
||||||
|
|
||||||
|
/* We iterate over the bindings, and save the timed values */
|
||||||
|
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
||||||
|
if (GST_IS_DIRECT_CONTROL_BINDING ((GstControlBinding *) value)) {
|
||||||
|
GstControlSource *source;
|
||||||
|
GstDirectControlBinding *binding;
|
||||||
|
|
||||||
|
binding = (GstDirectControlBinding *) value;
|
||||||
|
|
||||||
|
g_object_get (binding, "control-source", &source, NULL);
|
||||||
|
|
||||||
|
if (GST_IS_INTERPOLATION_CONTROL_SOURCE (source)) {
|
||||||
|
GList *timed_values, *tmp;
|
||||||
|
GstInterpolationMode mode;
|
||||||
|
|
||||||
|
append_printf_escaped (str,
|
||||||
|
"<binding type='direct' source_type='interpolation' property='%s'",
|
||||||
|
(gchar *) key);
|
||||||
|
g_object_get (source, "mode", &mode, NULL);
|
||||||
|
append_printf_escaped (str, " mode='%d'", mode);
|
||||||
|
append_printf_escaped (str, " values ='");
|
||||||
|
timed_values =
|
||||||
|
gst_timed_value_control_source_get_all
|
||||||
|
(GST_TIMED_VALUE_CONTROL_SOURCE (source));
|
||||||
|
for (tmp = timed_values; tmp; tmp = tmp->next) {
|
||||||
|
GstTimedValue *value;
|
||||||
|
|
||||||
|
value = (GstTimedValue *) tmp->data;
|
||||||
|
append_printf_escaped (str, " %lld:%f ",
|
||||||
|
(long long int) value->timestamp, value->value);
|
||||||
|
}
|
||||||
|
append_printf_escaped (str, "'/>\n");
|
||||||
|
} else
|
||||||
|
GST_DEBUG ("control source not in [interpolation]");
|
||||||
|
} else
|
||||||
|
GST_DEBUG ("Binding type not in [direct]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
_save_effect (GString * str, guint clip_id, GESTrackElement * trackelement,
|
_save_effect (GString * str, guint clip_id, GESTrackElement * trackelement,
|
||||||
GESTimeline * timeline)
|
GESTimeline * timeline)
|
||||||
|
@ -822,8 +924,12 @@ _save_effect (GString * str, guint clip_id, GESTrackElement * trackelement,
|
||||||
}
|
}
|
||||||
g_free (pspecs);
|
g_free (pspecs);
|
||||||
|
|
||||||
append_printf_escaped (str, " children-properties='%s'/>\n",
|
append_printf_escaped (str, " children-properties='%s'>\n",
|
||||||
gst_structure_to_string (structure));
|
gst_structure_to_string (structure));
|
||||||
|
|
||||||
|
_save_keyframes (str, trackelement);
|
||||||
|
|
||||||
|
append_printf_escaped (str, "</effect>\n");
|
||||||
gst_structure_free (structure);
|
gst_structure_free (structure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#include "test-utils.h"
|
#include "test-utils.h"
|
||||||
#include <ges/ges.h>
|
#include <ges/ges.h>
|
||||||
#include <gst/check/gstcheck.h>
|
#include <gst/check/gstcheck.h>
|
||||||
|
#include <gst/controller/gstdirectcontrolbinding.h>
|
||||||
|
#include <gst/controller/gstinterpolationcontrolsource.h>
|
||||||
|
|
||||||
static void
|
static void
|
||||||
project_loaded_cb (GESProject * project, GESTimeline * timeline,
|
project_loaded_cb (GESProject * project, GESTimeline * timeline,
|
||||||
|
@ -270,6 +272,190 @@ _test_project (GESProject * project, GESTimeline * timeline)
|
||||||
assert_equals_int (g_list_length ((GList *) profiles), 2);
|
assert_equals_int (g_list_length ((GList *) profiles), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_add_keyframes (GESTimeline * timeline)
|
||||||
|
{
|
||||||
|
GList *tracks;
|
||||||
|
GList *tmp;
|
||||||
|
|
||||||
|
tracks = ges_timeline_get_tracks (timeline);
|
||||||
|
for (tmp = tracks; tmp; tmp = tmp->next) {
|
||||||
|
GESTrack *track;
|
||||||
|
GList *track_elements;
|
||||||
|
GList *tmp_tck;
|
||||||
|
|
||||||
|
track = GES_TRACK (tmp->data);
|
||||||
|
switch (track->type) {
|
||||||
|
case GES_TRACK_TYPE_VIDEO:
|
||||||
|
track_elements = ges_track_get_elements (track);
|
||||||
|
|
||||||
|
for (tmp_tck = track_elements; tmp_tck; tmp_tck = tmp_tck->next) {
|
||||||
|
GESTrackElement *element = GES_TRACK_ELEMENT (tmp_tck->data);
|
||||||
|
|
||||||
|
if (GES_IS_EFFECT (element)) {
|
||||||
|
GstControlSource *source;
|
||||||
|
GstControlBinding *tmp_binding, *binding;
|
||||||
|
|
||||||
|
source = gst_interpolation_control_source_new ();
|
||||||
|
|
||||||
|
/* Check binding creation and replacement */
|
||||||
|
binding =
|
||||||
|
ges_track_element_get_control_binding (element,
|
||||||
|
"scratch-lines");
|
||||||
|
fail_unless (binding == NULL);
|
||||||
|
ges_track_element_set_property_controlling_parameters (element,
|
||||||
|
source, "scratch-lines", "direct");
|
||||||
|
tmp_binding =
|
||||||
|
ges_track_element_get_control_binding (element,
|
||||||
|
"scratch-lines");
|
||||||
|
fail_unless (tmp_binding != NULL);
|
||||||
|
ges_track_element_set_property_controlling_parameters (element,
|
||||||
|
source, "scratch-lines", "direct");
|
||||||
|
binding =
|
||||||
|
ges_track_element_get_control_binding (element,
|
||||||
|
"scratch-lines");
|
||||||
|
fail_unless (binding != tmp_binding);
|
||||||
|
|
||||||
|
|
||||||
|
g_object_set (source, "mode", GST_INTERPOLATION_MODE_LINEAR, NULL);
|
||||||
|
gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE
|
||||||
|
(source), 0 * GST_SECOND, 0.);
|
||||||
|
gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE
|
||||||
|
(source), 5 * GST_SECOND, 0.);
|
||||||
|
gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE
|
||||||
|
(source), 10 * GST_SECOND, 1.);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_check_keyframes (GESTimeline * timeline)
|
||||||
|
{
|
||||||
|
GList *tracks;
|
||||||
|
GList *tmp;
|
||||||
|
|
||||||
|
tracks = ges_timeline_get_tracks (timeline);
|
||||||
|
for (tmp = tracks; tmp; tmp = tmp->next) {
|
||||||
|
GESTrack *track;
|
||||||
|
GList *track_elements;
|
||||||
|
GList *tmp_tck;
|
||||||
|
|
||||||
|
track = GES_TRACK (tmp->data);
|
||||||
|
switch (track->type) {
|
||||||
|
case GES_TRACK_TYPE_VIDEO:
|
||||||
|
track_elements = ges_track_get_elements (track);
|
||||||
|
|
||||||
|
for (tmp_tck = track_elements; tmp_tck; tmp_tck = tmp_tck->next) {
|
||||||
|
GESTrackElement *element = GES_TRACK_ELEMENT (tmp_tck->data);
|
||||||
|
|
||||||
|
if (GES_IS_EFFECT (element)) {
|
||||||
|
GstControlBinding *binding;
|
||||||
|
GstControlSource *source;
|
||||||
|
GList *timed_values;
|
||||||
|
GstTimedValue *value;
|
||||||
|
|
||||||
|
binding =
|
||||||
|
ges_track_element_get_control_binding (element,
|
||||||
|
"scratch-lines");
|
||||||
|
fail_unless (binding != NULL);
|
||||||
|
g_object_get (binding, "control-source", &source, NULL);
|
||||||
|
fail_unless (source != NULL);
|
||||||
|
|
||||||
|
/* Now check keyframe position */
|
||||||
|
timed_values =
|
||||||
|
gst_timed_value_control_source_get_all
|
||||||
|
(GST_TIMED_VALUE_CONTROL_SOURCE (source));
|
||||||
|
value = timed_values->data;
|
||||||
|
fail_unless (value->value == 0.);
|
||||||
|
fail_unless (value->timestamp == 0 * GST_SECOND);
|
||||||
|
timed_values = timed_values->next;
|
||||||
|
value = timed_values->data;
|
||||||
|
fail_unless (value->value == 0.);
|
||||||
|
fail_unless (value->timestamp == 5 * GST_SECOND);
|
||||||
|
timed_values = timed_values->next;
|
||||||
|
value = timed_values->data;
|
||||||
|
fail_unless (value->value == 1.);
|
||||||
|
fail_unless (value->timestamp == 10 * GST_SECOND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_START_TEST (test_project_add_keyframes)
|
||||||
|
{
|
||||||
|
GMainLoop *mainloop;
|
||||||
|
//GESTimelinePipeline *pipeline;
|
||||||
|
GESProject *project;
|
||||||
|
GESTimeline *timeline;
|
||||||
|
GESAsset *formatter_asset;
|
||||||
|
gboolean saved;
|
||||||
|
gchar *uri = ges_test_file_uri ("test-keyframes.xges");
|
||||||
|
|
||||||
|
project = ges_project_new (uri);
|
||||||
|
mainloop = g_main_loop_new (NULL, FALSE);
|
||||||
|
|
||||||
|
/* Connect the signals */
|
||||||
|
g_signal_connect (project, "loaded", (GCallback) project_loaded_cb, mainloop);
|
||||||
|
g_signal_connect (project, "missing-uri", (GCallback) _set_new_uri, NULL);
|
||||||
|
|
||||||
|
/* Now extract a timeline from it */
|
||||||
|
GST_LOG ("Loading project");
|
||||||
|
timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
|
||||||
|
|
||||||
|
g_main_loop_run (mainloop);
|
||||||
|
|
||||||
|
GST_LOG ("Test first loading");
|
||||||
|
|
||||||
|
g_free (uri);
|
||||||
|
|
||||||
|
_add_keyframes (timeline);
|
||||||
|
|
||||||
|
uri = ges_test_file_uri ("test-keyframes-save.xges");
|
||||||
|
|
||||||
|
formatter_asset = ges_asset_request (GES_TYPE_FORMATTER, "ges", NULL);
|
||||||
|
saved =
|
||||||
|
ges_project_save (project, timeline, uri, formatter_asset, TRUE, NULL);
|
||||||
|
fail_unless (saved);
|
||||||
|
|
||||||
|
gst_object_unref (timeline);
|
||||||
|
gst_object_unref (project);
|
||||||
|
|
||||||
|
project = ges_project_new (uri);
|
||||||
|
|
||||||
|
ASSERT_OBJECT_REFCOUNT (project, "Our + cache", 2);
|
||||||
|
|
||||||
|
g_signal_connect (project, "loaded", (GCallback) project_loaded_cb, mainloop);
|
||||||
|
|
||||||
|
GST_LOG ("Loading saved project");
|
||||||
|
timeline = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), NULL));
|
||||||
|
fail_unless (GES_IS_TIMELINE (timeline));
|
||||||
|
|
||||||
|
g_main_loop_run (mainloop);
|
||||||
|
|
||||||
|
_check_keyframes (timeline);
|
||||||
|
|
||||||
|
gst_object_unref (timeline);
|
||||||
|
gst_object_unref (project);
|
||||||
|
g_free (uri);
|
||||||
|
|
||||||
|
g_main_loop_unref (mainloop);
|
||||||
|
g_signal_handlers_disconnect_by_func (project, (GCallback) project_loaded_cb,
|
||||||
|
mainloop);
|
||||||
|
g_signal_handlers_disconnect_by_func (project, (GCallback) asset_added_cb,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
GST_START_TEST (test_project_load_xges)
|
GST_START_TEST (test_project_load_xges)
|
||||||
{
|
{
|
||||||
gboolean saved;
|
gboolean saved;
|
||||||
|
@ -435,6 +621,7 @@ ges_suite (void)
|
||||||
tcase_add_test (tc_chain, test_project_simple);
|
tcase_add_test (tc_chain, test_project_simple);
|
||||||
tcase_add_test (tc_chain, test_project_add_assets);
|
tcase_add_test (tc_chain, test_project_add_assets);
|
||||||
tcase_add_test (tc_chain, test_project_load_xges);
|
tcase_add_test (tc_chain, test_project_load_xges);
|
||||||
|
tcase_add_test (tc_chain, test_project_add_keyframes);
|
||||||
/*tcase_add_test (tc_chain, test_load_xges_and_play); */
|
/*tcase_add_test (tc_chain, test_load_xges_and_play); */
|
||||||
tcase_add_test (tc_chain, test_project_unexistant_effect);
|
tcase_add_test (tc_chain, test_project_unexistant_effect);
|
||||||
|
|
||||||
|
|
23
tests/check/ges/test-keyframes.xges
Normal file
23
tests/check/ges/test-keyframes.xges
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<ges version="0.1">
|
||||||
|
<project metadatas='metadatas, name=(string)"Example\ project";'>
|
||||||
|
<encoding-profiles>
|
||||||
|
<encoding-profile name='first_profile' description='(null)' type='container' format='application/ogg'>
|
||||||
|
<stream-profile parent='first_profile' id='0' type='video' presence='0' format='video/x-h264' pass='0' variableframerate='0' />
|
||||||
|
<stream-profile parent='first_profile' id='1' type='audio' presence='0' format='audio/x-aac' />
|
||||||
|
</encoding-profile>
|
||||||
|
</encoding-profiles>
|
||||||
|
<resources>
|
||||||
|
<asset id="file:///test/not/exisiting"
|
||||||
|
extractable-type-name="GESUriClip"/>
|
||||||
|
</resources>
|
||||||
|
<timeline>
|
||||||
|
<track track-type="2" caps="audio/x-raw" track-id="0"/>
|
||||||
|
<track track-type="4" caps="video/x-raw" track-id="1"/>
|
||||||
|
<layer priority="0" properties='properties, auto-transition=(boolean)true;' metadatas='metadatas, a=(guint)3'>
|
||||||
|
<clip id="0" layer-priority='0' asset-id="file:///test/not/exisiting" type-name="GESUriClip" track-types="6" start="0" duration="10000000000">
|
||||||
|
<effect asset-id='agingtv' clip-id='0' type-name='GESEffect' track-type='4' track-id='1' metadatas='metadatas;' children-properties='properties, scratch-lines=(uint)12;'/>
|
||||||
|
</clip>
|
||||||
|
</layer>
|
||||||
|
</timeline>
|
||||||
|
</project>
|
||||||
|
</ges>
|
Loading…
Reference in a new issue