completion: Update to new gstreamer core helpers
[platform/upstream/gstreamer.git] / ges / ges-track-element.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
3  *               2009 Nokia Corporation
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:gestrackelement
23  * @title: GESTrackElement
24  * @short_description: Base Class for objects contained in a GESTrack
25  *
26  * #GESTrackElement is the Base Class for any object that can be contained in a
27  * #GESTrack.
28  *
29  * It contains the basic information as to the location of the object within
30  * its container, like the start position, the inpoint, the duration and the
31  * priority.
32  */
33 #include "ges-internal.h"
34 #include "ges-extractable.h"
35 #include "ges-track-element.h"
36 #include "ges-clip.h"
37 #include "ges-meta-container.h"
38
39 G_DEFINE_ABSTRACT_TYPE (GESTrackElement, ges_track_element,
40     GES_TYPE_TIMELINE_ELEMENT);
41
42 struct _GESTrackElementPrivate
43 {
44   GESTrackType track_type;
45
46   GstElement *nleobject;        /* The NleObject */
47   GstElement *element;          /* The element contained in the nleobject (can be NULL) */
48
49   GESTrack *track;
50
51   gboolean locked;              /* If TRUE, then moves in sync with its controlling
52                                  * GESClip */
53
54   GHashTable *bindings_hashtable;       /* We need this if we want to be able to serialize
55                                            and deserialize keyframes */
56 };
57
58 enum
59 {
60   PROP_0,
61   PROP_ACTIVE,
62   PROP_TRACK_TYPE,
63   PROP_TRACK,
64   PROP_LAST
65 };
66
67 static GParamSpec *properties[PROP_LAST];
68
69 enum
70 {
71   CONTROL_BINDING_ADDED,
72   CONTROL_BINDING_REMOVED,
73   LAST_SIGNAL
74 };
75
76 static guint ges_track_element_signals[LAST_SIGNAL] = { 0 };
77
78 static GstElement *ges_track_element_create_gnl_object_func (GESTrackElement *
79     object);
80
81 static gboolean _set_start (GESTimelineElement * element, GstClockTime start);
82 static gboolean _set_inpoint (GESTimelineElement * element,
83     GstClockTime inpoint);
84 static gboolean _set_duration (GESTimelineElement * element,
85     GstClockTime duration);
86 static gboolean _set_priority (GESTimelineElement * element, guint32 priority);
87 GESTrackType _get_track_types (GESTimelineElement * object);
88
89 static GParamSpec **default_list_children_properties (GESTrackElement * object,
90     guint * n_properties);
91
92 static void
93 _update_control_bindings (GESTimelineElement * element, GstClockTime inpoint,
94     GstClockTime duration);
95
96 static gboolean
97 _lookup_child (GESTrackElement * object,
98     const gchar * prop_name, GstElement ** element, GParamSpec ** pspec)
99 {
100   return
101       GES_TIMELINE_ELEMENT_GET_CLASS (object)->lookup_child
102       (GES_TIMELINE_ELEMENT (object), prop_name, (GObject **) element, pspec);
103 }
104
105 static gboolean
106 strv_find_str (const gchar ** strv, const char *str)
107 {
108   guint i;
109
110   if (strv == NULL)
111     return FALSE;
112
113   for (i = 0; strv[i]; i++) {
114     if (g_strcmp0 (strv[i], str) == 0)
115       return TRUE;
116   }
117
118   return FALSE;
119 }
120
121 static void
122 ges_track_element_get_property (GObject * object, guint property_id,
123     GValue * value, GParamSpec * pspec)
124 {
125   GESTrackElement *track_element = GES_TRACK_ELEMENT (object);
126
127   switch (property_id) {
128     case PROP_ACTIVE:
129       g_value_set_boolean (value, ges_track_element_is_active (track_element));
130       break;
131     case PROP_TRACK_TYPE:
132       g_value_set_flags (value, track_element->priv->track_type);
133       break;
134     case PROP_TRACK:
135       g_value_set_object (value, track_element->priv->track);
136       break;
137     default:
138       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
139   }
140 }
141
142 static void
143 ges_track_element_set_property (GObject * object, guint property_id,
144     const GValue * value, GParamSpec * pspec)
145 {
146   GESTrackElement *track_element = GES_TRACK_ELEMENT (object);
147
148   switch (property_id) {
149     case PROP_ACTIVE:
150       ges_track_element_set_active (track_element, g_value_get_boolean (value));
151       break;
152     case PROP_TRACK_TYPE:
153       track_element->priv->track_type = g_value_get_flags (value);
154       break;
155     default:
156       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
157   }
158 }
159
160 static void
161 ges_track_element_dispose (GObject * object)
162 {
163   GESTrackElement *element = GES_TRACK_ELEMENT (object);
164   GESTrackElementPrivate *priv = element->priv;
165
166   if (priv->bindings_hashtable)
167     g_hash_table_destroy (priv->bindings_hashtable);
168
169   if (priv->nleobject) {
170     GstState cstate;
171
172     if (priv->track != NULL) {
173       g_error ("%p Still in %p, this means that you forgot"
174           " to remove it from the GESTrack it is contained in. You always need"
175           " to remove a GESTrackElement from its track before dropping the last"
176           " reference\n"
177           "This problem may also be caused by a refcounting bug in"
178           " the application or GES itself.", object, priv->track);
179       gst_element_get_state (priv->nleobject, &cstate, NULL, 0);
180       if (cstate != GST_STATE_NULL)
181         gst_element_set_state (priv->nleobject, GST_STATE_NULL);
182     }
183
184     g_object_set_qdata (G_OBJECT (priv->nleobject),
185         NLE_OBJECT_TRACK_ELEMENT_QUARK, NULL);
186     gst_object_unref (priv->nleobject);
187     priv->nleobject = NULL;
188   }
189
190   G_OBJECT_CLASS (ges_track_element_parent_class)->dispose (object);
191 }
192
193 static void
194 ges_track_element_constructed (GObject * gobject)
195 {
196   GESTrackElementClass *class;
197   GstElement *nleobject;
198   gdouble media_duration_factor;
199   gchar *tmp;
200   GESTrackElement *object = GES_TRACK_ELEMENT (gobject);
201
202   GST_DEBUG_OBJECT (object, "Creating NleObject");
203
204   class = GES_TRACK_ELEMENT_GET_CLASS (object);
205   g_assert (class->create_gnl_object);
206
207   nleobject = class->create_gnl_object (object);
208   if (G_UNLIKELY (nleobject == NULL)) {
209     GST_ERROR_OBJECT (object, "Could not create NleObject");
210
211     return;
212   }
213
214   tmp = g_strdup_printf ("%s:%s", G_OBJECT_TYPE_NAME (object),
215       GST_OBJECT_NAME (nleobject));
216   gst_object_set_name (GST_OBJECT (nleobject), tmp);
217   g_free (tmp);
218
219   GST_DEBUG_OBJECT (object, "Got a valid NleObject, now filling it in");
220
221   object->priv->nleobject = gst_object_ref (nleobject);
222   g_object_set_qdata (G_OBJECT (nleobject), NLE_OBJECT_TRACK_ELEMENT_QUARK,
223       object);
224
225   /* Set some properties on the NleObject */
226   g_object_set (object->priv->nleobject,
227       "start", GES_TIMELINE_ELEMENT_START (object),
228       "inpoint", GES_TIMELINE_ELEMENT_INPOINT (object),
229       "duration", GES_TIMELINE_ELEMENT_DURATION (object),
230       "priority", GES_TIMELINE_ELEMENT_PRIORITY (object),
231       "active", object->active, NULL);
232
233   media_duration_factor =
234       ges_timeline_element_get_media_duration_factor (GES_TIMELINE_ELEMENT
235       (object));
236   g_object_set (object->priv->nleobject,
237       "media-duration-factor", media_duration_factor, NULL);
238
239   G_OBJECT_CLASS (ges_track_element_parent_class)->constructed (gobject);
240 }
241
242 static void
243 ges_track_element_class_init (GESTrackElementClass * klass)
244 {
245   GObjectClass *object_class = G_OBJECT_CLASS (klass);
246   GESTimelineElementClass *element_class = GES_TIMELINE_ELEMENT_CLASS (klass);
247
248   g_type_class_add_private (klass, sizeof (GESTrackElementPrivate));
249
250   object_class->get_property = ges_track_element_get_property;
251   object_class->set_property = ges_track_element_set_property;
252   object_class->dispose = ges_track_element_dispose;
253   object_class->constructed = ges_track_element_constructed;
254
255
256   /**
257    * GESTrackElement:active:
258    *
259    * Whether the object should be taken into account in the #GESTrack output.
260    * If #FALSE, then its contents will not be used in the resulting track.
261    */
262   properties[PROP_ACTIVE] =
263       g_param_spec_boolean ("active", "Active", "Use object in output", TRUE,
264       G_PARAM_READWRITE);
265   g_object_class_install_property (object_class, PROP_ACTIVE,
266       properties[PROP_ACTIVE]);
267
268   properties[PROP_TRACK_TYPE] = g_param_spec_flags ("track-type", "Track Type",
269       "The track type of the object", GES_TYPE_TRACK_TYPE,
270       GES_TRACK_TYPE_UNKNOWN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
271   g_object_class_install_property (object_class, PROP_TRACK_TYPE,
272       properties[PROP_TRACK_TYPE]);
273
274   properties[PROP_TRACK] = g_param_spec_object ("track", "Track",
275       "The track the object is in", GES_TYPE_TRACK, G_PARAM_READABLE);
276   g_object_class_install_property (object_class, PROP_TRACK,
277       properties[PROP_TRACK]);
278
279   /**
280    * GESTrackElement::control-binding-added:
281    * @track_element: a #GESTrackElement
282    * @control_binding: the #GstControlBinding that has been added
283    *
284    * The control-binding-added signal is emitted each time a control binding
285    * is added for a child property of @track_element
286    */
287   ges_track_element_signals[CONTROL_BINDING_ADDED] =
288       g_signal_new ("control-binding-added", G_TYPE_FROM_CLASS (klass),
289       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
290       G_TYPE_NONE, 1, GST_TYPE_CONTROL_BINDING);
291
292   /**
293    * GESTrackElement::control-binding-removed:
294    * @track_element: a #GESTrackElement
295    * @control_binding: the #GstControlBinding that has been removed
296    *
297    * The control-binding-removed signal is emitted each time a control binding
298    * is removed for a child property of @track_element
299    */
300   ges_track_element_signals[CONTROL_BINDING_REMOVED] =
301       g_signal_new ("control-binding-removed", G_TYPE_FROM_CLASS (klass),
302       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
303       G_TYPE_NONE, 1, GST_TYPE_CONTROL_BINDING);
304
305   element_class->set_start = _set_start;
306   element_class->set_duration = _set_duration;
307   element_class->set_inpoint = _set_inpoint;
308   element_class->set_priority = _set_priority;
309   element_class->get_track_types = _get_track_types;
310   element_class->deep_copy = ges_track_element_copy_properties;
311
312   klass->create_gnl_object = ges_track_element_create_gnl_object_func;
313   klass->list_children_properties = default_list_children_properties;
314   klass->lookup_child = _lookup_child;
315 }
316
317 static void
318 ges_track_element_init (GESTrackElement * self)
319 {
320   GESTrackElementPrivate *priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
321       GES_TYPE_TRACK_ELEMENT, GESTrackElementPrivate);
322
323   /* Sane default values */
324   GES_TIMELINE_ELEMENT_START (self) = 0;
325   GES_TIMELINE_ELEMENT_INPOINT (self) = 0;
326   GES_TIMELINE_ELEMENT_DURATION (self) = GST_SECOND;
327   GES_TIMELINE_ELEMENT_PRIORITY (self) = 0;
328   self->active = TRUE;
329
330   priv->bindings_hashtable = g_hash_table_new_full (g_str_hash, g_str_equal,
331       g_free, NULL);
332 }
333
334 static gfloat
335 interpolate_values_for_position (GstTimedValue * first_value,
336     GstTimedValue * second_value, guint64 position, gboolean absolute)
337 {
338   gfloat diff;
339   GstClockTime interval;
340   gfloat value_at_pos;
341
342   g_assert (second_value || first_value);
343
344   if (first_value == NULL)
345     return second_value->value;
346
347   if (second_value == NULL)
348     return first_value->value;
349
350   diff = second_value->value - first_value->value;
351   interval = second_value->timestamp - first_value->timestamp;
352
353   if (position > first_value->timestamp)
354     value_at_pos =
355         first_value->value + ((float) (position -
356             first_value->timestamp) / (float) interval) * diff;
357   else
358     value_at_pos =
359         first_value->value - ((float) (first_value->timestamp -
360             position) / (float) interval) * diff;
361
362   if (!absolute)
363     value_at_pos = CLAMP (value_at_pos, 0.0, 1.0);
364
365   return value_at_pos;
366 }
367
368 static void
369 _update_control_bindings (GESTimelineElement * element, GstClockTime inpoint,
370     GstClockTime duration)
371 {
372   GParamSpec **specs;
373   guint n, n_specs;
374   GstControlBinding *binding;
375   GstTimedValueControlSource *source;
376   GESTrackElement *self = GES_TRACK_ELEMENT (element);
377
378   specs = ges_track_element_list_children_properties (self, &n_specs);
379
380   for (n = 0; n < n_specs; ++n) {
381     GList *values, *tmp;
382     gboolean absolute;
383     GstTimedValue *last, *first, *prev = NULL, *next = NULL;
384     gfloat value_at_pos;
385
386     binding = ges_track_element_get_control_binding (self, specs[n]->name);
387
388     if (!binding)
389       continue;
390
391     g_object_get (binding, "control_source", &source, NULL);
392
393     g_object_get (binding, "absolute", &absolute, NULL);
394     if (duration == 0) {
395       gst_timed_value_control_source_unset_all (GST_TIMED_VALUE_CONTROL_SOURCE
396           (source));
397       continue;
398     }
399
400     values =
401         gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
402         (source));
403
404     if (g_list_length (values) == 0)
405       continue;
406
407     first = values->data;
408
409     for (tmp = values->next; tmp; tmp = tmp->next) {
410       next = tmp->data;
411
412       if (next->timestamp > inpoint)
413         break;
414     }
415
416     value_at_pos =
417         interpolate_values_for_position (first, next, inpoint, absolute);
418     gst_timed_value_control_source_unset (source, first->timestamp);
419     gst_timed_value_control_source_set (source, inpoint, value_at_pos);
420
421     values =
422         gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
423         (source));
424
425     if (duration != GST_CLOCK_TIME_NONE) {
426       last = g_list_last (values)->data;
427
428       for (tmp = g_list_last (values)->prev; tmp; tmp = tmp->prev) {
429         prev = tmp->data;
430
431         if (prev->timestamp < duration + inpoint)
432           break;
433       }
434
435       value_at_pos =
436           interpolate_values_for_position (prev, last, duration + inpoint,
437           absolute);
438
439       gst_timed_value_control_source_unset (source, last->timestamp);
440       gst_timed_value_control_source_set (source, duration + inpoint,
441           value_at_pos);
442       values =
443           gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
444           (source));
445     }
446
447     for (tmp = values; tmp; tmp = tmp->next) {
448       GstTimedValue *value = tmp->data;
449       if (value->timestamp < inpoint)
450         gst_timed_value_control_source_unset (source, value->timestamp);
451       else if (duration != GST_CLOCK_TIME_NONE
452           && value->timestamp > duration + inpoint)
453         gst_timed_value_control_source_unset (source, value->timestamp);
454     }
455   }
456
457   g_free (specs);
458 }
459
460 static gboolean
461 _set_start (GESTimelineElement * element, GstClockTime start)
462 {
463   GESTrackElement *object = GES_TRACK_ELEMENT (element);
464
465   g_return_val_if_fail (object->priv->nleobject, FALSE);
466
467   if (G_UNLIKELY (start == _START (object)))
468     return FALSE;
469
470   g_object_set (object->priv->nleobject, "start", start, NULL);
471
472   return TRUE;
473 }
474
475 static gboolean
476 _set_inpoint (GESTimelineElement * element, GstClockTime inpoint)
477 {
478   GESTrackElement *object = GES_TRACK_ELEMENT (element);
479
480   g_return_val_if_fail (object->priv->nleobject, FALSE);
481
482   if (G_UNLIKELY (inpoint == _INPOINT (object)))
483
484     return FALSE;
485
486   g_object_set (object->priv->nleobject, "inpoint", inpoint, NULL);
487   _update_control_bindings (element, inpoint, GST_CLOCK_TIME_NONE);
488
489   return TRUE;
490 }
491
492 static gboolean
493 _set_duration (GESTimelineElement * element, GstClockTime duration)
494 {
495   GESTrackElement *object = GES_TRACK_ELEMENT (element);
496   GESTrackElementPrivate *priv = object->priv;
497
498   g_return_val_if_fail (object->priv->nleobject, FALSE);
499
500   if (GST_CLOCK_TIME_IS_VALID (_MAXDURATION (element)) &&
501       duration > _INPOINT (object) + _MAXDURATION (element))
502     duration = _MAXDURATION (element) - _INPOINT (object);
503
504   if (G_UNLIKELY (duration == _DURATION (object)))
505     return FALSE;
506
507   g_object_set (priv->nleobject, "duration", duration, NULL);
508
509   _update_control_bindings (element, ges_timeline_element_get_inpoint (element),
510       duration);
511
512   return TRUE;
513 }
514
515 static gboolean
516 _set_priority (GESTimelineElement * element, guint32 priority)
517 {
518   GESTrackElement *object = GES_TRACK_ELEMENT (element);
519
520   g_return_val_if_fail (object->priv->nleobject, FALSE);
521
522   if (priority < MIN_NLE_PRIO) {
523     GST_INFO_OBJECT (element, "Priority (%d) < MIN_NLE_PRIO, setting it to %d",
524         priority, MIN_NLE_PRIO);
525     priority = MIN_NLE_PRIO;
526   }
527
528   GST_DEBUG_OBJECT (object, "priority:%" G_GUINT32_FORMAT, priority);
529
530   if (G_UNLIKELY (priority == _PRIORITY (object)))
531     return FALSE;
532
533   g_object_set (object->priv->nleobject, "priority", priority, NULL);
534
535   return TRUE;
536 }
537
538 GESTrackType
539 _get_track_types (GESTimelineElement * object)
540 {
541   return ges_track_element_get_track_type (GES_TRACK_ELEMENT (object));
542 }
543
544 /**
545  * ges_track_element_set_active:
546  * @object: a #GESTrackElement
547  * @active: visibility
548  *
549  * Sets the usage of the @object. If @active is %TRUE, the object will be used for
550  * playback and rendering, else it will be ignored.
551  *
552  * Returns: %TRUE if the property was toggled, else %FALSE
553  */
554 gboolean
555 ges_track_element_set_active (GESTrackElement * object, gboolean active)
556 {
557   g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
558   g_return_val_if_fail (object->priv->nleobject, FALSE);
559
560   GST_DEBUG_OBJECT (object, "object:%p, active:%d", object, active);
561
562   if (G_UNLIKELY (active == object->active))
563     return FALSE;
564
565   g_object_set (object->priv->nleobject, "active", active, NULL);
566
567   if (active != object->active) {
568     object->active = active;
569     if (GES_TRACK_ELEMENT_GET_CLASS (object)->active_changed)
570       GES_TRACK_ELEMENT_GET_CLASS (object)->active_changed (object, active);
571   }
572
573   return TRUE;
574 }
575
576 void
577 ges_track_element_set_track_type (GESTrackElement * object, GESTrackType type)
578 {
579   g_return_if_fail (GES_IS_TRACK_ELEMENT (object));
580
581   if (object->priv->track_type != type) {
582     object->priv->track_type = type;
583     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_TRACK_TYPE]);
584   }
585 }
586
587 GESTrackType
588 ges_track_element_get_track_type (GESTrackElement * object)
589 {
590   g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), GES_TRACK_TYPE_UNKNOWN);
591
592   return object->priv->track_type;
593 }
594
595 /* default 'create_gnl_object' virtual method implementation */
596 static GstElement *
597 ges_track_element_create_gnl_object_func (GESTrackElement * self)
598 {
599   GESTrackElementClass *klass = NULL;
600   GstElement *child = NULL;
601   GstElement *nleobject;
602
603   klass = GES_TRACK_ELEMENT_GET_CLASS (self);
604
605   if (G_UNLIKELY (self->priv->nleobject != NULL))
606     goto already_have_nleobject;
607
608   if (G_UNLIKELY (klass->nleobject_factorytype == NULL))
609     goto no_nlefactory;
610
611   GST_DEBUG ("Creating a supporting nleobject of type '%s'",
612       klass->nleobject_factorytype);
613
614   nleobject = gst_element_factory_make (klass->nleobject_factorytype, NULL);
615
616   if (G_UNLIKELY (nleobject == NULL))
617     goto no_nleobject;
618
619   if (klass->create_element) {
620     GST_DEBUG ("Calling subclass 'create_element' vmethod");
621     child = klass->create_element (self);
622
623     if (G_UNLIKELY (!child))
624       goto child_failure;
625
626     if (!gst_bin_add (GST_BIN (nleobject), child))
627       goto add_failure;
628
629     GST_DEBUG ("Succesfully got the element to put in the nleobject");
630     self->priv->element = child;
631   }
632
633   GST_DEBUG ("done");
634   return nleobject;
635
636
637   /* ERROR CASES */
638
639 already_have_nleobject:
640   {
641     GST_ERROR ("Already controlling a NleObject %s",
642         GST_ELEMENT_NAME (self->priv->nleobject));
643     return NULL;
644   }
645
646 no_nlefactory:
647   {
648     GST_ERROR ("No GESTrackElement::nleobject_factorytype implementation!");
649     return NULL;
650   }
651
652 no_nleobject:
653   {
654     GST_ERROR ("Error creating a nleobject of type '%s'",
655         klass->nleobject_factorytype);
656     return NULL;
657   }
658
659 child_failure:
660   {
661     GST_ERROR ("create_element returned NULL");
662     gst_object_unref (nleobject);
663     return NULL;
664   }
665
666 add_failure:
667   {
668     GST_ERROR ("Error adding the contents to the nleobject");
669     gst_object_unref (child);
670     gst_object_unref (nleobject);
671     return NULL;
672   }
673 }
674
675 /**
676  * ges_track_element_add_children_props:
677  * @self: The #GESTrackElement to set chidlren props on
678  * @element: The GstElement to retrieve properties from
679  * @wanted_categories: (array zero-terminated=1) (transfer none) (allow-none):
680  * An array of categories of GstElement to
681  * take into account (as defined in the factory meta "klass" field)
682  * @blacklist: (array zero-terminated=1) (transfer none) (allow-none): A
683  * blacklist of elements factory names to not take into account
684  * @whitelist: (array zero-terminated=1) (transfer none) (allow-none): A list
685  * of propery names to add as children properties
686  *
687  * Looks for the properties defines with the various parametters and add
688  * them to the hashtable of children properties.
689  *
690  * To be used by subclasses only
691  */
692 void
693 ges_track_element_add_children_props (GESTrackElement * self,
694     GstElement * element, const gchar ** wanted_categories,
695     const gchar ** blacklist, const gchar ** whitelist)
696 {
697   GValue item = { 0, };
698   GstIterator *it;
699   GParamSpec **parray;
700   GObjectClass *class;
701   const gchar *klass;
702   GstElementFactory *factory;
703   gboolean done = FALSE;
704
705   if (!GST_IS_BIN (element)) {
706     guint i;
707     GParamSpec *pspec;
708
709     GObjectClass *class = G_OBJECT_GET_CLASS (element);
710
711     for (i = 0; whitelist[i]; i++) {
712
713       pspec = g_object_class_find_property (class, whitelist[i]);
714       if (!pspec) {
715         GST_WARNING ("no such property : %s in element : %s", whitelist[i],
716             gst_element_get_name (element));
717         continue;
718       }
719
720       if (pspec->flags) {
721         ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT (self),
722             pspec, G_OBJECT (element));
723         GST_LOG_OBJECT (self,
724             "added property %s to controllable properties successfully !",
725             whitelist[i]);
726       } else
727         GST_WARNING
728             ("the property %s for element %s exists but is not writable",
729             whitelist[i], gst_element_get_name (element));
730
731     }
732     return;
733   }
734
735   /*  We go over child elements recursivly, and add writable properties to the
736    *  hashtable */
737   it = gst_bin_iterate_recurse (GST_BIN (element));
738   while (!done) {
739     switch (gst_iterator_next (it, &item)) {
740       case GST_ITERATOR_OK:
741       {
742         gchar **categories;
743         guint i;
744         GstElement *child = g_value_get_object (&item);
745
746         factory = gst_element_get_factory (child);
747         klass = gst_element_factory_get_metadata (factory,
748             GST_ELEMENT_METADATA_KLASS);
749
750         if (strv_find_str (blacklist, GST_OBJECT_NAME (factory))) {
751           GST_DEBUG_OBJECT (self, "%s blacklisted", GST_OBJECT_NAME (factory));
752           continue;
753         }
754
755         GST_DEBUG ("Looking at element '%s' of klass '%s'",
756             GST_ELEMENT_NAME (child), klass);
757
758         categories = g_strsplit (klass, "/", 0);
759
760         for (i = 0; categories[i]; i++) {
761           if ((!wanted_categories ||
762                   strv_find_str (wanted_categories, categories[i]))) {
763             guint i, nb_specs;
764
765             class = G_OBJECT_GET_CLASS (child);
766             parray = g_object_class_list_properties (class, &nb_specs);
767             for (i = 0; i < nb_specs; i++) {
768               if ((parray[i]->flags & G_PARAM_WRITABLE) &&
769                   (!whitelist || strv_find_str (whitelist, parray[i]->name))) {
770                 ges_timeline_element_add_child_property (GES_TIMELINE_ELEMENT
771                     (self), parray[i], G_OBJECT (child));
772               }
773             }
774             g_free (parray);
775
776             GST_DEBUG
777                 ("%d configurable properties of '%s' added to property hashtable",
778                 nb_specs, GST_ELEMENT_NAME (child));
779             break;
780           }
781         }
782
783         g_strfreev (categories);
784         g_value_reset (&item);
785         break;
786       }
787       case GST_ITERATOR_RESYNC:
788         /* FIXME, properly restart the process */
789         GST_DEBUG ("iterator resync");
790         gst_iterator_resync (it);
791         break;
792
793       case GST_ITERATOR_DONE:
794         GST_DEBUG ("iterator done");
795         done = TRUE;
796         break;
797
798       default:
799         break;
800     }
801     g_value_unset (&item);
802   }
803   gst_iterator_free (it);
804 }
805
806 /* INTERNAL USAGE */
807 gboolean
808 ges_track_element_set_track (GESTrackElement * object, GESTrack * track)
809 {
810   gboolean ret = TRUE;
811
812   g_return_val_if_fail (object->priv->nleobject, FALSE);
813
814   GST_DEBUG_OBJECT (object, "new track: %" GST_PTR_FORMAT, track);
815
816   object->priv->track = track;
817
818   if (object->priv->track) {
819     ges_track_element_set_track_type (object, track->type);
820
821     g_object_set (object->priv->nleobject,
822         "caps", ges_track_get_caps (object->priv->track), NULL);
823   }
824
825   g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_TRACK]);
826   return ret;
827 }
828
829 /**
830  * ges_track_element_get_all_control_bindings
831  * @trackelement: The #TrackElement from which to get all set bindings
832  *
833  * Returns: (element-type gchar* GstControlBinding)(transfer none): A
834  * #GHashTable containing all property_name: GstControlBinding
835  */
836 GHashTable *
837 ges_track_element_get_all_control_bindings (GESTrackElement * trackelement)
838 {
839   GESTrackElementPrivate *priv = GES_TRACK_ELEMENT (trackelement)->priv;
840
841   return priv->bindings_hashtable;
842 }
843
844 guint32
845 _ges_track_element_get_layer_priority (GESTrackElement * element)
846 {
847   if (_PRIORITY (element) < LAYER_HEIGHT + MIN_NLE_PRIO)
848     return 0;
849
850   return (_PRIORITY (element) - MIN_NLE_PRIO) / LAYER_HEIGHT;
851 }
852
853 /**
854  * ges_track_element_get_track:
855  * @object: a #GESTrackElement
856  *
857  * Get the #GESTrack to which this object belongs.
858  *
859  * Returns: (transfer none) (nullable): The #GESTrack to which this object
860  * belongs. Can be %NULL if it is not in any track
861  */
862 GESTrack *
863 ges_track_element_get_track (GESTrackElement * object)
864 {
865   g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
866
867   return object->priv->track;
868 }
869
870 /**
871  * ges_track_element_get_gnlobject:
872  * @object: a #GESTrackElement
873  *
874  * Get the NleObject object this object is controlling.
875  *
876  * Returns: (transfer none): the NleObject object this object is controlling.
877  *
878  * Deprecated: use #ges_track_element_get_nleobject instead.
879  */
880 GstElement *
881 ges_track_element_get_gnlobject (GESTrackElement * object)
882 {
883   g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
884
885   return object->priv->nleobject;
886 }
887
888 /**
889  * ges_track_element_get_nleobject:
890  * @object: a #GESTrackElement
891  *
892  * Get the GNonLin object this object is controlling.
893  *
894  * Returns: (transfer none): the GNonLin object this object is controlling.
895  *
896  * Since: 1.6
897  */
898 GstElement *
899 ges_track_element_get_nleobject (GESTrackElement * object)
900 {
901   g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
902
903   return object->priv->nleobject;
904 }
905
906 /**
907  * ges_track_element_get_element:
908  * @object: a #GESTrackElement
909  *
910  * Get the #GstElement this track element is controlling within GNonLin.
911  *
912  * Returns: (transfer none): the #GstElement this track element is controlling
913  * within GNonLin.
914  */
915 GstElement *
916 ges_track_element_get_element (GESTrackElement * object)
917 {
918   g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
919
920   return object->priv->element;
921 }
922
923 /**
924  * ges_track_element_is_active:
925  * @object: a #GESTrackElement
926  *
927  * Lets you know if @object will be used for playback and rendering,
928  * or not.
929  *
930  * Returns: %TRUE if @object is active, %FALSE otherwize
931  */
932 gboolean
933 ges_track_element_is_active (GESTrackElement * object)
934 {
935   g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
936   g_return_val_if_fail (object->priv->nleobject, FALSE);
937
938   return object->active;
939 }
940
941 /**
942  * ges_track_element_lookup_child:
943  * @object: object to lookup the property in
944  * @prop_name: name of the property to look up. You can specify the name of the
945  *     class as such: "ClassName::property-name", to guarantee that you get the
946  *     proper GParamSpec in case various GstElement-s contain the same property
947  *     name. If you don't do so, you will get the first element found, having
948  *     this property and the and the corresponding GParamSpec.
949  * @element: (out) (allow-none) (transfer full): pointer to a #GstElement that
950  *     takes the real object to set property on
951  * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
952  *     describing the property
953  *
954  * Looks up which @element and @pspec would be effected by the given @name. If various
955  * contained elements have this property name you will get the first one, unless you
956  * specify the class name in @name.
957  *
958  * Returns: TRUE if @element and @pspec could be found. FALSE otherwise. In that
959  * case the values for @pspec and @element are not modified. Unref @element after
960  * usage.
961  *
962  * Deprecated: Use #ges_timeline_element_lookup_child
963  */
964 gboolean
965 ges_track_element_lookup_child (GESTrackElement * object,
966     const gchar * prop_name, GstElement ** element, GParamSpec ** pspec)
967 {
968   return ges_timeline_element_lookup_child (GES_TIMELINE_ELEMENT (object),
969       prop_name, ((GObject **) element), pspec);
970 }
971
972 /**
973  * ges_track_element_set_child_property_by_pspec: (skip):
974  * @object: a #GESTrackElement
975  * @pspec: The #GParamSpec that specifies the property you want to set
976  * @value: the value
977  *
978  * Sets a property of a child of @object.
979  *
980  * Deprecated: Use #ges_timeline_element_set_child_property_by_spec
981  */
982 void
983 ges_track_element_set_child_property_by_pspec (GESTrackElement * object,
984     GParamSpec * pspec, GValue * value)
985 {
986   g_return_if_fail (GES_IS_TRACK_ELEMENT (object));
987
988   ges_timeline_element_set_child_property_by_pspec (GES_TIMELINE_ELEMENT
989       (object), pspec, value);
990
991   return;
992 }
993
994 /**
995  * ges_track_element_set_child_property_valist: (skip):
996  * @object: The #GESTrackElement parent object
997  * @first_property_name: The name of the first property to set
998  * @var_args: value for the first property, followed optionally by more
999  * name/return location pairs, followed by NULL
1000  *
1001  * Sets a property of a child of @object. If there are various child elements
1002  * that have the same property name, you can distinguish them using the following
1003  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1004  * corresponding property of the first element found will be set.
1005  *
1006  * Deprecated: Use #ges_timeline_element_set_child_property_valist
1007  */
1008 void
1009 ges_track_element_set_child_property_valist (GESTrackElement * object,
1010     const gchar * first_property_name, va_list var_args)
1011 {
1012   ges_timeline_element_set_child_property_valist (GES_TIMELINE_ELEMENT (object),
1013       first_property_name, var_args);
1014 }
1015
1016 /**
1017  * ges_track_element_set_child_properties: (skip):
1018  * @object: The #GESTrackElement parent object
1019  * @first_property_name: The name of the first property to set
1020  * @...: value for the first property, followed optionally by more
1021  * name/return location pairs, followed by NULL
1022  *
1023  * Sets a property of a child of @object. If there are various child elements
1024  * that have the same property name, you can distinguish them using the following
1025  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1026  * corresponding property of the first element found will be set.
1027  *
1028  * Deprecated: Use #ges_timeline_element_set_child_properties
1029  */
1030 void
1031 ges_track_element_set_child_properties (GESTrackElement * object,
1032     const gchar * first_property_name, ...)
1033 {
1034   va_list var_args;
1035
1036   g_return_if_fail (GES_IS_TRACK_ELEMENT (object));
1037
1038   va_start (var_args, first_property_name);
1039   ges_track_element_set_child_property_valist (object, first_property_name,
1040       var_args);
1041   va_end (var_args);
1042 }
1043
1044 /**
1045  * ges_track_element_get_child_property_valist: (skip):
1046  * @object: The #GESTrackElement parent object
1047  * @first_property_name: The name of the first property to get
1048  * @var_args: value for the first property, followed optionally by more
1049  * name/return location pairs, followed by NULL
1050  *
1051  * Gets a property of a child of @object. If there are various child elements
1052  * that have the same property name, you can distinguish them using the following
1053  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1054  * corresponding property of the first element found will be set.
1055  *
1056  * Deprecated: Use #ges_timeline_element_get_child_property_valist
1057  */
1058 void
1059 ges_track_element_get_child_property_valist (GESTrackElement * object,
1060     const gchar * first_property_name, va_list var_args)
1061 {
1062   ges_timeline_element_get_child_property_valist (GES_TIMELINE_ELEMENT (object),
1063       first_property_name, var_args);
1064 }
1065
1066 /**
1067  * ges_track_element_list_children_properties:
1068  * @object: The #GESTrackElement to get the list of children properties from
1069  * @n_properties: (out): return location for the length of the returned array
1070  *
1071  * Gets an array of #GParamSpec* for all configurable properties of the
1072  * children of @object.
1073  *
1074  * Returns: (transfer full) (array length=n_properties): an array of #GParamSpec* which should be freed after use or
1075  * %NULL if something went wrong
1076  *
1077  * Deprecated: Use #ges_timeline_element_list_children_properties
1078  */
1079 GParamSpec **
1080 ges_track_element_list_children_properties (GESTrackElement * object,
1081     guint * n_properties)
1082 {
1083   return
1084       ges_timeline_element_list_children_properties (GES_TIMELINE_ELEMENT
1085       (object), n_properties);
1086 }
1087
1088 /**
1089  * ges_track_element_get_child_properties: (skip):
1090  * @object: The origin #GESTrackElement
1091  * @first_property_name: The name of the first property to get
1092  * @...: return location for the first property, followed optionally by more
1093  * name/return location pairs, followed by NULL
1094  *
1095  * Gets properties of a child of @object.
1096  *
1097  * Deprecated: Use #ges_timeline_element_get_child_properties
1098  */
1099 void
1100 ges_track_element_get_child_properties (GESTrackElement * object,
1101     const gchar * first_property_name, ...)
1102 {
1103   va_list var_args;
1104
1105   g_return_if_fail (GES_IS_TRACK_ELEMENT (object));
1106
1107   va_start (var_args, first_property_name);
1108   ges_track_element_get_child_property_valist (object, first_property_name,
1109       var_args);
1110   va_end (var_args);
1111 }
1112
1113 /**
1114  * ges_track_element_get_child_property_by_pspec: (skip):
1115  * @object: a #GESTrackElement
1116  * @pspec: The #GParamSpec that specifies the property you want to get
1117  * @value: (out): return location for the value
1118  *
1119  * Gets a property of a child of @object.
1120  *
1121  * Deprecated: Use #ges_timeline_element_get_child_property_by_pspec
1122  */
1123 void
1124 ges_track_element_get_child_property_by_pspec (GESTrackElement * object,
1125     GParamSpec * pspec, GValue * value)
1126 {
1127   ges_timeline_element_get_child_property_by_pspec (GES_TIMELINE_ELEMENT
1128       (object), pspec, value);
1129 }
1130
1131 /**
1132  * ges_track_element_set_child_property: (skip):
1133  * @object: The origin #GESTrackElement
1134  * @property_name: The name of the property
1135  * @value: the value
1136  *
1137  * Sets a property of a GstElement contained in @object.
1138  *
1139  * Note that #ges_track_element_set_child_property is really
1140  * intended for language bindings, #ges_track_element_set_child_properties
1141  * is much more convenient for C programming.
1142  *
1143  * Returns: %TRUE if the property was set, %FALSE otherwize
1144  *
1145  * Deprecated: use #ges_timeline_element_set_child_property instead
1146  */
1147 gboolean
1148 ges_track_element_set_child_property (GESTrackElement * object,
1149     const gchar * property_name, GValue * value)
1150 {
1151   return ges_timeline_element_set_child_property (GES_TIMELINE_ELEMENT (object),
1152       property_name, value);
1153 }
1154
1155 /**
1156  * ges_track_element_get_child_property: (skip):
1157  * @object: The origin #GESTrackElement
1158  * @property_name: The name of the property
1159  * @value: (out): return location for the property value, it will
1160  * be initialized if it is initialized with 0
1161  *
1162  * In general, a copy is made of the property contents and
1163  * the caller is responsible for freeing the memory by calling
1164  * g_value_unset().
1165  *
1166  * Gets a property of a GstElement contained in @object.
1167  *
1168  * Note that #ges_track_element_get_child_property is really
1169  * intended for language bindings, #ges_track_element_get_child_properties
1170  * is much more convenient for C programming.
1171  *
1172  * Returns: %TRUE if the property was found, %FALSE otherwize
1173  *
1174  * Deprecated: Use #ges_timeline_element_get_child_property
1175  */
1176 gboolean
1177 ges_track_element_get_child_property (GESTrackElement * object,
1178     const gchar * property_name, GValue * value)
1179 {
1180   return ges_timeline_element_get_child_property (GES_TIMELINE_ELEMENT (object),
1181       property_name, value);
1182 }
1183
1184 static GParamSpec **
1185 default_list_children_properties (GESTrackElement * object,
1186     guint * n_properties)
1187 {
1188   return
1189       GES_TIMELINE_ELEMENT_GET_CLASS (object)->list_children_properties
1190       (GES_TIMELINE_ELEMENT (object), n_properties);
1191 }
1192
1193 void
1194 ges_track_element_copy_properties (GESTimelineElement * element,
1195     GESTimelineElement * elementcopy)
1196 {
1197   GParamSpec **specs;
1198   guint n, n_specs;
1199   GValue val = { 0 };
1200   GESTrackElement *copy = GES_TRACK_ELEMENT (elementcopy);
1201
1202   specs =
1203       ges_track_element_list_children_properties (GES_TRACK_ELEMENT (element),
1204       &n_specs);
1205   for (n = 0; n < n_specs; ++n) {
1206     if (!(specs[n]->flags & G_PARAM_WRITABLE))
1207       continue;
1208     g_value_init (&val, specs[n]->value_type);
1209     ges_track_element_get_child_property_by_pspec (GES_TRACK_ELEMENT (element),
1210         specs[n], &val);
1211     ges_track_element_set_child_property_by_pspec (copy, specs[n], &val);
1212     g_value_unset (&val);
1213   }
1214
1215   g_free (specs);
1216 }
1217
1218 static void
1219 _split_binding (GESTrackElement * element, GESTrackElement * new_element,
1220     guint64 position, GstTimedValueControlSource * source,
1221     GstTimedValueControlSource * new_source, gboolean absolute)
1222 {
1223   GstTimedValue *last_value = NULL;
1224   gboolean past_position = FALSE;
1225   GList *values, *tmp;
1226
1227   values =
1228       gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
1229       (source));
1230
1231   for (tmp = values; tmp; tmp = tmp->next) {
1232     GstTimedValue *value = tmp->data;
1233
1234     if (value->timestamp > position && !past_position) {
1235       gfloat value_at_pos;
1236
1237       /* FIXME We should be able to use gst_control_source_get_value so
1238        * all modes are handled. Right now that method only works if the value
1239        * we are looking for is between two actual keyframes which is not enough
1240        * in our case. bug #706621 */
1241       value_at_pos =
1242           interpolate_values_for_position (last_value, value, position,
1243           absolute);
1244
1245       past_position = TRUE;
1246
1247       gst_timed_value_control_source_set (new_source, position, value_at_pos);
1248       gst_timed_value_control_source_set (new_source, value->timestamp,
1249           value->value);
1250
1251       gst_timed_value_control_source_unset (source, value->timestamp);
1252       gst_timed_value_control_source_set (source, position, value_at_pos);
1253     } else if (past_position) {
1254       gst_timed_value_control_source_set (new_source, value->timestamp,
1255           value->value);
1256       gst_timed_value_control_source_unset (source, value->timestamp);
1257     }
1258     last_value = value;
1259
1260   }
1261 }
1262
1263 static void
1264 _copy_binding (GESTrackElement * element, GESTrackElement * new_element,
1265     guint64 position, GstTimedValueControlSource * source,
1266     GstTimedValueControlSource * new_source, gboolean absolute)
1267 {
1268   GList *values, *tmp;
1269
1270   values =
1271       gst_timed_value_control_source_get_all (GST_TIMED_VALUE_CONTROL_SOURCE
1272       (source));
1273   for (tmp = values; tmp; tmp = tmp->next) {
1274     GstTimedValue *value = tmp->data;
1275
1276     gst_timed_value_control_source_set (new_source, value->timestamp,
1277         value->value);
1278   }
1279 }
1280
1281 /* position == GST_CLOCK_TIME_NONE means that we do a simple copy
1282  * other position means that the function will do a splitting
1283  * and thus interpollate the values in the element and new_element
1284  */
1285 void
1286 ges_track_element_copy_bindings (GESTrackElement * element,
1287     GESTrackElement * new_element, guint64 position)
1288 {
1289   GParamSpec **specs;
1290   guint n, n_specs;
1291   gboolean absolute;
1292   GstControlBinding *binding;
1293   GstTimedValueControlSource *source, *new_source;
1294
1295   specs =
1296       ges_track_element_list_children_properties (GES_TRACK_ELEMENT (element),
1297       &n_specs);
1298   for (n = 0; n < n_specs; ++n) {
1299     GstInterpolationMode mode;
1300
1301     binding = ges_track_element_get_control_binding (element, specs[n]->name);
1302     if (!binding)
1303       continue;
1304
1305     /* FIXME : this should work as well with other types of control sources */
1306     g_object_get (binding, "control_source", &source, NULL);
1307     if (!GST_IS_TIMED_VALUE_CONTROL_SOURCE (source))
1308       continue;
1309
1310     g_object_get (binding, "absolute", &absolute, NULL);
1311     g_object_get (source, "mode", &mode, NULL);
1312
1313     new_source =
1314         GST_TIMED_VALUE_CONTROL_SOURCE (gst_interpolation_control_source_new
1315         ());
1316     g_object_set (new_source, "mode", mode, NULL);
1317
1318     if (GST_CLOCK_TIME_IS_VALID (position))
1319       _split_binding (element, new_element, position, source, new_source,
1320           absolute);
1321     else
1322       _copy_binding (element, new_element, position, source, new_source,
1323           absolute);
1324
1325     /* We only manage direct (absolute) bindings, see TODO in set_control_source */
1326     if (absolute)
1327       ges_track_element_set_control_source (new_element,
1328           GST_CONTROL_SOURCE (new_source), specs[n]->name, "direct-absolute");
1329     else
1330       ges_track_element_set_control_source (new_element,
1331           GST_CONTROL_SOURCE (new_source), specs[n]->name, "direct");
1332   }
1333
1334   g_free (specs);
1335 }
1336
1337 /**
1338  * ges_track_element_edit:
1339  * @object: the #GESTrackElement to edit
1340  * @layers: (element-type GESLayer): The layers you want the edit to
1341  *  happen in, %NULL means that the edition is done in all the
1342  *  #GESLayers contained in the current timeline.
1343  *      FIXME: This is not implemented yet.
1344  * @mode: The #GESEditMode in which the edition will happen.
1345  * @edge: The #GESEdge the edit should happen on.
1346  * @position: The position at which to edit @object (in nanosecond)
1347  *
1348  * Edit @object in the different exisiting #GESEditMode modes. In the case of
1349  * slide, and roll, you need to specify a #GESEdge
1350  *
1351  * Returns: %TRUE if the object as been edited properly, %FALSE if an error
1352  * occured
1353  */
1354 gboolean
1355 ges_track_element_edit (GESTrackElement * object,
1356     GList * layers, GESEditMode mode, GESEdge edge, guint64 position)
1357 {
1358   GESTrack *track = ges_track_element_get_track (object);
1359   GESTimeline *timeline;
1360
1361   g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
1362
1363   if (G_UNLIKELY (!track)) {
1364     GST_WARNING_OBJECT (object, "Trying to edit in %d mode but not in "
1365         "any Track yet.", mode);
1366     return FALSE;
1367   }
1368
1369   timeline = GES_TIMELINE (ges_track_get_timeline (track));
1370
1371   if (G_UNLIKELY (!timeline)) {
1372     GST_WARNING_OBJECT (object, "Trying to edit in %d mode but "
1373         "track %p is not in any timeline yet.", mode, track);
1374     return FALSE;
1375   }
1376
1377   switch (mode) {
1378     case GES_EDIT_MODE_NORMAL:
1379       return timeline_move_object (timeline, object, layers, edge, position);
1380       break;
1381     case GES_EDIT_MODE_TRIM:
1382       return timeline_trim_object (timeline, object, layers, edge, position);
1383       break;
1384     case GES_EDIT_MODE_RIPPLE:
1385       return timeline_ripple_object (timeline, object, layers, edge, position);
1386       break;
1387     case GES_EDIT_MODE_ROLL:
1388       return timeline_roll_object (timeline, object, layers, edge, position);
1389       break;
1390     case GES_EDIT_MODE_SLIDE:
1391       return timeline_slide_object (timeline, object, layers, edge, position);
1392       break;
1393     default:
1394       GST_ERROR ("Unkown edit mode: %d", mode);
1395       return FALSE;
1396   }
1397
1398   return TRUE;
1399 }
1400
1401 /**
1402  * ges_track_element_remove_control_binding:
1403  * @object: the #GESTrackElement on which to set a control binding
1404  * @property_name: The name of the property to control.
1405  *
1406  * Removes a #GstControlBinding from @object.
1407  *
1408  * Returns: %TRUE if the binding could be removed, %FALSE if an error
1409  * occured
1410  */
1411 gboolean
1412 ges_track_element_remove_control_binding (GESTrackElement * object,
1413     const gchar * property_name)
1414 {
1415   GESTrackElementPrivate *priv;
1416   GstControlBinding *binding;
1417   GstObject *target;
1418
1419   g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
1420
1421   priv = GES_TRACK_ELEMENT (object)->priv;
1422   binding =
1423       (GstControlBinding *) g_hash_table_lookup (priv->bindings_hashtable,
1424       property_name);
1425
1426   if (binding) {
1427     g_object_get (binding, "object", &target, NULL);
1428     GST_DEBUG_OBJECT (object, "Removing binding %p for property %s", binding,
1429         property_name);
1430
1431     gst_object_ref (binding);
1432     gst_object_remove_control_binding (target, binding);
1433
1434     g_signal_emit (object, ges_track_element_signals[CONTROL_BINDING_REMOVED],
1435         0, binding);
1436
1437     gst_object_unref (target);
1438     gst_object_unref (binding);
1439     g_hash_table_remove (priv->bindings_hashtable, property_name);
1440
1441     return TRUE;
1442   }
1443
1444   return FALSE;
1445 }
1446
1447 /**
1448  * ges_track_element_set_control_source:
1449  * @object: the #GESTrackElement on which to set a control binding
1450  * @source: the #GstControlSource to set on the binding.
1451  * @property_name: The name of the property to control.
1452  * @binding_type: The type of binding to create. Only "direct" is available for now.
1453  *
1454  * Creates a #GstControlBinding and adds it to the #GstElement concerned by the
1455  * property. Use the same syntax as #ges_track_element_lookup_child for
1456  * the property name.
1457  *
1458  * Returns: %TRUE if the binding could be created and added, %FALSE if an error
1459  * occured
1460  */
1461 gboolean
1462 ges_track_element_set_control_source (GESTrackElement * object,
1463     GstControlSource * source,
1464     const gchar * property_name, const gchar * binding_type)
1465 {
1466   GESTrackElementPrivate *priv;
1467   GstElement *element;
1468   GParamSpec *pspec;
1469   GstControlBinding *binding;
1470   gboolean direct, direct_absolute;
1471
1472   g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), FALSE);
1473   priv = GES_TRACK_ELEMENT (object)->priv;
1474
1475   if (G_UNLIKELY (!(GST_IS_CONTROL_SOURCE (source)))) {
1476     GST_WARNING
1477         ("You need to provide a non-null control source to build a new control binding");
1478     return FALSE;
1479   }
1480
1481   if (!ges_track_element_lookup_child (object, property_name, &element, &pspec)) {
1482     GST_WARNING ("You need to provide a valid and controllable property name");
1483     return FALSE;
1484   }
1485
1486   /* TODO : update this according to new types of bindings */
1487   direct = !g_strcmp0 (binding_type, "direct");
1488   direct_absolute = !g_strcmp0 (binding_type, "direct-absolute");
1489
1490   if (direct || direct_absolute) {
1491     /* First remove existing binding */
1492     binding =
1493         (GstControlBinding *) g_hash_table_lookup (priv->bindings_hashtable,
1494         property_name);
1495     if (binding) {
1496       GST_LOG ("Removing old binding %p for property %s", binding,
1497           property_name);
1498       gst_object_remove_control_binding (GST_OBJECT (element), binding);
1499     }
1500
1501     if (direct_absolute)
1502       binding =
1503           gst_direct_control_binding_new_absolute (GST_OBJECT (element),
1504           property_name, source);
1505     else
1506       binding =
1507           gst_direct_control_binding_new (GST_OBJECT (element), property_name,
1508           source);
1509
1510     gst_object_add_control_binding (GST_OBJECT (element), binding);
1511     g_hash_table_insert (priv->bindings_hashtable, g_strdup (property_name),
1512         binding);
1513     g_signal_emit (object, ges_track_element_signals[CONTROL_BINDING_ADDED],
1514         0, binding);
1515     return TRUE;
1516   }
1517
1518   GST_WARNING ("Binding type must be in [direct]");
1519
1520   return FALSE;
1521 }
1522
1523 /**
1524  * ges_track_element_get_control_binding:
1525  * @object: the #GESTrackElement in which to lookup the bindings.
1526  * @property_name: The property_name to which the binding is associated.
1527  *
1528  * Looks up the various controlled properties for that #GESTrackElement,
1529  * and returns the #GstControlBinding which controls @property_name.
1530  *
1531  * Returns: (transfer none) (nullable): the #GstControlBinding associated with
1532  * @property_name, or %NULL if that property is not controlled.
1533  */
1534 GstControlBinding *
1535 ges_track_element_get_control_binding (GESTrackElement * object,
1536     const gchar * property_name)
1537 {
1538   GESTrackElementPrivate *priv;
1539   GstControlBinding *binding;
1540
1541   g_return_val_if_fail (GES_IS_TRACK_ELEMENT (object), NULL);
1542
1543   priv = GES_TRACK_ELEMENT (object)->priv;
1544
1545   binding =
1546       (GstControlBinding *) g_hash_table_lookup (priv->bindings_hashtable,
1547       property_name);
1548   return binding;
1549 }