ges: makes "pass over" accurate and reset priority when transition is removed
[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  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:ges-timeline-layer
23  * @short_description: Non-overlaping sequence of #GESTimelineObject
24  *
25  * Responsible for the ordering of the various contained TimelineObject(s). A
26  * timeline layer has a "priority" property, which is used to manage the
27  * priorities of individual TimelineObjects. Two layers should not have the
28  * same priority within a given timeline.
29  */
30
31 #include "ges-internal.h"
32 #include "gesmarshal.h"
33 #include "ges-timeline-layer.h"
34 #include "ges.h"
35 #include "ges-timeline-source.h"
36
37 #define LAYER_HEIGHT 10
38
39 void track_object_added_cb (GESTimelineObject * object,
40     GESTrackObject * track_object, GESTimelineLayer * layer);
41 static void track_object_start_changed_cb (GESTrackObject * track_object,
42     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object);
43 void calculate_transition (GESTrackObject * track_object,
44     GESTimelineObject * object);
45
46 void
47 timeline_object_height_changed_cb (GESTimelineObject * obj,
48     GESTrackEffect * tr_eff, GESTimelineObject * second_obj);
49
50 G_DEFINE_TYPE (GESTimelineLayer, ges_timeline_layer, G_TYPE_INITIALLY_UNOWNED);
51
52 struct _GESTimelineLayerPrivate
53 {
54   /*< private > */
55   GList *objects_start;         /* The TimelineObjects sorted by start and
56                                  * priority */
57
58   guint32 priority;             /* The priority of the layer within the 
59                                  * containing timeline */
60
61   gboolean auto_transition;
62 };
63
64 enum
65 {
66   PROP_0,
67   PROP_PRIORITY,
68   PROP_AUTO_TRANSITION,
69   PROP_LAST
70 };
71
72 enum
73 {
74   OBJECT_ADDED,
75   OBJECT_REMOVED,
76   LAST_SIGNAL
77 };
78
79 static guint ges_timeline_layer_signals[LAST_SIGNAL] = { 0 };
80
81 static gboolean ges_timeline_layer_resync_priorities (GESTimelineLayer * layer);
82
83 static GList *track_get_by_layer (GESTrackObject * track_object);
84
85 static void compare (GList * compared, GESTrackObject * track_object,
86     gboolean ahead);
87
88 static void
89 ges_timeline_layer_get_property (GObject * object, guint property_id,
90     GValue * value, GParamSpec * pspec)
91 {
92   GESTimelineLayer *layer = GES_TIMELINE_LAYER (object);
93
94   switch (property_id) {
95     case PROP_PRIORITY:
96       g_value_set_uint (value, layer->priv->priority);
97       break;
98     case PROP_AUTO_TRANSITION:
99       g_value_set_boolean (value, layer->priv->auto_transition);
100       break;
101     default:
102       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
103   }
104 }
105
106 static void
107 ges_timeline_layer_set_property (GObject * object, guint property_id,
108     const GValue * value, GParamSpec * pspec)
109 {
110   GESTimelineLayer *layer = GES_TIMELINE_LAYER (object);
111
112   switch (property_id) {
113     case PROP_PRIORITY:
114       ges_timeline_layer_set_priority (layer, g_value_get_uint (value));
115       break;
116     case PROP_AUTO_TRANSITION:
117       ges_timeline_layer_set_auto_transition (layer,
118           g_value_get_boolean (value));
119       break;
120     default:
121       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
122   }
123 }
124
125 static void
126 ges_timeline_layer_dispose (GObject * object)
127 {
128   GESTimelineLayer *layer = GES_TIMELINE_LAYER (object);
129   GESTimelineLayerPrivate *priv = layer->priv;
130
131   GST_DEBUG ("Disposing layer");
132
133   while (priv->objects_start)
134     ges_timeline_layer_remove_object (layer,
135         (GESTimelineObject *) priv->objects_start->data);
136
137   G_OBJECT_CLASS (ges_timeline_layer_parent_class)->dispose (object);
138 }
139
140 static void
141 ges_timeline_layer_class_init (GESTimelineLayerClass * klass)
142 {
143   GObjectClass *object_class = G_OBJECT_CLASS (klass);
144
145   g_type_class_add_private (klass, sizeof (GESTimelineLayerPrivate));
146
147   object_class->get_property = ges_timeline_layer_get_property;
148   object_class->set_property = ges_timeline_layer_set_property;
149   object_class->dispose = ges_timeline_layer_dispose;
150
151   /**
152    * GESTimelineLayer:priority
153    *
154    * The priority of the layer in the #GESTimeline. 0 is the highest
155    * priority. Conceptually, a #GESTimeline is a stack of GESTimelineLayers,
156    * and the priority of the layer represents its position in the stack. Two
157    * layers should not have the same priority within a given GESTimeline.
158    */
159   g_object_class_install_property (object_class, PROP_PRIORITY,
160       g_param_spec_uint ("priority", "Priority",
161           "The priority of the layer", 0, G_MAXUINT, 0, G_PARAM_READWRITE));
162
163   /**
164    * GESTimelineLayer:auto_transitioning
165    *
166    * Sets whether transitions are added automatically when timeline objects overlap
167    */
168   g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
169       g_param_spec_boolean ("auto-transition", "Auto-Transition",
170           "whether the transitions are added", FALSE, G_PARAM_READWRITE));
171
172   /**
173    * GESTimelineLayer::object-added
174    * @layer: the #GESTimelineLayer
175    * @object: the #GESTimelineObject that was added.
176    *
177    * Will be emitted after the object was added to the layer.
178    */
179   ges_timeline_layer_signals[OBJECT_ADDED] =
180       g_signal_new ("object-added", G_TYPE_FROM_CLASS (klass),
181       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineLayerClass, object_added),
182       NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
183       GES_TYPE_TIMELINE_OBJECT);
184
185   /**
186    * GESTimelineLayer::object-removed
187    * @layer: the #GESTimelineLayer
188    * @object: the #GESTimelineObject that was removed
189    *
190    * Will be emitted after the object was removed from the layer.
191    */
192   ges_timeline_layer_signals[OBJECT_REMOVED] =
193       g_signal_new ("object-removed", G_TYPE_FROM_CLASS (klass),
194       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineLayerClass,
195           object_removed), NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
196       GES_TYPE_TIMELINE_OBJECT);
197 }
198
199 static void
200 ges_timeline_layer_init (GESTimelineLayer * self)
201 {
202   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
203       GES_TYPE_TIMELINE_LAYER, GESTimelineLayerPrivate);
204
205   self->priv->priority = 0;
206   self->priv->auto_transition = FALSE;
207   self->min_gnl_priority = 0;
208   self->max_gnl_priority = 9;
209 }
210
211 /**
212  * ges_timeline_layer_new:
213  *
214  * Creates a new #GESTimelineLayer.
215  *
216  * Returns: A new #GESTimelineLayer
217  */
218 GESTimelineLayer *
219 ges_timeline_layer_new (void)
220 {
221   return g_object_new (GES_TYPE_TIMELINE_LAYER, NULL);
222 }
223
224 void
225 ges_timeline_layer_set_timeline (GESTimelineLayer * layer,
226     GESTimeline * timeline)
227 {
228   GST_DEBUG ("layer:%p, timeline:%p", layer, timeline);
229
230   layer->timeline = timeline;
231 }
232
233 static gint
234 objects_start_compare (GESTimelineObject * a, GESTimelineObject * b)
235 {
236   if (a->start == b->start) {
237     if (a->priority < b->priority)
238       return -1;
239     if (a->priority > b->priority)
240       return 1;
241     return 0;
242   }
243   if (a->start < b->start)
244     return -1;
245   if (a->start > b->start)
246     return 1;
247   return 0;
248 }
249
250 /**
251  * ges_timeline_layer_add_object:
252  * @layer: a #GESTimelineLayer
253  * @object: (transfer full): the #GESTimelineObject to add.
254  *
255  * Adds the given object to the layer. Sets the object's parent, and thus
256  * takes ownership of the object.
257  *
258  * An object can only be added to one layer.
259  *
260  * Returns: TRUE if the object was properly added to the layer, or FALSE
261  * if the @layer refuses to add the object.
262  */
263
264 static GList *
265 track_get_by_layer (GESTrackObject * track_object)
266 {
267   GESTrack *track;
268   GList *tck_objects_list = NULL, *tmp = NULL, *return_list = NULL;
269   GESTimelineLayer *layer;
270   GESTimelineObject *tl_obj;
271   gint priority, compared_priority;
272
273   track = ges_track_object_get_track (track_object);
274   tl_obj = ges_track_object_get_timeline_object (track_object);
275   layer = ges_timeline_object_get_layer (tl_obj);
276   priority = ges_timeline_layer_get_priority (layer);
277
278   tck_objects_list = ges_track_get_objects (track);
279   for (tmp = tck_objects_list; tmp; tmp = tmp->next) {
280     tl_obj = ges_track_object_get_timeline_object (tmp->data);
281     layer = ges_timeline_object_get_layer (tl_obj);
282     compared_priority = ges_timeline_layer_get_priority (layer);
283     if (compared_priority == priority) {
284       g_object_ref (tmp->data);
285       return_list = g_list_append (return_list, tmp->data);
286     }
287   }
288
289   for (tmp = tck_objects_list; tmp; tmp = tmp->next)
290     g_object_unref (tmp->data);
291   g_list_free (tck_objects_list);
292   return return_list;
293 }
294
295 gboolean
296 ges_timeline_layer_add_object (GESTimelineLayer * layer,
297     GESTimelineObject * object)
298 {
299   GESTimelineLayer *tl_obj_layer;
300   guint32 maxprio, minprio, prio;
301
302   GST_DEBUG ("layer:%p, object:%p", layer, object);
303
304   tl_obj_layer = ges_timeline_object_get_layer (object);
305
306   if (G_UNLIKELY (tl_obj_layer)) {
307     GST_WARNING ("TimelineObject %p already belongs to another layer", object);
308     g_object_unref (tl_obj_layer);
309     return FALSE;
310   }
311
312   g_object_ref_sink (object);
313
314   /* Take a reference to the object and store it stored by start/priority */
315   layer->priv->objects_start =
316       g_list_insert_sorted (layer->priv->objects_start, object,
317       (GCompareFunc) objects_start_compare);
318
319   if (layer->priv->auto_transition) {
320     if (GES_IS_TIMELINE_SOURCE (object)
321         && (ges_timeline_layer_get_priority (layer) != 99)) {
322       g_signal_connect (G_OBJECT (object), "track-object-added",
323           G_CALLBACK (track_object_added_cb), layer);
324     }
325   }
326
327   /* Inform the object it's now in this layer */
328   ges_timeline_object_set_layer (object, layer);
329
330   GST_DEBUG ("current object priority : %d, layer min/max : %d/%d",
331       GES_TIMELINE_OBJECT_PRIORITY (object),
332       layer->min_gnl_priority, layer->max_gnl_priority);
333
334   /* Set the priority. */
335   maxprio = layer->max_gnl_priority;
336   minprio = layer->min_gnl_priority;
337   prio = GES_TIMELINE_OBJECT_PRIORITY (object);
338   if (minprio + prio > (maxprio)) {
339     GST_WARNING ("%p is out of the layer %p space, setting its priority to "
340         "setting its priority %d to failthe maximum priority of the layer %d",
341         object, layer, prio, maxprio - minprio);
342     ges_timeline_object_set_priority (object, LAYER_HEIGHT - 1);
343   }
344   /* If the object has an acceptable priority, we just let it with its current
345    * priority */
346
347   ges_timeline_layer_resync_priorities (layer);
348
349   /* emit 'object-added' */
350   g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_ADDED], 0, object);
351
352   return TRUE;
353 }
354
355 void
356 track_object_added_cb (GESTimelineObject * object,
357     GESTrackObject * track_object, GESTimelineLayer * layer)
358 {
359   if (GES_IS_TRACK_SOURCE (track_object)) {
360     g_signal_connect (G_OBJECT (track_object), "notify::start",
361         G_CALLBACK (track_object_start_changed_cb), object);
362     calculate_transition (track_object, object);
363   }
364
365   g_object_unref (layer);
366   return;
367 }
368
369 void
370 timeline_object_height_changed_cb (GESTimelineObject * obj,
371     GESTrackEffect * tr_eff, GESTimelineObject * second_obj)
372 {
373   gint priority, height;
374   g_object_get (obj, "height", &height, "priority", &priority, NULL);
375   g_object_set (second_obj, "priority", priority + height, NULL);
376 }
377
378 static void
379 track_object_start_changed_cb (GESTrackObject * track_object,
380     GParamSpec * arg G_GNUC_UNUSED, GESTimelineObject * object)
381 {
382   if (GES_IS_TRACK_SOURCE (track_object)) {
383     calculate_transition (track_object, object);
384   }
385 }
386
387 void
388 calculate_transition (GESTrackObject * track_object, GESTimelineObject * object)
389 {
390   GList *list, *cur, *compared, *compared_next, *tmp;
391
392   list = track_get_by_layer (track_object);
393   cur = g_list_find (list, track_object);
394
395   compared = cur->prev;
396
397   if (compared == NULL)
398     goto next;
399
400   while (!GES_IS_TRACK_SOURCE (compared->data)) {
401     compared = compared->prev;
402     if (compared == NULL)
403       goto next;
404   }
405
406   compare (compared, track_object, TRUE);
407
408 next:
409   cur = g_list_find (list, track_object);
410   compared_next = cur->next;
411
412   if (compared_next == NULL)
413     return;
414
415   while (!GES_IS_TRACK_SOURCE (compared_next->data)) {
416     compared_next = compared_next->next;
417     if (compared_next == NULL)
418       return;
419   }
420
421   compare (compared_next, track_object, FALSE);
422
423   for (tmp = list; tmp; tmp = tmp->next)
424     g_object_unref (tmp->data);
425   g_list_free (list);
426 }
427
428
429 static void
430 compare (GList * compared, GESTrackObject * track_object, gboolean ahead)
431 {
432   GList *tmp;
433   gint64 start, duration, compared_start, compared_duration, end, compared_end,
434       tr_start, tr_duration;
435   GESTimelineStandardTransition *tr = NULL;
436   GESTrack *track;
437   GESTimelineLayer *layer;
438   GESTimelineObject *object, *compared_object, *first_object, *second_object;
439   gint priority;
440
441   object = ges_track_object_get_timeline_object (track_object);
442   compared_object = ges_track_object_get_timeline_object (compared->data);
443   layer = ges_timeline_object_get_layer (object);
444
445   start = ges_track_object_get_start (track_object);
446   duration = ges_track_object_get_duration (track_object);
447   compared_start = ges_track_object_get_start (compared->data);
448   compared_duration = ges_track_object_get_duration (compared->data);
449   end = start + duration;
450   compared_end = compared_start + compared_duration;
451
452   if (ahead) {
453     for (tmp = compared->next; tmp; tmp = tmp->next) {
454       if GES_IS_TRACK_TRANSITION
455         (tmp->data) {
456         g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
457             NULL);
458         if (tr_start + tr_duration == compared_start + compared_duration) {
459           tr = GES_TIMELINE_STANDARD_TRANSITION
460               (ges_track_object_get_timeline_object (tmp->data));
461           break;
462         }
463         }
464     }
465
466     if (compared_end <= start) {
467       if (tr) {
468         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr));
469         g_object_get (compared_object, "priority", &priority, NULL);
470         g_object_set (object, "priority", priority, NULL);
471       }
472       goto clean;
473     } else if (start > compared_start && end < compared_end) {
474       if (tr) {
475         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr));
476       }
477       if (start - compared_start < compared_end - end) {
478         ges_track_object_set_start (track_object, compared_end - duration + 1);
479         compare (compared, track_object, FALSE);
480       } else {
481         ges_track_object_set_start (track_object, compared_start - 1);
482         compare (compared, track_object, FALSE);
483       }
484       goto clean;
485     } else if (start <= compared_start) {
486       if (tr) {
487         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr));
488       }
489       goto clean;
490     }
491
492   } else {
493     for (tmp = compared->prev; tmp; tmp = tmp->prev) {
494       if GES_IS_TRACK_TRANSITION
495         (tmp->data) {
496         g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
497             NULL);
498         if (tr_start == compared_start) {
499           tr = GES_TIMELINE_STANDARD_TRANSITION
500               (ges_track_object_get_timeline_object (tmp->data));
501           break;
502         }
503         }
504     }
505
506     if (start + duration <= compared_start) {
507       if (tr) {
508         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr));
509         g_object_get (object, "priority", &priority, NULL);
510         g_object_set (compared_object, "priority", priority, NULL);
511       }
512       goto clean;
513     } else if (start > compared_start) {
514       if (tr) {
515         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr));
516       }
517       goto clean;
518     } else if (start < compared_start && end > compared_end) {
519       if (tr) {
520         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr));
521       }
522       if (compared_start - start < end - compared_end) {
523         ges_track_object_set_start (track_object, compared_end - duration - 1);
524         compare (compared, track_object, TRUE);
525       } else {
526         ges_track_object_set_start (track_object, compared_start + 1);
527       }
528       goto clean;
529     }
530   }
531
532   if (tr == NULL) {
533     gint height;
534     tr = ges_timeline_standard_transition_new_for_nick ((gchar *) "crossfade");
535     track = ges_track_object_get_track (track_object);
536
537     ges_timeline_object_set_supported_formats (GES_TIMELINE_OBJECT (tr),
538         track->type);
539
540     ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (tr));
541
542     if (ahead) {
543       first_object = ges_track_object_get_timeline_object (compared->data);
544       second_object = object;
545     } else {
546       second_object = ges_track_object_get_timeline_object (compared->data);
547       first_object = object;
548     }
549
550     g_object_get (first_object, "priority", &priority, "height", &height, NULL);
551     g_object_set (second_object, "priority", priority + height, NULL);
552     g_signal_connect (first_object, "notify::height",
553         (GCallback) timeline_object_height_changed_cb, second_object);
554   }
555
556   if (ahead) {
557     g_object_set (tr, "start", start, "duration",
558         compared_duration + compared_start - start, NULL);
559   } else {
560     g_object_set (tr, "start", compared_start, "duration",
561         start + duration - compared_start, NULL);
562   }
563
564 clean:
565   g_object_unref (track_object);
566   g_object_unref (layer);
567 }
568
569
570 /**
571  * ges_timeline_layer_remove_object:
572  * @layer: a #GESTimelineLayer
573  * @object: the #GESTimelineObject to remove
574  *
575  * Removes the given @object from the @layer and unparents it.
576  * Unparenting it means the reference owned by @layer on the @object will be
577  * removed. If you wish to use the @object after this function, make sure you
578  * call g_object_ref() before removing it from the @layer.
579  *
580  * Returns: TRUE if the object could be removed, FALSE if the layer does
581  * not want to remove the object.
582  */
583 gboolean
584 ges_timeline_layer_remove_object (GESTimelineLayer * layer,
585     GESTimelineObject * object)
586 {
587   GESTimelineLayer *tl_obj_layer;
588
589   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
590   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
591
592   GST_DEBUG ("layer:%p, object:%p", layer, object);
593
594   tl_obj_layer = ges_timeline_object_get_layer (object);
595   if (G_UNLIKELY (tl_obj_layer != layer)) {
596     GST_WARNING ("TimelineObject doesn't belong to this layer");
597     if (tl_obj_layer != NULL)
598       g_object_unref (tl_obj_layer);
599     return FALSE;
600   }
601   g_object_unref (tl_obj_layer);
602
603   /* emit 'object-removed' */
604   g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_REMOVED], 0, object);
605
606   /* inform the object it's no longer in a layer */
607   ges_timeline_object_set_layer (object, NULL);
608
609   /* Remove it from our list of controlled objects */
610   layer->priv->objects_start =
611       g_list_remove (layer->priv->objects_start, object);
612
613   /* Remove our reference to the object */
614   g_object_unref (object);
615
616   return TRUE;
617 }
618
619 /**
620  * ges_timeline_layer_resync_priorities:
621  * @layer: a #GESTimelineLayer
622  *
623  * Resyncs the priorities of the objects controlled by @layer.
624  * This method
625  */
626 gboolean
627 ges_timeline_layer_resync_priorities (GESTimelineLayer * layer)
628 {
629   GList *tmp;
630   GESTimelineObject *obj;
631
632   GST_DEBUG ("Resync priorities of %p", layer);
633
634   /* TODO : Inhibit composition updates while doing this.
635    * Ideally we want to do it from an even higher level, but here will
636    * do in the meantime. */
637
638   for (tmp = layer->priv->objects_start; tmp; tmp = tmp->next) {
639     obj = GES_TIMELINE_OBJECT (tmp->data);
640     ges_timeline_object_set_priority (obj, GES_TIMELINE_OBJECT_PRIORITY (obj));
641   }
642
643   return TRUE;
644 }
645
646 /**
647  * ges_timeline_layer_set_priority:
648  * @layer: a #GESTimelineLayer
649  * @priority: the priority to set
650  *
651  * Sets the layer to the given @priority. See the documentation of the
652  * priority property for more information.
653  */
654 void
655 ges_timeline_layer_set_priority (GESTimelineLayer * layer, guint priority)
656 {
657   g_return_if_fail (GES_IS_TIMELINE_LAYER (layer));
658
659   GST_DEBUG ("layer:%p, priority:%d", layer, priority);
660
661   if (priority != layer->priv->priority) {
662     layer->priv->priority = priority;
663     layer->min_gnl_priority = (priority * LAYER_HEIGHT);
664     layer->max_gnl_priority = ((priority + 1) * LAYER_HEIGHT) - 1;
665
666     ges_timeline_layer_resync_priorities (layer);
667   }
668 }
669
670 /**
671  * ges_timeline_layer_get_auto_transition:
672  * @layer: a #GESTimelineLayer
673  *
674  * Get the priority of @layer within the timeline.
675  *
676  * Returns: The priority of the @layer within the timeline.
677  */
678 gboolean
679 ges_timeline_layer_get_auto_transition (GESTimelineLayer * layer)
680 {
681   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), 0);
682
683   return layer->priv->auto_transition;
684 }
685
686 /**
687  * ges_timeline_layer_set_auto_transition:
688  * @layer: a #GESTimelineLayer
689  * @priority: whether the auto_transition is active
690  *
691  * Sets the layer to the given @auto_transition. See the documentation of the
692  * priority auto_transition for more information.
693  */
694 void
695 ges_timeline_layer_set_auto_transition (GESTimelineLayer * layer,
696     gboolean auto_transition)
697 {
698   g_return_if_fail (GES_IS_TIMELINE_LAYER (layer));
699
700   layer->priv->auto_transition = auto_transition;
701 }
702
703 /**
704  * ges_timeline_layer_get_priority:
705  * @layer: a #GESTimelineLayer
706  *
707  * Get the priority of @layer within the timeline.
708  *
709  * Returns: The priority of the @layer within the timeline.
710  */
711 guint
712 ges_timeline_layer_get_priority (GESTimelineLayer * layer)
713 {
714   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), 0);
715
716   return layer->priv->priority;
717 }
718
719 /**
720  * ges_timeline_layer_get_objects:
721  * @layer: a #GESTimelineLayer
722  *
723  * Get the timeline objects this layer contains.
724  *
725  * Returns: (transfer full) (element-type GESTimelineObject): a #GList of
726  * timeline objects. The user is responsible for
727  * unreffing the contained objects and freeing the list.
728  */
729
730 GList *
731 ges_timeline_layer_get_objects (GESTimelineLayer * layer)
732 {
733   GList *ret = NULL;
734   GList *tmp;
735   GESTimelineLayerClass *klass;
736
737   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), NULL);
738
739   klass = GES_TIMELINE_LAYER_GET_CLASS (layer);
740
741   if (klass->get_objects) {
742     return klass->get_objects (layer);
743   }
744
745   for (tmp = layer->priv->objects_start; tmp; tmp = tmp->next) {
746     ret = g_list_prepend (ret, tmp->data);
747     g_object_ref (tmp->data);
748   }
749
750   ret = g_list_reverse (ret);
751   return ret;
752 }