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 ed44884ed204dd74f7053c5a7578aa7bea5a4c7c..d4672c123852a159a36c1120786a471622b1f9d7 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 045282565dfba86a0e7fc540673a7692dc297661..eb6f1092bf5b4f14b356e792e4450bd8c601b470 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 00947d8ba69a974c0aed762c3e8e51388c06e7f3..92cb496c276aa905bb4f2a6623a2b76d131dcca7 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 e28607bcf5b85e2d996a4d95bd0d34fbbc5ee6de..c648fc86c048db4da241cfd3567e8c870da3cdd7 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):