Merge branch '0.10'
[platform/upstream/gstreamer.git] / ges / ges-timeline-layer.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
3  *               2009 Nokia Corporation
4  *               2011 Mathieu Duponchelle <mathieu.duponchelle@epitech.eu>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:ges-timeline-layer
24  * @short_description: Non-overlapping sequence of #GESTimelineObject
25  *
26  * Responsible for the ordering of the various contained TimelineObject(s). A
27  * timeline layer has a "priority" property, which is used to manage the
28  * priorities of individual TimelineObjects. Two layers should not have the
29  * same priority within a given timeline.
30  */
31
32 #include "ges-internal.h"
33 #include "ges-timeline-layer.h"
34 #include "ges.h"
35 #include "ges-timeline-source.h"
36
37 #define LAYER_HEIGHT 1000
38
39 static void
40 track_object_removed_cb (GESTimelineObject * object,
41     GESTrackObject * track_object);
42 static void track_object_added_cb (GESTimelineObject * object,
43     GESTrackObject * track_object, GHashTable * signal_table);
44 static void track_object_changed_cb (GESTrackObject * track_object,
45     GParamSpec * arg G_GNUC_UNUSED);
46 static void calculate_transitions (GESTrackObject * track_object);
47 static void calculate_next_transition (GESTrackObject * track_object,
48     GESTimelineLayer * layer);
49
50 static void
51 timeline_object_height_changed_cb (GESTimelineObject * obj,
52     GESTrackEffect * tr_eff, GESTimelineObject * second_obj);
53
54 G_DEFINE_TYPE (GESTimelineLayer, ges_timeline_layer, G_TYPE_INITIALLY_UNOWNED);
55
56 struct _GESTimelineLayerPrivate
57 {
58   /*< private > */
59   GList *objects_start;         /* The TimelineObjects sorted by start and
60                                  * priority */
61
62   guint32 priority;             /* The priority of the layer within the 
63                                  * containing timeline */
64
65   gboolean auto_transition;
66
67
68   GHashTable *signal_table;
69 };
70
71 enum
72 {
73   PROP_0,
74   PROP_PRIORITY,
75   PROP_AUTO_TRANSITION,
76   PROP_LAST
77 };
78
79 enum
80 {
81   OBJECT_ADDED,
82   OBJECT_REMOVED,
83   LAST_SIGNAL
84 };
85
86 static guint ges_timeline_layer_signals[LAST_SIGNAL] = { 0 };
87
88 static gboolean ges_timeline_layer_resync_priorities (GESTimelineLayer * layer);
89
90 static GList *track_get_by_layer (GESTimelineLayer * layer, GESTrack * track);
91
92 static void compare (GList * compared, GESTrackObject * track_object,
93     gboolean ahead);
94
95 static void
96 ges_timeline_layer_get_property (GObject * object, guint property_id,
97     GValue * value, GParamSpec * pspec)
98 {
99   GESTimelineLayer *layer = GES_TIMELINE_LAYER (object);
100
101   switch (property_id) {
102     case PROP_PRIORITY:
103       g_value_set_uint (value, layer->priv->priority);
104       break;
105     case PROP_AUTO_TRANSITION:
106       g_value_set_boolean (value, layer->priv->auto_transition);
107       break;
108     default:
109       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
110   }
111 }
112
113 static void
114 ges_timeline_layer_set_property (GObject * object, guint property_id,
115     const GValue * value, GParamSpec * pspec)
116 {
117   GESTimelineLayer *layer = GES_TIMELINE_LAYER (object);
118
119   switch (property_id) {
120     case PROP_PRIORITY:
121       ges_timeline_layer_set_priority (layer, g_value_get_uint (value));
122       break;
123     case PROP_AUTO_TRANSITION:
124       ges_timeline_layer_set_auto_transition (layer,
125           g_value_get_boolean (value));
126       break;
127     default:
128       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
129   }
130 }
131
132 static void
133 ges_timeline_layer_dispose (GObject * object)
134 {
135   GESTimelineLayer *layer = GES_TIMELINE_LAYER (object);
136   GESTimelineLayerPrivate *priv = layer->priv;
137
138   GST_DEBUG ("Disposing layer");
139
140   while (priv->objects_start)
141     ges_timeline_layer_remove_object (layer,
142         (GESTimelineObject *) priv->objects_start->data);
143
144   G_OBJECT_CLASS (ges_timeline_layer_parent_class)->dispose (object);
145 }
146
147 static void
148 ges_timeline_layer_class_init (GESTimelineLayerClass * klass)
149 {
150   GObjectClass *object_class = G_OBJECT_CLASS (klass);
151
152   g_type_class_add_private (klass, sizeof (GESTimelineLayerPrivate));
153
154   object_class->get_property = ges_timeline_layer_get_property;
155   object_class->set_property = ges_timeline_layer_set_property;
156   object_class->dispose = ges_timeline_layer_dispose;
157
158   /**
159    * GESTimelineLayer:priority
160    *
161    * The priority of the layer in the #GESTimeline. 0 is the highest
162    * priority. Conceptually, a #GESTimeline is a stack of GESTimelineLayers,
163    * and the priority of the layer represents its position in the stack. Two
164    * layers should not have the same priority within a given GESTimeline.
165    */
166   g_object_class_install_property (object_class, PROP_PRIORITY,
167       g_param_spec_uint ("priority", "Priority",
168           "The priority of the layer", 0, G_MAXUINT, 0, G_PARAM_READWRITE));
169
170   /**
171    * GESTimelineLayer:auto-transition
172    *
173    * Sets whether transitions are added automagically when timeline objects overlap.
174    */
175   g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
176       g_param_spec_boolean ("auto-transition", "Auto-Transition",
177           "whether the transitions are added", FALSE, G_PARAM_READWRITE));
178
179   /**
180    * GESTimelineLayer::object-added
181    * @layer: the #GESTimelineLayer
182    * @object: the #GESTimelineObject that was added.
183    *
184    * Will be emitted after the object was added to the layer.
185    */
186   ges_timeline_layer_signals[OBJECT_ADDED] =
187       g_signal_new ("object-added", G_TYPE_FROM_CLASS (klass),
188       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineLayerClass, object_added),
189       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
190       GES_TYPE_TIMELINE_OBJECT);
191
192   /**
193    * GESTimelineLayer::object-removed
194    * @layer: the #GESTimelineLayer
195    * @object: the #GESTimelineObject that was removed
196    *
197    * Will be emitted after the object was removed from the layer.
198    */
199   ges_timeline_layer_signals[OBJECT_REMOVED] =
200       g_signal_new ("object-removed", G_TYPE_FROM_CLASS (klass),
201       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineLayerClass,
202           object_removed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
203       1, GES_TYPE_TIMELINE_OBJECT);
204 }
205
206 static void
207 ges_timeline_layer_init (GESTimelineLayer * self)
208 {
209   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
210       GES_TYPE_TIMELINE_LAYER, GESTimelineLayerPrivate);
211
212   self->priv->priority = 0;
213   self->priv->auto_transition = FALSE;
214   self->min_gnl_priority = 0;
215   self->max_gnl_priority = LAYER_HEIGHT;
216   self->priv->signal_table =
217       g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref,
218       NULL);
219 }
220
221 /**
222  * ges_timeline_layer_new:
223  *
224  * Creates a new #GESTimelineLayer.
225  *
226  * Returns: A new #GESTimelineLayer
227  */
228 GESTimelineLayer *
229 ges_timeline_layer_new (void)
230 {
231   return g_object_new (GES_TYPE_TIMELINE_LAYER, NULL);
232 }
233
234 /**
235  * ges_timeline_layer_get_timeline:
236  * @layer: The #GESTimelineLayer to get the parent #GESTimeline from
237  *
238  * Get the #GESTimeline in which #GESTimelineLayer currently is.
239  *
240  * Returns: (transfer none):  the #GESTimeline in which #GESTimelineLayer
241  * currently is or %NULL if not in any timeline yet.
242  */
243 GESTimeline *
244 ges_timeline_layer_get_timeline (GESTimelineLayer * layer)
245 {
246   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), NULL);
247
248   return layer->timeline;
249 }
250
251 void
252 ges_timeline_layer_set_timeline (GESTimelineLayer * layer,
253     GESTimeline * timeline)
254 {
255   GST_DEBUG ("layer:%p, timeline:%p", layer, timeline);
256
257   layer->timeline = timeline;
258 }
259
260 static gint
261 objects_start_compare (GESTimelineObject * a, GESTimelineObject * b)
262 {
263   if (a->start == b->start) {
264     if (a->priority < b->priority)
265       return -1;
266     if (a->priority > b->priority)
267       return 1;
268     return 0;
269   }
270   if (a->start < b->start)
271     return -1;
272   if (a->start > b->start)
273     return 1;
274   return 0;
275 }
276
277 static GList *
278 track_get_by_layer (GESTimelineLayer * layer, GESTrack * track)
279 {
280   GList *tck_objects_list = NULL, *tmp = NULL, *return_list = NULL;
281   GESTimelineObject *tl_obj;
282
283   tck_objects_list = ges_track_get_objects (track);
284   for (tmp = tck_objects_list; tmp; tmp = tmp->next) {
285     tl_obj = ges_track_object_get_timeline_object (tmp->data);
286
287     if (ges_timeline_object_get_layer (tl_obj) == layer) {
288       /*  We steal the reference from tck_objects_list */
289       return_list = g_list_append (return_list, tmp->data);
290
291     } else
292       g_object_unref (tmp->data);
293   }
294
295   return return_list;
296 }
297
298 /**
299  * ges_timeline_layer_add_object:
300  * @layer: a #GESTimelineLayer
301  * @object: (transfer full): the #GESTimelineObject to add.
302  *
303  * Adds the given object to the layer. Sets the object's parent, and thus
304  * takes ownership of the object.
305  *
306  * An object can only be added to one layer.
307  *
308  * Returns: TRUE if the object was properly added to the layer, or FALSE
309  * if the @layer refuses to add the object.
310  */
311 gboolean
312 ges_timeline_layer_add_object (GESTimelineLayer * layer,
313     GESTimelineObject * object)
314 {
315   GESTimelineLayer *tl_obj_layer;
316   guint32 maxprio, minprio, prio;
317
318   GST_DEBUG ("layer:%p, object:%p", layer, object);
319
320   tl_obj_layer = ges_timeline_object_get_layer (object);
321
322   if (G_UNLIKELY (tl_obj_layer)) {
323     GST_WARNING ("TimelineObject %p already belongs to another layer", object);
324     g_object_unref (tl_obj_layer);
325     return FALSE;
326   }
327
328   g_object_ref_sink (object);
329
330   /* Take a reference to the object and store it stored by start/priority */
331   layer->priv->objects_start =
332       g_list_insert_sorted (layer->priv->objects_start, object,
333       (GCompareFunc) objects_start_compare);
334
335   /* We have to wait for the track objects to be created to calculate transitions */
336   if (layer->priv->auto_transition) {
337     if (GES_IS_TIMELINE_SOURCE (object)) {
338       g_signal_connect (G_OBJECT (object), "track-object-added",
339           G_CALLBACK (track_object_added_cb), layer->priv->signal_table);
340       g_signal_connect (G_OBJECT (object), "track-object-removed",
341           G_CALLBACK (track_object_removed_cb), NULL);
342     }
343   }
344
345   /* Inform the object it's now in this layer */
346   ges_timeline_object_set_layer (object, layer);
347
348   GST_DEBUG ("current object priority : %d, layer min/max : %d/%d",
349       GES_TIMELINE_OBJECT_PRIORITY (object),
350       layer->min_gnl_priority, layer->max_gnl_priority);
351
352   /* Set the priority. */
353   maxprio = layer->max_gnl_priority;
354   minprio = layer->min_gnl_priority;
355   prio = GES_TIMELINE_OBJECT_PRIORITY (object);
356   if (minprio + prio > (maxprio)) {
357     GST_WARNING ("%p is out of the layer %p space, setting its priority to "
358         "setting its priority %d to failthe maximum priority of the layer %d",
359         object, layer, prio, maxprio - minprio);
360     ges_timeline_object_set_priority (object, LAYER_HEIGHT - 1);
361   }
362   /* If the object has an acceptable priority, we just let it with its current
363    * priority */
364
365   ges_timeline_layer_resync_priorities (layer);
366
367   /* emit 'object-added' */
368   g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_ADDED], 0, object);
369
370   return TRUE;
371 }
372
373 static void
374 track_object_duration_cb (GESTrackObject * track_object,
375     GParamSpec * arg G_GNUC_UNUSED)
376 {
377   GESTimelineLayer *layer;
378   GESTimelineObject *tlobj;
379
380   tlobj = ges_track_object_get_timeline_object (track_object);
381   layer = ges_timeline_object_get_layer (tlobj);
382   if (G_LIKELY (GES_IS_TRACK_SOURCE (track_object)))
383     GST_DEBUG ("Here we should recalculate");
384   calculate_next_transition (track_object, layer);
385 }
386
387 static void
388 track_object_deleted_cb (GESTrack * track, GESTrackObject * track_object)
389 {
390   GList *track_objects, *tmp, *cur;
391   GESTimelineLayer *layer;
392
393   track_objects = ges_track_get_objects (track);
394   cur = g_list_find (track_objects, track_object);
395   for (tmp = cur->next; tmp; tmp = tmp->next) {
396     if (GES_IS_TRACK_SOURCE (tmp->data)) {
397       break;
398     }
399     if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
400         || GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
401       layer =
402           ges_timeline_object_get_layer (ges_track_object_get_timeline_object
403           (tmp->data));
404       if (ges_timeline_layer_get_auto_transition (layer)) {
405         ges_track_enable_update (track, FALSE);
406         ges_timeline_layer_remove_object (layer,
407             ges_track_object_get_timeline_object (tmp->data));
408         ges_track_enable_update (track, TRUE);
409       }
410       g_object_unref (layer);
411     }
412   }
413
414   for (tmp = cur->prev; tmp; tmp = tmp->prev) {
415     if (GES_IS_TRACK_SOURCE (tmp->data)) {
416       break;
417     }
418     if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
419         || GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
420       layer =
421           ges_timeline_object_get_layer (ges_track_object_get_timeline_object
422           (tmp->data));
423       if (ges_timeline_layer_get_auto_transition (layer)) {
424         ges_track_enable_update (track, FALSE);
425         ges_timeline_layer_remove_object (layer,
426             ges_track_object_get_timeline_object (tmp->data));
427         ges_track_enable_update (track, TRUE);
428       }
429       g_object_unref (layer);
430     }
431   }
432   g_object_unref (track_object);
433 }
434
435 static void
436 track_object_added_cb (GESTimelineObject * object,
437     GESTrackObject * track_object, GHashTable * signal_table)
438 {
439   GESTrack *track;
440   gint ptr;
441
442   if (GES_IS_TRACK_SOURCE (track_object)) {
443     g_signal_connect (G_OBJECT (track_object), "notify::start",
444         G_CALLBACK (track_object_changed_cb), NULL);
445     g_signal_connect (G_OBJECT (track_object), "notify::duration",
446         G_CALLBACK (track_object_duration_cb), NULL);
447     calculate_transitions (track_object);
448   }
449   track = ges_track_object_get_track (track_object);
450   if (!g_hash_table_lookup (signal_table, track)) {
451     ptr = g_signal_connect (track, "track-object-removed",
452         (GCallback) track_object_deleted_cb, NULL);
453     g_hash_table_insert (signal_table, track, GINT_TO_POINTER (ptr));
454   }
455   return;
456 }
457
458 static void
459 track_object_removed_cb (GESTimelineObject * object,
460     GESTrackObject * track_object)
461 {
462   return;
463   if (GES_IS_TRACK_SOURCE (track_object)) {
464     g_signal_handlers_disconnect_by_func (track_object, track_object_changed_cb,
465         object);
466   }
467   return;
468 }
469
470 static void
471 timeline_object_height_changed_cb (GESTimelineObject * obj,
472     GESTrackEffect * tr_eff, GESTimelineObject * second_obj)
473 {
474   gint priority, height;
475   g_object_get (obj, "height", &height, "priority", &priority, NULL);
476   g_object_set (second_obj, "priority", priority + height, NULL);
477 }
478
479 static void
480 track_object_changed_cb (GESTrackObject * track_object,
481     GParamSpec * arg G_GNUC_UNUSED)
482 {
483   if (G_LIKELY (GES_IS_TRACK_SOURCE (track_object)))
484     calculate_transitions (track_object);
485 }
486
487 static void
488 calculate_next_transition_with_list (GESTrackObject * track_object,
489     GList * tckobjs_in_layer, GESTimelineLayer * layer)
490 {
491   GList *compared;
492
493   if (!(compared = g_list_find (tckobjs_in_layer, track_object)))
494     return;
495
496   if (compared == NULL)
497     /* This is the last TrackObject of the Track */
498     return;
499
500   do {
501     compared = compared->next;
502     if (compared == NULL)
503       return;
504   } while (!GES_IS_TRACK_SOURCE (compared->data));
505
506   compare (compared, track_object, FALSE);
507 }
508
509
510 static void
511 calculate_next_transition (GESTrackObject * track_object,
512     GESTimelineLayer * layer)
513 {
514   GESTrack *track = ges_track_object_get_track (track_object);
515   GList *tckobjs_in_layer = track_get_by_layer (layer, track);
516
517   if (ges_track_object_get_track (track_object)) {
518     calculate_next_transition_with_list (track_object, tckobjs_in_layer, layer);
519   }
520
521   g_list_foreach (tckobjs_in_layer, (GFunc) g_object_unref, NULL);
522   g_list_free (tckobjs_in_layer);
523 }
524
525 static void
526 calculate_transitions (GESTrackObject * track_object)
527 {
528   GList *tckobjs_in_layer, *compared;
529   GESTrack *track = ges_track_object_get_track (track_object);
530   GESTimelineLayer *layer;
531   GESTimelineObject *tlobj;
532
533   tlobj = ges_track_object_get_timeline_object (track_object);
534   layer = ges_timeline_object_get_layer (tlobj);
535   tckobjs_in_layer = track_get_by_layer (layer, track);
536   if (!(compared = g_list_find (tckobjs_in_layer, track_object)))
537     return;
538   do {
539     compared = compared->prev;
540
541     if (compared == NULL) {
542       /* Nothing before, let's check after */
543       calculate_next_transition_with_list (track_object, tckobjs_in_layer,
544           layer);
545       goto done;
546
547     }
548   } while (!GES_IS_TRACK_SOURCE (compared->data));
549
550   compare (compared, track_object, TRUE);
551
552   calculate_next_transition_with_list (track_object, tckobjs_in_layer, layer);
553
554 done:
555   g_list_foreach (tckobjs_in_layer, (GFunc) g_object_unref, NULL);
556   g_list_free (tckobjs_in_layer);
557 }
558
559
560 /* Compare:
561  * @compared: The #GList of #GESTrackObjects that we compare with @track_object
562  * @track_object: The #GESTrackObject that serves as a reference
563  * @ahead: %TRUE if we are comparing frontward %FALSE if we are comparing
564  * backward*/
565 static void
566 compare (GList * compared, GESTrackObject * track_object, gboolean ahead)
567 {
568   GList *tmp;
569   gint64 start, duration, compared_start, compared_duration, end, compared_end,
570       tr_start, tr_duration;
571   GESTimelineStandardTransition *trans = NULL;
572   GESTrack *track;
573   GESTimelineLayer *layer;
574   GESTimelineObject *object, *compared_object, *first_object, *second_object;
575   gint priority;
576
577   g_return_if_fail (compared);
578
579   GST_DEBUG ("Recalculating transitions");
580
581   object = ges_track_object_get_timeline_object (track_object);
582
583   if (!object) {
584     GST_WARNING ("Trackobject not in a timeline object: "
585         "Can not calulate transitions");
586
587     return;
588   }
589
590   compared_object = ges_track_object_get_timeline_object (compared->data);
591   layer = ges_timeline_object_get_layer (object);
592
593   start = ges_track_object_get_start (track_object);
594   duration = ges_track_object_get_duration (track_object);
595   compared_start = ges_track_object_get_start (compared->data);
596   compared_duration = ges_track_object_get_duration (compared->data);
597   end = start + duration;
598   compared_end = compared_start + compared_duration;
599
600   if (ahead) {
601     /* Make sure we remove the last transition we created it is not needed
602      * FIXME make it a smarter way */
603     if (compared->prev && GES_IS_TRACK_TRANSITION (compared->prev->data)) {
604       trans = GES_TIMELINE_STANDARD_TRANSITION
605           (ges_track_object_get_timeline_object (compared->prev->data));
606       g_object_get (compared->prev->data, "start", &tr_start, "duration",
607           &tr_duration, NULL);
608       if (tr_start >= compared_start && tr_start + tr_duration <= compared_end)
609         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
610       trans = NULL;
611     }
612
613     for (tmp = compared->next; tmp; tmp = tmp->next) {
614       /* If we have a transitionmnmnm we recaluculuculate its values */
615       if (GES_IS_TRACK_TRANSITION (tmp->data)) {
616         g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
617             NULL);
618
619         if (tr_start + tr_duration == compared_start + compared_duration) {
620           GESTimelineObject *tlobj;
621           tlobj = ges_track_object_get_timeline_object (tmp->data);
622
623           trans = GES_TIMELINE_STANDARD_TRANSITION (tlobj);
624           break;
625         }
626       }
627     }
628
629     if (compared_end <= start) {
630       if (trans) {
631         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
632         g_object_get (compared_object, "priority", &priority, NULL);
633         g_object_set (object, "priority", priority, NULL);
634       }
635
636       goto clean;
637     } else if (start > compared_start && end < compared_end) {
638       if (trans) {
639         /* Transition not needed anymore */
640         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
641       }
642       goto clean;
643     } else if (start <= compared_start) {
644       if (trans) {
645         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
646       }
647
648       goto clean;
649     }
650
651   } else {
652     if (compared->next && GES_IS_TRACK_TRANSITION (compared->next->data)) {
653       trans = GES_TIMELINE_STANDARD_TRANSITION
654           (ges_track_object_get_timeline_object (compared->next->data));
655       g_object_get (compared->prev->data, "start", &tr_start, "duration",
656           &tr_duration, NULL);
657       if (tr_start >= compared_start && tr_start + tr_duration <= compared_end)
658         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
659       trans = NULL;
660     }
661     for (tmp = compared->prev; tmp; tmp = tmp->prev) {
662       if GES_IS_TRACK_TRANSITION
663         (tmp->data) {
664         g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
665             NULL);
666         if (tr_start == compared_start) {
667           trans = GES_TIMELINE_STANDARD_TRANSITION
668               (ges_track_object_get_timeline_object (tmp->data));
669           break;
670         }
671         }
672     }
673
674     if (start + duration <= compared_start) {
675       if (trans) {
676         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
677         g_object_get (object, "priority", &priority, NULL);
678         g_object_set (compared_object, "priority", priority, NULL);
679       }
680       goto clean;
681
682     } else if (start > compared_start) {
683       if (trans)
684         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
685
686       goto clean;
687     } else if (start < compared_start && end > compared_end) {
688       if (trans) {
689         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (trans));
690       }
691
692       goto clean;
693     }
694   }
695
696   if (!trans) {
697     gint height;
698
699     trans =
700         ges_timeline_standard_transition_new_for_nick ((gchar *) "crossfade");
701     track = ges_track_object_get_track (track_object);
702
703     ges_timeline_object_set_supported_formats (GES_TIMELINE_OBJECT (trans),
704         track->type);
705
706     ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (trans));
707
708     if (ahead) {
709       first_object = ges_track_object_get_timeline_object (compared->data);
710       second_object = object;
711     } else {
712       second_object = ges_track_object_get_timeline_object (compared->data);
713       first_object = object;
714     }
715
716     g_object_get (first_object, "priority", &priority, "height", &height, NULL);
717     g_object_set (second_object, "priority", priority + height, NULL);
718     g_signal_connect (first_object, "notify::height",
719         (GCallback) timeline_object_height_changed_cb, second_object);
720   }
721
722   if (ahead) {
723     g_object_set (trans, "start", start, "duration",
724         compared_duration + compared_start - start, NULL);
725   } else {
726     g_object_set (trans, "start", compared_start, "duration",
727         start + duration - compared_start, NULL);
728   }
729
730 clean:
731   g_object_unref (layer);
732 }
733
734 static void
735 look_for_transition (GESTrackObject * track_object, GESTimelineLayer * layer)
736 {
737   GESTrack *track;
738   GList *track_objects, *tmp, *cur;
739
740   track = ges_track_object_get_track (track_object);
741
742   track_objects = ges_track_get_objects (track);
743
744   cur = g_list_find (track_objects, track_object);
745
746   for (tmp = cur->next; tmp; tmp = tmp->next) {
747     if (GES_IS_TRACK_SOURCE (tmp->data)) {
748       break;
749     }
750     if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
751         || GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
752       ges_timeline_layer_remove_object (layer,
753           ges_track_object_get_timeline_object (tmp->data));
754     }
755   }
756
757   for (tmp = cur->prev; tmp; tmp = tmp->prev) {
758     if (GES_IS_TRACK_SOURCE (tmp->data)) {
759       break;
760     }
761     if (GES_IS_TRACK_AUDIO_TRANSITION (tmp->data)
762         || GES_IS_TRACK_VIDEO_TRANSITION (tmp->data)) {
763       ges_timeline_layer_remove_object (layer,
764           ges_track_object_get_timeline_object (tmp->data));
765     }
766   }
767   g_list_foreach (track_objects, (GFunc) g_object_unref, NULL);
768   g_list_free (track_objects);
769 }
770
771 static gboolean
772 disconnect_handlers (GESTrack * track, gpointer * ptr)
773 {
774   g_signal_handler_disconnect (track, GPOINTER_TO_INT (ptr));
775
776   return TRUE;
777 }
778
779 /**
780  * ges_timeline_layer_remove_object:
781  * @layer: a #GESTimelineLayer
782  * @object: the #GESTimelineObject to remove
783  *
784  * Removes the given @object from the @layer and unparents it.
785  * Unparenting it means the reference owned by @layer on the @object will be
786  * removed. If you wish to use the @object after this function, make sure you
787  * call g_object_ref() before removing it from the @layer.
788  *
789  * Returns: TRUE if the object could be removed, FALSE if the layer does
790  * not want to remove the object.
791  */
792 gboolean
793 ges_timeline_layer_remove_object (GESTimelineLayer * layer,
794     GESTimelineObject * object)
795 {
796   GESTimelineLayer *tl_obj_layer;
797   GList *trackobjects, *tmp;
798
799   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
800   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
801
802   GST_DEBUG ("layer:%p, object:%p", layer, object);
803
804   tl_obj_layer = ges_timeline_object_get_layer (object);
805   if (G_UNLIKELY (tl_obj_layer != layer)) {
806     GST_WARNING ("TimelineObject doesn't belong to this layer");
807     if (tl_obj_layer != NULL)
808       g_object_unref (tl_obj_layer);
809     return FALSE;
810   }
811   g_object_unref (tl_obj_layer);
812
813   if (layer->priv->auto_transition) {
814     trackobjects = ges_timeline_object_get_track_objects (object);
815
816     for (tmp = trackobjects; tmp; tmp = tmp->next) {
817       look_for_transition (tmp->data, layer);
818     }
819
820     g_list_foreach (trackobjects, (GFunc) g_object_unref, NULL);
821     g_list_free (trackobjects);
822   }
823
824   g_hash_table_foreach_remove (layer->priv->signal_table,
825       (GHRFunc) disconnect_handlers, NULL);
826
827   /* emit 'object-removed' */
828   g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_REMOVED], 0, object);
829
830   /* inform the object it's no longer in a layer */
831   ges_timeline_object_set_layer (object, NULL);
832
833   /* Remove it from our list of controlled objects */
834   layer->priv->objects_start =
835       g_list_remove (layer->priv->objects_start, object);
836
837   /* Remove our reference to the object */
838   g_object_unref (object);
839
840   return TRUE;
841 }
842
843 /**
844  * ges_timeline_layer_resync_priorities:
845  * @layer: a #GESTimelineLayer
846  *
847  * Resyncs the priorities of the objects controlled by @layer.
848  * This method
849  */
850 gboolean
851 ges_timeline_layer_resync_priorities (GESTimelineLayer * layer)
852 {
853   GList *tmp;
854   GESTimelineObject *obj;
855
856   GST_DEBUG ("Resync priorities of %p", layer);
857
858   /* TODO : Inhibit composition updates while doing this.
859    * Ideally we want to do it from an even higher level, but here will
860    * do in the meantime. */
861
862   for (tmp = layer->priv->objects_start; tmp; tmp = tmp->next) {
863     obj = GES_TIMELINE_OBJECT (tmp->data);
864     ges_timeline_object_set_priority (obj, GES_TIMELINE_OBJECT_PRIORITY (obj));
865   }
866
867   return TRUE;
868 }
869
870 /**
871  * ges_timeline_layer_set_priority:
872  * @layer: a #GESTimelineLayer
873  * @priority: the priority to set
874  *
875  * Sets the layer to the given @priority. See the documentation of the
876  * priority property for more information.
877  */
878 void
879 ges_timeline_layer_set_priority (GESTimelineLayer * layer, guint priority)
880 {
881   g_return_if_fail (GES_IS_TIMELINE_LAYER (layer));
882
883   GST_DEBUG ("layer:%p, priority:%d", layer, priority);
884
885   if (priority != layer->priv->priority) {
886     layer->priv->priority = priority;
887     layer->min_gnl_priority = (priority * LAYER_HEIGHT);
888     layer->max_gnl_priority = ((priority + 1) * LAYER_HEIGHT) - 1;
889
890     ges_timeline_layer_resync_priorities (layer);
891   }
892 }
893
894 /**
895  * ges_timeline_layer_get_auto_transition:
896  * @layer: a #GESTimelineLayer
897  *
898  * Gets whether transitions are automatically added when objects
899  * overlap or not.
900  *
901  * Returns: %TRUE if transitions are automatically added, else %FALSE.
902  */
903 gboolean
904 ges_timeline_layer_get_auto_transition (GESTimelineLayer * layer)
905 {
906   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), 0);
907
908   return layer->priv->auto_transition;
909 }
910
911 /**
912  * ges_timeline_layer_set_auto_transition:
913  * @layer: a #GESTimelineLayer
914  * @auto_transition: whether the auto_transition is active
915  *
916  * Sets the layer to the given @auto_transition. See the documentation of the
917  * property auto_transition for more information.
918  */
919 void
920 ges_timeline_layer_set_auto_transition (GESTimelineLayer * layer,
921     gboolean auto_transition)
922 {
923
924   GList *tmp;
925   g_return_if_fail (GES_IS_TIMELINE_LAYER (layer));
926
927   if (auto_transition) {
928     for (tmp = layer->priv->objects_start; tmp; tmp = tmp->next) {
929       if (GES_IS_TIMELINE_SOURCE (tmp->data)) {
930         g_signal_connect (G_OBJECT (tmp->data), "track-object-added",
931             G_CALLBACK (track_object_added_cb), layer);
932         g_signal_connect (G_OBJECT (tmp->data), "track-object-removed",
933             G_CALLBACK (track_object_removed_cb), layer);
934       }
935     }
936     /* FIXME calculate all the transitions at that time */
937   }
938   layer->priv->auto_transition = auto_transition;
939 }
940
941 /**
942  * ges_timeline_layer_get_priority:
943  * @layer: a #GESTimelineLayer
944  *
945  * Get the priority of @layer within the timeline.
946  *
947  * Returns: The priority of the @layer within the timeline.
948  */
949 guint
950 ges_timeline_layer_get_priority (GESTimelineLayer * layer)
951 {
952   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), 0);
953
954   return layer->priv->priority;
955 }
956
957 /**
958  * ges_timeline_layer_get_objects:
959  * @layer: a #GESTimelineLayer
960  *
961  * Get the timeline objects this layer contains.
962  *
963  * Returns: (transfer full) (element-type GESTimelineObject): a #GList of
964  * timeline objects. The user is responsible for
965  * unreffing the contained objects and freeing the list.
966  */
967
968 GList *
969 ges_timeline_layer_get_objects (GESTimelineLayer * layer)
970 {
971   GList *ret = NULL;
972   GList *tmp;
973   GESTimelineLayerClass *klass;
974
975   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), NULL);
976
977   klass = GES_TIMELINE_LAYER_GET_CLASS (layer);
978
979   if (klass->get_objects) {
980     return klass->get_objects (layer);
981   }
982
983   for (tmp = layer->priv->objects_start; tmp; tmp = tmp->next) {
984     ret = g_list_prepend (ret, tmp->data);
985     g_object_ref (tmp->data);
986   }
987
988   ret = g_list_reverse (ret);
989   return ret;
990 }