docs: A round of updates
[platform/upstream/gstreamer.git] / ges / ges-timeline.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-timeline
23  * @short_description: Multimedia timeline
24  *
25  * #GESTimeline is the central object for any multimedia timeline.
26  * 
27  * Contains a list of #GESTimelineLayer which users should use to arrange the
28  * various timeline objects through time.
29  *
30  * The output type is determined by the #GESTrack that are set on
31  * the #GESTimeline.
32  *
33  * To save/load a timeline, you can use the ges_timeline_load_from_uri() and
34  * ges_timeline_save_to_uri() methods to use the default format. If you wish
35  * to specify the format to save/load the timeline from, please consult the
36  * documentation about #GESFormatter.
37  */
38
39 #include "gesmarshal.h"
40 #include "ges-internal.h"
41 #include "ges-timeline.h"
42 #include "ges-track.h"
43 #include "ges-timeline-layer.h"
44 #include "ges.h"
45
46
47 G_DEFINE_TYPE (GESTimeline, ges_timeline, GST_TYPE_BIN);
48
49 /* private structure to contain our track-related information */
50
51 typedef struct
52 {
53   GESTimeline *timeline;
54   GESTrack *track;
55   GstPad *pad;                  /* Pad from the track */
56   GstPad *ghostpad;
57 } TrackPrivate;
58
59 enum
60 {
61   TRACK_ADDED,
62   TRACK_REMOVED,
63   LAYER_ADDED,
64   LAYER_REMOVED,
65   LAST_SIGNAL
66 };
67
68 static GstBinClass *parent_class;
69
70 static guint ges_timeline_signals[LAST_SIGNAL] = { 0 };
71
72 gint custom_find_track (TrackPrivate * priv, GESTrack * track);
73 static GstStateChangeReturn
74 ges_timeline_change_state (GstElement * element, GstStateChange transition);
75 static void
76 discoverer_finished_cb (GstDiscoverer * discoverer, GESTimeline * timeline);
77 static void
78 discoverer_discovered_cb (GstDiscoverer * discoverer,
79     GstDiscovererInfo * info, GError * err, GESTimeline * timeline);
80
81 static void
82 ges_timeline_get_property (GObject * object, guint property_id,
83     GValue * value, GParamSpec * pspec)
84 {
85   switch (property_id) {
86     default:
87       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
88   }
89 }
90
91 static void
92 ges_timeline_set_property (GObject * object, guint property_id,
93     const GValue * value, GParamSpec * pspec)
94 {
95   switch (property_id) {
96     default:
97       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
98   }
99 }
100
101 static void
102 ges_timeline_dispose (GObject * object)
103 {
104   GESTimeline *timeline = GES_TIMELINE (object);
105
106   if (timeline->discoverer) {
107     gst_discoverer_stop (timeline->discoverer);
108     g_object_unref (timeline->discoverer);
109     timeline->discoverer = NULL;
110   }
111
112   while (timeline->layers) {
113     GESTimelineLayer *layer = (GESTimelineLayer *) timeline->layers->data;
114     ges_timeline_remove_layer (timeline, layer);
115   }
116
117   /* FIXME: it should be possible to remove tracks before removing
118    * layers, but at the moment this creates a problem because the track
119    * objects aren't notified that their gnlobjects have been destroyed.
120    */
121
122   while (timeline->tracks) {
123     TrackPrivate *priv = (TrackPrivate *) timeline->tracks->data;
124     ges_timeline_remove_track (timeline, priv->track);
125   }
126
127   G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
128 }
129
130 static void
131 ges_timeline_finalize (GObject * object)
132 {
133   G_OBJECT_CLASS (ges_timeline_parent_class)->finalize (object);
134 }
135
136 static void
137 ges_timeline_class_init (GESTimelineClass * klass)
138 {
139   GObjectClass *object_class = G_OBJECT_CLASS (klass);
140   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
141
142   parent_class = g_type_class_peek_parent (klass);
143
144   element_class->change_state = ges_timeline_change_state;
145
146   object_class->get_property = ges_timeline_get_property;
147   object_class->set_property = ges_timeline_set_property;
148   object_class->dispose = ges_timeline_dispose;
149   object_class->finalize = ges_timeline_finalize;
150
151   /**
152    * GESTimeline::track-added
153    * @timeline: the #GESTimeline
154    * @track: the #GESTrack that was added to the timeline
155    *
156    * Will be emitted after the track was added to the timeline.
157    */
158   ges_timeline_signals[TRACK_ADDED] =
159       g_signal_new ("track-added", G_TYPE_FROM_CLASS (klass),
160       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_added), NULL,
161       NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GES_TYPE_TRACK);
162
163   /**
164    * GESTimeline::track-removed
165    * @timeline: the #GESTimeline
166    * @track: the #GESTrack that was removed from the timeline
167    *
168    * Will be emitted after the track was removed from the timeline.
169    */
170   ges_timeline_signals[TRACK_REMOVED] =
171       g_signal_new ("track-removed", G_TYPE_FROM_CLASS (klass),
172       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_removed),
173       NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GES_TYPE_TRACK);
174
175   /**
176    * GESTimeline::layer-added
177    * @timeline: the #GESTimeline
178    * @layer: the #GESTimelineLayer that was added to the timeline
179    *
180    * Will be emitted after the layer was added to the timeline.
181    */
182   ges_timeline_signals[LAYER_ADDED] =
183       g_signal_new ("layer-added", G_TYPE_FROM_CLASS (klass),
184       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_added), NULL,
185       NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GES_TYPE_TIMELINE_LAYER);
186
187   /**
188    * GESTimeline::layer-removed
189    * @timeline: the #GESTimeline
190    * @layer: the #GESTimelineLayer that was removed from the timeline
191    *
192    * Will be emitted after the layer was removed from the timeline.
193    */
194   ges_timeline_signals[LAYER_REMOVED] =
195       g_signal_new ("layer-removed", G_TYPE_FROM_CLASS (klass),
196       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_removed),
197       NULL, NULL, ges_marshal_VOID__OBJECT, G_TYPE_NONE, 1,
198       GES_TYPE_TIMELINE_LAYER);
199 }
200
201 static void
202 ges_timeline_init (GESTimeline * self)
203 {
204   self->layers = NULL;
205   self->tracks = NULL;
206
207   /* New discoverer with a 15s timeout */
208   self->discoverer = gst_discoverer_new (15 * GST_SECOND, NULL);
209   g_signal_connect (self->discoverer, "finished",
210       G_CALLBACK (discoverer_finished_cb), self);
211   g_signal_connect (self->discoverer, "discovered",
212       G_CALLBACK (discoverer_discovered_cb), self);
213   gst_discoverer_start (self->discoverer);
214 }
215
216 /**
217  * ges_timeline_new:
218  *
219  * Creates a new empty #GESTimeline.
220  *
221  * Returns: The new timeline.
222  */
223
224 GESTimeline *
225 ges_timeline_new (void)
226 {
227   return g_object_new (GES_TYPE_TIMELINE, NULL);
228 }
229
230 /**
231  * ges_timeline_new_from_uri:
232  * @uri: the URI to load from
233  *
234  * Creates a timeline from the given URI.
235  *
236  * Returns: A new timeline if the uri was loaded successfully, or NULL if the
237  * uri could not be loaded 
238  */
239
240 GESTimeline *
241 ges_timeline_new_from_uri (gchar * uri)
242 {
243   GESTimeline *ret;
244
245   /* FIXME : we should have a GError** argument so the user can know why
246    * it wasn't able to load the uri 
247    */
248
249   ret = ges_timeline_new ();
250
251   if (!ges_timeline_load_from_uri (ret, uri)) {
252     g_object_unref (ret);
253     return NULL;
254   }
255
256   return ret;
257 }
258
259
260 /**
261  * ges_timeline_load_from_uri:
262  * @timeline: an empty #GESTimeline into which to load the formatter
263  * @uri: The URI to load from
264  *
265  * Loads the contents of URI into the given timeline.
266  *
267  * Returns: TRUE if the timeline was loaded successfully, or FALSE if the uri
268  * could not be loaded.
269  */
270
271 gboolean
272 ges_timeline_load_from_uri (GESTimeline * timeline, gchar * uri)
273 {
274   GESFormatter *p = NULL;
275   gboolean ret = FALSE;
276
277   /* FIXME : we should have a GError** argument so the user can know why
278    * it wasn't able to load the uri 
279    */
280
281   if (!(p = ges_formatter_new_for_uri (uri))) {
282     GST_ERROR ("unsupported uri '%s'", uri);
283     goto fail;
284   }
285
286   if (!ges_formatter_load_from_uri (p, timeline, uri)) {
287     GST_ERROR ("error deserializing formatter");
288     goto fail;
289   }
290
291   ret = TRUE;
292
293 fail:
294   if (p)
295     g_object_unref (p);
296   return ret;
297 }
298
299 /**
300  * ges_timeline_save_to_uri:
301  * @timeline: a #GESTimeline
302  * @uri: The location to save to
303  *
304  * Saves the timeline to the given location
305  *
306  * Returns: TRUE if the timeline was successfully saved to the given location,
307  * else FALSE.
308  */
309
310 gboolean
311 ges_timeline_save_to_uri (GESTimeline * timeline, gchar * uri)
312 {
313   GESFormatter *p = NULL;
314   gboolean ret = FALSE;
315
316   /* FIXME : How will the user be able to chose the format he
317    * wishes to store to ? */
318
319   /* FIXME : How will we ensure a timeline loaded with a certain format
320    * will be saved with the same one by default ? We need to make this
321    * easy from an API perspective */
322
323   /* FIXME : we should have a GError** argument so the user can know why
324    * it wasn't able to save
325    */
326
327   if (!(p = ges_formatter_new_for_uri (uri))) {
328     GST_ERROR ("unsupported uri '%s'", uri);
329     goto fail;
330   }
331
332   if (!ges_formatter_save_to_uri (p, timeline, uri)) {
333     GST_ERROR ("error serializing formatter");
334     goto fail;
335   }
336
337   ret = TRUE;
338
339 fail:
340   if (p)
341     g_object_unref (p);
342   return ret;
343 }
344
345 static void
346 add_object_to_track (GESTimelineObject * object, GESTrack * track)
347 {
348   if (!ges_timeline_object_create_track_objects (object, track)) {
349     GST_WARNING ("error creating track objects");
350   }
351 }
352
353 static void
354 add_object_to_tracks (GESTimeline * timeline, GESTimelineObject * object)
355 {
356   GList *tmp;
357
358   for (tmp = timeline->tracks; tmp; tmp = g_list_next (tmp)) {
359     TrackPrivate *priv = (TrackPrivate *) tmp->data;
360     GESTrack *track = priv->track;
361
362     GST_LOG ("Trying with track %p", track);
363     add_object_to_track (object, track);
364   }
365 }
366
367
368 static void
369 do_async_start (GESTimeline * timeline)
370 {
371   GstMessage *message;
372   GList *tmp;
373
374   timeline->async_pending = TRUE;
375
376   /* Freeze state of tracks */
377   for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
378     TrackPrivate *priv = (TrackPrivate *) tmp->data;
379     gst_element_set_locked_state ((GstElement *) priv->track, TRUE);
380   }
381
382   message = gst_message_new_async_start (GST_OBJECT_CAST (timeline), FALSE);
383   parent_class->handle_message (GST_BIN_CAST (timeline), message);
384 }
385
386 static void
387 do_async_done (GESTimeline * timeline)
388 {
389   GstMessage *message;
390
391   if (timeline->async_pending) {
392     GList *tmp;
393     /* Unfreeze state of tracks */
394     for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
395       TrackPrivate *priv = (TrackPrivate *) tmp->data;
396       gst_element_set_locked_state ((GstElement *) priv->track, FALSE);
397       gst_element_sync_state_with_parent ((GstElement *) priv->track);
398     }
399
400     GST_DEBUG_OBJECT (timeline, "Emitting async-done");
401     message = gst_message_new_async_done (GST_OBJECT_CAST (timeline));
402     parent_class->handle_message (GST_BIN_CAST (timeline), message);
403
404     timeline->async_pending = FALSE;
405   }
406 }
407
408 static void
409 discoverer_finished_cb (GstDiscoverer * discoverer, GESTimeline * timeline)
410 {
411   do_async_done (timeline);
412 }
413
414 static void
415 discoverer_discovered_cb (GstDiscoverer * discoverer,
416     GstDiscovererInfo * info, GError * err, GESTimeline * timeline)
417 {
418   GList *tmp;
419   gboolean found = FALSE;
420   gboolean is_image = FALSE;
421   GESTimelineFileSource *tfs = NULL;
422   const gchar *uri = gst_discoverer_info_get_uri (info);
423
424   GST_DEBUG ("Discovered uri %s", uri);
425
426   /* Find corresponding TimelineFileSource in the sources */
427   for (tmp = timeline->pendingobjects; tmp; tmp = tmp->next) {
428     tfs = (GESTimelineFileSource *) tmp->data;
429
430     if (!g_strcmp0 (tfs->uri, uri)) {
431       found = TRUE;
432       break;
433     }
434   }
435
436   if (found) {
437     GList *stream_list;
438
439     /* Remove object from list */
440     timeline->pendingobjects =
441         g_list_delete_link (timeline->pendingobjects, tmp);
442
443     /* FIXME : Handle errors in discovery */
444     stream_list = gst_discoverer_info_get_stream_list (info);
445
446     /* Update timelinefilesource properties based on info */
447     for (tmp = stream_list; tmp; tmp = tmp->next) {
448       GstDiscovererStreamInfo *sinf = (GstDiscovererStreamInfo *) tmp->data;
449
450       if (GST_IS_DISCOVERER_AUDIO_INFO (sinf))
451         tfs->supportedformats |= GES_TRACK_TYPE_AUDIO;
452       else if (GST_IS_DISCOVERER_VIDEO_INFO (sinf)) {
453         tfs->supportedformats |= GES_TRACK_TYPE_VIDEO;
454         if (gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
455                 sinf)) {
456           tfs->supportedformats |= GES_TRACK_TYPE_AUDIO;
457           is_image = TRUE;
458         }
459       }
460     }
461
462     if (stream_list)
463       gst_discoverer_stream_info_list_free (stream_list);
464
465     if (is_image) {
466       /* don't set max-duration on still images */
467       g_object_set (tfs, "is_image", (gboolean) TRUE, NULL);
468     }
469
470     else {
471       g_object_set (tfs, "max-duration",
472           gst_discoverer_info_get_duration (info), NULL);
473     }
474
475     /* Continue the processing on tfs */
476     add_object_to_tracks (timeline, GES_TIMELINE_OBJECT (tfs));
477   }
478 }
479
480 static GstStateChangeReturn
481 ges_timeline_change_state (GstElement * element, GstStateChange transition)
482 {
483   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
484   GESTimeline *timeline = GES_TIMELINE (element);
485
486   switch (transition) {
487     case GST_STATE_CHANGE_READY_TO_PAUSED:
488       if (timeline->pendingobjects) {
489         do_async_start (timeline);
490         ret = GST_STATE_CHANGE_ASYNC;
491       }
492       break;
493     default:
494       break;
495   }
496
497   {
498     GstStateChangeReturn bret;
499
500     bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
501     if (G_UNLIKELY (bret == GST_STATE_CHANGE_NO_PREROLL)) {
502       do_async_done (timeline);
503       ret = bret;
504     }
505   }
506
507   switch (transition) {
508     case GST_STATE_CHANGE_PAUSED_TO_READY:
509       do_async_done (timeline);
510       break;
511     default:
512       break;
513   }
514
515   return ret;
516
517 }
518
519 static void
520 layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
521     GESTimeline * timeline)
522 {
523   GST_DEBUG ("New TimelineObject %p added to layer %p", object, layer);
524
525   if (GES_IS_TIMELINE_FILE_SOURCE (object)) {
526     GESTimelineFileSource *tfs = GES_TIMELINE_FILE_SOURCE (object);
527
528     /* Send the filesource to the discoverer if:
529      * * it doesn't have specified supported formats
530      * * OR it doesn't have a specified max-duration
531      * * OR it doesn't have a valid duration  */
532
533     if (tfs->supportedformats == GES_TRACK_TYPE_UNKNOWN ||
534         tfs->maxduration == GST_CLOCK_TIME_NONE || object->duration == 0) {
535       GST_LOG ("Incomplete TimelineFileSource, discovering it");
536       timeline->pendingobjects =
537           g_list_append (timeline->pendingobjects, object);
538       gst_discoverer_discover_uri_async (timeline->discoverer,
539           GES_TIMELINE_FILE_SOURCE (object)->uri);
540     } else
541       add_object_to_tracks (timeline, object);
542   } else {
543     add_object_to_tracks (timeline, object);
544   }
545
546   GST_DEBUG ("done");
547 }
548
549
550 static void
551 layer_object_removed_cb (GESTimelineLayer * layer, GESTimelineObject * object,
552     GESTimeline * timeline)
553 {
554   GList *tmp, *next, *trackobjects;
555
556   GST_DEBUG ("TimelineObject %p removed from layer %p", object, layer);
557
558   /* Go over the object's track objects and figure out which one belongs to
559    * the list of tracks we control */
560
561   trackobjects = ges_timeline_object_get_track_objects (object);
562   for (tmp = trackobjects; tmp; tmp = tmp->next) {
563     GESTrackObject *trobj = (GESTrackObject *) tmp->data;
564
565     next = g_list_next (tmp);
566
567     GST_DEBUG ("Trying to remove TrackObject %p", trobj);
568     if (G_LIKELY (g_list_find_custom (timeline->tracks,
569                 (gconstpointer) trobj->track,
570                 (GCompareFunc) custom_find_track))) {
571       GST_DEBUG ("Belongs to one of the tracks we control");
572       ges_track_remove_object (trobj->track, trobj);
573
574       ges_timeline_object_release_track_object (object, trobj);
575     }
576
577     g_object_unref (GES_TRACK_OBJECT (tmp->data));
578   }
579   g_list_free (trackobjects);
580
581   GST_DEBUG ("Done");
582 }
583
584 /**
585  * ges_timeline_add_layer:
586  * @timeline: a #GESTimeline
587  * @layer: the #GESTimelineLayer to add
588  *
589  * Add the layer to the timeline. The reference to the @layer will be stolen
590  * by the @timeline.
591  *
592  * Returns: TRUE if the layer was properly added, else FALSE.
593  */
594 gboolean
595 ges_timeline_add_layer (GESTimeline * timeline, GESTimelineLayer * layer)
596 {
597   GList *objects, *tmp;
598
599   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
600
601   /* We can only add a layer that doesn't already belong to another timeline */
602   if (G_UNLIKELY (layer->timeline)) {
603     GST_WARNING ("Layer belongs to another timeline, can't add it");
604     return FALSE;
605   }
606
607   /* Add to the list of layers, make sure we don't already control it */
608   if (G_UNLIKELY (g_list_find (timeline->layers, (gconstpointer) layer))) {
609     GST_WARNING ("Layer is already controlled by this timeline");
610     return FALSE;
611   }
612
613   /* Reference is stolen */
614   timeline->layers = g_list_append (timeline->layers, layer);
615
616   /* Inform the layer that it belongs to a new timeline */
617   ges_timeline_layer_set_timeline (layer, timeline);
618
619   /* Connect to 'object-added'/'object-removed' signal from the new layer */
620   g_signal_connect (layer, "object-added", G_CALLBACK (layer_object_added_cb),
621       timeline);
622   g_signal_connect (layer, "object-removed",
623       G_CALLBACK (layer_object_removed_cb), timeline);
624
625   GST_DEBUG ("Done adding layer, emitting 'layer-added' signal");
626   g_signal_emit (timeline, ges_timeline_signals[LAYER_ADDED], 0, layer);
627
628   /* add any existing timeline objects to the timeline */
629   objects = ges_timeline_layer_get_objects (layer);
630   for (tmp = objects; tmp; tmp = tmp->next) {
631     layer_object_added_cb (layer, tmp->data, timeline);
632     g_object_unref (tmp->data);
633     tmp->data = NULL;
634   }
635   g_list_free (objects);
636
637   return TRUE;
638 }
639
640 /**
641  * ges_timeline_remove_layer:
642  * @timeline: a #GESTimeline
643  * @layer: the #GESTimelineLayer to remove
644  *
645  * Removes the layer from the timeline. The reference that the @timeline holds on
646  * the layer will be dropped. If you wish to use the @layer after calling this
647  * method, you need to take a reference before calling.
648  *
649  * Returns: TRUE if the layer was properly removed, else FALSE.
650  */
651
652 gboolean
653 ges_timeline_remove_layer (GESTimeline * timeline, GESTimelineLayer * layer)
654 {
655   GList *layer_objects, *tmp;
656
657   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
658
659   if (G_UNLIKELY (!g_list_find (timeline->layers, layer))) {
660     GST_WARNING ("Layer doesn't belong to this timeline");
661     return FALSE;
662   }
663
664   /* remove objects from any private data structures */
665
666   layer_objects = ges_timeline_layer_get_objects (layer);
667   for (tmp = layer_objects; tmp; tmp = tmp->next) {
668     layer_object_removed_cb (layer, GES_TIMELINE_OBJECT (tmp->data), timeline);
669     g_object_unref (G_OBJECT (tmp->data));
670     tmp->data = NULL;
671   }
672   g_list_free (layer_objects);
673
674   /* Disconnect signals */
675   GST_DEBUG ("Disconnecting signal callbacks");
676   g_signal_handlers_disconnect_by_func (layer, layer_object_added_cb, timeline);
677   g_signal_handlers_disconnect_by_func (layer, layer_object_removed_cb,
678       timeline);
679
680   timeline->layers = g_list_remove (timeline->layers, layer);
681
682   ges_timeline_layer_set_timeline (layer, NULL);
683
684   g_signal_emit (timeline, ges_timeline_signals[LAYER_REMOVED], 0, layer);
685
686   g_object_unref (layer);
687
688   return TRUE;
689 }
690
691 static void
692 pad_added_cb (GESTrack * track, GstPad * pad, TrackPrivate * priv)
693 {
694   gchar *padname;
695
696
697   GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
698
699   if (G_UNLIKELY (priv->pad)) {
700     GST_WARNING ("We are already controlling a pad for this track");
701     return;
702   }
703
704   /* Remember the pad */
705   priv->pad = pad;
706
707   /* ghost it ! */
708   GST_DEBUG ("Ghosting pad and adding it to ourself");
709   padname = g_strdup_printf ("track_%p_src", track);
710   priv->ghostpad = gst_ghost_pad_new (padname, pad);
711   g_free (padname);
712   gst_pad_set_active (priv->ghostpad, TRUE);
713   gst_element_add_pad (GST_ELEMENT (priv->timeline), priv->ghostpad);
714 }
715
716 static void
717 pad_removed_cb (GESTrack * track, GstPad * pad, TrackPrivate * priv)
718 {
719   GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
720
721   if (G_UNLIKELY (priv->pad != pad)) {
722     GST_WARNING ("Not the pad we're controlling");
723     return;
724   }
725
726   if (G_UNLIKELY (priv->ghostpad == NULL)) {
727     GST_WARNING ("We don't have a ghostpad for this pad !");
728     return;
729   }
730
731   GST_DEBUG ("Removing ghostpad");
732   gst_pad_set_active (priv->ghostpad, FALSE);
733   gst_element_remove_pad (GST_ELEMENT (priv->timeline), priv->ghostpad);
734   priv->ghostpad = NULL;
735   priv->pad = NULL;
736 }
737
738 gint
739 custom_find_track (TrackPrivate * priv, GESTrack * track)
740 {
741   if (priv->track == track)
742     return 0;
743   return -1;
744 }
745
746 /**
747  * ges_timeline_add_track:
748  * @timeline: a #GESTimeline
749  * @track: the #GESTrack to add
750  *
751  * Add a track to the timeline. The reference to the track will be stolen by the
752  * pipeline.
753  *
754  * Returns: TRUE if the track was properly added, else FALSE.
755  */
756
757 /* FIXME: create track objects for timeline objects which have already been
758  * added to existing layers.
759  */
760
761 gboolean
762 ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
763 {
764   TrackPrivate *priv;
765   GList *tmp;
766
767   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
768
769   /* make sure we don't already control it */
770   if (G_UNLIKELY (g_list_find_custom (timeline->tracks, (gconstpointer) track,
771               (GCompareFunc) custom_find_track))) {
772     GST_WARNING ("Track is already controlled by this timeline");
773     return FALSE;
774   }
775
776   /* Add the track to ourself (as a GstBin) 
777    * Reference is stolen ! */
778   if (G_UNLIKELY (!gst_bin_add (GST_BIN (timeline), GST_ELEMENT (track)))) {
779     GST_WARNING ("Couldn't add track to ourself (GST)");
780     return FALSE;
781   }
782
783   priv = g_new0 (TrackPrivate, 1);
784   priv->timeline = timeline;
785   priv->track = track;
786
787   /* Add the track to the list of tracks we track */
788   timeline->tracks = g_list_append (timeline->tracks, priv);
789
790   /* Listen to pad-added/-removed */
791   g_signal_connect (track, "pad-added", (GCallback) pad_added_cb, priv);
792   g_signal_connect (track, "pad-removed", (GCallback) pad_removed_cb, priv);
793
794   /* Inform the track that it's currently being used by ourself */
795   ges_track_set_timeline (track, timeline);
796
797   GST_DEBUG ("Done adding track, emitting 'track-added' signal");
798
799   /* emit 'track-added' */
800   g_signal_emit (timeline, ges_timeline_signals[TRACK_ADDED], 0, track);
801
802   /* ensure that each existing timeline object has the opportunity to create a
803    * track object for this track*/
804
805   for (tmp = timeline->layers; tmp; tmp = tmp->next) {
806     GList *objects, *obj;
807     objects = ges_timeline_layer_get_objects (tmp->data);
808
809     for (obj = objects; obj; obj = obj->next) {
810       add_object_to_track (obj->data, track);
811       g_object_unref (obj->data);
812       obj->data = NULL;
813     }
814     g_list_free (objects);
815   }
816
817   return TRUE;
818 }
819
820 /**
821  * ges_timeline_remove_track:
822  * @timeline: a #GESTimeline
823  * @track: the #GESTrack to remove
824  *
825  * Remove the @track from the @timeline. The reference stolen when adding the
826  * @track will be removed. If you wish to use the @track after calling this
827  * function you must ensure that you have a reference to it.
828  *
829  * Returns: TRUE if the @track was properly removed, else FALSE.
830  */
831
832 /* FIXME: release any track objects associated with this layer. currenly this
833  * will not happen if you remove the track before removing *all*
834  * timelineobjects which have a track object in this track.
835  */
836
837 gboolean
838 ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track)
839 {
840   GList *tmp;
841   TrackPrivate *priv;
842
843   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
844
845   if (G_UNLIKELY (!(tmp =
846               g_list_find_custom (timeline->tracks, (gconstpointer) track,
847                   (GCompareFunc) custom_find_track)))) {
848     GST_WARNING ("Track doesn't belong to this timeline");
849     return FALSE;
850   }
851
852   priv = tmp->data;
853   timeline->tracks = g_list_remove (timeline->tracks, priv);
854
855   ges_track_set_timeline (track, NULL);
856
857   /* Remove ghost pad */
858   if (priv->ghostpad) {
859     GST_DEBUG ("Removing ghostpad");
860     gst_pad_set_active (priv->ghostpad, FALSE);
861     gst_ghost_pad_set_target ((GstGhostPad *) priv->ghostpad, NULL);
862     gst_element_remove_pad (GST_ELEMENT (timeline), priv->ghostpad);
863   }
864
865   /* Remove pad-added/-removed handlers */
866   g_signal_handlers_disconnect_by_func (track, pad_added_cb, priv);
867   g_signal_handlers_disconnect_by_func (track, pad_removed_cb, priv);
868
869   /* Signal track removal to all layers/objects */
870   g_signal_emit (timeline, ges_timeline_signals[TRACK_REMOVED], 0, track);
871
872   /* remove track from our bin */
873   gst_object_ref (track);
874   if (G_UNLIKELY (!gst_bin_remove (GST_BIN (timeline), GST_ELEMENT (track)))) {
875     GST_WARNING ("Couldn't remove track to ourself (GST)");
876     gst_object_unref (track);
877     return FALSE;
878   }
879
880   /* set track state to NULL */
881
882   gst_element_set_state (GST_ELEMENT (track), GST_STATE_NULL);
883
884   gst_object_unref (track);
885
886   g_free (priv);
887
888   return TRUE;
889 }
890
891 /**
892  * ges_timeline_get_track_for_pad:
893  * @timeline: The #GESTimeline
894  * @pad: The #GstPad
895  *
896  * Search the #GESTrack corresponding to the given @timeline's @pad.
897  *
898  * Returns: The corresponding #GESTrack if it is found, or #NULL if there is
899  * an error.
900  */
901
902 GESTrack *
903 ges_timeline_get_track_for_pad (GESTimeline * timeline, GstPad * pad)
904 {
905   GList *tmp;
906
907   for (tmp = timeline->tracks; tmp; tmp = g_list_next (tmp)) {
908     TrackPrivate *priv = (TrackPrivate *) tmp->data;
909     if (pad == priv->ghostpad)
910       return priv->track;
911   }
912
913   return NULL;
914 }
915
916 /**
917  * ges_timeline_get_tracks:
918  * @timeline: a #GESTimeline
919  *
920  * Returns the list of #GESTrack used by the Timeline.
921  *
922  * Returns: A list of #GESTrack. The caller should unref each track
923  * once he is done with them.
924  */
925 GList *
926 ges_timeline_get_tracks (GESTimeline * timeline)
927 {
928   GList *tmp, *res = NULL;
929
930   for (tmp = timeline->tracks; tmp; tmp = g_list_next (tmp)) {
931     TrackPrivate *priv = (TrackPrivate *) tmp->data;
932     res = g_list_append (res, g_object_ref (priv->track));
933   }
934
935   return res;
936 }