clip: enforce duration-limit
authorHenry Wilkes <hwilkes@igalia.com>
Thu, 30 Apr 2020 11:10:22 +0000 (12:10 +0100)
committerHenry Wilkes <hwilkes@igalia.com>
Thu, 7 May 2020 09:06:52 +0000 (10:06 +0100)
Prevent setting of properties or that of children, if the clip would not
be able to set the corresponding duration if the duration-limit would
drop below the currently set duration.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-editing-services/-/merge_requests/169>

ges/ges-clip.c
ges/ges-internal.h
ges/ges-track-element.c
tests/check/ges/clip.c
tests/check/ges/test-utils.h

index abd84687f5ed39f7b1917ef2b5d920a10451f7dc..62cd38d914f21ab97a1e0b04a41b3c7310cb9685 100644 (file)
@@ -128,8 +128,10 @@ struct _GESClipPrivate
   gboolean prevent_resort;
 
   gboolean updating_max_duration;
-  gboolean prevent_max_duration_update;
+  gboolean setting_max_duration;
   gboolean setting_inpoint;
+  gboolean setting_priority;
+  gboolean setting_active;
 
   gboolean allow_any_track;
 
@@ -138,6 +140,8 @@ struct _GESClipPrivate
 
   GstClockTime duration_limit;
   gboolean prevent_duration_limit_update;
+
+  gboolean allow_any_remove;
 };
 
 enum
@@ -163,7 +167,10 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESClip, ges_clip,
         ges_extractable_interface_init));
 
 /****************************************************
+ *                                                  *
  *              Listen to our children              *
+ *                 and restrict them                *
+ *                                                  *
  ****************************************************/
 
 #define _IS_CORE_CHILD(child) GES_TRACK_ELEMENT_IS_CORE(child)
@@ -183,6 +190,9 @@ G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESClip, ges_clip,
   (GST_CLOCK_TIME_IS_VALID (first) && (!GST_CLOCK_TIME_IS_VALID (second) \
   || first < second))
 
+/****************************************************
+ *                 duration-limit                   *
+ ****************************************************/
 
 typedef struct _DurationLimitData
 {
@@ -230,6 +240,22 @@ _duration_limit_data_list (GESClip * clip)
   return list;
 }
 
+/* transfer-full of data */
+static GList *
+_duration_limit_data_list_with_data (GESClip * clip, DurationLimitData * data)
+{
+  GList *tmp, *list = g_list_append (NULL, data);
+
+  for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
+    GESTrackElement *child = tmp->data;
+    if (data->child == child)
+      continue;
+    list = g_list_prepend (list, _duration_limit_data_new (child));
+  }
+
+  return list;
+}
+
 static gint
 _cmp_by_track_then_priority (gconstpointer a_p, gconstpointer b_p)
 {
@@ -250,6 +276,7 @@ _cmp_by_track_then_priority (gconstpointer a_p, gconstpointer b_p)
   ((data->active && GST_CLOCK_TIME_IS_VALID (data->max_duration)) ? \
     data->max_duration - data->inpoint : GST_CLOCK_TIME_NONE)
 
+/* transfer-full of child_data */
 static GstClockTime
 _calculate_duration_limit (GESClip * self, GList * child_data)
 {
@@ -341,6 +368,30 @@ _update_duration_limit (GESClip * self)
   }
 }
 
+/* transfer full of child_data */
+static gboolean
+_can_update_duration_limit (GESClip * self, GList * child_data)
+{
+  GESTimeline *timeline = GES_TIMELINE_ELEMENT_TIMELINE (self);
+  GstClockTime duration = _calculate_duration_limit (self, child_data);
+  GESTimelineElement *element = GES_TIMELINE_ELEMENT (self);
+
+  if (_CLOCK_TIME_IS_LESS (duration, element->duration)) {
+    /* NOTE: timeline would normally not be NULL at this point */
+    if (timeline
+        && !timeline_tree_can_move_element (timeline_get_tree (timeline),
+            element, ges_timeline_element_get_layer_priority (element),
+            element->start, duration)) {
+      return FALSE;
+    }
+  }
+  return TRUE;
+}
+
+/****************************************************
+ *                    priority                      *
+ ****************************************************/
+
 /* @min_priority: The absolute minimum priority a child of @container should have
  * @max_priority: The absolute maximum priority a child of @container should have
  */
@@ -367,6 +418,32 @@ _get_priority_range (GESContainer * container, guint32 * min_priority,
       _PRIORITY (container));
 }
 
+gboolean
+ges_clip_can_set_priority_of_child (GESClip * clip, GESTrackElement * child,
+    guint32 priority)
+{
+  GList *child_data;
+  DurationLimitData *data;
+
+  if (clip->priv->setting_priority)
+    return TRUE;
+
+  data = _duration_limit_data_new (child);
+  data->priority = priority;
+
+  child_data = _duration_limit_data_list_with_data (clip, data);
+
+  if (!_can_update_duration_limit (clip, child_data)) {
+    GST_INFO_OBJECT (clip, "Cannot move the child %" GES_FORMAT " from "
+        "priority %" G_GUINT32_FORMAT " to %" G_GUINT32_FORMAT " because "
+        "the duration-limit cannot be adjusted", GES_ARGS (child),
+        _PRIORITY (child), priority);
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
 static void
 _child_priority_changed (GESContainer * container, GESTimelineElement * child)
 {
@@ -381,6 +458,93 @@ _child_priority_changed (GESContainer * container, GESTimelineElement * child)
   }
 }
 
+/****************************************************
+ *                    in-point                      *
+ ****************************************************/
+
+static gboolean
+_can_set_inpoint_of_core_children (GESClip * clip, GstClockTime inpoint)
+{
+  GList *tmp;
+  GList *child_data = NULL;
+
+  if (GES_TIMELINE_ELEMENT_BEING_EDITED (clip))
+    return TRUE;
+
+  /* setting the in-point of a core child will shift the in-point of all
+   * core children with an internal source */
+  for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
+    GESTimelineElement *child = tmp->data;
+    DurationLimitData *data =
+        _duration_limit_data_new (GES_TRACK_ELEMENT (child));
+
+    if (_IS_CORE_INTERNAL_SOURCE_CHILD (child)) {
+      if (GST_CLOCK_TIME_IS_VALID (child->maxduration)
+          && child->maxduration < inpoint) {
+        GST_INFO_OBJECT (clip, "Cannot set the in-point from %"
+            G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT " because it would "
+            "cause the in-point of its core child %" GES_FORMAT
+            " to exceed its max-duration", _INPOINT (clip), inpoint,
+            GES_ARGS (child));
+
+        _duration_limit_data_free (data);
+        g_list_free_full (child_data, _duration_limit_data_free);
+        return FALSE;
+      }
+
+      data->inpoint = inpoint;
+    }
+
+    child_data = g_list_prepend (child_data, data);
+  }
+
+  if (!_can_update_duration_limit (clip, child_data)) {
+    GST_INFO_OBJECT (clip, "Cannot set the in-point from %" G_GUINT64_FORMAT
+        " to %" G_GUINT64_FORMAT " because the duration-limit cannot be "
+        "adjusted", _INPOINT (clip), inpoint);
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/* Whether @clip can have its in-point set to @inpoint because none of
+ * its children have a max-duration below it */
+gboolean
+ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTrackElement * child,
+    GstClockTime inpoint)
+{
+  /* don't bother checking if we are setting the value */
+  if (clip->priv->setting_inpoint)
+    return TRUE;
+
+  if (GES_TIMELINE_ELEMENT_BEING_EDITED (child))
+    return TRUE;
+
+  if (!_IS_CORE_CHILD (child)) {
+    /* no other sibling will move */
+    GList *child_data;
+    DurationLimitData *data = _duration_limit_data_new (child);
+    data->inpoint = inpoint;
+
+    child_data = _duration_limit_data_list_with_data (clip, data);
+
+    if (!_can_update_duration_limit (clip, child_data)) {
+      GST_INFO_OBJECT (clip, "Cannot set the in-point of non-core child %"
+          GES_FORMAT " from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT
+          " because the duration-limit cannot be adjusted", GES_ARGS (child),
+          _INPOINT (child), inpoint);
+      return FALSE;
+    }
+
+    return TRUE;
+  }
+
+  /* setting the in-point of a core child will shift the in-point of all
+   * core children with an internal source */
+  return _can_set_inpoint_of_core_children (clip, inpoint);
+}
+
 /* returns TRUE if duration-limit needs to be updated */
 static gboolean
 _child_inpoint_changed (GESClip * self, GESTimelineElement * child)
@@ -406,7 +570,10 @@ _child_inpoint_changed (GESClip * self, GESTimelineElement * child)
   return FALSE;
 }
 
-/* called when a child is added, removed or their max-duration changes */
+/****************************************************
+ *                  max-duration                    *
+ ****************************************************/
+
 static void
 _update_max_duration (GESContainer * container)
 {
@@ -414,7 +581,7 @@ _update_max_duration (GESContainer * container)
   GstClockTime min = GST_CLOCK_TIME_NONE;
   GESClipPrivate *priv = GES_CLIP (container)->priv;
 
-  if (priv->prevent_max_duration_update)
+  if (priv->setting_max_duration)
     return;
 
   for (tmp = container->children; tmp; tmp = tmp->next) {
@@ -427,6 +594,32 @@ _update_max_duration (GESContainer * container)
   priv->updating_max_duration = FALSE;
 }
 
+gboolean
+ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child,
+    GstClockTime max_duration)
+{
+  GList *child_data;
+  DurationLimitData *data;
+
+  if (clip->priv->setting_max_duration)
+    return TRUE;
+
+  data = _duration_limit_data_new (child);
+  data->max_duration = max_duration;
+
+  child_data = _duration_limit_data_list_with_data (clip, data);
+
+  if (!_can_update_duration_limit (clip, child_data)) {
+    GST_INFO_OBJECT (clip, "Cannot set the max-duration of child %"
+        GES_FORMAT " from %" G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT
+        " because the duration-limit cannot be adjusted", GES_ARGS (child),
+        _MAXDURATION (child), max_duration);
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
 static void
 _child_max_duration_changed (GESContainer * container,
     GESTimelineElement * child)
@@ -438,20 +631,80 @@ _child_max_duration_changed (GESContainer * container,
   _update_max_duration (container);
 }
 
+/****************************************************
+ *               has-internal-source                *
+ ****************************************************/
+
 static void
 _child_has_internal_source_changed (GESClip * self, GESTimelineElement * child)
 {
   /* ignore non-core */
   /* if the track element is now registered to have no internal content,
-   * we don't have to do anything */
+   * we don't have to do anything
+   * Note that the change in max-duration and in-point will already trigger
+   * a change in the duration-limit, which can only increase since the
+   * max-duration is now GST_CLOCK_TIME_NONE */
   if (!_IS_CORE_INTERNAL_SOURCE_CHILD (child))
     return;
 
-  /* otherwise, we need to make its in-point match ours */
+  /* otherwise, we need to make its in-point match ours
+   * Note that the duration-limit will be GST_CLOCK_TIME_NONE, so this
+   * should not change the duration-limit */
   _set_inpoint0 (child, _INPOINT (self));
 }
 
-#define _IS_PROP(prop) (g_strcmp0 (name, prop) == 0)
+/****************************************************
+ *                     active                       *
+ ****************************************************/
+
+gboolean
+ges_clip_can_set_active_of_child (GESClip * clip, GESTrackElement * child,
+    gboolean active)
+{
+  GESTrack *track = ges_track_element_get_track (child);
+  gboolean is_core = _IS_CORE_CHILD (child);
+  GList *child_data = NULL;
+  DurationLimitData *data;
+
+  if (clip->priv->setting_active)
+    return TRUE;
+
+  /* We want to ensure that each active non-core element has a
+   * corresponding active core element in the same track */
+  if (!track || is_core == active) {
+    /* only the one child will change */
+    data = _duration_limit_data_new (child);
+    data->active = active;
+    child_data = _duration_limit_data_list_with_data (clip, data);
+  } else {
+    GList *tmp;
+    /* If we are core, make all the non-core elements in-active
+     * If we are non-core, make the core element active */
+    for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
+      GESTrackElement *sibling = tmp->data;
+      data = _duration_limit_data_new (sibling);
+
+      if (sibling == child)
+        data->active = active;
+
+      if (ges_track_element_get_track (sibling) == track
+          && _IS_CORE_CHILD (sibling) != is_core
+          && ges_track_element_is_active (sibling) != active)
+        data->active = active;
+
+      child_data = g_list_prepend (child_data, data);
+    }
+  }
+
+  if (!_can_update_duration_limit (clip, child_data)) {
+    GST_INFO_OBJECT (clip, "Cannot set the active of child %" GES_FORMAT
+        " from %i to %i because the duration-limit cannot be adjusted",
+        GES_ARGS (child), ges_track_element_is_active (child), active);
+    return FALSE;
+  }
+
+  return TRUE;
+}
 
 static void
 _child_active_changed (GESClip * self, GESTrackElement * child)
@@ -467,6 +720,7 @@ _child_active_changed (GESClip * self, GESTrackElement * child)
   if (self->priv->setting_active || !track || is_core == active)
     return;
 
+  self->priv->setting_active = TRUE;
   self->priv->prevent_duration_limit_update = TRUE;
 
   /* If we are core, make all the non-core elements in-active
@@ -489,11 +743,12 @@ _child_active_changed (GESClip * self, GESTrackElement * child)
     }
   }
 
+  self->priv->setting_active = FALSE;
   self->priv->prevent_duration_limit_update = prev_prevent;
 }
 
 /****************************************************
- *              Restrict our children               *
+ *                      track                       *
  ****************************************************/
 
 static GESTrackElement *
@@ -524,6 +779,8 @@ gboolean
 ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child,
     GESTrack * track)
 {
+  GList *child_data;
+  DurationLimitData *data;
   GESTrack *current_track = ges_track_element_get_track (child);
   GESTrackElement *core = NULL;
 
@@ -584,6 +841,28 @@ ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child,
       }
     }
   }
+
+  data = _duration_limit_data_new (child);
+  gst_clear_object (&data->track);
+  data->track = track ? gst_object_ref (track) : NULL;
+  if (core && !ges_track_element_is_active (core)) {
+    /* if core is set, then we are adding a non-core to a track containing
+     * a core track element. If this happens, but the core is in-active
+     * then we will make the non-core element also inactive upon setting
+     * its track */
+    data->active = FALSE;
+  }
+
+  child_data = _duration_limit_data_list_with_data (clip, data);
+
+  if (!_can_update_duration_limit (clip, child_data)) {
+    GST_INFO_OBJECT (clip, "Cannot move the child %" GES_FORMAT " from "
+        "track %" GST_PTR_FORMAT " to track %" GST_PTR_FORMAT " because "
+        "the duration-limit cannot be adjusted", GES_ARGS (child),
+        current_track, track);
+    return FALSE;
+  }
+
   return TRUE;
 }
 
@@ -617,12 +896,14 @@ _update_active_for_track (GESClip * self, GESTrackElement * child)
         " since the core child in the same track %" GST_PTR_FORMAT " is "
         "not active", GES_ARGS (child), track);
 
+    self->priv->setting_active = TRUE;
     self->priv->prevent_duration_limit_update = TRUE;
 
     if (!ges_track_element_set_active (child, FALSE))
       GST_ERROR_OBJECT (self, "Failed to de-activate child %" GES_FORMAT,
           GES_ARGS (child));
 
+    self->priv->setting_active = FALSE;
     self->priv->prevent_duration_limit_update = prev_prevent;
   }
 }
@@ -688,33 +969,6 @@ _set_start (GESTimelineElement * element, GstClockTime start)
   return TRUE;
 }
 
-/* Whether @clip can have its in-point set to @inpoint because none of
- * its children have a max-duration below it */
-gboolean
-ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTimelineElement * child,
-    GstClockTime inpoint)
-{
-  GList *tmp;
-
-  /* don't bother checking if we are setting the value */
-  if (clip->priv->setting_inpoint)
-    return TRUE;
-
-  /* non-core children do not effect our in-point */
-  if (!_IS_CORE_CHILD (child))
-    return TRUE;
-
-  for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
-    GESTimelineElement *child = tmp->data;
-
-    if (_IS_CORE_INTERNAL_SOURCE_CHILD (child)
-        && GST_CLOCK_TIME_IS_VALID (child->maxduration)
-        && child->maxduration < inpoint)
-      return FALSE;
-  }
-  return TRUE;
-}
-
 /* returns TRUE if we did not break early */
 static gboolean
 _set_childrens_inpoint (GESTimelineElement * element, GstClockTime inpoint,
@@ -754,6 +1008,12 @@ _set_childrens_inpoint (GESTimelineElement * element, GstClockTime inpoint,
 static gboolean
 _set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
 {
+  if (!_can_set_inpoint_of_core_children (GES_CLIP (element), inpoint)) {
+    GST_WARNING_OBJECT (element, "Cannot set the in-point to %"
+        GST_TIME_FORMAT, GST_TIME_ARGS (inpoint));
+    return FALSE;
+  }
+
   if (!_set_childrens_inpoint (element, inpoint, TRUE)) {
     _set_childrens_inpoint (element, element->inpoint, FALSE);
     return FALSE;
@@ -786,6 +1046,7 @@ static gboolean
 _set_max_duration (GESTimelineElement * element, GstClockTime maxduration)
 {
   GList *tmp;
+  GList *child_data = NULL;
   GESClip *self = GES_CLIP (element);
   GESClipPrivate *priv = self->priv;
   GstClockTime new_min = GST_CLOCK_TIME_NONE;
@@ -799,8 +1060,26 @@ _set_max_duration (GESTimelineElement * element, GstClockTime maxduration)
 
   /* else, we set every core child to have the same max duration */
 
+  /* check that the duration-limit can be changed */
+  for (tmp = GES_CONTAINER_CHILDREN (self); tmp; tmp = tmp->next) {
+    GESTrackElement *child = tmp->data;
+    DurationLimitData *data = _duration_limit_data_new (child);
+
+    if (_IS_CORE_INTERNAL_SOURCE_CHILD (child))
+      data->max_duration = maxduration;
+
+    child_data = g_list_prepend (child_data, data);
+  }
+
+  if (!_can_update_duration_limit (self, child_data)) {
+    GST_WARNING_OBJECT (self, "Cannot set the max-duration from %"
+        G_GUINT64_FORMAT " to %" G_GUINT64_FORMAT " because the "
+        "duration-limit cannot be adjusted", element->maxduration, maxduration);
+    return FALSE;
+  }
+
   priv->prevent_duration_limit_update = TRUE;
-  priv->prevent_max_duration_update = TRUE;
+  priv->setting_max_duration = TRUE;
   for (tmp = GES_CONTAINER_CHILDREN (element); tmp; tmp = tmp->next) {
     GESTimelineElement *child = tmp->data;
 
@@ -815,7 +1094,7 @@ _set_max_duration (GESTimelineElement * element, GstClockTime maxduration)
       }
     }
   }
-  priv->prevent_max_duration_update = FALSE;
+  priv->setting_max_duration = FALSE;
   priv->prevent_duration_limit_update = prev_prevent;
 
   if (!has_core) {
@@ -874,6 +1153,7 @@ _set_priority (GESTimelineElement * element, guint32 priority)
   /* offsets will remain constant for the children */
   priv->prevent_resort = TRUE;
   priv->prevent_duration_limit_update = TRUE;
+  priv->setting_priority = TRUE;
   for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
     GESTimelineElement *child = tmp->data;
     guint32 track_element_prio = min_prio + (child->priority - min_child_prio);
@@ -891,6 +1171,7 @@ _set_priority (GESTimelineElement * element, guint32 priority)
    * offsets. As such, the height and duration-limit remains the same as
    * well. */
   priv->prevent_resort = FALSE;
+  priv->setting_priority = FALSE;
   priv->prevent_duration_limit_update = prev_prevent;
 
   return TRUE;
@@ -1010,33 +1291,19 @@ _add_child (GESContainer * container, GESTimelineElement * element)
     /* NOTE: Core track elements that are base effects are added like any
      * other core elements. In particular, they are *not* added to the
      * list of added effects, so we do not increase nb_effects. */
-    GESTrackElement *core = (track && !priv->allow_any_track) ?
-        _find_core_in_track (self, track) : NULL;
-
-    if (core) {
-      GST_WARNING_OBJECT (self, "Cannot add the core child %" GES_FORMAT
-          " because it is in the same track %" GST_PTR_FORMAT " as an "
-          "existing core child %" GES_FORMAT, GES_ARGS (element), track,
-          GES_ARGS (core));
-      return FALSE;
-    }
 
     /* Set the core element to have the same in-point, which we don't
      * apply to effects */
-    if (ges_track_element_has_internal_source (track_el)) {
-      /* adding can fail if the max-duration of the element is smaller
-       * than the current in-point of the clip */
-      if (!_set_inpoint0 (element, _INPOINT (self))) {
-        GST_ERROR_OBJECT (element, "Could not set the in-point of the "
-            "element %" GES_FORMAT " to %" GST_TIME_FORMAT ". Not adding "
-            "as a child", GES_ARGS (element), GST_TIME_ARGS (_INPOINT (self)));
-        return FALSE;
-      }
-    }
+    GstClockTime new_inpoint;
+    if (ges_track_element_has_internal_source (track_el))
+      new_inpoint = _INPOINT (self);
+    else
+      new_inpoint = 0;
 
     /* new priority is that of the lowest priority core child. Usually
      * each core child has the same priority.
      * Also must be lower than all effects */
+
     new_prio = min_prio;
     for (tmp = container->children; tmp; tmp = tmp->next) {
       if (_IS_CORE_CHILD (tmp->data))
@@ -1044,20 +1311,50 @@ _add_child (GESContainer * container, GESTimelineElement * element)
       else if (_IS_TOP_EFFECT (tmp->data))
         new_prio = MAX (new_prio, _PRIORITY (tmp->data) + 1);
     }
+
+    if (track && !priv->allow_any_track) {
+      GList *child_data;
+      DurationLimitData *data;
+      GESTrackElement *core = _find_core_in_track (self, track);
+
+      if (core) {
+        GST_WARNING_OBJECT (self, "Cannot add the core child %" GES_FORMAT
+            " because it is in the same track %" GST_PTR_FORMAT " as an "
+            "existing core child %" GES_FORMAT, GES_ARGS (element), track,
+            GES_ARGS (core));
+        return FALSE;
+      }
+
+      data = _duration_limit_data_new (track_el);
+      data->inpoint = new_inpoint;
+      data->priority = new_prio;
+
+      child_data = _duration_limit_data_list_with_data (self, data);
+
+      if (!_can_update_duration_limit (self, child_data)) {
+        GST_WARNING_OBJECT (self, "Cannot add core %" GES_FORMAT " as a "
+            "child because the duration-limit cannot be adjusted",
+            GES_ARGS (element));
+        return FALSE;
+      }
+    }
+
+    /* adding can fail if the max-duration of the element is smaller
+     * than the current in-point of the clip */
+    if (!_set_inpoint0 (element, new_inpoint)) {
+      GST_WARNING_OBJECT (self, "Could not set the in-point of the "
+          "element %" GES_FORMAT " to %" GST_TIME_FORMAT ". Not adding "
+          "as a child", GES_ARGS (element), GST_TIME_ARGS (new_inpoint));
+      return FALSE;
+    }
+
     _set_priority0 (element, new_prio);
+
   } else if (GES_CLIP_CLASS_CAN_ADD_EFFECTS (klass) && _IS_TOP_EFFECT (element)) {
     /* Add the effect at the lowest priority among effects (just after
      * the core elements). Need to shift the core elements up by 1
      * to make room. */
 
-    if (track && !priv->allow_any_track && !_find_core_in_track (self, track)) {
-      GST_WARNING_OBJECT (self, "Cannot add the effect %" GES_FORMAT
-          " because its track %" GST_PTR_FORMAT " does not contain one "
-          "of the clip's core children", GES_ARGS (element), track);
-      return FALSE;
-    }
-    _update_active_for_track (self, track_el);
-
     /* new priority is the lowest priority effect */
     new_prio = min_prio;
     for (tmp = container->children; tmp; tmp = tmp->next) {
@@ -1070,12 +1367,48 @@ _add_child (GESContainer * container, GESTimelineElement * element)
         new_prio = MIN (new_prio, _PRIORITY (tmp->data));
     }
 
+    if (track && !priv->allow_any_track) {
+      GList *child_data, *tmp;
+      DurationLimitData *data;
+      GESTrackElement *core = _find_core_in_track (self, track);
+
+      if (!core) {
+        GST_WARNING_OBJECT (self, "Cannot add the effect %" GES_FORMAT
+            " because its track %" GST_PTR_FORMAT " does not contain one "
+            "of the clip's core children", GES_ARGS (element), track);
+        return FALSE;
+      }
+
+      data = _duration_limit_data_new (track_el);
+      data->priority = new_prio;
+      if (!ges_track_element_is_active (core))
+        data->active = FALSE;
+
+      child_data = _duration_limit_data_list_with_data (self, data);
+
+      for (tmp = child_data; tmp; tmp = tmp->next) {
+        data = tmp->data;
+        if (data->priority >= new_prio)
+          data->priority++;
+      }
+
+      if (!_can_update_duration_limit (self, child_data)) {
+        GST_WARNING_OBJECT (self, "Cannot add effect %" GES_FORMAT " as "
+            "a child because the duration-limit cannot be adjusted",
+            GES_ARGS (element));
+        return FALSE;
+      }
+    }
+
+    _update_active_for_track (self, track_el);
+
     priv->nb_effects++;
     GST_DEBUG_OBJECT (self, "Adding %ith effect: %" GES_FORMAT
         " Priority %i", priv->nb_effects, GES_ARGS (element), new_prio);
 
     /* changing priorities, and updating their offset */
     priv->prevent_resort = TRUE;
+    priv->setting_priority = TRUE;
     priv->prevent_duration_limit_update = TRUE;
 
     /* increase the priority of anything with a lower priority */
@@ -1087,6 +1420,7 @@ _add_child (GESContainer * container, GESTimelineElement * element)
     _set_priority0 (element, new_prio);
 
     priv->prevent_resort = FALSE;
+    priv->setting_priority = FALSE;
     priv->prevent_duration_limit_update = prev_prevent;
     /* no need to call _ges_container_sort_children (container) since
      * there is no change to the ordering yet (this happens after the
@@ -1120,7 +1454,34 @@ _add_child (GESContainer * container, GESTimelineElement * element)
 static gboolean
 _remove_child (GESContainer * container, GESTimelineElement * element)
 {
-  GESClipPrivate *priv = GES_CLIP (container)->priv;
+  GESTrackElement *el = GES_TRACK_ELEMENT (element);
+  GESClip *self = GES_CLIP (container);
+  GESClipPrivate *priv = self->priv;
+
+  /* check that the duration-limit can be changed */
+  /* If we are removing a core child, then all other children in the
+   * same track will be removed from the track, which will make the
+   * duration-limit increase, which is safe
+   * Similarly, if it has no track, the duration-limit will not change */
+  if (!priv->allow_any_remove && !_IS_CORE_CHILD (element) &&
+      ges_track_element_get_track (el)) {
+    GList *child_data = NULL;
+    GList *tmp;
+
+    for (tmp = container->children; tmp; tmp = tmp->next) {
+      GESTrackElement *child = tmp->data;
+      if (child == el)
+        continue;
+      child_data =
+          g_list_prepend (child_data, _duration_limit_data_new (child));
+    }
+
+    if (!_can_update_duration_limit (self, child_data)) {
+      GST_WARNING_OBJECT (self, "Cannot remove the child %" GES_FORMAT
+          " because the duration-limit cannot be adjusted", GES_ARGS (el));
+      return FALSE;
+    }
+  }
 
   /* NOTE: notifies are currently frozen by ges_container_add */
   if (_IS_TOP_EFFECT (element)) {
@@ -1130,6 +1491,7 @@ _remove_child (GESContainer * container, GESTimelineElement * element)
 
     /* changing priorities, so preventing a re-sort */
     priv->prevent_resort = TRUE;
+    priv->setting_priority = TRUE;
     priv->prevent_duration_limit_update = TRUE;
     for (tmp = container->children; tmp; tmp = tmp->next) {
       guint32 sibling_prio = GES_TIMELINE_ELEMENT_PRIORITY (tmp->data);
@@ -1139,6 +1501,7 @@ _remove_child (GESContainer * container, GESTimelineElement * element)
     }
     priv->nb_effects--;
     priv->prevent_resort = FALSE;
+    priv->setting_priority = FALSE;
     priv->prevent_duration_limit_update = prev_prevent;
     /* no need to re-sort the children since the rest keep the same
      * relative priorities */
@@ -1212,8 +1575,10 @@ _transfer_child (GESClip * from_clip, GESClip * to_clip,
   from_clip->priv->prevent_duration_limit_update = TRUE;
   to_clip->priv->prevent_duration_limit_update = TRUE;
 
+  from_clip->priv->allow_any_remove = TRUE;
   ges_container_remove (GES_CONTAINER (from_clip),
       GES_TIMELINE_ELEMENT (child));
+  from_clip->priv->allow_any_remove = FALSE;
 
   to_clip->priv->allow_any_track = TRUE;
   if (!ges_container_add (GES_CONTAINER (to_clip),
@@ -1669,6 +2034,8 @@ ges_clip_dispose (GObject * object)
 {
   GESClip *self = GES_CLIP (object);
 
+  self->priv->allow_any_remove = TRUE;
+
   g_list_free_full (self->priv->copied_track_elements, gst_object_unref);
   self->priv->copied_track_elements = NULL;
   g_clear_object (&self->priv->copied_layer);
@@ -2261,11 +2628,35 @@ ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
   else
     inc = +1;
 
+  /* check that the duration-limit can be changed */
+  for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
+    GESTimelineElement *child = tmp->data;
+    guint32 priority = child->priority;
+    DurationLimitData *data =
+        _duration_limit_data_new (GES_TRACK_ELEMENT (child));
+
+    if (child == element)
+      data->priority = new_prio;
+    else if ((inc == +1 && priority >= new_prio && priority < current_prio)
+        || (inc == -1 && priority <= new_prio && priority > current_prio))
+      data->priority = child->priority + inc;
+
+    child_data = g_list_prepend (child_data, data);
+  }
+
+  if (!_can_update_duration_limit (clip, child_data)) {
+    GST_WARNING_OBJECT (clip, "Cannot move top effect %" GES_FORMAT
+        " to index %i because the duration-limit cannot adjust",
+        GES_ARGS (effect), newindex);
+    return FALSE;
+  }
+
   GST_DEBUG_OBJECT (clip, "Setting top effect %" GST_PTR_FORMAT "priority: %i",
       effect, new_prio);
 
   /* prevent a re-sort of the list whilst we are traversing it! */
   clip->priv->prevent_resort = TRUE;
+  clip->priv->setting_priority = TRUE;
   for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
     GESTimelineElement *child = tmp->data;
     guint32 priority = child->priority;
@@ -2282,6 +2673,7 @@ ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect,
   _set_priority0 (element, new_prio);
 
   clip->priv->prevent_resort = FALSE;
+  clip->priv->setting_priority = FALSE;
   _ges_container_sort_children (GES_CONTAINER (clip));
   /* height should have stayed the same */
 
index 55841108d829ce0e7eb008fc37e991cbbd5607dc..8584cd4e78895ef2b238abe454fe7598e9a1a573 100644 (file)
@@ -404,7 +404,10 @@ G_GNUC_INTERNAL gboolean          ges_clip_is_moving_from_layer   (GESClip *clip
 G_GNUC_INTERNAL void              ges_clip_set_moving_from_layer  (GESClip *clip, gboolean is_moving);
 G_GNUC_INTERNAL GESTrackElement*  ges_clip_create_track_element   (GESClip *clip, GESTrackType type);
 G_GNUC_INTERNAL GList*            ges_clip_create_track_elements  (GESClip *clip, GESTrackType type);
-G_GNUC_INTERNAL gboolean          ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTimelineElement * child, GstClockTime inpoint);
+G_GNUC_INTERNAL gboolean          ges_clip_can_set_inpoint_of_child (GESClip * clip, GESTrackElement * child, GstClockTime inpoint);
+G_GNUC_INTERNAL gboolean          ges_clip_can_set_max_duration_of_child (GESClip * clip, GESTrackElement * child, GstClockTime max_duration);
+G_GNUC_INTERNAL gboolean          ges_clip_can_set_active_of_child (GESClip * clip, GESTrackElement * child, gboolean active);
+G_GNUC_INTERNAL gboolean          ges_clip_can_set_priority_of_child (GESClip * clip, GESTrackElement * child, guint32 priority);
 G_GNUC_INTERNAL gboolean          ges_clip_can_set_track_of_child (GESClip * clip, GESTrackElement * child, GESTrack * tack);
 G_GNUC_INTERNAL void              ges_clip_empty_from_track       (GESClip * clip, GESTrack * track);
 
index a8adc0141bbdd51baa78ee81e176b4ccbf68760e..6061ddf4b94c031a4bc3d884557bb52304513966 100644 (file)
@@ -629,21 +629,21 @@ static gboolean
 _set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
 {
   GESTrackElement *object = GES_TRACK_ELEMENT (element);
+  GESTimelineElement *parent = element->parent;
 
   g_return_val_if_fail (object->priv->nleobject, FALSE);
   if (inpoint && !object->priv->has_internal_source) {
-    GST_WARNING_OBJECT (element, "Can not set an in-point for a track "
+    GST_WARNING_OBJECT (element, "Cannot set an in-point for a track "
         "element that is not registered with internal content");
     return FALSE;
   }
 
-  if (GES_IS_CLIP (element->parent)
-      && !ges_clip_can_set_inpoint_of_child (GES_CLIP (element->parent),
-          element, inpoint)) {
-    GST_WARNING_OBJECT (element, "Can not set an in-point of %"
+  if (GES_IS_CLIP (parent)
+      && !ges_clip_can_set_inpoint_of_child (GES_CLIP (parent), object,
+          inpoint)) {
+    GST_WARNING_OBJECT (element, "Cannot set an in-point of %"
         GST_TIME_FORMAT " because the parent clip %" GES_FORMAT
-        " would not be able to follow",
-        GST_TIME_ARGS (inpoint), GES_ARGS (element->parent));
+        " would not allow it", GST_TIME_ARGS (inpoint), GES_ARGS (parent));
     return FALSE;
   }
 
@@ -677,14 +677,24 @@ static gboolean
 _set_max_duration (GESTimelineElement * element, GstClockTime max_duration)
 {
   GESTrackElement *object = GES_TRACK_ELEMENT (element);
+  GESTimelineElement *parent = element->parent;
 
   if (GST_CLOCK_TIME_IS_VALID (max_duration)
       && !object->priv->has_internal_source) {
-    GST_WARNING_OBJECT (element, "Can not set a max-duration for a track "
+    GST_WARNING_OBJECT (element, "Cannot set a max-duration for a track "
         "element that is not registered with internal content");
     return FALSE;
   }
 
+  if (GES_IS_CLIP (parent)
+      && !ges_clip_can_set_max_duration_of_child (GES_CLIP (parent), object,
+          max_duration)) {
+    GST_WARNING_OBJECT (element, "Cannot set a max-duration of %"
+        GST_TIME_FORMAT " because the parent clip %" GES_FORMAT
+        " would not allow it", GST_TIME_ARGS (max_duration), GES_ARGS (parent));
+    return FALSE;
+  }
+
   return TRUE;
 }
 
@@ -693,6 +703,7 @@ static gboolean
 _set_priority (GESTimelineElement * element, guint32 priority)
 {
   GESTrackElement *object = GES_TRACK_ELEMENT (element);
+  GESTimelineElement *parent = element->parent;
 
   g_return_val_if_fail (object->priv->nleobject, FALSE);
 
@@ -707,6 +718,15 @@ _set_priority (GESTimelineElement * element, guint32 priority)
   if (G_UNLIKELY (priority == _PRIORITY (object)))
     return FALSE;
 
+  if (GES_IS_CLIP (parent)
+      && !ges_clip_can_set_priority_of_child (GES_CLIP (parent), object,
+          priority)) {
+    GST_WARNING_OBJECT (element, "Cannot set a priority of %"
+        G_GUINT32_FORMAT " because the parent clip %" GES_FORMAT
+        " would not allow it", priority, GES_ARGS (parent));
+    return FALSE;
+  }
+
   g_object_set (object->priv->nleobject, "priority", priority, NULL);
 
   return TRUE;
@@ -730,6 +750,7 @@ _get_track_types (GESTimelineElement * object)
 gboolean
 ges_track_element_set_active (GESTrackElement * object, gboolean active)
 {
+  GESTimelineElement *parent;
   g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
   g_return_val_if_fail (object->priv->nleobject, FALSE);
 
@@ -738,6 +759,14 @@ ges_track_element_set_active (GESTrackElement * object, gboolean active)
   if (G_UNLIKELY (active == object->active))
     return FALSE;
 
+  parent = GES_TIMELINE_ELEMENT_PARENT (object);
+  if (GES_IS_CLIP (parent)
+      && !ges_clip_can_set_active_of_child (GES_CLIP (parent), object, active)) {
+    GST_WARNING_OBJECT (object, "Cannot set active to %i because the parent "
+        "clip %" GES_FORMAT " would not allow it", active, GES_ARGS (parent));
+    return FALSE;
+  }
+
   g_object_set (object->priv->nleobject, "active",
       active & object->priv->layer_active, NULL);
 
index 19f68e880124f51d73093bc916d6296841e8f279..261d74aeac88962c98d5ecdecd91aef275cd7bfd 100644 (file)
@@ -2316,7 +2316,7 @@ GST_START_TEST (test_children_inpoint)
   CHECK_OBJECT_PROPS (effect, 5, 67, 20);
 
   /* should not be able to set the in-point to non-zero */
-  fail_if (ges_timeline_element_set_inpoint (child0, 40));
+  assert_fail_set_inpoint (child0, 40);
 
   CHECK_OBJECT_PROPS (clip, 5, 30, 20);
   CHECK_OBJECT_PROPS (child0, 5, 0, 20);
@@ -2506,21 +2506,21 @@ GST_START_TEST (test_children_max_duration)
     /* can not set in-point above max_duration, nor max_duration below
      * in-point */
 
-    fail_if (ges_timeline_element_set_max_duration (child0, 29));
+    assert_fail_set_max_duration (child0, 29);
 
     CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1);
     CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400);
 
-    fail_if (ges_timeline_element_set_max_duration (child1, 29));
+    assert_fail_set_max_duration (child1, 29);
 
     CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1);
     CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400);
 
-    fail_if (ges_timeline_element_set_max_duration (clip, 29));
+    assert_fail_set_max_duration (clip, 29);
 
     CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1);
@@ -2530,21 +2530,21 @@ GST_START_TEST (test_children_max_duration)
     /* can't set the inpoint to (new_max), even though it is lower than
      * our own max-duration (new_max + 1) because it is too high for our
      * sibling child1 */
-    fail_if (ges_timeline_element_set_inpoint (child0, new_max));
+    assert_fail_set_inpoint (child0, new_max);
 
     CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1);
     CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400);
 
-    fail_if (ges_timeline_element_set_inpoint (child1, new_max));
+    assert_fail_set_inpoint (child1, new_max);
 
     CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1);
     CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400);
 
-    fail_if (ges_timeline_element_set_inpoint (clip, new_max));
+    assert_fail_set_inpoint (clip, new_max);
 
     CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1);
@@ -2591,14 +2591,14 @@ GST_START_TEST (test_children_max_duration)
     CHECK_OBJECT_PROPS_MAX (effect, 5, new_max + 2, 20, new_max + 500);
 
     /* but not higher than our own */
-    fail_if (ges_timeline_element_set_inpoint (effect, new_max + 501));
+    assert_fail_set_inpoint (effect, new_max + 501);
 
     CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1);
     CHECK_OBJECT_PROPS_MAX (child1, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (effect, 5, new_max + 2, 20, new_max + 500);
 
-    fail_if (ges_timeline_element_set_max_duration (effect, new_max + 1));
+    assert_fail_set_max_duration (effect, new_max + 1);
 
     CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, new_max - 2);
     CHECK_OBJECT_PROPS_MAX (child0, 5, 30, 20, new_max + 1);
@@ -2640,7 +2640,7 @@ GST_START_TEST (test_children_max_duration)
     CHECK_OBJECT_PROPS_MAX (effect, 5, 0, 20, 400);
 
     /* should not be able to set the max-duration to a valid time */
-    fail_if (ges_timeline_element_set_max_duration (child0, 40));
+    assert_fail_set_max_duration (child0, 40);
 
     CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, 180);
     CHECK_OBJECT_PROPS_MAX (child0, 5, 0, 20, GST_CLOCK_TIME_NONE);
@@ -2659,7 +2659,7 @@ GST_START_TEST (test_children_max_duration)
 
     /* should not be able to set the max of the clip to anything else
      * when it has no core children with an internal source */
-    fail_if (ges_timeline_element_set_max_duration (clip, 150));
+    assert_fail_set_max_duration (clip, 150);
 
     CHECK_OBJECT_PROPS_MAX (clip, 5, 30, 20, GST_CLOCK_TIME_NONE);
     CHECK_OBJECT_PROPS_MAX (child0, 5, 0, 20, GST_CLOCK_TIME_NONE);
@@ -3060,6 +3060,549 @@ GST_START_TEST (test_duration_limit)
 
 GST_END_TEST;
 
+GST_START_TEST (test_can_set_duration_limit)
+{
+  GESTimeline *timeline;
+  GESLayer *layer;
+  GESClip *clip;
+  GESTrackElement *source0, *source1;
+  GESTrackElement *effect0, *effect1, *effect2;
+  GESTrack *track0, *track1;
+  gint limit_notify_count = 0;
+
+  ges_init ();
+
+  timeline = ges_timeline_new ();
+  track0 = GES_TRACK (ges_video_track_new ());
+  track1 = GES_TRACK (ges_audio_track_new ());
+
+  fail_unless (ges_timeline_add_track (timeline, track0));
+  fail_unless (ges_timeline_add_track (timeline, track1));
+  /* add track3 later */
+
+  layer = ges_timeline_append_layer (timeline);
+
+  /* place a dummy clip at the start of the layer */
+  clip = GES_CLIP (ges_test_clip_new ());
+  assert_set_start (clip, 0);
+  assert_set_duration (clip, 20);
+
+  fail_unless (ges_layer_add_clip (layer, clip));
+
+  /* the clip we will be editing overlaps the first clip at its start */
+  clip = GES_CLIP (ges_test_clip_new ());
+
+  g_signal_connect (clip, "notify::duration-limit", G_CALLBACK (_count_cb),
+      &limit_notify_count);
+
+  assert_set_start (clip, 10);
+  assert_set_duration (clip, 20);
+
+  fail_unless (ges_layer_add_clip (layer, clip));
+
+  source0 =
+      ges_clip_find_track_element (clip, track0, GES_TYPE_VIDEO_TEST_SOURCE);
+  source1 =
+      ges_clip_find_track_element (clip, track1, GES_TYPE_AUDIO_TEST_SOURCE);
+
+  fail_unless (source0);
+  fail_unless (source1);
+
+  gst_object_unref (source0);
+  gst_object_unref (source1);
+
+  assert_equals_int (limit_notify_count, 0);
+  _assert_duration_limit (clip, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+
+  assert_set_inpoint (clip, 16);
+
+  assert_equals_int (limit_notify_count, 0);
+  _assert_duration_limit (clip, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, GST_CLOCK_TIME_NONE);
+
+  assert_set_max_duration (clip, 36);
+
+  assert_equals_int (limit_notify_count, 1);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
+
+  /* add effects */
+  effect0 = GES_TRACK_ELEMENT (ges_effect_new ("agingtv"));
+  effect1 = GES_TRACK_ELEMENT (ges_effect_new ("vertigotv"));
+  effect2 = GES_TRACK_ELEMENT (ges_effect_new ("alpha"));
+
+  ges_track_element_set_has_internal_source (effect0, TRUE);
+  fail_unless (ges_track_element_has_internal_source (effect1) == FALSE);
+  ges_track_element_set_has_internal_source (effect2, TRUE);
+
+  assert_set_max_duration (effect0, 10);
+  /* already set the track */
+  fail_unless (ges_track_add_element (track0, effect0));
+  /* cannot add because it would cause the duration-limit to go to 10,
+   * causing a full overlap with the clip at the beginning of the layer */
+
+  gst_object_ref (effect0);
+  fail_if (ges_container_add (GES_CONTAINER (clip),
+          GES_TIMELINE_ELEMENT (effect0)));
+
+  assert_equals_int (limit_notify_count, 1);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
+
+  /* removing from the track and adding will work, but track selection
+   * will fail */
+  fail_unless (ges_track_remove_element (track0, effect0));
+
+  _assert_add (clip, effect0);
+  fail_if (ges_track_element_get_track (effect0));
+  gst_object_unref (effect0);
+
+  assert_equals_int (limit_notify_count, 1);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 10);
+
+  fail_if (ges_clip_add_child_to_track (clip, effect0, track0, NULL));
+
+  /* set max-duration to 11 and we are fine to select a track */
+  assert_set_max_duration (effect0, 11);
+  assert_equals_int (limit_notify_count, 1);
+  _assert_duration_limit (clip, 20);
+
+  fail_unless (ges_clip_add_child_to_track (clip, effect0, track0,
+          NULL) == effect0);
+
+  assert_equals_int (limit_notify_count, 2);
+  _assert_duration_limit (clip, 11);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 11, 36);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 11, 36);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 11, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 11);
+
+  /* cannot set duration above the limit */
+  assert_fail_set_duration (clip, 12);
+  assert_fail_set_duration (source0, 12);
+  assert_fail_set_duration (effect0, 12);
+
+  /* allow the max_duration to increase again */
+  assert_set_max_duration (effect0, 25);
+
+  assert_equals_int (limit_notify_count, 3);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 11, 36);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 11, 36);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 11, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
+
+  assert_set_duration (clip, 20);
+
+  assert_equals_int (limit_notify_count, 3);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+
+  /* add another effect */
+  _assert_add (clip, effect1);
+  fail_unless (ges_track_element_get_track (effect1) == track0);
+
+  assert_equals_int (limit_notify_count, 3);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+
+  /* make source0 inactive and reduce its max-duration
+   * note that this causes effect0 and effect1 to also become in-active */
+  _assert_set_active (source0, FALSE);
+  _assert_active (source0, FALSE);
+  _assert_active (effect0, FALSE);
+  _assert_active (effect1, FALSE);
+
+  assert_equals_int (limit_notify_count, 3);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+
+  /* can set a low max duration whilst the source is inactive */
+  assert_set_max_duration (source0, 26);
+
+  assert_equals_int (limit_notify_count, 3);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+
+  /* add the last effect */
+  assert_set_inpoint (effect2, 7);
+  assert_set_max_duration (effect2, 17);
+  _assert_active (effect2, TRUE);
+
+  /* safe to add because the source is inactive */
+  assert_equals_int (limit_notify_count, 3);
+  _assert_add (clip, effect2);
+  assert_equals_int (limit_notify_count, 3);
+  _assert_active (source0, FALSE);
+  _assert_active (effect0, FALSE);
+  _assert_active (effect1, FALSE);
+  _assert_active (effect2, FALSE);
+
+  fail_unless (ges_track_element_get_track (effect2) == track0);
+
+  assert_equals_int (limit_notify_count, 3);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 16, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 16, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 16, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 17);
+
+  /* want to make the source and its effects active again */
+  assert_set_inpoint (source0, 6);
+  assert_set_max_duration (effect2, 33);
+
+  assert_equals_int (limit_notify_count, 4);
+  _assert_duration_limit (clip, 30);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
+
+  _assert_set_active (source0, TRUE);
+  _assert_set_active (effect0, TRUE);
+  _assert_set_active (effect1, TRUE);
+  _assert_set_active (effect2, TRUE);
+
+  assert_equals_int (limit_notify_count, 5);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
+
+  /* cannot set in-point of clip to 16, nor of either source */
+  assert_fail_set_inpoint (clip, 16);
+  assert_fail_set_inpoint (source0, 16);
+  assert_fail_set_inpoint (source1, 16);
+
+  assert_equals_int (limit_notify_count, 5);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
+
+  /* can set just below */
+  assert_set_inpoint (source1, 15);
+
+  assert_equals_int (limit_notify_count, 6);
+  _assert_duration_limit (clip, 11);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 15, 11, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 15, 11, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 15, 11, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 11, 33);
+
+  assert_set_inpoint (clip, 6);
+  assert_set_duration (clip, 20);
+
+  assert_equals_int (limit_notify_count, 7);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
+
+  /* cannot set in-point of non-core in a way that would cause limit to
+   * drop */
+  assert_fail_set_inpoint (effect2, 23);
+  assert_fail_set_inpoint (effect0, 15);
+
+  assert_equals_int (limit_notify_count, 7);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
+
+  /* can set just below */
+  assert_set_inpoint (effect2, 22);
+
+  assert_equals_int (limit_notify_count, 8);
+  _assert_duration_limit (clip, 11);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 11, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 11, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 11, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 22, 11, 33);
+
+  assert_set_inpoint (effect2, 7);
+  assert_set_duration (clip, 20);
+
+  assert_equals_int (limit_notify_count, 9);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
+
+  /* same but with max-duration */
+
+  /* core */
+  assert_fail_set_max_duration (clip, 16);
+  assert_fail_set_max_duration (source0, 16);
+  assert_fail_set_max_duration (source1, 16);
+
+  assert_equals_int (limit_notify_count, 9);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 36);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
+
+  assert_set_max_duration (source1, 17);
+
+  assert_equals_int (limit_notify_count, 10);
+  _assert_duration_limit (clip, 11);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 11, 17);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 11, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 11, 17);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 11, 33);
+
+  assert_set_max_duration (source1, 30);
+  assert_set_duration (clip, 20);
+
+  assert_equals_int (limit_notify_count, 11);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 30);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
+
+  assert_set_max_duration (clip, 17);
+
+  assert_equals_int (limit_notify_count, 12);
+  _assert_duration_limit (clip, 11);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 11, 17);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 11, 17);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 11, 17);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 11, 33);
+
+  assert_set_max_duration (clip, 26);
+  assert_set_duration (clip, 20);
+
+  assert_equals_int (limit_notify_count, 13);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
+
+  /* non-core */
+  assert_fail_set_max_duration (effect0, 10);
+  assert_fail_set_max_duration (effect2, 17);
+
+  assert_equals_int (limit_notify_count, 13);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
+
+  assert_set_max_duration (effect2, 18);
+
+  assert_equals_int (limit_notify_count, 14);
+  _assert_duration_limit (clip, 11);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 11, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 11, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 11, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 11, 18);
+
+  assert_set_max_duration (effect2, 33);
+  assert_set_duration (clip, 20);
+
+  assert_equals_int (limit_notify_count, 15);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 33);
+
+  /* test setting active */
+  _assert_active (effect2, TRUE);
+  _assert_set_active (effect2, FALSE);
+  assert_set_max_duration (effect2, 17);
+
+  assert_equals_int (limit_notify_count, 15);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 17);
+
+  fail_if (ges_track_element_set_active (effect2, TRUE));
+
+  assert_equals_int (limit_notify_count, 15);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 7, 20, 17);
+
+  assert_set_inpoint (effect2, 6);
+  _assert_set_active (effect2, TRUE);
+
+  assert_equals_int (limit_notify_count, 16);
+  _assert_duration_limit (clip, 11);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 11, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 11, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 11, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 11, 17);
+
+  /* make source0 in-active */
+  _assert_active (source0, TRUE);
+  _assert_set_active (source0, FALSE);
+  _assert_active (source0, FALSE);
+  _assert_active (effect0, FALSE);
+  _assert_active (effect1, FALSE);
+  _assert_active (effect2, FALSE);
+
+  assert_set_duration (source0, 20);
+
+  assert_equals_int (limit_notify_count, 17);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 20, 17);
+
+  /* lower duration-limit for source for when it becomes active */
+  assert_set_max_duration (source0, 16);
+
+  assert_equals_int (limit_notify_count, 17);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 16);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 16);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 20, 17);
+
+  /* cannot make the source active */
+  fail_if (ges_track_element_set_active (source0, TRUE));
+
+  assert_equals_int (limit_notify_count, 17);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 16);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 16);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 20, 17);
+
+  /* nor can we make the effects active because this would activate the
+   * source */
+  fail_if (ges_track_element_set_active (effect0, TRUE));
+  fail_if (ges_track_element_set_active (effect1, TRUE));
+  fail_if (ges_track_element_set_active (effect2, TRUE));
+
+  assert_equals_int (limit_notify_count, 17);
+  _assert_duration_limit (clip, 20);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 6, 20, 16);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 6, 20, 16);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 6, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 20, 17);
+
+  /* allow it to just succeed */
+  assert_set_inpoint (source0, 5);
+
+  assert_equals_int (limit_notify_count, 18);
+  _assert_duration_limit (clip, 21);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 5, 20, 16);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 5, 20, 16);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 5, 20, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 20, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 20, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 20, 17);
+
+  _assert_set_active (effect1, TRUE);
+
+  assert_equals_int (limit_notify_count, 19);
+  _assert_duration_limit (clip, 11);
+  CHECK_OBJECT_PROPS_MAX (clip, 10, 5, 11, 16);
+  CHECK_OBJECT_PROPS_MAX (source0, 10, 5, 11, 16);
+  CHECK_OBJECT_PROPS_MAX (source1, 10, 5, 11, 26);
+  CHECK_OBJECT_PROPS_MAX (effect0, 10, 0, 11, 25);
+  CHECK_OBJECT_PROPS_MAX (effect1, 10, 0, 11, GST_CLOCK_TIME_NONE);
+  CHECK_OBJECT_PROPS_MAX (effect2, 10, 6, 11, 17);
+
+  gst_object_unref (timeline);
+
+  ges_deinit ();
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_children_properties_contain)
 {
   GESTimeline *timeline;
@@ -3670,6 +4213,7 @@ ges_suite (void)
   tcase_add_test (tc_chain, test_children_inpoint);
   tcase_add_test (tc_chain, test_children_max_duration);
   tcase_add_test (tc_chain, test_duration_limit);
+  tcase_add_test (tc_chain, test_can_set_duration_limit);
   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);
index 27885b69c7bf2eaff9846678924e52e8158d514a..282f389742858ea37dd473bcfb6c0326be19ae11 100644 (file)
@@ -106,25 +106,43 @@ G_STMT_START {                                          \
       GST_TIME_ARGS (_MAX_DURATION(obj)), GST_TIME_ARGS (max_duration)); \
 }
 
-#define assert_set_start(obj, start) \
-  fail_unless (ges_timeline_element_set_start (\
-        GES_TIMELINE_ELEMENT (obj), start), \
-        "Could not set the start of " #obj " to " #start)
-
-#define assert_set_duration(obj, duration) \
-  fail_unless (ges_timeline_element_set_duration (\
-        GES_TIMELINE_ELEMENT (obj), duration), \
-        "Could not set the duration of " #obj " to " #duration)
-
-#define assert_set_inpoint(obj, inpoint) \
-  fail_unless (ges_timeline_element_set_inpoint (\
-        GES_TIMELINE_ELEMENT (obj), inpoint), \
-        "Could not set the in-point of " #obj " to " #inpoint)
-
-#define assert_set_max_duration(obj, max_duration) \
-  fail_unless (ges_timeline_element_set_max_duration (\
-        GES_TIMELINE_ELEMENT (obj), max_duration), \
-        "Could not set the max-duration of " #obj " to " #max_duration)
+#define __assert_timeline_element_set(obj, prop, val) \
+  fail_unless (ges_timeline_element_set_ ## prop ( \
+        GES_TIMELINE_ELEMENT (obj), val), "Could not set the " # prop \
+        " of " #obj "(%s) to " #val, GES_TIMELINE_ELEMENT_NAME (obj))
+
+#define __fail_timeline_element_set(obj, prop, val) \
+  fail_if (ges_timeline_element_set_ ## prop ( \
+        GES_TIMELINE_ELEMENT (obj), val), "Setting the " # prop \
+        " of " #obj "(%s) to " #val " did not fail as expected", \
+        GES_TIMELINE_ELEMENT_NAME (obj))
+
+
+#define assert_set_start(obj, val) \
+  __assert_timeline_element_set (obj, start, val)
+
+#define assert_set_duration(obj, val) \
+  __assert_timeline_element_set (obj, duration, val)
+
+#define assert_set_inpoint(obj, val) \
+  __assert_timeline_element_set (obj, inpoint, val)
+
+#define assert_set_max_duration(obj, val) \
+  __assert_timeline_element_set (obj, max_duration, val)
+
+
+#define assert_fail_set_start(obj, val) \
+  __fail_timeline_element_set (obj, start, val)
+
+#define assert_fail_set_duration(obj, val) \
+  __fail_timeline_element_set (obj, duration, val)
+
+#define assert_fail_set_inpoint(obj, val) \
+  __fail_timeline_element_set (obj, inpoint, val)
+
+#define assert_fail_set_max_duration(obj, val) \
+  __fail_timeline_element_set (obj, max_duration, val)
+
 
 #define assert_num_in_track(track, val) \
 { \