Merge remote-tracking branch 'origin/0.10'
[platform/upstream/gstreamer.git] / ges / ges-track-object.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., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:ges-track-object
23  * @short_description: Base Class for objects contained in a #GESTrack
24  *
25  * #GESTrackObject is the Base Class for any object that can be contained in a
26  * #GESTrack.
27  *
28  * It contains the basic information as to the location of the object within
29  * its container, like the start position, the in-point, the duration and the
30  * priority.
31  */
32
33 #include "ges-internal.h"
34 #include "ges-track-object.h"
35 #include "ges-timeline-object.h"
36 #include <gobject/gvaluecollector.h>
37
38 G_DEFINE_ABSTRACT_TYPE (GESTrackObject, ges_track_object,
39     G_TYPE_INITIALLY_UNOWNED);
40
41 struct _GESTrackObjectPrivate
42 {
43   /* These fields are only used before the gnlobject is available */
44   guint64 pending_start;
45   guint64 pending_inpoint;
46   guint64 pending_duration;
47   guint32 pending_priority;
48   gboolean pending_active;
49
50   GstElement *gnlobject;        /* The GnlObject */
51   GstElement *element;          /* The element contained in the gnlobject (can be NULL) */
52
53   /* We keep a link between properties name and elements internally
54    * The hashtable should look like
55    * {GParamaSpec ---> element,}*/
56   GHashTable *properties_hashtable;
57
58   GESTimelineObject *timelineobj;
59   GESTrack *track;
60
61   gboolean valid;
62
63   guint64 maxduration;
64
65   gboolean locked;              /* If TRUE, then moves in sync with its controlling
66                                  * GESTimelineObject */
67 };
68
69 enum
70 {
71   PROP_0,
72   PROP_START,
73   PROP_INPOINT,
74   PROP_DURATION,
75   PROP_PRIORITY,
76   PROP_ACTIVE,
77   PROP_LOCKED,
78   PROP_MAX_DURATION,
79   PROP_LAST
80 };
81
82 static GParamSpec *properties[PROP_LAST];
83
84 enum
85 {
86   DEEP_NOTIFY,
87   LAST_SIGNAL
88 };
89
90 static guint ges_track_object_signals[LAST_SIGNAL] = { 0 };
91
92 static GstElement *ges_track_object_create_gnl_object_func (GESTrackObject *
93     object);
94
95 static void gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg
96     G_GNUC_UNUSED, GESTrackObject * obj);
97
98 static void gnlobject_media_start_cb (GstElement * gnlobject, GParamSpec * arg
99     G_GNUC_UNUSED, GESTrackObject * obj);
100
101 static void gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg
102     G_GNUC_UNUSED, GESTrackObject * obj);
103
104 static void gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg
105     G_GNUC_UNUSED, GESTrackObject * obj);
106
107 static void gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg
108     G_GNUC_UNUSED, GESTrackObject * obj);
109
110 static void connect_properties_signals (GESTrackObject * object);
111 static void connect_signal (gpointer key, gpointer value, gpointer user_data);
112 static void gst_element_prop_changed_cb (GstElement * element, GParamSpec * arg
113     G_GNUC_UNUSED, GESTrackObject * obj);
114
115 static inline gboolean
116 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start);
117 static inline gboolean
118 ges_track_object_set_inpoint_internal (GESTrackObject * object,
119     guint64 inpoint);
120 static inline gboolean ges_track_object_set_duration_internal (GESTrackObject *
121     object, guint64 duration);
122 static inline gboolean ges_track_object_set_priority_internal (GESTrackObject *
123     object, guint32 priority);
124 static inline void
125 ges_track_object_set_locked_internal (GESTrackObject * object, gboolean locked);
126
127 static GParamSpec **default_list_children_properties (GESTrackObject * object,
128     guint * n_properties);
129
130 static void
131 ges_track_object_get_property (GObject * object, guint property_id,
132     GValue * value, GParamSpec * pspec)
133 {
134   GESTrackObject *tobj = GES_TRACK_OBJECT (object);
135
136   switch (property_id) {
137     case PROP_START:
138       g_value_set_uint64 (value, ges_track_object_get_start (tobj));
139       break;
140     case PROP_INPOINT:
141       g_value_set_uint64 (value, ges_track_object_get_inpoint (tobj));
142       break;
143     case PROP_DURATION:
144       g_value_set_uint64 (value, ges_track_object_get_duration (tobj));
145       break;
146     case PROP_PRIORITY:
147       g_value_set_uint (value, ges_track_object_get_priority (tobj));
148       break;
149     case PROP_ACTIVE:
150       g_value_set_boolean (value, ges_track_object_is_active (tobj));
151       break;
152     case PROP_LOCKED:
153       g_value_set_boolean (value, ges_track_object_is_locked (tobj));
154       break;
155     case PROP_MAX_DURATION:
156       g_value_set_uint64 (value, tobj->priv->maxduration);
157       break;
158     default:
159       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
160   }
161 }
162
163 static void
164 ges_track_object_set_property (GObject * object, guint property_id,
165     const GValue * value, GParamSpec * pspec)
166 {
167   GESTrackObject *tobj = GES_TRACK_OBJECT (object);
168
169   switch (property_id) {
170     case PROP_START:
171       ges_track_object_set_start_internal (tobj, g_value_get_uint64 (value));
172       break;
173     case PROP_INPOINT:
174       ges_track_object_set_inpoint_internal (tobj, g_value_get_uint64 (value));
175       break;
176     case PROP_DURATION:
177       ges_track_object_set_duration_internal (tobj, g_value_get_uint64 (value));
178       break;
179     case PROP_PRIORITY:
180       ges_track_object_set_priority_internal (tobj, g_value_get_uint (value));
181       break;
182     case PROP_ACTIVE:
183       ges_track_object_set_active (tobj, g_value_get_boolean (value));
184       break;
185     case PROP_LOCKED:
186       ges_track_object_set_locked_internal (tobj, g_value_get_boolean (value));
187       break;
188     case PROP_MAX_DURATION:
189       ges_track_object_set_max_duration (tobj, g_value_get_uint64 (value));
190       break;
191     default:
192       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
193   }
194 }
195
196 static void
197 ges_track_object_dispose (GObject * object)
198 {
199   GESTrackObjectPrivate *priv = GES_TRACK_OBJECT (object)->priv;
200   if (priv->properties_hashtable)
201     g_hash_table_destroy (priv->properties_hashtable);
202
203   G_OBJECT_CLASS (ges_track_object_parent_class)->dispose (object);
204 }
205
206 static void
207 ges_track_object_finalize (GObject * object)
208 {
209   G_OBJECT_CLASS (ges_track_object_parent_class)->finalize (object);
210 }
211
212 static void
213 ges_track_object_class_init (GESTrackObjectClass * klass)
214 {
215   GObjectClass *object_class = G_OBJECT_CLASS (klass);
216
217   g_type_class_add_private (klass, sizeof (GESTrackObjectPrivate));
218
219   object_class->get_property = ges_track_object_get_property;
220   object_class->set_property = ges_track_object_set_property;
221   object_class->dispose = ges_track_object_dispose;
222   object_class->finalize = ges_track_object_finalize;
223
224   /**
225    * GESTrackObject:start
226    *
227    * The position of the object in the container #GESTrack (in nanoseconds).
228    */
229   properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
230       "The position in the container", 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
231   g_object_class_install_property (object_class, PROP_START,
232       properties[PROP_START]);
233
234   /**
235    * GESTrackObject:in-point
236    *
237    * The in-point at which this #GESTrackObject will start outputting data
238    * from its contents (in nanoseconds).
239    *
240    * Ex: an in-point of 5 seconds means that the first outputted buffer will
241    * be the one located 5 seconds in the controlled resource.
242    */
243   properties[PROP_INPOINT] =
244       g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
245       G_MAXUINT64, 0, G_PARAM_READWRITE);
246   g_object_class_install_property (object_class, PROP_INPOINT,
247       properties[PROP_INPOINT]);
248
249   /**
250    * GESTrackObject:duration
251    *
252    * The duration (in nanoseconds) which will be used in the container #GESTrack
253    * starting from 'in-point'.
254    *
255    */
256   properties[PROP_DURATION] =
257       g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
258       G_MAXUINT64, GST_SECOND, G_PARAM_READWRITE);
259   g_object_class_install_property (object_class, PROP_DURATION,
260       properties[PROP_DURATION]);
261
262   /**
263    * GESTrackObject:priority
264    *
265    * The priority of the object within the containing #GESTrack.
266    * If two objects intersect over the same region of time, the @priority
267    * property is used to decide which one takes precedence.
268    *
269    * The highest priority (that supercedes everything) is 0, and then lowering
270    * priorities go in increasing numerical value (with #G_MAXUINT64 being the
271    * lowest priority).
272    */
273   properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
274       "The priority of the object", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
275   g_object_class_install_property (object_class, PROP_PRIORITY,
276       properties[PROP_PRIORITY]);
277
278   /**
279    * GESTrackObject:active
280    *
281    * Whether the object should be taken into account in the #GESTrack output.
282    * If #FALSE, then its contents will not be used in the resulting track.
283    */
284   properties[PROP_ACTIVE] =
285       g_param_spec_boolean ("active", "Active", "Use object in output", TRUE,
286       G_PARAM_READWRITE);
287   g_object_class_install_property (object_class, PROP_ACTIVE,
288       properties[PROP_ACTIVE]);
289
290   /**
291    * GESTrackObject:locked
292    *
293    * If %TRUE, then moves in sync with its controlling #GESTimelineObject
294    */
295   properties[PROP_LOCKED] =
296       g_param_spec_boolean ("locked", "Locked",
297       "Moves in sync with its controling TimelineObject", TRUE,
298       G_PARAM_READWRITE);
299   g_object_class_install_property (object_class, PROP_LOCKED,
300       properties[PROP_LOCKED]);
301
302   /**
303    * GESTrackObject:max-duration:
304    *
305    * The maximum duration (in nanoseconds) of the #GESTrackObject.
306    *
307    * Since: 0.10.XX
308    */
309   g_object_class_install_property (object_class, PROP_MAX_DURATION,
310       g_param_spec_uint64 ("max-duration", "Maximum duration",
311           "The duration of the object", GST_CLOCK_TIME_NONE, G_MAXUINT64,
312           G_MAXUINT64, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
313
314   /**
315    * GESTrackObject::deep-notify:
316    * @track_object: a #GESTrackObject
317    * @prop_object: the object that originated the signal
318    * @prop: the property that changed
319    *
320    * The deep notify signal is used to be notified of property changes of all
321    * the childs of @track_object
322    *
323    * Since: 0.10.2
324    */
325   ges_track_object_signals[DEEP_NOTIFY] =
326       g_signal_new ("deep-notify", G_TYPE_FROM_CLASS (klass),
327       G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED |
328       G_SIGNAL_NO_HOOKS, 0, NULL, NULL, g_cclosure_marshal_generic,
329       G_TYPE_NONE, 2, GST_TYPE_ELEMENT, G_TYPE_PARAM);
330
331   klass->create_gnl_object = ges_track_object_create_gnl_object_func;
332   /*  There is no 'get_props_hashtable' default implementation */
333   klass->get_props_hastable = NULL;
334   klass->list_children_properties = default_list_children_properties;
335 }
336
337 static void
338 ges_track_object_init (GESTrackObject * self)
339 {
340   GESTrackObjectPrivate *priv = self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
341       GES_TYPE_TRACK_OBJECT, GESTrackObjectPrivate);
342
343   /* Sane default values */
344   priv->pending_start = 0;
345   priv->pending_inpoint = 0;
346   priv->pending_duration = GST_SECOND;
347   priv->pending_priority = 1;
348   priv->pending_active = TRUE;
349   priv->locked = TRUE;
350   priv->properties_hashtable = NULL;
351   priv->maxduration = GST_CLOCK_TIME_NONE;
352 }
353
354 static inline gboolean
355 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start)
356 {
357   GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
358       object, GST_TIME_ARGS (start));
359
360   if (object->priv->gnlobject != NULL) {
361     if (G_UNLIKELY (start == object->start))
362       return FALSE;
363
364     g_object_set (object->priv->gnlobject, "start", start, NULL);
365   } else
366     object->priv->pending_start = start;
367   return TRUE;
368 };
369
370 /**
371  * ges_track_object_set_start:
372  * @object: a #GESTrackObject
373  * @start: the start position (in #GstClockTime)
374  *
375  * Sets the position of the object in the container #GESTrack.
376  */
377 void
378 ges_track_object_set_start (GESTrackObject * object, guint64 start)
379 {
380   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
381
382   if (ges_track_object_set_start_internal (object, start))
383 #if GLIB_CHECK_VERSION(2,26,0)
384     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
385 #else
386     g_object_notify (G_OBJECT (object), "start");
387 #endif
388 }
389
390 static inline gboolean
391 ges_track_object_set_inpoint_internal (GESTrackObject * object, guint64 inpoint)
392 {
393
394   GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
395       object, GST_TIME_ARGS (inpoint));
396
397   if (object->priv->gnlobject != NULL) {
398     if (G_UNLIKELY (inpoint == object->inpoint))
399       return FALSE;
400
401     g_object_set (object->priv->gnlobject, "media-start", inpoint, NULL);
402   } else
403     object->priv->pending_inpoint = inpoint;
404
405   return TRUE;
406 }
407
408 /**
409  * ges_track_object_set_inpoint:
410  * @object: a #GESTrackObject
411  * @inpoint: the in-point (in #GstClockTime)
412  *
413  * Set the offset within the contents of this #GESTrackObject
414  */
415 void
416 ges_track_object_set_inpoint (GESTrackObject * object, guint64 inpoint)
417 {
418   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
419
420   if (ges_track_object_set_inpoint_internal (object, inpoint))
421 #if GLIB_CHECK_VERSION(2,26,0)
422     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
423 #else
424     g_object_notify (G_OBJECT (object), "in-point");
425 #endif
426 }
427
428 static inline gboolean
429 ges_track_object_set_duration_internal (GESTrackObject * object,
430     guint64 duration)
431 {
432   GESTrackObjectPrivate *priv = object->priv;
433
434   GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
435       object, GST_TIME_ARGS (duration));
436
437   if (GST_CLOCK_TIME_IS_VALID (priv->maxduration) &&
438       duration > object->inpoint + priv->maxduration)
439     duration = priv->maxduration - object->inpoint;
440
441   if (priv->gnlobject != NULL) {
442     if (G_UNLIKELY (duration == object->duration))
443       return FALSE;
444
445     g_object_set (priv->gnlobject, "duration", duration,
446         "media-duration", duration, NULL);
447   } else
448     priv->pending_duration = duration;
449
450   return TRUE;
451 }
452
453 /**
454  * ges_track_object_set_duration:
455  * @object: a #GESTrackObject
456  * @duration: the duration (in #GstClockTime)
457  *
458  * Set the duration which will be used in the container #GESTrack
459  * starting from the 'in-point'
460  */
461 void
462 ges_track_object_set_duration (GESTrackObject * object, guint64 duration)
463 {
464   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
465
466   if (ges_track_object_set_duration_internal (object, duration))
467 #if GLIB_CHECK_VERSION(2,26,0)
468     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
469 #else
470     g_object_notify (G_OBJECT (object), "duration");
471 #endif
472 }
473
474 static inline gboolean
475 ges_track_object_set_priority_internal (GESTrackObject * object,
476     guint32 priority)
477 {
478   GST_DEBUG ("object:%p, priority:%" G_GUINT32_FORMAT, object, priority);
479
480   if (object->priv->gnlobject != NULL) {
481     if (G_UNLIKELY (priority == object->priority))
482       return FALSE;
483
484     g_object_set (object->priv->gnlobject, "priority", priority, NULL);
485   } else
486     object->priv->pending_priority = priority;
487   return TRUE;
488 }
489
490 /**
491  * ges_track_object_set_priority:
492  * @object: a #GESTrackObject
493  * @priority: the priority
494  *
495  * Sets the priority of the object withing the containing #GESTrack.
496  * If two objects intersect over the same region of time, the priority
497  * property is used to decide which one takes precedence.
498  *
499  * The highest priority (that supercedes everything) is 0, and then
500  * lowering priorities go in increasing numerical value (with G_MAXUINT32
501  * being the lowest priority).
502  */
503 void
504 ges_track_object_set_priority (GESTrackObject * object, guint32 priority)
505 {
506   if (ges_track_object_set_priority_internal (object, priority))
507 #if GLIB_CHECK_VERSION(2,26,0)
508     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_PRIORITY]);
509 #else
510     g_object_notify (G_OBJECT (object), "priority");
511 #endif
512 }
513
514
515 /**
516  * ges_track_object_set_active:
517  * @object: a #GESTrackObject
518  * @active: visibility
519  *
520  * Sets the usage of the @object. If @active is %TRUE, the object will be used for
521  * playback and rendering, else it will be ignored.
522  *
523  * Returns: %TRUE if the property was toggled, else %FALSE
524  */
525 gboolean
526 ges_track_object_set_active (GESTrackObject * object, gboolean active)
527 {
528   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
529
530   GST_DEBUG ("object:%p, active:%d", object, active);
531
532   if (object->priv->gnlobject != NULL) {
533     if (G_UNLIKELY (active == object->active))
534       return FALSE;
535
536     g_object_set (object->priv->gnlobject, "active", active, NULL);
537   } else
538     object->priv->pending_active = active;
539   return TRUE;
540 }
541
542 /* Callbacks from the GNonLin object */
543 static void
544 gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
545     GESTrackObject * obj)
546 {
547   guint64 start;
548   GESTrackObjectClass *klass;
549
550   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
551
552   g_object_get (gnlobject, "start", &start, NULL);
553
554   GST_DEBUG ("gnlobject start : %" GST_TIME_FORMAT " current : %"
555       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->start));
556
557   if (start != obj->start) {
558     obj->start = start;
559     if (klass->start_changed)
560       klass->start_changed (obj, start);
561   }
562 }
563
564 static void
565 gst_element_prop_changed_cb (GstElement * element, GParamSpec * arg
566     G_GNUC_UNUSED, GESTrackObject * obj)
567 {
568   g_signal_emit (obj, ges_track_object_signals[DEEP_NOTIFY], 0,
569       GST_ELEMENT (element), arg);
570 }
571
572 static void
573 connect_signal (gpointer key, gpointer value, gpointer user_data)
574 {
575   gchar *signame = g_strconcat ("notify::", G_PARAM_SPEC (key)->name, NULL);
576
577   g_signal_connect (G_OBJECT (value),
578       signame, G_CALLBACK (gst_element_prop_changed_cb),
579       GES_TRACK_OBJECT (user_data));
580
581   g_free (signame);
582 }
583
584 static void
585 connect_properties_signals (GESTrackObject * object)
586 {
587   if (G_UNLIKELY (!object->priv->properties_hashtable)) {
588     GST_WARNING ("The properties_hashtable hasn't been set");
589     return;
590   }
591
592   g_hash_table_foreach (object->priv->properties_hashtable,
593       (GHFunc) connect_signal, object);
594
595 }
596
597 /* Callbacks from the GNonLin object */
598 static void
599 gnlobject_media_start_cb (GstElement * gnlobject,
600     GParamSpec * arg G_GNUC_UNUSED, GESTrackObject * obj)
601 {
602   guint64 start;
603   GESTrackObjectClass *klass;
604
605   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
606
607   g_object_get (gnlobject, "media-start", &start, NULL);
608
609   GST_DEBUG ("gnlobject in-point : %" GST_TIME_FORMAT " current : %"
610       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->inpoint));
611
612   if (start != obj->inpoint) {
613     obj->inpoint = start;
614     if (klass->media_start_changed)
615       klass->media_start_changed (obj, start);
616   }
617 }
618
619 static void
620 gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
621     GESTrackObject * obj)
622 {
623   guint32 priority;
624   GESTrackObjectClass *klass;
625
626   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
627
628   g_object_get (gnlobject, "priority", &priority, NULL);
629
630   GST_DEBUG ("gnlobject priority : %d current : %d", priority, obj->priority);
631
632   if (priority != obj->priority) {
633     obj->priority = priority;
634     if (klass->gnl_priority_changed)
635       klass->gnl_priority_changed (obj, priority);
636   }
637 }
638
639 static void
640 gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
641     GESTrackObject * obj)
642 {
643   guint64 duration;
644   GESTrackObjectClass *klass;
645
646   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
647
648   g_object_get (gnlobject, "duration", &duration, NULL);
649
650   GST_DEBUG_OBJECT (gnlobject, "duration : %" GST_TIME_FORMAT " current : %"
651       GST_TIME_FORMAT, GST_TIME_ARGS (duration), GST_TIME_ARGS (obj->duration));
652
653   if (duration != obj->duration) {
654     obj->duration = duration;
655     if (klass->duration_changed)
656       klass->duration_changed (obj, duration);
657   }
658 }
659
660 static void
661 gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
662     GESTrackObject * obj)
663 {
664   gboolean active;
665   GESTrackObjectClass *klass;
666
667   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
668
669   g_object_get (gnlobject, "active", &active, NULL);
670
671   GST_DEBUG ("gnlobject active : %d current : %d", active, obj->active);
672
673   if (active != obj->active) {
674     obj->active = active;
675     if (klass->active_changed)
676       klass->active_changed (obj, active);
677   }
678 }
679
680
681 /* default 'create_gnl_object' virtual method implementation */
682 static GstElement *
683 ges_track_object_create_gnl_object_func (GESTrackObject * self)
684 {
685   GESTrackObjectClass *klass = NULL;
686   GstElement *child = NULL;
687   GstElement *gnlobject;
688
689   klass = GES_TRACK_OBJECT_GET_CLASS (self);
690
691   if (G_UNLIKELY (self->priv->gnlobject != NULL))
692     goto already_have_gnlobject;
693
694   if (G_UNLIKELY (klass->gnlobject_factorytype == NULL))
695     goto no_gnlfactory;
696
697   GST_DEBUG ("Creating a supporting gnlobject of type '%s'",
698       klass->gnlobject_factorytype);
699
700   gnlobject = gst_element_factory_make (klass->gnlobject_factorytype, NULL);
701
702   if (G_UNLIKELY (gnlobject == NULL))
703     goto no_gnlobject;
704
705   if (klass->create_element) {
706     GST_DEBUG ("Calling subclass 'create_element' vmethod");
707     child = klass->create_element (self);
708
709     if (G_UNLIKELY (!child))
710       goto child_failure;
711
712     if (!gst_bin_add (GST_BIN (gnlobject), child))
713       goto add_failure;
714
715     GST_DEBUG ("Succesfully got the element to put in the gnlobject");
716     self->priv->element = child;
717   }
718
719   GST_DEBUG ("done");
720   return gnlobject;
721
722
723   /* ERROR CASES */
724
725 already_have_gnlobject:
726   {
727     GST_ERROR ("Already controlling a GnlObject %s",
728         GST_ELEMENT_NAME (self->priv->gnlobject));
729     return NULL;
730   }
731
732 no_gnlfactory:
733   {
734     GST_ERROR ("No GESTrackObject::gnlobject_factorytype implementation!");
735     return NULL;
736   }
737
738 no_gnlobject:
739   {
740     GST_ERROR ("Error creating a gnlobject of type '%s'",
741         klass->gnlobject_factorytype);
742     return NULL;
743   }
744
745 child_failure:
746   {
747     GST_ERROR ("create_element returned NULL");
748     gst_object_unref (gnlobject);
749     return NULL;
750   }
751
752 add_failure:
753   {
754     GST_ERROR ("Error adding the contents to the gnlobject");
755     gst_object_unref (child);
756     gst_object_unref (gnlobject);
757     return NULL;
758   }
759 }
760
761 static gboolean
762 ensure_gnl_object (GESTrackObject * object)
763 {
764   GESTrackObjectClass *class;
765   GstElement *gnlobject;
766   GHashTable *props_hash;
767   gboolean res = TRUE;
768
769   if (object->priv->gnlobject && object->priv->valid)
770     return FALSE;
771
772   /* 1. Create the GnlObject */
773   GST_DEBUG ("Creating GnlObject");
774
775   class = GES_TRACK_OBJECT_GET_CLASS (object);
776
777   if (G_UNLIKELY (class->create_gnl_object == NULL)) {
778     GST_ERROR ("No 'create_gnl_object' implementation !");
779     goto done;
780   }
781
782   GST_DEBUG ("Calling virtual method");
783
784   /* 2. Fill in the GnlObject */
785   if (object->priv->gnlobject == NULL) {
786
787     /* call the create_gnl_object virtual method */
788     gnlobject = class->create_gnl_object (object);
789
790     if (G_UNLIKELY (gnlobject == NULL)) {
791       GST_ERROR
792           ("'create_gnl_object' implementation returned TRUE but no GnlObject is available");
793       goto done;
794     }
795
796     GST_DEBUG_OBJECT (object, "Got a valid GnlObject, now filling it in");
797
798     object->priv->gnlobject = gnlobject;
799
800     if (object->priv->timelineobj)
801       res = ges_timeline_object_fill_track_object (object->priv->timelineobj,
802           object, object->priv->gnlobject);
803     else
804       res = TRUE;
805
806     if (res) {
807       /* Connect to property notifications */
808       /* FIXME : remember the signalids so we can remove them later on !!! */
809       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::start",
810           G_CALLBACK (gnlobject_start_cb), object);
811       g_signal_connect (G_OBJECT (object->priv->gnlobject),
812           "notify::media-start", G_CALLBACK (gnlobject_media_start_cb), object);
813       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::duration",
814           G_CALLBACK (gnlobject_duration_cb), object);
815       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::priority",
816           G_CALLBACK (gnlobject_priority_cb), object);
817       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::active",
818           G_CALLBACK (gnlobject_active_cb), object);
819
820       /* Set some properties on the GnlObject */
821       g_object_set (object->priv->gnlobject,
822           "duration", object->priv->pending_duration,
823           "media-duration", object->priv->pending_duration,
824           "start", object->priv->pending_start,
825           "media-start", object->priv->pending_inpoint,
826           "priority", object->priv->pending_priority,
827           "active", object->priv->pending_active, NULL);
828
829       if (object->priv->track != NULL)
830         g_object_set (object->priv->gnlobject,
831             "caps", ges_track_get_caps (object->priv->track), NULL);
832
833       /*  We feed up the props_hashtable if possible */
834       if (class->get_props_hastable) {
835         props_hash = class->get_props_hastable (object);
836
837         if (props_hash == NULL) {
838           GST_DEBUG ("'get_props_hastable' implementation returned TRUE but no"
839               "properties_hashtable is available");
840         } else {
841           object->priv->properties_hashtable = props_hash;
842           connect_properties_signals (object);
843         }
844       }
845     }
846   }
847
848 done:
849   object->priv->valid = res;
850
851   GST_DEBUG ("Returning res:%d", res);
852
853   return res;
854 }
855
856 /* INTERNAL USAGE */
857 gboolean
858 ges_track_object_set_track (GESTrackObject * object, GESTrack * track)
859 {
860   GST_DEBUG ("object:%p, track:%p", object, track);
861
862   object->priv->track = track;
863
864   if (object->priv->track) {
865     /* If we already have a gnlobject, we just set its caps properly */
866     if (object->priv->gnlobject) {
867       g_object_set (object->priv->gnlobject,
868           "caps", ges_track_get_caps (object->priv->track), NULL);
869       return TRUE;
870     } else {
871       return ensure_gnl_object (object);
872     }
873   }
874
875   return TRUE;
876 }
877
878 /**
879  * ges_track_object_get_track:
880  * @object: a #GESTrackObject
881  *
882  * Get the #GESTrack to which this object belongs.
883  *
884  * Returns: (transfer none): The #GESTrack to which this object belongs. Can be %NULL if it
885  * is not in any track
886  */
887 GESTrack *
888 ges_track_object_get_track (GESTrackObject * object)
889 {
890   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
891
892   return object->priv->track;
893 }
894
895 /**
896  * ges_track_object_set_timeline_object:
897  * @object: The #GESTrackObject to set the parent to
898  * @tlobject: The #GESTimelineObject, parent of @tlobj or %NULL
899  *
900  * Set the #GESTimelineObject to which @object belongs.
901  */
902 void
903 ges_track_object_set_timeline_object (GESTrackObject * object,
904     GESTimelineObject * tlobject)
905 {
906   GST_DEBUG ("object:%p, timeline-object:%p", object, tlobject);
907
908   object->priv->timelineobj = tlobject;
909 }
910
911 /**
912  * ges_track_object_get_timeline_object:
913  * @object: a #GESTrackObject
914  *
915  * Get the #GESTimelineObject which is controlling this track object
916  *
917  * Returns: (transfer none): the #GESTimelineObject which is controlling
918  * this track object
919  */
920 GESTimelineObject *
921 ges_track_object_get_timeline_object (GESTrackObject * object)
922 {
923   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
924
925   return object->priv->timelineobj;
926 }
927
928 /**
929  * ges_track_object_get_gnlobject:
930  * @object: a #GESTrackObject
931  *
932  * Get the GNonLin object this object is controlling.
933  *
934  * Returns: (transfer none): the GNonLin object this object is controlling.
935  */
936 GstElement *
937 ges_track_object_get_gnlobject (GESTrackObject * object)
938 {
939   return object->priv->gnlobject;
940 }
941
942 /**
943  * ges_track_object_get_element:
944  * @object: a #GESTrackObject
945  *
946  * Get the #GstElement this track object is controlling within GNonLin.
947  *
948  * Returns: (transfer none): the #GstElement this track object is controlling
949  * within GNonLin.
950  */
951 GstElement *
952 ges_track_object_get_element (GESTrackObject * object)
953 {
954   return object->priv->element;
955 }
956
957 static inline void
958 ges_track_object_set_locked_internal (GESTrackObject * object, gboolean locked)
959 {
960   object->priv->locked = locked;
961 }
962
963 /**
964  * ges_track_object_set_locked:
965  * @object: a #GESTrackObject
966  * @locked: whether the object is lock to its parent
967  *
968  * Set the locking status of the @object in relationship to its controlling
969  * #GESTimelineObject. If @locked is %TRUE, then this object will move synchronously
970  * with its controlling #GESTimelineObject.
971  */
972 void
973 ges_track_object_set_locked (GESTrackObject * object, gboolean locked)
974 {
975   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
976
977   GST_DEBUG_OBJECT (object, "%s object", locked ? "Locking" : "Unlocking");
978
979   ges_track_object_set_locked_internal (object, locked);
980 #if GLIB_CHECK_VERSION(2,26,0)
981   g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_LOCKED]);
982 #else
983   g_object_notify (G_OBJECT (object), "locked");
984 #endif
985
986 }
987
988 /**
989  * ges_track_object_is_locked:
990  * @object: a #GESTrackObject
991  *
992  * Let you know if object us locked or not (moving synchronously).
993  *
994  * Returns: %TRUE if the object is moving synchronously to its controlling
995  * #GESTimelineObject, else %FALSE.
996  */
997 gboolean
998 ges_track_object_is_locked (GESTrackObject * object)
999 {
1000   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
1001
1002   return object->priv->locked;
1003 }
1004
1005 /**
1006  * ges_track_object_get_start:
1007  * @object: a #GESTrackObject
1008  *
1009  * Get the position of the object in the container #GESTrack.
1010  *
1011  * Returns: the start position (in #GstClockTime) or #GST_CLOCK_TIME_NONE
1012  * if something went wrong.
1013  *
1014  * Since: 0.10.2
1015  */
1016 guint64
1017 ges_track_object_get_start (GESTrackObject * object)
1018 {
1019   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), GST_CLOCK_TIME_NONE);
1020
1021   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1022     return object->priv->pending_start;
1023   else
1024     return object->start;
1025 }
1026
1027 /**
1028  * ges_track_object_get_inpoint:
1029  * @object: a #GESTrackObject
1030  *
1031  * Get the offset within the contents of this #GESTrackObject
1032  *
1033  * Returns: the in-point (in #GstClockTime) or #GST_CLOCK_TIME_NONE
1034  * if something went wrong.
1035  *
1036  * Since: 0.10.2
1037  */
1038 guint64
1039 ges_track_object_get_inpoint (GESTrackObject * object)
1040 {
1041   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), GST_CLOCK_TIME_NONE);
1042
1043   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1044     return object->priv->pending_inpoint;
1045   else
1046     return object->inpoint;
1047 }
1048
1049 /**
1050  * ges_track_object_get_duration:
1051  * @object: a #GESTrackObject
1052  *
1053  * Get the duration which will be used in the container #GESTrack
1054  * starting from the 'in-point'
1055  *
1056  * Returns: the duration (in #GstClockTime) or #GST_CLOCK_TIME_NONE
1057  * if something went wrong.
1058  *
1059  * Since: 0.10.2
1060  */
1061 guint64
1062 ges_track_object_get_duration (GESTrackObject * object)
1063 {
1064   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), GST_CLOCK_TIME_NONE);
1065
1066   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1067     return object->priv->pending_duration;
1068   else
1069     return object->duration;
1070 }
1071
1072 /**
1073  * ges_track_object_get_priority:
1074  * @object: a #GESTrackObject
1075  *
1076  * Get the priority of the object withing the containing #GESTrack.
1077  *
1078  * Returns: the priority of @object or -1 if something went wrong
1079  *
1080  * Since: 0.10.2
1081  */
1082 guint32
1083 ges_track_object_get_priority (GESTrackObject * object)
1084 {
1085   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), -1);
1086
1087   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1088     return object->priv->pending_priority;
1089   else
1090     return object->priority;
1091 }
1092
1093 /**
1094  * ges_track_object_is_active:
1095  * @object: a #GESTrackObject
1096  *
1097  * Lets you know if @object will be used for playback and rendering,
1098  * or not.
1099  *
1100  * Returns: %TRUE if @object is active, %FALSE otherwize
1101  *
1102  * Since: 0.10.2
1103  */
1104 gboolean
1105 ges_track_object_is_active (GESTrackObject * object)
1106 {
1107   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
1108
1109   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1110     return object->priv->pending_active;
1111   else
1112     return object->active;
1113 }
1114
1115 /**
1116  * ges_track_object_lookup_child:
1117  * @object: object to lookup the property in
1118  * @prop_name: name of the property to look up. You can specify the name of the
1119  *     class as such: "ClassName::property-name", to guarantee that you get the
1120  *     proper GParamSpec in case various GstElement-s contain the same property
1121  *     name. If you don't do so, you will get the first element found, having
1122  *     this property and the and the corresponding GParamSpec.
1123  * @element: (out) (allow-none) (transfer full): pointer to a #GstElement that
1124  *     takes the real object to set property on
1125  * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
1126  *     describing the property
1127  *
1128  * Looks up which @element and @pspec would be effected by the given @name. If various
1129  * contained elements have this property name you will get the first one, unless you
1130  * specify the class name in @name.
1131  *
1132  * Returns: TRUE if @element and @pspec could be found. FALSE otherwise. In that
1133  * case the values for @pspec and @element are not modified. Unref @element after
1134  * usage.
1135  *
1136  * Since: 0.10.2
1137  */
1138 gboolean
1139 ges_track_object_lookup_child (GESTrackObject * object, const gchar * prop_name,
1140     GstElement ** element, GParamSpec ** pspec)
1141 {
1142   GHashTableIter iter;
1143   gpointer key, value;
1144   gchar **names, *name, *classename;
1145   gboolean res;
1146   GESTrackObjectPrivate *priv;
1147
1148   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
1149
1150   priv = object->priv;
1151
1152   classename = NULL;
1153   res = FALSE;
1154
1155   names = g_strsplit (prop_name, "::", 2);
1156   if (names[1] != NULL) {
1157     classename = names[0];
1158     name = names[1];
1159   } else
1160     name = names[0];
1161
1162   g_hash_table_iter_init (&iter, priv->properties_hashtable);
1163   while (g_hash_table_iter_next (&iter, &key, &value)) {
1164     if (g_strcmp0 (G_PARAM_SPEC (key)->name, name) == 0) {
1165       if (classename == NULL ||
1166           g_strcmp0 (G_OBJECT_TYPE_NAME (G_OBJECT (value)), classename) == 0) {
1167         GST_DEBUG ("The %s property from %s has been found", name, classename);
1168         if (element)
1169           *element = g_object_ref (value);
1170
1171         *pspec = g_param_spec_ref (key);
1172         res = TRUE;
1173         break;
1174       }
1175     }
1176   }
1177   g_strfreev (names);
1178
1179   return res;
1180 }
1181
1182 /**
1183  * ges_track_object_set_child_property_by_pspec:
1184  * @object: a #GESTrackObject
1185  * @pspec: The #GParamSpec that specifies the property you want to set
1186  * @value: the value
1187  *
1188  * Sets a property of a child of @object.
1189  *
1190  * Since: 0.10.2
1191  */
1192 void
1193 ges_track_object_set_child_property_by_pspec (GESTrackObject * object,
1194     GParamSpec * pspec, GValue * value)
1195 {
1196   GstElement *element;
1197   GESTrackObjectPrivate *priv;
1198
1199   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1200
1201   priv = object->priv;
1202
1203   if (!priv->properties_hashtable)
1204     goto prop_hash_not_set;
1205
1206   element = g_hash_table_lookup (priv->properties_hashtable, pspec);
1207   if (!element)
1208     goto not_found;
1209
1210   g_object_set_property (G_OBJECT (element), pspec->name, value);
1211
1212   return;
1213
1214 not_found:
1215   {
1216     GST_ERROR ("The %s property doesn't exist", pspec->name);
1217     return;
1218   }
1219 prop_hash_not_set:
1220   {
1221     GST_DEBUG ("The child properties haven't been set on %p", object);
1222     return;
1223   }
1224 }
1225
1226 /**
1227  * ges_track_object_set_child_property_valist:
1228  * @object: The #GESTrackObject parent object
1229  * @first_property_name: The name of the first property to set
1230  * @var_args: value for the first property, followed optionally by more
1231  * name/return location pairs, followed by NULL
1232  *
1233  * Sets a property of a child of @object. If there are various child elements
1234  * that have the same property name, you can distinguish them using the following
1235  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1236  * corresponding property of the first element found will be set.
1237  *
1238  * Since: 0.10.2
1239  */
1240 void
1241 ges_track_object_set_child_property_valist (GESTrackObject * object,
1242     const gchar * first_property_name, va_list var_args)
1243 {
1244   const gchar *name;
1245   GParamSpec *pspec;
1246   GstElement *element;
1247
1248   gchar *error = NULL;
1249   GValue value = { 0, };
1250
1251   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1252
1253   name = first_property_name;
1254
1255   /* Note: This part is in big part copied from the gst_child_object_set_valist
1256    * method. */
1257
1258   /* iterate over pairs */
1259   while (name) {
1260     if (!ges_track_object_lookup_child (object, name, &element, &pspec))
1261       goto not_found;
1262
1263 #if GLIB_CHECK_VERSION(2,23,3)
1264     G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
1265         G_VALUE_NOCOPY_CONTENTS, &error);
1266 #else
1267     g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1268     G_VALUE_COLLECT (&value, var_args, G_VALUE_NOCOPY_CONTENTS, &error);
1269 #endif
1270
1271     if (error)
1272       goto cant_copy;
1273
1274     g_object_set_property (G_OBJECT (element), pspec->name, &value);
1275
1276     g_object_unref (element);
1277     g_value_unset (&value);
1278
1279     name = va_arg (var_args, gchar *);
1280   }
1281   return;
1282
1283 not_found:
1284   {
1285     GST_WARNING ("No property %s in OBJECT\n", name);
1286     return;
1287   }
1288 cant_copy:
1289   {
1290     GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
1291         error);
1292     g_value_unset (&value);
1293     return;
1294   }
1295 }
1296
1297 /**
1298  * ges_track_object_set_child_property:
1299  * @object: The #GESTrackObject parent object
1300  * @first_property_name: The name of the first property to set
1301  * @...: value for the first property, followed optionally by more
1302  * name/return location pairs, followed by NULL
1303  *
1304  * Sets a property of a child of @object. If there are various child elements
1305  * that have the same property name, you can distinguish them using the following
1306  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1307  * corresponding property of the first element found will be set.
1308  *
1309  * Since: 0.10.2
1310  */
1311 void
1312 ges_track_object_set_child_property (GESTrackObject * object,
1313     const gchar * first_property_name, ...)
1314 {
1315   va_list var_args;
1316
1317   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1318
1319   va_start (var_args, first_property_name);
1320   ges_track_object_set_child_property_valist (object, first_property_name,
1321       var_args);
1322   va_end (var_args);
1323 }
1324
1325 /**
1326  * ges_track_object_get_child_property_valist:
1327  * @object: The #GESTrackObject parent object
1328  * @first_property_name: The name of the first property to get
1329  * @var_args: value for the first property, followed optionally by more
1330  * name/return location pairs, followed by NULL
1331  *
1332  * Gets a property of a child of @object. If there are various child elements
1333  * that have the same property name, you can distinguish them using the following
1334  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1335  * corresponding property of the first element found will be set.
1336  *
1337  * Since: 0.10.2
1338  */
1339 void
1340 ges_track_object_get_child_property_valist (GESTrackObject * object,
1341     const gchar * first_property_name, va_list var_args)
1342 {
1343   const gchar *name;
1344   gchar *error = NULL;
1345   GValue value = { 0, };
1346   GParamSpec *pspec;
1347   GstElement *element;
1348
1349   g_return_if_fail (G_IS_OBJECT (object));
1350
1351   name = first_property_name;
1352
1353   /* This part is in big part copied from the gst_child_object_get_valist method */
1354   while (name) {
1355     if (!ges_track_object_lookup_child (object, name, &element, &pspec))
1356       goto not_found;
1357
1358     g_value_init (&value, pspec->value_type);
1359     g_object_get_property (G_OBJECT (element), pspec->name, &value);
1360     g_object_unref (element);
1361
1362     G_VALUE_LCOPY (&value, var_args, 0, &error);
1363     if (error)
1364       goto cant_copy;
1365     g_value_unset (&value);
1366     name = va_arg (var_args, gchar *);
1367   }
1368   return;
1369
1370 not_found:
1371   {
1372     GST_WARNING ("no property %s in object", name);
1373     return;
1374   }
1375 cant_copy:
1376   {
1377     GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
1378         error);
1379     g_value_unset (&value);
1380     return;
1381   }
1382 }
1383
1384 /**
1385  * ges_track_object_list_children_properties:
1386  * @object: The #GESTrackObject to get the list of children properties from
1387  * @n_properties: return location for the length of the returned array
1388  *
1389  * Gets an array of #GParamSpec* for all configurable properties of the
1390  * children of @object.
1391  *
1392  * Returns: (transfer full) (array): an array of #GParamSpec* which should be freed after use or
1393  * %NULL if something went wrong
1394  *
1395  * Since: 0.10.2
1396  */
1397 GParamSpec **
1398 ges_track_object_list_children_properties (GESTrackObject * object,
1399     guint * n_properties)
1400 {
1401   GESTrackObjectClass *class;
1402
1403   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
1404
1405   class = GES_TRACK_OBJECT_GET_CLASS (object);
1406
1407   return class->list_children_properties (object, n_properties);
1408 }
1409
1410 /**
1411  * ges_track_object_get_child_property:
1412  * @object: The origin #GESTrackObject
1413  * @first_property_name: The name of the first property to get
1414  * @...: return location for the first property, followed optionally by more
1415  * name/return location pairs, followed by NULL
1416  *
1417  * Gets properties of a child of @object.
1418  *
1419  * Since: 0.10.2
1420  */
1421 void
1422 ges_track_object_get_child_property (GESTrackObject * object,
1423     const gchar * first_property_name, ...)
1424 {
1425   va_list var_args;
1426
1427   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1428
1429   va_start (var_args, first_property_name);
1430   ges_track_object_get_child_property_valist (object, first_property_name,
1431       var_args);
1432   va_end (var_args);
1433 }
1434
1435 /**
1436  * ges_track_object_get_child_property_by_pspec:
1437  * @object: a #GESTrackObject
1438  * @pspec: The #GParamSpec that specifies the property you want to get
1439  * @value: return location for the value
1440  *
1441  * Gets a property of a child of @object.
1442  *
1443  * Since: 0.10.2
1444  */
1445 void
1446 ges_track_object_get_child_property_by_pspec (GESTrackObject * object,
1447     GParamSpec * pspec, GValue * value)
1448 {
1449   GstElement *element;
1450   GESTrackObjectPrivate *priv;
1451
1452   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1453
1454   priv = object->priv;
1455
1456   if (!priv->properties_hashtable)
1457     goto prop_hash_not_set;
1458
1459   element = g_hash_table_lookup (priv->properties_hashtable, pspec);
1460   if (!element)
1461     goto not_found;
1462
1463   g_object_get_property (G_OBJECT (element), pspec->name, value);
1464
1465   return;
1466
1467 not_found:
1468   {
1469     GST_ERROR ("The %s property doesn't exist", pspec->name);
1470     return;
1471   }
1472 prop_hash_not_set:
1473   {
1474     GST_ERROR ("The child properties haven't been set on %p", object);
1475     return;
1476   }
1477 }
1478
1479 static GParamSpec **
1480 default_list_children_properties (GESTrackObject * object, guint * n_properties)
1481 {
1482   GParamSpec **pspec, *spec;
1483   GHashTableIter iter;
1484   gpointer key, value;
1485
1486   guint i = 0;
1487
1488   if (!object->priv->properties_hashtable)
1489     goto prop_hash_not_set;
1490
1491   *n_properties = g_hash_table_size (object->priv->properties_hashtable);
1492   pspec = g_new (GParamSpec *, *n_properties);
1493
1494   g_hash_table_iter_init (&iter, object->priv->properties_hashtable);
1495   while (g_hash_table_iter_next (&iter, &key, &value)) {
1496     spec = G_PARAM_SPEC (key);
1497     pspec[i] = g_param_spec_ref (spec);
1498     i++;
1499   }
1500
1501   return pspec;
1502
1503 prop_hash_not_set:
1504   {
1505     *n_properties = 0;
1506     GST_ERROR ("The child properties haven't been set on %p", object);
1507     return NULL;
1508   }
1509 }
1510
1511 /**
1512  * ges_track_object_get_max_duration:
1513  * @object: The #GESTrackObject to retrieve max duration from
1514  *
1515  * Get the max duration of @object.
1516  *
1517  * Returns: The max duration of @object
1518  *
1519  * Since: 0.10.XX
1520  */
1521 guint64
1522 ges_track_object_get_max_duration (GESTrackObject * object)
1523 {
1524   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), 0);
1525
1526   return object->priv->maxduration;
1527 }
1528
1529 /**
1530  * ges_track_object_set_max_duration:
1531  * @object: The #GESTrackObject to retrieve max duration from
1532  * @maxduration: The maximum duration of @object
1533  *
1534  * Returns: Set the max duration of @object
1535  *
1536  * Since: 0.10.XX
1537  */
1538 void
1539 ges_track_object_set_max_duration (GESTrackObject * object, guint64 maxduration)
1540 {
1541   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1542
1543   object->priv->maxduration = maxduration;
1544 }
1545
1546 /**
1547  * ges_track_object_copy:
1548  * @object: The #GESTrackObject to copy
1549  * @deep: whether we want to create the gnlobject and copy it properties
1550  *
1551  * Copies @object
1552  *
1553  * Returns: The newly create #GESTrackObject, copied from @object
1554  *
1555  * Since: 0.10.XX
1556  */
1557 GESTrackObject *
1558 ges_track_object_copy (GESTrackObject * object, gboolean deep)
1559 {
1560   GESTrackObject *ret = NULL;
1561   GParameter *params;
1562   GParamSpec **specs;
1563   guint n, n_specs, n_params;
1564   GValue val = { 0 };
1565
1566   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
1567
1568   specs =
1569       g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &n_specs);
1570   params = g_new0 (GParameter, n_specs);
1571   n_params = 0;
1572
1573   for (n = 0; n < n_specs; ++n) {
1574     if (g_strcmp0 (specs[n]->name, "parent") &&
1575         (specs[n]->flags & G_PARAM_READWRITE) == G_PARAM_READWRITE) {
1576       params[n_params].name = g_intern_string (specs[n]->name);
1577       g_value_init (&params[n_params].value, specs[n]->value_type);
1578       g_object_get_property (G_OBJECT (object), specs[n]->name,
1579           &params[n_params].value);
1580       ++n_params;
1581     }
1582   }
1583
1584   ret = g_object_newv (G_TYPE_FROM_INSTANCE (object), n_params, params);
1585   g_free (specs);
1586   g_free (params);
1587   specs = NULL;
1588   params = NULL;
1589
1590   if (deep == FALSE)
1591     return ret;
1592
1593   ensure_gnl_object (ret);
1594   specs = ges_track_object_list_children_properties (object, &n_specs);
1595   for (n = 0; n < n_specs; ++n) {
1596     g_value_init (&val, specs[n]->value_type);
1597     g_object_get_property (G_OBJECT (object), specs[n]->name, &val);
1598     ges_track_object_set_child_property_by_pspec (ret, specs[n], &val);
1599     g_value_unset (&val);
1600   }
1601
1602   g_free (specs);
1603   g_free (params);
1604
1605   return ret;
1606 }
1607
1608 /**
1609  * ges_track_object_edit:
1610  * @object: the #GESTrackObject to edit
1611  * @layers: (element-type GESTimelineLayer): The layers you want the edit to
1612  *  happen in, %NULL means that the edition is done in all the
1613  *  #GESTimelineLayers contained in the current timeline.
1614  *      FIXME: This is not implemented yet.
1615  * @mode: The #GESEditMode in which the editition will happen.
1616  * @edge: The #GESEdge the edit should happen on.
1617  * @position: The position at which to edit @object (in nanosecond)
1618  *
1619  * Edit @object in the different exisiting #GESEditMode modes. In the case of
1620  * slide, and roll, you need to specify a #GESEdge
1621  *
1622  * Returns: %TRUE if the object as been edited properly, %FALSE if an error
1623  * occured
1624  *
1625  * Since: 0.10.XX
1626  */
1627 gboolean
1628 ges_track_object_edit (GESTrackObject * object,
1629     GList * layers, GESEditMode mode, GESEdge edge, guint64 position)
1630 {
1631   GESTrack *track = ges_track_object_get_track (object);
1632   GESTimeline *timeline;
1633
1634   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
1635
1636   if (G_UNLIKELY (!track)) {
1637     GST_WARNING_OBJECT (object, "Trying to edit in %d mode but not in"
1638         "any Track yet.", mode);
1639     return FALSE;
1640   } else if (position < 0) {
1641     GST_DEBUG_OBJECT (object, "Trying to move before 0, not moving");
1642     return FALSE;
1643   }
1644
1645   timeline = GES_TIMELINE (ges_track_get_timeline (track));
1646
1647   if (G_UNLIKELY (!timeline)) {
1648     GST_WARNING_OBJECT (object, "Trying to edit in %d mode but not in"
1649         "track %p no in any timeline yet.", mode, track);
1650     return FALSE;
1651   }
1652
1653   switch (mode) {
1654     case GES_EDIT_MODE_NORMAL:
1655       timeline_move_object (timeline, object, layers, edge, position);
1656       break;
1657     case GES_EDIT_MODE_TRIM:
1658       timeline_trim_object (timeline, object, layers, edge, position);
1659       break;
1660     case GES_EDIT_MODE_RIPPLE:
1661       timeline_ripple_object (timeline, object, layers, edge, position);
1662       break;
1663     case GES_EDIT_MODE_ROLL:
1664       timeline_roll_object (timeline, object, layers, edge, position);
1665       break;
1666     case GES_EDIT_MODE_SLIDE:
1667       timeline_slide_object (timeline, object, layers, edge, position);
1668       break;
1669     default:
1670       GST_ERROR ("Unkown edit mode: %d", mode);
1671       return FALSE;
1672   }
1673
1674   return TRUE;
1675 }