Reimplement the auto-transition feature
authorThibault Saunier <thibault.saunier@collabora.com>
Thu, 10 Jan 2013 21:50:54 +0000 (18:50 -0300)
committerThibault Saunier <thibault.saunier@collabora.com>
Thu, 10 Jan 2013 21:53:15 +0000 (18:53 -0300)
  + Actually implement unit tests

ges/Makefile.am
ges/ges-auto-transition.c [new file with mode: 0644]
ges/ges-auto-transition.h [new file with mode: 0644]
ges/ges-timeline-layer.c
ges/ges-timeline.c
tests/check/ges/layer.c

index 4d53b65..92f5953 100644 (file)
@@ -55,6 +55,7 @@ libges_@GST_API_VERSION@_la_SOURCES =                 \
        ges-project.c \
        ges-base-xml-formatter.c \
        ges-xml-formatter.c \
+       ges-auto-transition.c \
        ges-utils.c
 
 libges_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/ges/
@@ -111,6 +112,7 @@ libges_@GST_API_VERSION@include_HEADERS =   \
 
 noinst_HEADERS = \
        ges-internal.h \
+       ges-auto-transition.h \
        ges-internal-enums.h
 
 libges_@GST_API_VERSION@_la_CFLAGS = -I$(top_srcdir) $(GST_PBUTILS_CFLAGS) \
diff --git a/ges/ges-auto-transition.c b/ges/ges-auto-transition.c
new file mode 100644 (file)
index 0000000..12f2d80
--- /dev/null
@@ -0,0 +1,182 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*-  */
+/*
+ * gst-editing-services
+ * Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
+ *
+ * gst-editing-services is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
+ */
+
+/* This class warps a GESTimelineTransition, letting any implementation
+ * of a GESTimelineTransition to be used.
+ *
+ * NOTE: This is for internal use exclusively
+ */
+
+#include "ges-auto-transition.h"
+#include "ges-internal.h"
+enum
+{
+  DESTROY_ME,
+  LAST_SIGNAL
+};
+
+static guint auto_transition_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GESAutoTransition, ges_auto_transition, G_TYPE_OBJECT);
+
+static void
+neighbour_changed_cb (GESTimelineObject * obj, GParamSpec * arg G_GNUC_UNUSED,
+    GESAutoTransition * self)
+{
+  gint64 new_duration;
+
+  if (self->next_source->priority / LAYER_HEIGHT !=
+      self->previous_source->priority / LAYER_HEIGHT) {
+    GST_DEBUG_OBJECT (self, "Destroy changed layer");
+    g_signal_emit (self, auto_transition_signals[DESTROY_ME], 0);
+    return;
+  }
+
+  new_duration =
+      (self->previous_source->start + self->previous_source->duration) -
+      self->next_source->start;
+
+  if (new_duration <= 0 || new_duration >= self->previous_source->duration ||
+      new_duration >= self->next_source->duration) {
+
+    GST_DEBUG_OBJECT (self, "Destroy %" G_GINT64_FORMAT " not a valid duration",
+        new_duration);
+    g_signal_emit (self, auto_transition_signals[DESTROY_ME], 0);
+    return;
+  }
+
+  ges_timeline_object_set_start (self->timeline_transition,
+      self->next_source->start);
+  ges_timeline_object_set_duration (self->timeline_transition, new_duration);
+}
+
+static void
+_height_changed_cb (GESTimelineObject * obj, GParamSpec * arg G_GNUC_UNUSED,
+    GESAutoTransition * self)
+{
+  /* FIXME This is really not smart and we should properly implement timelineobject
+   * priority management at the TimelineLayer level */
+  ges_timeline_object_set_priority (self->next_timeline_object,
+      self->previous_timeline_object->priority +
+      self->previous_timeline_object->height);
+}
+
+static void
+_track_changed_cb (GESTrackObject * obj, GParamSpec * arg G_GNUC_UNUSED,
+    GESAutoTransition * self)
+{
+  if (ges_track_object_get_track (obj) == NULL) {
+    GST_DEBUG_OBJECT (self, "Neighboor %" GST_PTR_FORMAT
+        " removed from track ... auto destructing", obj);
+
+    g_signal_emit (self, auto_transition_signals[DESTROY_ME], 0);
+  }
+
+}
+
+static void
+ges_auto_transition_init (GESAutoTransition * ges_auto_transition)
+{
+}
+
+static void
+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->previous_timeline_object,
+      _height_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);
+
+  G_OBJECT_CLASS (ges_auto_transition_parent_class)->finalize (object);
+}
+
+static void
+ges_auto_transition_class_init (GESAutoTransitionClass * klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  auto_transition_signals[DESTROY_ME] =
+      g_signal_new ("destroy-me", G_TYPE_FROM_CLASS (klass),
+      G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, 0, NULL, NULL, NULL,
+      G_TYPE_NONE, 0);
+  object_class->finalize = ges_auto_transition_finalize;
+}
+
+
+GESAutoTransition *
+ges_auto_transition_new (GESTrackObject * transition,
+    GESTrackObject * previous_source, GESTrackObject * next_source)
+{
+  GESAutoTransition *self = g_object_new (GES_TYPE_AUTO_TRANSITION, NULL);
+
+  self->previous_source = previous_source;
+  self->next_source = next_source;
+  self->transition = transition;
+
+  self->previous_timeline_object =
+      ges_track_object_get_timeline_object (previous_source);
+  self->next_timeline_object =
+      ges_track_object_get_timeline_object (next_source);
+  self->timeline_transition = ges_track_object_get_timeline_object (transition);
+
+  g_signal_connect (previous_source, "notify::start",
+      G_CALLBACK (neighbour_changed_cb), self);
+  g_signal_connect (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 (self->previous_timeline_object, "notify::height",
+      G_CALLBACK (_height_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);
+
+  _height_changed_cb (self->previous_timeline_object, NULL, self);
+
+  GST_DEBUG_OBJECT (self, "Created transition %" GST_PTR_FORMAT
+      " between %" GST_PTR_FORMAT " and: %" GST_PTR_FORMAT
+      " in layer nb %i, start: %" GST_TIME_FORMAT " duration: %"
+      GST_TIME_FORMAT, transition, next_source, previous_source,
+      ges_timeline_layer_get_priority (ges_timeline_object_get_layer
+          (self->previous_timeline_object)), GST_TIME_ARGS (transition->start),
+      GST_TIME_ARGS (transition->duration));
+
+  self->key = g_strdup_printf ("%p%p", self->previous_source,
+      self->next_source);
+
+  return self;
+}
diff --git a/ges/ges-auto-transition.h b/ges/ges-auto-transition.h
new file mode 100644 (file)
index 0000000..be07e54
--- /dev/null
@@ -0,0 +1,78 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*-  */
+/*
+ * gst-editing-services
+ * Copyright (C) 2013 Thibault Saunier <thibault.saunier@collabora.com>
+ *
+ * gst-editing-services is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * gst-editing-services is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.";
+ */
+
+#ifndef _GES_AUTO_TRANSITION_H_
+#define _GES_AUTO_TRANSITION_H_
+
+#include <glib-object.h>
+#include "ges-track-object.h"
+#include "ges-timeline-object.h"
+#include "ges-timeline-layer.h"
+
+G_BEGIN_DECLS
+
+#define GES_TYPE_AUTO_TRANSITION             (ges_auto_transition_get_type ())
+#define GES_AUTO_TRANSITION(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GES_TYPE_AUTO_TRANSITION, GESAutoTransition))
+#define GES_AUTO_TRANSITION_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GES_TYPE_AUTO_TRANSITION, GESAutoTransitionClass))
+#define GES_IS_AUTO_TRANSITION(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GES_TYPE_AUTO_TRANSITION))
+#define GES_IS_AUTO_TRANSITION_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GES_TYPE_AUTO_TRANSITION))
+#define GES_AUTO_TRANSITION_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GES_TYPE_AUTO_TRANSITION, GESAutoTransitionClass))
+
+typedef struct _GESAutoTransitionClass GESAutoTransitionClass;
+typedef struct _GESAutoTransition GESAutoTransition;
+
+
+
+struct _GESAutoTransitionClass
+{
+  GObjectClass parent_class;
+
+  /* Padding for API extension */
+  gpointer _ges_reserved[GES_PADDING];
+};
+
+struct _GESAutoTransition
+{
+  GObject parent_instance;
+
+  /* <read only and construct only> */
+  GESTrackObject *previous_source;
+  GESTrackObject *next_source;
+  GESTrackObject *transition;
+
+  GESTimelineLayer *layer;
+
+  GESTimelineObject *previous_timeline_object;
+  GESTimelineObject *next_timeline_object;
+  GESTimelineObject *timeline_transition;
+
+  gchar *key;
+
+  /* Padding for API extension */
+  gpointer _ges_reserved[GES_PADDING];
+};
+
+GType ges_auto_transition_get_type (void) G_GNUC_CONST;
+
+GESAutoTransition * ges_auto_transition_new (GESTrackObject * transition,
+                                             GESTrackObject * previous_source,
+                                             GESTrackObject * next_source);
+
+G_END_DECLS
+#endif /* _GES_AUTO_TRANSITION_H_ */
index 91b06f6..1b9c328 100644 (file)
 #include "ges.h"
 #include "ges-timeline-source.h"
 
-static void
-timeline_object_height_changed_cb (GESTimelineObject * obj,
-    GESTrackEffect * tr_eff, GESTimelineObject * second_obj);
-
 static void ges_meta_container_interface_init
     (GESMetaContainerInterface * iface);
 
@@ -211,316 +207,6 @@ ges_timeline_layer_init (GESTimelineLayer * self)
   self->max_gnl_priority = LAYER_HEIGHT;
 }
 
-static GList *
-track_get_by_layer (GESTimelineLayer * layer, GESTrack * track)
-{
-  GESTrackObject *tckobj;
-  guint32 layer_prio = layer->priv->priority;
-
-  GList *tck_objects_list = NULL, *tmp = NULL, *return_list = NULL;
-
-  tck_objects_list = ges_track_get_objects (track);
-  for (tmp = tck_objects_list; tmp; tmp = tmp->next) {
-    tckobj = GES_TRACK_OBJECT (tmp->data);
-
-    if (tckobj->priority / LAYER_HEIGHT == layer_prio) {
-      /*  We steal the reference from tck_objects_list */
-      return_list = g_list_append (return_list, tmp->data);
-    } else
-      g_object_unref (tmp->data);
-  }
-
-  return return_list;
-}
-
-/* Compare:
- * @compared: The #GList of #GESTrackObjects that we compare with @track_object
- * @track_object: The #GESTrackObject that serves as a reference
- * @ahead: %TRUE if we are comparing frontward %FALSE if we are comparing
- * backward*/
-static void
-compare (GList * compared, GESTrackObject * track_object, gboolean ahead)
-{
-  GList *tmp;
-  gint64 start, duration, compared_start, compared_duration, end, compared_end,
-      tr_start, tr_duration;
-  GESTimelineStandardTransition *trans = NULL;
-  GESTrack *track;
-  GESTimelineLayer *layer;
-  GESTimelineObject *object, *compared_object, *first_object, *second_object;
-  gint priority;
-
-  g_return_if_fail (compared);
-
-  GST_DEBUG ("Recalculating transitions");
-
-  object = ges_track_object_get_timeline_object (track_object);
-
-  if (!object) {
-    GST_WARNING ("Trackobject not in a timeline object: "
-        "Can not calulate transitions");
-
-    return;
-  }
-
-  compared_object = ges_track_object_get_timeline_object (compared->data);
-  layer = ges_timeline_object_get_layer (object);
-
-  start = ges_track_object_get_start (track_object);
-  duration = ges_track_object_get_duration (track_object);
-  compared_start = ges_track_object_get_start (compared->data);
-  compared_duration = ges_track_object_get_duration (compared->data);
-  end = start + duration;
-  compared_end = compared_start + compared_duration;
-
-  if (ahead) {
-    /* Make sure we remove the last transition we created it is not needed
-     * FIXME make it a smarter way */
-    if (compared->prev && GES_IS_TRACK_TRANSITION (compared->prev->data)) {
-      trans = GES_TIMELINE_STANDARD_TRANSITION
-          (ges_track_object_get_timeline_object (compared->prev->data));
-      g_object_get (compared->prev->data, "start", &tr_start, "duration",
-          &tr_duration, NULL);
-      if (tr_start >= compared_start && tr_start + tr_duration <= compared_end)
-        ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
-      trans = NULL;
-    }
-
-    for (tmp = compared->next; tmp; tmp = tmp->next) {
-      /* If we have a transitionmnmnm we recaluculuculate its values */
-      if (GES_IS_TRACK_TRANSITION (tmp->data)) {
-        g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
-            NULL);
-
-        if (tr_start + tr_duration == compared_start + compared_duration) {
-          GESTimelineObject *tlobj;
-          tlobj = ges_track_object_get_timeline_object (tmp->data);
-
-          trans = GES_TIMELINE_STANDARD_TRANSITION (tlobj);
-          break;
-        }
-      }
-    }
-
-    if (compared_end <= start) {
-      if (trans) {
-        ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
-        g_object_get (compared_object, "priority", &priority, NULL);
-        g_object_set (object, "priority", priority, NULL);
-      }
-
-      goto clean;
-    } else if (start > compared_start && end < compared_end) {
-      if (trans) {
-        /* Transition not needed anymore */
-        ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
-      }
-      goto clean;
-    } else if (start <= compared_start) {
-      if (trans) {
-        ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
-      }
-
-      goto clean;
-    }
-
-  } else {
-    if (compared->next && GES_IS_TRACK_TRANSITION (compared->next->data)) {
-      trans = GES_TIMELINE_STANDARD_TRANSITION
-          (ges_track_object_get_timeline_object (compared->next->data));
-      g_object_get (compared->prev->data, "start", &tr_start, "duration",
-          &tr_duration, NULL);
-      if (tr_start >= compared_start && tr_start + tr_duration <= compared_end)
-        ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
-      trans = NULL;
-    }
-    for (tmp = compared->prev; tmp; tmp = tmp->prev) {
-      if GES_IS_TRACK_TRANSITION
-        (tmp->data) {
-        g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
-            NULL);
-        if (tr_start == compared_start) {
-          trans = GES_TIMELINE_STANDARD_TRANSITION
-              (ges_track_object_get_timeline_object (tmp->data));
-          break;
-        }
-        }
-    }
-
-    if (start + duration <= compared_start) {
-      if (trans) {
-        ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
-        g_object_get (object, "priority", &priority, NULL);
-        g_object_set (compared_object, "priority", priority, NULL);
-      }
-      goto clean;
-
-    } else if (start > compared_start) {
-      if (trans)
-        ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
-
-      goto clean;
-    } else if (start < compared_start && end > compared_end) {
-      if (trans) {
-        ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
-      }
-
-      goto clean;
-    }
-  }
-
-  if (!trans) {
-    gint height;
-
-    trans =
-        ges_timeline_standard_transition_new_for_nick ((gchar *) "crossfade");
-    track = ges_track_object_get_track (track_object);
-
-    ges_timeline_object_set_supported_formats (GES_TIMELINE_OBJECT (trans),
-        track->type);
-
-    ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (trans));
-
-    if (ahead) {
-      first_object = ges_track_object_get_timeline_object (compared->data);
-      second_object = object;
-    } else {
-      second_object = ges_track_object_get_timeline_object (compared->data);
-      first_object = object;
-    }
-
-    g_object_get (first_object, "priority", &priority, "height", &height, NULL);
-    g_object_set (second_object, "priority", priority + height, NULL);
-    g_signal_connect (first_object, "notify::height",
-        (GCallback) timeline_object_height_changed_cb, second_object);
-  }
-
-  if (ahead) {
-    g_object_set (trans, "start", start, "duration",
-        compared_duration + compared_start - start, NULL);
-  } else {
-    g_object_set (trans, "start", compared_start, "duration",
-        start + duration - compared_start, NULL);
-  }
-
-clean:
-  g_object_unref (layer);
-}
-
-static void
-calculate_next_transition_with_list (GESTrackObject * track_object,
-    GList * tckobjs_in_layer, GESTimelineLayer * layer)
-{
-  GList *compared;
-
-  if (!(compared = g_list_find (tckobjs_in_layer, track_object)))
-    return;
-
-  if (compared == NULL)
-    /* This is the last TrackObject of the Track */
-    return;
-
-  do {
-    compared = compared->next;
-    if (compared == NULL)
-      return;
-  } while (!GES_IS_TRACK_SOURCE (compared->data));
-
-  compare (compared, track_object, FALSE);
-}
-
-
-static void
-calculate_next_transition (GESTrackObject * track_object,
-    GESTimelineLayer * layer)
-{
-  GESTrack *track;
-  GList *tckobjs_in_layer;
-
-  if ((track = ges_track_object_get_track (track_object))) {
-    tckobjs_in_layer = track_get_by_layer (layer, track);
-    calculate_next_transition_with_list (track_object, tckobjs_in_layer, layer);
-
-    g_list_foreach (tckobjs_in_layer, (GFunc) g_object_unref, NULL);
-    g_list_free (tckobjs_in_layer);
-  }
-}
-
-static void
-calculate_transitions (GESTrackObject * track_object)
-{
-  GList *tckobjs_in_layer, *compared;
-  GESTimelineLayer *layer;
-  GESTimelineObject *tlobj;
-
-  GESTrack *track = ges_track_object_get_track (track_object);
-
-  if (track == NULL)
-    return;
-
-  tlobj = ges_track_object_get_timeline_object (track_object);
-  layer = ges_timeline_object_get_layer (tlobj);
-  tckobjs_in_layer = track_get_by_layer (layer, track);
-  if (!(compared = g_list_find (tckobjs_in_layer, track_object)))
-    return;
-  do {
-    compared = compared->prev;
-
-    if (compared == NULL) {
-      /* Nothing before, let's check after */
-      calculate_next_transition_with_list (track_object, tckobjs_in_layer,
-          layer);
-      goto done;
-
-    }
-  } while (!GES_IS_TRACK_SOURCE (compared->data));
-
-  compare (compared, track_object, TRUE);
-
-  calculate_next_transition_with_list (track_object, tckobjs_in_layer, layer);
-
-done:
-  g_list_foreach (tckobjs_in_layer, (GFunc) g_object_unref, NULL);
-  g_list_free (tckobjs_in_layer);
-}
-
-
-static void
-look_for_transition (GESTrackObject * track_object, GESTimelineLayer * layer)
-{
-  GESTrack *track;
-  GList *track_objects, *tmp, *cur;
-
-  track = ges_track_object_get_track (track_object);
-  track_objects = ges_track_get_objects (track);
-
-  cur = g_list_find (track_objects, track_object);
-
-  for (tmp = cur->next; tmp; tmp = tmp->next) {
-    if (GES_IS_TRACK_SOURCE (tmp->data)) {
-      break;
-    }
-    if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
-        || GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
-      ges_timeline_layer_remove_object (layer,
-          ges_track_object_get_timeline_object (tmp->data));
-    }
-  }
-
-  for (tmp = cur->prev; tmp; tmp = tmp->prev) {
-    if (GES_IS_TRACK_SOURCE (tmp->data)) {
-      break;
-    }
-    if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
-        || GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
-      ges_timeline_layer_remove_object (layer,
-          ges_track_object_get_timeline_object (tmp->data));
-    }
-  }
-  g_list_foreach (track_objects, (GFunc) g_object_unref, NULL);
-  g_list_free (track_objects);
-}
-
 /**
  * ges_timeline_layer_resync_priorities:
  * @layer: a #GESTimelineLayer
@@ -548,140 +234,6 @@ ges_timeline_layer_resync_priorities (GESTimelineLayer * layer)
   return TRUE;
 }
 
-/* Callbacks */
-
-static void
-track_object_duration_cb (GESTrackObject * track_object,
-    GParamSpec * arg G_GNUC_UNUSED)
-{
-  GESTimelineLayer *layer;
-  GESTimelineObject *tlobj;
-
-  tlobj = ges_track_object_get_timeline_object (track_object);
-  layer = ges_timeline_object_get_layer (tlobj);
-  if (G_LIKELY (GES_IS_TRACK_SOURCE (track_object)))
-    calculate_next_transition (track_object, layer);
-}
-
-static void
-track_object_removed_cb (GESTrack * track, GESTrackObject * track_object)
-{
-  GList *track_objects, *tmp, *cur;
-  GESTimelineLayer *layer;
-
-  track_objects = ges_track_get_objects (track);
-  cur = g_list_find (track_objects, track_object);
-  for (tmp = cur->next; tmp; tmp = tmp->next) {
-    if (GES_IS_TRACK_SOURCE (tmp->data)) {
-      break;
-    }
-    if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
-        || GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
-      layer =
-          ges_timeline_object_get_layer (ges_track_object_get_timeline_object
-          (tmp->data));
-      if (ges_timeline_layer_get_auto_transition (layer)) {
-        ges_track_enable_update (track, FALSE);
-        ges_timeline_layer_remove_object (layer,
-            ges_track_object_get_timeline_object (tmp->data));
-        ges_track_enable_update (track, TRUE);
-      }
-      g_object_unref (layer);
-    }
-  }
-
-  for (tmp = cur->prev; tmp; tmp = tmp->prev) {
-    if (GES_IS_TRACK_SOURCE (tmp->data)) {
-      break;
-    }
-    if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
-        || GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
-      layer =
-          ges_timeline_object_get_layer (ges_track_object_get_timeline_object
-          (tmp->data));
-      if (ges_timeline_layer_get_auto_transition (layer)) {
-        ges_track_enable_update (track, FALSE);
-        ges_timeline_layer_remove_object (layer,
-            ges_track_object_get_timeline_object (tmp->data));
-        ges_track_enable_update (track, TRUE);
-      }
-      g_object_unref (layer);
-    }
-  }
-  g_object_unref (track_object);
-}
-
-static void
-track_object_changed_cb (GESTrackObject * track_object,
-    GParamSpec * arg G_GNUC_UNUSED)
-{
-  if (G_LIKELY (GES_IS_TRACK_SOURCE (track_object)))
-    calculate_transitions (track_object);
-}
-
-static void
-track_object_added_cb (GESTrack * track, GESTrackObject * track_object,
-    GESTimelineLayer * layer)
-{
-  if (GES_IS_TRACK_SOURCE (track_object)) {
-    g_signal_connect (G_OBJECT (track_object), "notify::start",
-        G_CALLBACK (track_object_changed_cb), NULL);
-    g_signal_connect (G_OBJECT (track_object), "notify::duration",
-        G_CALLBACK (track_object_duration_cb), NULL);
-    calculate_transitions (track_object);
-  }
-
-}
-
-static void
-track_removed_cb (GESTrack * track, GESTrackObject * track_object,
-    GESTimelineLayer * layer)
-{
-  g_signal_handlers_disconnect_by_func (track, track_object_added_cb, layer);
-  g_signal_handlers_disconnect_by_func (track, track_object_removed_cb, NULL);
-}
-
-static void
-track_added_cb (GESTimeline * timeline, GESTrack * track,
-    GESTimelineLayer * layer)
-{
-  g_signal_connect (track, "track-object-removed",
-      (GCallback) track_object_removed_cb, NULL);
-  g_signal_connect (track, "track-object-added",
-      (GCallback) track_object_added_cb, NULL);
-}
-
-static void
-timeline_object_height_changed_cb (GESTimelineObject * obj,
-    GESTrackEffect * tr_eff, GESTimelineObject * second_obj)
-{
-  gint priority, height;
-  g_object_get (obj, "height", &height, "priority", &priority, NULL);
-  g_object_set (second_obj, "priority", priority + height, NULL);
-}
-
-static void
-start_calculating_transitions (GESTimelineLayer * layer)
-{
-  GList *tmp, *tracks = ges_timeline_get_tracks (layer->timeline);
-
-  g_signal_connect (layer->timeline, "track-added", G_CALLBACK (track_added_cb),
-      layer);
-  g_signal_connect (layer->timeline, "track-removed",
-      G_CALLBACK (track_removed_cb), layer);
-
-  for (tmp = tracks; tmp; tmp = tmp->next) {
-    g_signal_connect (G_OBJECT (tmp->data), "track-object-added",
-        G_CALLBACK (track_object_added_cb), layer);
-    g_signal_connect (G_OBJECT (tmp->data), "track-object-removed",
-        G_CALLBACK (track_object_removed_cb), NULL);
-  }
-
-  g_list_free_full (tracks, g_object_unref);
-
-  /* FIXME calculate all the transitions at that time */
-}
-
 static void
 new_asset_cb (GESAsset * source, GAsyncResult * res, NewAssetUData * udata)
 {
@@ -741,7 +293,6 @@ ges_timeline_layer_remove_object (GESTimelineLayer * layer,
     GESTimelineObject * object)
 {
   GESTimelineLayer *current_layer;
-  GList *trackobjects, *tmp;
 
   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
@@ -759,17 +310,6 @@ ges_timeline_layer_remove_object (GESTimelineLayer * layer,
   }
   g_object_unref (current_layer);
 
-  if (layer->priv->auto_transition && GES_IS_TIMELINE_SOURCE (object)) {
-    trackobjects = ges_timeline_object_get_track_objects (object);
-
-    for (tmp = trackobjects; tmp; tmp = tmp->next) {
-      look_for_transition (tmp->data, layer);
-    }
-
-    g_list_foreach (trackobjects, (GFunc) g_object_unref, NULL);
-    g_list_free (trackobjects);
-  }
-
   /* emit 'object-removed' */
   g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_REMOVED], 0, object);
 
@@ -842,9 +382,6 @@ ges_timeline_layer_set_auto_transition (GESTimelineLayer * layer,
 
   g_return_if_fail (GES_IS_TIMELINE_LAYER (layer));
 
-  if (auto_transition && layer->timeline)
-    start_calculating_transitions (layer);
-
   layer->priv->auto_transition = auto_transition;
   g_object_notify (G_OBJECT (layer), "auto-transition");
 }
@@ -946,7 +483,6 @@ ges_timeline_layer_add_object (GESTimelineLayer * layer,
 
   priv = layer->priv;
   current_layer = ges_timeline_object_get_layer (object);
-
   if (G_UNLIKELY (current_layer)) {
     GST_WARNING ("TimelineObject %p already belongs to another layer", object);
     g_object_unref (current_layer);
@@ -1107,18 +643,5 @@ ges_timeline_layer_set_timeline (GESTimelineLayer * layer,
 {
   GST_DEBUG ("layer:%p, timeline:%p", layer, timeline);
 
-  if (layer->priv->auto_transition == TRUE) {
-    if (layer->timeline != NULL) {
-      g_signal_handlers_disconnect_by_func (layer->timeline, track_added_cb,
-          layer);
-      g_signal_handlers_disconnect_by_func (layer->timeline, track_removed_cb,
-          layer);
-    }
-
-    layer->timeline = timeline;
-    if (timeline != NULL)
-      start_calculating_transitions (layer);
-
-  } else
-    layer->timeline = timeline;
+  layer->timeline = timeline;
 }
index 758e5d2..b8088ba 100644 (file)
@@ -42,6 +42,7 @@
 #include "ges-timeline.h"
 #include "ges-track.h"
 #include "ges-timeline-layer.h"
+#include "ges-auto-transition.h"
 #include "ges.h"
 
 typedef struct _MoveContext MoveContext;
@@ -143,8 +144,17 @@ struct _GESTimelinePrivate
    * probably through a ges_timeline_layer_get_track_objects () method */
   GHashTable *by_layer;         /* {layer: GSequence of TrackObject by start/priorities} */
 
+  /* The set of auto_transitions we control, currently the key is
+   * pointerToPreviousiTrackObjAdresspointerToNextTrackObjAdress as a string,
+   * ... not really optimal but it works */
+  GHashTable *auto_transitions;
+
   MoveContext movecontext;
 
+  /* This variable is set to %TRUE when it makes sense to update the transitions,
+   * and %FALSE otherwize */
+  gboolean needs_transitions_update;
+
   gboolean updates_enabled;
 };
 
@@ -292,12 +302,15 @@ ges_timeline_dispose (GObject * object)
   g_hash_table_unref (priv->by_start);
   g_hash_table_unref (priv->by_end);
   g_hash_table_unref (priv->by_object);
+  g_hash_table_unref (priv->by_layer);
   g_hash_table_unref (priv->obj_iters);
   g_sequence_free (priv->starts_ends);
   g_sequence_free (priv->tracksources);
   g_list_free (priv->movecontext.moving_tckobjs);
   g_hash_table_unref (priv->movecontext.moving_tlobjs);
 
+  g_hash_table_unref (priv->auto_transitions);
+
   G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
 }
 
@@ -513,6 +526,12 @@ ges_timeline_init (GESTimeline * self)
   priv->starts_ends = g_sequence_new (g_free);
   priv->tracksources = g_sequence_new (g_object_unref);
 
+  priv->auto_transitions =
+      g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gst_object_unref);
+  priv->needs_transitions_update = TRUE;
+
+  priv->updates_enabled = TRUE;
+
   g_signal_connect_after (self, "select-tracks-for-object",
       G_CALLBACK (select_tracks_for_object_default), NULL);
 }
@@ -629,6 +648,227 @@ sort_starts_ends_start (GESTimeline * timeline, TrackObjIters * iters)
   timeline_update_duration (timeline);
 }
 
+static void
+_destroy_auto_transition_cb (GESAutoTransition * auto_transition,
+    GESTimeline * timeline)
+{
+  GESTimelinePrivate *priv = timeline->priv;
+  GESTimelineObject *transition = auto_transition->timeline_transition;
+  GESTimelineLayer *layer = ges_timeline_object_get_layer (transition);
+
+  ges_timeline_layer_remove_object (layer, transition);
+  g_signal_handlers_disconnect_by_func (auto_transition,
+      _destroy_auto_transition_cb, timeline);
+
+  if (!g_hash_table_remove (priv->auto_transitions, auto_transition->key))
+    GST_WARNING_OBJECT (timeline, "Could not remove auto_transition %"
+        GST_PTR_FORMAT, auto_transition->key);
+}
+
+static GESAutoTransition *
+create_transition (GESTimeline * timeline, GESTrackObject * previous,
+    GESTrackObject * next, GESTimelineObject * transition,
+    GESTimelineLayer * layer, guint64 start, guint64 duration)
+{
+  GList *tckobjs;
+  GESAsset *asset;
+  GESAutoTransition *auto_transition;
+
+  if (transition == NULL) {
+    /* TODO make it possible to specify a Transition asset in the API */
+    asset = ges_asset_request (GES_TYPE_TIMELINE_STANDARD_TRANSITION,
+        "crossfade", NULL);
+    transition =
+        ges_timeline_layer_add_asset (layer, asset, start, 0, duration, 1,
+        ges_track_object_get_track_type (next));
+  } else {
+    GST_DEBUG_OBJECT (timeline,
+        "Reusing already existing transition: %" GST_PTR_FORMAT, transition);
+  }
+
+  /* We know there is only 1 TrackObject */
+  tckobjs = ges_timeline_object_get_track_objects (transition);
+  auto_transition = ges_auto_transition_new (tckobjs->data, previous, next);
+  g_list_free_full (tckobjs, gst_object_unref);
+
+  g_signal_connect (auto_transition, "destroy-me",
+      G_CALLBACK (_destroy_auto_transition_cb), timeline);
+
+  g_hash_table_insert (timeline->priv->auto_transitions,
+      auto_transition->key, auto_transition);
+
+  return auto_transition;
+}
+
+typedef GESAutoTransition *(*GetAutoTransitionFunc) (GESTimeline * timeline,
+    GESTimelineLayer * layer, GESTrack * track, GESTrackObject * previous,
+    GESTrackObject * next, GstClockTime transition_duration);
+
+static GESAutoTransition *
+_find_transition_from_auto_transitions (GESTimeline * timeline,
+    GESTimelineLayer * layer, GESTrack * track, GESTrackObject * prev,
+    GESTrackObject * next, GstClockTime transition_duration)
+{
+  GESAutoTransition *auto_transition;
+
+  gchar *key = g_strdup_printf ("%p%p", prev, next);
+
+  auto_transition = g_hash_table_lookup (timeline->priv->auto_transitions, key);
+  g_free (key);
+
+  return auto_transition;
+}
+
+static GESAutoTransition *
+_create_auto_transition_from_transitions (GESTimeline * timeline,
+    GESTimelineLayer * layer, GESTrack * track, GESTrackObject * prev,
+    GESTrackObject * next, GstClockTime transition_duration)
+{
+  GSequenceIter *tmp_iter;
+  GSequence *by_layer_sequence;
+
+  GESTimelinePrivate *priv = timeline->priv;
+  GESAutoTransition *auto_transition =
+      _find_transition_from_auto_transitions (timeline, layer, track, prev,
+      next, transition_duration);
+
+  if (auto_transition)
+    return auto_transition;
+
+
+  /* Try to find a transition that perfectly fits with the one that
+   * should be added at that place
+   * optimize: Use g_sequence_search instead of going over all the
+   * sequence */
+  by_layer_sequence = g_hash_table_lookup (priv->by_layer, layer);
+  for (tmp_iter = g_sequence_get_begin_iter (by_layer_sequence);
+      tmp_iter && !g_sequence_iter_is_end (tmp_iter);
+      tmp_iter = g_sequence_iter_next (tmp_iter)) {
+    GESTrackObject *maybe_transition = g_sequence_get (tmp_iter);
+
+    if (ges_track_object_get_track (maybe_transition) != track)
+      continue;
+
+    if (maybe_transition->start > next->start)
+      break;
+    else if (maybe_transition->start != next->start ||
+        maybe_transition->duration != transition_duration)
+      continue;
+    else if (GES_IS_TRACK_TRANSITION (maybe_transition))
+      /* Use that transition */
+      /* TODO We should make sure that the transition contains only
+       * TrackObject-s in @track and if it is not the case properly unlink the
+       * object to use it */
+      return create_transition (timeline, prev, next,
+          ges_track_object_get_timeline_object (maybe_transition), layer,
+          next->start, transition_duration);
+  }
+
+  return NULL;
+}
+
+/* Create all transition that do not exist on @layer.
+ * @get_auto_transition is called to check if a particular transition exists
+ * if @ track is specified, we will create the transitions only for that particular
+ * track */
+static void
+_create_transitions_on_layer (GESTimeline * timeline, GESTimelineLayer * layer,
+    GESTrack * track, GESTrackObject * initiating_obj,
+    GetAutoTransitionFunc get_auto_transition)
+{
+  guint32 layer_prio;
+  GSequenceIter *iter;
+  GESAutoTransition *transition;
+
+  GESTrack *ctrack = track;
+  GList *entered = NULL;        /* List of TrackObject for wich we walk through the
+                                 * "start" but not the "end" in the starts_ends list */
+  GESTimelinePrivate *priv = timeline->priv;
+
+  if (!layer || !ges_timeline_layer_get_auto_transition (layer))
+    return;
+
+  layer_prio = ges_timeline_layer_get_priority (layer);
+  for (iter = g_sequence_get_begin_iter (priv->starts_ends);
+      iter && !g_sequence_iter_is_end (iter);
+      iter = g_sequence_iter_next (iter)) {
+    GList *tmp;
+    guint *start_or_end = g_sequence_get (iter);
+    GESTrackObject *next = g_hash_table_lookup (timeline->priv->by_object,
+        start_or_end);
+
+    /* Only object that are in that layer and track */
+    if ((next->priority / LAYER_HEIGHT) != layer_prio ||
+        (track && track != ges_track_object_get_track (next)))
+      continue;
+
+    if (track == NULL)
+      ctrack = ges_track_object_get_track (next);
+
+    if (start_or_end == g_hash_table_lookup (priv->by_end, next)) {
+      if (initiating_obj == next) {
+        /* We passed the objects that initiated the research
+         * we are now done */
+        g_list_free (entered);
+        return;
+      }
+      entered = g_list_remove (entered, next);
+
+      continue;
+    }
+
+    for (tmp = entered; tmp; tmp = tmp->next) {
+      gint64 transition_duration;
+
+      GESTrackObject *prev = tmp->data;
+
+      if (ctrack != ges_track_object_get_track (prev))
+        continue;
+
+      transition_duration = (prev->start + prev->duration) - next->start;
+      if (transition_duration > 0 && transition_duration < prev->duration &&
+          transition_duration < next->duration) {
+        transition =
+            get_auto_transition (timeline, layer, ctrack, prev, next,
+            transition_duration);
+        if (!transition)
+          transition = create_transition (timeline, prev, next, NULL, layer,
+              next->start, transition_duration);
+      }
+    }
+
+    /* And add that object to the entered list so that it we can possibly set
+     * a transition on its end edge */
+    entered = g_list_append (entered, next);
+  }
+}
+
+/* @tck_obj must be a GESTrackSource */
+static void
+create_transitions (GESTimeline * timeline, GESTrackObject * tck_obj)
+{
+  GESTrack *track;
+  GList *layer_node;
+
+  GESTimelinePrivate *priv = timeline->priv;
+
+  if (!priv->needs_transitions_update || !priv->updates_enabled)
+    return;
+
+  GST_DEBUG_OBJECT (timeline, "Creating transitions around %p", tck_obj);
+
+  track = ges_track_object_get_track (tck_obj);
+  layer_node = g_list_find_custom (timeline->layers,
+      GINT_TO_POINTER (tck_obj->priority / LAYER_HEIGHT),
+      (GCompareFunc) find_layer_by_prio);
+
+  _create_transitions_on_layer (timeline,
+      layer_node ? layer_node->data : NULL, track, tck_obj,
+      _find_transition_from_auto_transitions);
+
+  GST_DEBUG_OBJECT (timeline, "Done updating transitions");
+}
+
 /* Timeline edition functions */
 static inline void
 init_movecontext (MoveContext * mv_ctx, gboolean first_init)
@@ -744,6 +984,7 @@ start_tracking_track_object (GESTimeline * timeline, GESTrackObject * tckobj)
     timeline->priv->movecontext.needs_move_ctx = TRUE;
 
     timeline_update_duration (timeline);
+    create_transitions (timeline, tckobj);
   }
 }
 
@@ -1084,9 +1325,12 @@ ges_timeline_trim_object_simple (GESTimeline * timeline, GESTrackObject * obj,
       duration = MAX (0, real_dur);
       duration = MIN (duration, max_duration - obj->inpoint);
 
+      timeline->priv->needs_transitions_update = FALSE;
       ges_track_object_set_start (obj, nstart);
-      ges_track_object_set_duration (obj, duration);
       ges_track_object_set_inpoint (obj, inpoint);
+      timeline->priv->needs_transitions_update = TRUE;
+
+      ges_track_object_set_duration (obj, duration);
       break;
     case GES_EDGE_END:
     {
@@ -1133,6 +1377,7 @@ timeline_ripple_object (GESTimeline * timeline, GESTrackObject * obj,
     case GES_EDGE_NONE:
       GST_DEBUG ("Simply rippling");
 
+      /* We should be smart here to avoid recalculate transitions when possible */
       cur = g_hash_table_lookup (timeline->priv->by_end, obj);
       snapped = ges_timeline_snap_position (timeline, obj, cur, position, TRUE);
       if (snapped)
@@ -1163,6 +1408,7 @@ timeline_ripple_object (GESTimeline * timeline, GESTrackObject * obj,
 
       break;
     case GES_EDGE_END:
+      timeline->priv->needs_transitions_update = FALSE;
       GST_DEBUG ("Rippling end");
 
       cur = g_hash_table_lookup (timeline->priv->by_end, obj);
@@ -1195,6 +1441,7 @@ timeline_ripple_object (GESTimeline * timeline, GESTrackObject * obj,
       }
 
       g_list_free (moved_tlobjs);
+      timeline->priv->needs_transitions_update = TRUE;
       GST_DEBUG ("Done Rippling end");
       break;
     case GES_EDGE_START:
@@ -1273,6 +1520,7 @@ timeline_roll_object (GESTimeline * timeline, GESTrackObject * obj,
   duration = ges_track_object_get_duration (obj);
   end = start + duration;
 
+  timeline->priv->needs_transitions_update = FALSE;
   switch (edge) {
     case GES_EDGE_START:
 
@@ -1588,6 +1836,15 @@ add_object_to_tracks (GESTimeline * timeline, GESTimelineObject * object,
 }
 
 static void
+layer_auto_transition_changed_cb (GESTimelineLayer * layer,
+    GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
+{
+  _create_transitions_on_layer (timeline, layer, NULL, NULL,
+      _create_auto_transition_from_transitions);
+
+}
+
+static void
 layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
     GESTimeline * timeline)
 {
@@ -1595,6 +1852,10 @@ layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
     GST_DEBUG ("TimelineObject %p is moving from a layer to another, not doing"
         " anything on it", object);
     timeline->priv->movecontext.needs_move_ctx = TRUE;
+
+    _create_transitions_on_layer (timeline, layer, NULL, NULL,
+        _find_transition_from_auto_transitions);
+
     return;
   }
 
@@ -1637,8 +1898,8 @@ layer_object_removed_cb (GESTimelineLayer * layer, GESTimelineObject * object,
                 ges_track_object_get_track (trobj),
                 (GCompareFunc) custom_find_track))) {
       GST_DEBUG ("Belongs to one of the tracks we control");
-      ges_track_remove_object (ges_track_object_get_track (trobj), trobj);
 
+      ges_track_remove_object (ges_track_object_get_track (trobj), trobj);
       ges_timeline_object_release_track_object (object, trobj);
     }
     /* removing the reference added by _get_track_objects() */
@@ -1674,6 +1935,7 @@ trackobj_start_changed_cb (GESTrackObject * child,
         timeline->priv->snapping_distance == 0)
       timeline->priv->movecontext.needs_move_ctx = TRUE;
 
+    create_transitions (timeline, child);
   }
 }
 
@@ -1736,6 +1998,8 @@ trackobj_duration_changed_cb (GESTrackObject * child,
         timeline->priv->snapping_distance == 0) {
       timeline->priv->movecontext.needs_move_ctx = TRUE;
     }
+
+    create_transitions (timeline, child);
   }
 }
 
@@ -1760,16 +2024,18 @@ track_object_removed_cb (GESTrack * track, GESTrackObject * object,
 {
 
   if (GES_IS_TRACK_SOURCE (object)) {
-
     /* Make sure to reinitialise the moving context next time */
     timeline->priv->movecontext.needs_move_ctx = TRUE;
   }
 
   /* Disconnect all signal handlers */
+  g_signal_handlers_disconnect_by_func (object, trackobj_start_changed_cb,
+      NULL);
   g_signal_handlers_disconnect_by_func (object, trackobj_duration_changed_cb,
       NULL);
   g_signal_handlers_disconnect_by_func (object, trackobj_priority_changed_cb,
       NULL);
+
   stop_tracking_track_object (timeline, object);
 }
 
@@ -2014,6 +2280,8 @@ ges_timeline_add_layer (GESTimeline * timeline, GESTimelineLayer * layer)
       G_CALLBACK (layer_object_removed_cb), timeline);
   g_signal_connect (layer, "notify::priority",
       G_CALLBACK (layer_priority_changed_cb), timeline);
+  g_signal_connect (layer, "notify::auto-transition",
+      G_CALLBACK (layer_auto_transition_changed_cb), timeline);
 
   GST_DEBUG ("Done adding layer, emitting 'layer-added' signal");
   g_signal_emit (timeline, ges_timeline_signals[LAYER_ADDED], 0, layer);
@@ -2069,10 +2337,13 @@ ges_timeline_remove_layer (GESTimeline * timeline, GESTimelineLayer * layer)
   g_signal_handlers_disconnect_by_func (layer, layer_object_added_cb, timeline);
   g_signal_handlers_disconnect_by_func (layer, layer_object_removed_cb,
       timeline);
+  g_signal_handlers_disconnect_by_func (layer, layer_priority_changed_cb,
+      timeline);
+  g_signal_handlers_disconnect_by_func (layer,
+      layer_auto_transition_changed_cb, timeline);
 
   g_hash_table_remove (timeline->priv->by_layer, layer);
   timeline->layers = g_list_remove (timeline->layers, layer);
-
   ges_timeline_layer_set_timeline (layer, NULL);
 
   g_signal_emit (timeline, ges_timeline_signals[LAYER_REMOVED], 0, layer);
@@ -2358,6 +2629,12 @@ ges_timeline_enable_update (GESTimeline * timeline, gboolean enabled)
   /* Make sure we reset the context */
   timeline->priv->movecontext.needs_move_ctx = TRUE;
   timeline->priv->updates_enabled = enabled;
+
+  for (tmp = timeline->layers; tmp; tmp = tmp->next) {
+    _create_transitions_on_layer (timeline, GES_TIMELINE_LAYER (tmp->data),
+        NULL, NULL, _find_transition_from_auto_transitions);
+  }
+
   if (res)
     g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_UPDATE]);
 
index 66f4040..12f602d 100644 (file)
@@ -282,40 +282,1120 @@ GST_START_TEST (test_layer_priorities)
 
 GST_END_TEST;
 
-GST_START_TEST (test_layer_automatic_transition)
+GST_START_TEST (test_single_layer_automatic_transition)
 {
+  GESAsset *asset;
   GESTimeline *timeline;
+  GList *objects, *current;
+  GESTimelineObject *transition;
   GESTimelineLayer *layer;
-  GESTimelineTestSource *src, *srcbis;
-  GList *objects = NULL, *tmp = NULL;
-  gboolean res = FALSE;
+  GESTimelineObject *src, *src1, *src2;
 
   ges_init ();
 
+  asset = ges_asset_request (GES_TYPE_TIMELINE_TEST_SOURCE, NULL, NULL);
+  fail_unless (GES_IS_ASSET (asset));
+
+  GST_DEBUG ("Create timeline");
   timeline = ges_timeline_new_audio_video ();
+  assert_is_type (timeline, GES_TYPE_TIMELINE);
+
+  GST_DEBUG ("Create first layer");
   layer = ges_timeline_layer_new ();
-  ges_timeline_add_layer (timeline, layer);
+  assert_is_type (layer, GES_TYPE_TIMELINE_LAYER);
 
-  g_object_set (layer, "auto-transition", TRUE, NULL);
-  src = ges_timeline_test_source_new ();
-  srcbis = ges_timeline_test_source_new ();
+  GST_DEBUG ("Add first layer to timeline");
+  fail_unless (ges_timeline_add_layer (timeline, layer));
 
-  g_object_set (srcbis, "start", (gint64) 5000, "duration", (gint64) 10000LL,
-      NULL);
-  g_object_set (src, "start", (gint64) 0, "duration", (gint64) 10000LL, NULL);
+  GST_DEBUG ("Set auto transition to first layer");
+  ges_timeline_layer_set_auto_transition (layer, TRUE);
+
+  GST_DEBUG ("Check that auto-transition was properly set");
+  fail_unless (ges_timeline_layer_get_auto_transition (layer));
+
+  GST_DEBUG ("Adding assets to first layer");
+  GST_DEBUG ("Adding object from 0 -- 1000 to first layer");
+  src = GES_TIMELINE_OBJECT (ges_timeline_layer_add_asset (layer, asset, 0, 0,
+          1000, 1, GES_TRACK_TYPE_UNKNOWN));
+  fail_unless (GES_IS_TIMELINE_OBJECT (src));
+
+  GST_DEBUG ("Adding object from 500 -- 1000 to first layer");
+  src1 = GES_TIMELINE_OBJECT (ges_timeline_layer_add_asset (layer, asset, 500,
+          0, 1000, 1, GES_TRACK_TYPE_UNKNOWN));
+  fail_unless (GES_IS_TIMELINE_OBJECT (src1));
+
+  /*
+   *        500__transition__1000
+   * 0___________src_________1000
+   *        500___________src1_________1500
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 0);
+  assert_equals_uint64 (src->duration, 1000);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1500 - 500);
+
+  GST_DEBUG ("Checking that a transition has been added");
+  objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 4);
+  assert_is_type (objects->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+
+  transition = objects->next->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  transition = objects->next->next->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Moving first source to 250");
+  ges_timeline_object_set_start (src, 250);
+
+  /*
+   *        500_____transition____1250
+   *    250___________src_________1250
+   *        500___________src1_________1500
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 250);
+  assert_equals_uint64 (src->duration, 1250 - 250);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1500 - 500);
 
-  ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (src));
-  ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (srcbis));
+  objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 4);
+  assert_is_type (objects->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+
+  transition = objects->next->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 750);
+
+  transition = objects->next->next->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_int (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 750);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG ("Moving second source to 250, the transitions should be removed");
+  ges_timeline_object_set_start (src1, 250);
+
+  /* The transition should be removed
+   *    250___________src_________1250
+   *    250___________src1________1250
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 250);
+  assert_equals_uint64 (src->duration, 1250 - 250);
+  assert_equals_uint64 (src1->start, 250);
+  assert_equals_uint64 (src1->duration, 1250 - 250);
 
   objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 2);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG ("Trimming second source to 500 no transition should be created "
+      "as they have the same end");
+  ges_timeline_object_edit (src1, NULL, -1, GES_EDIT_MODE_TRIM, GES_EDGE_START,
+      500);
+
+  /*    250___________src_________1250
+   *          500______src1_______1250
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 250);
+  assert_equals_uint64 (src->duration, 1250 - 250);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1250 - 500);
 
-  for (tmp = objects; tmp; tmp = tmp->next) {
-    if (GES_IS_TIMELINE_STANDARD_TRANSITION (tmp->data)) {
-      res = TRUE;
-    }
-  }
+  objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 2);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG ("Trimming second source to 500, no transition should be created");
+  ges_timeline_object_trim_start (src, 500);
+
+  /*        500___________src_________1250
+   *        500___________src1________1250
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 500);
+  assert_equals_uint64 (src->duration, 1250 - 500);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1250 - 500);
+
+  GST_DEBUG ("Trimming first source to 750, no transition should be created");
+  ges_timeline_object_trim_start (src, 750);
+
+  /*              750_______src_______1250
+   *        500___________src1________1250
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 750);
+  assert_equals_uint64 (src->duration, 1250 - 750);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1250 - 500);
+
+  objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 2);
+  g_list_free_full (objects, gst_object_unref);
+
+  objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 2);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG ("Moving first source to 500, no transition should be created");
+  ges_timeline_object_set_start (src, 500);
+
+  /*        500________src______1000
+   *        500___________src1________1250
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 500);
+  assert_equals_uint64 (src->duration, 1000 - 500);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1250 - 500);
+
+  objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 2);
+  g_list_free_full (objects, gst_object_unref);
+
+  objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 2);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG ("Moving first source to 600, no transition should be created");
+  ges_timeline_object_set_start (src, 600);
+  /*             600____src___1100
+   *        500___________src1________1250
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 600);
+  assert_equals_uint64 (src->duration, 1100 - 600);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1250 - 500);
+
+  objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 2);
+  g_list_free_full (objects, gst_object_unref);
+
+  objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 2);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG ("Adding asset to first layer");
+  GST_DEBUG ("Adding object from 1250 -- 1000 to first layer");
+  src2 =
+      GES_TIMELINE_OBJECT (ges_timeline_layer_add_asset (layer, asset, 1250, 0,
+          1000, 1, GES_TRACK_TYPE_UNKNOWN));
+  assert_is_type (src2, GES_TYPE_TIMELINE_TEST_SOURCE);
+
+  /*             600____src___1100
+   *        500___________src1________1250
+   *                                  1250___________src2________2250
+   */
+  assert_equals_uint64 (src->start, 600);
+  assert_equals_uint64 (src->duration, 1100 - 600);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1250 - 500);
+  assert_equals_uint64 (src2->start, 1250);
+  assert_equals_uint64 (src2->duration, 1000);
+
+  objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 3);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG
+      ("Changig first source duration to 800 2 transitions should be created");
+  ges_timeline_object_set_duration (src, 800);
+  /*             600__________________src_____________1400
+   *        500___________src1________1250
+   *                                  1250___________src2________2250
+   *             600_____trans1_______1250
+   *                                  1250___trans2___1400
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 600);
+  assert_equals_uint64 (src->duration, 1400 - 600);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1250 - 500);
+
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 7);
+  assert_is_type (objects->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+  fail_unless (objects->data == src1);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 600);
+  assert_equals_uint64 (transition->duration, 1250 - 600);
+  ASSERT_OBJECT_REFCOUNT (transition, "The layer and ourself own a ref", 2);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 600);
+  assert_equals_uint64 (transition->duration, 1250 - 600);
+  ASSERT_OBJECT_REFCOUNT (transition, "The layer and ourself own a ref", 2);
+
+  current = current->next;
+  fail_unless (current->data == src);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1250);
+  assert_equals_uint64 (transition->duration, 1400 - 1250);
+  ASSERT_OBJECT_REFCOUNT (transition, "The layer and ourself own a ref", 2);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1250);
+  assert_equals_uint64 (transition->duration, 1400 - 1250);
+  ASSERT_OBJECT_REFCOUNT (transition, "The layer and ourself own a ref", 2);
+
+  current = current->next;
+  fail_unless (current->data == src2);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG ("Back to previous state");
+  ges_timeline_object_set_duration (src, 1100 - 600);
+  /*             600____src___1100
+   *        500___________src1________1250
+   *                                  1250___________src2________2250
+   */
+  assert_equals_uint64 (src->start, 600);
+  assert_equals_uint64 (src->duration, 1100 - 600);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1250 - 500);
+  assert_equals_uint64 (src2->start, 1250);
+  assert_equals_uint64 (src2->duration, 1000);
+
+  /* We check that the transition as actually been freed */
+  fail_if (GES_IS_TIMELINE_STANDARD_TRANSITION (transition));
+
+  objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 3);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG
+      ("Set third object start to 1100, 1 new transition should be created");
+  ges_timeline_object_set_start (src2, 1100);
+  /*             600____src___1100
+   *        500___________src1________1250
+   *                          1100___________src2________2100
+   *                          ^__trans___^
+   */
+  assert_equals_uint64 (src->start, 600);
+  assert_equals_uint64 (src->duration, 1100 - 600);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1250 - 500);
+  assert_equals_uint64 (src2->start, 1100);
+  assert_equals_uint64 (src2->duration, 1000);
+
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 5);
+  assert_is_type (objects->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+  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_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1100);
+  assert_equals_uint64 (transition->duration, 1250 - 1100);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1100);
+  assert_equals_uint64 (transition->duration, 1250 - 1100);
+
+  current = current->next;
+  fail_unless (current->data == src2);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG ("Set third object start to 1000, Transition should be updated");
+  ges_timeline_object_edit (src2, NULL, -1, GES_EDIT_MODE_NORMAL,
+      GES_EDGE_START, 1000);
+  /*             600____src___1100
+   *                       !_tr__^
+   *        500___________src1________1250
+   *                       1000___________src2________2000
+   *                       ^____trans____^
+   */
+  assert_equals_uint64 (src->start, 600);
+  assert_equals_uint64 (src->duration, 500);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1250 - 500);
+  assert_equals_uint64 (src2->start, 1000);
+  assert_equals_uint64 (src2->duration, 1000);
+
+  current = objects = ges_timeline_layer_get_objects (layer);
+  current = objects;
+  assert_equals_int (g_list_length (objects), 7);
+  assert_is_type (objects->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+  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_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 1100 - 1000);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 1100 - 1000);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 1250 - 1000);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 1250 - 1000);
+
+  current = current->next;
+  fail_unless (current->data == src2);
+  g_list_free_full (objects, gst_object_unref);
+
+  g_object_unref (timeline);
+}
 
-  fail_unless (res == TRUE);
+GST_END_TEST;
+
+GST_START_TEST (test_multi_layer_automatic_transition)
+{
+  GESAsset *asset;
+  GESTimeline *timeline;
+  GList *objects, *current;
+  GESTimelineObject *transition;
+  GESTimelineLayer *layer, *layer1;
+  GESTimelineObject *src, *src1, *src2, *src3;
+
+  ges_init ();
+
+  asset = ges_asset_request (GES_TYPE_TIMELINE_TEST_SOURCE, NULL, NULL);
+  fail_unless (GES_IS_ASSET (asset));
+
+  GST_DEBUG ("Create timeline");
+  timeline = ges_timeline_new_audio_video ();
+  assert_is_type (timeline, GES_TYPE_TIMELINE);
+
+  GST_DEBUG ("Create first layer");
+  layer = ges_timeline_layer_new ();
+  assert_is_type (layer, GES_TYPE_TIMELINE_LAYER);
+
+  GST_DEBUG ("Add first layer to timeline");
+  fail_unless (ges_timeline_add_layer (timeline, layer));
+
+  GST_DEBUG ("Append a new layer to the timeline");
+  layer1 = ges_timeline_append_layer (timeline);
+  assert_is_type (layer1, GES_TYPE_TIMELINE_LAYER);
+
+  GST_DEBUG ("Set auto transition to first layer");
+  ges_timeline_layer_set_auto_transition (layer, TRUE);
+
+  GST_DEBUG ("Check that auto-transition was properly set");
+  fail_unless (ges_timeline_layer_get_auto_transition (layer));
+  fail_if (ges_timeline_layer_get_auto_transition (layer1));
+
+  GST_DEBUG ("Adding assets to first layer");
+  GST_DEBUG ("Adding object from 0 -- 1000 to first layer");
+  src = GES_TIMELINE_OBJECT (ges_timeline_layer_add_asset (layer, asset, 0, 0,
+          1000, 1, GES_TRACK_TYPE_UNKNOWN));
+  fail_unless (GES_IS_TIMELINE_OBJECT (src));
+
+  GST_DEBUG ("Adding object from 500 -- 1000 to first layer");
+  src1 = GES_TIMELINE_OBJECT (ges_timeline_layer_add_asset (layer, asset, 500,
+          0, 1000, 1, GES_TRACK_TYPE_UNKNOWN));
+  fail_unless (GES_IS_TIMELINE_OBJECT (src1));
+
+  /*
+   *        500__transition__1000
+   * 0___________src_________1000
+   *        500___________src1_________1500
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 0);
+  assert_equals_uint64 (src->duration, 1000);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1500 - 500);
+
+  GST_DEBUG ("Checking that a transition has been added");
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 4);
+  assert_is_type (current->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Adding object 2 from 500 -- 1000 to second layer");
+  src2 = GES_TIMELINE_OBJECT (ges_timeline_layer_add_asset (layer1, asset, 0,
+          0, 1000, 1, GES_TRACK_TYPE_UNKNOWN));
+  GST_DEBUG ("Adding object 3 from 500 -- 1000 to second layer");
+  src3 = GES_TIMELINE_OBJECT (ges_timeline_layer_add_asset (layer1, asset, 500,
+          0, 1000, 1, GES_TRACK_TYPE_UNKNOWN));
+  assert_is_type (src3, GES_TYPE_TIMELINE_TEST_SOURCE);
+
+  /*        500__transition__1000
+   * 0___________src_________1000
+   *        500___________src1_________1500
+   *----------------------------------------------------
+   * 0___________src2_________1000
+   *        500___________src3_________1500         Layer1
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 0);
+  assert_equals_uint64 (src->duration, 1000);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1500 - 500);
+  assert_equals_uint64 (src2->start, 0);
+  assert_equals_uint64 (src2->duration, 1000);
+  assert_equals_uint64 (src3->start, 500);
+  assert_equals_uint64 (src3->duration, 1500 - 500);
+
+  GST_DEBUG ("Checking transitions on first layer");
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 4);
+  assert_is_type (current->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Checking transitions on second layer");
+  current = objects = ges_timeline_layer_get_objects (layer1);
+  assert_equals_int (g_list_length (objects), 2);
+  fail_unless (current->data == src2);
+  fail_unless (current->next->data == src3);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG
+      ("Set auto transition to second layer, a new transition should be added");
+  ges_timeline_layer_set_auto_transition (layer1, TRUE);
+
+  /*        500__transition__1000
+   * 0___________src_________1000
+   *        500___________src1_________1500
+   *----------------------------------------------------
+   *        500__transition__1000
+   * 0__________src2_________1000
+   *        500___________src3_________1500         Layer1
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 0);
+  assert_equals_uint64 (src->duration, 1000);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1500 - 500);
+  assert_equals_uint64 (src2->start, 0);
+  assert_equals_uint64 (src2->duration, 1000);
+  assert_equals_uint64 (src3->start, 500);
+  assert_equals_uint64 (src3->duration, 1500 - 500);
+
+  GST_DEBUG ("Checking transitions on first layer");
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 4);
+  assert_is_type (current->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Checking transitions has been added on second layer");
+  current = objects = ges_timeline_layer_get_objects (layer1);
+  assert_equals_int (g_list_length (objects), 4);
+  assert_is_type (current->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Moving src3 to 1000. should remove transition");
+  ges_timeline_object_set_start (src3, 1000);
+
+  /*        500__transition__1000
+   * 0___________src_________1000
+   *        500___________src1_________1500                           Layer
+   *----------------------------------------------------
+   * 0__________src2_________1000
+   *                         1000___________src3_________2000         Layer1
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 0);
+  assert_equals_uint64 (src->duration, 1000);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1500 - 500);
+  assert_equals_uint64 (src2->start, 0);
+  assert_equals_uint64 (src2->duration, 1000);
+  assert_equals_uint64 (src3->start, 1000);
+  assert_equals_uint64 (src3->duration, 2000 - 1000);
+
+  GST_DEBUG ("Checking transitions on first layer");
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 4);
+  assert_is_type (current->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Checking transitions has been removed on second layer");
+  current = objects = ges_timeline_layer_get_objects (layer1);
+  assert_equals_int (g_list_length (objects), 2);
+  fail_unless (current->data == src2);
+  fail_unless (current->next->data == src3);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Moving src3 to first layer, should add a transition");
+  ges_timeline_object_move_to_layer (src3, layer);
+
+  /*        500__transition__1000
+   * 0___________src_________1000
+   *        500___________src1_________1500
+   *                         1000___________src3_________2000   Layer
+   *                         1000__tr__1500
+   *----------------------------------------------------
+   * 0__________src2_________1000                               Layer1
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 0);
+  assert_equals_uint64 (src->duration, 1000);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1500 - 500);
+  assert_equals_uint64 (src2->start, 0);
+  assert_equals_uint64 (src2->duration, 1000);
+  assert_equals_uint64 (src3->start, 1000);
+  assert_equals_uint64 (src3->duration, 2000 - 1000);
+
+  GST_DEBUG ("Checking transitions on first layer");
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 7);
+  fail_unless (current->data == src);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  fail_unless (current->data == src1);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 1500 - 1000);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 1500 - 1000);
+
+  current = current->next;
+  fail_unless (current->data == src3);
+
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Checking second layer");
+  current = objects = ges_timeline_layer_get_objects (layer1);
+  assert_equals_int (g_list_length (objects), 1);
+  fail_unless (current->data == src2);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG
+      ("Moving src to second layer, should remove first transition on first layer");
+  ges_timeline_object_move_to_layer (src, layer1);
+
+  /*        500___________src1_________1500
+   *                         1000___________src3_________2000   Layer
+   *                         1000__tr__1500
+   *----------------------------------------------------
+   * 0___________src_________1000
+   * 0__________src2_________1000                               Layer1
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 0);
+  assert_equals_uint64 (src->duration, 1000);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1500 - 500);
+  assert_equals_uint64 (src2->start, 0);
+  assert_equals_uint64 (src2->duration, 1000);
+  assert_equals_uint64 (src3->start, 1000);
+  assert_equals_uint64 (src3->duration, 2000 - 1000);
+
+  GST_DEBUG ("Checking transitions on first layer");
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 4);
+  fail_unless (current->data == src1);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  fail_unless (current->data == src3);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Checking second layer");
+  current = objects = ges_timeline_layer_get_objects (layer1);
+  assert_equals_int (g_list_length (objects), 2);
+  assert_is_type (current->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+  assert_is_type (current->next->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Edit src to first layer start=1500");
+  ges_timeline_object_edit (src, NULL, 0, GES_EDIT_MODE_NORMAL, GES_EDGE_NONE,
+      1500);
+  /*                                   1500___________src_________2500
+   *                                   1500______tr______2000
+   *        500___________src1_________1500                 ^
+   *                         1000_________^_src3_________2000   Layer
+   *                         1000__tr__1500
+   *---------------------------------------------------------------------------
+   * 0__________src2_________1000                               Layer1
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 1500);
+  assert_equals_uint64 (src->duration, 1000);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1500 - 500);
+  assert_equals_uint64 (src2->start, 0);
+  assert_equals_uint64 (src2->duration, 1000);
+  assert_equals_uint64 (src3->start, 1000);
+  assert_equals_uint64 (src3->duration, 2000 - 1000);
+
+  GST_DEBUG ("Checking transitions on first layer");
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 7);
+  fail_unless (current->data == src1);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  fail_unless (current->data == src3);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  fail_unless (current->data == src);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Checking second layer");
+  current = objects = ges_timeline_layer_get_objects (layer1);
+  assert_equals_int (g_list_length (objects), 1);
+  assert_is_type (current->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Ripple src1 to 700");
+  ges_timeline_object_edit (src1, NULL, 0, GES_EDIT_MODE_RIPPLE, GES_EDGE_NONE,
+      700);
+  /*                                           1700___________src_________2700
+   *                                           1700__tr__2000
+   *                700___________src1_________1700
+   *                         1000___________src3_________2000   Layer
+   *                         1000______tr______1700
+   *---------------------------------------------------------------------------
+   * 0__________src2_________1000                               Layer1
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 1700);
+  assert_equals_uint64 (src->duration, 1000);
+  assert_equals_uint64 (src1->start, 700);
+  assert_equals_uint64 (src1->duration, 1700 - 700);
+  assert_equals_uint64 (src2->start, 0);
+  assert_equals_uint64 (src2->duration, 1000);
+  assert_equals_uint64 (src3->start, 1000);
+  assert_equals_uint64 (src3->duration, 2000 - 1000);
+
+  GST_DEBUG ("Checking transitions on first layer");
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 7);
+  fail_unless (current->data == src1);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 1700 - 1000);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 1700 - 1000);
+
+  current = current->next;
+  fail_unless (current->data == src3);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1700);
+  assert_equals_uint64 (transition->duration, 2000 - 1700);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1700);
+  assert_equals_uint64 (transition->duration, 2000 - 1700);
+
+  current = current->next;
+  fail_unless (current->data == src);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Checking second layer");
+  current = objects = ges_timeline_layer_get_objects (layer1);
+  assert_equals_int (g_list_length (objects), 1);
+  assert_is_type (current->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  g_object_unref (timeline);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_layer_activate_automatic_transition)
+{
+  GESAsset *asset, *transition_asset;
+  GESTimeline *timeline;
+  GESTimelineLayer *layer;
+  GList *objects, *current;
+  GESTimelineObject *transition;
+  GESTimelineObject *src, *src1, *src2, *src3;
+
+  ges_init ();
+
+  asset = ges_asset_request (GES_TYPE_TIMELINE_TEST_SOURCE, NULL, NULL);
+  transition_asset =
+      ges_asset_request (GES_TYPE_TIMELINE_STANDARD_TRANSITION, "crossfade",
+      NULL);
+  fail_unless (GES_IS_ASSET (asset));
+
+  GST_DEBUG ("Create timeline");
+  timeline = ges_timeline_new_audio_video ();
+  assert_is_type (timeline, GES_TYPE_TIMELINE);
+
+  GST_DEBUG ("Append a layer to the timeline");
+  layer = ges_timeline_append_layer (timeline);
+  assert_is_type (layer, GES_TYPE_TIMELINE_LAYER);
+
+  GST_DEBUG ("Adding object from 0 -- 1000 to layer");
+  src = GES_TIMELINE_OBJECT (ges_timeline_layer_add_asset (layer, asset, 0, 0,
+          1000, 1, GES_TRACK_TYPE_UNKNOWN));
+  fail_unless (GES_IS_TIMELINE_OBJECT (src));
+
+  GST_DEBUG ("Adding object from 500 -- 1000 to first layer");
+  src1 = GES_TIMELINE_OBJECT (ges_timeline_layer_add_asset (layer, asset, 500,
+          0, 1000, 1, GES_TRACK_TYPE_UNKNOWN));
+  fail_unless (GES_IS_TIMELINE_OBJECT (src1));
+
+  GST_DEBUG ("Adding object from 1000 -- 2000 to layer");
+  src2 = GES_TIMELINE_OBJECT (ges_timeline_layer_add_asset (layer, asset, 1000,
+          0, 1000, 1, GES_TRACK_TYPE_UNKNOWN));
+  fail_unless (GES_IS_TIMELINE_OBJECT (src2));
+
+  GST_DEBUG ("Adding object from 2000 -- 2500 to layer");
+  src3 = GES_TIMELINE_OBJECT (ges_timeline_layer_add_asset (layer, asset, 2000,
+          0, 500, 1, GES_TRACK_TYPE_UNKNOWN));
+  fail_unless (GES_IS_TIMELINE_OBJECT (src3));
+
+  /*
+   * 0___________src_________1000
+   *        500___________src1_________1500
+   *                         1000____src2_______2000
+   *                                            2000_______src2_____2500
+   */
+  GST_DEBUG ("Checking src timing values");
+  assert_equals_uint64 (src->start, 0);
+  assert_equals_uint64 (src->duration, 1000);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1500 - 500);
+  assert_equals_uint64 (src2->start, 1000);
+  assert_equals_uint64 (src2->duration, 1000);
+  assert_equals_uint64 (src3->start, 2000);
+  assert_equals_uint64 (src3->duration, 500);
+
+  GST_DEBUG ("Checking that no transition has been added");
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 4);
+  assert_is_type (current->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG ("Adding transition from 1000 -- 1500 to layer");
+  transition =
+      GES_TIMELINE_OBJECT (ges_timeline_layer_add_asset (layer,
+          transition_asset, 1000, 0, 500, 1, GES_TRACK_TYPE_VIDEO));
+  fail_unless (GES_IS_TIMELINE_STANDARD_TRANSITION (transition));
+  objects = ges_timeline_object_get_track_objects (transition);
+  assert_equals_int (g_list_length (objects), 1);
+  g_list_free_full (objects, gst_object_unref);
+
+  GST_DEBUG ("Checking the transitions");
+  /*
+   * 0___________src_________1000
+   *        500___________src1_________1500
+   *                         1000__tr__1500 (1 of the 2 tracks only)
+   *                         1000____src2_______2000
+   *                                            2000_______src3_____2500
+   */
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 5);
+  current = current->next;
+  assert_is_type (current->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+  current = current->next;
+  assert_is_type (current->data, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  current = current->next;
+  assert_is_type (current->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+  current = current->next;
+  assert_is_type (current->data, GES_TYPE_TIMELINE_TEST_SOURCE);
+  g_list_free_full (objects, gst_object_unref);
+
+  ges_timeline_layer_set_auto_transition (layer, TRUE);
+  /*
+   * 0___________src_________1000
+   *        500______tr______1000
+   *        500___________src1_________1500
+   *                         1000__tr__1500
+   *                         1000____src2_______2000
+   *                                            2000_______src3_____2500
+   */
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 8);
+  assert_equals_uint64 (src->start, 0);
+  assert_equals_uint64 (src->duration, 1000);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1500 - 500);
+  assert_equals_uint64 (src2->start, 1000);
+  assert_equals_uint64 (src2->duration, 1000);
+  assert_equals_uint64 (src3->start, 2000);
+  assert_equals_uint64 (src3->duration, 500);
+
+  GST_DEBUG ("Checking transitions");
+  fail_unless (current->data == src);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  fail_unless (current->data == src1);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1000);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  fail_unless (current->data == src2);
+
+  current = current->next;
+  fail_unless (current->data == src3);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+  GST_DEBUG ("Moving src2 to 1200, check everything updates properly");
+  ges_timeline_object_set_start (src2, 1200);
+  /*
+   * 0___________src_________1000
+   *        500______tr______1000
+   *        500___________src1_________1500
+   *                           1200_tr_1500
+   *                           1200____src2_______2200
+   *                                          !__tr__^
+   *                                          2000_______src3_____2500
+   */
+  current = objects = ges_timeline_layer_get_objects (layer);
+  assert_equals_int (g_list_length (objects), 10);
+  assert_equals_uint64 (src->start, 0);
+  assert_equals_uint64 (src->duration, 1000);
+  assert_equals_uint64 (src1->start, 500);
+  assert_equals_uint64 (src1->duration, 1500 - 500);
+  assert_equals_uint64 (src2->start, 1200);
+  assert_equals_uint64 (src2->duration, 1000);
+  assert_equals_uint64 (src3->start, 2000);
+  assert_equals_uint64 (src3->duration, 500);
+
+  GST_DEBUG ("Checking transitions");
+  fail_unless (current->data == src);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 500);
+  assert_equals_uint64 (transition->duration, 500);
+
+  current = current->next;
+  fail_unless (current->data == src1);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1200);
+  assert_equals_uint64 (transition->duration, 300);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 1200);
+  assert_equals_uint64 (transition->duration, 300);
+
+  current = current->next;
+  fail_unless (current->data == src2);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 2000);
+  assert_equals_uint64 (transition->duration, 200);
+
+  current = current->next;
+  transition = current->data;
+  assert_is_type (transition, GES_TYPE_TIMELINE_STANDARD_TRANSITION);
+  assert_equals_uint64 (transition->start, 2000);
+  assert_equals_uint64 (transition->duration, 200);
+
+  current = current->next;
+  fail_unless (current->data == src3);
+  g_list_free_full (objects, gst_object_unref);
+  ASSERT_OBJECT_REFCOUNT (transition, "Only the layer owns a ref", 1);
+
+
+  gst_object_unref (timeline);
 }
 
 GST_END_TEST;
@@ -660,7 +1740,9 @@ ges_suite (void)
 
   tcase_add_test (tc_chain, test_layer_properties);
   tcase_add_test (tc_chain, test_layer_priorities);
-  tcase_add_test (tc_chain, test_layer_automatic_transition);
+  tcase_add_test (tc_chain, test_single_layer_automatic_transition);
+  tcase_add_test (tc_chain, test_multi_layer_automatic_transition);
+  tcase_add_test (tc_chain, test_layer_activate_automatic_transition);
   tcase_add_test (tc_chain, test_layer_meta_string);
   tcase_add_test (tc_chain, test_layer_meta_boolean);
   tcase_add_test (tc_chain, test_layer_meta_int);