Merge branch '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   /* At this point, the track object shouldn't have any gnlobject since
459    * it hasn't been added to a track yet.
460    * FIXME : This check seems a bit obsolete */
461   if (G_UNLIKELY (ges_track_object_get_gnlobject (object) != NULL)) {
462     GST_ERROR ("TrackObject already controls a gnlobject !");
463     return FALSE;
464   }
465
466   if (G_UNLIKELY (!ges_track_object_set_track (object, track))) {
467     GST_ERROR ("Couldn't properly add the object to the Track");
468     return FALSE;
469   }
470
471   GST_DEBUG ("Adding object %s to ourself %s",
472       GST_OBJECT_NAME (ges_track_object_get_gnlobject (object)),
473       GST_OBJECT_NAME (track->priv->composition));
474
475   if (G_UNLIKELY (!gst_bin_add (GST_BIN (track->priv->composition),
476               ges_track_object_get_gnlobject (object)))) {
477     GST_WARNING ("Couldn't add object to the GnlComposition");
478     return FALSE;
479   }
480
481   g_object_ref_sink (object);
482   track->priv->trackobjects =
483       g_list_insert_sorted (track->priv->trackobjects, object,
484       (GCompareFunc) objects_start_compare);
485
486   g_signal_emit (track, ges_track_signals[TRACK_OBJECT_ADDED], 0,
487       GES_TRACK_OBJECT (object));
488
489   g_signal_connect (GES_TRACK_OBJECT (object), "notify::start",
490       G_CALLBACK (sort_track_objects_cb), track);
491
492   g_signal_connect (GES_TRACK_OBJECT (object), "notify::priority",
493       G_CALLBACK (sort_track_objects_cb), track);
494
495   return TRUE;
496 }
497
498 /**
499  * ges_track_get_objects:
500  * @track: a #GESTrack
501  *
502  * Gets the #GESTrackObject contained in @track
503  *
504  * Returns: (transfer full) (element-type GESTrackObject): the list of
505  * #GESTrackObject present in the Track sorted by priority and start.
506  */
507 GList *
508 ges_track_get_objects (GESTrack * track)
509 {
510   GList *ret = NULL;
511   GList *tmp;
512
513   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
514
515   for (tmp = track->priv->trackobjects; tmp; tmp = tmp->next) {
516     ret = g_list_prepend (ret, tmp->data);
517     g_object_ref (tmp->data);
518   }
519
520   ret = g_list_reverse (ret);
521   return ret;
522 }
523
524 /**
525  * ges_track_remove_object:
526  * @track: a #GESTrack
527  * @object: the #GESTrackObject to remove
528  *
529  * Removes the object from the track and unparents it.
530  * Unparenting it means the reference owned by @track on the @object will be
531  * removed. If you wish to use the @object after this function, make sure you
532  * call g_object_ref() before removing it from the @track.
533  *
534  * Returns: #TRUE if the object was removed, else #FALSE if the track
535  * could not remove the object (like if it didn't belong to the track).
536  */
537 gboolean
538 ges_track_remove_object (GESTrack * track, GESTrackObject * object)
539 {
540   GESTrackPrivate *priv;
541   GstElement *gnlobject;
542
543   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
544   g_return_val_if_fail (GES_IS_TRACK_OBJECT (object), FALSE);
545
546   GST_DEBUG ("track:%p, object:%p", track, object);
547
548   priv = track->priv;
549
550   if (G_UNLIKELY (ges_track_object_get_track (object) != track)) {
551     GST_WARNING ("Object belongs to another track");
552     return FALSE;
553   }
554
555   if ((gnlobject = ges_track_object_get_gnlobject (object))) {
556     GST_DEBUG ("Removing GnlObject '%s' from composition '%s'",
557         GST_ELEMENT_NAME (gnlobject), GST_ELEMENT_NAME (priv->composition));
558     /* We can't just set state of gnlobject to GST_STATE_NULL, because it will
559      * result in deadlock. Adding a ref to the gnlobj so we finalize it after
560      * removing it from the composition */
561     gst_object_ref (gnlobject);
562     if (!gst_bin_remove (GST_BIN (priv->composition), gnlobject)) {
563       GST_WARNING ("Failed to remove gnlobject from composition");
564       return FALSE;
565     }
566
567     gst_element_set_state (gnlobject, GST_STATE_NULL);
568     /* Wait for the state change to actually happen */
569     gst_element_get_state (gnlobject, NULL, NULL, GST_CLOCK_TIME_NONE);
570     gst_object_unref (gnlobject);
571   }
572
573   g_signal_handlers_disconnect_by_func (object, sort_track_objects_cb, NULL);
574
575   ges_track_object_set_track (object, NULL);
576
577   g_signal_emit (track, ges_track_signals[TRACK_OBJECT_REMOVED], 0,
578       GES_TRACK_OBJECT (object));
579
580   priv->trackobjects = g_list_remove (priv->trackobjects, object);
581
582   g_object_unref (object);
583
584   return TRUE;
585 }
586
587 static void
588 pad_added_cb (GstElement * element, GstPad * pad, GESTrack * track)
589 {
590   GESTrackPrivate *priv = track->priv;
591
592   GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
593
594   /* ghost the pad */
595   priv->srcpad = gst_ghost_pad_new ("src", pad);
596
597   gst_pad_set_active (priv->srcpad, TRUE);
598
599   gst_element_add_pad (GST_ELEMENT (track), priv->srcpad);
600
601   GST_DEBUG ("done");
602 }
603
604 static void
605 pad_removed_cb (GstElement * element, GstPad * pad, GESTrack * track)
606 {
607   GESTrackPrivate *priv = track->priv;
608
609   GST_DEBUG ("track:%p, pad %s:%s", track, GST_DEBUG_PAD_NAME (pad));
610
611   if (G_LIKELY (priv->srcpad)) {
612     gst_pad_set_active (priv->srcpad, FALSE);
613     gst_element_remove_pad (GST_ELEMENT (track), priv->srcpad);
614     priv->srcpad = NULL;
615   }
616
617   GST_DEBUG ("done");
618 }
619
620 static void
621 composition_duration_cb (GstElement * composition,
622     GParamSpec * arg G_GNUC_UNUSED, GESTrack * obj)
623 {
624   guint64 duration;
625
626   g_object_get (composition, "duration", &duration, NULL);
627
628
629   if (obj->priv->duration != duration) {
630     GST_DEBUG ("composition duration : %" GST_TIME_FORMAT " current : %"
631         GST_TIME_FORMAT, GST_TIME_ARGS (duration),
632         GST_TIME_ARGS (obj->priv->duration));
633
634     obj->priv->duration = duration;
635
636 #if GLIB_CHECK_VERSION(2,26,0)
637     g_object_notify_by_pspec (G_OBJECT (obj), properties[ARG_DURATION]);
638 #else
639     g_object_notify (G_OBJECT (obj), "duration");
640 #endif
641   }
642 }
643
644 static void
645 sort_track_objects_cb (GESTrackObject * child,
646     GParamSpec * arg G_GNUC_UNUSED, GESTrack * track)
647 {
648   track->priv->trackobjects =
649       g_list_sort (track->priv->trackobjects,
650       (GCompareFunc) objects_start_compare);
651 }
652
653 static void
654 timeline_duration_cb (GESTimeline * timeline,
655     GParamSpec * arg G_GNUC_UNUSED, GESTrack * track)
656 {
657   guint64 duration;
658
659   g_object_get (timeline, "duration", &duration, NULL);
660   g_object_set (GES_TRACK (track)->priv->background, "duration", duration,
661       NULL);
662
663   GST_DEBUG_OBJECT (track, "Updating background duration to %" GST_TIME_FORMAT,
664       GST_TIME_ARGS (duration));
665 }
666
667 /**
668  * ges_track_get_caps:
669  * @track: a #GESTrack
670  *
671  * Get the #GstCaps this track is configured to output.
672  *
673  * Returns: The #GstCaps this track is configured to output.
674  */
675 const GstCaps *
676 ges_track_get_caps (GESTrack * track)
677 {
678   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
679
680   return track->priv->caps;
681 }
682
683 /**
684  * ges_track_get_timeline:
685  * @track: a #GESTrack
686  *
687  * Get the #GESTimeline this track belongs to. Can be %NULL.
688  *
689  * Returns: The #GESTimeline this track belongs to. Can be %NULL.
690  */
691 const GESTimeline *
692 ges_track_get_timeline (GESTrack * track)
693 {
694   g_return_val_if_fail (GES_IS_TRACK (track), NULL);
695
696   return track->priv->timeline;
697 }
698
699 /**
700  * ges_track_enable_update:
701  * @track: a #GESTrack
702  * @enabled: Whether the track should update on every change or not.
703  *
704  * Control whether the track is updated for every change happening within.
705  *
706  * Users will want to use this method with %FALSE before doing lots of changes,
707  * and then call again with %TRUE for the changes to take effect in one go.
708  *
709  * Returns: %TRUE if the update status could be changed, else %FALSE.
710  */
711 gboolean
712 ges_track_enable_update (GESTrack * track, gboolean enabled)
713 {
714   gboolean update;
715
716   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
717
718   g_object_set (track->priv->composition, "update", enabled, NULL);
719   g_object_get (track->priv->composition, "update", &update, NULL);
720
721   track->priv->updating = update;
722
723   return update == enabled;
724 }
725
726 /**
727  * ges_track_is_updating:
728  * @track: a #GESTrack
729  *
730  * Get whether the track is updated for every change happening within or not.
731  *
732  * Returns: %TRUE if @track is updating on every changes, else %FALSE.
733  */
734 gboolean
735 ges_track_is_updating (GESTrack * track)
736 {
737   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
738
739   return track->priv->updating;
740 }