mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-26 06:54:49 +00:00
clip: Make sure to create transition after a clip is splitted
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
This commit is contained in:
parent
31e7ca2ef7
commit
bd142e285d
5 changed files with 103 additions and 15 deletions
|
@ -1385,6 +1385,13 @@ ges_clip_split (GESClip * clip, guint64 position)
|
||||||
|
|
||||||
_set_duration0 (GES_TIMELINE_ELEMENT (clip), old_duration);
|
_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;
|
return new_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,9 @@ G_GNUC_INTERNAL
|
||||||
void
|
void
|
||||||
timeline_fill_gaps (GESTimeline *timeline);
|
timeline_fill_gaps (GESTimeline *timeline);
|
||||||
|
|
||||||
|
G_GNUC_INTERNAL void
|
||||||
|
timeline_create_transitions (GESTimeline * timeline, GESTrackElement * track_element);
|
||||||
|
|
||||||
G_GNUC_INTERNAL
|
G_GNUC_INTERNAL
|
||||||
void
|
void
|
||||||
track_resort_and_fill_gaps (GESTrack *track);
|
track_resort_and_fill_gaps (GESTrack *track);
|
||||||
|
|
|
@ -1065,8 +1065,9 @@ _create_transitions_on_layer (GESTimeline * timeline, GESLayer * layer,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* @track_element must be a GESSource */
|
/* @track_element must be a GESSource */
|
||||||
static void
|
void
|
||||||
create_transitions (GESTimeline * timeline, GESTrackElement * track_element)
|
timeline_create_transitions (GESTimeline * timeline,
|
||||||
|
GESTrackElement * track_element)
|
||||||
{
|
{
|
||||||
GESTrack *track;
|
GESTrack *track;
|
||||||
GList *layer_node;
|
GList *layer_node;
|
||||||
|
@ -1220,7 +1221,7 @@ start_tracking_track_element (GESTimeline * timeline,
|
||||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||||
|
|
||||||
timeline_update_duration (timeline);
|
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->snapping_distance == 0)
|
||||||
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
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;
|
timeline->priv->movecontext.needs_move_ctx = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
create_transitions (timeline, child);
|
timeline_create_transitions (timeline, child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ def create_main_loop():
|
||||||
mainloop.run = run
|
mainloop.run = run
|
||||||
return mainloop
|
return mainloop
|
||||||
|
|
||||||
|
|
||||||
def create_project(with_group=False, saved=False):
|
def create_project(with_group=False, saved=False):
|
||||||
"""Creates a project with two clips in a group."""
|
"""Creates a project with two clips in a group."""
|
||||||
project = GES.Project()
|
project = GES.Project()
|
||||||
|
@ -78,6 +79,7 @@ def create_project(with_group=False, saved=False):
|
||||||
|
|
||||||
return timeline
|
return timeline
|
||||||
|
|
||||||
|
|
||||||
class GESTest(unittest.TestCase):
|
class GESTest(unittest.TestCase):
|
||||||
def _log(self, func, format, *args):
|
def _log(self, func, format, *args):
|
||||||
string = format
|
string = format
|
||||||
|
@ -105,9 +107,22 @@ class GESTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
class GESSimpleTimelineTest(GESTest):
|
class GESSimpleTimelineTest(GESTest):
|
||||||
|
def __init__(self, *args):
|
||||||
|
self.track_types = [GES.TrackType.AUDIO, GES.TrackType.VIDEO]
|
||||||
|
super(GESSimpleTimelineTest, self).__init__(*args)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.timeline = GES.Timeline.new_audio_video()
|
self.timeline = GES.Timeline.new()
|
||||||
self.assertEqual(len(self.timeline.get_tracks()), 2)
|
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()
|
self.layer = self.timeline.append_layer()
|
||||||
|
|
||||||
def add_clip(self, start, in_point, duration):
|
def add_clip(self, start, in_point, duration):
|
||||||
|
|
|
@ -45,6 +45,7 @@ class TestTimeline(unittest.TestCase):
|
||||||
project = GES.Project.new(uri=timeline.get_asset().props.uri)
|
project = GES.Project.new(uri=timeline.get_asset().props.uri)
|
||||||
|
|
||||||
loaded_called = False
|
loaded_called = False
|
||||||
|
|
||||||
def loaded(unused_project, unused_timeline):
|
def loaded(unused_project, unused_timeline):
|
||||||
nonlocal loaded_called
|
nonlocal loaded_called
|
||||||
loaded_called = True
|
loaded_called = True
|
||||||
|
@ -62,6 +63,59 @@ class TestTimeline(unittest.TestCase):
|
||||||
self.assertTrue(loaded_called)
|
self.assertTrue(loaded_called)
|
||||||
handle.assert_not_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):
|
class TestEditing(GESSimpleTimelineTest):
|
||||||
|
|
||||||
def test_transition_disappears_when_moving_to_another_layer(self):
|
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)
|
self.assertEquals(len(self.layer.get_clips()), 4)
|
||||||
|
|
||||||
layer2 = self.timeline.append_layer()
|
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(self.layer.get_clips()), 1)
|
||||||
self.assertEquals(len(layer2.get_clips()), 1)
|
self.assertEquals(len(layer2.get_clips()), 1)
|
||||||
|
|
||||||
|
@ -83,7 +138,8 @@ class TestEditing(GESSimpleTimelineTest):
|
||||||
self.assertEquals(len(all_clips), 4)
|
self.assertEquals(len(all_clips), 4)
|
||||||
|
|
||||||
layer2 = self.timeline.append_layer()
|
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(self.layer.get_clips(), [])
|
||||||
self.assertEquals(set(layer2.get_clips()), set(all_clips))
|
self.assertEquals(set(layer2.get_clips()), set(all_clips))
|
||||||
|
|
||||||
|
@ -94,7 +150,8 @@ class TestEditing(GESSimpleTimelineTest):
|
||||||
all_clips = self.layer.get_clips()
|
all_clips = self.layer.get_clips()
|
||||||
self.assertEquals(len(all_clips), 4)
|
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))
|
self.assertEquals(set(self.layer.get_clips()), set(all_clips))
|
||||||
|
|
||||||
def test_transition_rippling_over_does_not_create_another_transition(self):
|
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)
|
clip1 = self.add_clip(0, 0, 17 * Gst.SECOND)
|
||||||
clip2 = clip1.split(7.0 * Gst.SECOND)
|
clip2 = clip1.split(7.0 * Gst.SECOND)
|
||||||
# Make a transition between the two clips
|
# 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
|
# Rippl clip1 and check that transitions ar always the sames
|
||||||
all_clips = self.layer.get_clips()
|
all_clips = self.layer.get_clips()
|
||||||
self.assertEquals(len(all_clips), 4)
|
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)
|
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)
|
self.assertEquals(len(self.layer.get_clips()), 4)
|
||||||
|
|
||||||
|
|
||||||
|
@ -156,7 +216,9 @@ class TestTransitions(GESSimpleTimelineTest):
|
||||||
clip2.connect("notify::start", property_changed_cb)
|
clip2.connect("notify::start", property_changed_cb)
|
||||||
|
|
||||||
# Move clip2 to create a transition with clip1.
|
# 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
|
# The clip-added signal is emitted twice, once for the video
|
||||||
# transition and once for the audio transition.
|
# 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"])
|
||||||
|
|
Loading…
Reference in a new issue