From 6b7c658b6a551a5b9170987ba44592d1d819e1ae Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Thu, 5 Mar 2020 15:56:28 -0300 Subject: [PATCH] ges: Make setting start/duration move or trim generic 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 | 2 + ges/ges-clip.c | 5 +- ges/ges-source-clip.c | 47 --------- ges/ges-timeline-element.c | 44 +++++++- ges/ges-timeline-tree.c | 106 ++++++++++++++++--- ges/ges-timeline.c | 10 +- tests/check/ges/group.c | 51 ++++++--- tests/check/python/common.py | 11 +- tests/check/python/test_timeline.py | 154 ++++++++++++++++++++++++++++ 9 files changed, 338 insertions(+), 92 deletions(-) diff --git a/ges/ges-auto-transition.c b/ges/ges-auto-transition.c index f28f37aee8..057267cb12 100644 --- a/ges/ges-auto-transition.c +++ b/ges/ges-auto-transition.c @@ -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; } diff --git a/ges/ges-clip.c b/ges/ges-clip.c index bdb5989a29..faaf9974d9 100644 --- a/ges/ges-clip.c +++ b/ges/ges-clip.c @@ -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; diff --git a/ges/ges-source-clip.c b/ges/ges-source-clip.c index 7f5903c14d..21c9f9562b 100644 --- a/ges/ges-source-clip.c +++ b/ges/ges-source-clip.c @@ -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 diff --git a/ges/ges-timeline-element.c b/ges/ges-timeline-element.c index ecf58a2f9b..b4c89d9f4d 100644 --- a/ges/ges-timeline-element.c +++ b/ges/ges-timeline-element.c @@ -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]); } diff --git a/ges/ges-timeline-tree.c b/ges/ges-timeline-tree.c index 6f875dcf80..8956ff74b3 100644 --- a/ges/ges-timeline-tree.c +++ b/ges/ges-timeline-tree.c @@ -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); diff --git a/ges/ges-timeline.c b/ges/ges-timeline.c index bf086b07f1..f8cb45c6d6 100644 --- a/ges/ges-timeline.c +++ b/ges/ges-timeline.c @@ -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; } diff --git a/tests/check/ges/group.c b/tests/check/ges/group.c index edbd9117b0..091601c417 100644 --- a/tests/check/ges/group.c +++ b/tests/check/ges/group.c @@ -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); diff --git a/tests/check/python/common.py b/tests/check/python/common.py index 81a439c6c4..0f55c929e6 100644 --- a/tests/check/python/common.py +++ b/tests/check/python/common.py @@ -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: diff --git a/tests/check/python/test_timeline.py b/tests/check/python/test_timeline.py index 2cfb7afb4e..4b3c9c91dd 100644 --- a/tests/check/python/test_timeline.py +++ b/tests/check/python/test_timeline.py @@ -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):