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