Merge branch '0.10'
[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 "ges-internal.h"
40 #include "ges-timeline.h"
41 #include "ges-track.h"
42 #include "ges-timeline-layer.h"
43 #include "ges.h"
44
45 static void track_duration_cb (GstElement * track,
46     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline);
47
48 G_DEFINE_TYPE (GESTimeline, ges_timeline, GST_TYPE_BIN);
49
50 #define GES_TIMELINE_PENDINGOBJS_GET_LOCK(timeline) \
51   (GES_TIMELINE(timeline)->priv->pendingobjects_lock)
52 #define GES_TIMELINE_PENDINGOBJS_LOCK(timeline) \
53   (g_mutex_lock(GES_TIMELINE_PENDINGOBJS_GET_LOCK (timeline)))
54 #define GES_TIMELINE_PENDINGOBJS_UNLOCK(timeline) \
55   (g_mutex_unlock(GES_TIMELINE_PENDINGOBJS_GET_LOCK (timeline)))
56
57 struct _GESTimelinePrivate
58 {
59   GList *layers;                /* A list of GESTimelineLayer sorted by priority */
60   GList *tracks;                /* A list of private track data */
61
62   /* The duration of the timeline */
63   gint64 duration;
64
65   /* discoverer used for virgin sources */
66   GstDiscoverer *discoverer;
67   GList *pendingobjects;
68   /* lock to avoid discovery of objects that will be removed */
69   GMutex *pendingobjects_lock;
70
71   /* Whether we are changing state asynchronously or not */
72   gboolean async_pending;
73 };
74
75 /* private structure to contain our track-related information */
76
77 typedef struct
78 {
79   GESTimeline *timeline;
80   GESTrack *track;
81   GstPad *pad;                  /* Pad from the track */
82   GstPad *ghostpad;
83 } TrackPrivate;
84
85 enum
86 {
87   PROP_0,
88   PROP_DURATION,
89   PROP_LAST
90 };
91
92 static GParamSpec *properties[PROP_LAST];
93
94 enum
95 {
96   TRACK_ADDED,
97   TRACK_REMOVED,
98   LAYER_ADDED,
99   LAYER_REMOVED,
100   DISCOVERY_ERROR,
101   LAST_SIGNAL
102 };
103
104 static GstBinClass *parent_class;
105
106 static guint ges_timeline_signals[LAST_SIGNAL] = { 0 };
107
108 static gint custom_find_track (TrackPrivate * tr_priv, GESTrack * track);
109 static GstStateChangeReturn
110 ges_timeline_change_state (GstElement * element, GstStateChange transition);
111 static void
112 discoverer_finished_cb (GstDiscoverer * discoverer, GESTimeline * timeline);
113 static void
114 discoverer_discovered_cb (GstDiscoverer * discoverer,
115     GstDiscovererInfo * info, GError * err, GESTimeline * timeline);
116
117 /* GObject Standard vmethods*/
118 static void
119 ges_timeline_get_property (GObject * object, guint property_id,
120     GValue * value, GParamSpec * pspec)
121 {
122   GESTimeline *timeline = GES_TIMELINE (object);
123
124   switch (property_id) {
125     default:
126       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
127     case PROP_DURATION:
128       g_value_set_uint64 (value, timeline->priv->duration);
129       break;
130   }
131 }
132
133 static void
134 ges_timeline_set_property (GObject * object, guint property_id,
135     const GValue * value, GParamSpec * pspec)
136 {
137   switch (property_id) {
138     default:
139       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
140   }
141 }
142
143 static void
144 ges_timeline_dispose (GObject * object)
145 {
146   GESTimelinePrivate *priv = GES_TIMELINE (object)->priv;
147
148   if (priv->discoverer) {
149     gst_discoverer_stop (priv->discoverer);
150     g_object_unref (priv->discoverer);
151     priv->discoverer = NULL;
152   }
153
154   while (priv->layers) {
155     GESTimelineLayer *layer = (GESTimelineLayer *) priv->layers->data;
156     ges_timeline_remove_layer (GES_TIMELINE (object), layer);
157   }
158
159   /* FIXME: it should be possible to remove tracks before removing
160    * layers, but at the moment this creates a problem because the track
161    * objects aren't notified that their gnlobjects have been destroyed.
162    */
163
164   while (priv->tracks) {
165     TrackPrivate *tr_priv = (TrackPrivate *) priv->tracks->data;
166     ges_timeline_remove_track (GES_TIMELINE (object), tr_priv->track);
167   }
168
169   G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
170 }
171
172 static void
173 ges_timeline_finalize (GObject * object)
174 {
175   GESTimeline *timeline = GES_TIMELINE (object);
176
177   g_mutex_free (timeline->priv->pendingobjects_lock);
178
179   G_OBJECT_CLASS (ges_timeline_parent_class)->finalize (object);
180 }
181
182 static void
183 ges_timeline_class_init (GESTimelineClass * klass)
184 {
185   GObjectClass *object_class = G_OBJECT_CLASS (klass);
186   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
187
188   g_type_class_add_private (klass, sizeof (GESTimelinePrivate));
189
190   parent_class = g_type_class_peek_parent (klass);
191
192   element_class->change_state = ges_timeline_change_state;
193
194   object_class->get_property = ges_timeline_get_property;
195   object_class->set_property = ges_timeline_set_property;
196   object_class->dispose = ges_timeline_dispose;
197   object_class->finalize = ges_timeline_finalize;
198
199   /**
200    * GESTimelineObject:duration
201    *
202    * Current duration (in nanoseconds) of the #GESTimeline
203    *
204    * Default value: 0
205    */
206   properties[PROP_DURATION] =
207       g_param_spec_uint64 ("duration", "Duration",
208       "The duration of the timeline", 0, G_MAXUINT64,
209       GST_CLOCK_TIME_NONE, G_PARAM_READABLE);
210   g_object_class_install_property (object_class, PROP_DURATION,
211       properties[PROP_DURATION]);
212
213   /**
214    * GESTimeline::track-added
215    * @timeline: the #GESTimeline
216    * @track: the #GESTrack that was added to the timeline
217    *
218    * Will be emitted after the track was added to the timeline.
219    */
220   ges_timeline_signals[TRACK_ADDED] =
221       g_signal_new ("track-added", G_TYPE_FROM_CLASS (klass),
222       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_added), NULL,
223       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_TRACK);
224
225   /**
226    * GESTimeline::track-removed
227    * @timeline: the #GESTimeline
228    * @track: the #GESTrack that was removed from the timeline
229    *
230    * Will be emitted after the track was removed from the timeline.
231    */
232   ges_timeline_signals[TRACK_REMOVED] =
233       g_signal_new ("track-removed", G_TYPE_FROM_CLASS (klass),
234       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_removed),
235       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_TRACK);
236
237   /**
238    * GESTimeline::layer-added
239    * @timeline: the #GESTimeline
240    * @layer: the #GESTimelineLayer that was added to the timeline
241    *
242    * Will be emitted after the layer was added to the timeline.
243    */
244   ges_timeline_signals[LAYER_ADDED] =
245       g_signal_new ("layer-added", G_TYPE_FROM_CLASS (klass),
246       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_added), NULL,
247       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
248       GES_TYPE_TIMELINE_LAYER);
249
250   /**
251    * GESTimeline::layer-removed
252    * @timeline: the #GESTimeline
253    * @layer: the #GESTimelineLayer that was removed from the timeline
254    *
255    * Will be emitted after the layer was removed from the timeline.
256    */
257   ges_timeline_signals[LAYER_REMOVED] =
258       g_signal_new ("layer-removed", G_TYPE_FROM_CLASS (klass),
259       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_removed),
260       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
261       GES_TYPE_TIMELINE_LAYER);
262
263   /**
264    * GESTimeline::discovery-error:
265    * @formatter: the #GESFormatter
266    * @source: The #GESTimelineFileSource that could not be discovered properly
267    * @error: (type GLib.Error): #GError, which will be non-NULL if an error
268    *                            occurred during discovery
269    */
270   ges_timeline_signals[DISCOVERY_ERROR] =
271       g_signal_new ("discovery-error", G_TYPE_FROM_CLASS (klass),
272       G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_generic,
273       G_TYPE_NONE, 2, GES_TYPE_TIMELINE_FILE_SOURCE, G_TYPE_ERROR);
274 }
275
276 static void
277 ges_timeline_init (GESTimeline * self)
278 {
279
280   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
281       GES_TYPE_TIMELINE, GESTimelinePrivate);
282
283   self->priv->layers = NULL;
284   self->priv->tracks = NULL;
285   self->priv->duration = 0;
286
287   self->priv->pendingobjects_lock = g_mutex_new ();
288   /* New discoverer with a 15s timeout */
289   self->priv->discoverer = gst_discoverer_new (15 * GST_SECOND, NULL);
290   g_signal_connect (self->priv->discoverer, "finished",
291       G_CALLBACK (discoverer_finished_cb), self);
292   g_signal_connect (self->priv->discoverer, "discovered",
293       G_CALLBACK (discoverer_discovered_cb), self);
294   gst_discoverer_start (self->priv->discoverer);
295 }
296
297 /* Private methods */
298
299 /* Sorting utils*/
300 static gint
301 sort_layers (gpointer a, gpointer b)
302 {
303   GESTimelineLayer *layer_a, *layer_b;
304   guint prio_a, prio_b;
305
306   layer_a = GES_TIMELINE_LAYER (a);
307   layer_b = GES_TIMELINE_LAYER (b);
308
309   prio_a = ges_timeline_layer_get_priority (layer_a);
310   prio_b = ges_timeline_layer_get_priority (layer_b);
311
312   if ((gint) prio_a > (guint) prio_b)
313     return 1;
314   if ((guint) prio_a < (guint) prio_b)
315     return -1;
316
317   return 0;
318 }
319
320 static gint
321 custom_find_track (TrackPrivate * tr_priv, GESTrack * track)
322 {
323   if (tr_priv->track == track)
324     return 0;
325   return -1;
326 }
327
328 static void
329 add_object_to_track (GESTimelineObject * object, GESTrack * track)
330 {
331   if (!ges_timeline_object_create_track_objects (object, track)) {
332     if ((track->type & ges_timeline_object_get_supported_formats (object))) {
333       GST_WARNING ("Error creating track objects");
334     }
335   }
336 }
337
338 static void
339 add_object_to_tracks (GESTimeline * timeline, GESTimelineObject * object)
340 {
341   GList *tmp;
342
343   for (tmp = timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
344     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
345     GESTrack *track = tr_priv->track;
346
347     GST_LOG ("Trying with track %p", track);
348     add_object_to_track (object, track);
349   }
350 }
351
352
353 static void
354 do_async_start (GESTimeline * timeline)
355 {
356   GstMessage *message;
357   GList *tmp;
358
359   timeline->priv->async_pending = TRUE;
360
361   /* Freeze state of tracks */
362   for (tmp = timeline->priv->tracks; tmp; tmp = tmp->next) {
363     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
364     gst_element_set_locked_state ((GstElement *) tr_priv->track, TRUE);
365   }
366
367   message = gst_message_new_async_start (GST_OBJECT_CAST (timeline));
368   parent_class->handle_message (GST_BIN_CAST (timeline), message);
369 }
370
371 static void
372 do_async_done (GESTimeline * timeline)
373 {
374   GstMessage *message;
375
376   if (timeline->priv->async_pending) {
377     GList *tmp;
378     /* Unfreeze state of tracks */
379     for (tmp = timeline->priv->tracks; tmp; tmp = tmp->next) {
380       TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
381       gst_element_set_locked_state ((GstElement *) tr_priv->track, FALSE);
382       gst_element_sync_state_with_parent ((GstElement *) tr_priv->track);
383     }
384
385     GST_DEBUG_OBJECT (timeline, "Emitting async-done");
386     message = gst_message_new_async_done (GST_OBJECT_CAST (timeline), FALSE);
387     parent_class->handle_message (GST_BIN_CAST (timeline), message);
388
389     timeline->priv->async_pending = FALSE;
390   }
391 }
392
393 /* Callbacks  */
394 static void
395 discoverer_finished_cb (GstDiscoverer * discoverer, GESTimeline * timeline)
396 {
397   do_async_done (timeline);
398 }
399
400 static void
401 discoverer_discovered_cb (GstDiscoverer * discoverer,
402     GstDiscovererInfo * info, GError * err, GESTimeline * timeline)
403 {
404   GList *tmp;
405   GList *stream_list;
406   GESTrackType tfs_supportedformats;
407
408   gboolean found = FALSE;
409   gboolean is_image = FALSE;
410   GESTimelineFileSource *tfs = NULL;
411   GESTimelinePrivate *priv = timeline->priv;
412   const gchar *uri = gst_discoverer_info_get_uri (info);
413
414   GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
415
416   /* Find corresponding TimelineFileSource in the sources */
417   for (tmp = priv->pendingobjects; tmp; tmp = tmp->next) {
418     tfs = (GESTimelineFileSource *) tmp->data;
419
420     if (!g_strcmp0 (ges_timeline_filesource_get_uri (tfs), uri)) {
421       found = TRUE;
422       break;
423     }
424   }
425
426   if (!found) {
427     GST_WARNING ("Discovered %s, that seems not to be in the list of sources"
428         "to discover", uri);
429     GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
430     return;
431   }
432
433   if (err) {
434     GError *propagate_error = NULL;
435
436     priv->pendingobjects = g_list_delete_link (priv->pendingobjects, tmp);
437     GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
438     GST_WARNING ("Error while discovering %s: %s", uri, err->message);
439
440     g_propagate_error (&propagate_error, err);
441     g_signal_emit (timeline, ges_timeline_signals[DISCOVERY_ERROR], 0, tfs,
442         propagate_error);
443
444     return;
445   }
446
447   /* Everything went fine... let's do our job! */
448   GST_DEBUG ("Discovered uri %s", uri);
449
450   /* The timeline file source will be updated with discovered information
451    * so it needs to not be finalized during this process */
452   g_object_ref (tfs);
453
454   /* Remove object from list */
455   priv->pendingobjects = g_list_delete_link (priv->pendingobjects, tmp);
456   GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
457
458   /* FIXME : Handle errors in discovery */
459   stream_list = gst_discoverer_info_get_stream_list (info);
460
461   tfs_supportedformats = ges_timeline_filesource_get_supported_formats (tfs);
462   if (tfs_supportedformats != GES_TRACK_TYPE_UNKNOWN)
463     goto check_image;
464
465   /* Update timelinefilesource properties based on info */
466   for (tmp = stream_list; tmp; tmp = tmp->next) {
467     GstDiscovererStreamInfo *sinf = (GstDiscovererStreamInfo *) tmp->data;
468
469     if (GST_IS_DISCOVERER_AUDIO_INFO (sinf)) {
470       tfs_supportedformats |= GES_TRACK_TYPE_AUDIO;
471       ges_timeline_filesource_set_supported_formats (tfs, tfs_supportedformats);
472     } else if (GST_IS_DISCOVERER_VIDEO_INFO (sinf)) {
473       tfs_supportedformats |= GES_TRACK_TYPE_VIDEO;
474       ges_timeline_filesource_set_supported_formats (tfs, tfs_supportedformats);
475       if (gst_discoverer_video_info_is_image ((GstDiscovererVideoInfo *)
476               sinf)) {
477         tfs_supportedformats |= GES_TRACK_TYPE_AUDIO;
478         ges_timeline_filesource_set_supported_formats (tfs,
479             tfs_supportedformats);
480         is_image = TRUE;
481       }
482     }
483   }
484
485   if (stream_list)
486     gst_discoverer_stream_info_list_free (stream_list);
487
488 check_image:
489
490   if (is_image) {
491     /* don't set max-duration on still images */
492     g_object_set (tfs, "is_image", (gboolean) TRUE, NULL);
493   }
494
495   /* Continue the processing on tfs */
496   add_object_to_tracks (timeline, GES_TIMELINE_OBJECT (tfs));
497
498   if (!is_image) {
499     g_object_set (tfs, "max-duration",
500         gst_discoverer_info_get_duration (info), NULL);
501   }
502
503   /* Remove the ref as the timeline file source is no longer needed here */
504   g_object_unref (tfs);
505 }
506
507 static void
508 layer_object_added_cb (GESTimelineLayer * layer, GESTimelineObject * object,
509     GESTimeline * timeline)
510 {
511   if (ges_timeline_object_is_moving_from_layer (object)) {
512     GST_DEBUG ("TimelineObject %p is moving from a layer to another, not doing"
513         " anything on it", object);
514     return;
515   }
516
517   GST_DEBUG ("New TimelineObject %p added to layer %p", object, layer);
518
519   if (GES_IS_TIMELINE_FILE_SOURCE (object)) {
520     GESTimelineFileSource *tfs = GES_TIMELINE_FILE_SOURCE (object);
521     GESTrackType tfs_supportedformats =
522         ges_timeline_filesource_get_supported_formats (tfs);
523     guint64 tfs_maxdur = ges_timeline_filesource_get_max_duration (tfs);
524     const gchar *tfs_uri;
525
526     /* Send the filesource to the discoverer if:
527      * * it doesn't have specified supported formats
528      * * OR it doesn't have a specified max-duration
529      * * OR it doesn't have a valid duration  */
530
531     if (tfs_supportedformats == GES_TRACK_TYPE_UNKNOWN ||
532         tfs_maxdur == GST_CLOCK_TIME_NONE || object->duration == 0) {
533       GST_LOG ("Incomplete TimelineFileSource, discovering it");
534       tfs_uri = ges_timeline_filesource_get_uri (tfs);
535
536       GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
537       timeline->priv->pendingobjects =
538           g_list_append (timeline->priv->pendingobjects, object);
539       GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
540
541       gst_discoverer_discover_uri_async (timeline->priv->discoverer, tfs_uri);
542     } else
543       add_object_to_tracks (timeline, object);
544   } else {
545     add_object_to_tracks (timeline, object);
546   }
547
548   GST_DEBUG ("done");
549 }
550
551 static void
552 layer_priority_changed_cb (GESTimelineLayer * layer,
553     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
554 {
555   timeline->priv->layers = g_list_sort (timeline->priv->layers, (GCompareFunc)
556       sort_layers);
557 }
558
559 static void
560 layer_object_removed_cb (GESTimelineLayer * layer, GESTimelineObject * object,
561     GESTimeline * timeline)
562 {
563   GList *trackobjects, *tmp;
564
565   if (ges_timeline_object_is_moving_from_layer (object)) {
566     GST_DEBUG ("TimelineObject %p is moving from a layer to another, not doing"
567         " anything on it", object);
568     return;
569   }
570
571   GST_DEBUG ("TimelineObject %p removed from layer %p", object, layer);
572
573   /* Go over the object's track objects and figure out which one belongs to
574    * the list of tracks we control */
575
576   trackobjects = ges_timeline_object_get_track_objects (object);
577   for (tmp = trackobjects; tmp; tmp = tmp->next) {
578     GESTrackObject *trobj = (GESTrackObject *) tmp->data;
579
580     GST_DEBUG ("Trying to remove TrackObject %p", trobj);
581     if (G_LIKELY (g_list_find_custom (timeline->priv->tracks,
582                 ges_track_object_get_track (trobj),
583                 (GCompareFunc) custom_find_track))) {
584       GST_DEBUG ("Belongs to one of the tracks we control");
585       ges_track_remove_object (ges_track_object_get_track (trobj), trobj);
586
587       ges_timeline_object_release_track_object (object, trobj);
588     }
589     /* removing the reference added by _get_track_objects() */
590     g_object_unref (trobj);
591   }
592
593   g_list_free (trackobjects);
594
595   /* if the object is a timeline file source that has not yet been discovered,
596    * it no longer needs to be discovered so remove it from the pendingobjects
597    * list if it belongs to this layer */
598   if (GES_IS_TIMELINE_FILE_SOURCE (object)) {
599     GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
600     timeline->priv->pendingobjects =
601         g_list_remove_all (timeline->priv->pendingobjects, object);
602     GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
603   }
604
605   GST_DEBUG ("Done");
606 }
607
608 static void
609 track_duration_cb (GstElement * track,
610     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
611 {
612   guint64 duration, max_duration = 0;
613   GList *tmp;
614
615   for (tmp = timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
616     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
617     g_object_get (tr_priv->track, "duration", &duration, NULL);
618     GST_DEBUG ("track duration : %" GST_TIME_FORMAT, GST_TIME_ARGS (duration));
619     max_duration = MAX (duration, max_duration);
620   }
621
622   if (timeline->priv->duration != max_duration) {
623     GST_DEBUG ("track duration : %" GST_TIME_FORMAT " current : %"
624         GST_TIME_FORMAT, GST_TIME_ARGS (max_duration),
625         GST_TIME_ARGS (timeline->priv->duration));
626
627     timeline->priv->duration = max_duration;
628
629 #if GLIB_CHECK_VERSION(2,26,0)
630     g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_DURATION]);
631 #else
632     g_object_notify (G_OBJECT (timeline), "duration");
633 #endif
634   }
635 }
636
637 static GstStateChangeReturn
638 ges_timeline_change_state (GstElement * element, GstStateChange transition)
639 {
640   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
641   GESTimeline *timeline = GES_TIMELINE (element);
642
643   switch (transition) {
644     case GST_STATE_CHANGE_READY_TO_PAUSED:
645       GES_TIMELINE_PENDINGOBJS_LOCK (timeline);
646       if (timeline->priv->pendingobjects) {
647         GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
648         do_async_start (timeline);
649         ret = GST_STATE_CHANGE_ASYNC;
650       } else {
651         GES_TIMELINE_PENDINGOBJS_UNLOCK (timeline);
652       }
653       break;
654     default:
655       break;
656   }
657
658   {
659     GstStateChangeReturn bret;
660
661     bret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
662     if (G_UNLIKELY (bret == GST_STATE_CHANGE_NO_PREROLL)) {
663       do_async_done (timeline);
664       ret = bret;
665     }
666   }
667
668   switch (transition) {
669     case GST_STATE_CHANGE_PAUSED_TO_READY:
670       do_async_done (timeline);
671       break;
672     default:
673       break;
674   }
675
676   return ret;
677
678 }
679
680 /**
681  * ges_timeline_new:
682  *
683  * Creates a new empty #GESTimeline.
684  *
685  * Returns: The new timeline.
686  */
687
688 GESTimeline *
689 ges_timeline_new (void)
690 {
691   return g_object_new (GES_TYPE_TIMELINE, NULL);
692 }
693
694 /**
695  * ges_timeline_new_from_uri:
696  * @uri: the URI to load from
697  *
698  * Creates a timeline from the given URI.
699  *
700  * Returns: A new timeline if the uri was loaded successfully, or NULL if the
701  * uri could not be loaded
702  */
703
704 GESTimeline *
705 ges_timeline_new_from_uri (const gchar * uri)
706 {
707   GESTimeline *ret;
708
709   /* FIXME : we should have a GError** argument so the user can know why
710    * it wasn't able to load the uri
711    */
712
713   ret = ges_timeline_new ();
714
715   if (!ges_timeline_load_from_uri (ret, uri)) {
716     g_object_unref (ret);
717     return NULL;
718   }
719
720   return ret;
721 }
722
723
724 /**
725  * ges_timeline_load_from_uri:
726  * @timeline: an empty #GESTimeline into which to load the formatter
727  * @uri: The URI to load from
728  *
729  * Loads the contents of URI into the given timeline.
730  *
731  * Returns: TRUE if the timeline was loaded successfully, or FALSE if the uri
732  * could not be loaded.
733  */
734
735 gboolean
736 ges_timeline_load_from_uri (GESTimeline * timeline, const gchar * uri)
737 {
738   GESFormatter *p = NULL;
739   gboolean ret = FALSE;
740
741   /* FIXME : we should have a GError** argument so the user can know why
742    * it wasn't able to load the uri
743    */
744
745   if (!(p = ges_formatter_new_for_uri (uri))) {
746     GST_ERROR ("unsupported uri '%s'", uri);
747     goto fail;
748   }
749
750   if (!ges_formatter_load_from_uri (p, timeline, uri)) {
751     GST_ERROR ("error deserializing formatter");
752     goto fail;
753   }
754
755   ret = TRUE;
756
757 fail:
758   if (p)
759     g_object_unref (p);
760   return ret;
761 }
762
763 /**
764  * ges_timeline_save_to_uri:
765  * @timeline: a #GESTimeline
766  * @uri: The location to save to
767  *
768  * Saves the timeline to the given location
769  *
770  * Returns: TRUE if the timeline was successfully saved to the given location,
771  * else FALSE.
772  */
773
774 gboolean
775 ges_timeline_save_to_uri (GESTimeline * timeline, const gchar * uri)
776 {
777   GESFormatter *p = NULL;
778   gboolean ret = FALSE;
779
780   /* FIXME : How will the user be able to chose the format he
781    * wishes to store to ? */
782
783   /* FIXME : How will we ensure a timeline loaded with a certain format
784    * will be saved with the same one by default ? We need to make this
785    * easy from an API perspective */
786
787   /* FIXME : we should have a GError** argument so the user can know why
788    * it wasn't able to save
789    */
790
791   if (!(p = ges_formatter_new_for_uri (uri))) {
792     GST_ERROR ("unsupported uri '%s'", uri);
793     goto fail;
794   }
795
796   if (!ges_formatter_save_to_uri (p, timeline, uri)) {
797     GST_ERROR ("error serializing formatter");
798     goto fail;
799   }
800
801   ret = TRUE;
802
803 fail:
804   if (p)
805     g_object_unref (p);
806   return ret;
807 }
808
809 /**
810  * ges_timeline_append_layer:
811  * @timeline: a #GESTimeline
812  *
813  * Append a newly creater #GESTimelineLayer to @timeline
814  * Note that you do not own any reference to the returned layer.
815  *
816  * Returns: (transfer none): The newly created #GESTimelineLayer, or the last (empty)
817  * #GESTimelineLayer of @timeline.
818  */
819 GESTimelineLayer *
820 ges_timeline_append_layer (GESTimeline * timeline)
821 {
822   guint32 priority;
823   GESTimelinePrivate *priv = timeline->priv;
824   GESTimelineLayer *layer;
825
826   layer = ges_timeline_layer_new ();
827   priority = g_list_length (priv->layers);
828   ges_timeline_layer_set_priority (layer, priority);
829
830   ges_timeline_add_layer (timeline, layer);
831
832   return layer;
833 }
834
835 /**
836  * ges_timeline_add_layer:
837  * @timeline: a #GESTimeline
838  * @layer: the #GESTimelineLayer to add
839  *
840  * Add the layer to the timeline. The reference to the @layer will be stolen
841  * by the @timeline.
842  *
843  * Returns: TRUE if the layer was properly added, else FALSE.
844  */
845 gboolean
846 ges_timeline_add_layer (GESTimeline * timeline, GESTimelineLayer * layer)
847 {
848   GList *objects, *tmp;
849   GESTimelinePrivate *priv = timeline->priv;
850
851   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
852
853   /* We can only add a layer that doesn't already belong to another timeline */
854   if (G_UNLIKELY (layer->timeline)) {
855     GST_WARNING ("Layer belongs to another timeline, can't add it");
856     return FALSE;
857   }
858
859   /* Add to the list of layers, make sure we don't already control it */
860   if (G_UNLIKELY (g_list_find (priv->layers, (gconstpointer) layer))) {
861     GST_WARNING ("Layer is already controlled by this timeline");
862     return FALSE;
863   }
864
865   g_object_ref_sink (layer);
866   priv->layers = g_list_insert_sorted (priv->layers, layer,
867       (GCompareFunc) sort_layers);
868
869   /* Inform the layer that it belongs to a new timeline */
870   ges_timeline_layer_set_timeline (layer, timeline);
871
872   /* Connect to 'object-added'/'object-removed' signal from the new layer */
873   g_signal_connect (layer, "object-added", G_CALLBACK (layer_object_added_cb),
874       timeline);
875   g_signal_connect (layer, "object-removed",
876       G_CALLBACK (layer_object_removed_cb), timeline);
877   g_signal_connect (layer, "notify::priority",
878       G_CALLBACK (layer_priority_changed_cb), timeline);
879
880   GST_DEBUG ("Done adding layer, emitting 'layer-added' signal");
881   g_signal_emit (timeline, ges_timeline_signals[LAYER_ADDED], 0, layer);
882
883   /* add any existing timeline objects to the timeline */
884   objects = ges_timeline_layer_get_objects (layer);
885   for (tmp = objects; tmp; tmp = tmp->next) {
886     layer_object_added_cb (layer, tmp->data, timeline);
887     g_object_unref (tmp->data);
888     tmp->data = NULL;
889   }
890   g_list_free (objects);
891
892   return TRUE;
893 }
894
895 /**
896  * ges_timeline_remove_layer:
897  * @timeline: a #GESTimeline
898  * @layer: the #GESTimelineLayer to remove
899  *
900  * Removes the layer from the timeline. The reference that the @timeline holds on
901  * the layer will be dropped. If you wish to use the @layer after calling this
902  * method, you need to take a reference before calling.
903  *
904  * Returns: TRUE if the layer was properly removed, else FALSE.
905  */
906
907 gboolean
908 ges_timeline_remove_layer (GESTimeline * timeline, GESTimelineLayer * layer)
909 {
910   GList *layer_objects, *tmp;
911   GESTimelinePrivate *priv = timeline->priv;
912
913   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
914
915   if (G_UNLIKELY (!g_list_find (priv->layers, layer))) {
916     GST_WARNING ("Layer doesn't belong to this timeline");
917     return FALSE;
918   }
919
920   /* remove objects from any private data structures */
921
922   layer_objects = ges_timeline_layer_get_objects (layer);
923   for (tmp = layer_objects; tmp; tmp = tmp->next) {
924     layer_object_removed_cb (layer, GES_TIMELINE_OBJECT (tmp->data), timeline);
925     g_object_unref (G_OBJECT (tmp->data));
926     tmp->data = NULL;
927   }
928   g_list_free (layer_objects);
929
930   /* Disconnect signals */
931   GST_DEBUG ("Disconnecting signal callbacks");
932   g_signal_handlers_disconnect_by_func (layer, layer_object_added_cb, timeline);
933   g_signal_handlers_disconnect_by_func (layer, layer_object_removed_cb,
934       timeline);
935
936   priv->layers = g_list_remove (priv->layers, layer);
937
938   ges_timeline_layer_set_timeline (layer, NULL);
939
940   g_signal_emit (timeline, ges_timeline_signals[LAYER_REMOVED], 0, layer);
941
942   g_object_unref (layer);
943
944   return TRUE;
945 }
946
947 static void
948 pad_added_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv)
949 {
950   gchar *padname;
951   gboolean no_more;
952   GList *tmp;
953
954   GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
955
956   if (G_UNLIKELY (tr_priv->pad)) {
957     GST_WARNING ("We are already controlling a pad for this track");
958     return;
959   }
960
961   /* Remember the pad */
962   GST_OBJECT_LOCK (track);
963   tr_priv->pad = pad;
964
965   no_more = TRUE;
966   for (tmp = tr_priv->timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
967     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
968
969     if (!tr_priv->pad) {
970       GST_LOG ("Found track without pad %p", tr_priv->track);
971       no_more = FALSE;
972     }
973   }
974   GST_OBJECT_UNLOCK (track);
975
976   /* ghost it ! */
977   GST_DEBUG ("Ghosting pad and adding it to ourself");
978   padname = g_strdup_printf ("track_%p_src", track);
979   tr_priv->ghostpad = gst_ghost_pad_new (padname, pad);
980   g_free (padname);
981   gst_pad_set_active (tr_priv->ghostpad, TRUE);
982   gst_element_add_pad (GST_ELEMENT (tr_priv->timeline), tr_priv->ghostpad);
983
984   if (no_more) {
985     GST_DEBUG ("Signaling no-more-pads");
986     gst_element_no_more_pads (GST_ELEMENT (tr_priv->timeline));
987   }
988 }
989
990 static void
991 pad_removed_cb (GESTrack * track, GstPad * pad, TrackPrivate * tr_priv)
992 {
993   GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
994
995   if (G_UNLIKELY (tr_priv->pad != pad)) {
996     GST_WARNING ("Not the pad we're controlling");
997     return;
998   }
999
1000   if (G_UNLIKELY (tr_priv->ghostpad == NULL)) {
1001     GST_WARNING ("We don't have a ghostpad for this pad !");
1002     return;
1003   }
1004
1005   GST_DEBUG ("Removing ghostpad");
1006   gst_pad_set_active (tr_priv->ghostpad, FALSE);
1007   gst_element_remove_pad (GST_ELEMENT (tr_priv->timeline), tr_priv->ghostpad);
1008   tr_priv->ghostpad = NULL;
1009   tr_priv->pad = NULL;
1010 }
1011
1012 /**
1013  * ges_timeline_add_track:
1014  * @timeline: a #GESTimeline
1015  * @track: the #GESTrack to add
1016  *
1017  * Add a track to the timeline. The reference to the track will be stolen by the
1018  * pipeline.
1019  *
1020  * Returns: TRUE if the track was properly added, else FALSE.
1021  */
1022
1023 /* FIXME: create track objects for timeline objects which have already been
1024  * added to existing layers.
1025  */
1026
1027 gboolean
1028 ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
1029 {
1030   TrackPrivate *tr_priv;
1031   GESTimelinePrivate *priv = timeline->priv;
1032   GList *tmp;
1033
1034   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
1035
1036   /* make sure we don't already control it */
1037   if (G_UNLIKELY (g_list_find_custom (priv->tracks, (gconstpointer) track,
1038               (GCompareFunc) custom_find_track))) {
1039     GST_WARNING ("Track is already controlled by this timeline");
1040     return FALSE;
1041   }
1042
1043   /* Add the track to ourself (as a GstBin)
1044    * Reference is stolen ! */
1045   if (G_UNLIKELY (!gst_bin_add (GST_BIN (timeline), GST_ELEMENT (track)))) {
1046     GST_WARNING ("Couldn't add track to ourself (GST)");
1047     return FALSE;
1048   }
1049
1050   tr_priv = g_new0 (TrackPrivate, 1);
1051   tr_priv->timeline = timeline;
1052   tr_priv->track = track;
1053
1054   /* Add the track to the list of tracks we track */
1055   priv->tracks = g_list_append (priv->tracks, tr_priv);
1056
1057   /* Listen to pad-added/-removed */
1058   g_signal_connect (track, "pad-added", (GCallback) pad_added_cb, tr_priv);
1059   g_signal_connect (track, "pad-removed", (GCallback) pad_removed_cb, tr_priv);
1060
1061   /* Inform the track that it's currently being used by ourself */
1062   ges_track_set_timeline (track, timeline);
1063
1064   GST_DEBUG ("Done adding track, emitting 'track-added' signal");
1065
1066   /* emit 'track-added' */
1067   g_signal_emit (timeline, ges_timeline_signals[TRACK_ADDED], 0, track);
1068
1069   /* ensure that each existing timeline object has the opportunity to create a
1070    * track object for this track*/
1071
1072   for (tmp = priv->layers; tmp; tmp = tmp->next) {
1073     GList *objects, *obj;
1074     objects = ges_timeline_layer_get_objects (tmp->data);
1075
1076     for (obj = objects; obj; obj = obj->next) {
1077       add_object_to_track (obj->data, track);
1078       g_object_unref (obj->data);
1079       obj->data = NULL;
1080     }
1081     g_list_free (objects);
1082   }
1083
1084   /* We connect to the duration change notify, so we can update
1085    * our duration accordingly */
1086   g_signal_connect (G_OBJECT (track), "notify::duration",
1087       G_CALLBACK (track_duration_cb), timeline);
1088   track_duration_cb (GST_ELEMENT (track), NULL, timeline);
1089
1090   return TRUE;
1091 }
1092
1093 /**
1094  * ges_timeline_remove_track:
1095  * @timeline: a #GESTimeline
1096  * @track: the #GESTrack to remove
1097  *
1098  * Remove the @track from the @timeline. The reference stolen when adding the
1099  * @track will be removed. If you wish to use the @track after calling this
1100  * function you must ensure that you have a reference to it.
1101  *
1102  * Returns: TRUE if the @track was properly removed, else FALSE.
1103  */
1104
1105 /* FIXME: release any track objects associated with this layer. currenly this
1106  * will not happen if you remove the track before removing *all*
1107  * timelineobjects which have a track object in this track.
1108  */
1109
1110 gboolean
1111 ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track)
1112 {
1113   GList *tmp;
1114   TrackPrivate *tr_priv;
1115   GESTimelinePrivate *priv = timeline->priv;
1116
1117   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
1118
1119   if (G_UNLIKELY (!(tmp =
1120               g_list_find_custom (priv->tracks, (gconstpointer) track,
1121                   (GCompareFunc) custom_find_track)))) {
1122     GST_WARNING ("Track doesn't belong to this timeline");
1123     return FALSE;
1124   }
1125
1126   tr_priv = tmp->data;
1127   priv->tracks = g_list_remove (priv->tracks, tr_priv);
1128
1129   ges_track_set_timeline (track, NULL);
1130
1131   /* Remove ghost pad */
1132   if (tr_priv->ghostpad) {
1133     GST_DEBUG ("Removing ghostpad");
1134     gst_pad_set_active (tr_priv->ghostpad, FALSE);
1135     gst_ghost_pad_set_target ((GstGhostPad *) tr_priv->ghostpad, NULL);
1136     gst_element_remove_pad (GST_ELEMENT (timeline), tr_priv->ghostpad);
1137   }
1138
1139   /* Remove pad-added/-removed handlers */
1140   g_signal_handlers_disconnect_by_func (track, pad_added_cb, tr_priv);
1141   g_signal_handlers_disconnect_by_func (track, pad_removed_cb, tr_priv);
1142   g_signal_handlers_disconnect_by_func (track, track_duration_cb,
1143       tr_priv->track);
1144
1145   /* Signal track removal to all layers/objects */
1146   g_signal_emit (timeline, ges_timeline_signals[TRACK_REMOVED], 0, track);
1147
1148   /* remove track from our bin */
1149   gst_object_ref (track);
1150   if (G_UNLIKELY (!gst_bin_remove (GST_BIN (timeline), GST_ELEMENT (track)))) {
1151     GST_WARNING ("Couldn't remove track to ourself (GST)");
1152     gst_object_unref (track);
1153     return FALSE;
1154   }
1155
1156   /* set track state to NULL */
1157
1158   gst_element_set_state (GST_ELEMENT (track), GST_STATE_NULL);
1159
1160   gst_object_unref (track);
1161
1162   g_free (tr_priv);
1163
1164   return TRUE;
1165 }
1166
1167 /**
1168  * ges_timeline_get_track_for_pad:
1169  * @timeline: The #GESTimeline
1170  * @pad: The #GstPad
1171  *
1172  * Search the #GESTrack corresponding to the given @timeline's @pad.
1173  *
1174  * Returns: (transfer none): The corresponding #GESTrack if it is found,
1175  * or %NULL if there is an error.
1176  */
1177
1178 GESTrack *
1179 ges_timeline_get_track_for_pad (GESTimeline * timeline, GstPad * pad)
1180 {
1181   GList *tmp;
1182
1183   for (tmp = timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
1184     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
1185     if (pad == tr_priv->ghostpad)
1186       return tr_priv->track;
1187   }
1188
1189   return NULL;
1190 }
1191
1192 /**
1193  * ges_timeline_get_tracks:
1194  * @timeline: a #GESTimeline
1195  *
1196  * Returns the list of #GESTrack used by the Timeline.
1197  *
1198  * Returns: (transfer full) (element-type GESTrack): A list of #GESTrack.
1199  * The caller should unref each track once he is done with them.
1200  */
1201 GList *
1202 ges_timeline_get_tracks (GESTimeline * timeline)
1203 {
1204   GList *tmp, *res = NULL;
1205
1206   for (tmp = timeline->priv->tracks; tmp; tmp = g_list_next (tmp)) {
1207     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
1208     res = g_list_append (res, g_object_ref (tr_priv->track));
1209   }
1210
1211   return res;
1212 }
1213
1214 /**
1215  * ges_timeline_get_layers:
1216  * @timeline: a #GESTimeline
1217  *
1218  * Get the list of #GESTimelineLayer present in the Timeline.
1219  *
1220  * Returns: (transfer full) (element-type GESTimelineLayer): the list of
1221  * #GESTimelineLayer present in the Timeline sorted by priority.
1222  * The caller should unref each Layer once he is done with them.
1223  */
1224 GList *
1225 ges_timeline_get_layers (GESTimeline * timeline)
1226 {
1227   GList *tmp, *res = NULL;
1228
1229   for (tmp = timeline->priv->layers; tmp; tmp = g_list_next (tmp)) {
1230     res = g_list_insert_sorted (res, g_object_ref (tmp->data),
1231         (GCompareFunc) sort_layers);
1232   }
1233
1234   return res;
1235 }
1236
1237 /**
1238  * ges_timeline_is_updating:
1239  * @timeline: a #GESTimeline
1240  *
1241  * Get whether the timeline is updated for every change happening within or not.
1242  *
1243  * Returns: %TRUE if @timeline is updating on every changes, else %FALSE.
1244  */
1245 gboolean
1246 ges_timeline_is_updating (GESTimeline * timeline)
1247 {
1248   GList *tmp;
1249
1250   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
1251
1252   for (tmp = timeline->priv->tracks; tmp; tmp = tmp->next) {
1253     if (!ges_track_is_updating (((TrackPrivate *) tmp->data)->track))
1254       return FALSE;
1255   }
1256
1257   return TRUE;
1258 }
1259
1260 /**
1261  * ges_timeline_enable_update:
1262  * @timeline: a #GESTimeline
1263  * @enabled: Whether the timeline should update on every change or not.
1264  *
1265  * Control whether the timeline is updated for every change happening within.
1266  *
1267  * Users will want to use this method with %FALSE before doing lots of changes,
1268  * and then call again with %TRUE for the changes to take effect in one go.
1269  *
1270  * Returns: %TRUE if the update status could be changed, else %FALSE.
1271  */
1272 gboolean
1273 ges_timeline_enable_update (GESTimeline * timeline, gboolean enabled)
1274 {
1275   GList *tmp;
1276   gboolean res = TRUE;
1277
1278   GST_DEBUG_OBJECT (timeline, "%s updates", enabled ? "Enabling" : "Disabling");
1279
1280   for (tmp = timeline->priv->tracks; tmp; tmp = tmp->next) {
1281     if (!ges_track_enable_update (((TrackPrivate *) tmp->data)->track, enabled))
1282       res = FALSE;
1283   }
1284
1285   return res;
1286 }