mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 11:41:09 +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 */
|
||||
GList *pending_assets;
|
||||
|
||||
/* current track element */
|
||||
GESTrackElement *current_track_element;
|
||||
};
|
||||
|
||||
static void
|
||||
|
@ -339,6 +342,7 @@ ges_base_xml_formatter_init (GESBaseXmlFormatter * self)
|
|||
g_str_equal, g_free, gst_object_unref);
|
||||
priv->layers = g_hash_table_new_full (g_direct_hash,
|
||||
g_direct_equal, NULL, (GDestroyNotify) _free_layer_entry);
|
||||
priv->current_track_element = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -828,6 +832,35 @@ ges_base_xml_formatter_add_track (GESBaseXmlFormatter * self,
|
|||
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
|
||||
ges_base_xml_formatter_add_track_element (GESBaseXmlFormatter * self,
|
||||
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);
|
||||
}
|
||||
priv->current_track_element = trackelement;
|
||||
}
|
||||
|
||||
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
|
||||
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*
|
||||
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,
|
||||
const gchar *metadatas,
|
||||
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,
|
||||
const GValue * value,
|
||||
GObject * object);;
|
||||
|
|
|
@ -66,6 +66,9 @@ struct _GESTrackElementPrivate
|
|||
|
||||
gboolean locked; /* If TRUE, then moves in sync with its controlling
|
||||
* GESClip */
|
||||
|
||||
GHashTable *bindings_hashtable; /* We need this if we want to be able to serialize
|
||||
and deserialize keyframes */
|
||||
};
|
||||
|
||||
enum
|
||||
|
@ -181,6 +184,9 @@ ges_track_element_dispose (GObject * object)
|
|||
if (priv->properties_hashtable)
|
||||
g_hash_table_destroy (priv->properties_hashtable);
|
||||
|
||||
if (priv->bindings_hashtable)
|
||||
g_hash_table_destroy (priv->bindings_hashtable);
|
||||
|
||||
if (priv->gnlobject) {
|
||||
GstState cstate;
|
||||
|
||||
|
@ -301,6 +307,8 @@ ges_track_element_init (GESTrackElement * self)
|
|||
priv->pending_active = TRUE;
|
||||
priv->locked = TRUE;
|
||||
priv->properties_hashtable = NULL;
|
||||
priv->bindings_hashtable = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -748,6 +756,14 @@ ges_track_element_set_track (GESTrackElement * object, GESTrack * track)
|
|||
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:
|
||||
* @object: a #GESTrackElement
|
||||
|
@ -1449,3 +1465,99 @@ ges_track_element_edit (GESTrackElement * object,
|
|||
|
||||
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-clip.h>
|
||||
#include <ges/ges-track.h>
|
||||
#include <gst/controller/gstdirectcontrolbinding.h>
|
||||
#include <gst/controller/gstinterpolationcontrolsource.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
@ -184,5 +186,15 @@ ges_track_element_edit (GESTrackElement * object,
|
|||
GList *layers, GESEditMode mode,
|
||||
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
|
||||
#endif /* _GES_TRACK_ELEMENT */
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
/* TODO Determine error codes numbers */
|
||||
|
||||
#include "ges.h"
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "ges-internal.h"
|
||||
|
||||
|
@ -513,6 +514,54 @@ wrong_type:
|
|||
"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
|
||||
_parse_effect (GMarkupParseContext * context, const gchar * element_name,
|
||||
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)
|
||||
_parse_effect (context, element_name, attribute_names,
|
||||
attribute_values, self, error);
|
||||
else if (g_strcmp0 (element_name, "binding") == 0)
|
||||
_parse_binding (context, element_name, attribute_names,
|
||||
attribute_values, self, error);
|
||||
else
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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
|
||||
_save_effect (GString * str, guint clip_id, GESTrackElement * trackelement,
|
||||
GESTimeline * timeline)
|
||||
|
@ -822,8 +924,12 @@ _save_effect (GString * str, guint clip_id, GESTrackElement * trackelement,
|
|||
}
|
||||
g_free (pspecs);
|
||||
|
||||
append_printf_escaped (str, " children-properties='%s'/>\n",
|
||||
append_printf_escaped (str, " children-properties='%s'>\n",
|
||||
gst_structure_to_string (structure));
|
||||
|
||||
_save_keyframes (str, trackelement);
|
||||
|
||||
append_printf_escaped (str, "</effect>\n");
|
||||
gst_structure_free (structure);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "test-utils.h"
|
||||
#include <ges/ges.h>
|
||||
#include <gst/check/gstcheck.h>
|
||||
#include <gst/controller/gstdirectcontrolbinding.h>
|
||||
#include <gst/controller/gstinterpolationcontrolsource.h>
|
||||
|
||||
static void
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
gboolean saved;
|
||||
|
@ -435,6 +621,7 @@ ges_suite (void)
|
|||
tcase_add_test (tc_chain, test_project_simple);
|
||||
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_add_keyframes);
|
||||
/*tcase_add_test (tc_chain, test_load_xges_and_play); */
|
||||
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