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