layer: Add ability to get clips in a given interval
[platform/upstream/gstreamer.git] / ges / ges-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  *               2013 Thibault Saunier <thibault.saunier@collabora.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 /**
24  * SECTION:geslayer
25  * @title: GESLayer
26  * @short_description: Non-overlapping sequence of GESClip
27  *
28  * Responsible for the ordering of the various contained Clip(s). A
29  * timeline layer has a "priority" property, which is used to manage the
30  * priorities of individual Clips. Two layers should not have the
31  * same priority within a given timeline.
32  */
33
34 #include "ges-internal.h"
35 #include "ges-layer.h"
36 #include "ges.h"
37 #include "ges-source-clip.h"
38
39 static void ges_meta_container_interface_init
40     (GESMetaContainerInterface * iface);
41
42 G_DEFINE_TYPE_WITH_CODE (GESLayer, ges_layer,
43     G_TYPE_INITIALLY_UNOWNED, G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE, NULL)
44     G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER,
45         ges_meta_container_interface_init));
46
47 struct _GESLayerPrivate
48 {
49   /*< private > */
50   GList *clips_start;           /* The Clips sorted by start and
51                                  * priority */
52
53   guint32 priority;             /* The priority of the layer within the
54                                  * containing timeline */
55   gboolean auto_transition;
56 };
57
58 typedef struct
59 {
60   GESClip *clip;
61   GESLayer *layer;
62 } NewAssetUData;
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_layer_signals[LAST_SIGNAL] = { 0 };
80
81 /* GObject standard vmethods */
82 static void
83 ges_layer_get_property (GObject * object, guint property_id,
84     GValue * value, GParamSpec * pspec)
85 {
86   GESLayer *layer = GES_LAYER (object);
87
88   switch (property_id) {
89     case PROP_PRIORITY:
90       g_value_set_uint (value, layer->priv->priority);
91       break;
92     case PROP_AUTO_TRANSITION:
93       g_value_set_boolean (value, layer->priv->auto_transition);
94       break;
95     default:
96       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
97   }
98 }
99
100 static void
101 ges_layer_set_property (GObject * object, guint property_id,
102     const GValue * value, GParamSpec * pspec)
103 {
104   GESLayer *layer = GES_LAYER (object);
105
106   switch (property_id) {
107     case PROP_PRIORITY:
108       ges_layer_set_priority (layer, g_value_get_uint (value));
109       break;
110     case PROP_AUTO_TRANSITION:
111       ges_layer_set_auto_transition (layer, g_value_get_boolean (value));
112       break;
113     default:
114       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
115   }
116 }
117
118 static void
119 ges_layer_dispose (GObject * object)
120 {
121   GESLayer *layer = GES_LAYER (object);
122   GESLayerPrivate *priv = layer->priv;
123
124   GST_DEBUG ("Disposing layer");
125
126   while (priv->clips_start)
127     ges_layer_remove_clip (layer, (GESClip *) priv->clips_start->data);
128
129   G_OBJECT_CLASS (ges_layer_parent_class)->dispose (object);
130 }
131
132 static gboolean
133 _register_metas (GESLayer * layer)
134 {
135   ges_meta_container_register_meta_float (GES_META_CONTAINER (layer),
136       GES_META_READ_WRITE, GES_META_VOLUME, 1.0);
137
138   return TRUE;
139 }
140
141 static void
142 ges_meta_container_interface_init (GESMetaContainerInterface * iface)
143 {
144
145 }
146
147 static void
148 ges_layer_class_init (GESLayerClass * klass)
149 {
150   GObjectClass *object_class = G_OBJECT_CLASS (klass);
151
152   g_type_class_add_private (klass, sizeof (GESLayerPrivate));
153
154   object_class->get_property = ges_layer_get_property;
155   object_class->set_property = ges_layer_set_property;
156   object_class->dispose = ges_layer_dispose;
157
158   /**
159    * GESLayer:priority:
160    *
161    * The priority of the layer in the #GESTimeline. 0 is the highest
162    * priority. Conceptually, a #GESTimeline is a stack of GESLayers,
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    * Note that the timeline needs to be commited (with #ges_timeline_commit)
167    * for the change to be taken into account.
168    */
169   g_object_class_install_property (object_class, PROP_PRIORITY,
170       g_param_spec_uint ("priority", "Priority",
171           "The priority of the layer", 0, G_MAXUINT, 0, G_PARAM_READWRITE));
172
173   /**
174    * GESLayer:auto-transition:
175    *
176    * Sets whether transitions are added automagically when clips overlap.
177    */
178   g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
179       g_param_spec_boolean ("auto-transition", "Auto-Transition",
180           "whether the transitions are added", FALSE, G_PARAM_READWRITE));
181
182   /**
183    * GESLayer::clip-added:
184    * @layer: the #GESLayer
185    * @clip: the #GESClip that was added.
186    *
187    * Will be emitted after the clip was added to the layer.
188    */
189   ges_layer_signals[OBJECT_ADDED] =
190       g_signal_new ("clip-added", G_TYPE_FROM_CLASS (klass),
191       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESLayerClass, object_added),
192       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_CLIP);
193
194   /**
195    * GESLayer::clip-removed:
196    * @layer: the #GESLayer
197    * @clip: the #GESClip that was removed
198    *
199    * Will be emitted after the clip was removed from the layer.
200    */
201   ges_layer_signals[OBJECT_REMOVED] =
202       g_signal_new ("clip-removed", G_TYPE_FROM_CLASS (klass),
203       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESLayerClass,
204           object_removed), NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE,
205       1, GES_TYPE_CLIP);
206 }
207
208 static void
209 ges_layer_init (GESLayer * self)
210 {
211   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
212       GES_TYPE_LAYER, GESLayerPrivate);
213
214   self->priv->priority = 0;
215   self->priv->auto_transition = FALSE;
216   self->min_nle_priority = MIN_NLE_PRIO;
217   self->max_nle_priority = LAYER_HEIGHT + MIN_NLE_PRIO;
218
219   _register_metas (self);
220 }
221
222 static gint
223 ges_layer_resync_priorities_by_type (GESLayer * layer,
224     gint starting_priority, GType type)
225 {
226   GstClockTime next_reset = 0;
227   gint priority = starting_priority, max_priority = priority;
228   GList *tmp;
229   GESTimelineElement *element;
230
231   for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
232
233     element = GES_TIMELINE_ELEMENT (tmp->data);
234
235     if (GES_IS_TRANSITION_CLIP (element)) {
236       /* Blindly set transitions priorities to 0 */
237       _set_priority0 (element, 0);
238       continue;
239     } else if (!g_type_is_a (G_OBJECT_TYPE (element), type))
240       continue;
241
242     if (element->start > next_reset) {
243       priority = starting_priority;
244       next_reset = 0;
245     }
246
247     if (element->start + element->duration > next_reset)
248       next_reset = element->start + element->duration;
249
250     _set_priority0 (element, priority);
251     priority = priority + GES_CONTAINER_HEIGHT (element);
252
253     if (priority > max_priority)
254       max_priority = priority;
255   }
256
257   return max_priority;
258 }
259
260 /**
261  * ges_layer_resync_priorities:
262  * @layer: a #GESLayer
263  *
264  * Resyncs the priorities of the clips controlled by @layer.
265  */
266 gboolean
267 ges_layer_resync_priorities (GESLayer * layer)
268 {
269   gint min_source_prios;
270
271   GST_INFO_OBJECT (layer, "Resync priorities (prio: %d)",
272       layer->priv->priority);
273
274   min_source_prios = ges_layer_resync_priorities_by_type (layer, 1,
275       GES_TYPE_OPERATION_CLIP);
276
277   ges_layer_resync_priorities_by_type (layer, min_source_prios,
278       GES_TYPE_SOURCE_CLIP);
279
280   return TRUE;
281 }
282
283 static void
284 new_asset_cb (GESAsset * source, GAsyncResult * res, NewAssetUData * udata)
285 {
286   GError *error = NULL;
287
288   GESAsset *asset = ges_asset_request_finish (res, &error);
289
290   GST_DEBUG_OBJECT (udata->layer, "%" GST_PTR_FORMAT " Asset loaded, "
291       "setting its asset", udata->clip);
292
293   if (error) {
294     GESProject *project = udata->layer->timeline ?
295         GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE
296             (udata->layer->timeline))) : NULL;
297     if (project) {
298       gchar *possible_id;
299
300       possible_id = ges_project_try_updating_id (project, source, error);
301       if (possible_id) {
302         ges_asset_request_async (ges_asset_get_extractable_type (source),
303             possible_id, NULL, (GAsyncReadyCallback) new_asset_cb, udata);
304         g_free (possible_id);
305         return;
306       }
307     }
308
309     GST_ERROR ("Asset could not be created for uri %s, error: %s",
310         ges_asset_get_id (asset), error->message);
311
312   } else {
313     GESProject *project = udata->layer->timeline ?
314         GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE
315             (udata->layer->timeline))) : NULL;
316     ges_extractable_set_asset (GES_EXTRACTABLE (udata->clip), asset);
317
318     ges_project_add_asset (project, asset);
319     ges_layer_add_clip (udata->layer, udata->clip);
320   }
321
322   gst_object_unref (asset);
323   g_slice_free (NewAssetUData, udata);
324 }
325
326 /**
327  * ges_layer_get_duration:
328  * @layer: The #GESLayer to get the duration from
329  *
330  * Lets you retrieve the duration of the layer, which means
331  * the end time of the last clip inside it
332  *
333  * Returns: The duration of a layer
334  */
335 GstClockTime
336 ges_layer_get_duration (GESLayer * layer)
337 {
338   GList *tmp;
339   GstClockTime duration = 0;
340
341   for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
342     duration = MAX (duration, _END (tmp->data));
343   }
344
345   return duration;
346 }
347
348 /* Public methods */
349 /**
350  * ges_layer_remove_clip:
351  * @layer: a #GESLayer
352  * @clip: the #GESClip to remove
353  *
354  * Removes the given @clip from the @layer and unparents it.
355  * Unparenting it means the reference owned by @layer on the @clip will be
356  * removed. If you wish to use the @clip after this function, make sure you
357  * call gst_object_ref() before removing it from the @layer.
358  *
359  * Returns: TRUE if the clip could be removed, FALSE if the layer does
360  * not want to remove the clip.
361  */
362 gboolean
363 ges_layer_remove_clip (GESLayer * layer, GESClip * clip)
364 {
365   GESLayer *current_layer;
366
367   g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
368   g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
369
370   GST_DEBUG ("layer:%p, clip:%p", layer, clip);
371
372   current_layer = ges_clip_get_layer (clip);
373   if (G_UNLIKELY (current_layer != layer)) {
374     GST_WARNING ("Clip doesn't belong to this layer");
375
376     if (current_layer != NULL)
377       gst_object_unref (current_layer);
378
379     return FALSE;
380   }
381   gst_object_unref (current_layer);
382
383   /* Remove it from our list of controlled objects */
384   layer->priv->clips_start = g_list_remove (layer->priv->clips_start, clip);
385
386   /* emit 'clip-removed' */
387   g_signal_emit (layer, ges_layer_signals[OBJECT_REMOVED], 0, clip);
388
389   /* inform the clip it's no longer in a layer */
390   ges_clip_set_layer (clip, NULL);
391   /* so neither in a timeline */
392   if (layer->timeline)
393     ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (clip), NULL);
394
395   /* Remove our reference to the clip */
396   gst_object_unref (clip);
397
398   return TRUE;
399 }
400
401 /**
402  * ges_layer_set_priority:
403  * @layer: a #GESLayer
404  * @priority: the priority to set
405  *
406  * Sets the layer to the given @priority. See the documentation of the
407  * priority property for more information.
408  */
409 void
410 ges_layer_set_priority (GESLayer * layer, guint priority)
411 {
412   g_return_if_fail (GES_IS_LAYER (layer));
413
414   GST_DEBUG ("layer:%p, priority:%d", layer, priority);
415
416   if (priority != layer->priv->priority) {
417     layer->priv->priority = priority;
418     layer->min_nle_priority = (priority * LAYER_HEIGHT) + MIN_NLE_PRIO;
419     layer->max_nle_priority = ((priority + 1) * LAYER_HEIGHT) + MIN_NLE_PRIO;
420
421     ges_layer_resync_priorities (layer);
422   }
423
424   g_object_notify (G_OBJECT (layer), "priority");
425 }
426
427 /**
428  * ges_layer_get_auto_transition:
429  * @layer: a #GESLayer
430  *
431  * Gets whether transitions are automatically added when objects
432  * overlap or not.
433  *
434  * Returns: %TRUE if transitions are automatically added, else %FALSE.
435  */
436 gboolean
437 ges_layer_get_auto_transition (GESLayer * layer)
438 {
439   g_return_val_if_fail (GES_IS_LAYER (layer), 0);
440
441   return layer->priv->auto_transition;
442 }
443
444 /**
445  * ges_layer_set_auto_transition:
446  * @layer: a #GESLayer
447  * @auto_transition: whether the auto_transition is active
448  *
449  * Sets the layer to the given @auto_transition. See the documentation of the
450  * property auto_transition for more information.
451  */
452 void
453 ges_layer_set_auto_transition (GESLayer * layer, gboolean auto_transition)
454 {
455
456   g_return_if_fail (GES_IS_LAYER (layer));
457
458   layer->priv->auto_transition = auto_transition;
459   g_object_notify (G_OBJECT (layer), "auto-transition");
460 }
461
462 /**
463  * ges_layer_get_priority:
464  * @layer: a #GESLayer
465  *
466  * Get the priority of @layer within the timeline.
467  *
468  * Returns: The priority of the @layer within the timeline.
469  */
470 guint
471 ges_layer_get_priority (GESLayer * layer)
472 {
473   g_return_val_if_fail (GES_IS_LAYER (layer), 0);
474
475   return layer->priv->priority;
476 }
477
478 /**
479  * ges_layer_get_clips:
480  * @layer: a #GESLayer
481  *
482  * Get the clips this layer contains.
483  *
484  * Returns: (transfer full) (element-type GESClip): a #GList of
485  * clips. The user is responsible for
486  * unreffing the contained objects and freeing the list.
487  */
488
489 GList *
490 ges_layer_get_clips (GESLayer * layer)
491 {
492   GESLayerClass *klass;
493
494   g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
495
496   klass = GES_LAYER_GET_CLASS (layer);
497
498   if (klass->get_objects) {
499     return klass->get_objects (layer);
500   }
501
502   return g_list_sort (g_list_copy_deep (layer->priv->clips_start,
503           (GCopyFunc) gst_object_ref, NULL),
504       (GCompareFunc) element_start_compare);
505 }
506
507 /**
508  * ges_layer_is_empty:
509  * @layer: The #GESLayer to check
510  *
511  * Convenience method to check if @layer is empty (doesn't contain any clip),
512  * or not.
513  *
514  * Returns: %TRUE if @layer is empty, %FALSE if it already contains at least
515  * one #GESClip
516  */
517 gboolean
518 ges_layer_is_empty (GESLayer * layer)
519 {
520   g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
521
522   return (layer->priv->clips_start == NULL);
523 }
524
525 /**
526  * ges_layer_add_clip:
527  * @layer: a #GESLayer
528  * @clip: (transfer full): the #GESClip to add.
529  *
530  * Adds the given clip to the layer. Sets the clip's parent, and thus
531  * takes ownership of the clip.
532  *
533  * An clip can only be added to one layer.
534  *
535  * Calling this method will construct and properly set all the media related
536  * elements on @clip. If you need to know when those objects (actually #GESTrackElement)
537  * are constructed, you should connect to the container::child-added signal which
538  * is emited right after those elements are ready to be used.
539  *
540  * Returns: %TRUE if the clip was properly added to the layer, or %FALSE
541  * if the @layer refuses to add the clip.
542  */
543 gboolean
544 ges_layer_add_clip (GESLayer * layer, GESClip * clip)
545 {
546   GESAsset *asset;
547   GESLayerPrivate *priv;
548   GESLayer *current_layer;
549
550   g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
551   g_return_val_if_fail (GES_IS_CLIP (clip), FALSE);
552
553   GST_DEBUG_OBJECT (layer, "adding clip:%p", clip);
554
555   priv = layer->priv;
556   current_layer = ges_clip_get_layer (clip);
557   if (G_UNLIKELY (current_layer)) {
558     GST_WARNING ("Clip %p already belongs to another layer", clip);
559     gst_object_unref (current_layer);
560
561     return FALSE;
562   }
563
564   asset = ges_extractable_get_asset (GES_EXTRACTABLE (clip));
565   if (asset == NULL) {
566     gchar *id;
567     NewAssetUData *mudata = g_slice_new (NewAssetUData);
568
569     mudata->clip = clip;
570     mudata->layer = layer;
571
572     GST_DEBUG_OBJECT (layer, "%" GST_PTR_FORMAT " as no reference to any "
573         "assets creating a asset... trying sync", clip);
574
575     id = ges_extractable_get_id (GES_EXTRACTABLE (clip));
576     asset = ges_asset_request (G_OBJECT_TYPE (clip), id, NULL);
577     if (asset == NULL) {
578       GESProject *project = layer->timeline ?
579           GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE
580               (layer->timeline))) : NULL;
581
582       ges_asset_request_async (G_OBJECT_TYPE (clip),
583           id, NULL, (GAsyncReadyCallback) new_asset_cb, mudata);
584
585       if (project)
586         ges_project_add_loading_asset (project, G_OBJECT_TYPE (clip), id);
587       g_free (id);
588
589       GST_LOG_OBJECT (layer, "Object added async");
590       return TRUE;
591     }
592     g_free (id);
593
594     ges_extractable_set_asset (GES_EXTRACTABLE (clip), asset);
595
596     g_slice_free (NewAssetUData, mudata);
597   }
598
599
600   gst_object_ref_sink (clip);
601
602   /* Take a reference to the clip and store it stored by start/priority */
603   priv->clips_start = g_list_insert_sorted (priv->clips_start, clip,
604       (GCompareFunc) element_start_compare);
605
606   /* Inform the clip it's now in this layer */
607   ges_clip_set_layer (clip, layer);
608
609   GST_DEBUG ("current clip priority : %d, Height: %d", _PRIORITY (clip),
610       LAYER_HEIGHT);
611
612   /* Set the priority. */
613   if (_PRIORITY (clip) > LAYER_HEIGHT) {
614     GST_WARNING_OBJECT (layer,
615         "%p is out of the layer space, setting its priority to "
616         "%d, setting it to the maximum priority of the layer: %d", clip,
617         _PRIORITY (clip), LAYER_HEIGHT - 1);
618     _set_priority0 (GES_TIMELINE_ELEMENT (clip), LAYER_HEIGHT - 1);
619   }
620
621   ges_layer_resync_priorities (layer);
622
623   ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (clip),
624       layer->timeline);
625
626   /* emit 'clip-added' */
627   g_signal_emit (layer, ges_layer_signals[OBJECT_ADDED], 0, clip);
628
629   return TRUE;
630 }
631
632 /**
633  * ges_layer_add_asset:
634  * @layer: a #GESLayer
635  * @asset: The asset to add to
636  * @start: The start value to set on the new #GESClip,
637  * if @start == GST_CLOCK_TIME_NONE, it will be set to
638  * the current duration of @layer
639  * @inpoint: The inpoint value to set on the new #GESClip
640  * @duration: The duration value to set on the new #GESClip
641  * @track_types: The #GESTrackType to set on the the new #GESClip
642  *
643  * Creates Clip from asset, adds it to layer and
644  * returns a reference to it.
645  *
646  * Returns: (transfer none): Created #GESClip
647  */
648 GESClip *
649 ges_layer_add_asset (GESLayer * layer,
650     GESAsset * asset, GstClockTime start, GstClockTime inpoint,
651     GstClockTime duration, GESTrackType track_types)
652 {
653   GESClip *clip;
654
655   g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
656   g_return_val_if_fail (GES_IS_ASSET (asset), NULL);
657   g_return_val_if_fail (g_type_is_a (ges_asset_get_extractable_type
658           (asset), GES_TYPE_CLIP), NULL);
659
660   GST_DEBUG_OBJECT (layer, "Adding asset %s with: start: %" GST_TIME_FORMAT
661       " inpoint: %" GST_TIME_FORMAT " duration: %" GST_TIME_FORMAT
662       " track types: %d (%s)", ges_asset_get_id (asset), GST_TIME_ARGS (start),
663       GST_TIME_ARGS (inpoint), GST_TIME_ARGS (duration), track_types,
664       ges_track_type_name (track_types));
665
666   clip = GES_CLIP (ges_asset_extract (asset, NULL));
667
668   if (!GST_CLOCK_TIME_IS_VALID (start)) {
669     start = ges_layer_get_duration (layer);
670
671     GST_DEBUG_OBJECT (layer,
672         "No start specified, setting it to %" GST_TIME_FORMAT,
673         GST_TIME_ARGS (start));
674   }
675
676   _set_start0 (GES_TIMELINE_ELEMENT (clip), start);
677   _set_inpoint0 (GES_TIMELINE_ELEMENT (clip), inpoint);
678   if (track_types != GES_TRACK_TYPE_UNKNOWN)
679     ges_clip_set_supported_formats (clip, track_types);
680
681   if (GST_CLOCK_TIME_IS_VALID (duration)) {
682     _set_duration0 (GES_TIMELINE_ELEMENT (clip), duration);
683   }
684
685   if (!ges_layer_add_clip (layer, clip)) {
686     gst_object_unref (clip);
687
688     return NULL;
689   }
690
691   return clip;
692 }
693
694 /**
695  * ges_layer_new:
696  *
697  * Creates a new #GESLayer.
698  *
699  * Returns: (transfer floating): A new #GESLayer
700  */
701 GESLayer *
702 ges_layer_new (void)
703 {
704   return g_object_new (GES_TYPE_LAYER, NULL);
705 }
706
707 /**
708  * ges_layer_get_timeline:
709  * @layer: The #GESLayer to get the parent #GESTimeline from
710  *
711  * Get the #GESTimeline in which #GESLayer currently is.
712  *
713  * Returns: (transfer none) (nullable): the #GESTimeline in which #GESLayer
714  * currently is or %NULL if not in any timeline yet.
715  */
716 GESTimeline *
717 ges_layer_get_timeline (GESLayer * layer)
718 {
719   g_return_val_if_fail (GES_IS_LAYER (layer), NULL);
720
721   return layer->timeline;
722 }
723
724 void
725 ges_layer_set_timeline (GESLayer * layer, GESTimeline * timeline)
726 {
727   GList *tmp;
728
729   GST_DEBUG ("layer:%p, timeline:%p", layer, timeline);
730
731   for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
732     ges_timeline_element_set_timeline (tmp->data, timeline);
733   }
734
735   layer->timeline = timeline;
736 }
737
738 /**
739  * ges_layer_get_clips_in_interval:
740  * @layer: a #GESLayer
741  * @start: start of the interval
742  * @end: end of the interval
743  *
744  * Gets the clips which appear between @start and @end on @layer.
745  *
746  * Returns: (transfer full) (element-type GESClip): a #GList of clips intersecting [@start, @end) interval on @layer.
747  */
748 GList *
749 ges_layer_get_clips_in_interval (GESLayer * layer, GstClockTime start,
750     GstClockTime end)
751 {
752   GList *tmp;
753   GList *intersecting_clips = NULL;
754   GstClockTime clip_start, clip_end;
755   gboolean clip_intersects;
756   for (tmp = layer->priv->clips_start; tmp; tmp = tmp->next) {
757     clip_intersects = FALSE;
758     clip_start = ges_timeline_element_get_start (tmp->data);
759     clip_end = clip_start + ges_timeline_element_get_duration (tmp->data);
760     if (start <= clip_start && clip_start < end)
761       clip_intersects = TRUE;
762     else if (start < clip_end && clip_end <= end)
763       clip_intersects = TRUE;
764     else if (clip_start < start && clip_end > end)
765       clip_intersects = TRUE;
766
767     if (clip_intersects)
768       intersecting_clips =
769           g_list_insert_sorted (intersecting_clips, tmp->data,
770           (GCompareFunc) element_start_compare);
771   }
772   return intersecting_clips;
773 }