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