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