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