diff --git a/subprojects/gst-editing-services/ges/ges-timeline-tree.c b/subprojects/gst-editing-services/ges/ges-timeline-tree.c index ed44884ed2..d4672c1238 100644 --- a/subprojects/gst-editing-services/ges/ges-timeline-tree.c +++ b/subprojects/gst-editing-services/ges/ges-timeline-tree.c @@ -929,6 +929,11 @@ timeline_tree_can_move_elements (GNode * root, GHashTable * moving, GError ** error) { TreeIterationData data = tree_iteration_data_init; + + if (ges_timeline_get_edit_apis_disabled (root->data)) { + return TRUE; + } + data.moving = moving; data.root = root; data.res = TRUE; @@ -1703,6 +1708,10 @@ timeline_tree_can_move_element (GNode * root, GHashTableIter iter; gpointer key, value; + if (ges_timeline_get_edit_apis_disabled (root->data)) { + return TRUE; + } + if (layer_prio == GES_TIMELINE_ELEMENT_NO_LAYER_PRIORITY && priority != layer_prio) { GST_INFO_OBJECT (element, "Cannot move to a layer when no layer " diff --git a/subprojects/gst-editing-services/ges/ges-timeline.c b/subprojects/gst-editing-services/ges/ges-timeline.c index 045282565d..eb6f1092bf 100644 --- a/subprojects/gst-editing-services/ges/ges-timeline.c +++ b/subprojects/gst-editing-services/ges/ges-timeline.c @@ -115,6 +115,10 @@ * transition object will be kept, but with its timing and layer adjusted * accordingly. * + * NOTE: if you know what you are doing and want to be in full control of the + * timeline layout, you can disable the edit APIs with + * #ges_timeline_disable_edit_apis. + * * ## Saving * * To save/load a timeline, you can use the ges_timeline_load_from_uri() @@ -234,6 +238,7 @@ struct _GESTimelinePrivate GstStreamCollection *stream_collection; gboolean rendering_smartly; + gboolean disable_edit_apis; }; /* private structure to contain our track-related information */ @@ -2947,7 +2952,7 @@ ges_timeline_commit_sync (GESTimeline * timeline) * Freezes the timeline from being committed. This is usually needed while the * timeline is being rendered to ensure that not change to the timeline are * taken into account during that moment. Once the rendering is done, you - * should call #ges_timeline_thaw_commit so that comiting becomes possible + * should call #ges_timeline_thaw_commit so that committing becomes possible * again and any call to `commit()` that happened during the rendering is * actually taken into account. * @@ -3038,6 +3043,7 @@ ges_timeline_set_auto_transition (GESTimeline * timeline, GESLayer *layer; g_return_if_fail (GES_IS_TIMELINE (timeline)); + g_return_if_fail (!timeline->priv->disable_edit_apis); CHECK_THREAD (timeline); timeline->priv->auto_transition = auto_transition; @@ -3387,3 +3393,64 @@ ges_timeline_get_frame_at (GESTimeline * self, GstClockTime timestamp) return gst_util_uint64_scale (timestamp, fps_n, fps_d * GST_SECOND); } + +/** + * ges_timeline_disable_edit_apis: + * @self: A #GESTimeline + * @disable_edit_apis: %TRUE to disable all the edit APIs so the user is in full + * control of ensuring timeline state validity %FALSE otherwise. + * + * WARNING: When using that mode, GES won't guarantee the coherence of the + * timeline. You need to ensure that the rules described in the [Overlaps and + * auto transitions](#overlaps-and-autotransitions) section are respected any time + * the timeline is [commited](ges_timeline_commit) (otherwise playback will most + * probably fail in different ways). + * + * When disabling editing APIs, GES won't be able to enforce the rules that + * makes the timeline overall state to be valid but some feature won't be + * usable: + * * #GESTimeline:snapping-distance + * * #GESTimeline:auto-transition + * + * Since: 1.22 + */ +void +ges_timeline_disable_edit_apis (GESTimeline * self, gboolean disable_edit_apis) +{ + CHECK_THREAD (self); + g_return_if_fail (GES_IS_TIMELINE (self)); + + if (disable_edit_apis) { + if (self->priv->snapping_distance > 0) { + GST_INFO_OBJECT (self, + "Disabling snapping as we are disabling edit APIs"); + + ges_timeline_set_snapping_distance (self, 0); + } + + if (self->priv->auto_transition || self->priv->auto_transitions) { + GST_INFO_OBJECT (self, + "Disabling auto transitions as we are disabling auto edit APIs"); + ges_timeline_set_auto_transition (self, FALSE); + } + } + + self->priv->disable_edit_apis = disable_edit_apis; +} + +/** + * ges_timeline_get_edit_apis_disabled: + * @self: A #GESTimeline + * + * Returns: %TRUE if edit APIs are disabled, %FALSE otherwise. + * + * Since: 1.22 + */ +gboolean +ges_timeline_get_edit_apis_disabled (GESTimeline * self) +{ + CHECK_THREAD (self); + g_return_val_if_fail (GES_IS_TIMELINE (self), FALSE); + + return self->priv->disable_edit_apis; +} diff --git a/subprojects/gst-editing-services/ges/ges-timeline.h b/subprojects/gst-editing-services/ges/ges-timeline.h index 00947d8ba6..92cb496c27 100644 --- a/subprojects/gst-editing-services/ges/ges-timeline.h +++ b/subprojects/gst-editing-services/ges/ges-timeline.h @@ -160,4 +160,9 @@ GES_API GESFrameNumber ges_timeline_get_frame_at (GESTimeline *self, GstClockTime timestamp); +GES_API +void ges_timeline_disable_edit_apis (GESTimeline * self, gboolean disable_edit_apis); +GES_API +gboolean ges_timeline_get_edit_apis_disabled (GESTimeline * self); + G_END_DECLS diff --git a/subprojects/gst-editing-services/tests/check/python/test_timeline.py b/subprojects/gst-editing-services/tests/check/python/test_timeline.py index e28607bcf5..c648fc86c0 100644 --- a/subprojects/gst-editing-services/tests/check/python/test_timeline.py +++ b/subprojects/gst-editing-services/tests/check/python/test_timeline.py @@ -3651,6 +3651,22 @@ class TestComplexEditing(common.GESTimelineConfigTest): c0, 0, GES.EditMode.EDIT_NORMAL, GES.Edge.EDGE_NONE, 10, 30, [c0], [c1], {c0 : {"start": 27}}, [], []) + def test_disable_timeline_editing_apis(self): + track = self.add_video_track() + self.assertEqual(self.timeline.props.auto_transition, True) + self.timeline.disable_edit_apis(True) + self.assertEqual(self.timeline.props.auto_transition, False) + + c0 = self.add_clip("c0", 0, [track], 0, 10) + # Without disabling edit API adding clip would fail + c1 = self.add_clip("c1", 0, [track], 0, 10) + self.assertTimelineConfig() + + c1.set_start(1) + c1.set_duration(1) + self.assertEqual(c1.get_start(), 1) + self.assertEqual(c1.get_duration(), 1) + class TestTransitions(common.GESSimpleTimelineTest):