ges: Make setting start/duration move or trim generic
authorThibault Saunier <tsaunier@igalia.com>
Thu, 5 Mar 2020 18:56:28 +0000 (15:56 -0300)
committerThibault Saunier <tsaunier@igalia.com>
Mon, 9 Mar 2020 14:48:37 +0000 (11:48 -0300)
We were implementing the logic for moving/trimming elements specific
to SourceClip but this was not correct ass the new timeline tree allows
us to handle that for all element types in a generic and nice way.

This make us need to have groups trimming properly implemented in the
timeline tree, leading to some fixes in the group tests.

This adds tests for the various cases known to not be handled properly
by the previous code.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-editing-services/issues/92

ges/ges-auto-transition.c
ges/ges-clip.c
ges/ges-source-clip.c
ges/ges-timeline-element.c
ges/ges-timeline-tree.c
ges/ges-timeline.c
tests/check/ges/group.c
tests/check/python/common.py
tests/check/python/test_timeline.py

index f28f37aee8acccda71f2c2566a9a9a33e0020510..057267cb123a5bdd7a6285225769aa1fe0488cd5 100644 (file)
@@ -91,9 +91,11 @@ neighbour_changed_cb (GESClip * clip, GParamSpec * arg G_GNUC_UNUSED,
   }
 
   self->positioning = TRUE;
+  ELEMENT_SET_FLAG (self->transition_clip, GES_TIMELINE_ELEMENT_SET_SIMPLE);
   _set_start0 (GES_TIMELINE_ELEMENT (self->transition_clip),
       _START (self->next_source));
   _set_duration0 (GES_TIMELINE_ELEMENT (self->transition_clip), new_duration);
+  ELEMENT_SET_FLAG (self->transition_clip, GES_TIMELINE_ELEMENT_SET_SIMPLE);
   self->positioning = FALSE;
 }
 
index bdb5989a295e9e3787cb3fb74fc1903ba0f9cf84..faaf9974d903980be5cdaf1a7de9a78a8ebb6c16 100644 (file)
@@ -218,8 +218,11 @@ _set_duration (GESTimelineElement * element, GstClockTime duration)
   for (tmp = container->children; tmp; tmp = g_list_next (tmp)) {
     GESTimelineElement *child = (GESTimelineElement *) tmp->data;
 
-    if (child != container->initiated_move)
+    if (child != container->initiated_move) {
+      ELEMENT_SET_FLAG (child, GES_TIMELINE_ELEMENT_SET_SIMPLE);
       _set_duration0 (GES_TIMELINE_ELEMENT (child), duration);
+      ELEMENT_UNSET_FLAG (child, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+    }
   }
   container->children_control_mode = GES_CHILDREN_UPDATE;
 
index 7f5903c14dcaf4bd77bdda8d98276d84dcd8bb97..21c9f9562bc18e3570d44b10e2e4bc8db512663b 100644 (file)
@@ -46,49 +46,6 @@ enum
 
 G_DEFINE_TYPE_WITH_PRIVATE (GESSourceClip, ges_source_clip, GES_TYPE_CLIP);
 
-static gboolean
-_set_start (GESTimelineElement * element, GstClockTime start)
-{
-  GESTimelineElement *toplevel =
-      ges_timeline_element_get_toplevel_parent (element);
-
-  gst_object_unref (toplevel);
-  if (element->timeline
-      && !ELEMENT_FLAG_IS_SET (element, GES_TIMELINE_ELEMENT_SET_SIMPLE)
-      && !ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
-    if (!ges_timeline_move_object_simple (element->timeline, element, NULL,
-            GES_EDGE_NONE, start))
-      return FALSE;
-    return -1;
-  }
-
-  return
-      GES_TIMELINE_ELEMENT_CLASS (ges_source_clip_parent_class)->set_start
-      (element, start);
-}
-
-static gboolean
-_set_duration (GESTimelineElement * element, GstClockTime duration)
-{
-  GESTimelineElement *toplevel =
-      ges_timeline_element_get_toplevel_parent (element);
-
-  gst_object_unref (toplevel);
-  if (element->timeline
-      && !ELEMENT_FLAG_IS_SET (element, GES_TIMELINE_ELEMENT_SET_SIMPLE)
-      && !ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
-    if (!timeline_trim_object (element->timeline, element,
-            GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element), NULL, GES_EDGE_END,
-            element->start + duration))
-      return FALSE;
-    return -1;
-  }
-
-  return
-      GES_TIMELINE_ELEMENT_CLASS (ges_source_clip_parent_class)->set_duration
-      (element, duration);
-}
-
 static void
 ges_source_clip_get_property (GObject * object, guint property_id,
     GValue * value, GParamSpec * pspec)
@@ -119,14 +76,10 @@ static void
 ges_source_clip_class_init (GESSourceClipClass * klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
 
   object_class->get_property = ges_source_clip_get_property;
   object_class->set_property = ges_source_clip_set_property;
   object_class->finalize = ges_source_clip_finalize;
-
-  element_class->set_start = _set_start;
-  element_class->set_duration = _set_duration;
 }
 
 static void
index ecf58a2f9b070ef5a2bf5a1265197df24cbe957a..b4c89d9f4d7ab779431c8cddb7bf0e3edf7ee954 100644 (file)
@@ -972,6 +972,7 @@ ges_timeline_element_get_timeline (GESTimelineElement * self)
 gboolean
 ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
 {
+  gboolean emit_notify = TRUE;
   GESTimelineElementClass *klass;
   GESTimelineElement *toplevel_container, *parent;
 
@@ -980,13 +981,24 @@ ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
   if (self->start == start)
     return TRUE;
 
-  klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
-
   GST_DEBUG_OBJECT (self, "current start: %" GST_TIME_FORMAT
       " new start: %" GST_TIME_FORMAT,
       GST_TIME_ARGS (GES_TIMELINE_ELEMENT_START (self)), GST_TIME_ARGS (start));
 
   toplevel_container = ges_timeline_element_get_toplevel_parent (self);
+
+  if (self->timeline
+      && !ELEMENT_FLAG_IS_SET (self, GES_TIMELINE_ELEMENT_SET_SIMPLE)
+      && !ELEMENT_FLAG_IS_SET (toplevel_container,
+          GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
+    if (!ges_timeline_move_object_simple (self->timeline, self, NULL,
+            GES_EDGE_NONE, start)) {
+      gst_object_unref (toplevel_container);
+      return FALSE;
+    }
+
+    emit_notify = FALSE;
+  }
   parent = self->parent;
 
   /* FIXME This should not belong to GESTimelineElement */
@@ -1003,9 +1015,10 @@ ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
   }
 
   gst_object_unref (toplevel_container);
+  klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
   if (klass->set_start) {
     gint res = klass->set_start (self, start);
-    if (res == TRUE) {
+    if (res == TRUE && emit_notify) {
       self->start = start;
       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_START]);
     }
@@ -1126,19 +1139,40 @@ ges_timeline_element_set_duration (GESTimelineElement * self,
     GstClockTime duration)
 {
   GESTimelineElementClass *klass;
+  gboolean emit_notify = TRUE;
+  GESTimelineElement *toplevel;
 
   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
 
-  klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
+  toplevel = ges_timeline_element_get_toplevel_parent (self);
+  if (self->timeline &&
+      !ELEMENT_FLAG_IS_SET (self, GES_TIMELINE_ELEMENT_SET_SIMPLE) &&
+      !ELEMENT_FLAG_IS_SET (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE)) {
+    gboolean res;
+
+    res = timeline_trim_object (self->timeline, self,
+        GES_TIMELINE_ELEMENT_LAYER_PRIORITY (self), NULL,
+        GES_EDGE_END, self->start + duration);
+
+    if (!res) {
+      gst_object_unref (toplevel);
+
+      return FALSE;
+    }
+
+    emit_notify = res == -1;
+  }
+  gst_object_unref (toplevel);
 
   GST_DEBUG_OBJECT (self, "current duration: %" GST_TIME_FORMAT
       " new duration: %" GST_TIME_FORMAT,
       GST_TIME_ARGS (GES_TIMELINE_ELEMENT_DURATION (self)),
       GST_TIME_ARGS (duration));
 
+  klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
   if (klass->set_duration) {
     gint res = klass->set_duration (self, duration);
-    if (res == TRUE) {
+    if (res == TRUE && emit_notify) {
       self->duration = duration;
       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION]);
     }
index 6f875dcf80e12e8da79d09069fa397abee706d5a..8956ff74b3a606f6b84d68051dd3e1a179e1e6f4 100644 (file)
@@ -75,6 +75,10 @@ struct _TreeIterationData
   GHashTable *moved_clips;
 
   GList *neighbours;
+
+  /* Data related to trimming groups */
+  GstClockTime trim_group_start;
+  GstClockTime trim_group_end;
 } tree_iteration_data_init = {
    .root = NULL,
    .res = TRUE,
@@ -91,6 +95,8 @@ struct _TreeIterationData
    .edge = GES_EDGE_NONE,
    .moved_clips = NULL,
    .neighbours = NULL,
+   .trim_group_start = GST_CLOCK_TIME_NONE,
+   .trim_group_end = GST_CLOCK_TIME_NONE,
 };
 /*  *INDENT-ON* */
 
@@ -783,6 +789,47 @@ error:
   goto done;
 }
 
+static gboolean
+trim_group_get_vals (TreeIterationData * data, GESTimelineElement * e,
+    GstClockTimeDiff * n_start, GstClockTimeDiff * n_inpoint,
+    GstClockTimeDiff * n_duration)
+{
+  GstClockTimeDiff offset;
+  GstClockTimeDiff group_nstart =
+      GST_CLOCK_DIFF (data->start_diff, data->trim_group_start);
+  GstClockTimeDiff group_nend =
+      GST_CLOCK_DIFF (data->duration_diff, data->trim_group_end);
+
+  if (data->edge == GES_EDGE_START &&
+      ((group_nstart >= e->start) || (e->start == data->trim_group_start))) {
+
+    offset = GST_CLOCK_DIFF (group_nstart, e->start);
+    *n_start = group_nstart;
+    *n_inpoint = GST_CLOCK_DIFF (offset, e->inpoint);
+    *n_duration =
+        GST_CLOCK_DIFF (*n_start, (GstClockTimeDiff) e->start + e->duration);
+
+    GST_DEBUG_OBJECT (data->element, "Trimming %" GES_FORMAT " start",
+        GES_ARGS (e));
+    return TRUE;
+  } else if (data->edge == GES_EDGE_END &&
+      ((group_nend <= _END (e)) || (_END (e) == data->trim_group_end))) {
+
+    offset = GST_CLOCK_DIFF (group_nend, _END (e));
+    *n_start = e->start;
+    *n_inpoint = e->inpoint;
+    *n_duration = GST_CLOCK_DIFF (offset, e->duration);
+
+    GST_DEBUG_OBJECT (data->element, "Trimming %" GES_FORMAT " end",
+        GES_ARGS (e));
+
+    return TRUE;
+  }
+
+  /* Ignoring child */
+  return FALSE;
+}
+
 static gboolean
 check_trim_child (GNode * node, TreeIterationData * data)
 {
@@ -793,6 +840,12 @@ check_trim_child (GNode * node, TreeIterationData * data)
       GST_CLOCK_DIFF (data->duration_diff, e->duration) :
       GST_CLOCK_DIFF (n_start, (GstClockTimeDiff) e->start + e->duration);
 
+  if (GST_CLOCK_TIME_IS_VALID (data->trim_group_start)
+      && !trim_group_get_vals (data, e, &n_start, &n_inpoint, &n_duration)) {
+    GST_DEBUG_OBJECT (data->element, "Not trimming");
+    return FALSE;
+  }
+
   if (!timeline_tree_can_move_element_internal (data->root, e,
           (gint64) ges_timeline_element_get_layer_priority (e) -
           data->priority_diff, n_start, n_inpoint, n_duration, NULL,
@@ -823,21 +876,33 @@ timeline_tree_can_trim_element_internal (GNode * root, TreeIterationData * data)
 
 static void
 trim_simple (GESTimelineElement * element, GstClockTimeDiff offset,
-    GESEdge edge)
+    GESEdge edge, TreeIterationData * data)
 {
+  GESTimelineElement *toplevel =
+      ges_timeline_element_get_toplevel_parent (element);
+
+  GstClockTimeDiff n_start = GST_CLOCK_DIFF (offset, element->start);
+  GstClockTimeDiff n_inpoint = GST_CLOCK_DIFF (offset, element->inpoint);
+  GstClockTimeDiff n_duration = edge == GES_EDGE_END
+      ? GST_CLOCK_DIFF (offset, element->duration)
+      : element->duration + offset;
+
+  if (data && GST_CLOCK_TIME_IS_VALID (data->trim_group_start))
+    g_assert (trim_group_get_vals (data, element, &n_start, &n_inpoint,
+            &n_duration));
+
   ELEMENT_SET_FLAG (element, GES_TIMELINE_ELEMENT_SET_SIMPLE);
-  if (edge == GES_EDGE_END) {
-    ges_timeline_element_set_duration (element, GST_CLOCK_DIFF (offset,
-            element->duration));
-  } else {
-    ges_timeline_element_set_start (element, GST_CLOCK_DIFF (offset,
-            element->start));
-    ges_timeline_element_set_inpoint (element, GST_CLOCK_DIFF (offset,
-            element->inpoint));
-    ges_timeline_element_set_duration (element, element->duration + offset);
+  ELEMENT_SET_FLAG (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+  if (edge != GES_EDGE_END) {
+    ges_timeline_element_set_start (element, n_start);
+    ges_timeline_element_set_inpoint (element, n_inpoint);
   }
+  ges_timeline_element_set_duration (element, n_duration);
+
   GST_LOG ("Trimmed %" GES_FORMAT, GES_ARGS (element));
   ELEMENT_UNSET_FLAG (element, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+  ELEMENT_UNSET_FLAG (toplevel, GES_TIMELINE_ELEMENT_SET_SIMPLE);
+  gst_object_unref (toplevel);
 }
 
 #define SET_TRIMMING_DATA(data, _edge, offset) G_STMT_START { \
@@ -845,6 +910,10 @@ trim_simple (GESTimelineElement * element, GstClockTimeDiff offset,
   data.start_diff = (_edge) == GES_EDGE_END ? 0 : (offset); \
   data.inpoint_diff = (_edge) == GES_EDGE_END ? 0 : (offset); \
   data.duration_diff = (_edge) == GES_EDGE_END ? (offset) : -(offset); \
+  if (GES_IS_GROUP (data.element)) {\
+    data.trim_group_start = data.element->start;\
+    data.trim_group_end = _END (data.element); \
+  } \
 } G_STMT_END
 
 
@@ -869,6 +938,10 @@ timeline_tree_trim (GNode * root, GESTimelineElement * element,
   };
   TreeIterationData data = tree_iteration_data_init;
 
+  /* Make sure to check all children of clips */
+  if (GES_IS_TRACK_ELEMENT (element) && element->parent)
+    element = element->parent;
+
   data.root = root;
   data.element = element;
   data.priority_diff =
@@ -876,13 +949,12 @@ timeline_tree_trim (GNode * root, GESTimelineElement * element,
       new_layer_priority;
   data.snapping = snapping_distance ? &snapping : NULL;
   data.moved_clips = g_hash_table_new (g_direct_hash, g_direct_equal);
-
   SET_TRIMMING_DATA (data, edge, offset);
   GST_INFO ("%" GES_FORMAT " trimming %s with offset %" G_GINT64_FORMAT "",
       GES_ARGS (element), edge == GES_EDGE_END ? "end" : "start", offset);
-  g_node_traverse (find_node (root, element), G_IN_ORDER,
-      G_TRAVERSE_LEAVES, -1, (GNodeTraverseFunc) add_element_to_list,
-      &data.movings);
+  g_node_traverse (find_node (root, get_toplevel_container (element)),
+      G_IN_ORDER, G_TRAVERSE_LEAVES, -1,
+      (GNodeTraverseFunc) add_element_to_list, &data.movings);
 
   if (!timeline_tree_can_trim_element_internal (root, &data)) {
     GST_INFO ("Can not trim object.");
@@ -909,7 +981,7 @@ timeline_tree_trim (GNode * root, GESTimelineElement * element,
 
   g_hash_table_iter_init (&iter, data.moved_clips);
   while (g_hash_table_iter_next (&iter, (gpointer *) & elem, NULL))
-    trim_simple (elem, offset, edge);
+    trim_simple (elem, offset, edge, &data);
 
   timeline_tree_create_transitions (root, ges_timeline_find_auto_transition);
   timeline_update_transition (root->data);
@@ -1155,9 +1227,9 @@ timeline_tree_roll (GNode * root, GESTimelineElement * element,
     }
   }
 
-  trim_simple (element, offset, edge);
+  trim_simple (element, offset, edge, NULL);
   for (tmp = data.neighbours; tmp; tmp = tmp->next)
-    trim_simple (tmp->data, offset, data.edge);
+    trim_simple (tmp->data, offset, data.edge, NULL);
 
 done:
   timeline_update_duration (root->data);
index bf086b07f11ac2608dc05eb4f66c35680ce8f4e9..f8cb45c6d6a72c5ec433d0b04efe4ec250c665e0 100644 (file)
@@ -1096,8 +1096,10 @@ _trim_transition (GESTimeline * timeline, GESTimelineElement * element,
   GESLayer *layer = ges_timeline_get_layer (timeline,
       GES_TIMELINE_ELEMENT_LAYER_PRIORITY (element));
 
-  if (!ges_layer_get_auto_transition (layer))
-    goto fail;
+  if (!ges_layer_get_auto_transition (layer)) {
+    gst_object_unref (layer);
+    return -1;
+  }
 
   gst_object_unref (layer);
   for (tmp = timeline->priv->auto_transitions; tmp; tmp = tmp->next) {
@@ -1123,10 +1125,6 @@ _trim_transition (GESTimeline * timeline, GESTimelineElement * element,
   }
 
   return FALSE;
-
-fail:
-  gst_object_unref (layer);
-  return FALSE;
 }
 
 
index edbd9117b0bfc5201af4b5ca15b201c893fbc911..091601c417475c763131625b4be7fbcfe99e4369 100644 (file)
@@ -195,40 +195,67 @@ GST_START_TEST (test_move_group)
    *            |----------------------------------|
    *            |        7---------     2----------|
    * layer1:    |        | clip1   |    |  clip2   |
-   *            |       22--------30   62----------|
+   *            |       20--------30   60----------|
    *            |----------------------------------|
    */
   ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 12);
   CHECK_OBJECT_PROPS (clip, 12, 2, 3);
-  CHECK_OBJECT_PROPS (clip1, 22, 7, 8);
-  CHECK_OBJECT_PROPS (clip2, 62, 2, 48);
+  CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
+  CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
   CHECK_OBJECT_PROPS (group, 12, 0, 98);
   ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
 
   /* Setting the duration would lead to overlaps */
-  ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), 10);
+  fail_if (ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group),
+          10));
   CHECK_OBJECT_PROPS (clip, 12, 2, 3);
-  CHECK_OBJECT_PROPS (clip1, 22, 7, 8);
-  CHECK_OBJECT_PROPS (clip2, 62, 2, 48);
+  CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
+  CHECK_OBJECT_PROPS (clip2, 60, 0, 50);
   CHECK_OBJECT_PROPS (group, 12, 0, 98);
   ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (group), 100);
   CHECK_OBJECT_PROPS (clip, 12, 2, 3);
-  CHECK_OBJECT_PROPS (clip1, 22, 7, 8);
-  CHECK_OBJECT_PROPS (clip2, 62, 2, 50);
+  CHECK_OBJECT_PROPS (clip1, 20, 5, 10);
+  CHECK_OBJECT_PROPS (clip2, 60, 0, 52);
   CHECK_OBJECT_PROPS (group, 12, 0, 100);
 
   ges_timeline_element_set_start (GES_TIMELINE_ELEMENT (group), 20);
   CHECK_OBJECT_PROPS (clip, 20, 2, 3);
-  CHECK_OBJECT_PROPS (clip1, 30, 7, 8);
-  CHECK_OBJECT_PROPS (clip2, 70, 2, 50);
+  CHECK_OBJECT_PROPS (clip1, 28, 5, 10);
+  CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
   CHECK_OBJECT_PROPS (group, 20, 0, 100);
 
+  /* Trim fails because clip inpoint would become negative */
   fail_if (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 10));
   CHECK_OBJECT_PROPS (clip, 20, 2, 3);
-  CHECK_OBJECT_PROPS (clip1, 30, 7, 8);
-  CHECK_OBJECT_PROPS (clip2, 70, 2, 50);
+  CHECK_OBJECT_PROPS (clip1, 28, 5, 10);
+  CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
   CHECK_OBJECT_PROPS (group, 20, 0, 100);
 
+  fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 18));
+  CHECK_OBJECT_PROPS (clip, 18, 0, 5);
+  CHECK_OBJECT_PROPS (clip1, 28, 5, 10);
+  CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
+  CHECK_OBJECT_PROPS (group, 18, 0, 102);
+
+  fail_unless (ges_timeline_element_set_duration (GES_TIMELINE_ELEMENT (clip),
+          17));
+  CHECK_OBJECT_PROPS (clip, 18, 0, 17);
+  CHECK_OBJECT_PROPS (clip1, 28, 5, 10);
+  CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
+  CHECK_OBJECT_PROPS (group, 18, 0, 102);
+
+  fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 30));
+  CHECK_OBJECT_PROPS (clip, 30, 12, 5);
+  CHECK_OBJECT_PROPS (clip1, 30, 7, 8);
+  CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
+  CHECK_OBJECT_PROPS (group, 30, 0, 90);
+
+  fail_unless (ges_timeline_element_trim (GES_TIMELINE_ELEMENT (group), 25));
+  CHECK_OBJECT_PROPS (clip, 25, 7, 10);
+  CHECK_OBJECT_PROPS (clip1, 25, 2, 13);
+  CHECK_OBJECT_PROPS (clip2, 68, 0, 52);
+  CHECK_OBJECT_PROPS (group, 25, 0, 95);
+
   ASSERT_OBJECT_REFCOUNT (group, "2 ref for the timeline", 2);
   check_destroyed (G_OBJECT (timeline), G_OBJECT (group), NULL);
   gst_object_unref (asset);
index 81a439c6c4a51f2462f5b9cdaabad9ffde3c1a7e..0f55c929e62c3d72d9ef3fe7a31444a1d8012ee1 100644 (file)
@@ -188,8 +188,8 @@ class GESSimpleTimelineTest(GESTest):
                          len(self.track_types))
         self.layer = self.timeline.append_layer()
 
-    def add_clip(self, start, in_point, duration):
-        clip = GES.TestClip()
+    def add_clip(self, start, in_point, duration, asset_type=GES.TestClip):
+        clip = GES.Asset.request(asset_type, None).extract()
         clip.props.start = start
         clip.props.in_point = in_point
         clip.props.duration = duration
@@ -197,11 +197,11 @@ class GESSimpleTimelineTest(GESTest):
 
         return clip
 
-    def append_clip(self, layer=0):
+    def append_clip(self, layer=0, asset_type=GES.TestClip):
         while len(self.timeline.get_layers()) < layer + 1:
             self.timeline.append_layer()
         layer = self.timeline.get_layers()[layer]
-        clip = GES.TestClip()
+        clip = GES.Asset.request(asset_type, None).extract()
         clip.props.start = layer.get_duration()
         clip.props.duration = 10
         self.assertTrue(layer.add_clip(clip))
@@ -215,6 +215,9 @@ class GESSimpleTimelineTest(GESTest):
             for clip in layer.get_clips():
                 layer_timings.append(
                     (type(clip), clip.props.start, clip.props.duration))
+                for child in clip.get_children(True):
+                    self.assertEqual(child.props.start, clip.props.start)
+                    self.assertEqual(child.props.duration, clip.props.duration)
 
             res.append(layer_timings)
         if topology != res:
index 2cfb7afb4e5746f3341c8655320b1ec249deb1b2..4b3c9c91dd5c8eae72e8643b4e0967d1bc107df2 100644 (file)
@@ -505,6 +505,160 @@ class TestEditing(common.GESSimpleTimelineTest):
             ]
         ])
 
+    def test_illegal_effect_move(self):
+        c0 = self.append_clip()
+        self.append_clip()
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 0, 10),
+                (GES.TestClip, 10, 10),
+            ]
+        ])
+
+        effect = GES.Effect.new("agingtv")
+        c0.add(effect)
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 0, 10),
+                (GES.TestClip, 10, 10),
+            ]
+        ])
+
+        self.assertFalse(effect.set_start(10))
+        self.assertEqual(effect.props.start, 0)
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 0, 10),
+                (GES.TestClip, 10, 10),
+            ]
+        ])
+
+
+        self.assertFalse(effect.set_duration(20))
+        self.assertEqual(effect.props.duration, 10)
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 0, 10),
+                (GES.TestClip, 10, 10),
+            ]
+        ])
+
+    def test_moving_overlay_clip_in_group(self):
+        c0 = self.append_clip()
+        overlay = self.append_clip(asset_type=GES.TextOverlayClip)
+        group = GES.Group.new()
+        group.add(c0)
+        group.add(overlay)
+
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 0, 10),
+                (GES.TextOverlayClip, 10, 10),
+            ]
+        ], groups=[(c0, overlay)])
+
+        self.assertTrue(overlay.set_start(20))
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 10, 10),
+                (GES.TextOverlayClip, 20, 10),
+            ]
+        ], groups=[(c0, overlay)])
+
+    def test_moving_group_in_group(self):
+        c0 = self.append_clip()
+        overlay = self.append_clip(asset_type=GES.TextOverlayClip)
+        group0 = GES.Group.new()
+        group0.add(c0)
+        group0.add(overlay)
+
+        c1 = self.append_clip()
+        group1 = GES.Group.new()
+        group1.add(group0)
+        group1.add(c1)
+
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 0, 10),
+                (GES.TextOverlayClip, 10, 10),
+                (GES.TestClip, 20, 10),
+            ]
+        ], groups=[(c1, group0), (c0, overlay)])
+
+        self.assertTrue(group0.set_start(10))
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 10, 10),
+                (GES.TextOverlayClip, 20, 10),
+                (GES.TestClip, 30, 10),
+            ]
+        ], groups=[(c1, group0), (c0, overlay)])
+        self.check_element_values(group0, 10, 0, 20)
+        self.check_element_values(group1, 10, 0, 30)
+
+    def test_illegal_group_child_move(self):
+        clip0 = self.append_clip()
+        _clip1 = self.add_clip(20, 0, 10)
+        overlay = self.add_clip(20, 0, 10, asset_type=GES.TextOverlayClip)
+
+        group = GES.Group.new()
+        group.add(clip0)
+        group.add(overlay)
+
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 0, 10),
+                (GES.TextOverlayClip, 20, 10),
+                (GES.TestClip, 20, 10),
+            ]
+        ], groups=[(clip0, overlay),])
+
+        # Can't move as clip0 and clip1 would fully overlap
+        self.assertFalse(overlay.set_start(40))
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 0, 10),
+                (GES.TextOverlayClip, 20, 10),
+                (GES.TestClip, 20, 10),
+            ]
+        ], groups=[(clip0, overlay)])
+
+    def test_child_duration_change(self):
+        c0 = self.append_clip()
+
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 0, 10),
+            ]
+        ])
+        self.assertTrue(c0.set_duration(40))
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 0, 40),
+            ]
+        ])
+
+        c0.children[0].set_duration(10)
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 0, 10),
+            ]
+        ])
+
+        self.assertTrue(c0.set_start(40))
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 40, 10),
+            ]
+        ])
+
+        c0.children[0].set_start(10)
+        self.assertTimelineTopology([
+            [
+                (GES.TestClip, 10, 10),
+            ]
+        ])
+
 
 class TestInvalidOverlaps(common.GESSimpleTimelineTest):