From d23e43ae1a94f65583fc5888cb3c91c5e551d0f8 Mon Sep 17 00:00:00 2001 From: Thibault Saunier Date: Tue, 23 Jun 2015 16:11:26 +0200 Subject: [PATCH] timeline: Disable movement that lead to 2 transition at a position 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 | 124 +++++++++++++++++++++++++++++++++----- ges/gstframepositionner.c | 1 - tests/check/ges/layer.c | 58 ++---------------- 3 files changed, 115 insertions(+), 68 deletions(-) diff --git a/ges/ges-timeline.c b/ges/ges-timeline.c index 0e6114ccd5..20d5eca5ae 100644 --- a/ges/ges-timeline.c +++ b/ges/ges-timeline.c @@ -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 diff --git a/ges/gstframepositionner.c b/ges/gstframepositionner.c index 9faebb4e71..e1b7827e70 100644 --- a/ges/gstframepositionner.c +++ b/ges/gstframepositionner.c @@ -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); diff --git a/tests/check/ges/layer.c b/tests/check/ges/layer.c index 35c4128421..a7f10fb80b 100644 --- a/tests/check/ges/layer.c +++ b/tests/check/ges/layer.c @@ -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);