Merge remote-tracking branch 'origin/0.10'
[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 = TRUE;
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     if (object->priv->timelineobj)
763       res = ges_timeline_object_fill_track_object (object->priv->timelineobj,
764           object, object->priv->gnlobject);
765     else
766       res = TRUE;
767
768     if (res) {
769       /* Connect to property notifications */
770       /* FIXME : remember the signalids so we can remove them later on !!! */
771       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::start",
772           G_CALLBACK (gnlobject_start_cb), object);
773       g_signal_connect (G_OBJECT (object->priv->gnlobject),
774           "notify::media-start", G_CALLBACK (gnlobject_media_start_cb), object);
775       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::duration",
776           G_CALLBACK (gnlobject_duration_cb), object);
777       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::priority",
778           G_CALLBACK (gnlobject_priority_cb), object);
779       g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::active",
780           G_CALLBACK (gnlobject_active_cb), object);
781
782       /* Set some properties on the GnlObject */
783       g_object_set (object->priv->gnlobject,
784           "caps", ges_track_get_caps (object->priv->track),
785           "duration", object->priv->pending_duration,
786           "media-duration", object->priv->pending_duration,
787           "start", object->priv->pending_start,
788           "media-start", object->priv->pending_inpoint,
789           "priority", object->priv->pending_priority,
790           "active", object->priv->pending_active, NULL);
791
792       /*  We feed up the props_hashtable if possible */
793       if (class->get_props_hastable) {
794         props_hash = class->get_props_hastable (object);
795
796         if (props_hash == NULL) {
797           GST_DEBUG ("'get_props_hastable' implementation returned TRUE but no"
798               "properties_hashtable is available");
799         } else {
800           object->priv->properties_hashtable = props_hash;
801           connect_properties_signals (object);
802         }
803       }
804     }
805   }
806
807 done:
808   object->priv->valid = res;
809
810   GST_DEBUG ("Returning res:%d", res);
811
812   return res;
813 }
814
815 /* INTERNAL USAGE */
816 gboolean
817 ges_track_object_set_track (GESTrackObject * object, GESTrack * track)
818 {
819   GST_DEBUG ("object:%p, track:%p", object, track);
820
821   object->priv->track = track;
822
823   if (object->priv->track)
824     return ensure_gnl_object (object);
825
826   return TRUE;
827 }
828
829 /**
830  * ges_track_object_get_track:
831  * @object: a #GESTrackObject
832  *
833  * Get the #GESTrack to which this object belongs.
834  *
835  * Returns: (transfer none): The #GESTrack to which this object belongs. Can be %NULL if it
836  * is not in any track
837  */
838 GESTrack *
839 ges_track_object_get_track (GESTrackObject * object)
840 {
841   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
842
843   return object->priv->track;
844 }
845
846
847 void
848 ges_track_object_set_timeline_object (GESTrackObject * object,
849     GESTimelineObject * tlobj)
850 {
851   GST_DEBUG ("object:%p, timeline-object:%p", object, tlobj);
852
853   object->priv->timelineobj = tlobj;
854 }
855
856 /**
857  * ges_track_object_get_timeline_object:
858  * @object: a #GESTrackObject
859  *
860  * Get the #GESTimelineObject which is controlling this track object
861  *
862  * Returns: (transfer none): the #GESTimelineObject which is controlling
863  * this track object
864  */
865 GESTimelineObject *
866 ges_track_object_get_timeline_object (GESTrackObject * object)
867 {
868   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
869
870   return object->priv->timelineobj;
871 }
872
873 /**
874  * ges_track_object_get_gnlobject:
875  * @object: a #GESTrackObject
876  *
877  * Get the GNonLin object this object is controlling.
878  *
879  * Returns: (transfer none): the GNonLin object this object is controlling.
880  */
881 GstElement *
882 ges_track_object_get_gnlobject (GESTrackObject * object)
883 {
884   return object->priv->gnlobject;
885 }
886
887 /**
888  * ges_track_object_get_element:
889  * @object: a #GESTrackObject
890  *
891  * Get the #GstElement this track object is controlling within GNonLin.
892  *
893  * Returns: (transfer none): the #GstElement this track object is controlling
894  * within GNonLin.
895  */
896 GstElement *
897 ges_track_object_get_element (GESTrackObject * object)
898 {
899   return object->priv->element;
900 }
901
902 static inline void
903 ges_track_object_set_locked_internal (GESTrackObject * object, gboolean locked)
904 {
905   object->priv->locked = locked;
906 }
907
908 /**
909  * ges_track_object_set_locked:
910  * @object: a #GESTrackObject
911  * @locked: whether the object is lock to its parent
912  *
913  * Set the locking status of the @object in relationship to its controlling
914  * #GESTimelineObject. If @locked is %TRUE, then this object will move synchronously
915  * with its controlling #GESTimelineObject.
916  */
917 void
918 ges_track_object_set_locked (GESTrackObject * object, gboolean locked)
919 {
920   g_return_if_fail (GES_IS_TRACK_OBJECT (object));
921
922   GST_DEBUG_OBJECT (object, "%s object", locked ? "Locking" : "Unlocking");
923
924   ges_track_object_set_locked_internal (object, locked);
925 #if GLIB_CHECK_VERSION(2,26,0)
926   g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_LOCKED]);
927 #else
928   g_object_notify (G_OBJECT (object), "locked");
929 #endif
930
931 }
932
933 /**
934  * ges_track_object_is_locked:
935  * @object: a #GESTrackObject
936  *
937  * Let you know if object us locked or not (moving synchronously).
938  *
939  * Returns: %TRUE if the object is moving synchronously to its controlling
940  * #GESTimelineObject, else %FALSE.
941  */
942 gboolean
943 ges_track_object_is_locked (GESTrackObject * object)
944 {
945   return object->priv->locked;
946 }
947
948 /**
949  * ges_track_object_get_start:
950  * @object: a #GESTrackObject
951  *
952  * Get the position of the object in the container #GESTrack.
953  *
954  * Returns: the start position (in #GstClockTime)
955  *
956  * Since: 0.10.2
957  */
958 guint64
959 ges_track_object_get_start (GESTrackObject * object)
960 {
961   if (G_UNLIKELY (object->priv->gnlobject == NULL))
962     return object->priv->pending_start;
963   else
964     return object->start;
965 }
966
967 /**
968  * ges_track_object_get_inpoint:
969  * @object: a #GESTrackObject
970  *
971  * Get the offset within the contents of this #GESTrackObject
972  *
973  * Returns: the in-point (in #GstClockTime)
974  *
975  * Since: 0.10.2
976  */
977 guint64
978 ges_track_object_get_inpoint (GESTrackObject * object)
979 {
980   if (G_UNLIKELY (object->priv->gnlobject == NULL))
981     return object->priv->pending_inpoint;
982   else
983     return object->inpoint;
984 }
985
986 /**
987  * ges_track_object_get_duration:
988  * @object: a #GESTrackObject
989  *
990  * Get the duration which will be used in the container #GESTrack
991  * starting from the 'in-point'
992  *
993  * Returns: the duration (in #GstClockTime)
994  *
995  * Since: 0.10.2
996  */
997 guint64
998 ges_track_object_get_duration (GESTrackObject * object)
999 {
1000   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1001     return object->priv->pending_duration;
1002   else
1003     return object->duration;
1004 }
1005
1006 /**
1007  * ges_track_object_get_priority:
1008  * @object: a #GESTrackObject
1009  *
1010  * Get the priority of the object withing the containing #GESTrack.
1011  *
1012  * Returns: the priority of @object
1013  *
1014  * Since: 0.10.2
1015  */
1016 guint32
1017 ges_track_object_get_priority (GESTrackObject * object)
1018 {
1019   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1020     return object->priv->pending_priority;
1021   else
1022     return object->priority;
1023 }
1024
1025 /**
1026  * ges_track_object_is_active:
1027  * @object: a #GESTrackObject
1028  *
1029  * Lets you know if @object will be used for playback and rendering,
1030  * or not.
1031  *
1032  * Returns: %TRUE if @object is active, %FALSE otherwize
1033  *
1034  * Since: 0.10.2
1035  */
1036 gboolean
1037 ges_track_object_is_active (GESTrackObject * object)
1038 {
1039   if (G_UNLIKELY (object->priv->gnlobject == NULL))
1040     return object->priv->pending_active;
1041   else
1042     return object->active;
1043 }
1044
1045 /**
1046  * ges_track_object_lookup_child:
1047  * @object: object to lookup the property in
1048  * @prop_name: name of the property to look up. You can specify the name of the
1049  *     class as such: "ClassName::property-name", to guarantee that you get the
1050  *     proper GParamSpec in case various GstElement-s contain the same property
1051  *     name. If you don't do so, you will get the first element found, having
1052  *     this property and the and the corresponding GParamSpec.
1053  * @element: (out) (allow-none) (transfer full): pointer to a #GstElement that
1054  *     takes the real object to set property on
1055  * @pspec: (out) (allow-none) (transfer full): pointer to take the #GParamSpec
1056  *     describing the property
1057  *
1058  * Looks up which @element and @pspec would be effected by the given @name. If various
1059  * contained elements have this property name you will get the first one, unless you
1060  * specify the class name in @name.
1061  *
1062  * Returns: TRUE if @element and @pspec could be found. FALSE otherwise. In that
1063  * case the values for @pspec and @element are not modified. Unref @element after
1064  * usage.
1065  *
1066  * Since: 0.10.2
1067  */
1068 gboolean
1069 ges_track_object_lookup_child (GESTrackObject * object, const gchar * prop_name,
1070     GstElement ** element, GParamSpec ** pspec)
1071 {
1072   GHashTableIter iter;
1073   gpointer key, value;
1074   gchar **names, *name, *classename;
1075   gboolean res;
1076   GESTrackObjectPrivate *priv = object->priv;
1077
1078   classename = NULL;
1079   res = FALSE;
1080
1081   names = g_strsplit (prop_name, "::", 2);
1082   if (names[1] != NULL) {
1083     classename = names[0];
1084     name = names[1];
1085   } else
1086     name = names[0];
1087
1088   g_hash_table_iter_init (&iter, priv->properties_hashtable);
1089   while (g_hash_table_iter_next (&iter, &key, &value)) {
1090     if (g_strcmp0 (G_PARAM_SPEC (key)->name, name) == 0) {
1091       if (classename == NULL ||
1092           g_strcmp0 (G_OBJECT_TYPE_NAME (G_OBJECT (value)), classename) == 0) {
1093         GST_DEBUG ("The %s property from %s has been found", name, classename);
1094         if (element)
1095           *element = g_object_ref (value);
1096
1097         *pspec = g_param_spec_ref (key);
1098         res = TRUE;
1099         break;
1100       }
1101     }
1102   }
1103   g_strfreev (names);
1104
1105   return res;
1106 }
1107
1108 /**
1109  * ges_track_object_set_child_property_by_pspec:
1110  * @object: a #GESTrackObject
1111  * @pspec: The #GParamSpec that specifies the property you want to set
1112  * @value: the value
1113  *
1114  * Sets a property of a child of @object.
1115  *
1116  * Since: 0.10.2
1117  */
1118 void
1119 ges_track_object_set_child_property_by_pspec (GESTrackObject * object,
1120     GParamSpec * pspec, GValue * value)
1121 {
1122   GstElement *element;
1123
1124   GESTrackObjectPrivate *priv = object->priv;
1125
1126   if (!priv->properties_hashtable)
1127     goto prop_hash_not_set;
1128
1129   element = g_hash_table_lookup (priv->properties_hashtable, pspec);
1130   if (!element)
1131     goto not_found;
1132
1133   g_object_set_property (G_OBJECT (element), pspec->name, value);
1134
1135   return;
1136
1137 not_found:
1138   {
1139     GST_ERROR ("The %s property doesn't exist", pspec->name);
1140     return;
1141   }
1142 prop_hash_not_set:
1143   {
1144     GST_DEBUG ("The child properties haven't been set on %p", object);
1145     return;
1146   }
1147 }
1148
1149 /**
1150  * ges_track_object_set_child_property_valist:
1151  * @object: The #GESTrackObject parent object
1152  * @first_property_name: The name of the first property to set
1153  * @var_args: value for the first property, followed optionally by more
1154  * name/return location pairs, followed by NULL
1155  *
1156  * Sets a property of a child of @object. If there are various child elements
1157  * that have the same property name, you can distinguish them using the following
1158  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1159  * corresponding property of the first element found will be set.
1160  *
1161  * Since: 0.10.2
1162  */
1163 void
1164 ges_track_object_set_child_property_valist (GESTrackObject * object,
1165     const gchar * first_property_name, va_list var_args)
1166 {
1167   const gchar *name;
1168   GParamSpec *pspec;
1169   GstElement *element;
1170
1171   gchar *error = NULL;
1172   GValue value = { 0, };
1173
1174   g_return_if_fail (G_IS_OBJECT (object));
1175
1176   name = first_property_name;
1177
1178   /* Note: This part is in big part copied from the gst_child_object_set_valist
1179    * method. */
1180
1181   /* iterate over pairs */
1182   while (name) {
1183     if (!ges_track_object_lookup_child (object, name, &element, &pspec))
1184       goto not_found;
1185
1186 #if GLIB_CHECK_VERSION(2,23,3)
1187     G_VALUE_COLLECT_INIT (&value, pspec->value_type, var_args,
1188         G_VALUE_NOCOPY_CONTENTS, &error);
1189 #else
1190     g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec));
1191     G_VALUE_COLLECT (&value, var_args, G_VALUE_NOCOPY_CONTENTS, &error);
1192 #endif
1193
1194     if (error)
1195       goto cant_copy;
1196
1197     g_object_set_property (G_OBJECT (element), pspec->name, &value);
1198
1199     g_object_unref (element);
1200     g_value_unset (&value);
1201
1202     name = va_arg (var_args, gchar *);
1203   }
1204   return;
1205
1206 not_found:
1207   {
1208     GST_WARNING ("No property %s in OBJECT\n", name);
1209     return;
1210   }
1211 cant_copy:
1212   {
1213     GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
1214         error);
1215     g_value_unset (&value);
1216     return;
1217   }
1218 }
1219
1220 /**
1221  * ges_track_object_set_child_property:
1222  * @object: The #GESTrackObject parent object
1223  * @first_property_name: The name of the first property to set
1224  * @...: value for the first property, followed optionally by more
1225  * name/return location pairs, followed by NULL
1226  *
1227  * Sets a property of a child of @object. If there are various child elements
1228  * that have the same property name, you can distinguish them using the following
1229  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1230  * corresponding property of the first element found will be set.
1231  *
1232  * Since: 0.10.2
1233  */
1234 void
1235 ges_track_object_set_child_property (GESTrackObject * object,
1236     const gchar * first_property_name, ...)
1237 {
1238   va_list var_args;
1239
1240   va_start (var_args, first_property_name);
1241   ges_track_object_set_child_property_valist (object, first_property_name,
1242       var_args);
1243   va_end (var_args);
1244 }
1245
1246 /**
1247  * ges_track_object_get_child_property_valist:
1248  * @object: The #GESTrackObject parent object
1249  * @first_property_name: The name of the first property to get
1250  * @var_args: value for the first property, followed optionally by more
1251  * name/return location pairs, followed by NULL
1252  *
1253  * Gets a property of a child of @object. If there are various child elements
1254  * that have the same property name, you can distinguish them using the following
1255  * syntax: 'ClasseName::property_name' as property name. If you don't, the
1256  * corresponding property of the first element found will be set.
1257  *
1258  * Since: 0.10.2
1259  */
1260 void
1261 ges_track_object_get_child_property_valist (GESTrackObject * object,
1262     const gchar * first_property_name, va_list var_args)
1263 {
1264   const gchar *name;
1265   gchar *error = NULL;
1266   GValue value = { 0, };
1267   GParamSpec *pspec;
1268   GstElement *element;
1269
1270   g_return_if_fail (G_IS_OBJECT (object));
1271
1272   name = first_property_name;
1273
1274   /* This part is in big part copied from the gst_child_object_get_valist method */
1275   while (name) {
1276     if (!ges_track_object_lookup_child (object, name, &element, &pspec))
1277       goto not_found;
1278
1279     g_value_init (&value, pspec->value_type);
1280     g_object_get_property (G_OBJECT (element), pspec->name, &value);
1281     g_object_unref (element);
1282
1283     G_VALUE_LCOPY (&value, var_args, 0, &error);
1284     if (error)
1285       goto cant_copy;
1286     g_value_unset (&value);
1287     name = va_arg (var_args, gchar *);
1288   }
1289   return;
1290
1291 not_found:
1292   {
1293     GST_WARNING ("no property %s in object", name);
1294     return;
1295   }
1296 cant_copy:
1297   {
1298     GST_WARNING ("error copying value %s in object %p: %s", pspec->name, object,
1299         error);
1300     g_value_unset (&value);
1301     return;
1302   }
1303 }
1304
1305 /**
1306  * ges_track_object_list_children_properties:
1307  * @object: The #GESTrackObject to get the list of children properties from
1308  * @n_properties: return location for the length of the returned array
1309  *
1310  * Gets an array of #GParamSpec* for all configurable properties of the
1311  * children of @object.
1312  *
1313  * Returns: (transfer full) (array): an array of #GParamSpec* which should be freed after use or
1314  * %NULL if something went wrong
1315  *
1316  * Since: 0.10.2
1317  */
1318 GParamSpec **
1319 ges_track_object_list_children_properties (GESTrackObject * object,
1320     guint * n_properties)
1321 {
1322   GESTrackObjectClass *class;
1323
1324   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
1325
1326   class = GES_TRACK_OBJECT_GET_CLASS (object);
1327
1328   return class->list_children_properties (object, n_properties);
1329 }
1330
1331 /**
1332  * ges_track_object_get_child_property:
1333  * @object: The origin #GESTrackObject
1334  * @first_property_name: The name of the first property to get
1335  * @...: return location for the first property, followed optionally by more
1336  * name/return location pairs, followed by NULL
1337  *
1338  * Gets properties of a child of @object.
1339  *
1340  * Since: 0.10.2
1341  */
1342 void
1343 ges_track_object_get_child_property (GESTrackObject * object,
1344     const gchar * first_property_name, ...)
1345 {
1346   va_list var_args;
1347
1348   va_start (var_args, first_property_name);
1349   ges_track_object_get_child_property_valist (object, first_property_name,
1350       var_args);
1351   va_end (var_args);
1352 }
1353
1354 /**
1355  * ges_track_object_get_child_property_by_pspec:
1356  * @object: a #GESTrackObject
1357  * @pspec: The #GParamSpec that specifies the property you want to get
1358  * @value: return location for the value
1359  *
1360  * Gets a property of a child of @object.
1361  *
1362  * Since: 0.10.2
1363  */
1364 void
1365 ges_track_object_get_child_property_by_pspec (GESTrackObject * object,
1366     GParamSpec * pspec, GValue * value)
1367 {
1368   GstElement *element;
1369
1370   GESTrackObjectPrivate *priv = object->priv;
1371
1372   if (!priv->properties_hashtable)
1373     goto prop_hash_not_set;
1374
1375   element = g_hash_table_lookup (priv->properties_hashtable, pspec);
1376   if (!element)
1377     goto not_found;
1378
1379   g_object_get_property (G_OBJECT (element), pspec->name, value);
1380
1381   return;
1382
1383 not_found:
1384   {
1385     GST_ERROR ("The %s property doesn't exist", pspec->name);
1386     return;
1387   }
1388 prop_hash_not_set:
1389   {
1390     GST_ERROR ("The child properties haven't been set on %p", object);
1391     return;
1392   }
1393 }
1394
1395 static GParamSpec **
1396 default_list_children_properties (GESTrackObject * object, guint * n_properties)
1397 {
1398   GParamSpec **pspec, *spec;
1399   GHashTableIter iter;
1400   gpointer key, value;
1401
1402   guint i = 0;
1403
1404   if (!object->priv->properties_hashtable)
1405     goto prop_hash_not_set;
1406
1407   *n_properties = g_hash_table_size (object->priv->properties_hashtable);
1408   pspec = g_new (GParamSpec *, *n_properties);
1409
1410   g_hash_table_iter_init (&iter, object->priv->properties_hashtable);
1411   while (g_hash_table_iter_next (&iter, &key, &value)) {
1412     spec = G_PARAM_SPEC (key);
1413     pspec[i] = g_param_spec_ref (spec);
1414     i++;
1415   }
1416
1417   return pspec;
1418
1419 prop_hash_not_set:
1420   {
1421     *n_properties = 0;
1422     GST_ERROR ("The child properties haven't been set on %p", object);
1423     return NULL;
1424   }
1425 }