clip: Make sure to create transition after a clip is splitted
authorThibault Saunier <tsaunier@igalia.com>
Sun, 18 Mar 2018 14:03:00 +0000 (11:03 -0300)
committerThibault Saunier <tsaunier@igalia.com>
Sun, 18 Mar 2018 14:31:17 +0000 (11:31 -0300)
In the (now tested) scenario where we have a transition on the right
side of a clip we are splitting, auto transitions can't be created
because we resize the clip after adding the new one, meaning that
there are 3 elements in the "transition zone", we need to force
auto transition creation after the splitting.

Fixes https://gitlab.gnome.org/GNOME/pitivi/issues/2142

ges/ges-clip.c
ges/ges-internal.h
ges/ges-timeline.c
tests/check/python/common.py
tests/check/python/test_timeline.py

index 34ac6fa..70c7ee3 100644 (file)
@@ -1385,6 +1385,13 @@ ges_clip_split (GESClip * clip, guint64 position)
 
   _set_duration0 (GES_TIMELINE_ELEMENT (clip), old_duration);
 
+  if (GES_TIMELINE_ELEMENT_TIMELINE (clip)) {
+    for (tmp = GES_CONTAINER_CHILDREN (new_object); tmp; tmp = tmp->next) {
+      timeline_create_transitions (GES_TIMELINE_ELEMENT_TIMELINE (tmp->data),
+          tmp->data);
+    }
+  }
+
   return new_object;
 }
 
index 9eaf01f..502b11b 100644 (file)
@@ -125,6 +125,9 @@ G_GNUC_INTERNAL
 void
 timeline_fill_gaps            (GESTimeline *timeline);
 
+G_GNUC_INTERNAL void
+timeline_create_transitions (GESTimeline * timeline, GESTrackElement * track_element);
+
 G_GNUC_INTERNAL
 void
 track_resort_and_fill_gaps    (GESTrack *track);
index 9febf1f..aba5788 100644 (file)
@@ -1065,8 +1065,9 @@ _create_transitions_on_layer (GESTimeline * timeline, GESLayer * layer,
 }
 
 /* @track_element must be a GESSource */
-static void
-create_transitions (GESTimeline * timeline, GESTrackElement * track_element)
+void
+timeline_create_transitions (GESTimeline * timeline,
+    GESTrackElement * track_element)
 {
   GESTrack *track;
   GList *layer_node;
@@ -1220,7 +1221,7 @@ start_tracking_track_element (GESTimeline * timeline,
     timeline->priv->movecontext.needs_move_ctx = TRUE;
 
     timeline_update_duration (timeline);
-    create_transitions (timeline, trackelement);
+    timeline_create_transitions (timeline, trackelement);
   }
 }
 
@@ -2598,7 +2599,7 @@ trackelement_start_changed_cb (GESTrackElement * child,
         timeline->priv->snapping_distance == 0)
       timeline->priv->movecontext.needs_move_ctx = TRUE;
 
-    create_transitions (timeline, child);
+    timeline_create_transitions (timeline, child);
   }
 }
 
@@ -2668,7 +2669,7 @@ trackelement_duration_changed_cb (GESTrackElement * child,
       timeline->priv->movecontext.needs_move_ctx = TRUE;
     }
 
-    create_transitions (timeline, child);
+    timeline_create_transitions (timeline, child);
   }
 }
 
index 23d81e9..81e7855 100644 (file)
@@ -55,6 +55,7 @@ def create_main_loop():
     mainloop.run = run
     return mainloop
 
+
 def create_project(with_group=False, saved=False):
     """Creates a project with two clips in a group."""
     project = GES.Project()
@@ -78,6 +79,7 @@ def create_project(with_group=False, saved=False):
 
     return timeline
 
+
 class GESTest(unittest.TestCase):
     def _log(self, func, format, *args):
         string = format
@@ -105,9 +107,22 @@ class GESTest(unittest.TestCase):
 
 
 class GESSimpleTimelineTest(GESTest):
+    def __init__(self, *args):
+        self.track_types = [GES.TrackType.AUDIO, GES.TrackType.VIDEO]
+        super(GESSimpleTimelineTest, self).__init__(*args)
+
     def setUp(self):
-        self.timeline = GES.Timeline.new_audio_video()
-        self.assertEqual(len(self.timeline.get_tracks()), 2)
+        self.timeline = GES.Timeline.new()
+        for track_type in self.track_types:
+            self.assertIn(
+                track_type, [GES.TrackType.AUDIO, GES.TrackType.VIDEO])
+            if track_type == GES.TrackType.AUDIO:
+                self.timeline.add_track(GES.AudioTrack.new())
+            else:
+                self.timeline.add_track(GES.VideoTrack.new())
+
+        self.assertEqual(len(self.timeline.get_tracks()),
+                         len(self.track_types))
         self.layer = self.timeline.append_layer()
 
     def add_clip(self, start, in_point, duration):
index f7d3556..1769b5f 100644 (file)
@@ -45,6 +45,7 @@ class TestTimeline(unittest.TestCase):
         project = GES.Project.new(uri=timeline.get_asset().props.uri)
 
         loaded_called = False
+
         def loaded(unused_project, unused_timeline):
             nonlocal loaded_called
             loaded_called = True
@@ -62,6 +63,59 @@ class TestTimeline(unittest.TestCase):
         self.assertTrue(loaded_called)
         handle.assert_not_called()
 
+
+class TestSplitting(GESSimpleTimelineTest):
+    def setUp(self):
+        self.track_types = [GES.TrackType.AUDIO]
+        super(TestSplitting, self).setUp()
+
+    def assertTimelineTopology(self, topology):
+        res = []
+        for layer in self.timeline.get_layers():
+            layer_timings = []
+            for clip in layer.get_clips():
+                layer_timings.append(
+                    (type(clip), clip.props.start, clip.props.duration))
+
+            res.append(layer_timings)
+
+        self.assertEqual(topology, res)
+        return res
+
+    def test_spliting_with_auto_transition_on_the_left(self):
+        self.timeline.props.auto_transition = True
+        clip1 = self.add_clip(0, 0, 100)
+        clip2 = self.add_clip(50, 0, 100)
+        self.assertTimelineTopology([
+            [  # Unique layer
+                (GES.TestClip, 0, 100),
+                (GES.TransitionClip, 50, 50),
+                (GES.TestClip, 50, 100)
+            ]
+        ])
+
+        clip1.split(25)
+        self.assertTimelineTopology([
+            [  # Unique layer
+                (GES.TestClip, 0, 25),
+                (GES.TestClip, 25, 75),
+                (GES.TransitionClip, 50, 50),
+                (GES.TestClip, 50, 100),
+            ]
+        ])
+
+        clip2.split(125)
+        self.assertTimelineTopology([
+            [  # Unique layer
+                (GES.TestClip, 0, 25),
+                (GES.TestClip, 25, 75),
+                (GES.TransitionClip, 50, 50),
+                (GES.TestClip, 50, 75),
+                (GES.TestClip, 125, 25),
+            ]
+        ])
+
+
 class TestEditing(GESSimpleTimelineTest):
 
     def test_transition_disappears_when_moving_to_another_layer(self):
@@ -71,7 +125,8 @@ class TestEditing(GESSimpleTimelineTest):
         self.assertEquals(len(self.layer.get_clips()), 4)
 
         layer2 = self.timeline.append_layer()
-        clip2.edit([], layer2.get_priority(), GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, clip2.props.start)
+        clip2.edit([], layer2.get_priority(), GES.EditMode.EDIT_NORMAL,
+                   GES.Edge.EDGE_NONE, clip2.props.start)
         self.assertEquals(len(self.layer.get_clips()), 1)
         self.assertEquals(len(layer2.get_clips()), 1)
 
@@ -83,7 +138,8 @@ class TestEditing(GESSimpleTimelineTest):
         self.assertEquals(len(all_clips), 4)
 
         layer2 = self.timeline.append_layer()
-        clip1.edit([], layer2.get_priority(), GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, clip1.props.start)
+        clip1.edit([], layer2.get_priority(), GES.EditMode.EDIT_RIPPLE,
+                   GES.Edge.EDGE_NONE, clip1.props.start)
         self.assertEquals(self.layer.get_clips(), [])
         self.assertEquals(set(layer2.get_clips()), set(all_clips))
 
@@ -94,7 +150,8 @@ class TestEditing(GESSimpleTimelineTest):
         all_clips = self.layer.get_clips()
         self.assertEquals(len(all_clips), 4)
 
-        clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, clip2.props.start + 1)
+        clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_RIPPLE,
+                   GES.Edge.EDGE_NONE, clip2.props.start + 1)
         self.assertEquals(set(self.layer.get_clips()), set(all_clips))
 
     def test_transition_rippling_over_does_not_create_another_transition(self):
@@ -103,14 +160,17 @@ class TestEditing(GESSimpleTimelineTest):
         clip1 = self.add_clip(0, 0, 17 * Gst.SECOND)
         clip2 = clip1.split(7.0 * Gst.SECOND)
         # Make a transition between the two clips
-        clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 4.5 * Gst.SECOND)
+        clip1.edit([], self.layer.get_priority(),
+                   GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 4.5 * Gst.SECOND)
 
         # Rippl clip1 and check that transitions ar always the sames
         all_clips = self.layer.get_clips()
         self.assertEquals(len(all_clips), 4)
-        clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 41.5 * Gst.SECOND)
+        clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_RIPPLE,
+                   GES.Edge.EDGE_NONE, 41.5 * Gst.SECOND)
         self.assertEquals(len(self.layer.get_clips()), 4)
-        clip1.edit([], self.layer.get_priority(), GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 35 * Gst.SECOND)
+        clip1.edit([], self.layer.get_priority(),
+                   GES.EditMode.EDIT_RIPPLE, GES.Edge.EDGE_NONE, 35 * Gst.SECOND)
         self.assertEquals(len(self.layer.get_clips()), 4)
 
 
@@ -156,7 +216,9 @@ class TestTransitions(GESSimpleTimelineTest):
         clip2.connect("notify::start", property_changed_cb)
 
         # Move clip2 to create a transition with clip1.
-        clip2.edit([], self.layer.get_priority(), GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 50)
+        clip2.edit([], self.layer.get_priority(),
+                   GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 50)
         # The clip-added signal is emitted twice, once for the video
         # transition and once for the audio transition.
-        self.assertEqual(signals, ["notify::start", "clip-added", "clip-added"])
+        self.assertEqual(
+            signals, ["notify::start", "clip-added", "clip-added"])