GESTrackObject: missing Since tag and typo fixes
[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   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  * Since: 0.10.2
918  */
919 guint64
920 ges_track_object_get_start (GESTrackObject * object)
921 {
922   if (G_UNLIKELY (object->priv->gnlobject == NULL))
923     return object->priv->pending_start;
924   else
925     return object->start;
926 }
927
928 /**
929  * ges_track_object_get_inpoint:
930  * @object: a #GESTrackObject
931  *
932  * Get the offset within the contents of this #GESTrackObject
933  *
934  * Returns: the in-point (in #GstClockTime)
935  *
936  * Since: 0.10.2
937  */
938 guint64
939 ges_track_object_get_inpoint (GESTrackObject * object)
940 {
941   if (G_UNLIKELY (object->priv->gnlobject == NULL))
942     return object->priv->pending_inpoint;
943   else
944     return object->inpoint;
945 }
946
947 /**
948  * ges_track_object_get_duration:
949  * @object: a #GESTrackObject
950  *
951  * Get the duration which will be used in the container #GESTrack
952  * starting from the 'in-point'
953  *
954  * Returns: the duration (in #GstClockTime)
955  *
956  * Since: 0.10.2
957  */
958 guint64
959 ges_track_object_get_duration (GESTrackObject * object)
960 {
961   if (G_UNLIKELY (object->priv->gnlobject == NULL))
962     return object->priv->pending_duration;
963   else
964     return object->duration;
965 }
966
967 /**
968  * ges_track_object_get_priority:
969  * @object: a #GESTrackObject
970  *
971  * Get the priority of the object withing the containing #GESTrack.
972  *
973  * Returns: the priority of @object
974  *
975  * Since: 0.10.2
976  */
977 guint32
978 ges_track_object_get_priority (GESTrackObject * object)
979 {
980   if (G_UNLIKELY (object->priv->gnlobject == NULL))
981     return object->priv->pending_priority;
982   else
983     return object->priority;
984 }
985
986 /**
987  * ges_track_object_is_active:
988  * @object: a #GESTrackObject
989  *
990  * Lets you know if @object will be used for playback and rendering,
991  * or not.
992  *
993  * Returns: %TRUE if @object is active, %FALSE otherwize
994  *
995  * Since: 0.10.2
996  */
997 gboolean
998 ges_track_object_is_active (GESTrackObject * object)
999 {
1000   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1001     return object->priv->pending_active;
1002   else
1003     return object->active;
1004 }
1005
1006 /**
1007  * ges_track_object_lookup_child:
1008  * @object: object to lookup the property in
1009  * @prop_name: name of the property to look up. You can specify the name of the
1010  *     class as such: "ClassName::property-name", to guarantee that you get the
1011  *     proper GParamSpec in case various GstElement-s contain the same property
1012  *     name. If you don't do so, you will get the first element found, having
1013  *     this property and the and the corresponding GParamSpec.
1014  * @element: (out) (allow-none) (transfer full): pointer to a #GstElement that
1015  *     takes the real object to set property on
1016  * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
1017  *     describing the property
1018  *
1019  * Looks up which @element and @pspec would be effected by the given @name. If various
1020  * contained elements have this property name you will get the first one, unless you
1021  * specify the class name in @name.
1022  *
1023  * Returns: TRUE if @element and @pspec could be found. FALSE otherwise. In that
1024  * case the values for @pspec and @element are not modified. Unref @element after
1025  * usage.
1026  *
1027  * Since: 0.10.2
1028  */
1029 gboolean
1030 ges_track_object_lookup_child (GESTrackObject * object, const gchar * prop_name,
1031     GstElement ** element, GParamSpec ** pspec)
1032 {
1033   GHashTableIter iter;
1034   gpointer key, value;
1035   gchar **names, *name, *classename;
1036   gboolean res;
1037   GESTrackObjectPrivate *priv = object->priv;
1038
1039   classename = NULL;
1040   res = FALSE;
1041
1042   names = g_strsplit (prop_name, "::", 2);
1043   if (names[1] != NULL) {
1044     classename = names[0];
1045     name = names[1];
1046   } else
1047     name = names[0];
1048
1049   g_hash_table_iter_init (&iter, priv->properties_hashtable);
1050   while (g_hash_table_iter_next (&iter, &key, &value)) {
1051     if (g_strcmp0 (G_PARAM_SPEC (key)->name, name) == 0) {
1052       if (classename == NULL ||
1053           g_strcmp0 (G_OBJECT_TYPE_NAME (G_OBJECT (value)), classename) == 0) {
1054         GST_DEBUG ("The %s property from %s has been found in %s", name,
1055             classename, GST_OBJECT_NAME (GST_OBJECT (element)));
1056         if (element)
1057           *element = g_object_ref (value);
1058
1059         *pspec = g_param_spec_ref (key);
1060         res = TRUE;
1061         break;
1062       }
1063     }
1064   }
1065   g_strfreev (names);
1066
1067   return res;
1068 }
1069
1070 /**
1071  * ges_track_object_set_child_property_by_pspec:
1072  * @object: a #GESTrackObject
1073  * @pspec: The #GParamSpec that specifies the property you want to set
1074  * @value: the value
1075  *
1076  * Sets a property of a child of @object.
1077  *
1078  * Since: 0.10.2
1079  */
1080 void
1081 ges_track_object_set_child_property_by_pspec (GESTrackObject * object,
1082     GParamSpec * pspec, GValue * value)
1083 {
1084   GstElement *element;
1085
1086   GESTrackObjectPrivate *priv = object->priv;
1087
1088   if (!priv->properties_hashtable)
1089     goto prop_hash_not_set;
1090
1091   element = g_hash_table_lookup (priv->properties_hashtable, pspec);
1092   if (!element)
1093     goto not_found;
1094
1095   g_object_set_property (G_OBJECT (element), pspec->name, value);
1096
1097   return;
1098
1099 not_found:
1100   {
1101     GST_ERROR ("The %s property doesn't exist", pspec->name);
1102     return;
1103   }
1104 prop_hash_not_set:
1105   {
1106     GST_DEBUG ("The child properties haven't been set on %p", object);
1107     return;
1108   }
1109 }
1110
1111 /**
1112  * ges_track_object_set_child_property_valist:
1113  * @object: The #GESTrackObject parent object
1114  * @first_property_name: The name of the first property to set
1115  * @var_args: value for the first property, followed optionally by more
1116  * name/return location pairs, followed by NULL
1117  *
1118  * Sets a property of a child of @object. If there are various child elements
1119  * that have the same property name, you can distinguish them using the following
1120  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1121  * corresponding property of the first element found will be set.
1122  *
1123  * Since: 0.10.2
1124  */
1125 void
1126 ges_track_object_set_child_property_valist (GESTrackObject * object,
1127     const gchar * first_property_name, va_list var_args)
1128 {
1129   const gchar *name;
1130   GParamSpec *pspec;
1131   GstElement *element;
1132
1133   gchar *error = NULL;
1134   GValue value = { 0, };
1135
1136   g_return_if_fail (G_IS_OBJECT (object));
1137
1138   name = first_property_name;
1139
1140   /* Note: This part is in big part copied from the gst_child_object_set_valist
1141    * method. */
1142
1143   /* iterate over pairs */
1144   while (name) {
1145     if (!ges_track_object_lookup_child (object, name, &element, &pspec))
1146       goto not_found;
1147
1148 #if GLIB_CHECK_VERSION(2,23,3)
1149     G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
1150         G_VALUE_NOCOPY_CONTENTS, &error);
1151 #else
1152     g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1153     G_VALUE_COLLECT (&value, var_args, G_VALUE_NOCOPY_CONTENTS, &error);
1154 #endif
1155
1156     if (error)
1157       goto cant_copy;
1158
1159     g_object_set_property (G_OBJECT (element), pspec->name, &value);
1160
1161     g_object_unref (element);
1162     g_value_unset (&value);
1163
1164     name = va_arg (var_args, gchar *);
1165   }
1166   return;
1167
1168 not_found:
1169   {
1170     GST_WARNING ("No property %s in OBJECT\n", name);
1171     return;
1172   }
1173 cant_copy:
1174   {
1175     GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
1176         error);
1177     g_value_unset (&value);
1178     return;
1179   }
1180 }
1181
1182 /**
1183  * ges_track_object_set_child_property:
1184  * @object: The #GESTrackObject parent object
1185  * @first_property_name: The name of the first property to set
1186  * @...: value for the first property, followed optionally by more
1187  * name/return location pairs, followed by NULL
1188  *
1189  * Sets a property of a child of @object. If there are various child elements
1190  * that have the same property name, you can distinguish them using the following
1191  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1192  * corresponding property of the first element found will be set.
1193  *
1194  * Since: 0.10.2
1195  */
1196 void
1197 ges_track_object_set_child_property (GESTrackObject * object,
1198     const gchar * first_property_name, ...)
1199 {
1200   va_list var_args;
1201
1202   va_start (var_args, first_property_name);
1203   ges_track_object_set_child_property_valist (object, first_property_name,
1204       var_args);
1205   va_end (var_args);
1206 }
1207
1208 /**
1209  * ges_track_object_get_child_property_valist:
1210  * @object: The #GESTrackObject parent object
1211  * @first_property_name: The name of the first property to get
1212  * @var_args: value for the first property, followed optionally by more
1213  * name/return location pairs, followed by NULL
1214  *
1215  * Gets a property of a child of @object. If there are various child elements
1216  * that have the same property name, you can distinguish them using the following
1217  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1218  * corresponding property of the first element found will be set.
1219  *
1220  * Since: 0.10.2
1221  */
1222 void
1223 ges_track_object_get_child_property_valist (GESTrackObject * object,
1224     const gchar * first_property_name, va_list var_args)
1225 {
1226   const gchar *name;
1227   gchar *error = NULL;
1228   GValue value = { 0, };
1229   GParamSpec *pspec;
1230   GstElement *element;
1231
1232   g_return_if_fail (G_IS_OBJECT (object));
1233
1234   name = first_property_name;
1235
1236   /* This part is in big part copied from the gst_child_object_get_valist method */
1237   while (name) {
1238     if (!ges_track_object_lookup_child (object, name, &element, &pspec))
1239       goto not_found;
1240
1241     g_value_init (&value, pspec->value_type);
1242     g_object_get_property (G_OBJECT (element), pspec->name, &value);
1243     g_object_unref (element);
1244
1245     G_VALUE_LCOPY (&value, var_args, 0, &error);
1246     if (error)
1247       goto cant_copy;
1248     g_value_unset (&value);
1249     name = va_arg (var_args, gchar *);
1250   }
1251   return;
1252
1253 not_found:
1254   {
1255     GST_WARNING ("no property %s in object", name);
1256     return;
1257   }
1258 cant_copy:
1259   {
1260     GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
1261         error);
1262     g_value_unset (&value);
1263     return;
1264   }
1265 }
1266
1267 /**
1268  * ges_track_object_list_children_properties:
1269  * @object: The #GESTrackObject to get the list of children properties from
1270  * @n_properties: return location for the length of the returned array
1271  *
1272  * Gets an array of #GParamSpec* for all configurable properties of the
1273  * children of @object.
1274  *
1275  * Returns: (transfer full) (array): an array of #GParamSpec* which should be freed after use or
1276  * %NULL if something went wrong
1277  *
1278  * Since: 0.10.2
1279  */
1280 GParamSpec **
1281 ges_track_object_list_children_properties (GESTrackObject * object,
1282     guint * n_properties)
1283 {
1284   GESTrackObjectClass *class;
1285
1286   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
1287
1288   class = GES_TRACK_OBJECT_GET_CLASS (object);
1289
1290   return class->list_children_properties (object, n_properties);
1291 }
1292
1293 /**
1294  * ges_track_object_get_child_property:
1295  * @object: The origin #GESTrackObject
1296  * @first_property_name: The name of the first property to get
1297  * @...: return location for the first property, followed optionally by more
1298  * name/return location pairs, followed by NULL
1299  *
1300  * Gets properties of a child of @object.
1301  *
1302  * Since: 0.10.2
1303  */
1304 void
1305 ges_track_object_get_child_property (GESTrackObject * object,
1306     const gchar * first_property_name, ...)
1307 {
1308   va_list var_args;
1309
1310   va_start (var_args, first_property_name);
1311   ges_track_object_get_child_property_valist (object, first_property_name,
1312       var_args);
1313   va_end (var_args);
1314 }
1315
1316 /**
1317  * ges_track_object_get_child_property_by_pspec:
1318  * @object: a #GESTrackObject
1319  * @pspec: The #GParamSpec that specifies the property you want to get
1320  * @value: return location for the value
1321  *
1322  * Gets a property of a child of @object.
1323  *
1324  * Since: 0.10.2
1325  */
1326 void
1327 ges_track_object_get_child_property_by_pspec (GESTrackObject * object,
1328     GParamSpec * pspec, GValue * value)
1329 {
1330   GstElement *element;
1331
1332   GESTrackObjectPrivate *priv = object->priv;
1333
1334   if (!priv->properties_hashtable)
1335     goto prop_hash_not_set;
1336
1337   element = g_hash_table_lookup (priv->properties_hashtable, pspec);
1338   if (!element)
1339     goto not_found;
1340
1341   g_object_get_property (G_OBJECT (element), pspec->name, value);
1342
1343   return;
1344
1345 not_found:
1346   {
1347     GST_ERROR ("The %s property doesn't exist", pspec->name);
1348     return;
1349   }
1350 prop_hash_not_set:
1351   {
1352     GST_ERROR ("The child properties haven't been set on %p", object);
1353     return;
1354   }
1355 }
1356
1357 static GParamSpec **
1358 default_list_children_properties (GESTrackObject * object, guint * n_properties)
1359 {
1360   GParamSpec **pspec, *spec;
1361   GHashTableIter iter;
1362   gpointer key, value;
1363
1364   guint i = 0;
1365
1366   if (!object->priv->properties_hashtable)
1367     goto prop_hash_not_set;
1368
1369   *n_properties = g_hash_table_size (object->priv->properties_hashtable);
1370   pspec = g_new (GParamSpec *, *n_properties);
1371
1372   g_hash_table_iter_init (&iter, object->priv->properties_hashtable);
1373   while (g_hash_table_iter_next (&iter, &key, &value)) {
1374     spec = G_PARAM_SPEC (key);
1375     pspec[i] = g_param_spec_ref (spec);
1376     i++;
1377   }
1378
1379   return pspec;
1380
1381 prop_hash_not_set:
1382   {
1383     *n_properties = 0;
1384     GST_ERROR ("The child properties haven't been set on %p", object);
1385     return NULL;
1386   }
1387 }