GESTrackObject: re-factor property setting code
[platform/upstream/gstreamer.git] / ges / ges-track-object.c
1 /* GStreamer Editing Services
2  * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
3  *               2009 Nokia Corporation
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:ges-track-object
23  * @short_description: Base Class for objects contained in a #GESTrack
24  *
25  * #GESTrackObject is the Base Class for any object that can be contained in a
26  * #GESTrack.
27  *
28  * It contains the basic information as to the location of the object within
29  * its container, like the start position, the in-point, the duration and the
30  * priority.
31  */
32
33 #include "ges-internal.h"
34 #include "ges-track-object.h"
35 #include "ges-timeline-object.h"
36
37 static GQuark _start_quark;
38 static GQuark _inpoint_quark;
39 static GQuark _duration_quark;
40 static GQuark _priority_quark;
41
42 #define _do_init \
43 { \
44   _start_quark = g_quark_from_static_string ("start"); \
45   _inpoint_quark = g_quark_from_static_string ("inpoint"); \
46   _duration_quark = g_quark_from_static_string ("duration"); \
47   _priority_quark = g_quark_from_static_string ("priority"); \
48 }
49
50 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GESTrackObject, ges_track_object,
51     G_TYPE_OBJECT, _do_init);
52
53 struct _GESTrackObjectPrivate
54 {
55   /* cache the base priority and offset */
56   guint32 base_priority;
57   guint32 priority_offset;
58
59   /* These fields are only used before the gnlobject is available */
60   guint64 pending_start;
61   guint64 pending_inpoint;
62   guint64 pending_duration;
63   guint32 pending_gnl_priority;
64   gboolean pending_active;
65
66   GstElement *gnlobject;        /* The GnlObject */
67   GstElement *element;          /* The element contained in the gnlobject (can be NULL) */
68
69   GESTimelineObject *timelineobj;
70   GESTrack *track;
71
72   gboolean valid;
73
74   gboolean locked;              /* If TRUE, then moves in sync with its controlling
75                                  * GESTimelineObject */
76 };
77
78 enum
79 {
80   PROP_0,
81   PROP_START,
82   PROP_INPOINT,
83   PROP_DURATION,
84   PROP_PRIORITY,
85   PROP_PRIORITY_OFFSET,
86   PROP_ACTIVE,
87   PROP_LAST
88 };
89
90 static GParamSpec *properties[PROP_LAST];
91
92 static GstElement *ges_track_object_create_gnl_object_func (GESTrackObject *
93     object);
94
95 void gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg
96     G_GNUC_UNUSED, GESTrackObject * obj);
97
98 void gnlobject_media_start_cb (GstElement * gnlobject, GParamSpec * arg
99     G_GNUC_UNUSED, GESTrackObject * obj);
100
101 void gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg
102     G_GNUC_UNUSED, GESTrackObject * obj);
103
104 void gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg
105     G_GNUC_UNUSED, GESTrackObject * obj);
106
107 void gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg
108     G_GNUC_UNUSED, GESTrackObject * obj);
109
110 static inline gboolean
111 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start);
112 static inline gboolean
113 ges_track_object_set_inpoint_internal (GESTrackObject * object,
114     guint64 inpoint);
115 static inline gboolean ges_track_object_set_duration_internal (GESTrackObject *
116     object, guint64 duration);
117 static inline gboolean ges_track_object_set_priority_internal (GESTrackObject *
118     object, guint32 priority);
119 static inline gboolean
120 ges_track_object_set_priority_offset_internal (GESTrackObject * object,
121     guint32 priority);
122
123 static gboolean ges_track_object_update_priority (GESTrackObject * object);
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, tobj->start);
134       break;
135     case PROP_INPOINT:
136       g_value_set_uint64 (value, tobj->inpoint);
137       break;
138     case PROP_DURATION:
139       g_value_set_uint64 (value, tobj->duration);
140       break;
141     case PROP_PRIORITY:
142       g_value_set_uint (value, tobj->priv->base_priority);
143       break;
144     case PROP_PRIORITY_OFFSET:
145       g_value_set_uint (value, tobj->priv->priority_offset);
146       break;
147     case PROP_ACTIVE:
148       g_value_set_boolean (value, tobj->active);
149       break;
150     default:
151       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
152   }
153 }
154
155 static void
156 ges_track_object_set_property (GObject * object, guint property_id,
157     const GValue * value, GParamSpec * pspec)
158 {
159   GESTrackObject *tobj = GES_TRACK_OBJECT (object);
160
161   switch (property_id) {
162     case PROP_START:
163       ges_track_object_set_start_internal (tobj, g_value_get_uint64 (value));
164       break;
165     case PROP_INPOINT:
166       ges_track_object_set_inpoint_internal (tobj, g_value_get_uint64 (value));
167       break;
168     case PROP_DURATION:
169       ges_track_object_set_duration_internal (tobj, g_value_get_uint64 (value));
170       break;
171     case PROP_PRIORITY:
172       ges_track_object_set_priority_internal (tobj, g_value_get_uint (value));
173       break;
174     case PROP_PRIORITY_OFFSET:
175       ges_track_object_set_priority_offset_internal (tobj, g_value_get_uint
176           (value));
177       break;
178     case PROP_ACTIVE:
179       ges_track_object_set_active (tobj, g_value_get_boolean (value));
180       break;
181     default:
182       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
183   }
184 }
185
186 static void
187 ges_track_object_dispose (GObject * object)
188 {
189   G_OBJECT_CLASS (ges_track_object_parent_class)->dispose (object);
190 }
191
192 static void
193 ges_track_object_finalize (GObject * object)
194 {
195   G_OBJECT_CLASS (ges_track_object_parent_class)->finalize (object);
196 }
197
198 static void
199 ges_track_object_class_init (GESTrackObjectClass * klass)
200 {
201   GObjectClass *object_class = G_OBJECT_CLASS (klass);
202
203   g_type_class_add_private (klass, sizeof (GESTrackObjectPrivate));
204
205   object_class->get_property = ges_track_object_get_property;
206   object_class->set_property = ges_track_object_set_property;
207   object_class->dispose = ges_track_object_dispose;
208   object_class->finalize = ges_track_object_finalize;
209
210   /**
211    * GESTrackObject:start
212    *
213    * The position of the object in the container #GESTrack (in nanoseconds).
214    */
215   properties[PROP_START] = g_param_spec_uint64 ("start", "Start",
216       "The position in the container", 0, G_MAXUINT64, 0, G_PARAM_READWRITE);
217   g_object_class_install_property (object_class, PROP_START,
218       properties[PROP_START]);
219
220   /**
221    * GESTrackObject:in-point
222    *
223    * The in-point at which this #GESTrackObject will start outputting data
224    * from its contents (in nanoseconds).
225    *
226    * Ex : an in-point of 5 seconds means that the first outputted buffer will
227    * be the one located 5 seconds in the controlled resource.
228    */
229   properties[PROP_INPOINT] =
230       g_param_spec_uint64 ("in-point", "In-point", "The in-point", 0,
231       G_MAXUINT64, 0, G_PARAM_READWRITE);
232   g_object_class_install_property (object_class, PROP_INPOINT,
233       properties[PROP_INPOINT]);
234
235   /**
236    * GESTrackObject:duration
237    *
238    * The duration (in nanoseconds) which will be used in the container #GESTrack
239    * starting from 'in-point'.
240    *
241    */
242   properties[PROP_DURATION] =
243       g_param_spec_uint64 ("duration", "Duration", "The duration to use", 0,
244       G_MAXUINT64, GST_SECOND, G_PARAM_READWRITE);
245   g_object_class_install_property (object_class, PROP_DURATION,
246       properties[PROP_DURATION]);
247
248   /**
249    * GESTrackObject:priority
250    *
251    * The priority of the object within the containing #GESTrack.
252    * If two objects intersect over the same region of time, the @priority
253    * property is used to decide which one takes precedence.
254    *
255    * The highest priority (that supercedes everything) is 0, and then lowering
256    * priorities go in increasing numerical value (with #G_MAXUINT64 being the
257    * lowest priority).
258    */
259   properties[PROP_PRIORITY] = g_param_spec_uint ("priority", "Priority",
260       "The priority of the object", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
261   g_object_class_install_property (object_class, PROP_PRIORITY,
262       properties[PROP_PRIORITY]);
263
264   /**
265    * GESTrackObject:priority-offset
266    *
267    * The priority of the object relative to its parent #GESTimelineObject.
268    */
269   properties[PROP_PRIORITY_OFFSET] =
270       g_param_spec_uint ("priority-offset", "Priority Offset",
271       "An offset from the base priority", 0, G_MAXUINT, 0, G_PARAM_READWRITE);
272   g_object_class_install_property (object_class, PROP_PRIORITY_OFFSET,
273       properties[PROP_PRIORITY_OFFSET]);
274
275   /**
276    * GESTrackObject:active
277    *
278    * Whether the object should be taken into account in the #GESTrack output.
279    * If #FALSE, then its contents will not be used in the resulting track.
280    */
281   properties[PROP_ACTIVE] =
282       g_param_spec_boolean ("active", "Active", "Use object in output", TRUE,
283       G_PARAM_READWRITE);
284   g_object_class_install_property (object_class, PROP_ACTIVE,
285       properties[PROP_ACTIVE]);
286
287   klass->create_gnl_object = ges_track_object_create_gnl_object_func;
288 }
289
290 static void
291 ges_track_object_init (GESTrackObject * self)
292 {
293   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
294       GES_TYPE_TRACK_OBJECT, GESTrackObjectPrivate);
295
296   /* Sane default values */
297   self->priv->pending_start = 0;
298   self->priv->pending_inpoint = 0;
299   self->priv->pending_duration = GST_SECOND;
300   self->priv->pending_gnl_priority = 1;
301   self->priv->pending_active = TRUE;
302   self->priv->locked = TRUE;
303 }
304
305 static inline gboolean
306 ges_track_object_set_start_internal (GESTrackObject * object, guint64 start)
307 {
308   GST_DEBUG ("object:%p, start:%" GST_TIME_FORMAT,
309       object, GST_TIME_ARGS (start));
310
311   if (object->priv->gnlobject != NULL) {
312     if (G_UNLIKELY (start == object->start))
313       return FALSE;
314
315     g_object_set (object->priv->gnlobject, "start", start, NULL);
316   } else
317     object->priv->pending_start = start;
318   return TRUE;
319 };
320
321 /**
322  * ges_track_object_set_start:
323  * @object: a #GESTrackObject
324  * @start: the start position (in #GstClockTime)
325  *
326  * Sets the position of the object in the container #GESTrack.
327  */
328 void
329 ges_track_object_set_start (GESTrackObject * object, guint64 start)
330 {
331   if (ges_track_object_set_start_internal (object, start))
332     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_START]);
333 }
334
335 static inline gboolean
336 ges_track_object_set_inpoint_internal (GESTrackObject * object, guint64 inpoint)
337 {
338
339   GST_DEBUG ("object:%p, inpoint:%" GST_TIME_FORMAT,
340       object, GST_TIME_ARGS (inpoint));
341
342   if (object->priv->gnlobject != NULL) {
343     if (G_UNLIKELY (inpoint == object->inpoint))
344       return FALSE;
345
346     g_object_set (object->priv->gnlobject, "media-start", inpoint, NULL);
347   } else
348     object->priv->pending_inpoint = inpoint;
349
350   return TRUE;
351 }
352
353 /**
354  * ges_track_object_set_inpoint:
355  * @object: a #GESTrackObject
356  * @inpoint: the in-point (in #GstClockTime)
357  *
358  * Set the offset within the contents of this #GESTrackObject
359  */
360 void
361 ges_track_object_set_inpoint (GESTrackObject * object, guint64 inpoint)
362 {
363   if (ges_track_object_set_inpoint_internal (object, inpoint))
364     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_INPOINT]);
365 }
366
367 static inline gboolean
368 ges_track_object_set_duration_internal (GESTrackObject * object,
369     guint64 duration)
370 {
371   GST_DEBUG ("object:%p, duration:%" GST_TIME_FORMAT,
372       object, GST_TIME_ARGS (duration));
373
374   if (object->priv->gnlobject != NULL) {
375     if (G_UNLIKELY (duration == object->duration))
376       return FALSE;
377
378     g_object_set (object->priv->gnlobject, "duration", duration,
379         "media-duration", duration, NULL);
380   } else
381     object->priv->pending_duration = duration;
382   return TRUE;
383 }
384
385 /**
386  * ges_track_object_set_duration:
387  * @object: a #GESTrackObject
388  * @duration: the duration (in #GstClockTime)
389  *
390  * Set the duration which will be used in the container #GESTrack
391  * starting from the 'in-point'
392  */
393 void
394 ges_track_object_set_duration (GESTrackObject * object, guint64 duration)
395 {
396   if (ges_track_object_set_duration_internal (object, duration))
397     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_DURATION]);
398 }
399
400
401 /* NOTE: we handle priority differently than other properties! the gnlpriority
402  * is object->base_priority + object->priority_offset! A change to either one
403  * will trigger an update to the gnonlin priority and a subsequent property
404  * notification.
405  */
406
407 static inline gboolean
408 ges_track_object_set_priority_internal (GESTrackObject * object,
409     guint32 priority)
410 {
411   guint32 save;
412
413   save = object->priv->base_priority;
414   GST_DEBUG ("object:%p, priority:%d", object, priority);
415
416   object->priv->base_priority = priority;
417   if (!ges_track_object_update_priority (object)) {
418     object->priv->base_priority = save;
419     return FALSE;
420   }
421   return TRUE;
422 }
423
424 /**
425  * ges_track_object_set_priority:
426  * @object: a #GESTrackObject
427  * @priority: the priority
428  *
429  * Sets the priority of the object withing the containing #GESTrack.
430  * If two objects intersect over the same region of time, the priority
431  * property is used to decide which one takes precedence.
432  *
433  * The highest priority (that supercedes everything) is 0, and then
434  * lowering priorities go in increasing numerical value (with G_MAXUINT32
435  * being the lowest priority).
436  */
437 void
438 ges_track_object_set_priority (GESTrackObject * object, guint32 priority)
439 {
440   if (ges_track_object_set_priority_internal (object, priority))
441     g_object_notify_by_pspec (G_OBJECT (object), properties[PROP_PRIORITY]);
442 }
443
444 gboolean
445 ges_track_object_set_priority_offset_internal (GESTrackObject * object,
446     guint32 priority_offset)
447 {
448   guint32 save;
449   save = object->priv->priority_offset;
450   GST_DEBUG ("object:%p, offset:%d", object, priority_offset);
451
452   object->priv->priority_offset = priority_offset;
453   if (!ges_track_object_update_priority (object)) {
454     object->priv->base_priority = save;
455     return FALSE;
456   }
457   return TRUE;
458 }
459
460 static gboolean
461 ges_track_object_update_priority (GESTrackObject * object)
462 {
463   guint32 priority, offset, gnl;
464
465   priority = object->priv->base_priority;
466   offset = object->priv->priority_offset;
467   gnl = priority + offset;
468   GST_DEBUG ("object:%p, base:%d, offset:%d: gnl:%d", object, priority, offset,
469       gnl);
470
471   if (object->priv->gnlobject != NULL) {
472     if (G_UNLIKELY (gnl == object->gnl_priority))
473       return FALSE;
474
475     g_object_set (object->priv->gnlobject, "priority", gnl, NULL);
476   } else
477     object->priv->pending_gnl_priority = gnl;
478   return TRUE;
479 }
480
481 /**
482  * ges_track_object_set_active:
483  * @object: a #GESTrackObject
484  * @active: visibility
485  *
486  * Sets the usage of the @object. If @active is %TRUE, the object will be used for
487  * playback and rendering, else it will be ignored.
488  *
489  * Returns: %TRUE if the property was toggled, else %FALSE
490  */
491 gboolean
492 ges_track_object_set_active (GESTrackObject * object, gboolean active)
493 {
494   GST_DEBUG ("object:%p, active:%d", object, active);
495
496   if (object->priv->gnlobject != NULL) {
497     if (G_UNLIKELY (active == object->active))
498       return FALSE;
499
500     g_object_set (object->priv->gnlobject, "active", active, NULL);
501   } else
502     object->priv->pending_active = active;
503   return TRUE;
504 }
505
506 /* Callbacks from the GNonLin object */
507 void
508 gnlobject_start_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
509     GESTrackObject * obj)
510 {
511   guint64 start;
512   GESTrackObjectClass *klass;
513
514   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
515
516   g_object_get (gnlobject, "start", &start, NULL);
517
518   GST_DEBUG ("gnlobject start : %" GST_TIME_FORMAT " current : %"
519       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->start));
520
521   if (start != obj->start) {
522     obj->start = start;
523     if (klass->start_changed)
524       klass->start_changed (obj, start);
525     /* FIXME : emit changed */
526   }
527 }
528
529 /* Callbacks from the GNonLin object */
530 void
531 gnlobject_media_start_cb (GstElement * gnlobject,
532     GParamSpec * arg G_GNUC_UNUSED, GESTrackObject * obj)
533 {
534   guint64 start;
535   GESTrackObjectClass *klass;
536
537   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
538
539   g_object_get (gnlobject, "media-start", &start, NULL);
540
541   GST_DEBUG ("gnlobject in-point : %" GST_TIME_FORMAT " current : %"
542       GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (obj->inpoint));
543
544   if (start != obj->inpoint) {
545     obj->inpoint = start;
546     if (klass->media_start_changed)
547       klass->media_start_changed (obj, start);
548     /* FIXME : emit changed */
549   }
550 }
551
552 void
553 gnlobject_priority_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
554     GESTrackObject * obj)
555 {
556   guint32 priority;
557   GESTrackObjectClass *klass;
558
559   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
560
561   g_object_get (gnlobject, "priority", &priority, NULL);
562
563   GST_DEBUG ("gnlobject priority : %d current : %d", priority,
564       obj->gnl_priority);
565
566   if (priority != obj->gnl_priority) {
567     obj->gnl_priority = priority;
568     if (klass->gnl_priority_changed)
569       klass->gnl_priority_changed (obj, priority);
570     /* FIXME : emit changed */
571   }
572 }
573
574 void
575 gnlobject_duration_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
576     GESTrackObject * obj)
577 {
578   guint64 duration;
579   GESTrackObjectClass *klass;
580
581   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
582
583   g_object_get (gnlobject, "duration", &duration, NULL);
584
585   GST_DEBUG ("gnlobject duration : %" GST_TIME_FORMAT " current : %"
586       GST_TIME_FORMAT, GST_TIME_ARGS (duration), GST_TIME_ARGS (obj->duration));
587
588   if (duration != obj->duration) {
589     obj->duration = duration;
590     if (klass->duration_changed)
591       klass->duration_changed (obj, duration);
592     /* FIXME : emit changed */
593   }
594 }
595
596 void
597 gnlobject_active_cb (GstElement * gnlobject, GParamSpec * arg G_GNUC_UNUSED,
598     GESTrackObject * obj)
599 {
600   gboolean active;
601   GESTrackObjectClass *klass;
602
603   klass = GES_TRACK_OBJECT_GET_CLASS (obj);
604
605   g_object_get (gnlobject, "active", &active, NULL);
606
607   GST_DEBUG ("gnlobject active : %d current : %d", active, obj->active);
608
609   if (active != obj->active) {
610     obj->active = active;
611     if (klass->active_changed)
612       klass->active_changed (obj, active);
613     /* FIXME : emit changed */
614   }
615 }
616
617
618 /* default 'create_gnl_object' virtual method implementation */
619 static GstElement *
620 ges_track_object_create_gnl_object_func (GESTrackObject * self)
621 {
622   GESTrackObjectClass *klass = NULL;
623   GstElement *child = NULL;
624   GstElement *gnlobject;
625
626   klass = GES_TRACK_OBJECT_GET_CLASS (self);
627
628   if (G_UNLIKELY (self->priv->gnlobject != NULL))
629     goto already_have_gnlobject;
630
631   if (G_UNLIKELY (klass->gnlobject_factorytype == NULL))
632     goto no_gnlfactory;
633
634   GST_DEBUG ("Creating a supporting gnlobject of type '%s'",
635       klass->gnlobject_factorytype);
636
637   gnlobject = gst_element_factory_make (klass->gnlobject_factorytype, NULL);
638
639   if (G_UNLIKELY (gnlobject == NULL))
640     goto no_gnlobject;
641
642   if (klass->create_element) {
643     GST_DEBUG ("Calling subclass 'create_element' vmethod");
644     child = klass->create_element (self);
645
646     if (G_UNLIKELY (!child))
647       goto child_failure;
648
649     if (!gst_bin_add (GST_BIN (gnlobject), child))
650       goto add_failure;
651
652     GST_DEBUG ("Succesfully got the element to put in the gnlobject");
653     self->priv->element = child;
654   }
655
656   GST_DEBUG ("done");
657   return gnlobject;
658
659
660   /* ERROR CASES */
661
662 already_have_gnlobject:
663   {
664     GST_ERROR ("Already controlling a GnlObject %s",
665         GST_ELEMENT_NAME (self->priv->gnlobject));
666     return NULL;
667   }
668
669 no_gnlfactory:
670   {
671     GST_ERROR ("No GESTrackObject::gnlobject_factorytype implementation!");
672     return NULL;
673   }
674
675 no_gnlobject:
676   {
677     GST_ERROR ("Error creating a gnlobject of type '%s'",
678         klass->gnlobject_factorytype);
679     return NULL;
680   }
681
682 child_failure:
683   {
684     GST_ERROR ("create_element returned NULL");
685     gst_object_unref (gnlobject);
686     return NULL;
687   }
688
689 add_failure:
690   {
691     GST_ERROR ("Error adding the contents to the gnlobject");
692     gst_object_unref (child);
693     gst_object_unref (gnlobject);
694     return NULL;
695   }
696 }
697
698 static gboolean
699 ensure_gnl_object (GESTrackObject * object)
700 {
701   GESTrackObjectClass *class;
702   GstElement *gnlobject;
703   gboolean res = TRUE;
704
705   if (object->priv->gnlobject && object->priv->valid)
706     return FALSE;
707
708   /* 1. Create the GnlObject */
709   GST_DEBUG ("Creating GnlObject");
710
711   class = GES_TRACK_OBJECT_GET_CLASS (object);
712
713   if (G_UNLIKELY (class->create_gnl_object == NULL)) {
714     GST_ERROR ("No 'create_gnl_object' implementation !");
715     return FALSE;
716   }
717
718   GST_DEBUG ("Calling virtual method");
719
720   /* call the create_gnl_object virtual method */
721   gnlobject = class->create_gnl_object (object);
722
723   if (G_UNLIKELY (gnlobject == NULL)) {
724     GST_ERROR
725         ("'create_gnl_object' implementation returned TRUE but no GnlObject is available");
726     return FALSE;
727   }
728
729   object->priv->gnlobject = gnlobject;
730
731   /* Connect to property notifications */
732   /* FIXME : remember the signalids so we can remove them later on !!! */
733   g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::start",
734       G_CALLBACK (gnlobject_start_cb), object);
735   g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::media-start",
736       G_CALLBACK (gnlobject_media_start_cb), object);
737   g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::duration",
738       G_CALLBACK (gnlobject_duration_cb), object);
739   g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::priority",
740       G_CALLBACK (gnlobject_priority_cb), object);
741   g_signal_connect (G_OBJECT (object->priv->gnlobject), "notify::active",
742       G_CALLBACK (gnlobject_active_cb), object);
743
744   /* 2. Fill in the GnlObject */
745   if (gnlobject) {
746     GST_DEBUG ("Got a valid GnlObject, now filling it in");
747
748     res =
749         ges_timeline_object_fill_track_object (object->priv->timelineobj,
750         object, object->priv->gnlobject);
751     if (res) {
752       /* Set some properties on the GnlObject */
753       g_object_set (object->priv->gnlobject,
754           "caps", ges_track_get_caps (object->priv->track),
755           "duration", object->priv->pending_duration,
756           "media-duration", object->priv->pending_duration,
757           "start", object->priv->pending_start,
758           "media-start", object->priv->pending_inpoint,
759           "priority", object->priv->pending_gnl_priority,
760           "active", object->priv->pending_active, NULL);
761
762     }
763   }
764
765   object->priv->valid = res;
766
767   GST_DEBUG ("Returning res:%d", res);
768
769   return res;
770 }
771
772 /* INTERNAL USAGE */
773 gboolean
774 ges_track_object_set_track (GESTrackObject * object, GESTrack * track)
775 {
776   GST_DEBUG ("object:%p, track:%p", object, track);
777
778   object->priv->track = track;
779
780   if (object->priv->track)
781     return ensure_gnl_object (object);
782
783   return TRUE;
784 }
785
786 /**
787  * ges_track_object_get_track:
788  * @object: a #GESTrackObject
789  *
790  * Returns: (transfer none): The #GESTrack to which this object belongs. Can be %NULL if it
791  * is not in any track
792  */
793 GESTrack *
794 ges_track_object_get_track (GESTrackObject * object)
795 {
796   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
797
798   return object->priv->track;
799 }
800
801
802 void
803 ges_track_object_set_timeline_object (GESTrackObject * object,
804     GESTimelineObject * tlobj)
805 {
806   GST_DEBUG ("object:%p, timeline-object:%p", object, tlobj);
807
808   object->priv->timelineobj = tlobj;
809 }
810
811 /**
812  * ges_track_object_get_timeline_object:
813  * @object: a #GESTrackObject
814  *
815  * Returns: (transfer none): the #GESTimelineObject which is controlling
816  * this track object
817  */
818 GESTimelineObject *
819 ges_track_object_get_timeline_object (GESTrackObject * object)
820 {
821   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), NULL);
822
823   return object->priv->timelineobj;
824 }
825
826 guint32
827 ges_track_object_get_priority_offset (GESTrackObject * object)
828 {
829   return object->priv->priority_offset;
830 }
831
832 /**
833  * ges_track_object_get_gnlobject:
834  * @object: a #GESTrackObject
835  *
836  * Returns: (transfer none): the GNonLin object this object is controlling.
837  */
838 GstElement *
839 ges_track_object_get_gnlobject (GESTrackObject * object)
840 {
841   return object->priv->gnlobject;
842 }
843
844 /**
845  * ges_track_object_get_element:
846  * @object: a #GESTrackObject
847  *
848  * Returns: (transfer none): the #GstElement this track object is controlling
849  * within GNonLin.
850  */
851 GstElement *
852 ges_track_object_get_element (GESTrackObject * object)
853 {
854   return object->priv->element;
855 }
856
857 /**
858  * ges_track_object_set_locked:
859  * @object: a #GESTrackObject
860  * @locked: whether the object is lock to its parent
861  *
862  * Set the locking status of the @object in relationship to its controlling
863  * #GESTimelineObject. If @locked is %TRUE, then this object will move synchronously
864  * with its controlling #GESTimelineObject.
865 */
866 void
867 ges_track_object_set_locked (GESTrackObject * object, gboolean locked)
868 {
869   object->priv->locked = locked;
870 }
871
872 /**
873  * ges_track_object_is_locked:
874  * @object: a #GESTrackObject
875  *
876  * Returns: %TRUE if the object is moving synchronously to its controlling
877  * #GESTimelineObject, else %FALSE.
878  */
879 gboolean
880 ges_track_object_is_locked (GESTrackObject * object)
881 {
882   return object->priv->locked;
883 }