if (priv->state != STATE_LOADING_CLIPS) {
GST_DEBUG_OBJECT (self, "Not loading control bindings in %s state.",
loading_state_name (priv->state));
- g_slist_free_full (timed_values, g_free);
- return;
+ goto done;
}
if (track_id[0] != '-' && priv->current_clip)
if (element == NULL) {
GST_WARNING ("No current track element to which we can append a binding");
- return;
+ goto done;
}
if (!g_strcmp0 (source_type, "interpolation")) {
GstControlSource *source;
source = gst_interpolation_control_source_new ();
+
+ /* add first before setting values to avoid clamping */
ges_track_element_set_control_source (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);
- g_slist_free_full (timed_values, g_free);
+
+ gst_object_unref (source);
} else
GST_WARNING ("This interpolation type is not supported\n");
+
+done:
+ g_slist_free_full (timed_values, g_free);
}
void
GstClockTime duration_limit;
gboolean prevent_duration_limit_update;
+ gboolean prevent_children_outpoint_update;
gboolean allow_any_remove;
return limit;
}
+static void
+_update_children_outpoints (GESClip * self)
+{
+ GList *tmp;
+
+ if (self->priv->prevent_children_outpoint_update)
+ return;
+
+ for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+ ges_track_element_update_outpoint (tmp->data);
+ }
+}
+
static void
_update_duration_limit (GESClip * self)
{
gboolean active = ges_track_element_is_active (child);
gboolean is_core = _IS_CORE_CHILD (child);
gboolean prev_prevent = self->priv->prevent_duration_limit_update;
+ gboolean prev_prevent_outpoint = self->priv->prevent_children_outpoint_update;
/* We want to ensure that each active non-core element has a
* corresponding active core element in the same track */
self->priv->setting_active = TRUE;
self->priv->prevent_duration_limit_update = TRUE;
+ self->priv->prevent_children_outpoint_update = TRUE;
/* If we are core, make all the non-core elements in-active
* If we are non-core, make the core element active (should only be one) */
self->priv->setting_active = FALSE;
self->priv->prevent_duration_limit_update = prev_prevent;
+ self->priv->prevent_children_outpoint_update = prev_prevent_outpoint;
}
/****************************************************
GESTrackElement *core;
gboolean active;
gboolean prev_prevent = self->priv->prevent_duration_limit_update;
+ gboolean prev_prevent_outpoint = self->priv->prevent_children_outpoint_update;
if (self->priv->allow_any_track || _IS_CORE_CHILD (child) || !track)
return;
self->priv->setting_active = TRUE;
self->priv->prevent_duration_limit_update = TRUE;
+ self->priv->prevent_children_outpoint_update = TRUE;
if (!ges_track_element_set_active (child, FALSE))
GST_ERROR_OBJECT (self, "Failed to de-activate child %" GES_FORMAT,
self->priv->setting_active = FALSE;
self->priv->prevent_duration_limit_update = prev_prevent;
+ self->priv->prevent_children_outpoint_update = prev_prevent_outpoint;
}
}
_child_property_changed_cb (GESTimelineElement * child, GParamSpec * pspec,
GESClip * self)
{
- gboolean update = FALSE;
+ gboolean update_limit = FALSE;
+ gboolean update_outpoint = FALSE;
const gchar *name = pspec->name;
if (_IS_PROP ("track")) {
- update = TRUE;
+ update_limit = TRUE;
+ update_outpoint = TRUE;
_update_active_for_track (self, GES_TRACK_ELEMENT (child));
} else if (_IS_PROP ("active")) {
- update = TRUE;
+ update_limit = TRUE;
+ update_outpoint = TRUE;
_child_active_changed (self, GES_TRACK_ELEMENT (child));
} else if (_IS_PROP ("priority")) {
- update = TRUE;
+ update_limit = TRUE;
+ update_outpoint = TRUE;
_child_priority_changed (GES_CONTAINER (self), child);
} else if (_IS_PROP ("in-point")) {
- update = _child_inpoint_changed (self, child);
+ /* update outpoint already handled by the track element */
+ update_limit = _child_inpoint_changed (self, child);
} else if (_IS_PROP ("max-duration")) {
- update = TRUE;
+ update_limit = TRUE;
_child_max_duration_changed (GES_CONTAINER (self), child);
} else if (_IS_PROP ("has-internal-source")) {
_child_has_internal_source_changed (self, child);
}
- if (update)
+ if (update_limit)
_update_duration_limit (self);
+ if (update_outpoint)
+ _update_children_outpoints (self);
}
/****************************************************
if (time_prop) {
g_free (time_prop);
_update_duration_limit (self);
+ _update_children_outpoints (self);
}
}
GList *tmp;
guint32 min_prio, max_prio, min_child_prio = G_MAXUINT32;
gboolean prev_prevent = priv->prevent_duration_limit_update;
+ gboolean prev_prevent_outpoint = priv->prevent_children_outpoint_update;
GESContainer *container = GES_CONTAINER (element);
for (tmp = container->children; tmp; tmp = g_list_next (tmp))
/* offsets will remain constant for the children */
priv->prevent_resort = TRUE;
priv->prevent_duration_limit_update = TRUE;
+ priv->prevent_children_outpoint_update = TRUE;
priv->setting_priority = TRUE;
for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
GESTimelineElement *child = tmp->data;
priv->prevent_resort = FALSE;
priv->setting_priority = FALSE;
priv->prevent_duration_limit_update = prev_prevent;
+ priv->prevent_children_outpoint_update = prev_prevent_outpoint;
return TRUE;
}
GESClipPrivate *priv = self->priv;
GESAsset *asset, *creator_asset;
gboolean prev_prevent = priv->prevent_duration_limit_update;
+ gboolean prev_prevent_outpoint = priv->prevent_children_outpoint_update;
GList *tmp;
GError *error = NULL;
priv->prevent_resort = TRUE;
priv->setting_priority = TRUE;
priv->prevent_duration_limit_update = TRUE;
+ priv->prevent_children_outpoint_update = TRUE;
/* increase the priority of anything with a lower priority */
for (tmp = container->children; tmp; tmp = tmp->next) {
priv->prevent_resort = FALSE;
priv->setting_priority = FALSE;
priv->prevent_duration_limit_update = prev_prevent;
+ priv->prevent_children_outpoint_update = prev_prevent_outpoint;
/* no need to call _ges_container_sort_children (container) since
* there is no change to the ordering yet (this happens after the
* child is actually added) */
if (_IS_TOP_EFFECT (element)) {
GList *tmp;
gboolean prev_prevent = priv->prevent_duration_limit_update;
+ gboolean prev_prevent_outpoint = priv->prevent_children_outpoint_update;
GST_DEBUG_OBJECT (container, "Resyncing effects priority.");
/* changing priorities, so preventing a re-sort */
priv->prevent_resort = TRUE;
priv->setting_priority = TRUE;
priv->prevent_duration_limit_update = TRUE;
+ priv->prevent_children_outpoint_update = TRUE;
for (tmp = container->children; tmp; tmp = tmp->next) {
guint32 sibling_prio = GES_TIMELINE_ELEMENT_PRIORITY (tmp->data);
if (sibling_prio > element->priority)
priv->prevent_resort = FALSE;
priv->setting_priority = FALSE;
priv->prevent_duration_limit_update = prev_prevent;
+ priv->prevent_children_outpoint_update = prev_prevent_outpoint;
/* no need to re-sort the children since the rest keep the same
* relative priorities */
/* height may have changed */
_update_max_duration (container);
_update_duration_limit (self);
+ _update_children_outpoints (self);
}
static void
_update_max_duration (container);
_update_duration_limit (self);
+ _update_children_outpoints (self);
+ ges_track_element_update_outpoint (GES_TRACK_ELEMENT (element));
}
static void
GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (to_clip);
gboolean prev_prevent_from = from_clip->priv->prevent_duration_limit_update;
gboolean prev_prevent_to = to_clip->priv->prevent_duration_limit_update;
+ gboolean prev_prevent_outpoint_from =
+ from_clip->priv->prevent_children_outpoint_update;
+ gboolean prev_prevent_outpoint_to =
+ to_clip->priv->prevent_children_outpoint_update;
/* We need to bump the refcount to avoid the object to be destroyed */
gst_object_ref (child);
from_clip->priv->prevent_duration_limit_update = TRUE;
to_clip->priv->prevent_duration_limit_update = TRUE;
+ from_clip->priv->prevent_children_outpoint_update = TRUE;
+ to_clip->priv->prevent_children_outpoint_update = TRUE;
from_clip->priv->allow_any_remove = TRUE;
ges_container_remove (GES_CONTAINER (from_clip),
from_clip->priv->prevent_duration_limit_update = prev_prevent_from;
to_clip->priv->prevent_duration_limit_update = prev_prevent_to;
+ from_clip->priv->prevent_children_outpoint_update =
+ prev_prevent_outpoint_from;
+ to_clip->priv->prevent_children_outpoint_update = prev_prevent_outpoint_to;
gst_object_unref (child);
}
{
GList *tmp;
gboolean prev_prevent = clip->priv->prevent_duration_limit_update;
+ gboolean prev_prevent_outpoint = clip->priv->prevent_children_outpoint_update;
if (track == NULL)
return;
/* allow us to remove in any order */
clip->priv->allow_any_track = TRUE;
clip->priv->prevent_duration_limit_update = TRUE;
+ clip->priv->prevent_children_outpoint_update = TRUE;
for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
GESTrackElement *child = tmp->data;
}
clip->priv->allow_any_track = FALSE;
clip->priv->prevent_duration_limit_update = prev_prevent;
+ clip->priv->prevent_children_outpoint_update = prev_prevent_outpoint;
_update_duration_limit (clip);
+ _update_children_outpoints (clip);
}
static GESTrackElement *
GList *tmp, *transitions = NULL;
GESClip *new_object;
gboolean no_core = FALSE;
- GstClockTime start, inpoint, duration, old_duration, new_duration,
- new_inpoint;
+ GstClockTime start, duration, old_duration, new_duration, new_inpoint;
GESTimelineElement *element;
GESTimeline *timeline;
GHashTable *track_for_copy;
duration = element->duration;
start = element->start;
- inpoint = element->inpoint;
if (position >= start + duration || position <= start) {
GST_WARNING_OBJECT (clip, "Can not split %" GST_TIME_FORMAT
/* Create the new Clip */
new_object = GES_CLIP (ges_timeline_element_copy (element, FALSE));
new_object->priv->prevent_duration_limit_update = TRUE;
+ new_object->priv->prevent_children_outpoint_update = TRUE;
GST_DEBUG_OBJECT (new_object, "New 'splitted' clip");
/* Set new timing properties on the Clip */
GESTrack *track = ges_track_element_get_track (orig);
GESAutoTransition *trans;
- /* FIXME: is position - start + inpoint always the correct splitting
- * point for control bindings? What coordinate system are control
- * bindings given in? */
- copy = ges_clip_copy_track_element_into (new_object, orig,
- position - start + inpoint);
+ copy = ges_clip_copy_track_element_into (new_object, orig, new_inpoint);
if (!copy)
continue;
g_list_free_full (transitions, gst_object_unref);
new_object->priv->prevent_duration_limit_update = FALSE;
+ new_object->priv->prevent_children_outpoint_update = FALSE;
_update_duration_limit (new_object);
+ _update_children_outpoints (new_object);
return new_object;
}
G_GNUC_INTERNAL void ges_track_element_copy_bindings (GESTrackElement *element,
GESTrackElement *new_element,
guint64 position);
+G_GNUC_INTERNAL void ges_track_element_freeze_control_sources (GESTrackElement * object,
+ gboolean freeze);
+G_GNUC_INTERNAL void ges_track_element_update_outpoint (GESTrackElement * self);
G_GNUC_INTERNAL void
ges_track_element_set_creator_asset (GESTrackElement * self,
/* freeze the auto-transitions whilst we edit */
ges_timeline_freeze_auto_transitions (root->data, TRUE);
+ g_hash_table_iter_init (&iter, edits);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (GES_IS_TRACK_ELEMENT (key))
+ ges_track_element_freeze_control_sources (GES_TRACK_ELEMENT (key), TRUE);
+ }
+
g_hash_table_iter_init (&iter, edits);
while (g_hash_table_iter_next (&iter, &key, &value)) {
GESTimelineElement *element = key;
if (!perform_element_edit (element, edit_data))
no_errors = FALSE;
}
+
+ g_hash_table_iter_init (&iter, edits);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (GES_IS_TRACK_ELEMENT (key))
+ ges_track_element_freeze_control_sources (GES_TRACK_ELEMENT (key), FALSE);
+ }
+
/* allow the transitions to update if they can */
ges_timeline_freeze_auto_transitions (root->data, FALSE);
* tracks and take responsibility for updating them. The only track
* elements that are not automatically created by clips, but a user is
* likely to want to create, are #GESEffect-s.
+ *
+ * ## Control Bindings for Children Properties
+ *
+ * You can set up control bindings for a track element child property
+ * using ges_track_element_set_control_source(). A
+ * #GstTimedValueControlSource should specify the timed values using the
+ * internal source coordinates (see #GESTimelineElement). By default,
+ * these will be updated to lie between the #GESTimelineElement:in-point
+ * and out-point of the element. This can be switched off by setting
+ * #GESTrackElement:auto-clamp-control-sources to %FALSE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
gboolean has_internal_source_forbidden;
gboolean has_internal_source;
- gboolean locked; /* If TRUE, then moves in sync with its controlling
- * GESClip */
gboolean layer_active;
GHashTable *bindings_hashtable; /* We need this if we want to be able to serialize
and deserialize keyframes */
GESAsset *creator_asset;
+
+ GstClockTime outpoint;
+ gboolean freeze_control_sources;
+ gboolean auto_clamp_control_sources;
};
enum
PROP_TRACK_TYPE,
PROP_TRACK,
PROP_HAS_INTERNAL_SOURCE,
+ PROP_AUTO_CLAMP_CONTROL_SOURCES,
PROP_LAST
};
static gboolean _set_priority (GESTimelineElement * element, guint32 priority);
GESTrackType _get_track_types (GESTimelineElement * object);
-static void
-_update_control_bindings (GESTimelineElement * element, GstClockTime inpoint,
- GstClockTime duration);
-
static gboolean
_lookup_child (GESTrackElement * object,
const gchar * prop_name, GstElement ** element, GParamSpec ** pspec)
g_value_set_boolean (value,
ges_track_element_has_internal_source (track_element));
break;
+ case PROP_AUTO_CLAMP_CONTROL_SOURCES:
+ g_value_set_boolean (value,
+ ges_track_element_get_auto_clamp_control_sources (track_element));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
ges_track_element_set_has_internal_source (track_element,
g_value_get_boolean (value));
break;
+ case PROP_AUTO_CLAMP_CONTROL_SOURCES:
+ ges_track_element_set_auto_clamp_control_sources (track_element,
+ g_value_get_boolean (value));
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
g_object_class_install_property (object_class, PROP_HAS_INTERNAL_SOURCE,
properties[PROP_HAS_INTERNAL_SOURCE]);
+ /**
+ * GESTrackElement:auto-clamp-control-sources:
+ *
+ * Whether the control sources on the element (see
+ * ges_track_element_set_control_source()) will be automatically
+ * updated whenever the #GESTimelineElement:in-point or out-point of the
+ * element change in value.
+ *
+ * See ges_track_element_clamp_control_source() for how this is done
+ * per control source.
+ *
+ * Default value: %TRUE
+ */
+ properties[PROP_AUTO_CLAMP_CONTROL_SOURCES] =
+ g_param_spec_boolean ("auto-clamp-control-sources",
+ "Auto-Clamp Control Sources", "Whether to automatically update the "
+ "control sources with a change in in-point or out-point", TRUE,
+ G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
+ g_object_class_install_property (object_class,
+ PROP_AUTO_CLAMP_CONTROL_SOURCES,
+ properties[PROP_AUTO_CLAMP_CONTROL_SOURCES]);
+
/**
* GESTrackElement::control-binding-added:
* @track_element: A #GESTrackElement
* because it calls g_object_new_with_properties.
*/
self->priv->has_internal_source = TRUE;
+
+ self->priv->outpoint = GST_CLOCK_TIME_NONE;
+ self->priv->auto_clamp_control_sources = TRUE;
}
static gfloat
diff = second_value->value - first_value->value;
interval = second_value->timestamp - first_value->timestamp;
+ /* FIXME: properly support non-linear timed control sources */
if (position > first_value->timestamp)
value_at_pos =
first_value->value + ((float) (position -
}
static void
-_update_control_bindings (GESTimelineElement * element, GstClockTime inpoint,
- GstClockTime duration)
+_update_control_source (GstTimedValueControlSource * source, gboolean absolute,
+ GstClockTime inpoint, GstClockTime outpoint)
{
- GParamSpec **specs;
- guint n, n_specs;
- GstControlBinding *binding;
- GstTimedValueControlSource *source;
- GESTrackElement *self = GES_TRACK_ELEMENT (element);
-
- specs = ges_track_element_list_children_properties (self, &n_specs);
-
- for (n = 0; n < n_specs; ++n) {
- GList *values, *tmp;
- gboolean absolute;
- GstTimedValue *last, *first, *prev = NULL, *next = NULL;
- gfloat value_at_pos;
-
- binding = ges_track_element_get_control_binding (self, specs[n]->name);
-
- if (!binding)
- continue;
-
- g_object_get (binding, "control_source", &source, NULL);
+ GList *values, *tmp;
+ GstTimedValue *last, *first, *prev = NULL, *next = NULL;
+ gfloat value_at_pos;
- g_object_get (binding, "absolute", &absolute, NULL);
- if (duration == 0) {
- gst_timed_value_control_source_unset_all (GST_TIMED_VALUE_CONTROL_SOURCE
- (source));
- continue;
- }
+ if (inpoint == outpoint) {
+ gst_timed_value_control_source_unset_all (source);
+ return;
+ }
- values =
- gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
- (source));
+ values = gst_timed_value_control_source_get_all (source);
- if (g_list_length (values) == 0)
- continue;
+ if (g_list_length (values) == 0)
+ return;
- first = values->data;
+ first = values->data;
- for (tmp = values->next; tmp; tmp = tmp->next) {
- next = tmp->data;
+ for (tmp = values->next; tmp; tmp = tmp->next) {
+ next = tmp->data;
- if (next->timestamp > inpoint)
- break;
+ if (next->timestamp == inpoint) {
+ /* just leave this value in place */
+ first = NULL;
+ break;
}
- g_list_free (values);
+ if (next->timestamp > inpoint)
+ break;
+ }
+ g_list_free (values);
+ if (first) {
value_at_pos =
interpolate_values_for_position (first, next, inpoint, absolute);
gst_timed_value_control_source_unset (source, first->timestamp);
gst_timed_value_control_source_set (source, inpoint, value_at_pos);
+ }
- values =
- gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
- (source));
+ if (GST_CLOCK_TIME_IS_VALID (outpoint)) {
+ values = gst_timed_value_control_source_get_all (source);
- if (duration != GST_CLOCK_TIME_NONE) {
- last = g_list_last (values)->data;
+ last = g_list_last (values)->data;
- for (tmp = g_list_last (values)->prev; tmp; tmp = tmp->prev) {
- prev = tmp->data;
+ for (tmp = g_list_last (values)->prev; tmp; tmp = tmp->prev) {
+ prev = tmp->data;
- if (prev->timestamp < duration + inpoint)
- break;
+ if (prev->timestamp == outpoint) {
+ /* leave this value in place */
+ last = NULL;
+ break;
}
- g_list_free (values);
+ if (prev->timestamp < outpoint)
+ break;
+ }
+ g_list_free (values);
+ if (last) {
value_at_pos =
- interpolate_values_for_position (prev, last, duration + inpoint,
- absolute);
+ interpolate_values_for_position (prev, last, outpoint, absolute);
gst_timed_value_control_source_unset (source, last->timestamp);
- gst_timed_value_control_source_set (source, duration + inpoint,
- value_at_pos);
- values =
- gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
- (source));
+ gst_timed_value_control_source_set (source, outpoint, value_at_pos);
}
+ }
- for (tmp = values; tmp; tmp = tmp->next) {
- GstTimedValue *value = tmp->data;
- if (value->timestamp < inpoint)
- gst_timed_value_control_source_unset (source, value->timestamp);
- else if (duration != GST_CLOCK_TIME_NONE
- && value->timestamp > duration + inpoint)
- gst_timed_value_control_source_unset (source, value->timestamp);
- }
- g_list_free (values);
+ values = gst_timed_value_control_source_get_all (source);
+
+ for (tmp = values; tmp; tmp = tmp->next) {
+ GstTimedValue *value = tmp->data;
+ if (value->timestamp < inpoint)
+ gst_timed_value_control_source_unset (source, value->timestamp);
+ else if (GST_CLOCK_TIME_IS_VALID (outpoint) && value->timestamp > outpoint)
+ gst_timed_value_control_source_unset (source, value->timestamp);
}
+ g_list_free (values);
+}
- g_free (specs);
+static void
+_update_control_bindings (GESTrackElement * self, GstClockTime inpoint,
+ GstClockTime outpoint)
+{
+ gchar *name;
+ GstControlBinding *binding;
+ GstControlSource *source;
+ gboolean absolute;
+ gpointer value, key;
+ GHashTableIter iter;
+
+ if (self->priv->freeze_control_sources)
+ return;
+
+ g_hash_table_iter_init (&iter, self->priv->bindings_hashtable);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ binding = value;
+ name = key;
+ g_object_get (binding, "control-source", &source, "absolute", &absolute,
+ NULL);
+
+ if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source)) {
+ GST_INFO_OBJECT (self, "Not updating %s because it does not have a"
+ " timed value control source", name);
+ gst_object_unref (source);
+ continue;
+ }
+
+ _update_control_source (GST_TIMED_VALUE_CONTROL_SOURCE (source), absolute,
+ inpoint, outpoint);
+ gst_object_unref (source);
+ }
}
static gboolean
return TRUE;
}
+static void
+ges_track_element_update_outpoint_full (GESTrackElement * self,
+ GstClockTime inpoint, GstClockTime duration)
+{
+ GstClockTime current_inpoint = _INPOINT (self);
+ gboolean increase = (inpoint > current_inpoint);
+ GstClockTime outpoint = GST_CLOCK_TIME_NONE;
+ GESTimelineElement *parent = GES_TIMELINE_ELEMENT_PARENT (self);
+ GESTrackElementPrivate *priv = self->priv;
+
+ if (GES_IS_CLIP (parent) && ges_track_element_get_track (self)
+ && ges_track_element_is_active (self)
+ && GST_CLOCK_TIME_IS_VALID (duration)) {
+ outpoint =
+ ges_clip_get_internal_time_from_timeline_time (GES_CLIP (parent), self,
+ _START (self) + duration, NULL);
+
+ if (!GST_CLOCK_TIME_IS_VALID (outpoint))
+ GST_ERROR_OBJECT (self, "Got an invalid out-point");
+ else if (increase)
+ outpoint += (inpoint - current_inpoint);
+ else
+ outpoint -= (current_inpoint - inpoint);
+ }
+
+ if ((priv->outpoint != outpoint || inpoint != current_inpoint)
+ && self->priv->auto_clamp_control_sources)
+ _update_control_bindings (self, inpoint, outpoint);
+
+ priv->outpoint = outpoint;
+}
+
+void
+ges_track_element_update_outpoint (GESTrackElement * self)
+{
+ GESTimelineElement *el = GES_TIMELINE_ELEMENT (self);
+ ges_track_element_update_outpoint_full (self, el->inpoint, el->duration);
+}
+
static gboolean
_set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
{
}
g_object_set (object->priv->nleobject, "inpoint", inpoint, NULL);
- _update_control_bindings (element, inpoint, GST_CLOCK_TIME_NONE);
+
+ ges_track_element_update_outpoint_full (object, inpoint, element->duration);
return TRUE;
}
GESTrackElement *object = GES_TRACK_ELEMENT (element);
GESTrackElementPrivate *priv = object->priv;
- g_return_val_if_fail (object->priv->nleobject, FALSE);
-
- if (GST_CLOCK_TIME_IS_VALID (_MAXDURATION (element)) &&
- duration > _INPOINT (object) + _MAXDURATION (element))
- duration = _MAXDURATION (element) - _INPOINT (object);
+ g_return_val_if_fail (priv->nleobject, FALSE);
g_object_set (priv->nleobject, "duration", duration, NULL);
- _update_control_bindings (element, ges_timeline_element_get_inpoint (element),
- duration);
+ ges_track_element_update_outpoint_full (object, element->inpoint, duration);
return TRUE;
}
if (!binding)
continue;
- g_object_get (binding, "control_source", &source, NULL);
+ g_object_get (binding, "control-source", &source, "absolute", &absolute,
+ NULL);
if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source)) {
GST_FIXME_OBJECT (element,
"Implement support for control source type: %s",
G_OBJECT_TYPE_NAME (source));
+ gst_object_unref (source);
continue;
}
- g_object_get (binding, "absolute", &absolute, NULL);
g_object_get (source, "mode", &mode, NULL);
new_source =
else
ges_track_element_set_control_source (new_element,
GST_CONTROL_SOURCE (new_source), specs[n]->name, "direct");
+
+ gst_object_unref (source);
+ gst_object_unref (new_source);
}
g_free (specs);
GstControlSource * source,
const gchar * property_name, const gchar * binding_type)
{
+ gboolean ret = FALSE;
GESTrackElementPrivate *priv;
GstElement *element;
- GParamSpec *pspec;
GstControlBinding *binding;
gboolean direct, direct_absolute;
return FALSE;
}
- if (!ges_track_element_lookup_child (object, property_name, &element, &pspec)) {
+ if (!ges_track_element_lookup_child (object, property_name, &element, NULL)) {
GST_WARNING ("You need to provide a valid and controllable property name");
return FALSE;
}
direct = !g_strcmp0 (binding_type, "direct");
direct_absolute = !g_strcmp0 (binding_type, "direct-absolute");
- if (direct || direct_absolute) {
- /* First remove existing binding */
- if (ges_track_element_remove_control_binding (object, property_name)) {
- GST_LOG ("Removed old binding for property %s", property_name);
- }
+ if (!direct && !direct_absolute) {
+ GST_WARNING_OBJECT (object, "Binding type must be in "
+ "[direct, direct-absolute]");
+ goto done;
+ }
- if (direct_absolute)
- binding =
- gst_direct_control_binding_new_absolute (GST_OBJECT (element),
- property_name, source);
- else
- binding =
- gst_direct_control_binding_new (GST_OBJECT (element), property_name,
- source);
-
- gst_object_add_control_binding (GST_OBJECT (element), binding);
- /* FIXME: maybe we should force the
- * "ChildTypeName:property-name"
- * format convention for child property names in bindings_hashtable.
- * Currently the table may also contain
- * "property-name"
- * as keys.
- */
- g_hash_table_insert (priv->bindings_hashtable, g_strdup (property_name),
- binding);
- g_signal_emit (object, ges_track_element_signals[CONTROL_BINDING_ADDED],
- 0, binding);
- return TRUE;
+ /* First remove existing binding */
+ if (ges_track_element_remove_control_binding (object, property_name))
+ GST_LOG_OBJECT (object, "Removed old binding for property %s",
+ property_name);
+
+ if (direct_absolute)
+ binding = gst_direct_control_binding_new_absolute (GST_OBJECT (element),
+ property_name, source);
+ else
+ binding = gst_direct_control_binding_new (GST_OBJECT (element),
+ property_name, source);
+
+ gst_object_add_control_binding (GST_OBJECT (element), binding);
+ /* FIXME: maybe we should force the
+ * "ChildTypeName:property-name"
+ * format convention for child property names in bindings_hashtable.
+ * Currently the table may also contain
+ * "property-name"
+ * as keys.
+ */
+ g_hash_table_insert (priv->bindings_hashtable, g_strdup (property_name),
+ binding);
+
+ if (GST_IS_TIMED_VALUE_CONTROL_SOURCE (source)
+ && priv->auto_clamp_control_sources) {
+ /* Make sure we have the control source used by the binding */
+ g_object_get (binding, "control-source", &source, NULL);
+
+ _update_control_source (GST_TIMED_VALUE_CONTROL_SOURCE (source),
+ direct_absolute, _INPOINT (object), priv->outpoint);
+
+ gst_object_unref (source);
}
- GST_WARNING ("Binding type must be in [direct]");
+ g_signal_emit (object, ges_track_element_signals[CONTROL_BINDING_ADDED],
+ 0, binding);
- return FALSE;
+ ret = TRUE;
+
+done:
+ gst_object_unref (element);
+
+ return ret;
}
/**
return binding;
}
+/**
+ * ges_track_element_clamp_control_source:
+ * @object: A #GESTrackElement
+ * @property_name: The name of the child property to clamp the control
+ * source of
+ *
+ * Clamp the #GstTimedValueControlSource for the specified child property
+ * to lie between the #GESTimelineElement:in-point and out-point of the
+ * element. The out-point is the #GES_TIMELINE_ELEMENT_END of the element
+ * translated from the timeline coordinates to the internal source
+ * coordinates of the element.
+ *
+ * If the property does not have a #GstTimedValueControlSource set by
+ * ges_track_element_set_control_source(), nothing happens. Otherwise, if
+ * a timed value for the control source lies before the in-point of the
+ * element, or after its out-point, then it will be removed. At the
+ * in-point and out-point times, a new interpolated value will be placed.
+ */
+void
+ges_track_element_clamp_control_source (GESTrackElement * object,
+ const gchar * property_name)
+{
+ GstControlBinding *binding;
+ GstControlSource *source;
+ gboolean absolute;
+
+ g_return_if_fail (GES_IS_TRACK_ELEMENT (object));
+
+ binding = ges_track_element_get_control_binding (object, property_name);
+
+ if (!binding)
+ return;
+
+ g_object_get (binding, "control-source", &source, "absolute", &absolute,
+ NULL);
+
+ if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source)) {
+ gst_object_unref (source);
+ return;
+ }
+
+ _update_control_source (GST_TIMED_VALUE_CONTROL_SOURCE (source), absolute,
+ _INPOINT (object), object->priv->outpoint);
+ gst_object_unref (source);
+}
+
+/**
+ * ges_track_element_set_auto_clamp_control_sources:
+ * @object: A #GESTrackElement
+ * @auto_clamp: Whether to automatically clamp the control sources for the
+ * child properties of @object
+ *
+ * Sets #GESTrackElement:auto-clamp-control-sources. If set to %TRUE, this
+ * will immediately clamp all the control sources.
+ */
+void
+ges_track_element_set_auto_clamp_control_sources (GESTrackElement * object,
+ gboolean auto_clamp)
+{
+ g_return_if_fail (GES_IS_TRACK_ELEMENT (object));
+
+ if (auto_clamp == object->priv->auto_clamp_control_sources)
+ return;
+
+ object->priv->auto_clamp_control_sources = auto_clamp;
+ if (auto_clamp)
+ _update_control_bindings (object, _INPOINT (object),
+ object->priv->outpoint);
+
+ g_object_notify_by_pspec (G_OBJECT (object),
+ properties[PROP_AUTO_CLAMP_CONTROL_SOURCES]);
+}
+
+/**
+ * ges_track_element_get_auto_clamp_control_sources:
+ * @object: A #GESTrackElement
+ *
+ * Gets #GESTrackElement:auto-clamp-control-sources.
+ *
+ * Returns: Whether the control sources for the child properties of
+ * @object are automatically clamped.
+ */
+gboolean
+ges_track_element_get_auto_clamp_control_sources (GESTrackElement * object)
+{
+ g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
+
+ return object->priv->auto_clamp_control_sources;
+}
+
+void
+ges_track_element_freeze_control_sources (GESTrackElement * object,
+ gboolean freeze)
+{
+ object->priv->freeze_control_sources = freeze;
+ if (!freeze && object->priv->auto_clamp_control_sources)
+ _update_control_bindings (object, _INPOINT (object),
+ object->priv->outpoint);
+}
/**
* ges_track_element_is_core:
const gchar *property_name,
const gchar *binding_type);
+GES_API void
+ges_track_element_clamp_control_source (GESTrackElement * object,
+ const gchar * property_name);
+
+GES_API void
+ges_track_element_set_auto_clamp_control_sources (GESTrackElement * object,
+ gboolean auto_clamp);
+GES_API gboolean
+ges_track_element_get_auto_clamp_control_sources (GESTrackElement * object);
+
GES_API GstControlBinding *
ges_track_element_get_control_binding (GESTrackElement *object,
const gchar *property_name);
append_escaped (str, g_markup_printf_escaped ("'/>\n"), depth);
} else
GST_DEBUG ("control source not in [interpolation]");
+
+ gst_object_unref (source);
} else
GST_DEBUG ("Binding type not in [direct, direct-absolute]");
}
CHECK_OBJECT_PROPS (clip, 0 * GST_SECOND, 10 * GST_SECOND, 5 * GST_SECOND);
check_layer (clip, 0);
+
gst_object_unref (timeline);
+ gst_object_unref (source);
+ gst_object_unref (splitsource);
ges_deinit ();
}
check_layer (clip, 0);
gst_object_unref (timeline);
+ gst_object_unref (source);
+ gst_object_unref (splitsource);
+
ges_deinit ();
}
GstControlBinding *binding = ges_track_element_get_control_binding ( \
GES_TRACK_ELEMENT (element), prop_name); \
fail_unless (binding, "No control binding found for %s on %s", \
- prop_name, element->name); \
+ prop_name, GES_TIMELINE_ELEMENT_NAME (element)); \
g_object_get (G_OBJECT (binding), "control-source", &source, \
"object", &found_object, NULL); \
\
- fail_unless (found_object == child); \
+ if (child) \
+ fail_unless (found_object == child); \
g_object_unref (found_object); \
\
fail_unless (GST_IS_INTERPOLATION_CONTROL_SOURCE (source)); \
for (i = 0, tmp1 = timed_vals, tmp2 = found_timed_vals; tmp1 && tmp2; \
tmp1 = tmp1->next, tmp2 = tmp2->next, i++) { \
GstTimedValue *val1 = tmp1->data, *val2 = tmp2->data; \
- fail_unless (val1->timestamp == val2->timestamp && \
- val1->value == val2->value, "The %ith timed value (%lu: %g) " \
- "does not match the found timed value (%lu: %g)", \
- i, val1->timestamp, val1->value, val2->timestamp, val2->value); \
+ gdouble diff = (val1->value > val2->value) ? \
+ val1->value - val2->value : val2->value - val1->value; \
+ fail_unless (val1->timestamp == val2->timestamp && diff < 0.0001, \
+ "The %ith timed value (%lu: %g) does not match the found timed " \
+ "value (%lu: %g)", i, val1->timestamp, val1->value, \
+ val2->timestamp, val2->value); \
} \
fail_unless (tmp1 == NULL, "Found too few timed values"); \
fail_unless (tmp2 == NULL, "Found too many timed values"); \
\
g_list_free (found_timed_vals); \
g_object_get (G_OBJECT (source), "mode", &found_mode, NULL); \
- fail_unless (found_mode == GST_INTERPOLATION_MODE_CUBIC); \
+ fail_unless (found_mode == mode); \
g_object_unref (source); \
}
/* find the track element where the child property comes from */
fail_unless (track_el = _el_with_child_prop (clip, sub_child, prop));
_assert_int_val_child_prop (track_el, val, 30, prop, "posx");
+ ges_track_element_set_auto_clamp_control_sources (GES_TRACK_ELEMENT
+ (track_el), FALSE);
/* set a control binding */
timed_vals = g_slist_prepend (NULL, _new_timed_value (200, 5));
GST_END_TEST;
+#define _THREE_TIMED_VALS(timed_vals, tm1, val1, tm2, val2, tm3, val3) \
+ if (timed_vals) \
+ g_slist_free_full (timed_vals, g_free); \
+ timed_vals = g_slist_prepend (NULL, _new_timed_value (tm3, val3)); \
+ timed_vals = g_slist_prepend (timed_vals, _new_timed_value (tm2, val2)); \
+ timed_vals = g_slist_prepend (timed_vals, _new_timed_value (tm1, val1));
+
+#define _TWO_TIMED_VALS(timed_vals, tm1, val1, tm2, val2) \
+ if (timed_vals) \
+ g_slist_free_full (timed_vals, g_free); \
+ timed_vals = g_slist_prepend (NULL, _new_timed_value (tm2, val2)); \
+ timed_vals = g_slist_prepend (timed_vals, _new_timed_value (tm1, val1));
+
+#define _assert_control_source(obj, prop, vals) \
+ _assert_binding (obj, prop, NULL, vals, GST_INTERPOLATION_MODE_LINEAR);
+
+GST_START_TEST (test_children_property_bindings_with_rate_effects)
+{
+ GESTimeline *timeline;
+ GESTrack *track;
+ GESLayer *layer;
+ GESClip *clip;
+ GESTrackElement *video_source, *rate0, *rate1, *overlay;
+ GstControlSource *ctrl_source;
+ GSList *video_source_vals = NULL, *overlay_vals = NULL;
+ GValue value = G_VALUE_INIT;
+ GstControlBinding *binding;
+
+ ges_init ();
+
+ g_value_init (&value, G_TYPE_DOUBLE);
+
+ timeline = ges_timeline_new ();
+ track = GES_TRACK (ges_video_track_new ());
+ fail_unless (ges_timeline_add_track (timeline, track));
+
+ layer = ges_timeline_append_layer (timeline);
+
+ clip = GES_CLIP (ges_test_clip_new ());
+ assert_set_duration (clip, 4);
+ assert_set_start (clip, 20);
+ assert_set_inpoint (clip, 3);
+
+ fail_unless (ges_layer_add_clip (layer, clip));
+
+ video_source = ges_clip_find_track_element (clip, track, GES_TYPE_SOURCE);
+ fail_unless (video_source);
+ gst_object_unref (video_source);
+
+ rate0 = GES_TRACK_ELEMENT (ges_effect_new ("videorate rate=0.5"));
+ rate1 = GES_TRACK_ELEMENT (ges_effect_new ("videorate rate=4.0"));
+ overlay = GES_TRACK_ELEMENT (ges_effect_new ("textoverlay"));
+ ges_track_element_set_has_internal_source (overlay, TRUE);
+ assert_set_inpoint (overlay, 9);
+
+ fail_unless (ges_clip_add_top_effect (clip, GES_BASE_EFFECT (rate0), -1,
+ NULL));
+ fail_unless (ges_clip_add_top_effect (clip, GES_BASE_EFFECT (overlay), 0,
+ NULL));
+ fail_unless (ges_clip_add_top_effect (clip, GES_BASE_EFFECT (rate1), 0,
+ NULL));
+
+ fail_unless (ges_track_element_get_auto_clamp_control_sources (video_source));
+ fail_unless (ges_track_element_get_auto_clamp_control_sources (overlay));
+
+ /* source's alpha property */
+ _THREE_TIMED_VALS (video_source_vals, 1, 0.7, 7, 1.0, 15, 0.2);
+
+ ctrl_source = GST_CONTROL_SOURCE (gst_interpolation_control_source_new ());
+ g_object_set (G_OBJECT (ctrl_source), "mode",
+ GST_INTERPOLATION_MODE_LINEAR, NULL);
+ fail_unless (gst_timed_value_control_source_set_from_list
+ (GST_TIMED_VALUE_CONTROL_SOURCE (ctrl_source), video_source_vals));
+
+ fail_unless (ges_track_element_set_control_source (video_source, ctrl_source,
+ "alpha", "direct"));
+ gst_object_unref (ctrl_source);
+
+ /* values have been clamped between its in-point:3 and its
+ * out-point:11 (4ns in timeline is 8ns in source) */
+ _THREE_TIMED_VALS (video_source_vals, 3, 0.8, 7, 1.0, 11, 0.6);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* overlay's xpos property */
+ _THREE_TIMED_VALS (overlay_vals, 9, 12, 17, 16, 25, 8);
+
+ ctrl_source = GST_CONTROL_SOURCE (gst_interpolation_control_source_new ());
+ g_object_set (G_OBJECT (ctrl_source), "mode",
+ GST_INTERPOLATION_MODE_LINEAR, NULL);
+ fail_unless (gst_timed_value_control_source_set_from_list
+ (GST_TIMED_VALUE_CONTROL_SOURCE (ctrl_source), overlay_vals));
+
+ fail_unless (ges_track_element_set_control_source (overlay, ctrl_source,
+ "xpos", "direct-absolute"));
+ gst_object_unref (ctrl_source);
+
+ /* unchanged since values are at the edges already
+ * in-point:9 out-point:25 (4ns in timeline is 16ns in source) */
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* setting the in-point changes the in-point and out-point */
+ /* increase in-point */
+ assert_set_inpoint (video_source, 5);
+
+ _THREE_TIMED_VALS (video_source_vals, 5, 0.9, 7, 1.0, 13, 0.4);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* decrease in-point */
+ assert_set_inpoint (overlay, 7);
+
+ _THREE_TIMED_VALS (overlay_vals, 7, 11, 17, 16, 23, 10);
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* when trimming start, out-point should stay the same */
+ fail_unless (ges_timeline_element_edit_full (GES_TIMELINE_ELEMENT (clip),
+ -1, GES_EDIT_MODE_TRIM, GES_EDGE_START, 19, NULL));
+
+ /* in-point of video_source now 3 */
+ _THREE_TIMED_VALS (video_source_vals, 3, 0.8, 7, 1.0, 13, 0.4);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* in-point of video_source now 3 */
+ _THREE_TIMED_VALS (overlay_vals, 3, 9, 17, 16, 23, 10);
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* trim forwards */
+ fail_unless (ges_timeline_element_edit_full (GES_TIMELINE_ELEMENT (clip),
+ -1, GES_EDIT_MODE_TRIM, GES_EDGE_START, 20, NULL));
+
+ /* in-point of video_source now 5 again */
+ _THREE_TIMED_VALS (video_source_vals, 5, 0.9, 7, 1.0, 13, 0.4);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* in-point of overlay now 7 again */
+ _THREE_TIMED_VALS (overlay_vals, 7, 11, 17, 16, 23, 10);
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* trim end */
+ fail_unless (ges_timeline_element_edit_full (GES_TIMELINE_ELEMENT (clip),
+ -1, GES_EDIT_MODE_TRIM, GES_EDGE_END, 25, NULL));
+
+ /* out-point of video_source now 15 */
+ _THREE_TIMED_VALS (video_source_vals, 5, 0.9, 7, 1.0, 15, 0.2);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* out-point of overlay now 27 */
+ _THREE_TIMED_VALS (overlay_vals, 7, 11, 17, 16, 27, 6);
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* trim backwards */
+ fail_unless (ges_timeline_element_edit_full (GES_TIMELINE_ELEMENT (clip),
+ -1, GES_EDIT_MODE_TRIM, GES_EDGE_END, 23, NULL));
+
+ /* out-point of video_source now 11 */
+ _THREE_TIMED_VALS (video_source_vals, 5, 0.9, 7, 1.0, 11, 0.6);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* in-point of overlay now 19 */
+ _THREE_TIMED_VALS (overlay_vals, 7, 11, 17, 16, 19, 14);
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* changing the rate changes the out-point */
+ _assert_set_rate (rate0, "rate", 1.0, value);
+
+ /* out-point of video_source now 17 */
+ _THREE_TIMED_VALS (video_source_vals, 5, 0.9, 7, 1.0, 17, 0.0);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* unchanged for overlay, which is after rate0 */
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* change back */
+ _assert_set_rate (rate0, "rate", 0.5, value);
+
+ _THREE_TIMED_VALS (video_source_vals, 5, 0.9, 7, 1.0, 11, 0.6);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* unchanged for overlay, which is after rate0 */
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* make inactive */
+ fail_unless (ges_track_element_set_active (rate0, FALSE));
+
+ _THREE_TIMED_VALS (video_source_vals, 5, 0.9, 7, 1.0, 17, 0.0);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* unchanged for overlay, which is after rate0 */
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* make active again */
+ fail_unless (ges_track_element_set_active (rate0, TRUE));
+
+ _THREE_TIMED_VALS (video_source_vals, 5, 0.9, 7, 1.0, 11, 0.6);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* unchanged for overlay, which is after rate0 */
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* change order */
+ fail_unless (ges_clip_set_top_effect_index (clip, GES_BASE_EFFECT (overlay),
+ 2));
+
+ /* video source unchanged */
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* new out-point is 13
+ * new value is interpolated between the previous value
+ * (at time 7, value 11) and the *final* value (at time 19, value 14)
+ * Not the middle value at time 17, value 16! */
+ _TWO_TIMED_VALS (overlay_vals, 7, 11, 13, 12.5);
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* removing time effect changes out-point */
+ gst_object_ref (rate0);
+ fail_unless (ges_clip_remove_top_effect (clip, GES_BASE_EFFECT (rate0),
+ NULL));
+
+ _THREE_TIMED_VALS (video_source_vals, 5, 0.9, 7, 1.0, 17, 0.0);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ _TWO_TIMED_VALS (overlay_vals, 7, 11, 19, 14);
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* adding also changes it */
+ fail_unless (ges_clip_add_top_effect (clip, GES_BASE_EFFECT (rate0), 2,
+ NULL));
+
+ _THREE_TIMED_VALS (video_source_vals, 5, 0.9, 7, 1.0, 11, 0.6);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* unchanged for overlay */
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ /* new value will use the value already set at in-point if possible */
+
+ assert_set_inpoint (video_source, 7);
+
+ _TWO_TIMED_VALS (video_source_vals, 7, 1.0, 13, 0.4);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* same with out-point for overlay */
+ binding = ges_track_element_get_control_binding (overlay, "xpos");
+ fail_unless (binding);
+ g_object_get (binding, "control-source", &ctrl_source, NULL);
+
+ fail_unless (gst_timed_value_control_source_set
+ (GST_TIMED_VALUE_CONTROL_SOURCE (ctrl_source), 11, 5));
+ gst_object_unref (ctrl_source);
+ _THREE_TIMED_VALS (overlay_vals, 7, 11, 11, 5, 19, 14);
+
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ fail_unless (ges_timeline_element_edit_full (GES_TIMELINE_ELEMENT (clip),
+ -1, GES_EDIT_MODE_TRIM, GES_EDGE_END, 21, NULL));
+
+ _TWO_TIMED_VALS (video_source_vals, 7, 1.0, 9, 0.8);
+ _assert_control_source (video_source, "alpha", video_source_vals);
+
+ /* overlay uses existing value, rather than an interpolation */
+ _TWO_TIMED_VALS (overlay_vals, 7, 11, 11, 5);
+ _assert_control_source (overlay, "xpos", overlay_vals);
+
+ g_slist_free_full (video_source_vals, g_free);
+ g_slist_free_full (overlay_vals, g_free);
+
+ gst_object_unref (timeline);
+ g_value_unset (&value);
+
+ ges_deinit ();
+}
+
+GST_END_TEST;
+
GST_START_TEST (test_unchanged_after_layer_add_failure)
{
GList *found;
tcase_add_test (tc_chain, test_children_properties_contain);
tcase_add_test (tc_chain, test_children_properties_change);
tcase_add_test (tc_chain, test_copy_paste_children_properties);
+ tcase_add_test (tc_chain, test_children_property_bindings_with_rate_effects);
tcase_add_test (tc_chain, test_unchanged_after_layer_add_failure);
tcase_add_test (tc_chain, test_convert_time);
(source), 5 * GST_SECOND, 0.);
gst_timed_value_control_source_set (GST_TIMED_VALUE_CONTROL_SOURCE
(source), 10 * GST_SECOND, 1.);
+
+ gst_object_unref (source);
} else if (GES_IS_VIDEO_SOURCE (element)) {
/* Adding children properties */
gint64 posx = 42;
fail_unless (value->value == 1.);
fail_unless (value->timestamp == 10 * GST_SECOND);
g_list_free (timed_values);
+ gst_object_unref (source);
}
/* Checking children properties */
else if (GES_IS_VIDEO_SOURCE (element)) {