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