GESTimelineLayer: Subclass from GInitiallyUnowned
[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, *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     GST_DEBUG ("Trying to remove TrackObject %p", trobj);
566     if (G_LIKELY (g_list_find_custom (timeline->tracks,
567                 ges_track_object_get_track (trobj),
568                 (GCompareFunc) custom_find_track))) {
569       GST_DEBUG ("Belongs to one of the tracks we control");
570       ges_track_remove_object (ges_track_object_get_track (trobj), trobj);
571
572       ges_timeline_object_release_track_object (object, trobj);
573     }
574
575     g_object_unref (GES_TRACK_OBJECT (tmp->data));
576   }
577   g_list_free (trackobjects);
578
579   GST_DEBUG ("Done");
580 }
581
582 /**
583  * ges_timeline_add_layer:
584  * @timeline: a #GESTimeline
585  * @layer: the #GESTimelineLayer to add
586  *
587  * Add the layer to the timeline. The reference to the @layer will be stolen
588  * by the @timeline.
589  *
590  * Returns: TRUE if the layer was properly added, else FALSE.
591  */
592 gboolean
593 ges_timeline_add_layer (GESTimeline * timeline, GESTimelineLayer * layer)
594 {
595   GList *objects, *tmp;
596
597   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
598
599   /* We can only add a layer that doesn't already belong to another timeline */
600   if (G_UNLIKELY (layer->timeline)) {
601     GST_WARNING ("Layer belongs to another timeline, can't add it");
602     return FALSE;
603   }
604
605   /* Add to the list of layers, make sure we don't already control it */
606   if (G_UNLIKELY (g_list_find (timeline->layers, (gconstpointer) layer))) {
607     GST_WARNING ("Layer is already controlled by this timeline");
608     return FALSE;
609   }
610
611   g_object_ref_sink (layer);
612   timeline->layers = g_list_append (timeline->layers, layer);
613
614   /* Inform the layer that it belongs to a new timeline */
615   ges_timeline_layer_set_timeline (layer, timeline);
616
617   /* Connect to 'object-added'/'object-removed' signal from the new layer */
618   g_signal_connect (layer, "object-added", G_CALLBACK (layer_object_added_cb),
619       timeline);
620   g_signal_connect (layer, "object-removed",
621       G_CALLBACK (layer_object_removed_cb), timeline);
622
623   GST_DEBUG ("Done adding layer, emitting 'layer-added' signal");
624   g_signal_emit (timeline, ges_timeline_signals[LAYER_ADDED], 0, layer);
625
626   /* add any existing timeline objects to the timeline */
627   objects = ges_timeline_layer_get_objects (layer);
628   for (tmp = objects; tmp; tmp = tmp->next) {
629     layer_object_added_cb (layer, tmp->data, timeline);
630     g_object_unref (tmp->data);
631     tmp->data = NULL;
632   }
633   g_list_free (objects);
634
635   return TRUE;
636 }
637
638 /**
639  * ges_timeline_remove_layer:
640  * @timeline: a #GESTimeline
641  * @layer: the #GESTimelineLayer to remove
642  *
643  * Removes the layer from the timeline. The reference that the @timeline holds on
644  * the layer will be dropped. If you wish to use the @layer after calling this
645  * method, you need to take a reference before calling.
646  *
647  * Returns: TRUE if the layer was properly removed, else FALSE.
648  */
649
650 gboolean
651 ges_timeline_remove_layer (GESTimeline * timeline, GESTimelineLayer * layer)
652 {
653   GList *layer_objects, *tmp;
654
655   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
656
657   if (G_UNLIKELY (!g_list_find (timeline->layers, layer))) {
658     GST_WARNING ("Layer doesn't belong to this timeline");
659     return FALSE;
660   }
661
662   /* remove objects from any private data structures */
663
664   layer_objects = ges_timeline_layer_get_objects (layer);
665   for (tmp = layer_objects; tmp; tmp = tmp->next) {
666     layer_object_removed_cb (layer, GES_TIMELINE_OBJECT (tmp->data), timeline);
667     g_object_unref (G_OBJECT (tmp->data));
668     tmp->data = NULL;
669   }
670   g_list_free (layer_objects);
671
672   /* Disconnect signals */
673   GST_DEBUG ("Disconnecting signal callbacks");
674   g_signal_handlers_disconnect_by_func (layer, layer_object_added_cb, timeline);
675   g_signal_handlers_disconnect_by_func (layer, layer_object_removed_cb,
676       timeline);
677
678   timeline->layers = g_list_remove (timeline->layers, layer);
679
680   ges_timeline_layer_set_timeline (layer, NULL);
681
682   g_signal_emit (timeline, ges_timeline_signals[LAYER_REMOVED], 0, layer);
683
684   g_object_unref (layer);
685
686   return TRUE;
687 }
688
689 static void
690 pad_added_cb (GESTrack * track, GstPad * pad, TrackPrivate * priv)
691 {
692   gchar *padname;
693
694
695   GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
696
697   if (G_UNLIKELY (priv->pad)) {
698     GST_WARNING ("We are already controlling a pad for this track");
699     return;
700   }
701
702   /* Remember the pad */
703   priv->pad = pad;
704
705   /* ghost it ! */
706   GST_DEBUG ("Ghosting pad and adding it to ourself");
707   padname = g_strdup_printf ("track_%p_src", track);
708   priv->ghostpad = gst_ghost_pad_new (padname, pad);
709   g_free (padname);
710   gst_pad_set_active (priv->ghostpad, TRUE);
711   gst_element_add_pad (GST_ELEMENT (priv->timeline), priv->ghostpad);
712 }
713
714 static void
715 pad_removed_cb (GESTrack * track, GstPad * pad, TrackPrivate * priv)
716 {
717   GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
718
719   if (G_UNLIKELY (priv->pad != pad)) {
720     GST_WARNING ("Not the pad we're controlling");
721     return;
722   }
723
724   if (G_UNLIKELY (priv->ghostpad == NULL)) {
725     GST_WARNING ("We don't have a ghostpad for this pad !");
726     return;
727   }
728
729   GST_DEBUG ("Removing ghostpad");
730   gst_pad_set_active (priv->ghostpad, FALSE);
731   gst_element_remove_pad (GST_ELEMENT (priv->timeline), priv->ghostpad);
732   priv->ghostpad = NULL;
733   priv->pad = NULL;
734 }
735
736 gint
737 custom_find_track (TrackPrivate * priv, GESTrack * track)
738 {
739   if (priv->track == track)
740     return 0;
741   return -1;
742 }
743
744 /**
745  * ges_timeline_add_track:
746  * @timeline: a #GESTimeline
747  * @track: the #GESTrack to add
748  *
749  * Add a track to the timeline. The reference to the track will be stolen by the
750  * pipeline.
751  *
752  * Returns: TRUE if the track was properly added, else FALSE.
753  */
754
755 /* FIXME: create track objects for timeline objects which have already been
756  * added to existing layers.
757  */
758
759 gboolean
760 ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
761 {
762   TrackPrivate *priv;
763   GList *tmp;
764
765   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
766
767   /* make sure we don't already control it */
768   if (G_UNLIKELY (g_list_find_custom (timeline->tracks, (gconstpointer) track,
769               (GCompareFunc) custom_find_track))) {
770     GST_WARNING ("Track is already controlled by this timeline");
771     return FALSE;
772   }
773
774   /* Add the track to ourself (as a GstBin) 
775    * Reference is stolen ! */
776   if (G_UNLIKELY (!gst_bin_add (GST_BIN (timeline), GST_ELEMENT (track)))) {
777     GST_WARNING ("Couldn't add track to ourself (GST)");
778     return FALSE;
779   }
780
781   priv = g_new0 (TrackPrivate, 1);
782   priv->timeline = timeline;
783   priv->track = track;
784
785   /* Add the track to the list of tracks we track */
786   timeline->tracks = g_list_append (timeline->tracks, priv);
787
788   /* Listen to pad-added/-removed */
789   g_signal_connect (track, "pad-added", (GCallback) pad_added_cb, priv);
790   g_signal_connect (track, "pad-removed", (GCallback) pad_removed_cb, priv);
791
792   /* Inform the track that it's currently being used by ourself */
793   ges_track_set_timeline (track, timeline);
794
795   GST_DEBUG ("Done adding track, emitting 'track-added' signal");
796
797   /* emit 'track-added' */
798   g_signal_emit (timeline, ges_timeline_signals[TRACK_ADDED], 0, track);
799
800   /* ensure that each existing timeline object has the opportunity to create a
801    * track object for this track*/
802
803   for (tmp = timeline->layers; tmp; tmp = tmp->next) {
804     GList *objects, *obj;
805     objects = ges_timeline_layer_get_objects (tmp->data);
806
807     for (obj = objects; obj; obj = obj->next) {
808       add_object_to_track (obj->data, track);
809       g_object_unref (obj->data);
810       obj->data = NULL;
811     }
812     g_list_free (objects);
813   }
814
815   return TRUE;
816 }
817
818 /**
819  * ges_timeline_remove_track:
820  * @timeline: a #GESTimeline
821  * @track: the #GESTrack to remove
822  *
823  * Remove the @track from the @timeline. The reference stolen when adding the
824  * @track will be removed. If you wish to use the @track after calling this
825  * function you must ensure that you have a reference to it.
826  *
827  * Returns: TRUE if the @track was properly removed, else FALSE.
828  */
829
830 /* FIXME: release any track objects associated with this layer. currenly this
831  * will not happen if you remove the track before removing *all*
832  * timelineobjects which have a track object in this track.
833  */
834
835 gboolean
836 ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track)
837 {
838   GList *tmp;
839   TrackPrivate *priv;
840
841   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
842
843   if (G_UNLIKELY (!(tmp =
844               g_list_find_custom (timeline->tracks, (gconstpointer) track,
845                   (GCompareFunc) custom_find_track)))) {
846     GST_WARNING ("Track doesn't belong to this timeline");
847     return FALSE;
848   }
849
850   priv = tmp->data;
851   timeline->tracks = g_list_remove (timeline->tracks, priv);
852
853   ges_track_set_timeline (track, NULL);
854
855   /* Remove ghost pad */
856   if (priv->ghostpad) {
857     GST_DEBUG ("Removing ghostpad");
858     gst_pad_set_active (priv->ghostpad, FALSE);
859     gst_ghost_pad_set_target ((GstGhostPad *) priv->ghostpad, NULL);
860     gst_element_remove_pad (GST_ELEMENT (timeline), priv->ghostpad);
861   }
862
863   /* Remove pad-added/-removed handlers */
864   g_signal_handlers_disconnect_by_func (track, pad_added_cb, priv);
865   g_signal_handlers_disconnect_by_func (track, pad_removed_cb, priv);
866
867   /* Signal track removal to all layers/objects */
868   g_signal_emit (timeline, ges_timeline_signals[TRACK_REMOVED], 0, track);
869
870   /* remove track from our bin */
871   gst_object_ref (track);
872   if (G_UNLIKELY (!gst_bin_remove (GST_BIN (timeline), GST_ELEMENT (track)))) {
873     GST_WARNING ("Couldn't remove track to ourself (GST)");
874     gst_object_unref (track);
875     return FALSE;
876   }
877
878   /* set track state to NULL */
879
880   gst_element_set_state (GST_ELEMENT (track), GST_STATE_NULL);
881
882   gst_object_unref (track);
883
884   g_free (priv);
885
886   return TRUE;
887 }
888
889 /**
890  * ges_timeline_get_track_for_pad:
891  * @timeline: The #GESTimeline
892  * @pad: The #GstPad
893  *
894  * Search the #GESTrack corresponding to the given @timeline's @pad.
895  *
896  * Returns: (transfer none): The corresponding #GESTrack if it is found,
897  * or #NULL if there is an error.
898  */
899
900 GESTrack *
901 ges_timeline_get_track_for_pad (GESTimeline * timeline, GstPad * pad)
902 {
903   GList *tmp;
904
905   for (tmp = timeline->tracks; tmp; tmp = g_list_next (tmp)) {
906     TrackPrivate *priv = (TrackPrivate *) tmp->data;
907     if (pad == priv->ghostpad)
908       return priv->track;
909   }
910
911   return NULL;
912 }
913
914 /**
915  * ges_timeline_get_tracks:
916  * @timeline: a #GESTimeline
917  *
918  * Returns the list of #GESTrack used by the Timeline.
919  *
920  * Returns: (transfer full) (element-type GESTrack): A list of #GESTrack.
921  * The caller should unref each track once he is done with them.
922  */
923 GList *
924 ges_timeline_get_tracks (GESTimeline * timeline)
925 {
926   GList *tmp, *res = NULL;
927
928   for (tmp = timeline->tracks; tmp; tmp = g_list_next (tmp)) {
929     TrackPrivate *priv = (TrackPrivate *) tmp->data;
930     res = g_list_append (res, g_object_ref (priv->track));
931   }
932
933   return res;
934 }