timeline-tree: fix overlap check
authorHenry Wilkes <hwilkes@igalia.com>
Wed, 1 Apr 2020 20:34:48 +0000 (21:34 +0100)
committerHenry Wilkes <hwilkes@igalia.com>
Wed, 8 Apr 2020 13:35:28 +0000 (14:35 +0100)
Previously, the code was not able to detect that an element overlaps on
its end, nor could it detect that an element overlaps two elements that
already overlap.

ges/ges-timeline-tree.c
tests/check/python/test_timeline.py

index 93be05227aa285013d5c5de84da45d99a6281d4e..d0a03d689ca801adc246051459988734dcafbda5 100644 (file)
@@ -64,6 +64,8 @@ struct _TreeIterationData
   /* Elements overlaping on the start/end of @element */
   GESTimelineElement *overlaping_on_start;
   GESTimelineElement *overlaping_on_end;
+  GstClockTime overlap_start_final_time;
+  GstClockTime overlap_end_first_time;
 
   /* Timestamp after which elements will be rippled */
   GstClockTime ripple_time;
@@ -90,6 +92,8 @@ struct _TreeIterationData
    .movings = NULL,
    .overlaping_on_start = NULL,
    .overlaping_on_end = NULL,
+   .overlap_start_final_time = GST_CLOCK_TIME_NONE,
+   .overlap_end_first_time = GST_CLOCK_TIME_NONE,
    .ripple_time = GST_CLOCK_TIME_NONE,
    .snapping = NULL,
    .edge = GES_EDGE_NONE,
@@ -436,6 +440,7 @@ check_track_elements_overlaps_and_values (GNode * node,
   }
 
   if (moving_start < end && moving_start > start) {
+    /* moving_start is between the start and end of the node */
     GST_LOG ("Overlap start: %s<%p> [%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT
         "] and %s<%p> [%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT " (%"
         G_GINT64_FORMAT ")]", e->name, e, start, end, data->element->name,
@@ -445,9 +450,18 @@ check_track_elements_overlaps_and_values (GNode * node,
           data->overlaping_on_start->name, e->name);
       goto error;
     }
-
+    if (GST_CLOCK_TIME_IS_VALID (data->overlap_end_first_time) &&
+        end > data->overlap_end_first_time) {
+      GST_INFO ("%s overlaps %s at start and %s at end, but they already "
+          "overlap each other", data->element->name, e->name,
+          data->overlaping_on_end->name);
+      goto error;
+    }
+    /* record the time at which the overlapped ends */
+    data->overlap_start_final_time = end;
     data->overlaping_on_start = node->data;
-  } else if (moving_end > end && end > moving_start) {
+  } else if (moving_end < end && moving_end > start) {
+    /* moving_end is between the start and end of the node */
     GST_LOG ("Overlap end: %s<%p> [%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT
         "] and %s<%p> [%" G_GINT64_FORMAT "-%" G_GINT64_FORMAT " (%"
         G_GINT64_FORMAT ")]", e->name, e, start, end, data->element->name,
@@ -458,6 +472,15 @@ check_track_elements_overlaps_and_values (GNode * node,
           data->overlaping_on_end->name, e->name);
       goto error;
     }
+    if (GST_CLOCK_TIME_IS_VALID (data->overlap_start_final_time) &&
+        start < data->overlap_start_final_time) {
+      GST_INFO ("%s overlaps %s at end and %s at start, but they already "
+          "overlap each other", data->element->name, e->name,
+          data->overlaping_on_start->name);
+      goto error;
+    }
+    /* record the time at which the overlapped starts */
+    data->overlap_end_first_time = start;
     data->overlaping_on_end = node->data;
   }
 
index 2ac941b367e8feb6df71ceb41fbeb97126c27d01..9ff87186c87fc976a4e58ec5a66896e3d379f4f0 100644 (file)
@@ -1188,6 +1188,84 @@ class TestInvalidOverlaps(common.GESSimpleTimelineTest):
         ])
 
 
+class TestConfigurationRules(common.GESSimpleTimelineTest):
+
+    def _try_add_clip(self, start, duration, layer=None):
+        if layer is None:
+            layer = self.layer
+        asset = GES.Asset.request(GES.TestClip, None)
+        # large inpoint to allow trims
+        return layer.add_asset (asset, start, 1000, duration,
+                GES.TrackType.UNKNOWN)
+
+    def test_full_overlap_add(self):
+        clip1 = self._try_add_clip(50, 50)
+        self.assertIsNotNone(clip1)
+        self.assertIsNone(self._try_add_clip(50, 50))
+        self.assertIsNone(self._try_add_clip(49, 51))
+        self.assertIsNone(self._try_add_clip(51, 49))
+
+    def test_triple_overlap_add(self):
+        clip1 = self._try_add_clip(0, 50)
+        self.assertIsNotNone(clip1)
+        clip2 = self._try_add_clip(40, 50)
+        self.assertIsNotNone(clip2)
+        self.assertIsNone(self._try_add_clip(40, 10))
+        self.assertIsNone(self._try_add_clip(30, 30))
+        self.assertIsNone(self._try_add_clip(1, 88))
+
+    def test_full_overlap_move(self):
+        clip1 = self._try_add_clip(0, 50)
+        self.assertIsNotNone(clip1)
+        clip2 = self._try_add_clip(50, 50)
+        self.assertIsNotNone(clip2)
+        self.assertFalse(clip2.set_start(0))
+
+    def test_triple_overlap_move(self):
+        clip1 = self._try_add_clip(0, 50)
+        self.assertIsNotNone(clip1)
+        clip2 = self._try_add_clip(40, 50)
+        self.assertIsNotNone(clip2)
+        clip3 = self._try_add_clip(100, 60)
+        self.assertIsNotNone(clip3)
+        self.assertFalse(clip3.set_start(30))
+
+    def test_full_overlap_move_into_layer(self):
+        clip1 = self._try_add_clip(0, 50)
+        self.assertIsNotNone(clip1)
+        layer2 = self.timeline.append_layer()
+        clip2 = self._try_add_clip(0, 50, layer2)
+        self.assertIsNotNone(clip2)
+        self.assertFalse(clip2.move_to_layer(self.layer))
+
+    def test_triple_overlap_move_into_layer(self):
+        clip1 = self._try_add_clip(0, 50)
+        self.assertIsNotNone(clip1)
+        clip2 = self._try_add_clip(40, 50)
+        self.assertIsNotNone(clip2)
+        layer2 = self.timeline.append_layer()
+        clip3 = self._try_add_clip(30, 30, layer2)
+        self.assertIsNotNone(clip3)
+        self.assertFalse(clip3.move_to_layer(self.layer))
+
+    def test_full_overlap_trim(self):
+        clip1 = self._try_add_clip(0, 50)
+        self.assertIsNotNone(clip1)
+        clip2 = self._try_add_clip(50, 50)
+        self.assertIsNotNone(clip2)
+        self.assertFalse(clip2.trim(0))
+        self.assertFalse(clip1.set_duration(100))
+
+    def test_triple_overlap_trim(self):
+        clip1 = self._try_add_clip(0, 20)
+        self.assertIsNotNone(clip1)
+        clip2 = self._try_add_clip(10, 30)
+        self.assertIsNotNone(clip2)
+        clip3 = self._try_add_clip(30, 20)
+        self.assertIsNotNone(clip3)
+        self.assertFalse(clip3.trim(19))
+        self.assertFalse(clip1.set_duration(31))
+
 class TestSnapping(common.GESSimpleTimelineTest):
 
     def test_snapping(self):