timeline: Disable movement that lead to 2 transition at a position
authorThibault Saunier <tsaunier@gnome.org>
Tue, 23 Jun 2015 14:11:26 +0000 (16:11 +0200)
committerThibault Saunier <tsaunier@gnome.org>
Fri, 3 Jul 2015 12:06:54 +0000 (14:06 +0200)
We should never let 3 objects to overlap at a same position, for that
we introduce a "rollback" feature and whenever such an editing happens,
we rollback object position to whatever it was before the move.

ges/ges-timeline.c
ges/gstframepositionner.c
tests/check/ges/layer.c

index 0e6114c..20d5eca 100644 (file)
@@ -153,6 +153,11 @@ struct _GESTimelinePrivate
 
   /* The auto-transition of the timeline */
   gboolean auto_transition;
+  /* Use to determine that a edit action should be rolled
+   * back because it leads to a wrong state of the element
+   * position (currently only happens if 3 clips overlap) */
+  gboolean needs_rollback;
+  gboolean rolling_back;
 
   /* Timeline edition modes and snapping management */
   guint64 snapping_distance;
@@ -844,19 +849,25 @@ _find_transition_from_auto_transitions (GESTimeline * timeline,
     GESTrackElement * next, GstClockTime transition_duration)
 {
   GList *tmp;
-  GESAutoTransition *auto_transition = NULL;
-
-  gchar *key = g_strdup_printf ("%p%p", prev, next);
 
   for (tmp = timeline->priv->auto_transitions; tmp; tmp = tmp->next) {
-    if (!g_strcmp0 (GES_AUTO_TRANSITION (tmp->data)->key, key)) {
-      auto_transition = tmp->data;
-      break;
+    GESAutoTransition *auto_trans = (GESAutoTransition *) tmp->data;
+
+    /* We already have a transition linked to one of the elements we want to
+     * find a transition for */
+    if (auto_trans->previous_source == prev || auto_trans->next_source == next) {
+      if (auto_trans->previous_source != prev
+          || auto_trans->next_source != next) {
+        timeline->priv->needs_rollback = TRUE;
+        GST_INFO_OBJECT (timeline, "Failed creating auto transition, "
+            " trying to have 3 clips overlapping, rolling back");
+      }
+
+      return auto_trans;
     }
   }
-  g_free (key);
 
-  return auto_transition;
+  return NULL;
 }
 
 static GESAutoTransition *
@@ -1282,7 +1293,10 @@ done:
   if (emit) {
     GstClockTime snap_time = ret ? *ret : GST_CLOCK_TIME_NONE;
 
-    ges_timeline_emit_snappig (timeline, trackelement, ret);
+    if (!timeline->priv->needs_rollback)
+      ges_timeline_emit_snappig (timeline, trackelement, ret);
+    else
+      ges_timeline_emit_snappig (timeline, trackelement, NULL);
 
     GST_DEBUG_OBJECT (timeline, "Snaping at %" GST_TIME_FORMAT,
         GST_TIME_ARGS (snap_time));
@@ -1646,7 +1660,7 @@ timeline_ripple_object (GESTimeline * timeline, GESTrackElement * obj,
   MoveContext *mv_ctx = &timeline->priv->movecontext;
 
   mv_ctx->ignore_needs_ctx = TRUE;
-
+  timeline->priv->needs_rollback = FALSE;
   if (!ges_timeline_set_moving_context (timeline, obj, GES_EDIT_MODE_RIPPLE,
           edge, layers))
     goto error;
@@ -1678,6 +1692,30 @@ timeline_ripple_object (GESTimeline * timeline, GESTrackElement * obj,
       g_list_free (moved_clips);
       _set_start0 (GES_TIMELINE_ELEMENT (obj), position);
 
+      if (timeline->priv->needs_rollback && !timeline->priv->rolling_back) {
+        timeline->priv->rolling_back = TRUE;
+        for (tmp = mv_ctx->moving_trackelements; tmp; tmp = tmp->next) {
+          trackelement = GES_TRACK_ELEMENT (tmp->data);
+          new_start = _START (trackelement) - offset;
+
+          container = add_toplevel_container (mv_ctx, trackelement);
+          /* Make sure not to move 2 times the same Clip */
+          if (g_list_find (moved_clips, container) == NULL) {
+            _set_start0 (GES_TIMELINE_ELEMENT (trackelement), new_start);
+            moved_clips = g_list_prepend (moved_clips, container);
+          }
+
+        }
+        g_list_free (moved_clips);
+        _set_start0 (GES_TIMELINE_ELEMENT (obj), position - offset);
+
+        ges_timeline_emit_snappig (timeline, obj, NULL);
+        mv_ctx->needs_move_ctx = TRUE;
+        timeline->priv->rolling_back = FALSE;
+
+        goto error;
+      }
+
       break;
     case GES_EDGE_END:
       timeline->priv->needs_transitions_update = FALSE;
@@ -1711,7 +1749,6 @@ timeline_ripple_object (GESTimeline * timeline, GESTrackElement * obj,
         }
         if (GES_IS_GROUP (container))
           container->children_control_mode = GES_CHILDREN_UPDATE;
-
       }
 
       g_list_free (moved_clips);
@@ -1755,17 +1792,37 @@ timeline_trim_object (GESTimeline * timeline, GESTrackElement * object,
     GList * layers, GESEdge edge, guint64 position)
 {
   gboolean ret = FALSE;
+  GstClockTime cpos;
   MoveContext *mv_ctx = &timeline->priv->movecontext;
 
   mv_ctx->ignore_needs_ctx = TRUE;
 
+  timeline->priv->needs_rollback = FALSE;
   if (!ges_timeline_set_moving_context (timeline, object, GES_EDIT_MODE_TRIM,
           edge, layers))
     goto end;
 
+  switch (edge) {
+    case GES_EDGE_START:
+      cpos = GES_TIMELINE_ELEMENT_START (object);
+      break;
+    case GES_EDGE_END:
+      cpos = GES_TIMELINE_ELEMENT_END (object);
+      break;
+    default:
+      goto end;
+  }
   ret = ges_timeline_trim_object_simple (timeline,
       GES_TIMELINE_ELEMENT (object), layers, edge, position, TRUE);
 
+  if (timeline->priv->needs_rollback && !timeline->priv->rolling_back) {
+    timeline->priv->rolling_back = TRUE;
+    ret = FALSE;
+    timeline_trim_object (timeline, object, layers, edge, cpos);
+    ges_timeline_emit_snappig (timeline, object, NULL);
+    timeline->priv->rolling_back = FALSE;
+  }
+
 end:
   mv_ctx->ignore_needs_ctx = FALSE;
 
@@ -1921,6 +1978,7 @@ ges_timeline_move_object_simple (GESTimeline * timeline,
     GESTimelineElement * element, GList * layers, GESEdge edge,
     guint64 position)
 {
+  GstClockTime cpos = GES_TIMELINE_ELEMENT_START (element);
   guint64 *snap_end, *snap_st, *cur, off1, off2, end;
   GESTrackElement *track_element;
 
@@ -1930,6 +1988,7 @@ ges_timeline_move_object_simple (GESTimeline * timeline,
       g_list_find (timeline->priv->movecontext.moving_trackelements, element))
     return FALSE;
 
+  timeline->priv->needs_rollback = FALSE;
   track_element = GES_TRACK_ELEMENT (element);
   end = position + _DURATION (get_toplevel_container (track_element));
   cur = g_hash_table_lookup (timeline->priv->by_end, track_element);
@@ -1963,10 +2022,20 @@ ges_timeline_move_object_simple (GESTimeline * timeline,
     ges_timeline_emit_snappig (timeline, track_element, snap_st);
   } else
     ges_timeline_emit_snappig (timeline, track_element, NULL);
-
+  timeline->priv->needs_rollback = FALSE;
 
   _set_start0 (GES_TIMELINE_ELEMENT (track_element), position);
 
+  if (timeline->priv->needs_rollback && !timeline->priv->rolling_back) {
+    timeline->priv->needs_rollback = FALSE;
+    timeline->priv->rolling_back = TRUE;
+    ges_timeline_move_object_simple (timeline, element, layers, edge, cpos);
+    ges_timeline_emit_snappig (timeline, track_element, NULL);
+    timeline->priv->rolling_back = FALSE;
+
+    return FALSE;
+  }
+
   return TRUE;
 }
 
@@ -1976,8 +2045,6 @@ timeline_context_to_layer (GESTimeline * timeline, gint offset)
   gboolean ret = TRUE;
   MoveContext *mv_ctx = &timeline->priv->movecontext;
 
-
-
   /* Layer's priority is always positive */
   if (offset != 0 && (offset > 0 || mv_ctx->min_move_layer >= -offset)) {
     GHashTableIter iter;
@@ -1990,6 +2057,7 @@ timeline_context_to_layer (GESTimeline * timeline, gint offset)
     GST_DEBUG ("Moving %d object, offset %d",
         g_hash_table_size (mv_ctx->toplevel_containers), offset);
 
+    timeline->priv->needs_rollback = FALSE;
     g_hash_table_iter_init (&iter, mv_ctx->toplevel_containers);
     while (g_hash_table_iter_next (&iter, (gpointer *) & key,
             (gpointer *) & value)) {
@@ -2029,6 +2097,13 @@ timeline_context_to_layer (GESTimeline * timeline, gint offset)
     mv_ctx->min_move_layer = mv_ctx->min_move_layer + offset;
 
     mv_ctx->ignore_needs_ctx = FALSE;
+
+    if (timeline->priv->needs_rollback && !timeline->priv->rolling_back) {
+      ret = FALSE;
+      timeline->priv->rolling_back = TRUE;
+      timeline_context_to_layer (timeline, -offset);
+      timeline->priv->rolling_back = FALSE;
+    }
   }
 
   return ret;
@@ -2117,9 +2192,30 @@ static void
 layer_auto_transition_changed_cb (GESLayer * layer,
     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
 {
+  timeline->priv->needs_rollback = FALSE;
   _create_transitions_on_layer (timeline, layer, NULL, NULL,
       _create_auto_transition_from_transitions);
+  if (timeline->priv->needs_rollback) {
+    GList *tmp, *trans;
+
+    ges_layer_set_auto_transition (layer, FALSE);
+    GST_ERROR_OBJECT (layer, "Has overlapping transition, "
+        " we can't handle that, setting auto_transition"
+        " to FALSE, and removing all transitions");
+    trans = g_list_copy (timeline->priv->auto_transitions);
+    for (tmp = trans; tmp; tmp = tmp->next) {
+      g_signal_emit_by_name (tmp->data, "destroy-me");
+    }
+
+    g_list_free (trans);
 
+    trans = ges_layer_get_clips (layer);
+    for (tmp = trans; tmp; tmp = tmp->next) {
+      if (GES_IS_TRANSITION_CLIP (tmp->data))
+        ges_layer_remove_clip (layer, tmp->data);
+    }
+    g_list_free_full (trans, gst_object_unref);
+  }
 }
 
 static void
index 9faebb4..e1b7827 100644 (file)
@@ -141,7 +141,6 @@ sync_properties_from_caps (GstFramePositionner * pos, GstCaps * caps)
   pos->track_width = width;
   pos->track_height = height;
 
-  GST_ERROR_OBJECT (pos, "syncing size from caps : %d %d", width, height);
   GST_DEBUG_OBJECT (pos, "syncing framerate from caps : %d/%d", pos->fps_n,
       pos->fps_d);
 
index 35c4128..a7f10fb 100644 (file)
@@ -671,59 +671,9 @@ GST_START_TEST (test_single_layer_automatic_transition)
   fail_unless (current->data == src2);
   g_list_free_full (objects, gst_object_unref);
 
-  GST_DEBUG ("Set third clip start to 1000, Transition should be updated");
-  ges_container_edit (GES_CONTAINER (src2), NULL, -1,
-      GES_EDIT_MODE_NORMAL, GES_EDGE_START, 1000);
-  ges_timeline_commit (timeline);
-  /*             600____src___1100
-   *                       !_tr__^
-   *        500___________src1________1250
-   *                       1000___________src2________2000
-   *                       ^____trans____^
-   */
-  assert_equals_uint64 (_START (src), 600);
-  assert_equals_uint64 (_DURATION (src), 500);
-  assert_equals_uint64 (_START (src1), 500);
-  assert_equals_uint64 (_DURATION (src1), 1250 - 500);
-  assert_equals_uint64 (_START (src2), 1000);
-  assert_equals_uint64 (_DURATION (src2), 1000);
-
-  current = objects = ges_layer_get_clips (layer);
-  current = objects;
-  assert_equals_int (g_list_length (objects), 7);
-  assert_is_type (objects->data, GES_TYPE_TEST_CLIP);
-  fail_unless (current->data == src1);
-
-  current = current->next;
-  fail_unless (current->data == src);
-
-  current = current->next;
-  transition = current->data;
-  assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
-  assert_equals_uint64 (_START (transition), 1000);
-  assert_equals_uint64 (_DURATION (transition), 1100 - 1000);
-
-  current = current->next;
-  transition = current->data;
-  assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
-  assert_equals_uint64 (_START (transition), 1000);
-  assert_equals_uint64 (_DURATION (transition), 1100 - 1000);
-
-  current = current->next;
-  transition = current->data;
-  assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
-  assert_equals_uint64 (_START (transition), 1000);
-  assert_equals_uint64 (_DURATION (transition), 1250 - 1000);
-
-  current = current->next;
-  transition = current->data;
-  assert_is_type (transition, GES_TYPE_TRANSITION_CLIP);
-  assert_equals_uint64 (_START (transition), 1000);
-  assert_equals_uint64 (_DURATION (transition), 1250 - 1000);
-
-  current = current->next;
-  fail_unless (current->data == src2);
-  g_list_free_full (objects, gst_object_unref);
+  GST_DEBUG ("Check that we can not create 2 transitions at the same place");
+  fail_if (ges_container_edit (GES_CONTAINER (src2), NULL, -1,
+          GES_EDIT_MODE_NORMAL, GES_EDGE_START, 1000));
 
   /*
    *        500___________src1________1250
@@ -731,6 +681,8 @@ GST_START_TEST (test_single_layer_automatic_transition)
    *                       ^____trans____^
    */
   ges_layer_remove_clip (layer, GES_CLIP (src));
+  fail_unless (ges_container_edit (GES_CONTAINER (src2), NULL, -1,
+          GES_EDIT_MODE_NORMAL, GES_EDGE_START, 1000));
   assert_equals_uint64 (_START (src1), 500);
   assert_equals_uint64 (_DURATION (src1), 1250 - 500);
   assert_equals_uint64 (_START (src2), 1000);