element: Properly handle the fact that pasting can return NULL
[platform/upstream/gst-editing-services.git] / ges / ges-timeline-element.c
1 /* gst-editing-services
2  * Copyright (C) <2013> Thibault Saunier <thibault.saunier@collabora.com>
3  *               <2013> Collabora Ltd.
4  *
5  * gst-editing-services is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * gst-editing-services is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 /**
20  * SECTION:gestimelineelement
21  * @title: GESTimelineElement
22  * @short_description: Base Class for all elements that will be in a way or
23  * another inside a GESTimeline.
24  *
25  * The GESTimelineElement base class implements the notion of timing as well
26  * as priority. A GESTimelineElement can have a parent object which will be
27  * responsible for controlling its timing properties.
28  */
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "ges-utils.h"
34 #include "ges-timeline-element.h"
35 #include "ges-extractable.h"
36 #include "ges-meta-container.h"
37 #include "ges-internal.h"
38 #include "ges-effect.h"
39
40 #include <string.h>
41 #include <gobject/gvaluecollector.h>
42
43 /* maps type name quark => count */
44 static GData *object_name_counts = NULL;
45
46 static void
47 extractable_set_asset (GESExtractable * extractable, GESAsset * asset)
48 {
49   GES_TIMELINE_ELEMENT (extractable)->asset = asset;
50 }
51
52 static void
53 ges_extractable_interface_init (GESExtractableInterface * iface)
54 {
55   iface->set_asset = extractable_set_asset;
56 }
57
58 enum
59 {
60   PROP_0,
61   PROP_PARENT,
62   PROP_TIMELINE,
63   PROP_START,
64   PROP_INPOINT,
65   PROP_DURATION,
66   PROP_MAX_DURATION,
67   PROP_PRIORITY,
68   PROP_NAME,
69   PROP_SERIALIZE,
70   PROP_LAST
71 };
72
73 enum
74 {
75   DEEP_NOTIFY,
76   LAST_SIGNAL
77 };
78
79 static guint ges_timeline_element_signals[LAST_SIGNAL] = { 0 };
80
81 static GParamSpec *properties[PROP_LAST] = { NULL, };
82
83 typedef struct
84 {
85   GObject *child;
86   gulong handler_id;
87 } ChildPropHandler;
88
89 struct _GESTimelineElementPrivate
90 {
91   gboolean serialize;
92
93   /* We keep a link between properties name and elements internally
94    * The hashtable should look like
95    * {GParamaSpec ---> child}*/
96   GHashTable *children_props;
97
98   GESTimelineElement *copied_from;
99
100   GESTimelineElementFlags flags;
101 };
102
103 typedef struct
104 {
105   GObject *child;
106   GParamSpec *arg;
107   GESTimelineElement *self;
108 } EmitDeepNotifyInIdleData;
109
110 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESTimelineElement, ges_timeline_element,
111     G_TYPE_INITIALLY_UNOWNED, G_ADD_PRIVATE (GESTimelineElement)
112     G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE, ges_extractable_interface_init)
113     G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER, NULL));
114
115 static void
116 _set_child_property (GESTimelineElement * self G_GNUC_UNUSED, GObject * child,
117     GParamSpec * pspec, GValue * value)
118 {
119   g_object_set_property (child, pspec->name, value);
120 }
121
122 static gboolean
123 _lookup_child (GESTimelineElement * self, const gchar * prop_name,
124     GObject ** child, GParamSpec ** pspec)
125 {
126   GHashTableIter iter;
127   gpointer key, value;
128   gchar **names, *name, *classename;
129   gboolean res;
130
131   classename = NULL;
132   res = FALSE;
133
134   names = g_strsplit (prop_name, "::", 2);
135   if (names[1] != NULL) {
136     classename = names[0];
137     name = names[1];
138   } else
139     name = names[0];
140
141   g_hash_table_iter_init (&iter, self->priv->children_props);
142   while (g_hash_table_iter_next (&iter, &key, &value)) {
143     if (g_strcmp0 (G_PARAM_SPEC (key)->name, name) == 0) {
144       ChildPropHandler *handler = (ChildPropHandler *) value;
145       if (classename == NULL ||
146           g_strcmp0 (G_OBJECT_TYPE_NAME (G_OBJECT (handler->child)),
147               classename) == 0 ||
148           g_strcmp0 (g_type_name (G_PARAM_SPEC (key)->owner_type),
149               classename) == 0) {
150         GST_DEBUG_OBJECT (self, "The %s property from %s has been found", name,
151             classename);
152         if (child)
153           *child = gst_object_ref (handler->child);
154
155         if (pspec)
156           *pspec = g_param_spec_ref (key);
157         res = TRUE;
158         break;
159       }
160     }
161   }
162   g_strfreev (names);
163
164   return res;
165 }
166
167 static GParamSpec **
168 default_list_children_properties (GESTimelineElement * self,
169     guint * n_properties)
170 {
171   GParamSpec **pspec, *spec;
172   GHashTableIter iter;
173   gpointer key, value;
174
175   guint i = 0;
176
177   *n_properties = g_hash_table_size (self->priv->children_props);
178   pspec = g_new (GParamSpec *, *n_properties);
179
180   g_hash_table_iter_init (&iter, self->priv->children_props);
181   while (g_hash_table_iter_next (&iter, &key, &value)) {
182     spec = G_PARAM_SPEC (key);
183     pspec[i] = g_param_spec_ref (spec);
184     i++;
185   }
186
187   return pspec;
188 }
189
190 static void
191 _get_property (GObject * object, guint property_id,
192     GValue * value, GParamSpec * pspec)
193 {
194   GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
195
196   switch (property_id) {
197     case PROP_PARENT:
198       g_value_take_object (value, self->parent);
199       break;
200     case PROP_TIMELINE:
201       g_value_take_object (value, self->timeline);
202       break;
203     case PROP_START:
204       g_value_set_uint64 (value, self->start);
205       break;
206     case PROP_INPOINT:
207       g_value_set_uint64 (value, self->inpoint);
208       break;
209     case PROP_DURATION:
210       g_value_set_uint64 (value, self->duration);
211       break;
212     case PROP_MAX_DURATION:
213       g_value_set_uint64 (value, self->maxduration);
214       break;
215     case PROP_PRIORITY:
216       g_value_set_uint (value, self->priority);
217       break;
218     case PROP_NAME:
219       g_value_take_string (value, ges_timeline_element_get_name (self));
220       break;
221     case PROP_SERIALIZE:
222       g_value_set_boolean (value, self->priv->serialize);
223       break;
224     default:
225       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
226   }
227 }
228
229 static void
230 _set_property (GObject * object, guint property_id,
231     const GValue * value, GParamSpec * pspec)
232 {
233   GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
234
235   switch (property_id) {
236     case PROP_PARENT:
237       ges_timeline_element_set_parent (self, g_value_get_object (value));
238       break;
239     case PROP_TIMELINE:
240       ges_timeline_element_set_timeline (self, g_value_get_object (value));
241       break;
242     case PROP_START:
243       ges_timeline_element_set_start (self, g_value_get_uint64 (value));
244       break;
245     case PROP_INPOINT:
246       ges_timeline_element_set_inpoint (self, g_value_get_uint64 (value));
247       break;
248     case PROP_DURATION:
249       ges_timeline_element_set_duration (self, g_value_get_uint64 (value));
250       break;
251     case PROP_PRIORITY:
252       ges_timeline_element_set_priority (self, g_value_get_uint (value));
253       break;
254     case PROP_MAX_DURATION:
255       ges_timeline_element_set_max_duration (self, g_value_get_uint64 (value));
256       break;
257     case PROP_NAME:
258       ges_timeline_element_set_name (self, g_value_get_string (value));
259       break;
260     case PROP_SERIALIZE:
261       self->priv->serialize = g_value_get_boolean (value);
262       break;
263     default:
264       G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
265   }
266 }
267
268 static void
269 ges_timeline_element_dispose (GObject * object)
270 {
271   GESTimelineElement *self = GES_TIMELINE_ELEMENT (object);
272
273   if (self->priv->children_props) {
274     g_hash_table_unref (self->priv->children_props);
275     self->priv->children_props = NULL;
276   }
277
278   g_clear_object (&self->priv->copied_from);
279
280   G_OBJECT_CLASS (ges_timeline_element_parent_class)->dispose (object);
281 }
282
283 static void
284 ges_timeline_element_finalize (GObject * self)
285 {
286   GESTimelineElement *tle = GES_TIMELINE_ELEMENT (self);
287
288   g_free (tle->name);
289
290   G_OBJECT_CLASS (ges_timeline_element_parent_class)->finalize (self);
291 }
292
293 static void
294 _child_prop_handler_free (ChildPropHandler * handler)
295 {
296   g_object_freeze_notify (handler->child);
297   if (handler->handler_id)
298     g_signal_handler_disconnect (handler->child, handler->handler_id);
299   g_object_thaw_notify (handler->child);
300   gst_object_unref (handler->child);
301   g_slice_free (ChildPropHandler, handler);
302 }
303
304 static void
305 ges_timeline_element_init (GESTimelineElement * self)
306 {
307   self->priv = ges_timeline_element_get_instance_private (self);
308
309   self->priv->serialize = TRUE;
310
311   self->priv->children_props =
312       g_hash_table_new_full ((GHashFunc) ges_pspec_hash, ges_pspec_equal,
313       (GDestroyNotify) g_param_spec_unref,
314       (GDestroyNotify) _child_prop_handler_free);
315 }
316
317 static void
318 ges_timeline_element_class_init (GESTimelineElementClass * klass)
319 {
320   GObjectClass *object_class = G_OBJECT_CLASS (klass);
321
322   object_class->get_property = _get_property;
323   object_class->set_property = _set_property;
324
325   /**
326    * GESTimelineElement:parent:
327    *
328    * The parent container of the object
329    */
330   properties[PROP_PARENT] =
331       g_param_spec_object ("parent", "Parent",
332       "The parent container of the object", GES_TYPE_TIMELINE_ELEMENT,
333       G_PARAM_READWRITE);
334
335   /**
336    * GESTimelineElement:timeline:
337    *
338    * The timeline in which @element is
339    */
340   properties[PROP_TIMELINE] =
341       g_param_spec_object ("timeline", "Timeline",
342       "The timeline the object is in", GES_TYPE_TIMELINE, G_PARAM_READWRITE);
343
344   /**
345    * GESTimelineElement:start:
346    *
347    * The position of the object in its container (in nanoseconds).
348    */
349   properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
350       "The position in the container", 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
351
352   /**
353    * GESTimelineElement:in-point:
354    *
355    * The in-point at which this #GESTimelineElement will start outputting data
356    * from its contents (in nanoseconds).
357    *
358    * Ex : an in-point of 5 seconds means that the first outputted buffer will
359    * be the one located 5 seconds in the controlled resource.
360    */
361   properties[PROP_INPOINT] =
362       g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
363       G_MAXUINT64, 0, G_PARAM_READWRITE);
364
365   /**
366    * GESTimelineElement:duration:
367    *
368    * The duration (in nanoseconds) which will be used in the container
369    */
370   properties[PROP_DURATION] =
371       g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
372       G_MAXUINT64, GST_CLOCK_TIME_NONE, G_PARAM_READWRITE);
373
374   /**
375    * GESTimelineElement:max-duration:
376    *
377    * The maximum duration (in nanoseconds) of the #GESTimelineElement.
378    */
379   properties[PROP_MAX_DURATION] =
380       g_param_spec_uint64 ("max-duration", "Maximum duration",
381       "The maximum duration of the object", 0, G_MAXUINT64, GST_CLOCK_TIME_NONE,
382       G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
383
384   /**
385    * GESTimelineElement:priority:
386    *
387    * The priority of the object.
388    *
389    * Setting GESTimelineElement priorities is deprecated
390    * as all priority management is done by GES itself now.
391    */
392   properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
393       "The priority of the object", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
394
395   /**
396    * GESTimelineElement:name:
397    *
398    * The name of the object
399    */
400   properties[PROP_NAME] =
401       g_param_spec_string ("name", "Name", "The name of the timeline object",
402       NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
403
404   /**
405    * GESTimelineElement:serialize:
406    *
407    * Whether the element should be serialized.
408    */
409   properties[PROP_SERIALIZE] = g_param_spec_boolean ("serialize", "Serialize",
410       "Whether the element should be serialized", TRUE,
411       G_PARAM_READWRITE | GES_PARAM_NO_SERIALIZATION);
412
413   g_object_class_install_properties (object_class, PROP_LAST, properties);
414
415   /**
416    * GESTimelineElement::deep-notify:
417    * @timeline_element: a #GESTtimelineElement
418    * @prop_object: the object that originated the signal
419    * @prop: the property that changed
420    *
421    * The deep notify signal is used to be notified of property changes of all
422    * the childs of @timeline_element
423    */
424   ges_timeline_element_signals[DEEP_NOTIFY] =
425       g_signal_new ("deep-notify", G_TYPE_FROM_CLASS (klass),
426       G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED |
427       G_SIGNAL_NO_HOOKS, 0, NULL, NULL, g_cclosure_marshal_generic,
428       G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_PARAM);
429
430   object_class->dispose = ges_timeline_element_dispose;
431   object_class->finalize = ges_timeline_element_finalize;
432
433   klass->set_parent = NULL;
434   klass->set_start = NULL;
435   klass->set_inpoint = NULL;
436   klass->set_duration = NULL;
437   klass->set_max_duration = NULL;
438   klass->set_priority = NULL;
439
440   klass->ripple = NULL;
441   klass->ripple_end = NULL;
442   klass->roll_start = NULL;
443   klass->roll_end = NULL;
444   klass->trim = NULL;
445
446   klass->list_children_properties = default_list_children_properties;
447   klass->lookup_child = _lookup_child;
448   klass->set_child_property = _set_child_property;
449 }
450
451 static void
452 _set_name (GESTimelineElement * self, const gchar * wanted_name)
453 {
454   const gchar *type_name;
455   gchar *lowcase_type;
456   gint count;
457   GQuark q;
458   guint i, l;
459   gchar *name = NULL;
460
461   if (!object_name_counts) {
462     g_datalist_init (&object_name_counts);
463   }
464
465   q = g_type_qname (G_OBJECT_TYPE (self));
466   count = GPOINTER_TO_INT (g_datalist_id_get_data (&object_name_counts, q));
467
468   /* GstFooSink -> foosink<N> */
469   type_name = g_quark_to_string (q);
470   if (strncmp (type_name, "GES", 3) == 0)
471     type_name += 3;
472
473   lowcase_type = g_strdup (type_name);
474   l = strlen (lowcase_type);
475   for (i = 0; i < l; i++)
476     lowcase_type[i] = g_ascii_tolower (lowcase_type[i]);
477
478   if (wanted_name == NULL) {
479     /* give the 20th "uriclip" element and the first "uriclip2" (if needed in the future)
480      * different names */
481     l = strlen (type_name);
482     if (l > 0 && g_ascii_isdigit (type_name[l - 1])) {
483       name = g_strdup_printf ("%s-%d", lowcase_type, count++);
484     } else {
485       name = g_strdup_printf ("%s%d", lowcase_type, count++);
486     }
487   } else {
488     /* If the wanted name uses the same 'namespace' as default, make
489      * sure it does not badly interfere with our counting system */
490
491     if (g_str_has_prefix (wanted_name, lowcase_type)) {
492       guint64 tmpcount =
493           g_ascii_strtoull (&wanted_name[strlen (lowcase_type)], NULL, 10);
494
495       if (tmpcount > count) {
496         count = tmpcount + 1;
497         GST_DEBUG_OBJECT (self, "Using same naming %s but updated count to %i",
498             wanted_name, count);
499       } else if (tmpcount < count) {
500         name = g_strdup_printf ("%s%d", lowcase_type, count);
501         count++;
502         GST_DEBUG_OBJECT (self, "Name %s already allocated, giving: %s instead"
503             " New count is %i", wanted_name, name, count);
504       } else {
505         count++;
506         GST_DEBUG_OBJECT (self, "Perfect name, just bumping object count");
507       }
508     }
509
510     if (name == NULL)
511       name = g_strdup (wanted_name);
512   }
513
514   g_free (lowcase_type);
515   g_datalist_id_set_data (&object_name_counts, q, GINT_TO_POINTER (count));
516
517   g_free (self->name);
518   self->name = name;
519 }
520
521 /*********************************************
522  *            API implementation             *
523  *********************************************/
524
525 /**
526  * ges_timeline_element_set_parent:
527  * @self: a #GESTimelineElement
528  * @parent: new parent of self
529  *
530  * Sets the parent of @self to @parent. The parents needs to already
531  * own a hard reference on @self.
532  *
533  * Returns: %TRUE if @parent could be set or %FALSE when @self
534  * already had a parent or @self and @parent are the same.
535  */
536 gboolean
537 ges_timeline_element_set_parent (GESTimelineElement * self,
538     GESTimelineElement * parent)
539 {
540   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
541   g_return_val_if_fail (parent == NULL
542       || GES_IS_TIMELINE_ELEMENT (parent), FALSE);
543
544   if (self == parent) {
545     GST_INFO_OBJECT (self, "Trying to add %p in itself, not a good idea!",
546         self);
547     gst_object_ref_sink (self);
548     gst_object_unref (self);
549     return FALSE;
550   }
551
552   GST_DEBUG_OBJECT (self, "set parent to %" GST_PTR_FORMAT, parent);
553
554   if (self->parent != NULL && parent != NULL)
555     goto had_parent;
556
557   if (GES_TIMELINE_ELEMENT_GET_CLASS (self)->set_parent) {
558     if (!GES_TIMELINE_ELEMENT_GET_CLASS (self)->set_parent (self, parent))
559       return FALSE;
560   }
561
562   self->parent = parent;
563
564   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PARENT]);
565   return TRUE;
566
567   /* ERROR handling */
568 had_parent:
569   {
570     GST_WARNING_OBJECT (self, "set parent failed, object already had a parent");
571     gst_object_ref_sink (self);
572     gst_object_unref (self);
573     return FALSE;
574   }
575 }
576
577 /**
578  * ges_timeline_element_get_parent:
579  * @self: a #GESTimelineElement
580  *
581  * Returns the parent of @self. This function increases the refcount
582  * of the parent object so you should gst_object_unref() it after usage.
583  *
584  * Returns: (transfer full) (nullable): parent of @self, this can be %NULL if
585  * @self has no parent. unref after usage.
586  */
587 GESTimelineElement *
588 ges_timeline_element_get_parent (GESTimelineElement * self)
589 {
590   GESTimelineElement *result = NULL;
591
592   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), NULL);
593
594   result = self->parent;
595   if (G_LIKELY (result))
596     gst_object_ref (result);
597
598   return result;
599 }
600
601 /**
602  * ges_timeline_element_set_timeline:
603  * @self: a #GESTimelineElement
604  * @timeline: The #GESTimeline @self is in
605  *
606  * Sets the timeline of @self to @timeline.
607  *
608  * Returns: %TRUE if @timeline could be set or %FALSE when @timeline
609  * already had a timeline.
610  */
611 gboolean
612 ges_timeline_element_set_timeline (GESTimelineElement * self,
613     GESTimeline * timeline)
614 {
615   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
616   g_return_val_if_fail (timeline == NULL || GES_IS_TIMELINE (timeline), FALSE);
617
618   GST_DEBUG_OBJECT (self, "set timeline to %" GST_PTR_FORMAT, timeline);
619
620   if (timeline != NULL && G_UNLIKELY (self->timeline != NULL))
621     goto had_timeline;
622
623   if (timeline == NULL) {
624     if (self->timeline) {
625       if (!timeline_remove_element (self->timeline, self)) {
626         GST_INFO_OBJECT (self, "Could not remove from"
627             " currently set timeline %" GST_PTR_FORMAT, self->timeline);
628         return FALSE;
629       }
630     }
631   } else {
632     if (!timeline_add_element (timeline, self)) {
633       GST_INFO_OBJECT (self, "Could not add to timeline %" GST_PTR_FORMAT,
634           self);
635       return FALSE;
636     }
637   }
638
639   self->timeline = timeline;
640
641   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_TIMELINE]);
642   return TRUE;
643
644   /* ERROR handling */
645 had_timeline:
646   {
647     GST_DEBUG_OBJECT (self, "set timeline failed, object already had a "
648         "timeline");
649     return FALSE;
650   }
651 }
652
653 /**
654  * ges_timeline_element_get_timeline:
655  * @self: a #GESTimelineElement
656  *
657  * Returns the timeline of @self. This function increases the refcount
658  * of the timeline so you should gst_object_unref() it after usage.
659  *
660  * Returns: (transfer full) (nullable): timeline of @self, this can be %NULL if
661  * @self has no timeline. unref after usage.
662  */
663 GESTimeline *
664 ges_timeline_element_get_timeline (GESTimelineElement * self)
665 {
666   GESTimeline *result = NULL;
667
668   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), NULL);
669
670   result = self->timeline;
671   if (G_LIKELY (result))
672     gst_object_ref (result);
673
674   return result;
675 }
676
677 /**
678  * ges_timeline_element_set_start:
679  * @self: a #GESTimelineElement
680  * @start: the position in #GstClockTime
681  *
682  * Set the position of the object in its containing layer.
683  *
684  * Note that if the snapping-distance property of the timeline containing
685  * @self is set, @self will properly snap to the edges around @start.
686  *
687  * Returns: %TRUE if @start could be set.
688  */
689 gboolean
690 ges_timeline_element_set_start (GESTimelineElement * self, GstClockTime start)
691 {
692   GESTimelineElementClass *klass;
693   GESTimelineElement *toplevel_container, *parent;
694
695   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
696
697   if (self->start == start)
698     return TRUE;
699
700   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
701
702   GST_DEBUG_OBJECT (self, "current start: %" GST_TIME_FORMAT
703       " new start: %" GST_TIME_FORMAT,
704       GST_TIME_ARGS (GES_TIMELINE_ELEMENT_START (self)), GST_TIME_ARGS (start));
705
706   toplevel_container = ges_timeline_element_get_toplevel_parent (self);
707   parent = self->parent;
708
709   /* FIXME This should not belong to GESTimelineElement */
710   if (toplevel_container &&
711       ((gint64) (_START (toplevel_container) + start - _START (self))) < 0 &&
712       parent
713       && GES_CONTAINER (parent)->children_control_mode == GES_CHILDREN_UPDATE) {
714     GST_INFO_OBJECT (self,
715         "Can not move the object as it would imply its "
716         "container to have a negative start value");
717
718     gst_object_unref (toplevel_container);
719     return FALSE;
720   }
721
722   gst_object_unref (toplevel_container);
723   if (klass->set_start) {
724     gboolean res = klass->set_start (self, start);
725     if (res) {
726       self->start = start;
727       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_START]);
728     }
729
730     GST_DEBUG_OBJECT (self, "New start: %" GST_TIME_FORMAT,
731         GST_TIME_ARGS (GES_TIMELINE_ELEMENT_START (self)));
732     return res;
733   }
734
735   GST_WARNING_OBJECT (self, "No set_start virtual method implementation"
736       " on class %s. Can not set start %" GST_TIME_FORMAT,
737       G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (start));
738   return FALSE;
739 }
740
741 /**
742  * ges_timeline_element_set_inpoint:
743  * @self: a #GESTimelineElement
744  * @inpoint: the in-point in #GstClockTime
745  *
746  * Set the in-point, that is the moment at which the @self will start
747  * outputting data from its contents.
748  *
749  * Returns: %TRUE if @inpoint could be set.
750  */
751 gboolean
752 ges_timeline_element_set_inpoint (GESTimelineElement * self,
753     GstClockTime inpoint)
754 {
755   GESTimelineElementClass *klass;
756
757   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
758
759   GST_DEBUG_OBJECT (self, "current inpoint: %" GST_TIME_FORMAT
760       " new inpoint: %" GST_TIME_FORMAT, GST_TIME_ARGS (inpoint),
761       GST_TIME_ARGS (GES_TIMELINE_ELEMENT_INPOINT (self)));
762
763   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
764
765   if (klass->set_inpoint) {
766     gboolean res = klass->set_inpoint (self, inpoint);
767     if (res) {
768       self->inpoint = inpoint;
769       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_INPOINT]);
770     }
771
772     return res;
773   }
774
775   GST_DEBUG_OBJECT (self, "No set_inpoint virtual method implementation"
776       " on class %s. Can not set inpoint %" GST_TIME_FORMAT,
777       G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (inpoint));
778
779   return FALSE;
780 }
781
782 /**
783  * ges_timeline_element_set_max_duration:
784  * @self: a #GESTimelineElement
785  * @maxduration: the maximum duration in #GstClockTime
786  *
787  * Set the maximun duration of the object
788  *
789  * Returns: %TRUE if @maxduration could be set.
790  */
791 gboolean
792 ges_timeline_element_set_max_duration (GESTimelineElement * self,
793     GstClockTime maxduration)
794 {
795   GESTimelineElementClass *klass;
796
797   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
798
799   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
800
801   GST_DEBUG_OBJECT (self, "current duration: %" GST_TIME_FORMAT
802       " new duration: %" GST_TIME_FORMAT,
803       GST_TIME_ARGS (GES_TIMELINE_ELEMENT_MAX_DURATION (self)),
804       GST_TIME_ARGS (maxduration));
805
806   if (klass->set_max_duration) {
807     if (klass->set_max_duration (self, maxduration) == FALSE)
808       return FALSE;
809   }
810
811   self->maxduration = maxduration;
812   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MAX_DURATION]);
813   return TRUE;
814 }
815
816 /**
817  * ges_timeline_element_set_duration:
818  * @self: a #GESTimelineElement
819  * @duration: the duration in #GstClockTime
820  *
821  * Set the duration of the object
822  *
823  * Note that if the timeline snap-distance property of the timeline containing
824  * @self is set, @self will properly snap to its neighboors.
825  *
826  * Returns: %TRUE if @duration could be set.
827  */
828 gboolean
829 ges_timeline_element_set_duration (GESTimelineElement * self,
830     GstClockTime duration)
831 {
832   GESTimelineElementClass *klass;
833
834   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
835
836   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
837
838   GST_DEBUG_OBJECT (self, "current duration: %" GST_TIME_FORMAT
839       " new duration: %" GST_TIME_FORMAT,
840       GST_TIME_ARGS (GES_TIMELINE_ELEMENT_DURATION (self)),
841       GST_TIME_ARGS (duration));
842
843   if (klass->set_duration) {
844     gboolean res = klass->set_duration (self, duration);
845     if (res) {
846       self->duration = duration;
847       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DURATION]);
848     }
849
850     return res;
851   }
852
853   GST_WARNING_OBJECT (self, "No set_duration virtual method implementation"
854       " on class %s. Can not set duration %" GST_TIME_FORMAT,
855       G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (duration));
856   return FALSE;
857 }
858
859 /**
860  * ges_timeline_element_get_start:
861  * @self: a #GESTimelineElement
862  *
863  * Returns: The @start of @self
864  */
865 GstClockTime
866 ges_timeline_element_get_start (GESTimelineElement * self)
867 {
868   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), GST_CLOCK_TIME_NONE);
869
870   return self->start;
871 }
872
873 /**
874  * ges_timeline_element_get_inpoint:
875  * @self: a #GESTimelineElement
876  *
877  * Returns: The @inpoint of @self
878  */
879 GstClockTime
880 ges_timeline_element_get_inpoint (GESTimelineElement * self)
881 {
882   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), GST_CLOCK_TIME_NONE);
883
884   return self->inpoint;
885 }
886
887 /**
888  * ges_timeline_element_get_duration:
889  * @self: a #GESTimelineElement
890  *
891  * Returns: The @duration of @self
892  */
893 GstClockTime
894 ges_timeline_element_get_duration (GESTimelineElement * self)
895 {
896   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), GST_CLOCK_TIME_NONE);
897
898   return self->duration;
899 }
900
901 /**
902  * ges_timeline_element_get_max_duration:
903  * @self: a #GESTimelineElement
904  *
905  * Returns: The @maxduration of @self
906  */
907 GstClockTime
908 ges_timeline_element_get_max_duration (GESTimelineElement * self)
909 {
910   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), GST_CLOCK_TIME_NONE);
911
912   return self->maxduration;
913 }
914
915 /**
916  * ges_timeline_element_get_priority:
917  * @self: a #GESTimelineElement
918  *
919  * Returns: The @priority of @self
920  */
921 guint32
922 ges_timeline_element_get_priority (GESTimelineElement * self)
923 {
924   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), 0);
925
926   return self->priority;
927 }
928
929 /**
930  * ges_timeline_element_set_priority:
931  * @self: a #GESTimelineElement
932  * @priority: the priority
933  *
934  * Sets the priority of the object within the containing layer
935  *
936  * Deprecated: All priority management is done by GES itself now.
937  * To set #GESEffect priorities #ges_clip_set_top_effect_index should
938  * be used.
939  *
940  * Returns: %TRUE if @priority could be set.
941  */
942 gboolean
943 ges_timeline_element_set_priority (GESTimelineElement * self, guint32 priority)
944 {
945   GESTimelineElementClass *klass;
946
947   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
948
949   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
950
951   GST_DEBUG_OBJECT (self, "current priority: %d new priority: %d",
952       self->priority, priority);
953
954   if (klass->set_priority) {
955     gboolean res = klass->set_priority (self, priority);
956     if (res) {
957       self->priority = priority;
958       g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PRIORITY]);
959     }
960
961     return res;
962   }
963
964   GST_WARNING_OBJECT (self, "No set_priority virtual method implementation"
965       " on class %s. Can not set priority %d", G_OBJECT_CLASS_NAME (klass),
966       priority);
967   return FALSE;
968 }
969
970 /**
971  * ges_timeline_element_ripple:
972  * @self: The #GESTimelineElement to ripple.
973  * @start: The new start of @self in ripple mode.
974  *
975  * Edits @self in ripple mode. It allows you to modify the
976  * start of @self and move the following neighbours accordingly.
977  * This will change the overall timeline duration.
978  *
979  * Returns: %TRUE if the self as been rippled properly, %FALSE if an error
980  * occured
981  */
982 gboolean
983 ges_timeline_element_ripple (GESTimelineElement * self, GstClockTime start)
984 {
985   GESTimelineElementClass *klass;
986
987   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
988
989   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
990
991   if (klass->ripple)
992     return klass->ripple (self, start);
993
994   GST_WARNING_OBJECT (self, "No ripple virtual method implementation"
995       " on class %s. Can not ripple to %" GST_TIME_FORMAT,
996       G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (start));
997
998   return FALSE;
999 }
1000
1001 /**
1002  * ges_timeline_element_ripple_end:
1003  * @self: The #GESTimelineElement to ripple.
1004  * @end: The new end (start + duration) of @self in ripple mode. It will
1005  *       basically only change the duration of @self.
1006  *
1007  * Edits @self in ripple mode. It allows you to modify the
1008  * duration of a @self and move the following neighbours accordingly.
1009  * This will change the overall timeline duration.
1010  *
1011  * Returns: %TRUE if the self as been rippled properly, %FALSE if an error
1012  * occured
1013  */
1014 gboolean
1015 ges_timeline_element_ripple_end (GESTimelineElement * self, GstClockTime end)
1016 {
1017   GESTimelineElementClass *klass;
1018
1019   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
1020
1021   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
1022
1023   if (klass->ripple_end) {
1024     return klass->ripple_end (self, end);
1025   }
1026
1027   GST_WARNING_OBJECT (self, "No ripple virtual method implementation"
1028       " on class %s. Can not ripple end to %" GST_TIME_FORMAT,
1029       G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (end));
1030
1031   return FALSE;
1032 }
1033
1034 /**
1035  * ges_timeline_element_roll_start:
1036  * @self: The #GESTimelineElement to roll
1037  * @start: The new start of @self in roll mode, it will also adapat
1038  * the in-point of @self according
1039  *
1040  * Edits @self in roll mode. It allows you to modify the
1041  * start and inpoint of a @self and "resize" (basicly change the duration
1042  * in this case) of the previous neighbours accordingly.
1043  * This will not change the overall timeline duration.
1044  *
1045  * Returns: %TRUE if the self as been roll properly, %FALSE if an error
1046  * occured
1047  */
1048 gboolean
1049 ges_timeline_element_roll_start (GESTimelineElement * self, GstClockTime start)
1050 {
1051   GESTimelineElementClass *klass;
1052
1053   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
1054
1055   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
1056
1057   if (klass->roll_start) {
1058     return klass->roll_start (self, start);
1059   }
1060
1061   GST_WARNING_OBJECT (self, "No ripple virtual method implementation"
1062       " on class %s. Can not roll to %" GST_TIME_FORMAT,
1063       G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (start));
1064
1065   return FALSE;
1066 }
1067
1068 /**
1069  * ges_timeline_element_roll_end:
1070  * @self: The #GESTimelineElement to roll.
1071  * @end: The new end (start + duration) of @self in roll mode
1072  *
1073  * Edits @self in roll mode. It allows you to modify the
1074  * duration of a @self and trim (basicly change the start + inpoint
1075  * in this case) the following neighbours accordingly.
1076  * This will not change the overall timeline duration.
1077  *
1078  * Returns: %TRUE if the self as been rolled properly, %FALSE if an error
1079  * occured
1080  */
1081 gboolean
1082 ges_timeline_element_roll_end (GESTimelineElement * self, GstClockTime end)
1083 {
1084   GESTimelineElementClass *klass;
1085
1086   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
1087
1088   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
1089
1090   if (klass->roll_end)
1091     return klass->roll_end (self, end);
1092
1093   GST_WARNING_OBJECT (self, "No ripple virtual method implementation"
1094       " on class %s. Can not roll end to %" GST_TIME_FORMAT,
1095       G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (end));
1096
1097   return FALSE;
1098 }
1099
1100 /**
1101  * ges_timeline_element_trim:
1102  * @self: The #GESTimelineElement to trim.
1103  * @start: The new start of @self in trim mode, will adapt the inpoint
1104  * of @self accordingly
1105  *
1106  * Edits @self in trim mode. It allows you to modify the
1107  * inpoint and start of @self.
1108  * This will not change the overall timeline duration.
1109  *
1110  * Note that to trim the end of an self you can just set its duration. The same way
1111  * as this method, it will take into account the snapping-distance property of the
1112  * timeline in which @self is.
1113  *
1114  * Returns: %TRUE if the self as been trimmed properly, %FALSE if an error
1115  * occured
1116  */
1117 gboolean
1118 ges_timeline_element_trim (GESTimelineElement * self, GstClockTime start)
1119 {
1120   GESTimelineElementClass *klass;
1121
1122   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
1123
1124   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
1125
1126   if (klass->trim)
1127     return klass->trim (self, start);
1128
1129   GST_WARNING_OBJECT (self, "No ripple virtual method implementation"
1130       " on class %s. Can not trim to %" GST_TIME_FORMAT,
1131       G_OBJECT_CLASS_NAME (klass), GST_TIME_ARGS (start));
1132
1133   return FALSE;
1134 }
1135
1136 /**
1137  * ges_timeline_element_copy:
1138  * @self: The #GESTimelineElement to copy
1139  * @deep: whether we want to create the elements @self contains or not
1140  *
1141  * Copies @self
1142  *
1143  * Returns: (transfer floating): The newly create #GESTimelineElement, copied from @self
1144  */
1145 GESTimelineElement *
1146 ges_timeline_element_copy (GESTimelineElement * self, gboolean deep)
1147 {
1148   GESAsset *asset;
1149   GParameter *params;
1150   GParamSpec **specs;
1151   GESTimelineElementClass *klass;
1152   guint n, n_specs, n_params;
1153
1154   GESTimelineElement *ret = NULL;
1155
1156   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
1157
1158   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
1159
1160   specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (self), &n_specs);
1161   params = g_new0 (GParameter, n_specs);
1162   n_params = 0;
1163
1164   for (n = 0; n < n_specs; ++n) {
1165     /* We do not want the timeline or the name to be copied */
1166     if (g_strcmp0 (specs[n]->name, "parent") &&
1167         g_strcmp0 (specs[n]->name, "timeline") &&
1168         g_strcmp0 (specs[n]->name, "name") &&
1169         (specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) {
1170       params[n_params].name = g_intern_string (specs[n]->name);
1171       g_value_init (&params[n_params].value, specs[n]->value_type);
1172       g_object_get_property (G_OBJECT (self), specs[n]->name,
1173           &params[n_params].value);
1174       ++n_params;
1175     }
1176   }
1177
1178 #if GLIB_CHECK_VERSION(2, 53, 1)
1179   {
1180     gint i;
1181     GValue *values;
1182     const gchar **names;
1183     values = g_malloc0 (sizeof (GValue) * n_specs);
1184     names = g_malloc0 (sizeof (gchar *) * n_specs);
1185
1186     for (i = 0; i < n_params; i++) {
1187       values[i] = params[i].value;
1188       names[i] = params[i].name;
1189     }
1190
1191     ret =
1192         GES_TIMELINE_ELEMENT (g_object_new_with_properties (G_OBJECT_TYPE
1193             (self), n_params, names, values));
1194     g_free (names);
1195     g_free (values);
1196   }
1197 #else
1198   ret = g_object_newv (G_OBJECT_TYPE (self), n_params, params);
1199 #endif
1200
1201   while (n_params--)
1202     g_value_unset (&params[n_params].value);
1203
1204   g_free (specs);
1205   g_free (params);
1206
1207
1208   asset = ges_extractable_get_asset (GES_EXTRACTABLE (self));
1209   if (asset)
1210     ges_extractable_set_asset (GES_EXTRACTABLE (ret), asset);
1211   if (deep) {
1212     if (klass->deep_copy)
1213       klass->deep_copy (self, ret);
1214     else
1215       GST_WARNING_OBJECT (self, "No deep_copy virtual method implementation"
1216           " on class %s. Can not finish the copy", G_OBJECT_CLASS_NAME (klass));
1217   }
1218
1219   if (deep) {
1220     ret->priv->copied_from = gst_object_ref (self);
1221   }
1222
1223   return ret;
1224 }
1225
1226 /**
1227  * ges_timeline_element_get_toplevel_parent:
1228  * @self: The #GESTimelineElement to get the toplevel parent from
1229  *
1230  * Gets the toplevel #GESTimelineElement controlling @self
1231  *
1232  * Returns: (transfer full): The toplevel controlling parent of @self
1233  */
1234 GESTimelineElement *
1235 ges_timeline_element_get_toplevel_parent (GESTimelineElement * self)
1236 {
1237   GESTimelineElement *toplevel = self;
1238
1239   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), NULL);
1240
1241   while (GES_TIMELINE_ELEMENT_PARENT (toplevel))
1242     toplevel = GES_TIMELINE_ELEMENT_PARENT (toplevel);
1243
1244   return gst_object_ref (toplevel);
1245 }
1246
1247 /**
1248  * ges_timeline_element_get_name:
1249  * @self: a #GESTimelineElement
1250  *
1251  * Returns a copy of the name of @self.
1252  * Caller should g_free() the return value after usage.
1253  *
1254  * Returns: (transfer full): The name of @self
1255  */
1256 gchar *
1257 ges_timeline_element_get_name (GESTimelineElement * self)
1258 {
1259   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), NULL);
1260
1261   return g_strdup (self->name);
1262 }
1263
1264 /**
1265  * ges_timeline_element_set_name:
1266  * @self: a #GESTimelineElement
1267  * @name: (allow-none): The name @self should take (if avalaible<)
1268  *
1269  * Sets the name of object, or gives @self a guaranteed unique name (if name is NULL).
1270  * This function makes a copy of the provided name, so the caller retains ownership
1271  * of the name it sent.
1272  */
1273 gboolean
1274 ges_timeline_element_set_name (GESTimelineElement * self, const gchar * name)
1275 {
1276   gboolean result = TRUE, readd_to_timeline = FALSE;
1277
1278   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
1279
1280   if (name != NULL && !g_strcmp0 (name, self->name)) {
1281     GST_DEBUG_OBJECT (self, "Same name!");
1282     return TRUE;
1283   }
1284
1285   /* parented objects cannot be renamed */
1286   if (self->timeline != NULL && name) {
1287     GESTimelineElement *tmp = ges_timeline_get_element (self->timeline, name);
1288
1289     if (tmp) {
1290       gst_object_unref (tmp);
1291       goto had_timeline;
1292     }
1293
1294     timeline_remove_element (self->timeline, self);
1295     readd_to_timeline = TRUE;
1296   }
1297
1298   _set_name (self, name);
1299
1300   if (readd_to_timeline)
1301     timeline_add_element (self->timeline, self);
1302
1303   return result;
1304
1305   /* error */
1306 had_timeline:
1307   {
1308     GST_WARNING ("Object %s already in a timeline can't be renamed to %s",
1309         self->name, name);
1310     return FALSE;
1311   }
1312 }
1313
1314 static gboolean
1315 emit_deep_notify_in_idle (EmitDeepNotifyInIdleData * data)
1316 {
1317   g_signal_emit (data->self, ges_timeline_element_signals[DEEP_NOTIFY], 0,
1318       data->child, data->arg);
1319
1320   gst_object_unref (data->child);
1321   g_param_spec_unref (data->arg);
1322   gst_object_unref (data->self);
1323   g_slice_free (EmitDeepNotifyInIdleData, data);
1324
1325   return FALSE;
1326 }
1327
1328 static void
1329 child_prop_changed_cb (GObject * child, GParamSpec * arg
1330     G_GNUC_UNUSED, GESTimelineElement * self)
1331 {
1332   EmitDeepNotifyInIdleData *data;
1333
1334   /* Emit "deep-notify" right away if in main thread */
1335   if (g_main_context_acquire (g_main_context_default ())) {
1336     g_main_context_release (g_main_context_default ());
1337     g_signal_emit (self, ges_timeline_element_signals[DEEP_NOTIFY], 0,
1338         child, arg);
1339     return;
1340   }
1341
1342   data = g_slice_new (EmitDeepNotifyInIdleData);
1343
1344   data->child = gst_object_ref (child);
1345   data->arg = g_param_spec_ref (arg);
1346   data->self = gst_object_ref (self);
1347
1348   g_idle_add ((GSourceFunc) emit_deep_notify_in_idle, data);
1349 }
1350
1351 gboolean
1352 ges_timeline_element_add_child_property (GESTimelineElement * self,
1353     GParamSpec * pspec, GObject * child)
1354 {
1355   gchar *signame;
1356   ChildPropHandler *handler;
1357
1358   if (g_hash_table_contains (self->priv->children_props, pspec)) {
1359     GST_INFO_OBJECT (self, "Child property already exists: %s", pspec->name);
1360     return FALSE;
1361   }
1362
1363   GST_DEBUG_OBJECT (self, "Adding child property: %" GST_PTR_FORMAT "::%s",
1364       child, pspec->name);
1365
1366   signame = g_strconcat ("notify::", pspec->name, NULL);
1367   handler = (ChildPropHandler *) g_slice_new0 (ChildPropHandler);
1368   handler->child = gst_object_ref (child);
1369   handler->handler_id =
1370       g_signal_connect (child, signame, G_CALLBACK (child_prop_changed_cb),
1371       self);
1372   g_hash_table_insert (self->priv->children_props, g_param_spec_ref (pspec),
1373       handler);
1374
1375   g_free (signame);
1376
1377   return TRUE;
1378 }
1379
1380 /**
1381  * ges_timeline_element_get_child_property_by_pspec:
1382  * @self: a #GESTrackElement
1383  * @pspec: The #GParamSpec that specifies the property you want to get
1384  * @value: (out): return location for the value
1385  *
1386  * Gets a property of a child of @self.
1387  */
1388 void
1389 ges_timeline_element_get_child_property_by_pspec (GESTimelineElement * self,
1390     GParamSpec * pspec, GValue * value)
1391 {
1392   ChildPropHandler *handler;
1393
1394   g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
1395
1396   handler = g_hash_table_lookup (self->priv->children_props, pspec);
1397   if (!handler)
1398     goto not_found;
1399
1400   g_object_get_property (G_OBJECT (handler->child), pspec->name, value);
1401
1402   return;
1403
1404 not_found:
1405   {
1406     GST_ERROR_OBJECT (self, "The %s property doesn't exist", pspec->name);
1407     return;
1408   }
1409 }
1410
1411 /**
1412  * ges_timeline_element_set_child_property_by_pspec:
1413  * @self: a #GESTimelineElement
1414  * @pspec: The #GParamSpec that specifies the property you want to set
1415  * @value: the value
1416  *
1417  * Sets a property of a child of @self.
1418  */
1419 void
1420 ges_timeline_element_set_child_property_by_pspec (GESTimelineElement * self,
1421     GParamSpec * pspec, const GValue * value)
1422 {
1423   ChildPropHandler *handler;
1424   GESTimelineElementClass *klass;
1425
1426   g_return_if_fail (GES_IS_TRACK_ELEMENT (self));
1427
1428   handler = g_hash_table_lookup (self->priv->children_props, pspec);
1429
1430   if (!handler)
1431     goto not_found;
1432
1433   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
1434   g_assert (klass->set_child_property);
1435   klass->set_child_property (self, handler->child, pspec, (GValue *) value);
1436
1437   return;
1438
1439 not_found:
1440   {
1441     GST_ERROR ("The %s property doesn't exist", pspec->name);
1442     return;
1443   }
1444 }
1445
1446 /**
1447  * ges_timeline_element_set_child_property:
1448  * @self: The origin #GESTimelineElement
1449  * @property_name: The name of the property
1450  * @value: the value
1451  *
1452  * Sets a property of a child of @self
1453  *
1454  * Note that #ges_timeline_element_set_child_property is really
1455  * intended for language bindings, #ges_timeline_element_set_child_properties
1456  * is much more convenient for C programming.
1457  *
1458  * Returns: %TRUE if the property was set, %FALSE otherwize
1459  */
1460 gboolean
1461 ges_timeline_element_set_child_property (GESTimelineElement * self,
1462     const gchar * property_name, const GValue * value)
1463 {
1464   GParamSpec *pspec;
1465   GESTimelineElementClass *klass;
1466   GObject *child;
1467
1468   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
1469
1470   if (!ges_timeline_element_lookup_child (self, property_name, &child, &pspec))
1471     goto not_found;
1472
1473   klass = GES_TIMELINE_ELEMENT_GET_CLASS (self);
1474   g_assert (klass->set_child_property);
1475   klass->set_child_property (self, child, pspec, (GValue *) value);
1476
1477   gst_object_unref (child);
1478   g_param_spec_unref (pspec);
1479
1480   return TRUE;
1481
1482 not_found:
1483   {
1484     GST_WARNING_OBJECT (self, "The %s property doesn't exist", property_name);
1485
1486     return FALSE;
1487   }
1488 }
1489
1490 /**
1491 * ges_timeline_element_get_child_property:
1492 * @self: The origin #GESTimelineElement
1493 * @property_name: The name of the property
1494 * @value: (out): return location for the property value, it will
1495 * be initialized if it is initialized with 0
1496 *
1497 * In general, a copy is made of the property contents and
1498 * the caller is responsible for freeing the memory by calling
1499 * g_value_unset().
1500 *
1501 * Gets a property of a GstElement contained in @object.
1502 *
1503 * Note that #ges_timeline_element_get_child_property is really
1504 * intended for language bindings, #ges_timeline_element_get_child_properties
1505 * is much more convenient for C programming.
1506 *
1507 * Returns: %TRUE if the property was found, %FALSE otherwize
1508 */
1509 gboolean
1510 ges_timeline_element_get_child_property (GESTimelineElement * self,
1511     const gchar * property_name, GValue * value)
1512 {
1513   GParamSpec *pspec;
1514   GObject *child;
1515
1516   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
1517
1518   if (!ges_timeline_element_lookup_child (self, property_name, &child, &pspec))
1519     goto not_found;
1520
1521   if (G_VALUE_TYPE (value) == G_TYPE_INVALID)
1522     g_value_init (value, pspec->value_type);
1523
1524   g_object_get_property (child, pspec->name, value);
1525
1526   gst_object_unref (child);
1527   g_param_spec_unref (pspec);
1528
1529   return TRUE;
1530
1531 not_found:
1532   {
1533     GST_WARNING_OBJECT (self, "The %s property doesn't exist", property_name);
1534
1535     return FALSE;
1536   }
1537 }
1538
1539 /**
1540  * ges_timeline_element_lookup_child:
1541  * @self: object to lookup the property in
1542  * @prop_name: name of the property to look up. You can specify the name of the
1543  *     class as such: "ClassName::property-name", to guarantee that you get the
1544  *     proper GParamSpec in case various GstElement-s contain the same property
1545  *     name. If you don't do so, you will get the first element found, having
1546  *     this property and the and the corresponding GParamSpec.
1547  * @child: (out) (allow-none) (transfer full): pointer to a #GstElement that
1548  *     takes the real object to set property on
1549  * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
1550  *     describing the property
1551  *
1552  * Looks up which @element and @pspec would be effected by the given @name. If various
1553  * contained elements have this property name you will get the first one, unless you
1554  * specify the class name in @name.
1555  *
1556  * Returns: TRUE if @element and @pspec could be found. FALSE otherwise. In that
1557  * case the values for @pspec and @element are not modified. Unref @element after
1558  * usage.
1559  */
1560 gboolean
1561 ges_timeline_element_lookup_child (GESTimelineElement * self,
1562     const gchar * prop_name, GObject ** child, GParamSpec ** pspec)
1563 {
1564   GESTimelineElementClass *class;
1565
1566   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
1567   class = GES_TIMELINE_ELEMENT_GET_CLASS (self);
1568   g_return_val_if_fail (class->lookup_child, FALSE);
1569
1570   return class->lookup_child (self, prop_name, child, pspec);
1571 }
1572
1573 /**
1574  * ges_timeline_element_set_child_property_valist:
1575  * @self: The #GESTimelineElement parent object
1576  * @first_property_name: The name of the first property to set
1577  * @var_args: value for the first property, followed optionally by more
1578  * name/return location pairs, followed by NULL
1579  *
1580  * Sets a property of a child of @self. If there are various child elements
1581  * that have the same property name, you can distinguish them using the following
1582  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1583  * corresponding property of the first element found will be set.
1584  */
1585 void
1586 ges_timeline_element_set_child_property_valist (GESTimelineElement * self,
1587     const gchar * first_property_name, va_list var_args)
1588 {
1589   const gchar *name;
1590   GParamSpec *pspec;
1591   GObject *child;
1592
1593   gchar *error = NULL;
1594   GValue value = { 0, };
1595
1596   g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
1597
1598   name = first_property_name;
1599
1600   /* Note: This part is in big part copied from the gst_child_object_set_valist
1601    * method. */
1602
1603   /* iterate over pairs */
1604   while (name) {
1605     if (!ges_timeline_element_lookup_child (self, name, &child, &pspec))
1606       goto not_found;
1607
1608     G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
1609         G_VALUE_NOCOPY_CONTENTS, &error);
1610
1611     if (error)
1612       goto cant_copy;
1613
1614     g_object_set_property (child, pspec->name, &value);
1615
1616     gst_object_unref (child);
1617     g_param_spec_unref (pspec);
1618     g_value_unset (&value);
1619
1620     name = va_arg (var_args, gchar *);
1621   }
1622   return;
1623
1624 not_found:
1625   {
1626     GST_WARNING_OBJECT (self, "No property %s in OBJECT\n", name);
1627     return;
1628   }
1629 cant_copy:
1630   {
1631     GST_WARNING_OBJECT (self, "error copying value %s in %p: %s", pspec->name,
1632         self, error);
1633
1634     gst_object_unref (child);
1635     g_param_spec_unref (pspec);
1636     g_value_unset (&value);
1637     return;
1638   }
1639 }
1640
1641 /**
1642  * ges_timeline_element_set_child_properties:
1643  * @self: The #GESTimelineElement parent object
1644  * @first_property_name: The name of the first property to set
1645  * @...: value for the first property, followed optionally by more
1646  * name/return location pairs, followed by NULL
1647  *
1648  * Sets a property of a child of @self. If there are various child elements
1649  * that have the same property name, you can distinguish them using the following
1650  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1651  * corresponding property of the first element found will be set.
1652  */
1653 void
1654 ges_timeline_element_set_child_properties (GESTimelineElement * self,
1655     const gchar * first_property_name, ...)
1656 {
1657   va_list var_args;
1658
1659   g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
1660
1661   va_start (var_args, first_property_name);
1662   ges_timeline_element_set_child_property_valist (self, first_property_name,
1663       var_args);
1664   va_end (var_args);
1665 }
1666
1667 /**
1668  * ges_timeline_element_get_child_property_valist:
1669  * @self: The #GESTimelineElement parent object
1670  * @first_property_name: The name of the first property to get
1671  * @var_args: value for the first property, followed optionally by more
1672  * name/return location pairs, followed by NULL
1673  *
1674  * Gets a property of a child of @self. If there are various child elements
1675  * that have the same property name, you can distinguish them using the following
1676  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1677  * corresponding property of the first element found will be set.
1678  */
1679 void
1680 ges_timeline_element_get_child_property_valist (GESTimelineElement * self,
1681     const gchar * first_property_name, va_list var_args)
1682 {
1683   const gchar *name;
1684   gchar *error = NULL;
1685   GValue value = { 0, };
1686   GParamSpec *pspec;
1687   GObject *child;
1688
1689   g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
1690
1691   name = first_property_name;
1692
1693   /* This part is in big part copied from the gst_child_object_get_valist method */
1694   while (name) {
1695     if (!ges_timeline_element_lookup_child (self, name, &child, &pspec))
1696       goto not_found;
1697
1698     g_value_init (&value, pspec->value_type);
1699     g_object_get_property (child, pspec->name, &value);
1700     gst_object_unref (child);
1701     g_param_spec_unref (pspec);
1702
1703     G_VALUE_LCOPY (&value, var_args, 0, &error);
1704     if (error)
1705       goto cant_copy;
1706     g_value_unset (&value);
1707     name = va_arg (var_args, gchar *);
1708   }
1709   return;
1710
1711 not_found:
1712   {
1713     GST_WARNING_OBJECT (self, "no child property %s", name);
1714     return;
1715   }
1716 cant_copy:
1717   {
1718     GST_WARNING_OBJECT (self, "error copying value %s in %s", pspec->name,
1719         error);
1720
1721     g_value_unset (&value);
1722     return;
1723   }
1724 }
1725
1726 static gint
1727 compare_gparamspec (GParamSpec ** a, GParamSpec ** b, gpointer udata)
1728 {
1729   return g_strcmp0 ((*a)->name, (*b)->name);
1730 }
1731
1732
1733 /**
1734  * ges_timeline_element_list_children_properties:
1735  * @self: The #GESTimelineElement to get the list of children properties from
1736  * @n_properties: (out): return location for the length of the returned array
1737  *
1738  * Gets an array of #GParamSpec* for all configurable properties of the
1739  * children of @self.
1740  *
1741  * Returns: (transfer full) (array length=n_properties): an array of #GParamSpec* which should be freed after use or
1742  * %NULL if something went wrong
1743  */
1744 GParamSpec **
1745 ges_timeline_element_list_children_properties (GESTimelineElement * self,
1746     guint * n_properties)
1747 {
1748   GParamSpec **ret;
1749   GESTimelineElementClass *class;
1750
1751   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), NULL);
1752
1753   class = GES_TIMELINE_ELEMENT_GET_CLASS (self);
1754
1755   if (!class->list_children_properties) {
1756     GST_INFO_OBJECT (self, "No %s->list_children_properties implementation",
1757         G_OBJECT_TYPE_NAME (self));
1758
1759     *n_properties = 0;
1760     return NULL;
1761   }
1762
1763   ret = class->list_children_properties (self, n_properties);
1764   g_qsort_with_data (ret, *n_properties, sizeof (GParamSpec *),
1765       (GCompareDataFunc) compare_gparamspec, NULL);
1766
1767   return ret;
1768 }
1769
1770 /**
1771  * ges_timeline_element_get_child_properties:
1772  * @self: The origin #GESTimelineElement
1773  * @first_property_name: The name of the first property to get
1774  * @...: return location for the first property, followed optionally by more
1775  * name/return location pairs, followed by NULL
1776  *
1777  * Gets properties of a child of @self.
1778  */
1779 void
1780 ges_timeline_element_get_child_properties (GESTimelineElement * self,
1781     const gchar * first_property_name, ...)
1782 {
1783   va_list var_args;
1784
1785   g_return_if_fail (GES_IS_TIMELINE_ELEMENT (self));
1786
1787   va_start (var_args, first_property_name);
1788   ges_timeline_element_get_child_property_valist (self, first_property_name,
1789       var_args);
1790   va_end (var_args);
1791 }
1792
1793 gboolean
1794 ges_timeline_element_remove_child_property (GESTimelineElement * self,
1795     GParamSpec * pspec)
1796 {
1797   return g_hash_table_remove (self->priv->children_props, pspec);
1798 }
1799
1800 /**
1801  * ges_timeline_element_get_track_types:
1802  * @self: A #GESTimelineElement
1803  *
1804  * Gets all the TrackTypes @self will interact with
1805  *
1806  * Since: 1.6.0
1807  */
1808 GESTrackType
1809 ges_timeline_element_get_track_types (GESTimelineElement * self)
1810 {
1811   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), 0);
1812   g_return_val_if_fail (GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_track_types,
1813       0);
1814
1815   return GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_track_types (self);
1816 }
1817
1818 /**
1819  * ges_timeline_element_paste:
1820  * @self: The #GESTimelineElement to paste
1821  * @paste_position: The position in the timeline the element should
1822  * be copied to, meaning it will become the start of @self
1823  *
1824  * Paste @self inside the timeline. @self must have been created
1825  * using ges_timeline_element_copy with recurse=TRUE set,
1826  * otherwise it will fail.
1827  *
1828  * Returns: (transfer full): New element resulting of pasting @self
1829  * or %NULL
1830  *
1831  * Since: 1.6.0
1832  */
1833 GESTimelineElement *
1834 ges_timeline_element_paste (GESTimelineElement * self,
1835     GstClockTime paste_position)
1836 {
1837   GESTimelineElement *res;
1838   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self), FALSE);
1839
1840   if (!self->priv->copied_from) {
1841     GST_ERROR_OBJECT (self, "Is not being 'deeply' copied!");
1842
1843     return NULL;
1844   }
1845
1846   if (!GES_TIMELINE_ELEMENT_GET_CLASS (self)->paste) {
1847     GST_ERROR_OBJECT (self, "No paste vmethod implemented");
1848
1849     return NULL;
1850   }
1851
1852   res = GES_TIMELINE_ELEMENT_GET_CLASS (self)->paste (self,
1853       self->priv->copied_from, paste_position);
1854
1855   g_clear_object (&self->priv->copied_from);
1856
1857   return res ? g_object_ref (res) : res;
1858 }
1859
1860 /**
1861  * ges_timeline_element_get_layer_priority:
1862  * @self: A #GESTimelineElement
1863  *
1864  * Returns: The priority of the first layer the element is in (note that only
1865  * groups can span over several layers). %GES_TIMELINE_ELEMENT_NO_LAYER_PRIORITY
1866  * means that the element is not in a layer.
1867  *
1868  * Since: 1.16
1869  */
1870 guint32
1871 ges_timeline_element_get_layer_priority (GESTimelineElement * self)
1872 {
1873   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (self),
1874       GES_TIMELINE_ELEMENT_NO_LAYER_PRIORITY);
1875
1876   if (!GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_layer_priority)
1877     return self->priority;
1878
1879   return GES_TIMELINE_ELEMENT_GET_CLASS (self)->get_layer_priority (self);
1880 }
1881
1882 /* Internal */
1883 gdouble
1884 ges_timeline_element_get_media_duration_factor (GESTimelineElement * self)
1885 {
1886   gdouble media_duration_factor;
1887   GESEffectClass *class;
1888   GList *props;
1889
1890   media_duration_factor = 1.0;
1891
1892   class = GES_EFFECT_CLASS (g_type_class_ref (GES_TYPE_EFFECT));
1893
1894   for (props = class->rate_properties; props != NULL; props = props->next) {
1895     GObject *child;
1896     GParamSpec *pspec;
1897     if (ges_timeline_element_lookup_child (self, props->data, &child, &pspec)) {
1898       if (G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_FLOAT) {
1899         gfloat rate_change;
1900         g_object_get (child, pspec->name, &rate_change, NULL);
1901         media_duration_factor *= rate_change;
1902       } else if (G_PARAM_SPEC_VALUE_TYPE (pspec) == G_TYPE_DOUBLE) {
1903         gdouble rate_change;
1904         g_object_get (child, pspec->name, &rate_change, NULL);
1905         media_duration_factor *= rate_change;
1906       } else {
1907         GST_WARNING_OBJECT (self,
1908             "Rate property %s in child %" GST_PTR_FORMAT
1909             " is of unsupported type %s", pspec->name, child,
1910             G_VALUE_TYPE_NAME (pspec->value_type));
1911       }
1912
1913       gst_object_unref (child);
1914       g_param_spec_unref (pspec);
1915
1916       GST_DEBUG_OBJECT (self,
1917           "Added rate changing property %s, set to value %lf",
1918           (const char *) props->data, media_duration_factor);
1919     }
1920   }
1921
1922   g_type_class_unref (class);
1923   return media_duration_factor;
1924 }
1925
1926 /* Internal */
1927 GESTimelineElement *
1928 ges_timeline_element_get_copied_from (GESTimelineElement * self)
1929 {
1930   GESTimelineElement *copied_from = self->priv->copied_from;
1931   self->priv->copied_from = NULL;
1932   return copied_from;
1933 }
1934
1935 GESTimelineElementFlags
1936 ges_timeline_element_flags (GESTimelineElement * self)
1937 {
1938   return self->priv->flags;
1939 }
1940
1941 void
1942 ges_timeline_element_set_flags (GESTimelineElement * self,
1943     GESTimelineElementFlags flags)
1944 {
1945   self->priv->flags = flags;
1946
1947 }