GESTrackObject: fixe a few issues with the get_props_hastable vmethod
[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
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    * {'ClassName-propertyName' ---> element,}*/
56   GHashTable *properties_hashtable;
57
58   GESTimelineObject *timelineobj;
59   GESTrack *track;
60
61   gboolean valid;
62
63   gboolean locked;              /* If TRUE, then moves in sync with its controlling
64                                  * GESTimelineObject */
65 };
66
67 enum
68 {
69   PROP_0,
70   PROP_START,
71   PROP_INPOINT,
72   PROP_DURATION,
73   PROP_PRIORITY,
74   PROP_ACTIVE,
75   PROP_LAST
76 };
77
78 static GParamSpec *properties[PROP_LAST];
79
80 enum
81 {
82   DEEP_NOTIFY,
83   LAST_SIGNAL
84 };
85
86 static guint ges_track_object_signals[LAST_SIGNAL] = { 0 };
87
88 static GstElement *ges_track_object_create_gnl_object_func (GESTrackObject *
89     object);
90
91 static void gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg
92     G_GNUC_UNUSED, GESTrackObject * obj);
93
94 static void gnlobject_media_start_cb (GstElement * gnlobject, GParamSpec * arg
95     G_GNUC_UNUSED, GESTrackObject * obj);
96
97 static void gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg
98     G_GNUC_UNUSED, GESTrackObject * obj);
99
100 static void gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg
101     G_GNUC_UNUSED, GESTrackObject * obj);
102
103 static void gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg
104     G_GNUC_UNUSED, GESTrackObject * obj);
105
106 static void connect_properties_signals (GESTrackObject * object);
107 static void connect_signal (gpointer key, gpointer value, gpointer user_data);
108 static void gst_element_prop_changed_cb (GstElement * element, GParamSpec * arg
109     G_GNUC_UNUSED, GESTrackObject * obj);
110
111 static inline gboolean
112 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start);
113 static inline gboolean
114 ges_track_object_set_inpoint_internal (GESTrackObject * object,
115     guint64 inpoint);
116 static inline gboolean ges_track_object_set_duration_internal (GESTrackObject *
117     object, guint64 duration);
118 static inline gboolean ges_track_object_set_priority_internal (GESTrackObject *
119     object, guint32 priority);
120
121 static void
122 ges_track_object_get_property (GObject * object, guint property_id,
123     GValue * value, GParamSpec * pspec)
124 {
125   GESTrackObject *tobj = GES_TRACK_OBJECT (object);
126
127   switch (property_id) {
128     case PROP_START:
129       g_value_set_uint64 (value, ges_track_object_get_start (tobj));
130       break;
131     case PROP_INPOINT:
132       g_value_set_uint64 (value, ges_track_object_get_inpoint (tobj));
133       break;
134     case PROP_DURATION:
135       g_value_set_uint64 (value, ges_track_object_get_duration (tobj));
136       break;
137     case PROP_PRIORITY:
138       g_value_set_uint (value, ges_track_object_get_priority (tobj));
139       break;
140     case PROP_ACTIVE:
141       g_value_set_boolean (value, ges_track_object_is_active (tobj));
142       break;
143     default:
144       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
145   }
146 }
147
148 static void
149 ges_track_object_set_property (GObject * object, guint property_id,
150     const GValue * value, GParamSpec * pspec)
151 {
152   GESTrackObject *tobj = GES_TRACK_OBJECT (object);
153
154   switch (property_id) {
155     case PROP_START:
156       ges_track_object_set_start_internal (tobj, g_value_get_uint64 (value));
157       break;
158     case PROP_INPOINT:
159       ges_track_object_set_inpoint_internal (tobj, g_value_get_uint64 (value));
160       break;
161     case PROP_DURATION:
162       ges_track_object_set_duration_internal (tobj, g_value_get_uint64 (value));
163       break;
164     case PROP_PRIORITY:
165       ges_track_object_set_priority_internal (tobj, g_value_get_uint (value));
166       break;
167     case PROP_ACTIVE:
168       ges_track_object_set_active (tobj, g_value_get_boolean (value));
169       break;
170     default:
171       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
172   }
173 }
174
175 static void
176 ges_track_object_dispose (GObject * object)
177 {
178   GESTrackObjectPrivate *priv = GES_TRACK_OBJECT (object)->priv;
179   if (priv->properties_hashtable)
180     g_hash_table_destroy (priv->properties_hashtable);
181
182   G_OBJECT_CLASS (ges_track_object_parent_class)->dispose (object);
183 }
184
185 static void
186 ges_track_object_finalize (GObject * object)
187 {
188   G_OBJECT_CLASS (ges_track_object_parent_class)->finalize (object);
189 }
190
191 static void
192 ges_track_object_class_init (GESTrackObjectClass * klass)
193 {
194   GObjectClass *object_class = G_OBJECT_CLASS (klass);
195
196   g_type_class_add_private (klass, sizeof (GESTrackObjectPrivate));
197
198   object_class->get_property = ges_track_object_get_property;
199   object_class->set_property = ges_track_object_set_property;
200   object_class->dispose = ges_track_object_dispose;
201   object_class->finalize = ges_track_object_finalize;
202
203   /**
204    * GESTrackObject:start
205    *
206    * The position of the object in the container #GESTrack (in nanoseconds).
207    */
208   properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
209       "The position in the container", 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
210   g_object_class_install_property (object_class, PROP_START,
211       properties[PROP_START]);
212
213   /**
214    * GESTrackObject:in-point
215    *
216    * The in-point at which this #GESTrackObject will start outputting data
217    * from its contents (in nanoseconds).
218    *
219    * Ex : an in-point of 5 seconds means that the first outputted buffer will
220    * be the one located 5 seconds in the controlled resource.
221    */
222   properties[PROP_INPOINT] =
223       g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
224       G_MAXUINT64, 0, G_PARAM_READWRITE);
225   g_object_class_install_property (object_class, PROP_INPOINT,
226       properties[PROP_INPOINT]);
227
228   /**
229    * GESTrackObject:duration
230    *
231    * The duration (in nanoseconds) which will be used in the container #GESTrack
232    * starting from 'in-point'.
233    *
234    */
235   properties[PROP_DURATION] =
236       g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
237       G_MAXUINT64, GST_SECOND, G_PARAM_READWRITE);
238   g_object_class_install_property (object_class, PROP_DURATION,
239       properties[PROP_DURATION]);
240
241   /**
242    * GESTrackObject:priority
243    *
244    * The priority of the object within the containing #GESTrack.
245    * If two objects intersect over the same region of time, the @priority
246    * property is used to decide which one takes precedence.
247    *
248    * The highest priority (that supercedes everything) is 0, and then lowering
249    * priorities go in increasing numerical value (with #G_MAXUINT64 being the
250    * lowest priority).
251    */
252   properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
253       "The priority of the object", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
254   g_object_class_install_property (object_class, PROP_PRIORITY,
255       properties[PROP_PRIORITY]);
256
257   /**
258    * GESTrackObject:active
259    *
260    * Whether the object should be taken into account in the #GESTrack output.
261    * If #FALSE, then its contents will not be used in the resulting track.
262    */
263   properties[PROP_ACTIVE] =
264       g_param_spec_boolean ("active", "Active", "Use object in output", TRUE,
265       G_PARAM_READWRITE);
266   g_object_class_install_property (object_class, PROP_ACTIVE,
267       properties[PROP_ACTIVE]);
268
269   /**
270    * GESTrackObject::deep-notify:
271    * @track_object: a #GESTrackObject
272    * @prop_object: the object that originated the signal
273    * @prop: the property that changed
274    *
275    * The deep notify signal is used to be notified of property changes of all
276    * the childs of @track_object
277    */
278   ges_track_object_signals[DEEP_NOTIFY] =
279       g_signal_new ("deep-notify", G_TYPE_FROM_CLASS (klass),
280       G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED |
281       G_SIGNAL_NO_HOOKS, 0, NULL, NULL, gst_marshal_VOID__OBJECT_PARAM,
282       G_TYPE_NONE, 2, GST_TYPE_ELEMENT, G_TYPE_PARAM);
283
284   klass->create_gnl_object = ges_track_object_create_gnl_object_func;
285   /*  There is no 'get_props_hastable' default implementation */
286   klass->get_props_hastable = NULL;
287 }
288
289 static void
290 ges_track_object_init (GESTrackObject * self)
291 {
292   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
293       GES_TYPE_TRACK_OBJECT, GESTrackObjectPrivate);
294
295   /* Sane default values */
296   self->priv->pending_start = 0;
297   self->priv->pending_inpoint = 0;
298   self->priv->pending_duration = GST_SECOND;
299   self->priv->pending_priority = 1;
300   self->priv->pending_active = TRUE;
301   self->priv->locked = TRUE;
302   self->priv->properties_hashtable = NULL;
303 }
304
305 static inline gboolean
306 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start)
307 {
308   GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
309       object, GST_TIME_ARGS (start));
310
311   if (object->priv->gnlobject != NULL) {
312     if (G_UNLIKELY (start == object->start))
313       return FALSE;
314
315     g_object_set (object->priv->gnlobject, "start", start, NULL);
316   } else
317     object->priv->pending_start = start;
318   return TRUE;
319 };
320
321 /**
322  * ges_track_object_set_start:
323  * @object: a #GESTrackObject
324  * @start: the start position (in #GstClockTime)
325  *
326  * Sets the position of the object in the container #GESTrack.
327  */
328 void
329 ges_track_object_set_start (GESTrackObject * object, guint64 start)
330 {
331   if (ges_track_object_set_start_internal (object, start))
332 #if GLIB_CHECK_VERSION(2,26,0)
333     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
334 #else
335     g_object_notify (G_OBJECT (object), "start");
336 #endif
337 }
338
339 static inline gboolean
340 ges_track_object_set_inpoint_internal (GESTrackObject * object, guint64 inpoint)
341 {
342
343   GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
344       object, GST_TIME_ARGS (inpoint));
345
346   if (object->priv->gnlobject != NULL) {
347     if (G_UNLIKELY (inpoint == object->inpoint))
348       return FALSE;
349
350     g_object_set (object->priv->gnlobject, "media-start", inpoint, NULL);
351   } else
352     object->priv->pending_inpoint = inpoint;
353
354   return TRUE;
355 }
356
357 /**
358  * ges_track_object_set_inpoint:
359  * @object: a #GESTrackObject
360  * @inpoint: the in-point (in #GstClockTime)
361  *
362  * Set the offset within the contents of this #GESTrackObject
363  */
364 void
365 ges_track_object_set_inpoint (GESTrackObject * object, guint64 inpoint)
366 {
367   if (ges_track_object_set_inpoint_internal (object, inpoint))
368 #if GLIB_CHECK_VERSION(2,26,0)
369     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
370 #else
371     g_object_notify (G_OBJECT (object), "in-point");
372 #endif
373 }
374
375 static inline gboolean
376 ges_track_object_set_duration_internal (GESTrackObject * object,
377     guint64 duration)
378 {
379   GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
380       object, GST_TIME_ARGS (duration));
381
382   if (object->priv->gnlobject != NULL) {
383     if (G_UNLIKELY (duration == object->duration))
384       return FALSE;
385
386     g_object_set (object->priv->gnlobject, "duration", duration,
387         "media-duration", duration, NULL);
388   } else
389     object->priv->pending_duration = duration;
390   return TRUE;
391 }
392
393 /**
394  * ges_track_object_set_duration:
395  * @object: a #GESTrackObject
396  * @duration: the duration (in #GstClockTime)
397  *
398  * Set the duration which will be used in the container #GESTrack
399  * starting from the 'in-point'
400  */
401 void
402 ges_track_object_set_duration (GESTrackObject * object, guint64 duration)
403 {
404   if (ges_track_object_set_duration_internal (object, duration))
405 #if GLIB_CHECK_VERSION(2,26,0)
406     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
407 #else
408     g_object_notify (G_OBJECT (object), "duration");
409 #endif
410 }
411
412 static inline gboolean
413 ges_track_object_set_priority_internal (GESTrackObject * object,
414     guint32 priority)
415 {
416   GST_DEBUG ("object:%p, priority:%" G_GUINT32_FORMAT, object, priority);
417
418   if (object->priv->gnlobject != NULL) {
419     if (G_UNLIKELY (priority == object->priority))
420       return FALSE;
421
422     g_object_set (object->priv->gnlobject, "priority", priority, NULL);
423   } else
424     object->priv->pending_priority = priority;
425   return TRUE;
426 }
427
428 /**
429  * ges_track_object_set_priority:
430  * @object: a #GESTrackObject
431  * @priority: the priority
432  *
433  * Sets the priority of the object withing the containing #GESTrack.
434  * If two objects intersect over the same region of time, the priority
435  * property is used to decide which one takes precedence.
436  *
437  * The highest priority (that supercedes everything) is 0, and then
438  * lowering priorities go in increasing numerical value (with G_MAXUINT32
439  * being the lowest priority).
440  */
441 void
442 ges_track_object_set_priority (GESTrackObject * object, guint32 priority)
443 {
444   if (ges_track_object_set_priority_internal (object, priority))
445 #if GLIB_CHECK_VERSION(2,26,0)
446     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_PRIORITY]);
447 #else
448     g_object_notify (G_OBJECT (object), "priority");
449 #endif
450 }
451
452
453 /**
454  * ges_track_object_set_active:
455  * @object: a #GESTrackObject
456  * @active: visibility
457  *
458  * Sets the usage of the @object. If @active is %TRUE, the object will be used for
459  * playback and rendering, else it will be ignored.
460  *
461  * Returns: %TRUE if the property was toggled, else %FALSE
462  */
463 gboolean
464 ges_track_object_set_active (GESTrackObject * object, gboolean active)
465 {
466   GST_DEBUG ("object:%p, active:%d", object, active);
467
468   if (object->priv->gnlobject != NULL) {
469     if (G_UNLIKELY (active == object->active))
470       return FALSE;
471
472     g_object_set (object->priv->gnlobject, "active", active, NULL);
473   } else
474     object->priv->pending_active = active;
475   return TRUE;
476 }
477
478 /* Callbacks from the GNonLin object */
479 static void
480 gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
481     GESTrackObject * obj)
482 {
483   guint64 start;
484   GESTrackObjectClass *klass;
485
486   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
487
488   g_object_get (gnlobject, "start", &start, NULL);
489
490   GST_DEBUG ("gnlobject start : %" GST_TIME_FORMAT " current : %"
491       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->start));
492
493   if (start != obj->start) {
494     obj->start = start;
495     if (klass->start_changed)
496       klass->start_changed (obj, start);
497   }
498 }
499
500 static void
501 gst_element_prop_changed_cb (GstElement * element, GParamSpec * arg
502     G_GNUC_UNUSED, GESTrackObject * obj)
503 {
504   g_signal_emit (obj, ges_track_object_signals[DEEP_NOTIFY], 0,
505       GST_ELEMENT (element), arg);
506 }
507
508 static void
509 connect_signal (gpointer key, gpointer value, gpointer user_data)
510 {
511   gchar **name = g_strsplit ((char *) key, "-", 2);
512   gchar *signame = g_strconcat ("notify::", (gchar *) name[1], NULL);
513   g_strfreev (name);
514
515   g_signal_connect (G_OBJECT (value),
516       signame, G_CALLBACK (gst_element_prop_changed_cb),
517       GES_TRACK_OBJECT (user_data));
518
519   g_free (signame);
520 }
521
522 static void
523 connect_properties_signals (GESTrackObject * object)
524 {
525   if (G_UNLIKELY (object->priv->properties_hashtable)) {
526     GST_WARNING ("The properties_hashtable hasn't been set");
527   }
528
529   g_hash_table_foreach (object->priv->properties_hashtable,
530       (GHFunc) connect_signal, object);
531
532 }
533
534 /* Callbacks from the GNonLin object */
535 static void
536 gnlobject_media_start_cb (GstElement * gnlobject,
537     GParamSpec * arg G_GNUC_UNUSED, GESTrackObject * obj)
538 {
539   guint64 start;
540   GESTrackObjectClass *klass;
541
542   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
543
544   g_object_get (gnlobject, "media-start", &start, NULL);
545
546   GST_DEBUG ("gnlobject in-point : %" GST_TIME_FORMAT " current : %"
547       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->inpoint));
548
549   if (start != obj->inpoint) {
550     obj->inpoint = start;
551     if (klass->media_start_changed)
552       klass->media_start_changed (obj, start);
553   }
554 }
555
556 static void
557 gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
558     GESTrackObject * obj)
559 {
560   guint32 priority;
561   GESTrackObjectClass *klass;
562
563   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
564
565   g_object_get (gnlobject, "priority", &priority, NULL);
566
567   GST_DEBUG ("gnlobject priority : %d current : %d", priority, obj->priority);
568
569   if (priority != obj->priority) {
570     obj->priority = priority;
571     if (klass->gnl_priority_changed)
572       klass->gnl_priority_changed (obj, priority);
573   }
574 }
575
576 static void
577 gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
578     GESTrackObject * obj)
579 {
580   guint64 duration;
581   GESTrackObjectClass *klass;
582
583   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
584
585   g_object_get (gnlobject, "duration", &duration, NULL);
586
587   GST_DEBUG ("gnlobject duration : %" GST_TIME_FORMAT " current : %"
588       GST_TIME_FORMAT, GST_TIME_ARGS (duration), GST_TIME_ARGS (obj->duration));
589
590   if (duration != obj->duration) {
591     obj->duration = duration;
592     if (klass->duration_changed)
593       klass->duration_changed (obj, duration);
594   }
595 }
596
597 static void
598 gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
599     GESTrackObject * obj)
600 {
601   gboolean active;
602   GESTrackObjectClass *klass;
603
604   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
605
606   g_object_get (gnlobject, "active", &active, NULL);
607
608   GST_DEBUG ("gnlobject active : %d current : %d", active, obj->active);
609
610   if (active != obj->active) {
611     obj->active = active;
612     if (klass->active_changed)
613       klass->active_changed (obj, active);
614   }
615 }
616
617
618 /* default 'create_gnl_object' virtual method implementation */
619 static GstElement *
620 ges_track_object_create_gnl_object_func (GESTrackObject * self)
621 {
622   GESTrackObjectClass *klass = NULL;
623   GstElement *child = NULL;
624   GstElement *gnlobject;
625
626   klass = GES_TRACK_OBJECT_GET_CLASS (self);
627
628   if (G_UNLIKELY (self->priv->gnlobject != NULL))
629     goto already_have_gnlobject;
630
631   if (G_UNLIKELY (klass->gnlobject_factorytype == NULL))
632     goto no_gnlfactory;
633
634   GST_DEBUG ("Creating a supporting gnlobject of type '%s'",
635       klass->gnlobject_factorytype);
636
637   gnlobject = gst_element_factory_make (klass->gnlobject_factorytype, NULL);
638
639   if (G_UNLIKELY (gnlobject == NULL))
640     goto no_gnlobject;
641
642   if (klass->create_element) {
643     GST_DEBUG ("Calling subclass 'create_element' vmethod");
644     child = klass->create_element (self);
645
646     if (G_UNLIKELY (!child))
647       goto child_failure;
648
649     if (!gst_bin_add (GST_BIN (gnlobject), child))
650       goto add_failure;
651
652     GST_DEBUG ("Succesfully got the element to put in the gnlobject");
653     self->priv->element = child;
654   }
655
656   GST_DEBUG ("done");
657   return gnlobject;
658
659
660   /* ERROR CASES */
661
662 already_have_gnlobject:
663   {
664     GST_ERROR ("Already controlling a GnlObject %s",
665         GST_ELEMENT_NAME (self->priv->gnlobject));
666     return NULL;
667   }
668
669 no_gnlfactory:
670   {
671     GST_ERROR ("No GESTrackObject::gnlobject_factorytype implementation!");
672     return NULL;
673   }
674
675 no_gnlobject:
676   {
677     GST_ERROR ("Error creating a gnlobject of type '%s'",
678         klass->gnlobject_factorytype);
679     return NULL;
680   }
681
682 child_failure:
683   {
684     GST_ERROR ("create_element returned NULL");
685     gst_object_unref (gnlobject);
686     return NULL;
687   }
688
689 add_failure:
690   {
691     GST_ERROR ("Error adding the contents to the gnlobject");
692     gst_object_unref (child);
693     gst_object_unref (gnlobject);
694     return NULL;
695   }
696 }
697
698 static gboolean
699 ensure_gnl_object (GESTrackObject * object)
700 {
701   GESTrackObjectClass *class;
702   GstElement *gnlobject;
703   GHashTable *props_hash;
704   gboolean res = FALSE;
705
706   if (object->priv->gnlobject && object->priv->valid)
707     return FALSE;
708
709   /* 1. Create the GnlObject */
710   GST_DEBUG ("Creating GnlObject");
711
712   class = GES_TRACK_OBJECT_GET_CLASS (object);
713
714   if (G_UNLIKELY (class->create_gnl_object == NULL)) {
715     GST_ERROR ("No 'create_gnl_object' implementation !");
716     goto done;
717   }
718
719   GST_DEBUG ("Calling virtual method");
720
721   /* call the create_gnl_object virtual method */
722   gnlobject = class->create_gnl_object (object);
723
724   if (G_UNLIKELY (gnlobject == NULL)) {
725     GST_ERROR
726         ("'create_gnl_object' implementation returned TRUE but no GnlObject is available");
727     goto done;
728   }
729
730   object->priv->gnlobject = gnlobject;
731
732   /* 2. Fill in the GnlObject */
733   if (gnlobject) {
734     GST_DEBUG ("Got a valid GnlObject, now filling it in");
735
736     res =
737         ges_timeline_object_fill_track_object (object->priv->timelineobj,
738         object, object->priv->gnlobject);
739     if (res) {
740       /* Connect to property notifications */
741       /* FIXME : remember the signalids so we can remove them later on !!! */
742       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::start",
743           G_CALLBACK (gnlobject_start_cb), object);
744       g_signal_connect (G_OBJECT (object->priv->gnlobject),
745           "notify::media-start", G_CALLBACK (gnlobject_media_start_cb), object);
746       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::duration",
747           G_CALLBACK (gnlobject_duration_cb), object);
748       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::priority",
749           G_CALLBACK (gnlobject_priority_cb), object);
750       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::active",
751           G_CALLBACK (gnlobject_active_cb), object);
752
753       /* Set some properties on the GnlObject */
754       g_object_set (object->priv->gnlobject,
755           "caps", ges_track_get_caps (object->priv->track),
756           "duration", object->priv->pending_duration,
757           "media-duration", object->priv->pending_duration,
758           "start", object->priv->pending_start,
759           "media-start", object->priv->pending_inpoint,
760           "priority", object->priv->pending_priority,
761           "active", object->priv->pending_active, NULL);
762
763       /*  We feed up the props_hashtable if possible */
764       if (class->get_props_hastable) {
765         props_hash = class->get_props_hastable (object);
766
767         if (props_hash == NULL) {
768           GST_DEBUG ("'get_props_hastable' implementation returned TRUE but no\
769                  properties_hashtable is available");
770         } else {
771           object->priv->properties_hashtable = props_hash;
772           connect_properties_signals (object);
773         }
774       }
775     }
776   }
777
778 done:
779   object->priv->valid = res;
780
781   GST_DEBUG ("Returning res:%d", res);
782
783   return res;
784 }
785
786 /* INTERNAL USAGE */
787 gboolean
788 ges_track_object_set_track (GESTrackObject * object, GESTrack * track)
789 {
790   GST_DEBUG ("object:%p, track:%p", object, track);
791
792   object->priv->track = track;
793
794   if (object->priv->track)
795     return ensure_gnl_object (object);
796
797   return TRUE;
798 }
799
800 /**
801  * ges_track_object_get_track:
802  * @object: a #GESTrackObject
803  *
804  * Get the #GESTrack to which this object belongs. 
805  *
806  * Returns: (transfer none): The #GESTrack to which this object belongs. Can be %NULL if it
807  * is not in any track
808  */
809 GESTrack *
810 ges_track_object_get_track (GESTrackObject * object)
811 {
812   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
813
814   return object->priv->track;
815 }
816
817
818 void
819 ges_track_object_set_timeline_object (GESTrackObject * object,
820     GESTimelineObject * tlobj)
821 {
822   GST_DEBUG ("object:%p, timeline-object:%p", object, tlobj);
823
824   object->priv->timelineobj = tlobj;
825 }
826
827 /**
828  * ges_track_object_get_timeline_object:
829  * @object: a #GESTrackObject
830  *
831  * Get the #GESTimelineObject which is controlling this track object
832  *
833  * Returns: (transfer none): the #GESTimelineObject which is controlling
834  * this track object
835  */
836 GESTimelineObject *
837 ges_track_object_get_timeline_object (GESTrackObject * object)
838 {
839   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
840
841   return object->priv->timelineobj;
842 }
843
844 /**
845  * ges_track_object_get_gnlobject:
846  * @object: a #GESTrackObject
847  *
848  * Get the GNonLin object this object is controlling.
849  *
850  * Returns: (transfer none): the GNonLin object this object is controlling.
851  */
852 GstElement *
853 ges_track_object_get_gnlobject (GESTrackObject * object)
854 {
855   return object->priv->gnlobject;
856 }
857
858 /**
859  * ges_track_object_get_element:
860  * @object: a #GESTrackObject
861  *
862  * Get the #GstElement this track object is controlling within GNonLin.
863  *
864  * Returns: (transfer none): the #GstElement this track object is controlling
865  * within GNonLin.
866  */
867 GstElement *
868 ges_track_object_get_element (GESTrackObject * object)
869 {
870   return object->priv->element;
871 }
872
873 /**
874  * ges_track_object_set_locked:
875  * @object: a #GESTrackObject
876  * @locked: whether the object is lock to its parent
877  *
878  * Set the locking status of the @object in relationship to its controlling
879  * #GESTimelineObject. If @locked is %TRUE, then this object will move synchronously
880  * with its controlling #GESTimelineObject.
881 */
882 void
883 ges_track_object_set_locked (GESTrackObject * object, gboolean locked)
884 {
885   object->priv->locked = locked;
886 }
887
888 /**
889  * ges_track_object_is_locked:
890  * @object: a #GESTrackObject
891  *
892  * Let you know if object us locked or not (moving synchronously).
893  *
894  * Returns: %TRUE if the object is moving synchronously to its controlling
895  * #GESTimelineObject, else %FALSE.
896  */
897 gboolean
898 ges_track_object_is_locked (GESTrackObject * object)
899 {
900   return object->priv->locked;
901 }
902
903 /**
904  * ges_track_object_get_start:
905  * @object: a #GESTrackObject
906  *
907  * Get the position of the object in the container #GESTrack.
908  *
909  * Returns: the start position (in #GstClockTime)
910  */
911 guint64
912 ges_track_object_get_start (GESTrackObject * object)
913 {
914   if (G_UNLIKELY (object->priv->gnlobject == NULL))
915     return object->priv->pending_start;
916   else
917     return object->start;
918 }
919
920 /**
921  * ges_track_object_get_inpoint:
922  * @object: a #GESTrackObject
923  *
924  * Get the offset within the contents of this #GESTrackObject
925  *
926  * Returns: the in-point (in #GstClockTime)
927  */
928 guint64
929 ges_track_object_get_inpoint (GESTrackObject * object)
930 {
931   if (G_UNLIKELY (object->priv->gnlobject == NULL))
932     return object->priv->pending_inpoint;
933   else
934     return object->inpoint;
935 }
936
937 /**
938  * ges_track_object_get_duration:
939  * @object: a #GESTrackObject
940  *
941  * Get the duration which will be used in the container #GESTrack
942  * starting from the 'in-point'
943  *
944  * Returns: the duration (in #GstClockTime)
945  */
946 guint64
947 ges_track_object_get_duration (GESTrackObject * object)
948 {
949   if (G_UNLIKELY (object->priv->gnlobject == NULL))
950     return object->priv->pending_duration;
951   else
952     return object->duration;
953 }
954
955 /**
956  * ges_track_object_get_priority:
957  * @object: a #GESTrackObject
958  *
959  * Get the priority of the object withing the containing #GESTrack.
960  *
961  * Returns: the priority of @object
962  */
963 guint32
964 ges_track_object_get_priority (GESTrackObject * object)
965 {
966   if (G_UNLIKELY (object->priv->gnlobject == NULL))
967     return object->priv->pending_priority;
968   else
969     return object->priority;
970 }
971
972 /**
973  * ges_track_object_is_active:
974  * @object: a #GESTrackObject
975  *
976  * Lets you know if @object will be used for playback and rendering,
977  * or not.
978  *
979  * Returns: %TRUE if @object is active, %FALSE otherwize
980  */
981 gboolean
982 ges_track_object_is_active (GESTrackObject * object)
983 {
984   if (G_UNLIKELY (object->priv->gnlobject == NULL))
985     return object->priv->pending_active;
986   else
987     return object->active;
988 }
989
990 /**
991  * ges_track_object_set_child_property:
992  * @object: a #GESTrackObject
993  * @property_name: The name of the property to set
994  * @value: the value
995  *
996  * Sets a property of a child of @object. The property name
997  * should look like ClasseName-property-name
998  */
999 void
1000 ges_track_object_set_child_property (GESTrackObject * object,
1001     const gchar * property_name, GValue * value)
1002 {
1003   GESTrackObjectPrivate *priv = object->priv;
1004
1005   if (priv->properties_hashtable) {
1006     GstElement *element;
1007     gchar **prop_name;
1008
1009     element = g_hash_table_lookup (priv->properties_hashtable, property_name);
1010     if (element) {
1011       prop_name = g_strsplit (property_name, "-", 2);
1012       g_object_set_property (G_OBJECT (element), prop_name[1], value);
1013       g_strfreev (prop_name);
1014     } else {
1015       GST_ERROR ("The %s property doesn't exist", property_name);
1016     }
1017   } else {
1018     GST_DEBUG ("The child properties haven't been set on %p", object);
1019   }
1020 }
1021
1022 /**
1023 * ges_track_object_get_child_property:
1024 * @object: The origin #GESTrackObject
1025 * @property_name: The name of the property
1026 * @value: return location for the property value
1027 *
1028 * Gets a property of a child of @object.
1029 */
1030 void
1031 ges_track_object_get_child_property (GESTrackObject * object,
1032     const gchar * property_name, gpointer value)
1033 {
1034   GESTrackObjectPrivate *priv = object->priv;
1035
1036   if (priv->properties_hashtable) {
1037     GstElement *element;
1038     gchar **prop_name;
1039
1040     element = g_hash_table_lookup (priv->properties_hashtable, property_name);
1041     if (element) {
1042       prop_name = g_strsplit (property_name, "-", 2);
1043       g_object_get (G_OBJECT (element), prop_name[1], value, NULL);
1044       g_strfreev (prop_name);
1045     } else {
1046       GST_ERROR ("The %s property doesn't exist", property_name);
1047     }
1048   } else {
1049     GST_DEBUG ("The child properties haven't been set on %p", object);
1050   }
1051 }