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