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