ges: add an auto-transition to the layer
[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, tr_start,
434       tr_duration;
435   GESTimelineStandardTransition *tr = NULL;
436   GESTrack *track;
437   GESTimelineLayer *layer;
438   GESTimelineObject *object, *first_object, *second_object;
439   gint priority;
440
441   object = ges_track_object_get_timeline_object (track_object);
442   layer = ges_timeline_object_get_layer (object);
443
444   start = ges_track_object_get_start (track_object);
445   duration = ges_track_object_get_duration (track_object);
446   compared_start = ges_track_object_get_start (compared->data);
447   compared_duration = ges_track_object_get_duration (compared->data);
448
449   if (ahead) {
450     for (tmp = compared->next; tmp; tmp = tmp->next) {
451       if GES_IS_TRACK_TRANSITION
452         (tmp->data) {
453         g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
454             NULL);
455         if (tr_start + tr_duration == compared_start + compared_duration) {
456           tr = GES_TIMELINE_STANDARD_TRANSITION
457               (ges_track_object_get_timeline_object (tmp->data));
458           break;
459         }
460         }
461     }
462     if (compared_start + compared_duration <= start) {
463       if (tr) {
464         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr));
465       }
466       goto clean;
467     } else if (start + duration < compared_start + compared_duration) {
468       if (tr) {
469         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr));
470       }
471       ges_track_object_set_start (track_object, compared_start - 5);
472       compare (compared, track_object, FALSE);
473       goto clean;
474     }
475   } else {
476     for (tmp = compared->prev; tmp; tmp = tmp->prev) {
477       if GES_IS_TRACK_TRANSITION
478         (tmp->data) {
479         g_object_get (tmp->data, "start", &tr_start, "duration", &tr_duration,
480             NULL);
481         if (tr_start == compared_start) {
482           tr = GES_TIMELINE_STANDARD_TRANSITION
483               (ges_track_object_get_timeline_object (tmp->data));
484           break;
485         }
486         }
487     }
488     if (start + duration <= compared_start) {
489       if (tr) {
490         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr));
491       }
492       goto clean;
493     } else if (start + 10 >= compared_start) {
494       if (tr) {
495         ges_timeline_layer_remove_object (layer, GES_TIMELINE_OBJECT (tr));
496       }
497       goto clean;
498     }
499   }
500
501   if (tr == NULL) {
502     gint height;
503     tr = ges_timeline_standard_transition_new_for_nick ((gchar *) "crossfade");
504     track = ges_track_object_get_track (track_object);
505
506     ges_timeline_object_set_supported_formats (GES_TIMELINE_OBJECT (tr),
507         track->type);
508
509     ges_timeline_layer_add_object (layer, GES_TIMELINE_OBJECT (tr));
510
511     if (ahead) {
512       first_object = ges_track_object_get_timeline_object (compared->data);
513       second_object = object;
514     } else {
515       second_object = ges_track_object_get_timeline_object (compared->data);
516       first_object = object;
517     }
518
519     g_object_get (first_object, "priority", &priority, "height", &height, NULL);
520     g_object_set (second_object, "priority", priority + height, NULL);
521     g_signal_connect (first_object, "notify::height",
522         (GCallback) timeline_object_height_changed_cb, second_object);
523   }
524
525   if (ahead) {
526     g_object_set (tr, "start", start, "duration",
527         compared_duration + compared_start - start, NULL);
528   } else {
529     g_object_set (tr, "start", compared_start, "duration",
530         start + duration - compared_start, NULL);
531   }
532
533 clean:
534   g_object_unref (track_object);
535   g_object_unref (layer);
536 }
537
538
539 /**
540  * ges_timeline_layer_remove_object:
541  * @layer: a #GESTimelineLayer
542  * @object: the #GESTimelineObject to remove
543  *
544  * Removes the given @object from the @layer and unparents it.
545  * Unparenting it means the reference owned by @layer on the @object will be
546  * removed. If you wish to use the @object after this function, make sure you
547  * call g_object_ref() before removing it from the @layer.
548  *
549  * Returns: TRUE if the object could be removed, FALSE if the layer does
550  * not want to remove the object.
551  */
552 gboolean
553 ges_timeline_layer_remove_object (GESTimelineLayer * layer,
554     GESTimelineObject * object)
555 {
556   GESTimelineLayer *tl_obj_layer;
557
558   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), FALSE);
559   g_return_val_if_fail (GES_IS_TIMELINE_OBJECT (object), FALSE);
560
561   GST_DEBUG ("layer:%p, object:%p", layer, object);
562
563   tl_obj_layer = ges_timeline_object_get_layer (object);
564   if (G_UNLIKELY (tl_obj_layer != layer)) {
565     GST_WARNING ("TimelineObject doesn't belong to this layer");
566     if (tl_obj_layer != NULL)
567       g_object_unref (tl_obj_layer);
568     return FALSE;
569   }
570   g_object_unref (tl_obj_layer);
571
572   /* emit 'object-removed' */
573   g_signal_emit (layer, ges_timeline_layer_signals[OBJECT_REMOVED], 0, object);
574
575   /* inform the object it's no longer in a layer */
576   ges_timeline_object_set_layer (object, NULL);
577
578   /* Remove it from our list of controlled objects */
579   layer->priv->objects_start =
580       g_list_remove (layer->priv->objects_start, object);
581
582   /* Remove our reference to the object */
583   g_object_unref (object);
584
585   return TRUE;
586 }
587
588 /**
589  * ges_timeline_layer_resync_priorities:
590  * @layer: a #GESTimelineLayer
591  *
592  * Resyncs the priorities of the objects controlled by @layer.
593  * This method
594  */
595 gboolean
596 ges_timeline_layer_resync_priorities (GESTimelineLayer * layer)
597 {
598   GList *tmp;
599   GESTimelineObject *obj;
600
601   GST_DEBUG ("Resync priorities of %p", layer);
602
603   /* TODO : Inhibit composition updates while doing this.
604    * Ideally we want to do it from an even higher level, but here will
605    * do in the meantime. */
606
607   for (tmp = layer->priv->objects_start; tmp; tmp = tmp->next) {
608     obj = GES_TIMELINE_OBJECT (tmp->data);
609     ges_timeline_object_set_priority (obj, GES_TIMELINE_OBJECT_PRIORITY (obj));
610   }
611
612   return TRUE;
613 }
614
615 /**
616  * ges_timeline_layer_set_priority:
617  * @layer: a #GESTimelineLayer
618  * @priority: the priority to set
619  *
620  * Sets the layer to the given @priority. See the documentation of the
621  * priority property for more information.
622  */
623 void
624 ges_timeline_layer_set_priority (GESTimelineLayer * layer, guint priority)
625 {
626   g_return_if_fail (GES_IS_TIMELINE_LAYER (layer));
627
628   GST_DEBUG ("layer:%p, priority:%d", layer, priority);
629
630   if (priority != layer->priv->priority) {
631     layer->priv->priority = priority;
632     layer->min_gnl_priority = (priority * LAYER_HEIGHT);
633     layer->max_gnl_priority = ((priority + 1) * LAYER_HEIGHT) - 1;
634
635     ges_timeline_layer_resync_priorities (layer);
636   }
637 }
638
639 /**
640  * ges_timeline_layer_get_auto_transition:
641  * @layer: a #GESTimelineLayer
642  *
643  * Get the priority of @layer within the timeline.
644  *
645  * Returns: The priority of the @layer within the timeline.
646  */
647 gboolean
648 ges_timeline_layer_get_auto_transition (GESTimelineLayer * layer)
649 {
650   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), 0);
651
652   return layer->priv->auto_transition;
653 }
654
655 /**
656  * ges_timeline_layer_set_auto_transition:
657  * @layer: a #GESTimelineLayer
658  * @priority: whether the auto_transition is active
659  *
660  * Sets the layer to the given @auto_transition. See the documentation of the
661  * priority auto_transition for more information.
662  */
663 void
664 ges_timeline_layer_set_auto_transition (GESTimelineLayer * layer,
665     gboolean auto_transition)
666 {
667   g_return_if_fail (GES_IS_TIMELINE_LAYER (layer));
668
669   layer->priv->auto_transition = auto_transition;
670 }
671
672 /**
673  * ges_timeline_layer_get_priority:
674  * @layer: a #GESTimelineLayer
675  *
676  * Get the priority of @layer within the timeline.
677  *
678  * Returns: The priority of the @layer within the timeline.
679  */
680 guint
681 ges_timeline_layer_get_priority (GESTimelineLayer * layer)
682 {
683   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), 0);
684
685   return layer->priv->priority;
686 }
687
688 /**
689  * ges_timeline_layer_get_objects:
690  * @layer: a #GESTimelineLayer
691  *
692  * Get the timeline objects this layer contains.
693  *
694  * Returns: (transfer full) (element-type GESTimelineObject): a #GList of
695  * timeline objects. The user is responsible for
696  * unreffing the contained objects and freeing the list.
697  */
698
699 GList *
700 ges_timeline_layer_get_objects (GESTimelineLayer * layer)
701 {
702   GList *ret = NULL;
703   GList *tmp;
704   GESTimelineLayerClass *klass;
705
706   g_return_val_if_fail (GES_IS_TIMELINE_LAYER (layer), NULL);
707
708   klass = GES_TIMELINE_LAYER_GET_CLASS (layer);
709
710   if (klass->get_objects) {
711     return klass->get_objects (layer);
712   }
713
714   for (tmp = layer->priv->objects_start; tmp; tmp = tmp->next) {
715     ret = g_list_prepend (ret, tmp->data);
716     g_object_ref (tmp->data);
717   }
718
719   ret = g_list_reverse (ret);
720   return ret;
721 }