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