Merge remote-tracking branch 'origin/0.10'
[platform/upstream/gstreamer.git] / ges / ges-track.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
23  * @short_description: Composition of objects
24  *
25  * Corresponds to one output format (i.e. audio OR video).
26  *
27  * Contains the compatible TrackObject(s).
28  *
29  * Wraps GNonLin's 'gnlcomposition' element.
30  */
31
32 #include "ges-internal.h"
33 #include "ges-track.h"
34 #include "ges-track-object.h"
35
36 G_DEFINE_TYPE (GESTrack, ges_track, GST_TYPE_BIN);
37
38 struct _GESTrackPrivate
39 {
40   /*< private > */
41   GESTimeline *timeline;
42   GList *trackobjects;
43   guint64 duration;
44
45   GstCaps *caps;
46
47   GstElement *composition;      /* The composition associated with this track */
48   GstElement *background;       /* The backgrond, handle the gaps in the track */
49   GstPad *srcpad;               /* The source GhostPad */
50
51   gboolean updating;
52 };
53
54 enum
55 {
56   ARG_0,
57   ARG_CAPS,
58   ARG_TYPE,
59   ARG_DURATION,
60   ARG_LAST,
61   TRACK_OBJECT_ADDED,
62   TRACK_OBJECT_REMOVED,
63   LAST_SIGNAL
64 };
65
66 static guint ges_track_signals[LAST_SIGNAL] = { 0 };
67
68 static GParamSpec *properties[ARG_LAST];
69
70 static void pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track);
71 static void
72 pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track);
73 static void composition_duration_cb (GstElement * composition, GParamSpec * arg
74     G_GNUC_UNUSED, GESTrack * obj);
75 static void
76 sort_track_objects_cb (GESTrackObject * child,
77     GParamSpec * arg G_GNUC_UNUSED, GESTrack * track);
78
79 static void timeline_duration_cb (GESTimeline * timeline,
80     GParamSpec * arg G_GNUC_UNUSED, GESTrack * track);
81
82 static void
83 ges_track_get_property (GObject * object, guint property_id,
84     GValue * value, GParamSpec * pspec)
85 {
86   GESTrack *track = GES_TRACK (object);
87
88   switch (property_id) {
89     case ARG_CAPS:
90       gst_value_set_caps (value, track->priv->caps);
91       break;
92     case ARG_TYPE:
93       g_value_set_flags (value, track->type);
94       break;
95     case ARG_DURATION:
96       g_value_set_uint64 (value, track->priv->duration);
97       break;
98     default:
99       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
100   }
101 }
102
103 static void
104 ges_track_set_property (GObject * object, guint property_id,
105     const GValue * value, GParamSpec * pspec)
106 {
107   GESTrack *track = GES_TRACK (object);
108
109   switch (property_id) {
110     case ARG_CAPS:
111       ges_track_set_caps (track, gst_value_get_caps (value));
112       break;
113     case ARG_TYPE:
114       track->type = g_value_get_flags (value);
115       break;
116     default:
117       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
118   }
119 }
120
121 static void
122 ges_track_dispose (GObject * object)
123 {
124   GESTrack *track = (GESTrack *) object;
125   GESTrackPrivate *priv = track->priv;
126
127   while (priv->trackobjects) {
128     GESTrackObject *trobj = GES_TRACK_OBJECT (priv->trackobjects->data);
129     ges_track_remove_object (track, trobj);
130     ges_timeline_object_release_track_object ((GESTimelineObject *)
131         ges_track_object_get_timeline_object (trobj), trobj);
132   }
133
134   if (priv->composition) {
135     gst_bin_remove (GST_BIN (object), priv->composition);
136     priv->composition = NULL;
137   }
138
139   if (priv->caps) {
140     gst_caps_unref (priv->caps);
141     priv->caps = NULL;
142   }
143
144   G_OBJECT_CLASS (ges_track_parent_class)->dispose (object);
145 }
146
147 static void
148 ges_track_constructed (GObject * object)
149 {
150   GObjectClass *parent_class;
151   GstElement *background = NULL;
152   GESTrack *self = GES_TRACK (object);
153   GESTrackPrivate *priv = self->priv;
154
155   if ((priv->background = gst_element_factory_make ("gnlsource", "background"))) {
156     g_object_set (priv->background, "priority", G_MAXUINT, NULL);
157
158     switch (self->type) {
159       case GES_TRACK_TYPE_VIDEO:
160         background = gst_element_factory_make ("videotestsrc", "background");
161         g_object_set (background, "pattern", 2, NULL);
162         break;
163       case GES_TRACK_TYPE_AUDIO:
164         background = gst_element_factory_make ("audiotestsrc", "background");
165         g_object_set (background, "wave", 4, NULL);
166         break;
167       default:
168         break;
169     }
170
171     if (background) {
172       if (!gst_bin_add (GST_BIN (priv->background), background))
173         GST_ERROR ("Couldn't add background");
174       else {
175         if (!gst_bin_add (GST_BIN (priv->composition), priv->background))
176           GST_ERROR ("Couldn't add background");
177       }
178
179     }
180   }
181
182   parent_class = ges_track_parent_class;
183   if (parent_class->constructed)
184     parent_class->constructed (object);
185
186   G_OBJECT_CLASS (parent_class)->constructed (object);
187 }
188
189 static void
190 ges_track_finalize (GObject * object)
191 {
192   G_OBJECT_CLASS (ges_track_parent_class)->finalize (object);
193 }
194
195 static void
196 ges_track_class_init (GESTrackClass * klass)
197 {
198   GObjectClass *object_class = G_OBJECT_CLASS (klass);
199
200   g_type_class_add_private (klass, sizeof (GESTrackPrivate));
201
202   object_class->get_property = ges_track_get_property;
203   object_class->set_property = ges_track_set_property;
204   object_class->dispose = ges_track_dispose;
205   object_class->finalize = ges_track_finalize;
206   object_class->constructed = ges_track_constructed;
207
208   /**
209    * GESTrack:caps
210    *
211    * Caps used to filter/choose the output stream. This is generally set to
212    * a generic set of caps like 'video/x-raw' for raw video.
213    *
214    * Default value: #GST_CAPS_ANY.
215    */
216   properties[ARG_CAPS] = g_param_spec_boxed ("caps", "Caps",
217       "Caps used to filter/choose the output stream",
218       GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
219   g_object_class_install_property (object_class, ARG_CAPS,
220       properties[ARG_CAPS]);
221
222   /**
223    * GESTrack:duration
224    *
225    * Current duration of the track
226    *
227    * Default value: O
228    */
229   properties[ARG_DURATION] = g_param_spec_uint64 ("duration", "Duration",
230       "The current duration of the track", 0, G_MAXUINT64, GST_SECOND,
231       G_PARAM_READABLE);
232   g_object_class_install_property (object_class, ARG_DURATION,
233       properties[ARG_DURATION]);
234
235   /**
236    * GESTrack:track-type
237    *
238    * Type of stream the track outputs. This is used when creating the #GESTrack
239    * to specify in generic terms what type of content will be outputted.
240    *
241    * It also serves as a 'fast' way to check what type of data will be outputted
242    * from the #GESTrack without having to actually check the #GESTrack's caps
243    * property.
244    */
245   properties[ARG_TYPE] = g_param_spec_flags ("track-type", "TrackType",
246       "Type of stream the track outputs",
247       GES_TYPE_TRACK_TYPE, GES_TRACK_TYPE_CUSTOM,
248       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
249   g_object_class_install_property (object_class, ARG_TYPE,
250       properties[ARG_TYPE]);
251
252   /**
253    * GESTrack::track-object-added
254    * @object: the #GESTrack
255    * @effect: the #GESTrackObject that was added.
256    *
257    * Will be emitted after a track object was added to the track.
258    *
259    * Since: 0.10.2
260    */
261   ges_track_signals[TRACK_OBJECT_ADDED] =
262       g_signal_new ("track-object-added", G_TYPE_FROM_CLASS (klass),
263       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
264       G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
265
266   /**
267    * GESTrack::track-object-removed
268    * @object: the #GESTrack
269    * @effect: the #GESTrackObject that was removed.
270    *
271    * Will be emitted after a track object was removed from the track.
272    *
273    * Since: 0.10.2
274    */
275   ges_track_signals[TRACK_OBJECT_REMOVED] =
276       g_signal_new ("track-object-removed", G_TYPE_FROM_CLASS (klass),
277       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
278       G_TYPE_NONE, 1, GES_TYPE_TRACK_OBJECT);
279 }
280
281 static void
282 ges_track_init (GESTrack * self)
283 {
284   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
285       GES_TYPE_TRACK, GESTrackPrivate);
286
287   self->priv->composition = gst_element_factory_make ("gnlcomposition", NULL);
288   self->priv->updating = TRUE;
289
290   g_signal_connect (G_OBJECT (self->priv->composition), "notify::duration",
291       G_CALLBACK (composition_duration_cb), self);
292   g_signal_connect (self->priv->composition, "pad-added",
293       (GCallback) pad_added_cb, self);
294   g_signal_connect (self->priv->composition, "pad-removed",
295       (GCallback) pad_removed_cb, self);
296
297   if (!gst_bin_add (GST_BIN (self), self->priv->composition))
298     GST_ERROR ("Couldn't add composition to bin !");
299 }
300
301 /**
302  * ges_track_new:
303  * @type: The type of track
304  * @caps: (transfer full): The caps to restrict the output of the track to.
305  *
306  * Creates a new #GESTrack with the given @type and @caps.
307  *
308  * The newly created track will steal a reference to the caps. If you wish to
309  * use those caps elsewhere, you will have to take an extra reference.
310  *
311  * Returns: A new #GESTrack.
312  */
313 GESTrack *
314 ges_track_new (GESTrackType type, GstCaps * caps)
315 {
316   GESTrack *track;
317
318   track = g_object_new (GES_TYPE_TRACK, "caps", caps, "track-type", type, NULL);
319   gst_caps_unref (caps);
320
321   return track;
322 }
323
324 /**
325  * ges_track_video_raw_new:
326  *
327  * Creates a new #GESTrack of type #GES_TRACK_TYPE_VIDEO and with generic
328  * raw video caps ("video/x-raw");
329  *
330  * Returns: A new #GESTrack.
331  */
332 GESTrack *
333 ges_track_video_raw_new (void)
334 {
335   GESTrack *track;
336   GstCaps *caps = gst_caps_new_empty_simple ("video/x-raw");
337
338   track = ges_track_new (GES_TRACK_TYPE_VIDEO, caps);
339
340   return track;
341 }
342
343 /**
344  * ges_track_audio_raw_new:
345  *
346  * Creates a new #GESTrack of type #GES_TRACK_TYPE_AUDIO and with generic
347  * raw audio caps ("audio/x-raw");
348  *
349  * Returns: A new #GESTrack.
350  */
351 GESTrack *
352 ges_track_audio_raw_new (void)
353 {
354   GESTrack *track;
355   GstCaps *caps = gst_caps_new_empty_simple ("audio/x-raw");
356
357   track = ges_track_new (GES_TRACK_TYPE_AUDIO, caps);
358
359   return track;
360 }
361
362 /**
363  * ges_track_set_timeline:
364  * @track: a #GESTrack
365  * @timeline: a #GESTimeline
366  *
367  * Sets @timeline as the timeline controlling @track.
368  */
369 void
370 ges_track_set_timeline (GESTrack * track, GESTimeline * timeline)
371 {
372   GST_DEBUG ("track:%p, timeline:%p", track, timeline);
373
374   if (track->priv->timeline)
375     g_signal_handlers_disconnect_by_func (track,
376         timeline_duration_cb, track->priv->timeline);
377
378   if (timeline)
379     g_signal_connect (G_OBJECT (timeline), "notify::duration",
380         G_CALLBACK (timeline_duration_cb), track);
381
382   track->priv->timeline = timeline;
383 }
384
385 /**
386  * ges_track_set_caps:
387  * @track: a #GESTrack
388  * @caps: the #GstCaps to set
389  *
390  * Sets the given @caps on the track.
391  */
392 void
393 ges_track_set_caps (GESTrack * track, const GstCaps * caps)
394 {
395   GESTrackPrivate *priv;
396
397   g_return_if_fail (GES_IS_TRACK (track));
398   g_return_if_fail (GST_IS_CAPS (caps));
399
400   GST_DEBUG ("track:%p, caps:%" GST_PTR_FORMAT, track, caps);
401
402   priv = track->priv;
403
404   if (priv->caps)
405     gst_caps_unref (priv->caps);
406   priv->caps = gst_caps_copy (caps);
407
408   g_object_set (priv->composition, "caps", caps, NULL);
409   /* FIXME : update all trackobjects ? */
410 }
411
412
413 /* FIXME : put the compare function in the utils */
414
415 static gint
416 objects_start_compare (GESTrackObject * a, GESTrackObject * b)
417 {
418   if (a->start == b->start) {
419     if (a->priority < b->priority)
420       return -1;
421     if (a->priority > b->priority)
422       return 1;
423     return 0;
424   }
425   if (a->start < b->start)
426     return -1;
427   if (a->start > b->start)
428     return 1;
429   return 0;
430 }
431
432 /**
433  * ges_track_add_object:
434  * @track: a #GESTrack
435  * @object: (transfer full): the #GESTrackObject to add
436  *
437  * Adds the given object to the track. Sets the object's controlling track,
438  * and thus takes ownership of the @object.
439  *
440  * An object can only be added to one track.
441  *
442  * Returns: #TRUE if the object was properly added. #FALSE if the track does not
443  * want to accept the object.
444  */
445 gboolean
446 ges_track_add_object (GESTrack * track, GESTrackObject * object)
447 {
448   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
449   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
450
451   GST_DEBUG ("track:%p, object:%p", track, object);
452
453   if (G_UNLIKELY (ges_track_object_get_track (object) != NULL)) {
454     GST_WARNING ("Object already belongs to another track");
455     return FALSE;
456   }
457
458   if (G_UNLIKELY (!ges_track_object_set_track (object, track))) {
459     GST_ERROR ("Couldn't properly add the object to the Track");
460     return FALSE;
461   }
462
463   GST_DEBUG ("Adding object %s to ourself %s",
464       GST_OBJECT_NAME (ges_track_object_get_gnlobject (object)),
465       GST_OBJECT_NAME (track->priv->composition));
466
467   if (G_UNLIKELY (!gst_bin_add (GST_BIN (track->priv->composition),
468               ges_track_object_get_gnlobject (object)))) {
469     GST_WARNING ("Couldn't add object to the GnlComposition");
470     return FALSE;
471   }
472
473   g_object_ref_sink (object);
474   track->priv->trackobjects =
475       g_list_insert_sorted (track->priv->trackobjects, object,
476       (GCompareFunc) objects_start_compare);
477
478   g_signal_emit (track, ges_track_signals[TRACK_OBJECT_ADDED], 0,
479       GES_TRACK_OBJECT (object));
480
481   g_signal_connect (GES_TRACK_OBJECT (object), "notify::start",
482       G_CALLBACK (sort_track_objects_cb), track);
483
484   g_signal_connect (GES_TRACK_OBJECT (object), "notify::priority",
485       G_CALLBACK (sort_track_objects_cb), track);
486
487   return TRUE;
488 }
489
490 /**
491  * ges_track_get_objects:
492  * @track: a #GESTrack
493  *
494  * Gets the #GESTrackObject contained in @track
495  *
496  * Returns: (transfer full) (element-type GESTrackObject): the list of
497  * #GESTrackObject present in the Track sorted by priority and start.
498  */
499 GList *
500 ges_track_get_objects (GESTrack * track)
501 {
502   GList *ret = NULL;
503   GList *tmp;
504
505   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
506
507   for (tmp = track->priv->trackobjects; tmp; tmp = tmp->next) {
508     ret = g_list_prepend (ret, tmp->data);
509     g_object_ref (tmp->data);
510   }
511
512   ret = g_list_reverse (ret);
513   return ret;
514 }
515
516 /**
517  * ges_track_remove_object:
518  * @track: a #GESTrack
519  * @object: the #GESTrackObject to remove
520  *
521  * Removes the object from the track and unparents it.
522  * Unparenting it means the reference owned by @track on the @object will be
523  * removed. If you wish to use the @object after this function, make sure you
524  * call g_object_ref() before removing it from the @track.
525  *
526  * Returns: #TRUE if the object was removed, else #FALSE if the track
527  * could not remove the object (like if it didn't belong to the track).
528  */
529 gboolean
530 ges_track_remove_object (GESTrack * track, GESTrackObject * object)
531 {
532   GESTrackPrivate *priv;
533   GstElement *gnlobject;
534
535   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
536   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
537
538   GST_DEBUG ("track:%p, object:%p", track, object);
539
540   priv = track->priv;
541
542   if (G_UNLIKELY (ges_track_object_get_track (object) != track)) {
543     GST_WARNING ("Object belongs to another track");
544     return FALSE;
545   }
546
547   if ((gnlobject = ges_track_object_get_gnlobject (object))) {
548     GST_DEBUG ("Removing GnlObject '%s' from composition '%s'",
549         GST_ELEMENT_NAME (gnlobject), GST_ELEMENT_NAME (priv->composition));
550     /* We can't just set state of gnlobject to GST_STATE_NULL, because it will
551      * result in deadlock. Adding a ref to the gnlobj so we finalize it after
552      * removing it from the composition */
553     gst_object_ref (gnlobject);
554     if (!gst_bin_remove (GST_BIN (priv->composition), gnlobject)) {
555       GST_WARNING ("Failed to remove gnlobject from composition");
556       return FALSE;
557     }
558
559     gst_element_set_state (gnlobject, GST_STATE_NULL);
560     /* Wait for the state change to actually happen */
561     gst_element_get_state (gnlobject, NULL, NULL, GST_CLOCK_TIME_NONE);
562     gst_object_unref (gnlobject);
563   }
564
565   g_signal_handlers_disconnect_by_func (object, sort_track_objects_cb, NULL);
566
567   ges_track_object_set_track (object, NULL);
568
569   g_signal_emit (track, ges_track_signals[TRACK_OBJECT_REMOVED], 0,
570       GES_TRACK_OBJECT (object));
571
572   priv->trackobjects = g_list_remove (priv->trackobjects, object);
573
574   g_object_unref (object);
575
576   return TRUE;
577 }
578
579 static void
580 pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track)
581 {
582   GESTrackPrivate *priv = track->priv;
583
584   GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
585
586   /* ghost the pad */
587   priv->srcpad = gst_ghost_pad_new ("src", pad);
588
589   gst_pad_set_active (priv->srcpad, TRUE);
590
591   gst_element_add_pad (GST_ELEMENT (track), priv->srcpad);
592
593   GST_DEBUG ("done");
594 }
595
596 static void
597 pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track)
598 {
599   GESTrackPrivate *priv = track->priv;
600
601   GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
602
603   if (G_LIKELY (priv->srcpad)) {
604     gst_pad_set_active (priv->srcpad, FALSE);
605     gst_element_remove_pad (GST_ELEMENT (track), priv->srcpad);
606     priv->srcpad = NULL;
607   }
608
609   GST_DEBUG ("done");
610 }
611
612 static void
613 composition_duration_cb (GstElement * composition,
614     GParamSpec * arg G_GNUC_UNUSED, GESTrack * obj)
615 {
616   guint64 duration;
617
618   g_object_get (composition, "duration", &duration, NULL);
619
620
621   if (obj->priv->duration != duration) {
622     GST_DEBUG ("composition duration : %" GST_TIME_FORMAT " current : %"
623         GST_TIME_FORMAT, GST_TIME_ARGS (duration),
624         GST_TIME_ARGS (obj->priv->duration));
625
626     obj->priv->duration = duration;
627
628 #if GLIB_CHECK_VERSION(2,26,0)
629     g_object_notify_by_pspec (G_OBJECT (obj), properties[ARG_DURATION]);
630 #else
631     g_object_notify (G_OBJECT (obj), "duration");
632 #endif
633   }
634 }
635
636 static void
637 sort_track_objects_cb (GESTrackObject * child,
638     GParamSpec * arg G_GNUC_UNUSED, GESTrack * track)
639 {
640   track->priv->trackobjects =
641       g_list_sort (track->priv->trackobjects,
642       (GCompareFunc) objects_start_compare);
643 }
644
645 static void
646 timeline_duration_cb (GESTimeline * timeline,
647     GParamSpec * arg G_GNUC_UNUSED, GESTrack * track)
648 {
649   guint64 duration;
650
651   g_object_get (timeline, "duration", &duration, NULL);
652   g_object_set (GES_TRACK (track)->priv->background, "duration", duration,
653       NULL);
654
655   GST_DEBUG_OBJECT (track, "Updating background duration to %" GST_TIME_FORMAT,
656       GST_TIME_ARGS (duration));
657 }
658
659 /**
660  * ges_track_get_caps:
661  * @track: a #GESTrack
662  *
663  * Get the #GstCaps this track is configured to output.
664  *
665  * Returns: The #GstCaps this track is configured to output.
666  */
667 const GstCaps *
668 ges_track_get_caps (GESTrack * track)
669 {
670   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
671
672   return track->priv->caps;
673 }
674
675 /**
676  * ges_track_get_timeline:
677  * @track: a #GESTrack
678  *
679  * Get the #GESTimeline this track belongs to. Can be %NULL.
680  *
681  * Returns: The #GESTimeline this track belongs to. Can be %NULL.
682  */
683 const GESTimeline *
684 ges_track_get_timeline (GESTrack * track)
685 {
686   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
687
688   return track->priv->timeline;
689 }
690
691 /**
692  * ges_track_enable_update:
693  * @track: a #GESTrack
694  * @enabled: Whether the track should update on every change or not.
695  *
696  * Control whether the track is updated for every change happening within.
697  *
698  * Users will want to use this method with %FALSE before doing lots of changes,
699  * and then call again with %TRUE for the changes to take effect in one go.
700  *
701  * Returns: %TRUE if the update status could be changed, else %FALSE.
702  */
703 gboolean
704 ges_track_enable_update (GESTrack * track, gboolean enabled)
705 {
706   gboolean update;
707
708   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
709
710   g_object_set (track->priv->composition, "update", enabled, NULL);
711   g_object_get (track->priv->composition, "update", &update, NULL);
712
713   track->priv->updating = update;
714
715   return update == enabled;
716 }
717
718 /**
719  * ges_track_is_updating:
720  * @track: a #GESTrack
721  *
722  * Get whether the track is updated for every change happening within or not.
723  *
724  * Returns: %TRUE if @track is updating on every changes, else %FALSE.
725  */
726 gboolean
727 ges_track_is_updating (GESTrack * track)
728 {
729   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
730
731   return track->priv->updating;
732 }