diff --git a/ges/ges-auto-transition.c b/ges/ges-auto-transition.c index dd89c4a53c..e36955bb1f 100644 --- a/ges/ges-auto-transition.c +++ b/ges/ges-auto-transition.c @@ -39,8 +39,8 @@ static guint auto_transition_signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (GESAutoTransition, ges_auto_transition, G_TYPE_OBJECT); static void -neighbour_changed_cb (GESClip * clip, GParamSpec * arg G_GNUC_UNUSED, - GESAutoTransition * self) +neighbour_changed_cb (G_GNUC_UNUSED GObject * object, + G_GNUC_UNUSED GParamSpec * arg, GESAutoTransition * self) { gint64 new_duration; guint32 layer_prio; @@ -110,7 +110,36 @@ _track_changed_cb (GESTrackElement * track_element, g_signal_emit (self, auto_transition_signals[DESTROY_ME], 0); } +} +static void +_connect_to_source (GESAutoTransition * self, GESTrackElement * source) +{ + g_signal_connect (source, "notify::start", + G_CALLBACK (neighbour_changed_cb), self); + g_signal_connect_after (source, "notify::priority", + G_CALLBACK (neighbour_changed_cb), self); + g_signal_connect (source, "notify::duration", + G_CALLBACK (neighbour_changed_cb), self); + + g_signal_connect (source, "notify::track", + G_CALLBACK (_track_changed_cb), self); +} + +static void +_disconnect_from_source (GESAutoTransition * self, GESTrackElement * source) +{ + g_signal_handlers_disconnect_by_func (source, neighbour_changed_cb, self); + g_signal_handlers_disconnect_by_func (source, _track_changed_cb, self); +} + +void +ges_auto_transition_set_previous_source (GESAutoTransition * self, + GESTrackElement * source) +{ + _disconnect_from_source (self, self->previous_source); + _connect_to_source (self, source); + self->previous_source = source; } static void @@ -123,16 +152,8 @@ ges_auto_transition_finalize (GObject * object) { GESAutoTransition *self = GES_AUTO_TRANSITION (object); - g_signal_handlers_disconnect_by_func (self->previous_source, - neighbour_changed_cb, self); - g_signal_handlers_disconnect_by_func (self->next_source, neighbour_changed_cb, - self); - g_signal_handlers_disconnect_by_func (self->next_source, _track_changed_cb, - self); - g_signal_handlers_disconnect_by_func (self->previous_source, - _track_changed_cb, self); - - g_free (self->key); + _disconnect_from_source (self, self->previous_source); + _disconnect_from_source (self, self->next_source); G_OBJECT_CLASS (ges_auto_transition_parent_class)->finalize (object); } @@ -149,7 +170,6 @@ ges_auto_transition_class_init (GESAutoTransitionClass * klass) object_class->finalize = ges_auto_transition_finalize; } - GESAutoTransition * ges_auto_transition_new (GESTrackElement * transition, GESTrackElement * previous_source, GESTrackElement * next_source) @@ -161,48 +181,26 @@ ges_auto_transition_new (GESTrackElement * transition, self->next_source = next_source; self->transition = transition; - self->previous_clip = - GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (previous_source)); - self->next_clip = GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (next_source)); self->transition_clip = GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (transition)); - g_signal_connect (previous_source, "notify::start", - G_CALLBACK (neighbour_changed_cb), self); - g_signal_connect_after (previous_source, "notify::priority", - G_CALLBACK (neighbour_changed_cb), self); - g_signal_connect (next_source, "notify::start", - G_CALLBACK (neighbour_changed_cb), self); - g_signal_connect (next_source, "notify::priority", - G_CALLBACK (neighbour_changed_cb), self); - g_signal_connect (previous_source, "notify::duration", - G_CALLBACK (neighbour_changed_cb), self); - g_signal_connect (next_source, "notify::duration", - G_CALLBACK (neighbour_changed_cb), self); - - g_signal_connect (next_source, "notify::track", - G_CALLBACK (_track_changed_cb), self); - g_signal_connect (previous_source, "notify::track", - G_CALLBACK (_track_changed_cb), self); + _connect_to_source (self, previous_source); + _connect_to_source (self, next_source); GST_DEBUG_OBJECT (self, "Created transition %" GST_PTR_FORMAT " between %" GST_PTR_FORMAT "[%" GST_TIME_FORMAT " - %" GST_TIME_FORMAT "] and: %" GST_PTR_FORMAT "[%" GST_TIME_FORMAT " - %" GST_TIME_FORMAT "]" - " in layer nb %i, start: %" GST_TIME_FORMAT " duration: %" - GST_TIME_FORMAT, transition, previous_source, + " in layer nb %" G_GUINT32_FORMAT ", start: %" GST_TIME_FORMAT + " duration: %" GST_TIME_FORMAT, transition, previous_source, GST_TIME_ARGS (_START (previous_source)), GST_TIME_ARGS (_END (previous_source)), next_source, GST_TIME_ARGS (_START (next_source)), GST_TIME_ARGS (_END (next_source)), - ges_layer_get_priority (ges_clip_get_layer - (self->previous_clip)), + GES_TIMELINE_ELEMENT_LAYER_PRIORITY (next_source), GST_TIME_ARGS (_START (transition)), GST_TIME_ARGS (_DURATION (transition))); - self->key = g_strdup_printf ("%p%p", self->previous_source, - self->next_source); - return self; } @@ -211,5 +209,5 @@ ges_auto_transition_update (GESAutoTransition * self) { GST_INFO ("Updating info %s", GES_TIMELINE_ELEMENT_NAME (self->transition_clip)); - neighbour_changed_cb (self->previous_clip, NULL, self); + neighbour_changed_cb (NULL, NULL, self); } diff --git a/ges/ges-auto-transition.h b/ges/ges-auto-transition.h index a6e2ebb34e..29f6238884 100644 --- a/ges/ges-auto-transition.h +++ b/ges/ges-auto-transition.h @@ -49,15 +49,9 @@ struct _GESAutoTransition GESTrackElement *next_source; GESTrackElement *transition; - GESLayer *layer; - - GESClip *previous_clip; - GESClip *next_clip; GESClip *transition_clip; gboolean positioning; - gchar *key; - gboolean frozen; /* Padding for API extension */ diff --git a/ges/ges-clip.c b/ges/ges-clip.c index 1fbb6521de..13126d93b5 100644 --- a/ges/ges-clip.c +++ b/ges/ges-clip.c @@ -2204,7 +2204,7 @@ ges_clip_set_top_effect_index (GESClip * clip, GESBaseEffect * effect, GESClip * ges_clip_split (GESClip * clip, guint64 position) { - GList *tmp; + GList *tmp, *transitions = NULL; GESClip *new_object; GstClockTime start, inpoint, duration, old_duration, new_duration; gdouble media_duration_factor; @@ -2272,6 +2272,7 @@ ges_clip_split (GESClip * clip, guint64 position) * binding values */ track_for_copy = g_hash_table_new_full (NULL, NULL, gst_object_unref, gst_object_unref); + /* _add_child will add core elements at the lowest priority and new * non-core effects at the lowest effect priority, so we need to add the * highest priority children first to preserve the effect order. The @@ -2279,14 +2280,29 @@ ges_clip_split (GESClip * clip, guint64 position) for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) { GESTrackElement *copy, *orig = tmp->data; GESTrack *track = ges_track_element_get_track (orig); + GESAutoTransition *trans; + /* FIXME: is position - start + inpoint always the correct splitting * point for control bindings? What coordinate system are control * bindings given in? */ copy = ges_clip_copy_track_element_into (new_object, orig, position - start + inpoint); - if (copy && track) + + if (!copy) + continue; + + if (track) g_hash_table_insert (track_for_copy, gst_object_ref (copy), gst_object_ref (track)); + + trans = timeline ? + ges_timeline_get_auto_transition_at_end (timeline, orig) : NULL; + + if (trans) { + trans->frozen = TRUE; + ges_auto_transition_set_previous_source (trans, copy); + transitions = g_list_append (transitions, trans); + } } GES_TIMELINE_ELEMENT_SET_BEING_EDITED (clip); @@ -2310,7 +2326,14 @@ ges_clip_split (GESClip * clip, guint64 position) } } + for (tmp = transitions; tmp; tmp = tmp->next) { + GESAutoTransition *trans = tmp->data; + trans->frozen = FALSE; + ges_auto_transition_update (trans); + } + g_hash_table_unref (track_for_copy); + g_list_free_full (transitions, gst_object_unref); return new_object; } diff --git a/ges/ges-internal.h b/ges/ges-internal.h index 5c4100649c..ae1f275cc3 100644 --- a/ges/ges-internal.h +++ b/ges/ges-internal.h @@ -106,6 +106,9 @@ GstDebugCategory * _ges_debug (void); G_GNUC_INTERNAL void ges_timeline_freeze_auto_transitions (GESTimeline * timeline, gboolean freeze); +G_GNUC_INTERNAL GESAutoTransition * +ges_timeline_get_auto_transition_at_end (GESTimeline * timeline, GESTrackElement * source); + G_GNUC_INTERNAL gboolean ges_timeline_edit (GESTimeline * timeline, GESTimelineElement * element, GList * layers, gint64 new_layer_priority, GESEditMode mode, GESEdge edge, @@ -155,6 +158,11 @@ ges_timeline_add_clip (GESTimeline * timeline, GESClip * clip); G_GNUC_INTERNAL void ges_timeline_remove_clip (GESTimeline * timeline, GESClip * clip); +G_GNUC_INTERNAL void +ges_auto_transition_set_previous_source (GESAutoTransition * self, GESTrackElement * source); + + + G_GNUC_INTERNAL void track_resort_and_fill_gaps (GESTrack *track); diff --git a/ges/ges-timeline.c b/ges/ges-timeline.c index fc7a628e2a..6ffadf8d8a 100644 --- a/ges/ges-timeline.c +++ b/ges/ges-timeline.c @@ -980,6 +980,34 @@ ges_timeline_find_auto_transition (GESTimeline * timeline, return NULL; } +GESAutoTransition * +ges_timeline_get_auto_transition_at_end (GESTimeline * timeline, + GESTrackElement * source) +{ + GList *tmp, *auto_transitions; + GESAutoTransition *ret = NULL; + + LOCK_DYN (timeline); + auto_transitions = g_list_copy_deep (timeline->priv->auto_transitions, + (GCopyFunc) gst_object_ref, NULL); + UNLOCK_DYN (timeline); + + for (tmp = auto_transitions; tmp; tmp = tmp->next) { + 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 == source) { + ret = gst_object_ref (auto_trans); + break; + } + } + + g_list_free_full (auto_transitions, gst_object_unref); + + return ret; +} + static GESAutoTransition * _create_auto_transition_from_transitions (GESTimeline * timeline, GESTrackElement * prev, GESTrackElement * next, @@ -1168,12 +1196,12 @@ _edit_auto_transition (GESTimeline * timeline, GESTimelineElement * element, } if (edge == GES_EDGE_END) - replace = GES_TIMELINE_ELEMENT (auto_transition->previous_clip); + replace = GES_TIMELINE_ELEMENT (auto_transition->previous_source); else - replace = GES_TIMELINE_ELEMENT (auto_transition->next_clip); + replace = GES_TIMELINE_ELEMENT (auto_transition->next_source); - GST_INFO_OBJECT (element, "Trimming clip %" GES_FORMAT " in place " - "of trimming the corresponding auto-transition", GES_ARGS (replace)); + GST_INFO_OBJECT (element, "Trimming %" GES_FORMAT " in place of " + "trimming the corresponding auto-transition", GES_ARGS (replace)); return ges_timeline_element_edit (replace, layers, -1, mode, edge, position); } diff --git a/tests/check/ges/clip.c b/tests/check/ges/clip.c index 1e7b29b7e0..405d078cbb 100644 --- a/tests/check/ges/clip.c +++ b/tests/check/ges/clip.c @@ -308,6 +308,136 @@ GST_START_TEST (test_split_direct_absolute_bindings) GST_END_TEST; +static GESTimelineElement * +_find_auto_transition (GESTrack * track, GESClip * from_clip, GESClip * to_clip) +{ + GstClockTime start, end; + GList *tmp, *track_els; + GESTimelineElement *ret = NULL; + GESLayer *layer0, *layer1; + + layer0 = ges_clip_get_layer (from_clip); + layer1 = ges_clip_get_layer (to_clip); + + fail_unless (layer0 == layer1, "%" GES_FORMAT " and %" GES_FORMAT " do not " + "share the same layer", GES_ARGS (from_clip), GES_ARGS (to_clip)); + gst_object_unref (layer1); + + start = GES_TIMELINE_ELEMENT_START (to_clip); + end = GES_TIMELINE_ELEMENT_START (from_clip) + + GES_TIMELINE_ELEMENT_DURATION (from_clip); + + fail_if (end <= start, "%" GES_FORMAT " starts after %" GES_FORMAT " ends", + GES_ARGS (to_clip), GES_ARGS (from_clip)); + + track_els = ges_track_get_elements (track); + + for (tmp = track_els; tmp; tmp = tmp->next) { + GESTimelineElement *el = tmp->data; + if (GES_IS_TRANSITION (el) && el->start == start + && (el->start + el->duration) == end) { + fail_if (ret, "Found two transitions %" GES_FORMAT " and %" GES_FORMAT + " between %" GES_FORMAT " and %" GES_FORMAT " in track %" + GST_PTR_FORMAT, GES_ARGS (el), GES_ARGS (ret), GES_ARGS (from_clip), + GES_ARGS (to_clip), track); + ret = el; + } + } + fail_unless (ret, "Found no transitions between %" GES_FORMAT " and %" + GES_FORMAT " in track %" GST_PTR_FORMAT, GES_ARGS (from_clip), + GES_ARGS (to_clip), track); + + g_list_free_full (track_els, gst_object_unref); + + fail_unless (GES_IS_CLIP (ret->parent), "Transition %" GES_FORMAT + " between %" GES_FORMAT " and %" GES_FORMAT " in track %" + GST_PTR_FORMAT " has no parent clip", GES_ARGS (ret), + GES_ARGS (from_clip), GES_ARGS (to_clip), track); + + layer1 = ges_clip_get_layer (GES_CLIP (ret->parent)); + + fail_unless (layer0 == layer1, "Transition %" GES_FORMAT " between %" + GES_FORMAT " and %" GES_FORMAT " in track %" GST_PTR_FORMAT + " belongs to layer %" GST_PTR_FORMAT " rather than %" GST_PTR_FORMAT, + GES_ARGS (ret), GES_ARGS (from_clip), GES_ARGS (to_clip), track, + layer1, layer0); + + gst_object_unref (layer0); + gst_object_unref (layer1); + + return ret; +} + +GST_START_TEST (test_split_with_auto_transitions) +{ + GESTimeline *timeline; + GESLayer *layer; + GESTrack *tracks[3]; + GESTimelineElement *found; + GESTimelineElement *prev_trans[3]; + GESTimelineElement *post_trans[3]; + GESAsset *asset; + GESClip *clip, *split, *prev, *post; + guint i; + + ges_init (); + + timeline = ges_timeline_new (); + + ges_timeline_set_auto_transition (timeline, TRUE); + + tracks[0] = GES_TRACK (ges_audio_track_new ()); + tracks[1] = GES_TRACK (ges_audio_track_new ()); + tracks[2] = GES_TRACK (ges_video_track_new ()); + + for (i = 0; i < 3; i++) + fail_unless (ges_timeline_add_track (timeline, tracks[i])); + + layer = ges_timeline_append_layer (timeline); + asset = ges_asset_request (GES_TYPE_TEST_CLIP, NULL, NULL); + + prev = ges_layer_add_asset (layer, asset, 0, 0, 10, GES_TRACK_TYPE_UNKNOWN); + clip = ges_layer_add_asset (layer, asset, 5, 0, 20, GES_TRACK_TYPE_UNKNOWN); + post = ges_layer_add_asset (layer, asset, 20, 0, 10, GES_TRACK_TYPE_UNKNOWN); + + fail_unless (prev); + fail_unless (clip); + fail_unless (post); + + for (i = 0; i < 3; i++) { + prev_trans[i] = _find_auto_transition (tracks[i], prev, clip); + post_trans[i] = _find_auto_transition (tracks[i], clip, post); + /* 3 sources, 2 auto-transitions */ + assert_num_in_track (tracks[i], 5); + + } + + /* cannot split within a transition */ + fail_if (ges_clip_split (clip, 5)); + fail_if (ges_clip_split (clip, 20)); + + /* we should keep the same auto-transitions during a split */ + split = ges_clip_split (clip, 15); + fail_unless (split); + + for (i = 0; i < 3; i++) { + found = _find_auto_transition (tracks[i], prev, clip); + fail_unless (found == prev_trans[i], "Transition between %" GES_FORMAT + " and %" GES_FORMAT " changed", GES_ARGS (prev), GES_ARGS (clip)); + + found = _find_auto_transition (tracks[i], split, post); + fail_unless (found == post_trans[i], "Transition between %" GES_FORMAT + " and %" GES_FORMAT " changed", GES_ARGS (clip), GES_ARGS (post)); + } + + gst_object_unref (timeline); + gst_object_unref (asset); + + ges_deinit (); +} + +GST_END_TEST; + static GPtrArray * _select_none (GESTimeline * timeline, GESClip * clip, GESTrackElement * track_element, guint * called_p) @@ -3329,6 +3459,7 @@ ges_suite (void) tcase_add_test (tc_chain, test_split_ordering); tcase_add_test (tc_chain, test_split_direct_bindings); tcase_add_test (tc_chain, test_split_direct_absolute_bindings); + tcase_add_test (tc_chain, test_split_with_auto_transitions); tcase_add_test (tc_chain, test_clip_group_ungroup); tcase_add_test (tc_chain, test_clip_can_group); tcase_add_test (tc_chain, test_adding_children_to_track);