ges: Add API to disable timeline coherence checks
authorThibault Saunier <tsaunier@igalia.com>
Wed, 26 Oct 2022 02:12:00 +0000 (23:12 -0300)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Fri, 2 Dec 2022 18:37:29 +0000 (18:37 +0000)
There are cases where user might want to be in full control of the
timeline and not be limited by the checks that are being done by GES
to go from one timeline layout to another, this should be doable as
it is a valid use case.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3501>

subprojects/gst-editing-services/ges/ges-timeline-tree.c
subprojects/gst-editing-services/ges/ges-timeline.c
subprojects/gst-editing-services/ges/ges-timeline.h
subprojects/gst-editing-services/tests/check/python/test_timeline.py

index ed44884..d4672c1 100644 (file)
@@ -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 "
index 0452825..eb6f109 100644 (file)
  * 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;
+}
index 00947d8..92cb496 100644 (file)
@@ -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
index e28607b..c648fc8 100644 (file)
@@ -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):