GESTrackObject: add a ges_track_object_list_children_properties method
[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 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   ges_track_object_signals[DEEP_NOTIFY] =
283       g_signal_new ("deep-notify", G_TYPE_FROM_CLASS (klass),
284       G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED |
285       G_SIGNAL_NO_HOOKS, 0, NULL, NULL, gst_marshal_VOID__OBJECT_PARAM,
286       G_TYPE_NONE, 2, GST_TYPE_ELEMENT, G_TYPE_PARAM);
287
288   klass->create_gnl_object = ges_track_object_create_gnl_object_func;
289   /*  There is no 'get_props_hastable' default implementation */
290   klass->get_props_hastable = NULL;
291   klass->list_children_properties = default_list_children_properties;
292 }
293
294 static void
295 ges_track_object_init (GESTrackObject * self)
296 {
297   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
298       GES_TYPE_TRACK_OBJECT, GESTrackObjectPrivate);
299
300   /* Sane default values */
301   self->priv->pending_start = 0;
302   self->priv->pending_inpoint = 0;
303   self->priv->pending_duration = GST_SECOND;
304   self->priv->pending_priority = 1;
305   self->priv->pending_active = TRUE;
306   self->priv->locked = TRUE;
307   self->priv->properties_hashtable = NULL;
308 }
309
310 static inline gboolean
311 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start)
312 {
313   GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
314       object, GST_TIME_ARGS (start));
315
316   if (object->priv->gnlobject != NULL) {
317     if (G_UNLIKELY (start == object->start))
318       return FALSE;
319
320     g_object_set (object->priv->gnlobject, "start", start, NULL);
321   } else
322     object->priv->pending_start = start;
323   return TRUE;
324 };
325
326 /**
327  * ges_track_object_set_start:
328  * @object: a #GESTrackObject
329  * @start: the start position (in #GstClockTime)
330  *
331  * Sets the position of the object in the container #GESTrack.
332  */
333 void
334 ges_track_object_set_start (GESTrackObject * object, guint64 start)
335 {
336   if (ges_track_object_set_start_internal (object, start))
337 #if GLIB_CHECK_VERSION(2,26,0)
338     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
339 #else
340     g_object_notify (G_OBJECT (object), "start");
341 #endif
342 }
343
344 static inline gboolean
345 ges_track_object_set_inpoint_internal (GESTrackObject * object, guint64 inpoint)
346 {
347
348   GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
349       object, GST_TIME_ARGS (inpoint));
350
351   if (object->priv->gnlobject != NULL) {
352     if (G_UNLIKELY (inpoint == object->inpoint))
353       return FALSE;
354
355     g_object_set (object->priv->gnlobject, "media-start", inpoint, NULL);
356   } else
357     object->priv->pending_inpoint = inpoint;
358
359   return TRUE;
360 }
361
362 /**
363  * ges_track_object_set_inpoint:
364  * @object: a #GESTrackObject
365  * @inpoint: the in-point (in #GstClockTime)
366  *
367  * Set the offset within the contents of this #GESTrackObject
368  */
369 void
370 ges_track_object_set_inpoint (GESTrackObject * object, guint64 inpoint)
371 {
372   if (ges_track_object_set_inpoint_internal (object, inpoint))
373 #if GLIB_CHECK_VERSION(2,26,0)
374     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
375 #else
376     g_object_notify (G_OBJECT (object), "in-point");
377 #endif
378 }
379
380 static inline gboolean
381 ges_track_object_set_duration_internal (GESTrackObject * object,
382     guint64 duration)
383 {
384   GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
385       object, GST_TIME_ARGS (duration));
386
387   if (object->priv->gnlobject != NULL) {
388     if (G_UNLIKELY (duration == object->duration))
389       return FALSE;
390
391     g_object_set (object->priv->gnlobject, "duration", duration,
392         "media-duration", duration, NULL);
393   } else
394     object->priv->pending_duration = duration;
395   return TRUE;
396 }
397
398 /**
399  * ges_track_object_set_duration:
400  * @object: a #GESTrackObject
401  * @duration: the duration (in #GstClockTime)
402  *
403  * Set the duration which will be used in the container #GESTrack
404  * starting from the 'in-point'
405  */
406 void
407 ges_track_object_set_duration (GESTrackObject * object, guint64 duration)
408 {
409   if (ges_track_object_set_duration_internal (object, duration))
410 #if GLIB_CHECK_VERSION(2,26,0)
411     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
412 #else
413     g_object_notify (G_OBJECT (object), "duration");
414 #endif
415 }
416
417 static inline gboolean
418 ges_track_object_set_priority_internal (GESTrackObject * object,
419     guint32 priority)
420 {
421   GST_DEBUG ("object:%p, priority:%" G_GUINT32_FORMAT, object, priority);
422
423   if (object->priv->gnlobject != NULL) {
424     if (G_UNLIKELY (priority == object->priority))
425       return FALSE;
426
427     g_object_set (object->priv->gnlobject, "priority", priority, NULL);
428   } else
429     object->priv->pending_priority = priority;
430   return TRUE;
431 }
432
433 /**
434  * ges_track_object_set_priority:
435  * @object: a #GESTrackObject
436  * @priority: the priority
437  *
438  * Sets the priority of the object withing the containing #GESTrack.
439  * If two objects intersect over the same region of time, the priority
440  * property is used to decide which one takes precedence.
441  *
442  * The highest priority (that supercedes everything) is 0, and then
443  * lowering priorities go in increasing numerical value (with G_MAXUINT32
444  * being the lowest priority).
445  */
446 void
447 ges_track_object_set_priority (GESTrackObject * object, guint32 priority)
448 {
449   if (ges_track_object_set_priority_internal (object, priority))
450 #if GLIB_CHECK_VERSION(2,26,0)
451     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_PRIORITY]);
452 #else
453     g_object_notify (G_OBJECT (object), "priority");
454 #endif
455 }
456
457
458 /**
459  * ges_track_object_set_active:
460  * @object: a #GESTrackObject
461  * @active: visibility
462  *
463  * Sets the usage of the @object. If @active is %TRUE, the object will be used for
464  * playback and rendering, else it will be ignored.
465  *
466  * Returns: %TRUE if the property was toggled, else %FALSE
467  */
468 gboolean
469 ges_track_object_set_active (GESTrackObject * object, gboolean active)
470 {
471   GST_DEBUG ("object:%p, active:%d", object, active);
472
473   if (object->priv->gnlobject != NULL) {
474     if (G_UNLIKELY (active == object->active))
475       return FALSE;
476
477     g_object_set (object->priv->gnlobject, "active", active, NULL);
478   } else
479     object->priv->pending_active = active;
480   return TRUE;
481 }
482
483 /* Callbacks from the GNonLin object */
484 static void
485 gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
486     GESTrackObject * obj)
487 {
488   guint64 start;
489   GESTrackObjectClass *klass;
490
491   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
492
493   g_object_get (gnlobject, "start", &start, NULL);
494
495   GST_DEBUG ("gnlobject start : %" GST_TIME_FORMAT " current : %"
496       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->start));
497
498   if (start != obj->start) {
499     obj->start = start;
500     if (klass->start_changed)
501       klass->start_changed (obj, start);
502   }
503 }
504
505 static void
506 gst_element_prop_changed_cb (GstElement * element, GParamSpec * arg
507     G_GNUC_UNUSED, GESTrackObject * obj)
508 {
509   g_signal_emit (obj, ges_track_object_signals[DEEP_NOTIFY], 0,
510       GST_ELEMENT (element), arg);
511 }
512
513 static void
514 connect_signal (gpointer key, gpointer value, gpointer user_data)
515 {
516   gchar **name = g_strsplit ((char *) key, "-", 2);
517   gchar *signame = g_strconcat ("notify::", (gchar *) name[1], NULL);
518   g_strfreev (name);
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   object->priv->locked = locked;
892 }
893
894 /**
895  * ges_track_object_is_locked:
896  * @object: a #GESTrackObject
897  *
898  * Let you know if object us locked or not (moving synchronously).
899  *
900  * Returns: %TRUE if the object is moving synchronously to its controlling
901  * #GESTimelineObject, else %FALSE.
902  */
903 gboolean
904 ges_track_object_is_locked (GESTrackObject * object)
905 {
906   return object->priv->locked;
907 }
908
909 /**
910  * ges_track_object_get_start:
911  * @object: a #GESTrackObject
912  *
913  * Get the position of the object in the container #GESTrack.
914  *
915  * Returns: the start position (in #GstClockTime)
916  */
917 guint64
918 ges_track_object_get_start (GESTrackObject * object)
919 {
920   if (G_UNLIKELY (object->priv->gnlobject == NULL))
921     return object->priv->pending_start;
922   else
923     return object->start;
924 }
925
926 /**
927  * ges_track_object_get_inpoint:
928  * @object: a #GESTrackObject
929  *
930  * Get the offset within the contents of this #GESTrackObject
931  *
932  * Returns: the in-point (in #GstClockTime)
933  */
934 guint64
935 ges_track_object_get_inpoint (GESTrackObject * object)
936 {
937   if (G_UNLIKELY (object->priv->gnlobject == NULL))
938     return object->priv->pending_inpoint;
939   else
940     return object->inpoint;
941 }
942
943 /**
944  * ges_track_object_get_duration:
945  * @object: a #GESTrackObject
946  *
947  * Get the duration which will be used in the container #GESTrack
948  * starting from the 'in-point'
949  *
950  * Returns: the duration (in #GstClockTime)
951  */
952 guint64
953 ges_track_object_get_duration (GESTrackObject * object)
954 {
955   if (G_UNLIKELY (object->priv->gnlobject == NULL))
956     return object->priv->pending_duration;
957   else
958     return object->duration;
959 }
960
961 /**
962  * ges_track_object_get_priority:
963  * @object: a #GESTrackObject
964  *
965  * Get the priority of the object withing the containing #GESTrack.
966  *
967  * Returns: the priority of @object
968  */
969 guint32
970 ges_track_object_get_priority (GESTrackObject * object)
971 {
972   if (G_UNLIKELY (object->priv->gnlobject == NULL))
973     return object->priv->pending_priority;
974   else
975     return object->priority;
976 }
977
978 /**
979  * ges_track_object_is_active:
980  * @object: a #GESTrackObject
981  *
982  * Lets you know if @object will be used for playback and rendering,
983  * or not.
984  *
985  * Returns: %TRUE if @object is active, %FALSE otherwize
986  */
987 gboolean
988 ges_track_object_is_active (GESTrackObject * object)
989 {
990   if (G_UNLIKELY (object->priv->gnlobject == NULL))
991     return object->priv->pending_active;
992   else
993     return object->active;
994 }
995
996 /*
997  * ges_track_object_lookup_child:
998  * @object: object to lookup the property in
999  * @prop_name: name of the property to look up. You can specify the name of the
1000  *     class as such: ClassName::property-name, to guarantee that you get the
1001  *     proper GParamSpec in case various GstElement-s contain the same property
1002  *     name. If you don't do so, you will get the first element found, having
1003  *     this property and the and the corresponding GParamSpec.
1004  * @element: (out) (allow-none) (transfer full): pointer to a #GstElement that
1005  *     takes the real object to set property on
1006  * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
1007  *     describing the property
1008  *
1009  * Looks up which @element and @pspec would be effected by the given @name. If various
1010  * contained elements have this property name you will get the first one, unless you
1011  * specify the class name in @name.
1012  *
1013  * Returns: TRUE if @element and @pspec could be found. FALSE otherwise. In that
1014  * case the values for @pspec and @element are not modified. Unref @element after
1015  * usage.
1016  */
1017 gboolean
1018 ges_track_object_lookup_child (GESTrackObject * object, const gchar * prop_name,
1019     GstElement ** element, GParamSpec ** pspec)
1020 {
1021   GHashTableIter iter;
1022   gpointer key, value;
1023   gchar **names, *name, *classename;
1024   gboolean res;
1025   GESTrackObjectPrivate *priv = object->priv;
1026
1027   classename = NULL;
1028   res = FALSE;
1029
1030   names = g_strsplit (prop_name, "::", 2);
1031   if (names[1] != NULL) {
1032     classename = names[0];
1033     name = names[1];
1034   } else
1035     name = names[0];
1036
1037   g_hash_table_iter_init (&iter, priv->properties_hashtable);
1038   while (g_hash_table_iter_next (&iter, &key, &value)) {
1039     if (g_strcmp0 (G_PARAM_SPEC (key)->name, name) == 0) {
1040       if (classename == NULL ||
1041           g_strcmp0 (G_OBJECT_TYPE_NAME (G_OBJECT (value)), classename) == 0) {
1042         GST_DEBUG ("The %s property from %s has been found in %s", name,
1043             classename, GST_OBJECT_NAME (GST_OBJECT (element)));
1044         if (element)
1045           *element = g_object_ref (value);
1046
1047         *pspec = g_param_spec_ref (key);
1048         res = TRUE;
1049         break;
1050       }
1051     }
1052   }
1053   g_strfreev (names);
1054
1055   return res;
1056 }
1057
1058 /**
1059  * ges_track_object_set_child_property_by_pspec:
1060  * @object: a #GESTrackObject
1061  * @pspec: The #GParamSpec that specifies the property you want to set
1062  * @value: the value
1063  *
1064  * Sets a property of a child of @object.
1065  */
1066 void
1067 ges_track_object_set_child_property_by_pspec (GESTrackObject * object,
1068     GParamSpec * pspec, GValue * value)
1069 {
1070   GstElement *element;
1071
1072   GESTrackObjectPrivate *priv = object->priv;
1073
1074   if (!priv->properties_hashtable)
1075     goto prop_hash_not_set;
1076
1077   element = g_hash_table_lookup (priv->properties_hashtable, pspec);
1078   if (!element)
1079     goto not_found;
1080
1081   g_object_set_property (G_OBJECT (element), pspec->name, value);
1082
1083   return;
1084
1085 not_found:
1086   {
1087     GST_ERROR ("The %s property doesn't exist", pspec->name);
1088     return;
1089   }
1090 prop_hash_not_set:
1091   {
1092     GST_DEBUG ("The child properties haven't been set on %p", object);
1093     return;
1094   }
1095 }
1096
1097 /**
1098  * ges_track_object_set_child_property_valist:
1099  * @object: The #GESTrackObject parent object
1100  * @first_property_name: The name of the first property to set
1101  * @var_args: value for the first property, followed optionally by more
1102  * name/return location pairs, followed by NULL
1103  *
1104  * Sets a property of a child of @object. If there are various child elements
1105  * that have the same property name, you can distinguish them using the following
1106  * synthaxe: 'ClasseName::property_name' as property name. If you don't, the
1107  * corresponding property of the first element found will be set.
1108  */
1109 void
1110 ges_track_object_set_child_property_valist (GESTrackObject * object,
1111     const gchar * first_property_name, va_list var_args)
1112 {
1113   const gchar *name;
1114   GParamSpec *pspec;
1115   GstElement *element;
1116
1117   gchar *error = NULL;
1118   GValue value = { 0, };
1119
1120   g_return_if_fail (G_IS_OBJECT (object));
1121
1122   name = first_property_name;
1123
1124   /* Note: This part is in big part copied from the gst_child_object_set_valist
1125    * method. */
1126
1127   /* iterate over pairs */
1128   while (name) {
1129     if (!ges_track_object_lookup_child (object, name, &element, &pspec))
1130       goto not_found;
1131
1132 #if GLIB_CHECK_VERSION(2,23,3)
1133     G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
1134         G_VALUE_NOCOPY_CONTENTS, &error);
1135 #else
1136     g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1137     G_VALUE_COLLECT (&value, var_args, G_VALUE_NOCOPY_CONTENTS, &error);
1138 #endif
1139
1140     if (error)
1141       goto cant_copy;
1142
1143     g_object_set_property (G_OBJECT (element), pspec->name, &value);
1144
1145     g_object_unref (element);
1146     g_value_unset (&value);
1147
1148     name = va_arg (var_args, gchar *);
1149   }
1150   return;
1151
1152 not_found:
1153   {
1154     GST_WARNING ("No property %s in OBJECT\n", name);
1155     return;
1156   }
1157 cant_copy:
1158   {
1159     GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
1160         error);
1161     g_value_unset (&value);
1162     return;
1163   }
1164 }
1165
1166 /**
1167  * ges_track_object_set_child_property:
1168  * @object: The #GESTrackObject parent object
1169  * @first_property_name: The name of the first property to set
1170  * @...: value for the first property, followed optionally by more
1171  * name/return location pairs, followed by NULL
1172  *
1173  * Sets a property of a child of @object. If there are various child elements
1174  * that have the same property name, you can distinguish them using the following
1175  * synthaxe: 'ClasseName::property_name' as property name. If you don't, the
1176  * corresponding property of the first element found will be set.
1177  */
1178 void
1179 ges_track_object_set_child_property (GESTrackObject * object,
1180     const gchar * first_property_name, ...)
1181 {
1182   va_list var_args;
1183
1184   va_start (var_args, first_property_name);
1185   ges_track_object_set_child_property_valist (object, first_property_name,
1186       var_args);
1187   va_end (var_args);
1188 }
1189
1190 /**
1191  * ges_track_object_get_child_property_valist:
1192  * @object: The #GESTrackObject parent object
1193  * @first_property_name: The name of the first property to get
1194  * @var_args: value for the first property, followed optionally by more
1195  * name/return location pairs, followed by NULL
1196  *
1197  * Gets a property of a child of @object. If there are various child elements
1198  * that have the same property name, you can distinguish them using the following
1199  * synthaxe: 'ClasseName::property_name' as property name. If you don't, the
1200  * corresponding property of the first element found will be set.
1201  */
1202 void
1203 ges_track_object_get_child_property_valist (GESTrackObject * object,
1204     const gchar * first_property_name, va_list var_args)
1205 {
1206   const gchar *name;
1207   gchar *error = NULL;
1208   GValue value = { 0, };
1209   GParamSpec *pspec;
1210   GstElement *element;
1211
1212   g_return_if_fail (G_IS_OBJECT (object));
1213
1214   name = first_property_name;
1215
1216   /* This part is in big part copied from the gst_child_object_get_valist method */
1217   while (name) {
1218     if (!ges_track_object_lookup_child (object, name, &element, &pspec))
1219       goto not_found;
1220
1221     g_value_init (&value, pspec->value_type);
1222     g_object_get_property (G_OBJECT (element), pspec->name, &value);
1223     g_object_unref (element);
1224
1225     G_VALUE_LCOPY (&value, var_args, 0, &error);
1226     if (error)
1227       goto cant_copy;
1228     g_value_unset (&value);
1229     name = va_arg (var_args, gchar *);
1230   }
1231   return;
1232
1233 not_found:
1234   {
1235     GST_WARNING ("no property %s in object", name);
1236     return;
1237   }
1238 cant_copy:
1239   {
1240     GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
1241         error);
1242     g_value_unset (&value);
1243     return;
1244   }
1245 }
1246
1247 /**
1248  * ges_track_object_list_children_properties:
1249  * @object: The #GESTrackObject to get the list of children properties from
1250  * @n_properties: return location for the length of the returned array
1251  *
1252  * Gets an array of #GParamSpec* for all configurable properties of the
1253  * children of @object.
1254  *
1255  * Returns: an array of #GParamSpec* which should be freed after use or %NULL
1256  * if something went wrong
1257  */
1258 GParamSpec **
1259 ges_track_object_list_children_properties (GESTrackObject * object,
1260     guint * n_properties)
1261 {
1262   GESTrackObjectClass *class;
1263   class = GES_TRACK_OBJECT_GET_CLASS (object);
1264
1265   return class->list_children_properties (object, n_properties);
1266 }
1267
1268 /**
1269  * ges_track_object_get_child_property:
1270  * @object: The origin #GESTrackObject
1271  * @first_property_name: The name of the first property to get
1272  * @...: return location for the first property, followed optionally by more
1273  * name/return location pairs, followed by NULL
1274  *
1275  * Gets properties of a child of @object.
1276  */
1277 void
1278 ges_track_object_get_child_property (GESTrackObject * object,
1279     const gchar * first_property_name, ...)
1280 {
1281   va_list var_args;
1282
1283   va_start (var_args, first_property_name);
1284   ges_track_object_get_child_property_valist (object, first_property_name,
1285       var_args);
1286   va_end (var_args);
1287 }
1288
1289 /**
1290  * ges_track_object_get_child_property_by_pspec:
1291  * @object: a #GESTrackObject
1292  * @pspec: The #GParamSpec that specifies the property you want to get
1293  * @value: return location for the value
1294  *
1295  * Gets a property of a child of @object.
1296  */
1297 void
1298 ges_track_object_get_child_property_by_pspec (GESTrackObject * object,
1299     GParamSpec * pspec, GValue * value)
1300 {
1301   GstElement *element;
1302
1303   GESTrackObjectPrivate *priv = object->priv;
1304
1305   if (!priv->properties_hashtable)
1306     goto prop_hash_not_set;
1307
1308   element = g_hash_table_lookup (priv->properties_hashtable, pspec);
1309   if (!element)
1310     goto not_found;
1311
1312   g_object_get_property (G_OBJECT (element), pspec->name, value);
1313
1314   return;
1315
1316 not_found:
1317   {
1318     GST_ERROR ("The %s property doesn't exist", pspec->name);
1319     return;
1320   }
1321 prop_hash_not_set:
1322   {
1323     GST_ERROR ("The child properties haven't been set on %p", object);
1324     return;
1325   }
1326 }
1327
1328 GParamSpec **
1329 default_list_children_properties (GESTrackObject * object, guint * n_properties)
1330 {
1331   GParamSpec **pspec, *spec;
1332   GHashTableIter iter;
1333   gpointer key, value;
1334
1335   guint i = 0;
1336
1337   if (!object->priv->properties_hashtable)
1338     goto prop_hash_not_set;
1339
1340   *n_properties = g_hash_table_size (object->priv->properties_hashtable);
1341   pspec = g_new (GParamSpec *, *n_properties);
1342
1343   g_hash_table_iter_init (&iter, object->priv->properties_hashtable);
1344   while (g_hash_table_iter_next (&iter, &key, &value)) {
1345     spec = G_PARAM_SPEC (key);
1346     pspec[i] = g_param_spec_ref (spec);
1347     i++;
1348   }
1349
1350   return pspec;
1351
1352 prop_hash_not_set:
1353   {
1354     GST_ERROR ("The child properties haven't been set on %p", object);
1355     return NULL;
1356   }
1357 }