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;
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
{
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);
}
object_class->finalize = ges_auto_transition_finalize;
}
-
GESAutoTransition *
ges_auto_transition_new (GESTrackElement * transition,
GESTrackElement * previous_source, GESTrackElement * next_source)
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;
}
{
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);
}
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 */
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;
* 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
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);
}
}
+ 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;
}
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,
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);
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,
}
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);
}
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)
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);