mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
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.
This commit is contained in:
parent
42700e98c5
commit
d23e43ae1a
3 changed files with 115 additions and 68 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
g_free (key);
|
||||
GESAutoTransition *auto_trans = (GESAutoTransition *) tmp->data;
|
||||
|
||||
return auto_transition;
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GESAutoTransition *
|
||||
|
@ -1282,7 +1293,10 @@ done:
|
|||
if (emit) {
|
||||
GstClockTime snap_time = ret ? *ret : GST_CLOCK_TIME_NONE;
|
||||
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue