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