713ec4d9c8364de6988797260ea42ba6c6c2a6b9
[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", 0, G_MAXUINT64, G_MAXUINT64,
312           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   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
341       GES_TYPE_TRACK_OBJECT, GESTrackObjectPrivate);
342
343   /* Sane default values */
344   self->priv->pending_start = 0;
345   self->priv->pending_inpoint = 0;
346   self->priv->pending_duration = GST_SECOND;
347   self->priv->pending_priority = 1;
348   self->priv->pending_active = TRUE;
349   self->priv->locked = TRUE;
350   self->priv->properties_hashtable = NULL;
351 }
352
353 static inline gboolean
354 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start)
355 {
356   GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
357       object, GST_TIME_ARGS (start));
358
359   if (object->priv->gnlobject != NULL) {
360     if (G_UNLIKELY (start == object->start))
361       return FALSE;
362
363     g_object_set (object->priv->gnlobject, "start", start, NULL);
364   } else
365     object->priv->pending_start = start;
366   return TRUE;
367 };
368
369 /**
370  * ges_track_object_set_start:
371  * @object: a #GESTrackObject
372  * @start: the start position (in #GstClockTime)
373  *
374  * Sets the position of the object in the container #GESTrack.
375  */
376 void
377 ges_track_object_set_start (GESTrackObject * object, guint64 start)
378 {
379   if (ges_track_object_set_start_internal (object, start))
380 #if GLIB_CHECK_VERSION(2,26,0)
381     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
382 #else
383     g_object_notify (G_OBJECT (object), "start");
384 #endif
385 }
386
387 static inline gboolean
388 ges_track_object_set_inpoint_internal (GESTrackObject * object, guint64 inpoint)
389 {
390
391   GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
392       object, GST_TIME_ARGS (inpoint));
393
394   if (object->priv->gnlobject != NULL) {
395     if (G_UNLIKELY (inpoint == object->inpoint))
396       return FALSE;
397
398     g_object_set (object->priv->gnlobject, "media-start", inpoint, NULL);
399   } else
400     object->priv->pending_inpoint = inpoint;
401
402   return TRUE;
403 }
404
405 /**
406  * ges_track_object_set_inpoint:
407  * @object: a #GESTrackObject
408  * @inpoint: the in-point (in #GstClockTime)
409  *
410  * Set the offset within the contents of this #GESTrackObject
411  */
412 void
413 ges_track_object_set_inpoint (GESTrackObject * object, guint64 inpoint)
414 {
415   if (ges_track_object_set_inpoint_internal (object, inpoint))
416 #if GLIB_CHECK_VERSION(2,26,0)
417     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
418 #else
419     g_object_notify (G_OBJECT (object), "in-point");
420 #endif
421 }
422
423 static inline gboolean
424 ges_track_object_set_duration_internal (GESTrackObject * object,
425     guint64 duration)
426 {
427   GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
428       object, GST_TIME_ARGS (duration));
429
430   if (object->priv->gnlobject != NULL) {
431     if (G_UNLIKELY (duration == object->duration))
432       return FALSE;
433
434     g_object_set (object->priv->gnlobject, "duration", duration,
435         "media-duration", duration, NULL);
436   } else
437     object->priv->pending_duration = duration;
438   return TRUE;
439 }
440
441 /**
442  * ges_track_object_set_duration:
443  * @object: a #GESTrackObject
444  * @duration: the duration (in #GstClockTime)
445  *
446  * Set the duration which will be used in the container #GESTrack
447  * starting from the 'in-point'
448  */
449 void
450 ges_track_object_set_duration (GESTrackObject * object, guint64 duration)
451 {
452   if (ges_track_object_set_duration_internal (object, duration))
453 #if GLIB_CHECK_VERSION(2,26,0)
454     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
455 #else
456     g_object_notify (G_OBJECT (object), "duration");
457 #endif
458 }
459
460 static inline gboolean
461 ges_track_object_set_priority_internal (GESTrackObject * object,
462     guint32 priority)
463 {
464   GST_DEBUG ("object:%p, priority:%" G_GUINT32_FORMAT, object, priority);
465
466   if (object->priv->gnlobject != NULL) {
467     if (G_UNLIKELY (priority == object->priority))
468       return FALSE;
469
470     g_object_set (object->priv->gnlobject, "priority", priority, NULL);
471   } else
472     object->priv->pending_priority = priority;
473   return TRUE;
474 }
475
476 /**
477  * ges_track_object_set_priority:
478  * @object: a #GESTrackObject
479  * @priority: the priority
480  *
481  * Sets the priority of the object withing the containing #GESTrack.
482  * If two objects intersect over the same region of time, the priority
483  * property is used to decide which one takes precedence.
484  *
485  * The highest priority (that supercedes everything) is 0, and then
486  * lowering priorities go in increasing numerical value (with G_MAXUINT32
487  * being the lowest priority).
488  */
489 void
490 ges_track_object_set_priority (GESTrackObject * object, guint32 priority)
491 {
492   if (ges_track_object_set_priority_internal (object, priority))
493 #if GLIB_CHECK_VERSION(2,26,0)
494     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_PRIORITY]);
495 #else
496     g_object_notify (G_OBJECT (object), "priority");
497 #endif
498 }
499
500
501 /**
502  * ges_track_object_set_active:
503  * @object: a #GESTrackObject
504  * @active: visibility
505  *
506  * Sets the usage of the @object. If @active is %TRUE, the object will be used for
507  * playback and rendering, else it will be ignored.
508  *
509  * Returns: %TRUE if the property was toggled, else %FALSE
510  */
511 gboolean
512 ges_track_object_set_active (GESTrackObject * object, gboolean active)
513 {
514   GST_DEBUG ("object:%p, active:%d", object, active);
515
516   if (object->priv->gnlobject != NULL) {
517     if (G_UNLIKELY (active == object->active))
518       return FALSE;
519
520     g_object_set (object->priv->gnlobject, "active", active, NULL);
521   } else
522     object->priv->pending_active = active;
523   return TRUE;
524 }
525
526 /* Callbacks from the GNonLin object */
527 static void
528 gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
529     GESTrackObject * obj)
530 {
531   guint64 start;
532   GESTrackObjectClass *klass;
533
534   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
535
536   g_object_get (gnlobject, "start", &start, NULL);
537
538   GST_DEBUG ("gnlobject start : %" GST_TIME_FORMAT " current : %"
539       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->start));
540
541   if (start != obj->start) {
542     obj->start = start;
543     if (klass->start_changed)
544       klass->start_changed (obj, start);
545   }
546 }
547
548 static void
549 gst_element_prop_changed_cb (GstElement * element, GParamSpec * arg
550     G_GNUC_UNUSED, GESTrackObject * obj)
551 {
552   g_signal_emit (obj, ges_track_object_signals[DEEP_NOTIFY], 0,
553       GST_ELEMENT (element), arg);
554 }
555
556 static void
557 connect_signal (gpointer key, gpointer value, gpointer user_data)
558 {
559   gchar *signame = g_strconcat ("notify::", G_PARAM_SPEC (key)->name, NULL);
560
561   g_signal_connect (G_OBJECT (value),
562       signame, G_CALLBACK (gst_element_prop_changed_cb),
563       GES_TRACK_OBJECT (user_data));
564
565   g_free (signame);
566 }
567
568 static void
569 connect_properties_signals (GESTrackObject * object)
570 {
571   if (G_UNLIKELY (!object->priv->properties_hashtable)) {
572     GST_WARNING ("The properties_hashtable hasn't been set");
573     return;
574   }
575
576   g_hash_table_foreach (object->priv->properties_hashtable,
577       (GHFunc) connect_signal, object);
578
579 }
580
581 /* Callbacks from the GNonLin object */
582 static void
583 gnlobject_media_start_cb (GstElement * gnlobject,
584     GParamSpec * arg G_GNUC_UNUSED, GESTrackObject * obj)
585 {
586   guint64 start;
587   GESTrackObjectClass *klass;
588
589   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
590
591   g_object_get (gnlobject, "media-start", &start, NULL);
592
593   GST_DEBUG ("gnlobject in-point : %" GST_TIME_FORMAT " current : %"
594       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->inpoint));
595
596   if (start != obj->inpoint) {
597     obj->inpoint = start;
598     if (klass->media_start_changed)
599       klass->media_start_changed (obj, start);
600   }
601 }
602
603 static void
604 gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
605     GESTrackObject * obj)
606 {
607   guint32 priority;
608   GESTrackObjectClass *klass;
609
610   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
611
612   g_object_get (gnlobject, "priority", &priority, NULL);
613
614   GST_DEBUG ("gnlobject priority : %d current : %d", priority, obj->priority);
615
616   if (priority != obj->priority) {
617     obj->priority = priority;
618     if (klass->gnl_priority_changed)
619       klass->gnl_priority_changed (obj, priority);
620   }
621 }
622
623 static void
624 gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
625     GESTrackObject * obj)
626 {
627   guint64 duration;
628   GESTrackObjectClass *klass;
629
630   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
631
632   g_object_get (gnlobject, "duration", &duration, NULL);
633
634   GST_DEBUG ("gnlobject duration : %" GST_TIME_FORMAT " current : %"
635       GST_TIME_FORMAT, GST_TIME_ARGS (duration), GST_TIME_ARGS (obj->duration));
636
637   if (duration != obj->duration) {
638     obj->duration = duration;
639     if (klass->duration_changed)
640       klass->duration_changed (obj, duration);
641   }
642 }
643
644 static void
645 gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
646     GESTrackObject * obj)
647 {
648   gboolean active;
649   GESTrackObjectClass *klass;
650
651   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
652
653   g_object_get (gnlobject, "active", &active, NULL);
654
655   GST_DEBUG ("gnlobject active : %d current : %d", active, obj->active);
656
657   if (active != obj->active) {
658     obj->active = active;
659     if (klass->active_changed)
660       klass->active_changed (obj, active);
661   }
662 }
663
664
665 /* default 'create_gnl_object' virtual method implementation */
666 static GstElement *
667 ges_track_object_create_gnl_object_func (GESTrackObject * self)
668 {
669   GESTrackObjectClass *klass = NULL;
670   GstElement *child = NULL;
671   GstElement *gnlobject;
672
673   klass = GES_TRACK_OBJECT_GET_CLASS (self);
674
675   if (G_UNLIKELY (self->priv->gnlobject != NULL))
676     goto already_have_gnlobject;
677
678   if (G_UNLIKELY (klass->gnlobject_factorytype == NULL))
679     goto no_gnlfactory;
680
681   GST_DEBUG ("Creating a supporting gnlobject of type '%s'",
682       klass->gnlobject_factorytype);
683
684   gnlobject = gst_element_factory_make (klass->gnlobject_factorytype, NULL);
685
686   if (G_UNLIKELY (gnlobject == NULL))
687     goto no_gnlobject;
688
689   if (klass->create_element) {
690     GST_DEBUG ("Calling subclass 'create_element' vmethod");
691     child = klass->create_element (self);
692
693     if (G_UNLIKELY (!child))
694       goto child_failure;
695
696     if (!gst_bin_add (GST_BIN (gnlobject), child))
697       goto add_failure;
698
699     GST_DEBUG ("Succesfully got the element to put in the gnlobject");
700     self->priv->element = child;
701   }
702
703   GST_DEBUG ("done");
704   return gnlobject;
705
706
707   /* ERROR CASES */
708
709 already_have_gnlobject:
710   {
711     GST_ERROR ("Already controlling a GnlObject %s",
712         GST_ELEMENT_NAME (self->priv->gnlobject));
713     return NULL;
714   }
715
716 no_gnlfactory:
717   {
718     GST_ERROR ("No GESTrackObject::gnlobject_factorytype implementation!");
719     return NULL;
720   }
721
722 no_gnlobject:
723   {
724     GST_ERROR ("Error creating a gnlobject of type '%s'",
725         klass->gnlobject_factorytype);
726     return NULL;
727   }
728
729 child_failure:
730   {
731     GST_ERROR ("create_element returned NULL");
732     gst_object_unref (gnlobject);
733     return NULL;
734   }
735
736 add_failure:
737   {
738     GST_ERROR ("Error adding the contents to the gnlobject");
739     gst_object_unref (child);
740     gst_object_unref (gnlobject);
741     return NULL;
742   }
743 }
744
745 static gboolean
746 ensure_gnl_object (GESTrackObject * object)
747 {
748   GESTrackObjectClass *class;
749   GstElement *gnlobject;
750   GHashTable *props_hash;
751   gboolean res = TRUE;
752
753   if (object->priv->gnlobject && object->priv->valid)
754     return FALSE;
755
756   /* 1. Create the GnlObject */
757   GST_DEBUG ("Creating GnlObject");
758
759   class = GES_TRACK_OBJECT_GET_CLASS (object);
760
761   if (G_UNLIKELY (class->create_gnl_object == NULL)) {
762     GST_ERROR ("No 'create_gnl_object' implementation !");
763     goto done;
764   }
765
766   GST_DEBUG ("Calling virtual method");
767
768   /* call the create_gnl_object virtual method */
769   gnlobject = class->create_gnl_object (object);
770
771   if (G_UNLIKELY (gnlobject == NULL)) {
772     GST_ERROR
773         ("'create_gnl_object' implementation returned TRUE but no GnlObject is available");
774     goto done;
775   }
776
777   object->priv->gnlobject = gnlobject;
778
779   /* 2. Fill in the GnlObject */
780   if (gnlobject) {
781     GST_DEBUG ("Got a valid GnlObject, now filling it in");
782
783     if (object->priv->timelineobj)
784       res = ges_timeline_object_fill_track_object (object->priv->timelineobj,
785           object, object->priv->gnlobject);
786     else
787       res = TRUE;
788
789     if (res) {
790       /* Connect to property notifications */
791       /* FIXME : remember the signalids so we can remove them later on !!! */
792       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::start",
793           G_CALLBACK (gnlobject_start_cb), object);
794       g_signal_connect (G_OBJECT (object->priv->gnlobject),
795           "notify::media-start", G_CALLBACK (gnlobject_media_start_cb), object);
796       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::duration",
797           G_CALLBACK (gnlobject_duration_cb), object);
798       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::priority",
799           G_CALLBACK (gnlobject_priority_cb), object);
800       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::active",
801           G_CALLBACK (gnlobject_active_cb), object);
802
803       /* Set some properties on the GnlObject */
804       g_object_set (object->priv->gnlobject,
805           "caps", ges_track_get_caps (object->priv->track),
806           "duration", object->priv->pending_duration,
807           "media-duration", object->priv->pending_duration,
808           "start", object->priv->pending_start,
809           "media-start", object->priv->pending_inpoint,
810           "priority", object->priv->pending_priority,
811           "active", object->priv->pending_active, NULL);
812
813       /*  We feed up the props_hashtable if possible */
814       if (class->get_props_hastable) {
815         props_hash = class->get_props_hastable (object);
816
817         if (props_hash == NULL) {
818           GST_DEBUG ("'get_props_hastable' implementation returned TRUE but no"
819               "properties_hashtable is available");
820         } else {
821           object->priv->properties_hashtable = props_hash;
822           connect_properties_signals (object);
823         }
824       }
825     }
826   }
827
828 done:
829   object->priv->valid = res;
830
831   GST_DEBUG ("Returning res:%d", res);
832
833   return res;
834 }
835
836 /* INTERNAL USAGE */
837 gboolean
838 ges_track_object_set_track (GESTrackObject * object, GESTrack * track)
839 {
840   GST_DEBUG ("object:%p, track:%p", object, track);
841
842   object->priv->track = track;
843
844   if (object->priv->track)
845     return ensure_gnl_object (object);
846
847   return TRUE;
848 }
849
850 /**
851  * ges_track_object_get_track:
852  * @object: a #GESTrackObject
853  *
854  * Get the #GESTrack to which this object belongs.
855  *
856  * Returns: (transfer none): The #GESTrack to which this object belongs. Can be %NULL if it
857  * is not in any track
858  */
859 GESTrack *
860 ges_track_object_get_track (GESTrackObject * object)
861 {
862   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
863
864   return object->priv->track;
865 }
866
867 /**
868  * ges_track_object_set_timeline_object:
869  * @object: The #GESTrackObject to set the parent to
870  * @tlobject: The #GESTimelineObject, parent of @tlobj or %NULL
871  *
872  * Set the #GESTimelineObject to which @object belongs.
873  */
874 void
875 ges_track_object_set_timeline_object (GESTrackObject * object,
876     GESTimelineObject * tlobject)
877 {
878   GST_DEBUG ("object:%p, timeline-object:%p", object, tlobject);
879
880   object->priv->timelineobj = tlobject;
881 }
882
883 /**
884  * ges_track_object_get_timeline_object:
885  * @object: a #GESTrackObject
886  *
887  * Get the #GESTimelineObject which is controlling this track object
888  *
889  * Returns: (transfer none): the #GESTimelineObject which is controlling
890  * this track object
891  */
892 GESTimelineObject *
893 ges_track_object_get_timeline_object (GESTrackObject * object)
894 {
895   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
896
897   return object->priv->timelineobj;
898 }
899
900 /**
901  * ges_track_object_get_gnlobject:
902  * @object: a #GESTrackObject
903  *
904  * Get the GNonLin object this object is controlling.
905  *
906  * Returns: (transfer none): the GNonLin object this object is controlling.
907  */
908 GstElement *
909 ges_track_object_get_gnlobject (GESTrackObject * object)
910 {
911   return object->priv->gnlobject;
912 }
913
914 /**
915  * ges_track_object_get_element:
916  * @object: a #GESTrackObject
917  *
918  * Get the #GstElement this track object is controlling within GNonLin.
919  *
920  * Returns: (transfer none): the #GstElement this track object is controlling
921  * within GNonLin.
922  */
923 GstElement *
924 ges_track_object_get_element (GESTrackObject * object)
925 {
926   return object->priv->element;
927 }
928
929 static inline void
930 ges_track_object_set_locked_internal (GESTrackObject * object, gboolean locked)
931 {
932   object->priv->locked = locked;
933 }
934
935 /**
936  * ges_track_object_set_locked:
937  * @object: a #GESTrackObject
938  * @locked: whether the object is lock to its parent
939  *
940  * Set the locking status of the @object in relationship to its controlling
941  * #GESTimelineObject. If @locked is %TRUE, then this object will move synchronously
942  * with its controlling #GESTimelineObject.
943  */
944 void
945 ges_track_object_set_locked (GESTrackObject * object, gboolean locked)
946 {
947   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
948
949   GST_DEBUG_OBJECT (object, "%s object", locked ? "Locking" : "Unlocking");
950
951   ges_track_object_set_locked_internal (object, locked);
952 #if GLIB_CHECK_VERSION(2,26,0)
953   g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_LOCKED]);
954 #else
955   g_object_notify (G_OBJECT (object), "locked");
956 #endif
957
958 }
959
960 /**
961  * ges_track_object_is_locked:
962  * @object: a #GESTrackObject
963  *
964  * Let you know if object us locked or not (moving synchronously).
965  *
966  * Returns: %TRUE if the object is moving synchronously to its controlling
967  * #GESTimelineObject, else %FALSE.
968  */
969 gboolean
970 ges_track_object_is_locked (GESTrackObject * object)
971 {
972   return object->priv->locked;
973 }
974
975 /**
976  * ges_track_object_get_start:
977  * @object: a #GESTrackObject
978  *
979  * Get the position of the object in the container #GESTrack.
980  *
981  * Returns: the start position (in #GstClockTime)
982  *
983  * Since: 0.10.2
984  */
985 guint64
986 ges_track_object_get_start (GESTrackObject * object)
987 {
988   if (G_UNLIKELY (object->priv->gnlobject == NULL))
989     return object->priv->pending_start;
990   else
991     return object->start;
992 }
993
994 /**
995  * ges_track_object_get_inpoint:
996  * @object: a #GESTrackObject
997  *
998  * Get the offset within the contents of this #GESTrackObject
999  *
1000  * Returns: the in-point (in #GstClockTime)
1001  *
1002  * Since: 0.10.2
1003  */
1004 guint64
1005 ges_track_object_get_inpoint (GESTrackObject * object)
1006 {
1007   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1008     return object->priv->pending_inpoint;
1009   else
1010     return object->inpoint;
1011 }
1012
1013 /**
1014  * ges_track_object_get_duration:
1015  * @object: a #GESTrackObject
1016  *
1017  * Get the duration which will be used in the container #GESTrack
1018  * starting from the 'in-point'
1019  *
1020  * Returns: the duration (in #GstClockTime)
1021  *
1022  * Since: 0.10.2
1023  */
1024 guint64
1025 ges_track_object_get_duration (GESTrackObject * object)
1026 {
1027   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1028     return object->priv->pending_duration;
1029   else
1030     return object->duration;
1031 }
1032
1033 /**
1034  * ges_track_object_get_priority:
1035  * @object: a #GESTrackObject
1036  *
1037  * Get the priority of the object withing the containing #GESTrack.
1038  *
1039  * Returns: the priority of @object
1040  *
1041  * Since: 0.10.2
1042  */
1043 guint32
1044 ges_track_object_get_priority (GESTrackObject * object)
1045 {
1046   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1047     return object->priv->pending_priority;
1048   else
1049     return object->priority;
1050 }
1051
1052 /**
1053  * ges_track_object_is_active:
1054  * @object: a #GESTrackObject
1055  *
1056  * Lets you know if @object will be used for playback and rendering,
1057  * or not.
1058  *
1059  * Returns: %TRUE if @object is active, %FALSE otherwize
1060  *
1061  * Since: 0.10.2
1062  */
1063 gboolean
1064 ges_track_object_is_active (GESTrackObject * object)
1065 {
1066   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1067     return object->priv->pending_active;
1068   else
1069     return object->active;
1070 }
1071
1072 /**
1073  * ges_track_object_lookup_child:
1074  * @object: object to lookup the property in
1075  * @prop_name: name of the property to look up. You can specify the name of the
1076  *     class as such: "ClassName::property-name", to guarantee that you get the
1077  *     proper GParamSpec in case various GstElement-s contain the same property
1078  *     name. If you don't do so, you will get the first element found, having
1079  *     this property and the and the corresponding GParamSpec.
1080  * @element: (out) (allow-none) (transfer full): pointer to a #GstElement that
1081  *     takes the real object to set property on
1082  * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
1083  *     describing the property
1084  *
1085  * Looks up which @element and @pspec would be effected by the given @name. If various
1086  * contained elements have this property name you will get the first one, unless you
1087  * specify the class name in @name.
1088  *
1089  * Returns: TRUE if @element and @pspec could be found. FALSE otherwise. In that
1090  * case the values for @pspec and @element are not modified. Unref @element after
1091  * usage.
1092  *
1093  * Since: 0.10.2
1094  */
1095 gboolean
1096 ges_track_object_lookup_child (GESTrackObject * object, const gchar * prop_name,
1097     GstElement ** element, GParamSpec ** pspec)
1098 {
1099   GHashTableIter iter;
1100   gpointer key, value;
1101   gchar **names, *name, *classename;
1102   gboolean res;
1103   GESTrackObjectPrivate *priv = object->priv;
1104
1105   classename = NULL;
1106   res = FALSE;
1107
1108   names = g_strsplit (prop_name, "::", 2);
1109   if (names[1] != NULL) {
1110     classename = names[0];
1111     name = names[1];
1112   } else
1113     name = names[0];
1114
1115   g_hash_table_iter_init (&iter, priv->properties_hashtable);
1116   while (g_hash_table_iter_next (&iter, &key, &value)) {
1117     if (g_strcmp0 (G_PARAM_SPEC (key)->name, name) == 0) {
1118       if (classename == NULL ||
1119           g_strcmp0 (G_OBJECT_TYPE_NAME (G_OBJECT (value)), classename) == 0) {
1120         GST_DEBUG ("The %s property from %s has been found", name, classename);
1121         if (element)
1122           *element = g_object_ref (value);
1123
1124         *pspec = g_param_spec_ref (key);
1125         res = TRUE;
1126         break;
1127       }
1128     }
1129   }
1130   g_strfreev (names);
1131
1132   return res;
1133 }
1134
1135 /**
1136  * ges_track_object_set_child_property_by_pspec:
1137  * @object: a #GESTrackObject
1138  * @pspec: The #GParamSpec that specifies the property you want to set
1139  * @value: the value
1140  *
1141  * Sets a property of a child of @object.
1142  *
1143  * Since: 0.10.2
1144  */
1145 void
1146 ges_track_object_set_child_property_by_pspec (GESTrackObject * object,
1147     GParamSpec * pspec, GValue * value)
1148 {
1149   GstElement *element;
1150
1151   GESTrackObjectPrivate *priv = object->priv;
1152
1153   if (!priv->properties_hashtable)
1154     goto prop_hash_not_set;
1155
1156   element = g_hash_table_lookup (priv->properties_hashtable, pspec);
1157   if (!element)
1158     goto not_found;
1159
1160   g_object_set_property (G_OBJECT (element), pspec->name, value);
1161
1162   return;
1163
1164 not_found:
1165   {
1166     GST_ERROR ("The %s property doesn't exist", pspec->name);
1167     return;
1168   }
1169 prop_hash_not_set:
1170   {
1171     GST_DEBUG ("The child properties haven't been set on %p", object);
1172     return;
1173   }
1174 }
1175
1176 /**
1177  * ges_track_object_set_child_property_valist:
1178  * @object: The #GESTrackObject parent object
1179  * @first_property_name: The name of the first property to set
1180  * @var_args: value for the first property, followed optionally by more
1181  * name/return location pairs, followed by NULL
1182  *
1183  * Sets a property of a child of @object. If there are various child elements
1184  * that have the same property name, you can distinguish them using the following
1185  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1186  * corresponding property of the first element found will be set.
1187  *
1188  * Since: 0.10.2
1189  */
1190 void
1191 ges_track_object_set_child_property_valist (GESTrackObject * object,
1192     const gchar * first_property_name, va_list var_args)
1193 {
1194   const gchar *name;
1195   GParamSpec *pspec;
1196   GstElement *element;
1197
1198   gchar *error = NULL;
1199   GValue value = { 0, };
1200
1201   g_return_if_fail (G_IS_OBJECT (object));
1202
1203   name = first_property_name;
1204
1205   /* Note: This part is in big part copied from the gst_child_object_set_valist
1206    * method. */
1207
1208   /* iterate over pairs */
1209   while (name) {
1210     if (!ges_track_object_lookup_child (object, name, &element, &pspec))
1211       goto not_found;
1212
1213 #if GLIB_CHECK_VERSION(2,23,3)
1214     G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
1215         G_VALUE_NOCOPY_CONTENTS, &error);
1216 #else
1217     g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1218     G_VALUE_COLLECT (&value, var_args, G_VALUE_NOCOPY_CONTENTS, &error);
1219 #endif
1220
1221     if (error)
1222       goto cant_copy;
1223
1224     g_object_set_property (G_OBJECT (element), pspec->name, &value);
1225
1226     g_object_unref (element);
1227     g_value_unset (&value);
1228
1229     name = va_arg (var_args, gchar *);
1230   }
1231   return;
1232
1233 not_found:
1234   {
1235     GST_WARNING ("No property %s in OBJECT\n", name);
1236     return;
1237   }
1238 cant_copy:
1239   {
1240     GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
1241         error);
1242     g_value_unset (&value);
1243     return;
1244   }
1245 }
1246
1247 /**
1248  * ges_track_object_set_child_property:
1249  * @object: The #GESTrackObject parent object
1250  * @first_property_name: The name of the first property to set
1251  * @...: value for the first property, followed optionally by more
1252  * name/return location pairs, followed by NULL
1253  *
1254  * Sets a property of a child of @object. If there are various child elements
1255  * that have the same property name, you can distinguish them using the following
1256  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1257  * corresponding property of the first element found will be set.
1258  *
1259  * Since: 0.10.2
1260  */
1261 void
1262 ges_track_object_set_child_property (GESTrackObject * object,
1263     const gchar * first_property_name, ...)
1264 {
1265   va_list var_args;
1266
1267   va_start (var_args, first_property_name);
1268   ges_track_object_set_child_property_valist (object, first_property_name,
1269       var_args);
1270   va_end (var_args);
1271 }
1272
1273 /**
1274  * ges_track_object_get_child_property_valist:
1275  * @object: The #GESTrackObject parent object
1276  * @first_property_name: The name of the first property to get
1277  * @var_args: value for the first property, followed optionally by more
1278  * name/return location pairs, followed by NULL
1279  *
1280  * Gets a property of a child of @object. If there are various child elements
1281  * that have the same property name, you can distinguish them using the following
1282  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1283  * corresponding property of the first element found will be set.
1284  *
1285  * Since: 0.10.2
1286  */
1287 void
1288 ges_track_object_get_child_property_valist (GESTrackObject * object,
1289     const gchar * first_property_name, va_list var_args)
1290 {
1291   const gchar *name;
1292   gchar *error = NULL;
1293   GValue value = { 0, };
1294   GParamSpec *pspec;
1295   GstElement *element;
1296
1297   g_return_if_fail (G_IS_OBJECT (object));
1298
1299   name = first_property_name;
1300
1301   /* This part is in big part copied from the gst_child_object_get_valist method */
1302   while (name) {
1303     if (!ges_track_object_lookup_child (object, name, &element, &pspec))
1304       goto not_found;
1305
1306     g_value_init (&value, pspec->value_type);
1307     g_object_get_property (G_OBJECT (element), pspec->name, &value);
1308     g_object_unref (element);
1309
1310     G_VALUE_LCOPY (&value, var_args, 0, &error);
1311     if (error)
1312       goto cant_copy;
1313     g_value_unset (&value);
1314     name = va_arg (var_args, gchar *);
1315   }
1316   return;
1317
1318 not_found:
1319   {
1320     GST_WARNING ("no property %s in object", name);
1321     return;
1322   }
1323 cant_copy:
1324   {
1325     GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
1326         error);
1327     g_value_unset (&value);
1328     return;
1329   }
1330 }
1331
1332 /**
1333  * ges_track_object_list_children_properties:
1334  * @object: The #GESTrackObject to get the list of children properties from
1335  * @n_properties: return location for the length of the returned array
1336  *
1337  * Gets an array of #GParamSpec* for all configurable properties of the
1338  * children of @object.
1339  *
1340  * Returns: (transfer full) (array): an array of #GParamSpec* which should be freed after use or
1341  * %NULL if something went wrong
1342  *
1343  * Since: 0.10.2
1344  */
1345 GParamSpec **
1346 ges_track_object_list_children_properties (GESTrackObject * object,
1347     guint * n_properties)
1348 {
1349   GESTrackObjectClass *class;
1350
1351   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
1352
1353   class = GES_TRACK_OBJECT_GET_CLASS (object);
1354
1355   return class->list_children_properties (object, n_properties);
1356 }
1357
1358 /**
1359  * ges_track_object_get_child_property:
1360  * @object: The origin #GESTrackObject
1361  * @first_property_name: The name of the first property to get
1362  * @...: return location for the first property, followed optionally by more
1363  * name/return location pairs, followed by NULL
1364  *
1365  * Gets properties of a child of @object.
1366  *
1367  * Since: 0.10.2
1368  */
1369 void
1370 ges_track_object_get_child_property (GESTrackObject * object,
1371     const gchar * first_property_name, ...)
1372 {
1373   va_list var_args;
1374
1375   va_start (var_args, first_property_name);
1376   ges_track_object_get_child_property_valist (object, first_property_name,
1377       var_args);
1378   va_end (var_args);
1379 }
1380
1381 /**
1382  * ges_track_object_get_child_property_by_pspec:
1383  * @object: a #GESTrackObject
1384  * @pspec: The #GParamSpec that specifies the property you want to get
1385  * @value: return location for the value
1386  *
1387  * Gets a property of a child of @object.
1388  *
1389  * Since: 0.10.2
1390  */
1391 void
1392 ges_track_object_get_child_property_by_pspec (GESTrackObject * object,
1393     GParamSpec * pspec, GValue * value)
1394 {
1395   GstElement *element;
1396
1397   GESTrackObjectPrivate *priv = object->priv;
1398
1399   if (!priv->properties_hashtable)
1400     goto prop_hash_not_set;
1401
1402   element = g_hash_table_lookup (priv->properties_hashtable, pspec);
1403   if (!element)
1404     goto not_found;
1405
1406   g_object_get_property (G_OBJECT (element), pspec->name, value);
1407
1408   return;
1409
1410 not_found:
1411   {
1412     GST_ERROR ("The %s property doesn't exist", pspec->name);
1413     return;
1414   }
1415 prop_hash_not_set:
1416   {
1417     GST_ERROR ("The child properties haven't been set on %p", object);
1418     return;
1419   }
1420 }
1421
1422 static GParamSpec **
1423 default_list_children_properties (GESTrackObject * object, guint * n_properties)
1424 {
1425   GParamSpec **pspec, *spec;
1426   GHashTableIter iter;
1427   gpointer key, value;
1428
1429   guint i = 0;
1430
1431   if (!object->priv->properties_hashtable)
1432     goto prop_hash_not_set;
1433
1434   *n_properties = g_hash_table_size (object->priv->properties_hashtable);
1435   pspec = g_new (GParamSpec *, *n_properties);
1436
1437   g_hash_table_iter_init (&iter, object->priv->properties_hashtable);
1438   while (g_hash_table_iter_next (&iter, &key, &value)) {
1439     spec = G_PARAM_SPEC (key);
1440     pspec[i] = g_param_spec_ref (spec);
1441     i++;
1442   }
1443
1444   return pspec;
1445
1446 prop_hash_not_set:
1447   {
1448     *n_properties = 0;
1449     GST_ERROR ("The child properties haven't been set on %p", object);
1450     return NULL;
1451   }
1452 }
1453
1454 /**
1455  * ges_track_object_get_max_duration:
1456  * @object: The #GESTrackObject to retrieve max duration from
1457  *
1458  * Get the max duration of @object.
1459  *
1460  * Returns: The max duration of @object
1461  *
1462  * Since: 0.10.XX
1463  */
1464 guint64
1465 ges_track_object_get_max_duration (GESTrackObject * object)
1466 {
1467   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), 0);
1468
1469   return object->priv->maxduration;
1470 }
1471
1472 /**
1473  * ges_track_object_set_max_duration:
1474  * @object: The #GESTrackObject to retrieve max duration from
1475  * @maxduration: The maximum duration of @object
1476  *
1477  * Returns: Set the max duration of @object
1478  *
1479  * Since: 0.10.XX
1480  */
1481 void
1482 ges_track_object_set_max_duration (GESTrackObject * object, guint64 maxduration)
1483 {
1484   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
1485
1486   object->priv->maxduration = maxduration;
1487 }