44eb72bdd9a8e4071df84b8a47be2f2a894d03c9
[platform/upstream/gst-editing-services.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  *               2012 Thibault Saunier <tsaunier@gnome.org>
5  *               2012 Collabora Ltd.
6  *                 Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 /**
25  * SECTION:gestimeline
26  * @title: GESTimeline
27  * @short_description: Multimedia timeline
28  *
29  * #GESTimeline is the central object for any multimedia timeline.
30  *
31  * Contains a list of #GESLayer which users should use to arrange the
32  * various clips through time.
33  *
34  * The output type is determined by the #GESTrack that are set on
35  * the #GESTimeline.
36  *
37  * To save/load a timeline, you can use the ges_timeline_load_from_uri() and
38  * ges_timeline_save_to_uri() methods to use the default format. If you wish
39  *
40  * Note that any change you make in the timeline will not actually be taken
41  * into account until you call the #ges_timeline_commit method.
42  */
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46
47 #include "ges-internal.h"
48 #include "ges-project.h"
49 #include "ges-container.h"
50 #include "ges-timeline.h"
51 #include "ges-track.h"
52 #include "ges-layer.h"
53 #include "ges-auto-transition.h"
54 #include "ges.h"
55
56 typedef struct _MoveContext MoveContext;
57
58 static GPtrArray *select_tracks_for_object_default (GESTimeline * timeline,
59     GESClip * clip, GESTrackElement * tr_obj, gpointer user_data);
60 static inline void init_movecontext (MoveContext * mv_ctx, gboolean first_init);
61 static void ges_extractable_interface_init (GESExtractableInterface * iface);
62 static void ges_meta_container_interface_init
63     (GESMetaContainerInterface * iface);
64
65 GST_DEBUG_CATEGORY_STATIC (ges_timeline_debug);
66 #undef GST_CAT_DEFAULT
67 #define GST_CAT_DEFAULT ges_timeline_debug
68
69 /* lock to protect dynamic callbacks, like pad-added */
70 #define DYN_LOCK(timeline) (&GES_TIMELINE (timeline)->priv->dyn_mutex)
71 #define LOCK_DYN(timeline) G_STMT_START {                       \
72     GST_LOG_OBJECT (timeline, "Getting dynamic lock from %p", \
73         g_thread_self());                                       \
74     g_rec_mutex_lock (DYN_LOCK (timeline));                     \
75     GST_LOG_OBJECT (timeline, "Got Dynamic lock from %p",     \
76         g_thread_self());         \
77   } G_STMT_END
78
79 #define UNLOCK_DYN(timeline) G_STMT_START {                         \
80     GST_LOG_OBJECT (timeline, "Unlocking dynamic lock from %p", \
81         g_thread_self());                                         \
82     g_rec_mutex_unlock (DYN_LOCK (timeline));                     \
83     GST_LOG_OBJECT (timeline, "Unlocked Dynamic lock from %p",  \
84         g_thread_self());         \
85   } G_STMT_END
86
87 #define CHECK_THREAD(timeline) g_assert(timeline->priv->valid_thread == g_thread_self())
88
89 typedef struct TrackObjIters
90 {
91   GSequenceIter *iter_start;
92   GSequenceIter *iter_end;
93   GSequenceIter *iter_obj;
94   GSequenceIter *iter_by_layer;
95
96   GESLayer *layer;
97   GESTrackElement *trackelement;
98 } TrackObjIters;
99
100 static void
101 _destroy_obj_iters (TrackObjIters * iters)
102 {
103   g_slice_free (TrackObjIters, iters);
104 }
105
106 /*  The move context is used for the timeline editing modes functions in order to
107  *  + Ripple / Roll /  Slide / Move / Trim
108  *
109  * The context aims at avoiding to recalculate values/objects on each call of the
110  * editing functions.
111  */
112 struct _MoveContext
113 {
114   GESClip *clip;
115   GESEdge edge;
116   GESEditMode mode;
117
118   /* The  start of the moving context */
119   GstClockTime start;
120
121   /* Ripple and Roll Objects */
122   GList *moving_trackelements;
123
124   /* We use it as a set of Clip to move between layers */
125   GHashTable *toplevel_containers;
126   /* Min priority of the objects currently in toplevel_containers */
127   guint min_move_layer;
128   /* Max priority of the objects currently in toplevel_containers */
129   guint max_layer_prio;
130
131   /* Never trim so duration would become < 0 */
132   guint64 max_trim_pos;
133
134   /* Never trim so inpoint + duration would change */
135   guint64 min_trim_pos;
136
137   /* fields to force/avoid new context */
138   /* Set to %TRUE when the track is doing updates of track element
139    * properties so we don't end up always needing new move context */
140   gboolean ignore_needs_ctx;
141   gboolean needs_move_ctx;
142
143   /* Last snapping  properties */
144   GESTrackElement *last_snaped1;
145   GESTrackElement *last_snaped2;
146   GstClockTime *last_snap_ts;
147
148   /* Priority of the layer where we are moving current clip
149    * -1 if not moving any clip to a new layer. */
150   GESLayer *moving_to_layer;
151 };
152
153 struct _GESTimelinePrivate
154 {
155   /* The duration of the timeline */
156   gint64 duration;
157
158   /* The auto-transition of the timeline */
159   gboolean auto_transition;
160   /* Use to determine that a edit action should be rolled
161    * back because it leads to a wrong state of the element
162    * position (currently only happens if 3 clips overlap) */
163   gboolean needs_rollback;
164   gboolean rolling_back;
165
166   /* Timeline edition modes and snapping management */
167   guint64 snapping_distance;
168
169   /* FIXME: Should we offer an API over those fields ?
170    * FIXME: Should other classes than subclasses of Source also
171    * be tracked? */
172
173   /* Snapping fields */
174   GHashTable *by_start;         /* {Source: start} */
175   GHashTable *by_end;           /* {Source: end} */
176   GHashTable *by_object;        /* {timecode: Source} */
177   GHashTable *obj_iters;        /* {Source: TrackObjIters} */
178   GSequence *starts_ends;       /* Sorted list of starts/ends */
179   /* We keep 1 reference to our trackelement here */
180   GSequence *tracksources;      /* Source-s sorted by start/priorities */
181
182   GRecMutex dyn_mutex;
183   GList *priv_tracks;
184   /* FIXME: We should definitly offer an API over this,
185    * probably through a ges_layer_get_track_elements () method */
186   GHashTable *by_layer;         /* {layer: GSequence of TrackElement by start/priorities} */
187
188   /* Avoid sorting layers when we are actually resyncing them ourself */
189   gboolean resyncing_layers;
190   GList *auto_transitions;
191
192   MoveContext movecontext;
193
194   /* This variable is set to %TRUE when it makes sense to update the transitions,
195    * and %FALSE otherwize */
196   gboolean needs_transitions_update;
197
198   /* While we are creating and adding the TrackElements for a clip, we need to
199    * ignore the child-added signal */
200   GESClip *ignore_track_element_added;
201   GList *groups;
202
203   guint group_id;
204
205   GHashTable *all_elements;
206
207   /* With GST_OBJECT_LOCK */
208   guint expected_async_done;
209   /* With GST_OBJECT_LOCK */
210   guint expected_commited;
211
212   /* For ges_timeline_commit_sync */
213   GMutex commited_lock;
214   GCond commited_cond;
215
216   GThread *valid_thread;
217 };
218
219 /* private structure to contain our track-related information */
220
221 typedef struct
222 {
223   GESTimeline *timeline;
224   GESTrack *track;
225   GstPad *pad;                  /* Pad from the track */
226   GstPad *ghostpad;
227
228   gulong probe_id;
229 } TrackPrivate;
230
231 enum
232 {
233   PROP_0,
234   PROP_DURATION,
235   PROP_AUTO_TRANSITION,
236   PROP_SNAPPING_DISTANCE,
237   PROP_UPDATE,
238   PROP_LAST
239 };
240
241 static GParamSpec *properties[PROP_LAST];
242
243 enum
244 {
245   TRACK_ADDED,
246   TRACK_REMOVED,
247   LAYER_ADDED,
248   LAYER_REMOVED,
249   GROUP_ADDED,
250   GROUP_REMOVED,
251   SNAPING_STARTED,
252   SNAPING_ENDED,
253   SELECT_TRACKS_FOR_OBJECT,
254   COMMITED,
255   LAST_SIGNAL
256 };
257
258 G_DEFINE_TYPE_WITH_CODE (GESTimeline, ges_timeline, GST_TYPE_BIN,
259     G_ADD_PRIVATE (GESTimeline)
260     G_IMPLEMENT_INTERFACE (GES_TYPE_EXTRACTABLE, ges_extractable_interface_init)
261     G_IMPLEMENT_INTERFACE (GES_TYPE_META_CONTAINER,
262         ges_meta_container_interface_init));
263
264 static GstBinClass *parent_class;
265
266 static guint ges_timeline_signals[LAST_SIGNAL] = { 0 };
267
268 static gint custom_find_track (TrackPrivate * tr_priv, GESTrack * track);
269
270 static guint nb_assets = 0;
271
272 /* GESExtractable implementation */
273 static gchar *
274 extractable_check_id (GType type, const gchar * id)
275 {
276   gchar *res;
277
278   if (id == NULL)
279     res = g_strdup_printf ("%s-%i", "project", nb_assets);
280   else
281     res = g_strdup (id);
282
283   nb_assets++;
284
285   return res;
286 }
287
288 static gchar *
289 extractable_get_id (GESExtractable * self)
290 {
291   GESAsset *asset;
292
293   if (!(asset = ges_extractable_get_asset (self)))
294     return NULL;
295
296   return g_strdup (ges_asset_get_id (asset));
297 }
298
299 static void
300 ges_extractable_interface_init (GESExtractableInterface * iface)
301 {
302   iface->asset_type = GES_TYPE_PROJECT;
303   iface->check_id = (GESExtractableCheckId) extractable_check_id;
304   iface->get_id = extractable_get_id;
305 }
306
307 static void
308 ges_meta_container_interface_init (GESMetaContainerInterface * iface)
309 {
310 }
311
312 /* GObject Standard vmethods*/
313 static void
314 ges_timeline_get_property (GObject * object, guint property_id,
315     GValue * value, GParamSpec * pspec)
316 {
317   GESTimeline *timeline = GES_TIMELINE (object);
318
319   switch (property_id) {
320     case PROP_DURATION:
321       g_value_set_uint64 (value, timeline->priv->duration);
322       break;
323     case PROP_AUTO_TRANSITION:
324       g_value_set_boolean (value, timeline->priv->auto_transition);
325       break;
326     case PROP_SNAPPING_DISTANCE:
327       g_value_set_uint64 (value, timeline->priv->snapping_distance);
328       break;
329     default:
330       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
331   }
332 }
333
334 static void
335 ges_timeline_set_property (GObject * object, guint property_id,
336     const GValue * value, GParamSpec * pspec)
337 {
338   GESTimeline *timeline = GES_TIMELINE (object);
339
340   switch (property_id) {
341     case PROP_AUTO_TRANSITION:
342       ges_timeline_set_auto_transition (timeline, g_value_get_boolean (value));
343       break;
344     case PROP_SNAPPING_DISTANCE:
345       timeline->priv->snapping_distance = g_value_get_uint64 (value);
346       break;
347     default:
348       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
349   }
350 }
351
352 static void
353 ges_timeline_dispose (GObject * object)
354 {
355   GESTimeline *tl = GES_TIMELINE (object);
356   GESTimelinePrivate *priv = tl->priv;
357   GList *tmp, *groups;
358
359   while (tl->layers) {
360     GESLayer *layer = (GESLayer *) tl->layers->data;
361     ges_timeline_remove_layer (GES_TIMELINE (object), layer);
362   }
363
364   /* FIXME: it should be possible to remove tracks before removing
365    * layers, but at the moment this creates a problem because the track
366    * objects aren't notified that their nleobjects have been destroyed.
367    */
368
369   while (tl->tracks)
370     ges_timeline_remove_track (GES_TIMELINE (object), tl->tracks->data);
371
372   groups = g_list_copy (priv->groups);
373   for (tmp = groups; tmp; tmp = tmp->next) {
374     GList *elems = ges_container_ungroup (tmp->data, FALSE);
375
376     g_list_free_full (elems, gst_object_unref);
377   }
378   g_list_free (priv->groups);
379   g_list_free (groups);
380
381   g_hash_table_unref (priv->by_start);
382   g_hash_table_unref (priv->by_end);
383   g_hash_table_unref (priv->by_object);
384   g_hash_table_unref (priv->by_layer);
385   g_hash_table_unref (priv->obj_iters);
386   g_sequence_free (priv->starts_ends);
387   g_sequence_free (priv->tracksources);
388   g_list_free (priv->movecontext.moving_trackelements);
389   g_hash_table_unref (priv->movecontext.toplevel_containers);
390
391   g_list_free_full (priv->auto_transitions, gst_object_unref);
392
393   g_hash_table_unref (priv->all_elements);
394
395   G_OBJECT_CLASS (ges_timeline_parent_class)->dispose (object);
396 }
397
398 static void
399 ges_timeline_finalize (GObject * object)
400 {
401   GESTimeline *tl = GES_TIMELINE (object);
402
403   g_rec_mutex_clear (&tl->priv->dyn_mutex);
404
405   G_OBJECT_CLASS (ges_timeline_parent_class)->finalize (object);
406 }
407
408
409
410 static void
411 ges_timeline_handle_message (GstBin * bin, GstMessage * message)
412 {
413   GESTimeline *timeline = GES_TIMELINE (bin);
414
415   if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ELEMENT) {
416     GstMessage *amessage = NULL;
417     const GstStructure *mstructure = gst_message_get_structure (message);
418
419     if (gst_structure_has_name (mstructure, "NleCompositionStartUpdate")) {
420       if (g_strcmp0 (gst_structure_get_string (mstructure, "reason"), "Seek")) {
421         GST_INFO_OBJECT (timeline,
422             "A composition is starting an update because of %s"
423             " not considering async", gst_structure_get_string (mstructure,
424                 "reason"));
425
426         goto forward;
427       }
428
429       GST_OBJECT_LOCK (timeline);
430       if (timeline->priv->expected_async_done == 0) {
431         amessage = gst_message_new_async_start (GST_OBJECT_CAST (bin));
432         timeline->priv->expected_async_done = g_list_length (timeline->tracks);
433         GST_INFO_OBJECT (timeline, "Posting ASYNC_START %s",
434             gst_structure_get_string (mstructure, "reason"));
435       }
436       GST_OBJECT_UNLOCK (timeline);
437
438     } else if (gst_structure_has_name (mstructure, "NleCompositionUpdateDone")) {
439       if (g_strcmp0 (gst_structure_get_string (mstructure, "reason"), "Seek")) {
440         GST_INFO_OBJECT (timeline,
441             "A composition is done updating because of %s"
442             " not considering async", gst_structure_get_string (mstructure,
443                 "reason"));
444
445         goto forward;
446       }
447
448       GST_OBJECT_LOCK (timeline);
449       timeline->priv->expected_async_done -= 1;
450       if (timeline->priv->expected_async_done == 0) {
451         amessage = gst_message_new_async_done (GST_OBJECT_CAST (bin),
452             GST_CLOCK_TIME_NONE);
453         GST_INFO_OBJECT (timeline, "Posting ASYNC_DONE %s",
454             gst_structure_get_string (mstructure, "reason"));
455       }
456       GST_OBJECT_UNLOCK (timeline);
457     }
458
459     if (amessage)
460       gst_element_post_message (GST_ELEMENT_CAST (bin), amessage);
461   }
462
463 forward:
464   gst_element_post_message (GST_ELEMENT_CAST (bin), message);
465 }
466
467 /* we collect the first result */
468 static gboolean
469 _gst_array_accumulator (GSignalInvocationHint * ihint,
470     GValue * return_accu, const GValue * handler_return, gpointer dummy)
471 {
472   gpointer array;
473
474   array = g_value_get_boxed (handler_return);
475   if (!(ihint->run_type & G_SIGNAL_RUN_CLEANUP))
476     g_value_set_boxed (return_accu, array);
477
478   return FALSE;
479 }
480
481 static void
482 ges_timeline_class_init (GESTimelineClass * klass)
483 {
484   GObjectClass *object_class = G_OBJECT_CLASS (klass);
485   GstBinClass *bin_class = GST_BIN_CLASS (klass);
486
487   GST_DEBUG_CATEGORY_INIT (ges_timeline_debug, "gestimeline",
488       GST_DEBUG_FG_YELLOW, "ges timeline");
489
490   parent_class = g_type_class_peek_parent (klass);
491
492   object_class->get_property = ges_timeline_get_property;
493   object_class->set_property = ges_timeline_set_property;
494   object_class->dispose = ges_timeline_dispose;
495   object_class->finalize = ges_timeline_finalize;
496
497   bin_class->handle_message = GST_DEBUG_FUNCPTR (ges_timeline_handle_message);
498
499   /**
500    * GESTimeline:duration:
501    *
502    * Current duration (in nanoseconds) of the #GESTimeline
503    */
504   properties[PROP_DURATION] =
505       g_param_spec_uint64 ("duration", "Duration",
506       "The duration of the timeline", 0, G_MAXUINT64,
507       GST_CLOCK_TIME_NONE, G_PARAM_READABLE);
508   g_object_class_install_property (object_class, PROP_DURATION,
509       properties[PROP_DURATION]);
510
511   /**
512    * GESTimeline:auto-transition:
513    *
514    * Sets whether transitions are added automagically when clips overlap.
515    */
516   g_object_class_install_property (object_class, PROP_AUTO_TRANSITION,
517       g_param_spec_boolean ("auto-transition", "Auto-Transition",
518           "whether the transitions are added", FALSE, G_PARAM_READWRITE));
519
520   /**
521    * GESTimeline:snapping-distance:
522    *
523    * Distance (in nanoseconds) from which a moving object will snap
524    * with it neighboors. 0 means no snapping.
525    */
526   properties[PROP_SNAPPING_DISTANCE] =
527       g_param_spec_uint64 ("snapping-distance", "Snapping distance",
528       "Distance from which moving an object will snap with neighboors", 0,
529       G_MAXUINT64, 0, G_PARAM_READWRITE);
530   g_object_class_install_property (object_class, PROP_SNAPPING_DISTANCE,
531       properties[PROP_SNAPPING_DISTANCE]);
532
533   /**
534    * GESTimeline::track-added:
535    * @timeline: the #GESTimeline
536    * @track: the #GESTrack that was added to the timeline
537    *
538    * Will be emitted after the track was added to the timeline.
539    */
540   ges_timeline_signals[TRACK_ADDED] =
541       g_signal_new ("track-added", G_TYPE_FROM_CLASS (klass),
542       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_added), NULL,
543       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_TRACK);
544
545   /**
546    * GESTimeline::track-removed:
547    * @timeline: the #GESTimeline
548    * @track: the #GESTrack that was removed from the timeline
549    *
550    * Will be emitted after the track was removed from the timeline.
551    */
552   ges_timeline_signals[TRACK_REMOVED] =
553       g_signal_new ("track-removed", G_TYPE_FROM_CLASS (klass),
554       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, track_removed),
555       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_TRACK);
556
557   /**
558    * GESTimeline::layer-added:
559    * @timeline: the #GESTimeline
560    * @layer: the #GESLayer that was added to the timeline
561    *
562    * Will be emitted after a new layer is added to the timeline.
563    */
564   ges_timeline_signals[LAYER_ADDED] =
565       g_signal_new ("layer-added", G_TYPE_FROM_CLASS (klass),
566       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_added), NULL,
567       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_LAYER);
568
569   /**
570    * GESTimeline::layer-removed:
571    * @timeline: the #GESTimeline
572    * @layer: the #GESLayer that was removed from the timeline
573    *
574    * Will be emitted after the layer was removed from the timeline.
575    */
576   ges_timeline_signals[LAYER_REMOVED] =
577       g_signal_new ("layer-removed", G_TYPE_FROM_CLASS (klass),
578       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, layer_removed),
579       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_LAYER);
580
581   /**
582    * GESTimeline::group-added
583    * @timeline: the #GESTimeline
584    * @group: the #GESGroup
585    *
586    * Will be emitted after a new group is added to to the timeline.
587    */
588   ges_timeline_signals[GROUP_ADDED] =
589       g_signal_new ("group-added", G_TYPE_FROM_CLASS (klass),
590       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, group_added), NULL,
591       NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1, GES_TYPE_GROUP);
592
593   /**
594    * GESTimeline::group-removed
595    * @timeline: the #GESTimeline
596    * @group: the #GESGroup
597    * @children: (element-type GES.Container) (transfer container): a list of #GESContainer
598    *
599    * Will be emitted after a group has been removed from the timeline.
600    */
601   ges_timeline_signals[GROUP_REMOVED] =
602       g_signal_new ("group-removed", G_TYPE_FROM_CLASS (klass),
603       G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GESTimelineClass, group_removed),
604       NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 2, GES_TYPE_GROUP,
605       G_TYPE_PTR_ARRAY);
606
607   /**
608    * GESTimeline::snapping-started:
609    * @timeline: the #GESTimeline
610    * @obj1: the first #GESTrackElement that was snapping.
611    * @obj2: the second #GESTrackElement that was snapping.
612    * @position: the position where the two objects finally snapping.
613    *
614    * Will be emitted when the 2 #GESTrackElement first snapped
615    */
616   ges_timeline_signals[SNAPING_STARTED] =
617       g_signal_new ("snapping-started", G_TYPE_FROM_CLASS (klass),
618       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
619       G_TYPE_NONE, 3, GES_TYPE_TRACK_ELEMENT, GES_TYPE_TRACK_ELEMENT,
620       G_TYPE_UINT64);
621
622   /**
623    * GESTimeline::snapping-ended:
624    * @timeline: the #GESTimeline
625    * @obj1: the first #GESTrackElement that was snapping.
626    * @obj2: the second #GESTrackElement that was snapping.
627    * @position: the position where the two objects finally snapping.
628    *
629    * Will be emitted when the 2 #GESTrackElement ended to snap
630    */
631   ges_timeline_signals[SNAPING_ENDED] =
632       g_signal_new ("snapping-ended", G_TYPE_FROM_CLASS (klass),
633       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
634       G_TYPE_NONE, 3, GES_TYPE_TRACK_ELEMENT, GES_TYPE_TRACK_ELEMENT,
635       G_TYPE_UINT64);
636
637   /**
638    * GESTimeline::select-tracks-for-object:
639    * @timeline: the #GESTimeline
640    * @clip: The #GESClip on which @track_element will land
641    * @track_element: The #GESTrackElement for which to choose the tracks it should land into
642    *
643    * Returns: (transfer full) (element-type GESTrack): a #GPtrArray of #GESTrack-s where that object should be added
644    */
645   ges_timeline_signals[SELECT_TRACKS_FOR_OBJECT] =
646       g_signal_new ("select-tracks-for-object", G_TYPE_FROM_CLASS (klass),
647       G_SIGNAL_RUN_LAST, 0, _gst_array_accumulator, NULL, NULL,
648       G_TYPE_PTR_ARRAY, 2, GES_TYPE_CLIP, GES_TYPE_TRACK_ELEMENT);
649
650   /**
651    * GESTimeline::commited:
652    * @timeline: the #GESTimeline
653    *
654    * This signal will be emitted once the changes initiated by #ges_timeline_commit
655    * have been executed in the backend. Use #ges_timeline_commit_sync if you
656    * don't need to do anything in the meantime.
657    */
658   ges_timeline_signals[COMMITED] =
659       g_signal_new ("commited", G_TYPE_FROM_CLASS (klass),
660       G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
661 }
662
663 static void
664 ges_timeline_init (GESTimeline * self)
665 {
666   GESTimelinePrivate *priv = self->priv;
667
668   self->priv = ges_timeline_get_instance_private (self);
669
670   priv = self->priv;
671   self->layers = NULL;
672   self->tracks = NULL;
673   self->priv->duration = 0;
674   self->priv->auto_transition = FALSE;
675   priv->snapping_distance = 0;
676   priv->expected_async_done = 0;
677   priv->expected_commited = 0;
678
679   /* Move context initialization */
680   init_movecontext (&self->priv->movecontext, TRUE);
681   priv->movecontext.ignore_needs_ctx = FALSE;
682
683   priv->priv_tracks = NULL;
684   priv->by_start = g_hash_table_new (g_direct_hash, g_direct_equal);
685   priv->by_end = g_hash_table_new (g_direct_hash, g_direct_equal);
686   priv->by_object = g_hash_table_new (g_direct_hash, g_direct_equal);
687   priv->by_layer = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
688       (GDestroyNotify) g_sequence_free);
689   priv->obj_iters = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
690       (GDestroyNotify) _destroy_obj_iters);
691   priv->starts_ends = g_sequence_new (g_free);
692   priv->tracksources = g_sequence_new (gst_object_unref);
693
694   priv->needs_transitions_update = TRUE;
695
696   priv->all_elements =
697       g_hash_table_new_full (g_str_hash, g_str_equal, g_free, gst_object_unref);
698
699   priv->group_id = -1;
700
701   g_signal_connect_after (self, "select-tracks-for-object",
702       G_CALLBACK (select_tracks_for_object_default), NULL);
703
704   g_rec_mutex_init (&priv->dyn_mutex);
705   g_mutex_init (&priv->commited_lock);
706   priv->valid_thread = g_thread_self ();
707 }
708
709 /* Private methods */
710
711 static inline GESContainer *
712 get_toplevel_container (gpointer element)
713 {
714   GESTimelineElement *ret =
715       ges_timeline_element_get_toplevel_parent ((GESTimelineElement
716           *) (element));
717
718   /*  We own a ref to the elements ourself */
719   gst_object_unref (ret);
720   return (GESContainer *) ret;
721 }
722
723 /* Sorting utils*/
724 static gint
725 sort_layers (gpointer a, gpointer b)
726 {
727   GESLayer *layer_a, *layer_b;
728   guint prio_a, prio_b;
729
730   layer_a = GES_LAYER (a);
731   layer_b = GES_LAYER (b);
732
733   prio_a = ges_layer_get_priority (layer_a);
734   prio_b = ges_layer_get_priority (layer_b);
735
736   if ((gint) prio_a > (guint) prio_b)
737     return 1;
738   if ((guint) prio_a < (guint) prio_b)
739     return -1;
740
741   return 0;
742 }
743
744 static void
745 _resync_layers (GESTimeline * timeline)
746 {
747   GList *tmp;
748   gint i = 0;
749
750   timeline->priv->resyncing_layers = TRUE;
751   for (tmp = timeline->layers; tmp; tmp = tmp->next) {
752     layer_set_priority (tmp->data, i, TRUE);
753     i++;
754   }
755   timeline->priv->resyncing_layers = FALSE;
756 }
757
758 static void
759 timeline_update_duration (GESTimeline * timeline)
760 {
761   GstClockTime *cduration;
762   GSequenceIter *it = g_sequence_get_end_iter (timeline->priv->starts_ends);
763
764   it = g_sequence_iter_prev (it);
765
766   if (g_sequence_iter_is_end (it)) {
767     timeline->priv->duration = 0;
768     g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_DURATION]);
769     return;
770   }
771
772   cduration = g_sequence_get (it);
773
774   if (cduration && timeline->priv->duration != *cduration) {
775     GST_DEBUG ("track duration : %" GST_TIME_FORMAT " current : %"
776         GST_TIME_FORMAT, GST_TIME_ARGS (*cduration),
777         GST_TIME_ARGS (timeline->priv->duration));
778
779     timeline->priv->duration = *cduration;
780
781     g_object_notify_by_pspec (G_OBJECT (timeline), properties[PROP_DURATION]);
782   }
783 }
784
785 static gint
786 find_layer_by_prio (GESLayer * a, gpointer pprio)
787 {
788   gint prio = GPOINTER_TO_INT (pprio), lprio = ges_layer_get_priority (a);
789
790   if (lprio < prio)
791     return -1;
792   if (lprio > prio)
793     return 1;
794   return 0;
795 }
796
797 static void
798 sort_track_elements (GESTimeline * timeline, TrackObjIters * iters)
799 {
800   g_sequence_sort_changed (iters->iter_obj,
801       (GCompareDataFunc) element_start_compare, NULL);
802 }
803
804 static gint
805 compare_uint64 (guint64 * a, guint64 * b, gpointer user_data)
806 {
807   if (*a > *b)
808     return 1;
809   else if (*a == *b)
810     return 0;
811   else
812     return -1;
813 }
814
815 static gint
816 custom_find_track (TrackPrivate * tr_priv, GESTrack * track)
817 {
818   if (tr_priv->track == track)
819     return 0;
820   return -1;
821 }
822
823 static inline void
824 sort_starts_ends_end (GESTimeline * timeline, TrackObjIters * iters)
825 {
826   GESTimelineElement *obj = GES_TIMELINE_ELEMENT (iters->trackelement);
827   GESTimelinePrivate *priv = timeline->priv;
828   guint64 *end = g_hash_table_lookup (priv->by_end, obj);
829
830   *end = _START (obj) + _DURATION (obj);
831
832   g_sequence_sort_changed (iters->iter_end, (GCompareDataFunc) compare_uint64,
833       NULL);
834   timeline_update_duration (timeline);
835 }
836
837 static inline void
838 sort_starts_ends_start (GESTimeline * timeline, TrackObjIters * iters)
839 {
840   GESTimelineElement *obj = GES_TIMELINE_ELEMENT (iters->trackelement);
841   GESTimelinePrivate *priv = timeline->priv;
842   guint64 *start = g_hash_table_lookup (priv->by_start, obj);
843
844   *start = _START (obj);
845
846   g_sequence_sort_changed (iters->iter_start,
847       (GCompareDataFunc) compare_uint64, NULL);
848   timeline_update_duration (timeline);
849 }
850
851 static void
852 _destroy_auto_transition_cb (GESAutoTransition * auto_transition,
853     GESTimeline * timeline)
854 {
855   GESTimelinePrivate *priv = timeline->priv;
856   MoveContext *mv_ctx = &timeline->priv->movecontext;
857   GESClip *transition = auto_transition->transition_clip;
858   GESLayer *layer = ges_clip_get_layer (transition);
859   GESContainer *toplevel_prev =
860       get_toplevel_container (auto_transition->previous_clip), *toplevel_next =
861       get_toplevel_container (auto_transition->next_clip);
862
863   if (g_hash_table_lookup (mv_ctx->toplevel_containers, toplevel_prev) &&
864       g_hash_table_lookup (mv_ctx->toplevel_containers, toplevel_next)) {
865     GESLayer *nlayer = mv_ctx->moving_to_layer;
866
867     if (!nlayer)
868       return;
869
870     ges_clip_move_to_layer (transition, nlayer);
871
872     return;
873   }
874
875   ges_layer_remove_clip (layer, transition);
876   g_signal_handlers_disconnect_by_func (auto_transition,
877       _destroy_auto_transition_cb, timeline);
878
879
880   priv->auto_transitions =
881       g_list_remove (priv->auto_transitions, auto_transition);
882   gst_object_unref (auto_transition);
883 }
884
885 static GESAutoTransition *
886 create_transition (GESTimeline * timeline, GESTrackElement * previous,
887     GESTrackElement * next, GESClip * transition,
888     GESLayer * layer, guint64 start, guint64 duration)
889 {
890   GESAsset *asset;
891   GESAutoTransition *auto_transition;
892
893   if (transition == NULL) {
894     /* TODO make it possible to specify a Transition asset in the API */
895     asset = ges_asset_request (GES_TYPE_TRANSITION_CLIP, "crossfade", NULL);
896     transition =
897         ges_layer_add_asset (layer, asset, start, 0, duration,
898         ges_track_element_get_track_type (next));
899   } else {
900     GST_DEBUG_OBJECT (timeline,
901         "Reusing already existing transition: %" GST_PTR_FORMAT, transition);
902   }
903
904   /* We know there is only 1 TrackElement */
905   auto_transition =
906       ges_auto_transition_new (GES_CONTAINER_CHILDREN (transition)->data,
907       previous, next);
908
909   g_signal_connect (auto_transition, "destroy-me",
910       G_CALLBACK (_destroy_auto_transition_cb), timeline);
911
912   timeline->priv->auto_transitions =
913       g_list_prepend (timeline->priv->auto_transitions, auto_transition);
914
915   return auto_transition;
916 }
917
918 typedef GESAutoTransition *(*GetAutoTransitionFunc) (GESTimeline * timeline,
919     GESLayer * layer, GESTrack * track, GESTrackElement * previous,
920     GESTrackElement * next, GstClockTime transition_duration);
921
922 static GESAutoTransition *
923 _find_transition_from_auto_transitions (GESTimeline * timeline,
924     GESLayer * layer, GESTrack * track, GESTrackElement * prev,
925     GESTrackElement * next, GstClockTime transition_duration)
926 {
927   GList *tmp;
928
929   for (tmp = timeline->priv->auto_transitions; tmp; tmp = tmp->next) {
930     GESAutoTransition *auto_trans = (GESAutoTransition *) tmp->data;
931
932     /* We already have a transition linked to one of the elements we want to
933      * find a transition for */
934     if (auto_trans->previous_source == prev || auto_trans->next_source == next) {
935       if (auto_trans->previous_source != prev
936           || auto_trans->next_source != next) {
937         timeline->priv->needs_rollback = TRUE;
938         GST_INFO_OBJECT (timeline, "Failed creating auto transition, "
939             " trying to have 3 clips overlapping, rolling back");
940       }
941
942       return auto_trans;
943     }
944   }
945
946   return NULL;
947 }
948
949 static GESAutoTransition *
950 _create_auto_transition_from_transitions (GESTimeline * timeline,
951     GESLayer * layer, GESTrack * track, GESTrackElement * prev,
952     GESTrackElement * next, GstClockTime transition_duration)
953 {
954   GSequenceIter *tmp_iter;
955   GSequence *by_layer_sequence;
956
957   GESTimelinePrivate *priv = timeline->priv;
958   GESAutoTransition *auto_transition =
959       _find_transition_from_auto_transitions (timeline, layer, track, prev,
960       next, transition_duration);
961
962
963   if (auto_transition) {
964     if (timeline->priv->needs_rollback) {
965       GST_WARNING_OBJECT (timeline,
966           "Created an auto transition where we have 3 overlapping clips"
967           " removing it as this case is NOT allowed nor supported");
968       g_signal_emit_by_name (auto_transition, "destroy-me");
969       timeline->priv->needs_rollback = FALSE;
970       return NULL;
971     }
972     return auto_transition;
973   }
974
975
976   /* Try to find a transition that perfectly fits with the one that
977    * should be added at that place
978    * optimize: Use g_sequence_search instead of going over all the
979    * sequence */
980   by_layer_sequence = g_hash_table_lookup (priv->by_layer, layer);
981   for (tmp_iter = g_sequence_get_begin_iter (by_layer_sequence);
982       tmp_iter && !g_sequence_iter_is_end (tmp_iter);
983       tmp_iter = g_sequence_iter_next (tmp_iter)) {
984     GESTrackElement *maybe_transition = g_sequence_get (tmp_iter);
985
986     if (ges_track_element_get_track (maybe_transition) != track)
987       continue;
988
989     if (_START (maybe_transition) > _START (next))
990       break;
991     else if (_START (maybe_transition) != _START (next) ||
992         _DURATION (maybe_transition) != transition_duration)
993       continue;
994     else if (GES_IS_TRANSITION (maybe_transition))
995       /* Use that transition */
996       /* TODO We should make sure that the transition contains only
997        * TrackElement-s in @track and if it is not the case properly unlink the
998        * object to use it */
999       return create_transition (timeline, prev, next,
1000           GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (maybe_transition)), layer,
1001           _START (next), transition_duration);
1002   }
1003
1004   return NULL;
1005 }
1006
1007 /* Create all transition that do not exist on @layer.
1008  * @get_auto_transition is called to check if a particular transition exists.
1009  * If @track is specified, we will create the transitions only for that particular
1010  * track. */
1011 static void
1012 _create_transitions_on_layer (GESTimeline * timeline, GESLayer * layer,
1013     GESTrack * track, GESTrackElement * initiating_obj,
1014     GetAutoTransitionFunc get_auto_transition)
1015 {
1016   guint32 layer_prio;
1017   GSequenceIter *iter;
1018   GESAutoTransition *transition;
1019   GESContainer *toplevel_next;
1020   MoveContext *mv_ctx = &timeline->priv->movecontext;
1021   GESTrack *ctrack = track;
1022   GList *entered = NULL;        /* List of TrackElement for wich we walk through the
1023                                  * "start" but not the "end" in the starts_ends list */
1024   GESTimelinePrivate *priv = timeline->priv;
1025
1026   if (!layer || !ges_layer_get_auto_transition (layer))
1027     return;
1028
1029   layer_prio = ges_layer_get_priority (layer);
1030   for (iter = g_sequence_get_begin_iter (priv->starts_ends);
1031       iter && !g_sequence_iter_is_end (iter);
1032       iter = g_sequence_iter_next (iter)) {
1033     GList *tmp;
1034     guint *start_or_end = g_sequence_get (iter);
1035     GESTrackElement *next = g_hash_table_lookup (timeline->priv->by_object,
1036         start_or_end);
1037     GESContainer *toplevel =
1038         get_toplevel_container (GES_TIMELINE_ELEMENT (next));
1039
1040     /* Only object that are in that layer and track */
1041     if (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (next) != layer_prio ||
1042         (track && track != ges_track_element_get_track (next)))
1043       continue;
1044
1045     if (track == NULL)
1046       ctrack = ges_track_element_get_track (next);
1047
1048     if (start_or_end == g_hash_table_lookup (priv->by_end, next)) {
1049       if (initiating_obj == next) {
1050         /* We passed the objects that initiated the research
1051          * we are now done */
1052         g_list_free (entered);
1053         return;
1054       }
1055       entered = g_list_remove (entered, next);
1056
1057       continue;
1058     }
1059
1060     toplevel_next = get_toplevel_container (next);
1061     for (tmp = entered; tmp; tmp = tmp->next) {
1062       gint64 transition_duration;
1063       GESTrackElement *prev = tmp->data;
1064       GESContainer *toplevel_prev = get_toplevel_container (prev);
1065
1066       /* If we are not in the same track, we do not create a transition */
1067       if (ctrack != ges_track_element_get_track (prev))
1068         continue;
1069
1070       /* If elements are in the same toplevel element, we do not create a transition */
1071       if (get_toplevel_container (GES_TIMELINE_ELEMENT (prev)) == toplevel)
1072         continue;
1073
1074       /* If the element is inside a container we are moving, we do not
1075        * create a transition */
1076       if (g_hash_table_lookup (mv_ctx->toplevel_containers, toplevel_prev) &&
1077           g_hash_table_lookup (mv_ctx->toplevel_containers, toplevel_next))
1078         continue;
1079
1080       transition_duration = (_START (prev) + _DURATION (prev)) - _START (next);
1081       if (transition_duration > 0 && transition_duration < _DURATION (prev) &&
1082           transition_duration < _DURATION (next)) {
1083         transition =
1084             get_auto_transition (timeline, layer, ctrack, prev, next,
1085             transition_duration);
1086         if (!transition)
1087           create_transition (timeline, prev, next, NULL, layer,
1088               _START (next), transition_duration);
1089       }
1090     }
1091
1092     /* And add that object to the entered list so that it we can possibly set
1093      * a transition on its end edge */
1094     entered = g_list_append (entered, next);
1095   }
1096 }
1097
1098 /* @track_element must be a GESSource */
1099 void
1100 timeline_create_transitions (GESTimeline * timeline,
1101     GESTrackElement * track_element)
1102 {
1103   GESTrack *track;
1104   GList *layer_node;
1105
1106   GESTimelinePrivate *priv = timeline->priv;
1107   MoveContext *mv_ctx = &timeline->priv->movecontext;
1108
1109   if (!priv->needs_transitions_update)
1110     return;
1111
1112   if (mv_ctx->moving_trackelements &&
1113       GES_TIMELINE_ELEMENT_START (track_element) > mv_ctx->start) {
1114     GST_DEBUG_OBJECT (timeline, "Not creating transition around %"
1115         GES_TIMELINE_ELEMENT_FORMAT " as it is not the first rippled"
1116         " element", GES_TIMELINE_ELEMENT_ARGS (track_element));
1117     return;
1118   }
1119
1120   track = ges_track_element_get_track (track_element);
1121   layer_node = g_list_find_custom (timeline->layers,
1122       GINT_TO_POINTER (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (track_element)),
1123       (GCompareFunc) find_layer_by_prio);
1124
1125   _create_transitions_on_layer (timeline,
1126       layer_node ? layer_node->data : NULL, track, track_element,
1127       _find_transition_from_auto_transitions);
1128
1129   GST_DEBUG_OBJECT (timeline, "Done updating transitions");
1130 }
1131
1132 /* Timeline edition functions */
1133 static inline void
1134 init_movecontext (MoveContext * mv_ctx, gboolean first_init)
1135 {
1136   if (G_UNLIKELY (first_init))
1137     mv_ctx->toplevel_containers =
1138         g_hash_table_new (g_direct_hash, g_direct_equal);
1139
1140   mv_ctx->moving_trackelements = NULL;
1141   mv_ctx->start = G_MAXUINT64;
1142   mv_ctx->max_trim_pos = G_MAXUINT64;
1143   mv_ctx->min_move_layer = G_MAXUINT;
1144   mv_ctx->max_layer_prio = 0;
1145   mv_ctx->last_snaped1 = NULL;
1146   mv_ctx->last_snaped2 = NULL;
1147   mv_ctx->last_snap_ts = NULL;
1148   mv_ctx->moving_to_layer = NULL;
1149 }
1150
1151 static inline void
1152 clean_movecontext (MoveContext * mv_ctx)
1153 {
1154   g_list_free (mv_ctx->moving_trackelements);
1155   g_hash_table_remove_all (mv_ctx->toplevel_containers);
1156   init_movecontext (mv_ctx, FALSE);
1157 }
1158
1159 static void
1160 stop_tracking_track_element (GESTimeline * timeline,
1161     GESTrackElement * trackelement)
1162 {
1163   guint64 *start, *end;
1164   TrackObjIters *iters;
1165   GESTimelinePrivate *priv = timeline->priv;
1166
1167   iters = g_hash_table_lookup (priv->obj_iters, trackelement);
1168   if (G_LIKELY (iters->iter_by_layer)) {
1169     g_sequence_remove (iters->iter_by_layer);
1170   } else {
1171     GST_WARNING_OBJECT (timeline, "TrackElement %p was in no layer",
1172         trackelement);
1173   }
1174
1175   if (GES_IS_SOURCE (trackelement)) {
1176     start = g_hash_table_lookup (priv->by_start, trackelement);
1177     end = g_hash_table_lookup (priv->by_end, trackelement);
1178
1179     g_hash_table_remove (priv->by_start, trackelement);
1180     g_hash_table_remove (priv->by_end, trackelement);
1181     g_hash_table_remove (priv->by_object, end);
1182     g_hash_table_remove (priv->by_object, start);
1183     g_sequence_remove (iters->iter_start);
1184     g_sequence_remove (iters->iter_end);
1185     g_sequence_remove (iters->iter_obj);
1186     timeline_update_duration (timeline);
1187   }
1188   g_hash_table_remove (priv->obj_iters, trackelement);
1189 }
1190
1191 static void
1192 start_tracking_track_element (GESTimeline * timeline,
1193     GESTrackElement * trackelement)
1194 {
1195   guint64 *pstart, *pend;
1196   GSequence *by_layer_sequence;
1197   TrackObjIters *iters;
1198   GESTimelinePrivate *priv = timeline->priv;
1199
1200   guint layer_prio = GES_TIMELINE_ELEMENT_LAYER_PRIORITY (trackelement);
1201   GList *layer_node = g_list_find_custom (timeline->layers,
1202       GINT_TO_POINTER (layer_prio), (GCompareFunc) find_layer_by_prio);
1203   GESLayer *layer = layer_node ? layer_node->data : NULL;
1204
1205   iters = g_slice_new0 (TrackObjIters);
1206
1207   /* We add all TrackElement to obj_iters as we always follow them
1208    * in the by_layer Sequences */
1209   g_hash_table_insert (priv->obj_iters, trackelement, iters);
1210
1211   /* Track all objects by layer */
1212   if (G_UNLIKELY (layer == NULL)) {
1213     /* We handle the case where we have TrackElement that are in no layer by not
1214      * tracking them
1215      *
1216      * FIXME: Check if we should rather try to track them in some sort of
1217      * dummy layer, or even refuse TrackElements to be added in Tracks if
1218      * they land in no layer the timeline controls.
1219      */
1220     GST_ERROR_OBJECT (timeline, "Adding a TrackElement that lands in no layer "
1221         "we are controlling");
1222   } else {
1223     by_layer_sequence = g_hash_table_lookup (priv->by_layer, layer);
1224     iters->iter_by_layer =
1225         g_sequence_insert_sorted (by_layer_sequence, trackelement,
1226         (GCompareDataFunc) element_start_compare, NULL);
1227     iters->layer = layer;
1228   }
1229
1230   if (GES_IS_SOURCE (trackelement)) {
1231     /* Track only sources for timeline edition and snapping */
1232     pstart = g_malloc (sizeof (guint64));
1233     pend = g_malloc (sizeof (guint64));
1234     *pstart = _START (trackelement);
1235     *pend = *pstart + _DURATION (trackelement);
1236
1237     iters->iter_start = g_sequence_insert_sorted (priv->starts_ends, pstart,
1238         (GCompareDataFunc) compare_uint64, NULL);
1239     iters->iter_end = g_sequence_insert_sorted (priv->starts_ends, pend,
1240         (GCompareDataFunc) compare_uint64, NULL);
1241     iters->iter_obj =
1242         g_sequence_insert_sorted (priv->tracksources,
1243         gst_object_ref (trackelement), (GCompareDataFunc) element_start_compare,
1244         NULL);
1245     iters->trackelement = trackelement;
1246
1247     g_hash_table_insert (priv->by_start, trackelement, pstart);
1248     g_hash_table_insert (priv->by_object, pstart, trackelement);
1249     g_hash_table_insert (priv->by_end, trackelement, pend);
1250     g_hash_table_insert (priv->by_object, pend, trackelement);
1251
1252     timeline->priv->movecontext.needs_move_ctx = TRUE;
1253
1254     timeline_update_duration (timeline);
1255     timeline_create_transitions (timeline, trackelement);
1256   }
1257 }
1258
1259 static inline void
1260 ges_timeline_emit_snappig (GESTimeline * timeline, GESTrackElement * obj1,
1261     guint64 * timecode)
1262 {
1263   GESTrackElement *obj2;
1264   MoveContext *mv_ctx = &timeline->priv->movecontext;
1265   GstClockTime snap_time = timecode ? *timecode : 0;
1266   GstClockTime last_snap_ts = mv_ctx->last_snap_ts ?
1267       *mv_ctx->last_snap_ts : GST_CLOCK_TIME_NONE;
1268
1269   GST_DEBUG_OBJECT (timeline, "Distance: %" GST_TIME_FORMAT " snapping at %"
1270       GST_TIME_FORMAT, GST_TIME_ARGS (timeline->priv->snapping_distance),
1271       GST_TIME_ARGS (snap_time));
1272
1273   if (timecode == NULL) {
1274     if (mv_ctx->last_snaped1 != NULL && mv_ctx->last_snaped2 != NULL) {
1275       g_signal_emit (timeline, ges_timeline_signals[SNAPING_ENDED], 0,
1276           mv_ctx->last_snaped1, mv_ctx->last_snaped2, last_snap_ts);
1277
1278       /* We then need to recalculate the moving context */
1279       timeline->priv->movecontext.needs_move_ctx = TRUE;
1280     }
1281
1282     return;
1283   }
1284
1285   obj2 = g_hash_table_lookup (timeline->priv->by_object, timecode);
1286
1287   if (last_snap_ts != *timecode) {
1288     g_signal_emit (timeline, ges_timeline_signals[SNAPING_ENDED], 0,
1289         mv_ctx->last_snaped1, mv_ctx->last_snaped2, (last_snap_ts));
1290
1291     /* We want the snap start signal to be emited anyway */
1292     mv_ctx->last_snap_ts = NULL;
1293   }
1294
1295   if (mv_ctx->last_snap_ts == NULL) {
1296
1297     mv_ctx->last_snaped1 = obj1;
1298     mv_ctx->last_snaped2 = obj2;
1299     mv_ctx->last_snap_ts = timecode;
1300
1301     g_signal_emit (timeline, ges_timeline_signals[SNAPING_STARTED], 0,
1302         obj1, obj2, *timecode);
1303
1304   }
1305 }
1306
1307 static GstClockTime *
1308 ges_timeline_snap_position (GESTimeline * timeline,
1309     GESTrackElement * trackelement, GstClockTime * current,
1310     GstClockTime timecode, gboolean emit)
1311 {
1312   GESTimelinePrivate *priv = timeline->priv;
1313   GSequenceIter *iter, *end_iter;
1314   GESContainer *container = get_toplevel_container (trackelement);
1315   GstClockTime *ret = NULL;
1316   GstClockTime smallest_offset = G_MAXUINT64;
1317   GstClockTime tmp_pos;
1318
1319   tmp_pos = timecode - priv->snapping_distance;
1320   /* Rippling, not snapping with previous elements */
1321   if (priv->movecontext.moving_trackelements)
1322     tmp_pos = timecode;
1323   iter = g_sequence_search (priv->starts_ends, &tmp_pos,
1324       (GCompareDataFunc) compare_uint64, NULL);
1325
1326   tmp_pos = timecode + priv->snapping_distance;
1327   end_iter = g_sequence_search (priv->starts_ends, &tmp_pos,
1328       (GCompareDataFunc) compare_uint64, NULL);
1329
1330   for (; iter != end_iter && !g_sequence_iter_is_end (iter);
1331       iter = g_sequence_iter_next (iter)) {
1332     GstClockTime *iter_tc = g_sequence_get (iter);
1333     GESTrackElement *tmp_trackelement =
1334         g_hash_table_lookup (priv->by_object, iter_tc);
1335     GESContainer *tmp_container = get_toplevel_container (tmp_trackelement);
1336     GstClockTimeDiff diff;
1337
1338     if (tmp_container == container)
1339       continue;
1340
1341     if (g_hash_table_lookup (priv->movecontext.toplevel_containers,
1342             tmp_container))
1343       continue;
1344
1345     if (timecode > *iter_tc)
1346       diff = timecode - *iter_tc;
1347     else
1348       diff = *iter_tc - timecode;
1349
1350     if (diff > smallest_offset)
1351       break;
1352
1353     smallest_offset = diff;
1354     ret = iter_tc;
1355   }
1356
1357   /* We emit the snapping signal only if we snapped with a different value
1358    * than the current one */
1359   if (emit) {
1360     GstClockTime snap_time = ret ? *ret : GST_CLOCK_TIME_NONE;
1361
1362     if (!timeline->priv->needs_rollback)
1363       ges_timeline_emit_snappig (timeline, trackelement, ret);
1364     else
1365       ges_timeline_emit_snappig (timeline, trackelement, NULL);
1366
1367     GST_DEBUG_OBJECT (timeline, "Snaping at %" GST_TIME_FORMAT,
1368         GST_TIME_ARGS (snap_time));
1369   }
1370
1371   return ret;
1372 }
1373
1374 static inline GESContainer *
1375 add_toplevel_container (MoveContext * mv_ctx, GESTrackElement * trackelement)
1376 {
1377   guint layer_prio;
1378   GESContainer *toplevel = get_toplevel_container (trackelement);
1379
1380   /* Avoid recalculating */
1381   if (!g_hash_table_lookup (mv_ctx->toplevel_containers, toplevel)) {
1382     if (GES_IS_CLIP (toplevel)) {
1383
1384       layer_prio = ges_clip_get_layer_priority (GES_CLIP (toplevel));
1385       if (layer_prio == (guint32) - 1) {
1386         GST_WARNING_OBJECT (toplevel, "Not in any layer, can not move"
1387             " between layers");
1388
1389         return toplevel;
1390       }
1391       mv_ctx->min_move_layer = MIN (mv_ctx->min_move_layer, layer_prio);
1392       mv_ctx->max_layer_prio = MAX (mv_ctx->max_layer_prio, layer_prio);
1393     } else if GES_IS_GROUP
1394       (toplevel) {
1395       mv_ctx->min_move_layer = MIN (mv_ctx->min_move_layer,
1396           _PRIORITY (toplevel));
1397       mv_ctx->max_layer_prio = MAX (mv_ctx->max_layer_prio,
1398           _PRIORITY (toplevel) + GES_CONTAINER_HEIGHT (toplevel));
1399     } else
1400       g_assert_not_reached ();
1401
1402     mv_ctx->start = MIN (mv_ctx->start, GES_TIMELINE_ELEMENT_START (toplevel));
1403     g_hash_table_insert (mv_ctx->toplevel_containers, toplevel, toplevel);
1404
1405   }
1406
1407   return toplevel;
1408 }
1409
1410 static gboolean
1411 ges_move_context_set_objects (GESTimeline * timeline, GESTrackElement * obj,
1412     GESEdge edge)
1413 {
1414   TrackObjIters *iters;
1415   GESTrackElement *tmptrackelement;
1416   guint64 start, tmpend, moving_point = _START (obj);
1417   GSequenceIter *iter, *trackelement_iter, *tmpiter;
1418
1419   MoveContext *mv_ctx = &timeline->priv->movecontext;
1420   iters = g_hash_table_lookup (timeline->priv->obj_iters, obj);
1421   trackelement_iter = iters->iter_obj;
1422   switch (edge) {
1423     case GES_EDGE_START:
1424       /* set it properly in the context of "trimming" */
1425       mv_ctx->max_trim_pos = 0;
1426       mv_ctx->min_trim_pos = 0;
1427       start = _START (obj);
1428
1429       if (g_sequence_iter_is_begin (trackelement_iter))
1430         break;
1431
1432       /* Look for the objects */
1433       for (iter = g_sequence_iter_prev (trackelement_iter);
1434           iter && !g_sequence_iter_is_end (iter);
1435           iter = g_sequence_iter_prev (iter)) {
1436
1437         tmptrackelement = GES_TRACK_ELEMENT (g_sequence_get (iter));
1438         tmpend = _START (tmptrackelement) + _DURATION (tmptrackelement);
1439
1440         if (tmpend <= start && GES_TIMELINE_ELEMENT_PARENT (tmptrackelement)
1441             != GES_TIMELINE_ELEMENT_PARENT (obj)) {
1442           mv_ctx->max_trim_pos =
1443               MAX (mv_ctx->max_trim_pos, _START (tmptrackelement));
1444           mv_ctx->min_trim_pos = MAX (mv_ctx->min_trim_pos,
1445               _START (tmptrackelement) - _INPOINT (tmptrackelement));
1446           mv_ctx->moving_trackelements =
1447               g_list_prepend (mv_ctx->moving_trackelements, tmptrackelement);
1448         }
1449
1450
1451         if (g_sequence_iter_is_begin (iter))
1452           break;
1453       }
1454       break;
1455
1456     case GES_EDGE_END:
1457       moving_point = _START (obj) + _DURATION (obj);
1458       /* fall-through */
1459     case GES_EDGE_NONE:        /* In this case only works for ripple */
1460       mv_ctx->max_trim_pos = G_MAXUINT64;
1461
1462       iter = trackelement_iter;
1463       tmpiter = g_sequence_iter_prev (iter);
1464
1465       /* Make sure to get the first TimelineElement starting at
1466        * @moving_point */
1467       while (tmpiter && !g_sequence_iter_is_end (tmpiter)) {
1468         tmptrackelement = GES_TRACK_ELEMENT (g_sequence_get (iter));
1469
1470         if (GES_TIMELINE_ELEMENT_START (tmptrackelement) != moving_point)
1471           break;
1472
1473         iter = tmpiter;
1474         tmpiter = g_sequence_iter_prev (tmpiter);
1475
1476         if (g_sequence_iter_is_begin (tmpiter))
1477           break;
1478       }
1479
1480       /* Look for folowing objects */
1481       for (; iter && !g_sequence_iter_is_end (iter);
1482           iter = g_sequence_iter_next (iter)) {
1483         tmptrackelement = GES_TRACK_ELEMENT (g_sequence_get (iter));
1484
1485         if (_START (tmptrackelement) >= moving_point &&
1486             GES_TIMELINE_ELEMENT_PARENT (tmptrackelement) !=
1487             GES_TIMELINE_ELEMENT_PARENT (obj)) {
1488           tmpend = _START (tmptrackelement) + _DURATION (tmptrackelement);
1489           mv_ctx->max_trim_pos = MIN (mv_ctx->max_trim_pos, tmpend);
1490           mv_ctx->moving_trackelements =
1491               g_list_prepend (mv_ctx->moving_trackelements, tmptrackelement);
1492         }
1493       }
1494       break;
1495     default:
1496       GST_DEBUG ("Edge type %d no supported", edge);
1497       return FALSE;
1498   }
1499
1500   return TRUE;
1501 }
1502
1503 static gboolean
1504 ges_timeline_set_moving_context (GESTimeline * timeline, GESTrackElement * obj,
1505     GESEditMode mode, GESEdge edge, GList * layers)
1506 {
1507   /* A TrackElement that could initiate movement for other object */
1508   GESTrackElement *editor_trackelement = NULL;
1509   MoveContext *mv_ctx = &timeline->priv->movecontext;
1510   GESClip *clip = GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (obj));
1511
1512   /* Still in the same mv_ctx */
1513   if ((mv_ctx->clip == clip && mv_ctx->mode == mode &&
1514           mv_ctx->edge == edge && !mv_ctx->needs_move_ctx)) {
1515
1516     GST_DEBUG ("Keeping the same moving mv_ctx");
1517     return TRUE;
1518   }
1519
1520
1521   GST_DEBUG_OBJECT (clip,
1522       "Changing context:\nold: obj: %p, mode: %d, edge: %d \n"
1523       "new: obj: %p, mode: %d, edge: %d ! Has changed %i", mv_ctx->clip,
1524       mv_ctx->mode, mv_ctx->edge, clip, mode, edge, mv_ctx->needs_move_ctx);
1525
1526   /* Make sure snapping context is reset when changing the moving context */
1527   ges_timeline_emit_snappig (timeline, NULL, NULL);
1528   clean_movecontext (mv_ctx);
1529   mv_ctx->edge = edge;
1530   mv_ctx->mode = mode;
1531   mv_ctx->clip = clip;
1532   mv_ctx->needs_move_ctx = FALSE;
1533
1534   /* We try to find a Source inside the Clip so we can set the
1535    * moving context Else we just move the selected one only */
1536   if (GES_IS_SOURCE (obj) == FALSE) {
1537     GList *tmp;
1538
1539     for (tmp = GES_CONTAINER_CHILDREN (clip); tmp; tmp = tmp->next) {
1540       if (GES_IS_SOURCE (tmp->data)) {
1541         editor_trackelement = tmp->data;
1542         break;
1543       }
1544     }
1545   } else {
1546     editor_trackelement = obj;
1547   }
1548
1549   if (editor_trackelement) {
1550     switch (mode) {
1551       case GES_EDIT_MODE_RIPPLE:
1552       case GES_EDIT_MODE_ROLL:
1553         if (!(ges_move_context_set_objects (timeline, editor_trackelement,
1554                     edge)))
1555           return FALSE;
1556       default:
1557         break;
1558     }
1559     add_toplevel_container (&timeline->priv->movecontext, editor_trackelement);
1560   } else {
1561     /* We add the main object to the toplevel_containers set */
1562     add_toplevel_container (&timeline->priv->movecontext, obj);
1563   }
1564
1565
1566   return TRUE;
1567 }
1568
1569 static gboolean
1570 _trim_transition (GESTimeline * timeline, GESLayer * layer,
1571     GESTrackElement * element, GESEdge edge, GstClockTime position)
1572 {
1573
1574   GList *tmp;
1575
1576   if (!ges_layer_get_auto_transition (layer))
1577     goto fail;
1578
1579   gst_object_unref (layer);
1580   for (tmp = timeline->priv->auto_transitions; tmp; tmp = tmp->next) {
1581     GESAutoTransition *auto_transition = tmp->data;
1582
1583     if (auto_transition->transition == GES_TRACK_ELEMENT (element)) {
1584       /* Trimming an auto transition mean trimming its neighboors */
1585       if (!auto_transition->positioning) {
1586         if (edge == GES_EDGE_END) {
1587           ges_container_edit (GES_CONTAINER (auto_transition->previous_clip),
1588               NULL, -1, GES_EDIT_MODE_TRIM, GES_EDGE_END, position);
1589         } else {
1590           ges_container_edit (GES_CONTAINER (auto_transition->next_clip),
1591               NULL, -1, GES_EDIT_MODE_TRIM, GES_EDGE_START, position);
1592         }
1593
1594         return TRUE;
1595       }
1596
1597       return FALSE;
1598     }
1599   }
1600
1601   return FALSE;
1602
1603 fail:
1604   gst_object_unref (layer);
1605   return FALSE;
1606 }
1607
1608 gboolean
1609 ges_timeline_trim_object_simple (GESTimeline * timeline,
1610     GESTimelineElement * element, GList * layers, GESEdge edge,
1611     guint64 position, gboolean snapping)
1612 {
1613   guint64 start, inpoint, duration, max_duration, *snapped, *cur;
1614   gboolean ret = TRUE;
1615   gint64 real_dur;
1616   GESTrackElement *track_element;
1617
1618   if (GES_IS_TRANSITION (element)) {
1619     return _trim_transition (timeline,
1620         ges_clip_get_layer (GES_CLIP (GES_TIMELINE_ELEMENT_PARENT (element))),
1621         GES_TRACK_ELEMENT (element), edge, position);
1622   } else if (GES_IS_SOURCE (element) == FALSE) {
1623     return FALSE;
1624   }
1625
1626   track_element = GES_TRACK_ELEMENT (element);
1627   GST_DEBUG_OBJECT (track_element, "Trimming to %" GST_TIME_FORMAT
1628       " %s snaping, edge %i", GST_TIME_ARGS (position),
1629       snapping ? "Is" : "Not", edge);
1630
1631   start = _START (track_element);
1632   g_object_get (track_element, "max-duration", &max_duration, NULL);
1633
1634   switch (edge) {
1635     case GES_EDGE_START:
1636     {
1637       GESTimelineElement *toplevel;
1638       GESChildrenControlMode old_mode;
1639       gboolean use_inpoint;
1640       toplevel = ges_timeline_element_get_toplevel_parent (element);
1641
1642       if (position < _START (toplevel) && _START (toplevel) < _START (element)) {
1643         GST_DEBUG_OBJECT (toplevel, "Not trimming %p as not at begining "
1644             "of the container", element);
1645
1646         gst_object_unref (toplevel);
1647         return FALSE;
1648       }
1649
1650       old_mode = GES_CONTAINER (toplevel)->children_control_mode;
1651       if (GES_IS_GROUP (toplevel) && old_mode == GES_CHILDREN_UPDATE) {
1652         GST_DEBUG_OBJECT (toplevel, "Setting children udpate mode to"
1653             " UPDDATE_ALL_VALUES so we can trim without moving the contained");
1654         /* The container will update its values itself according to new
1655          * values of the children */
1656         GES_CONTAINER (toplevel)->children_control_mode =
1657             GES_CHILDREN_UPDATE_ALL_VALUES;
1658       }
1659
1660       inpoint = _INPOINT (track_element);
1661       duration = _DURATION (track_element);
1662
1663       if (snapping) {
1664         cur = g_hash_table_lookup (timeline->priv->by_start, track_element);
1665
1666         snapped = ges_timeline_snap_position (timeline, track_element, cur,
1667             position, TRUE);
1668         if (snapped)
1669           position = *snapped;
1670       }
1671
1672       /* Calculate new values */
1673       position = MIN (position, start + duration);
1674
1675       use_inpoint =
1676           GES_TIMELINE_ELEMENT_GET_CLASS (track_element)->set_inpoint ? TRUE :
1677           FALSE;
1678
1679       if (use_inpoint && inpoint + position < start) {
1680         GST_ERROR_OBJECT (timeline, "Track element %s inpoint %" GST_TIME_FORMAT
1681             " would be negative,"
1682             " not trimming", GES_TIMELINE_ELEMENT_NAME (track_element),
1683             GST_TIME_ARGS (inpoint));
1684         gst_object_unref (toplevel);
1685         return FALSE;
1686       }
1687
1688       inpoint = inpoint + position - start;
1689       real_dur = _END (element) - position;
1690       if (use_inpoint)
1691         duration = CLAMP (real_dur, 0, max_duration > inpoint ?
1692             max_duration - inpoint : G_MAXUINT64);
1693       else
1694         duration = real_dur;
1695
1696
1697       /* If we already are at max duration or duration == 0 do no useless work */
1698       if ((duration == _DURATION (track_element) &&
1699               _DURATION (track_element) == _MAXDURATION (track_element)) ||
1700           (duration == 0 && _DURATION (element) == 0)) {
1701         GST_DEBUG_OBJECT (track_element,
1702             "Duration already == max_duration, no triming");
1703         gst_object_unref (toplevel);
1704         return FALSE;
1705       }
1706
1707       timeline->priv->needs_transitions_update = FALSE;
1708       _set_start0 (GES_TIMELINE_ELEMENT (track_element), position);
1709       _set_inpoint0 (GES_TIMELINE_ELEMENT (track_element), inpoint);
1710       timeline->priv->needs_transitions_update = TRUE;
1711
1712       _set_duration0 (GES_TIMELINE_ELEMENT (track_element), duration);
1713       if (GES_IS_GROUP (toplevel))
1714         GES_CONTAINER (toplevel)->children_control_mode = old_mode;
1715
1716       gst_object_unref (toplevel);
1717       break;
1718     }
1719     case GES_EDGE_END:
1720     {
1721       cur = g_hash_table_lookup (timeline->priv->by_end, track_element);
1722       snapped = ges_timeline_snap_position (timeline, track_element, cur,
1723           position, TRUE);
1724       if (snapped)
1725         position = *snapped;
1726
1727       /* Calculate new values */
1728       real_dur = position - start;
1729       duration = MAX (0, real_dur);
1730       duration = MIN (duration, max_duration - _INPOINT (track_element));
1731
1732       /* Not moving, avoid overhead */
1733       if (duration == _DURATION (track_element)) {
1734         GST_DEBUG_OBJECT (track_element, "No change in duration");
1735         return FALSE;
1736       }
1737
1738       _set_duration0 (GES_TIMELINE_ELEMENT (track_element), duration);
1739       break;
1740     }
1741     default:
1742       GST_WARNING ("Can not trim with %i GESEdge", edge);
1743       return FALSE;
1744   }
1745
1746   return ret;
1747 }
1748
1749 gboolean
1750 timeline_ripple_object (GESTimeline * timeline, GESTrackElement * obj,
1751     GList * layers, GESEdge edge, guint64 position)
1752 {
1753   GList *tmp, *moved_clips = NULL;
1754   GESTrackElement *trackelement;
1755   GESContainer *container;
1756   guint64 duration, new_start, *snapped, *cur;
1757   gint64 offset;
1758
1759   MoveContext *mv_ctx = &timeline->priv->movecontext;
1760
1761   mv_ctx->ignore_needs_ctx = TRUE;
1762   timeline->priv->needs_rollback = FALSE;
1763   if (!ges_timeline_set_moving_context (timeline, obj, GES_EDIT_MODE_RIPPLE,
1764           edge, layers))
1765     goto error;
1766
1767   switch (edge) {
1768     case GES_EDGE_NONE:
1769       GST_DEBUG ("Simply rippling");
1770
1771       /* We should be smart here to avoid recalculate transitions when possible */
1772       cur = g_hash_table_lookup (timeline->priv->by_end, obj);
1773       snapped = ges_timeline_snap_position (timeline, obj, cur, position, TRUE);
1774       if (snapped)
1775         position = *snapped;
1776
1777       offset = position - _START (obj);
1778
1779       for (tmp = mv_ctx->moving_trackelements; tmp; tmp = tmp->next) {
1780         trackelement = GES_TRACK_ELEMENT (tmp->data);
1781         new_start = _START (trackelement) + offset;
1782
1783         container = add_toplevel_container (mv_ctx, trackelement);
1784         /* Make sure not to move 2 times the same Clip */
1785         if (g_list_find (moved_clips, container) == NULL) {
1786           _set_start0 (GES_TIMELINE_ELEMENT (trackelement), new_start);
1787           moved_clips = g_list_prepend (moved_clips, container);
1788         }
1789
1790       }
1791       g_list_free (moved_clips);
1792       _set_start0 (GES_TIMELINE_ELEMENT (obj), position);
1793
1794       moved_clips = NULL;
1795       if (timeline->priv->needs_rollback && !timeline->priv->rolling_back) {
1796         timeline->priv->rolling_back = TRUE;
1797         for (tmp = mv_ctx->moving_trackelements; tmp; tmp = tmp->next) {
1798           trackelement = GES_TRACK_ELEMENT (tmp->data);
1799           new_start = _START (trackelement) - offset;
1800
1801           container = add_toplevel_container (mv_ctx, trackelement);
1802           /* Make sure not to move 2 times the same Clip */
1803           if (g_list_find (moved_clips, container) == NULL) {
1804             _set_start0 (GES_TIMELINE_ELEMENT (trackelement), new_start);
1805             moved_clips = g_list_prepend (moved_clips, container);
1806           }
1807
1808         }
1809         g_list_free (moved_clips);
1810         moved_clips = NULL;
1811         _set_start0 (GES_TIMELINE_ELEMENT (obj), position - offset);
1812
1813         ges_timeline_emit_snappig (timeline, obj, NULL);
1814         mv_ctx->needs_move_ctx = TRUE;
1815         timeline->priv->rolling_back = FALSE;
1816
1817         goto error;
1818       }
1819
1820       break;
1821     case GES_EDGE_END:
1822       timeline->priv->needs_transitions_update = FALSE;
1823       GST_DEBUG ("Rippling end");
1824
1825       cur = g_hash_table_lookup (timeline->priv->by_end, obj);
1826       snapped = ges_timeline_snap_position (timeline, obj, cur, position, TRUE);
1827       if (snapped)
1828         position = *snapped;
1829
1830       duration = _DURATION (obj);
1831
1832       if (!ges_timeline_trim_object_simple (timeline,
1833               GES_TIMELINE_ELEMENT (obj), NULL, GES_EDGE_END, position,
1834               FALSE)) {
1835         return FALSE;
1836       }
1837
1838       offset = _DURATION (obj) - duration;
1839       for (tmp = mv_ctx->moving_trackelements; tmp; tmp = tmp->next) {
1840         trackelement = GES_TRACK_ELEMENT (tmp->data);
1841         new_start = _START (trackelement) + offset;
1842
1843         container = add_toplevel_container (mv_ctx, trackelement);
1844         if (GES_IS_GROUP (container))
1845           container->children_control_mode = GES_CHILDREN_UPDATE_OFFSETS;
1846         /* Make sure not to move 2 times the same Clip */
1847         if (g_list_find (moved_clips, container) == NULL) {
1848           _set_start0 (GES_TIMELINE_ELEMENT (trackelement), new_start);
1849           moved_clips = g_list_prepend (moved_clips, container);
1850         }
1851         if (GES_IS_GROUP (container))
1852           container->children_control_mode = GES_CHILDREN_UPDATE;
1853       }
1854
1855       g_list_free (moved_clips);
1856       timeline->priv->needs_transitions_update = TRUE;
1857       GST_DEBUG ("Done Rippling end");
1858       break;
1859     case GES_EDGE_START:
1860       GST_INFO ("Ripple start doesn't make sense, trimming instead");
1861       timeline->priv->movecontext.needs_move_ctx = TRUE;
1862       timeline_trim_object (timeline, obj, layers, edge, position);
1863       break;
1864     default:
1865       GST_DEBUG ("Can not ripple edge: %i", edge);
1866
1867       break;
1868   }
1869
1870   mv_ctx->ignore_needs_ctx = FALSE;
1871
1872   return TRUE;
1873
1874 error:
1875   mv_ctx->ignore_needs_ctx = FALSE;
1876
1877   return FALSE;
1878 }
1879
1880 gboolean
1881 timeline_slide_object (GESTimeline * timeline, GESTrackElement * obj,
1882     GList * layers, GESEdge edge, guint64 position)
1883 {
1884
1885   /* FIXME implement me! */
1886   GST_FIXME_OBJECT (timeline, "Slide mode editing not implemented yet");
1887
1888   return FALSE;
1889 }
1890
1891 gboolean
1892 timeline_trim_object (GESTimeline * timeline, GESTrackElement * object,
1893     GList * layers, GESEdge edge, guint64 position)
1894 {
1895   gboolean ret = FALSE;
1896   GstClockTime cpos;
1897   MoveContext *mv_ctx = &timeline->priv->movecontext;
1898
1899   mv_ctx->ignore_needs_ctx = TRUE;
1900
1901   timeline->priv->needs_rollback = FALSE;
1902   if (!ges_timeline_set_moving_context (timeline, object, GES_EDIT_MODE_TRIM,
1903           edge, layers))
1904     goto end;
1905
1906   switch (edge) {
1907     case GES_EDGE_START:
1908       cpos = GES_TIMELINE_ELEMENT_START (object);
1909       break;
1910     case GES_EDGE_END:
1911       cpos = GES_TIMELINE_ELEMENT_END (object);
1912       break;
1913     default:
1914       goto end;
1915   }
1916   ret = ges_timeline_trim_object_simple (timeline,
1917       GES_TIMELINE_ELEMENT (object), layers, edge, position, TRUE);
1918
1919   if (timeline->priv->needs_rollback && !timeline->priv->rolling_back) {
1920     timeline->priv->rolling_back = TRUE;
1921     ret = FALSE;
1922     timeline_trim_object (timeline, object, layers, edge, cpos);
1923     ges_timeline_emit_snappig (timeline, object, NULL);
1924     timeline->priv->rolling_back = FALSE;
1925   }
1926
1927 end:
1928   mv_ctx->ignore_needs_ctx = FALSE;
1929
1930   return ret;
1931 }
1932
1933 gboolean
1934 timeline_roll_object (GESTimeline * timeline, GESTrackElement * obj,
1935     GList * layers, GESEdge edge, guint64 position)
1936 {
1937   MoveContext *mv_ctx = &timeline->priv->movecontext;
1938   guint64 start, duration, end, tmpstart, tmpduration, tmpend, *snapped, *cur;
1939   gboolean ret = TRUE;
1940   GList *tmp;
1941
1942   mv_ctx->ignore_needs_ctx = TRUE;
1943
1944   GST_DEBUG_OBJECT (obj, "Rolling object to %" GST_TIME_FORMAT,
1945       GST_TIME_ARGS (position));
1946
1947   if (!ges_timeline_set_moving_context (timeline, obj, GES_EDIT_MODE_ROLL,
1948           edge, layers))
1949     goto error;
1950
1951   start = _START (obj);
1952   duration = _DURATION (obj);
1953   end = start + duration;
1954
1955   timeline->priv->needs_transitions_update = FALSE;
1956   switch (edge) {
1957     case GES_EDGE_START:
1958
1959       /* Avoid negative durations */
1960       if (position < mv_ctx->max_trim_pos || position > end ||
1961           position < mv_ctx->min_trim_pos)
1962         goto error;
1963
1964       cur = g_hash_table_lookup (timeline->priv->by_start, obj);
1965       snapped = ges_timeline_snap_position (timeline, obj, cur, position, TRUE);
1966       if (snapped)
1967         position = *snapped;
1968
1969       ret &= ges_timeline_trim_object_simple (timeline,
1970           GES_TIMELINE_ELEMENT (obj), layers, GES_EDGE_START, position, FALSE);
1971
1972       if (!ret) {
1973         GST_INFO_OBJECT (timeline, "Could not trim %s",
1974             GES_TIMELINE_ELEMENT_NAME (obj));
1975
1976         return FALSE;
1977       }
1978
1979
1980       /* In the case we reached max_duration we just make sure to roll
1981        * everything to the real new position */
1982       position = _START (obj);
1983
1984       /* Send back changes to the neighbourhood */
1985       for (tmp = mv_ctx->moving_trackelements; tmp; tmp = tmp->next) {
1986         GESTimelineElement *tmpelement = GES_TIMELINE_ELEMENT (tmp->data);
1987
1988         tmpstart = _START (tmpelement);
1989         tmpduration = _DURATION (tmpelement);
1990         tmpend = tmpstart + tmpduration;
1991
1992         /* Check that the object should be resized at this position
1993          * even if an error accurs, we keep doing our job */
1994         if (tmpend == start) {
1995           ret &= ges_timeline_trim_object_simple (timeline, tmpelement, NULL,
1996               GES_EDGE_END, position, FALSE);
1997           break;
1998         }
1999       }
2000       break;
2001     case GES_EDGE_END:
2002
2003       /* Avoid negative durations */
2004       if (position > mv_ctx->max_trim_pos || position < start)
2005         goto error;
2006
2007       end = _START (obj) + _DURATION (obj);
2008
2009       cur = g_hash_table_lookup (timeline->priv->by_end, obj);
2010       snapped = ges_timeline_snap_position (timeline, obj, cur, position, TRUE);
2011       if (snapped)
2012         position = *snapped;
2013
2014       ret &= ges_timeline_trim_object_simple (timeline,
2015           GES_TIMELINE_ELEMENT (obj), NULL, GES_EDGE_END, position, FALSE);
2016
2017       if (ret == FALSE) {
2018         GST_DEBUG_OBJECT (timeline, "No triming, bailing out");
2019         goto done;
2020       }
2021
2022       /* In the case we reached max_duration we just make sure to roll
2023        * everything to the real new position */
2024       position = _START (obj) + _DURATION (obj);
2025
2026       /* Send back changes to the neighbourhood */
2027       for (tmp = mv_ctx->moving_trackelements; tmp; tmp = tmp->next) {
2028         GESTimelineElement *tmpelement = GES_TIMELINE_ELEMENT (tmp->data);
2029
2030         tmpstart = _START (tmpelement);
2031         tmpduration = _DURATION (tmpelement);
2032         tmpend = tmpstart + tmpduration;
2033
2034         /* Check that the object should be resized at this position
2035          * even if an error accure, we keep doing our job */
2036         if (end == tmpstart) {
2037           ret &= ges_timeline_trim_object_simple (timeline, tmpelement, NULL,
2038               GES_EDGE_START, position, FALSE);
2039         }
2040       }
2041       break;
2042     default:
2043       GST_DEBUG ("Edge type %i not handled here", edge);
2044       break;
2045   }
2046
2047 done:
2048   timeline->priv->needs_transitions_update = TRUE;
2049   mv_ctx->ignore_needs_ctx = FALSE;
2050
2051   return ret;
2052
2053 error:
2054   GST_DEBUG_OBJECT (obj, "Could not roll edge %d to %" GST_TIME_FORMAT,
2055       edge, GST_TIME_ARGS (position));
2056
2057   ret = FALSE;
2058   goto done;
2059 }
2060
2061 gboolean
2062 timeline_move_object (GESTimeline * timeline, GESTrackElement * object,
2063     GList * layers, GESEdge edge, guint64 position)
2064 {
2065   if (!ges_timeline_set_moving_context (timeline, object, GES_EDIT_MODE_NORMAL,
2066           edge, layers)) {
2067     GST_DEBUG_OBJECT (object, "Could not move to %" GST_TIME_FORMAT,
2068         GST_TIME_ARGS (position));
2069
2070     return FALSE;
2071   }
2072
2073   return ges_timeline_move_object_simple (timeline,
2074       GES_TIMELINE_ELEMENT (object), layers, edge, position);
2075 }
2076
2077 gboolean
2078 ges_timeline_move_object_simple (GESTimeline * timeline,
2079     GESTimelineElement * element, GList * layers, GESEdge edge,
2080     guint64 position)
2081 {
2082   GstClockTime cpos = GES_TIMELINE_ELEMENT_START (element);
2083   guint64 *snap_end, *snap_st, *cur, position_offset, off1, off2, top_end;
2084   GESTrackElement *track_element;
2085   GESContainer *toplevel;
2086
2087   /* We only work with GESSource-s and we check that we are not already moving
2088    * the specified element ourself */
2089   if (GES_IS_SOURCE (element) == FALSE ||
2090       g_list_find (timeline->priv->movecontext.moving_trackelements, element))
2091     return FALSE;
2092
2093   timeline->priv->needs_rollback = FALSE;
2094   track_element = GES_TRACK_ELEMENT (element);
2095   toplevel = get_toplevel_container (track_element);
2096   position_offset = position - _START (track_element);
2097
2098   top_end = _START (toplevel) + _DURATION (toplevel) + position_offset;
2099   cur = g_hash_table_lookup (timeline->priv->by_end, track_element);
2100
2101   GST_DEBUG_OBJECT (timeline, "Moving %" GST_PTR_FORMAT " to %"
2102       GST_TIME_FORMAT " (end %" GST_TIME_FORMAT ")", element,
2103       GST_TIME_ARGS (position), GST_TIME_ARGS (top_end));
2104
2105   snap_end = ges_timeline_snap_position (timeline, track_element, cur, top_end,
2106       FALSE);
2107   if (snap_end)
2108     off1 = top_end > *snap_end ? top_end - *snap_end : *snap_end - top_end;
2109   else
2110     off1 = G_MAXUINT64;
2111
2112   cur = g_hash_table_lookup (timeline->priv->by_start, track_element);
2113   snap_st =
2114       ges_timeline_snap_position (timeline, track_element, cur, position,
2115       FALSE);
2116   if (snap_st)
2117     off2 = position > *snap_st ? position - *snap_st : *snap_st - position;
2118   else
2119     off2 = G_MAXUINT64;
2120
2121   /* In the case we could snap on both sides, we snap on the end */
2122   if (snap_end && off1 <= off2) {
2123     position = position + *snap_end - top_end;
2124     ges_timeline_emit_snappig (timeline, track_element, snap_end);
2125   } else if (snap_st) {
2126     position = position + *snap_st - position;
2127     ges_timeline_emit_snappig (timeline, track_element, snap_st);
2128   } else
2129     ges_timeline_emit_snappig (timeline, track_element, NULL);
2130   timeline->priv->needs_rollback = FALSE;
2131
2132   _set_start0 (GES_TIMELINE_ELEMENT (track_element), position);
2133
2134   if (timeline->priv->needs_rollback && !timeline->priv->rolling_back) {
2135     timeline->priv->needs_rollback = FALSE;
2136     timeline->priv->rolling_back = TRUE;
2137     ges_timeline_move_object_simple (timeline, element, layers, edge, cpos);
2138     ges_timeline_emit_snappig (timeline, track_element, NULL);
2139     timeline->priv->rolling_back = FALSE;
2140
2141     return FALSE;
2142   }
2143
2144   return TRUE;
2145 }
2146
2147 gboolean
2148 timeline_context_to_layer (GESTimeline * timeline, gint offset)
2149 {
2150   gboolean ret = TRUE;
2151   GHashTableIter iter;
2152   GESContainer *key, *value;
2153   GESLayer *new_layer;
2154   guint prio;
2155   MoveContext *mv_ctx = &timeline->priv->movecontext;
2156
2157   /* Layer's priority is always positive */
2158   if (offset == 0)
2159     return ret;
2160
2161   if (offset < 0 && mv_ctx->min_move_layer < -offset)
2162     return ret;
2163
2164   GST_DEBUG ("Moving %d object, offset %d",
2165       g_hash_table_size (mv_ctx->toplevel_containers), offset);
2166
2167   mv_ctx->ignore_needs_ctx = TRUE;
2168   timeline->priv->needs_rollback = FALSE;
2169   g_hash_table_iter_init (&iter, mv_ctx->toplevel_containers);
2170   while (g_hash_table_iter_next (&iter, (gpointer *) & key,
2171           (gpointer *) & value)) {
2172
2173     if (GES_IS_CLIP (value)) {
2174       prio = ges_clip_get_layer_priority (GES_CLIP (value));
2175
2176       /* We know that the layer exists as we created it */
2177       new_layer = GES_LAYER (g_list_nth_data (timeline->layers, prio + offset));
2178
2179       if (new_layer == NULL) {
2180         do {
2181           new_layer = ges_timeline_append_layer (timeline);
2182         } while (ges_layer_get_priority (new_layer) < prio + offset);
2183       }
2184
2185       mv_ctx->moving_to_layer = new_layer;
2186       ret &= ges_clip_move_to_layer (GES_CLIP (key), new_layer);
2187     } else if (GES_IS_GROUP (value)) {
2188       guint32 last_prio = _PRIORITY (value) + offset +
2189           GES_CONTAINER_HEIGHT (value) - 1;
2190
2191       new_layer = GES_LAYER (g_list_nth_data (timeline->layers, last_prio));
2192
2193       if (new_layer == NULL) {
2194         do {
2195           new_layer = ges_timeline_append_layer (timeline);
2196         } while (ges_layer_get_priority (new_layer) < last_prio);
2197       }
2198
2199       mv_ctx->moving_to_layer = NULL;
2200       _set_priority0 (GES_TIMELINE_ELEMENT (value), _PRIORITY (value) + offset);
2201     }
2202   }
2203
2204   /* Readjust min_move_layer */
2205   mv_ctx->min_move_layer = mv_ctx->min_move_layer + offset;
2206   mv_ctx->ignore_needs_ctx = FALSE;
2207
2208   if (timeline->priv->needs_rollback && !timeline->priv->rolling_back) {
2209     ret = FALSE;
2210     timeline->priv->rolling_back = TRUE;
2211     timeline_context_to_layer (timeline, -offset);
2212     timeline->priv->rolling_back = FALSE;
2213   }
2214   mv_ctx->moving_to_layer = NULL;
2215
2216   return ret;
2217 }
2218
2219 void
2220 timeline_add_group (GESTimeline * timeline, GESGroup * group)
2221 {
2222   GST_DEBUG_OBJECT (timeline, "Adding group %" GST_PTR_FORMAT, group);
2223
2224   timeline->priv->movecontext.needs_move_ctx = TRUE;
2225   timeline->priv->groups = g_list_prepend (timeline->priv->groups,
2226       gst_object_ref_sink (group));
2227
2228   ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (group), timeline);
2229 }
2230
2231 /**
2232  * timeline_emit_group_added:
2233  * @timeline: a #GESTimeline
2234  * @group: group that was added
2235  *
2236  * Emit group-added signal.
2237  */
2238 void
2239 timeline_emit_group_added (GESTimeline * timeline, GESGroup * group)
2240 {
2241   g_signal_emit (timeline, ges_timeline_signals[GROUP_ADDED], 0, group);
2242 }
2243
2244 /**
2245  * timeline_emit_group_removed:
2246  * @timeline: a #GESTimeline
2247  * @group: group that was removed
2248  * @array: (element-type GESTimelineElement): children that were removed
2249  *
2250  * Emit group-removed signal.
2251  */
2252 void
2253 timeline_emit_group_removed (GESTimeline * timeline, GESGroup * group,
2254     GPtrArray * array)
2255 {
2256   g_signal_emit (timeline, ges_timeline_signals[GROUP_REMOVED], 0, group,
2257       array);
2258 }
2259
2260 void
2261 timeline_remove_group (GESTimeline * timeline, GESGroup * group)
2262 {
2263   GST_DEBUG_OBJECT (timeline, "Removing group %" GST_PTR_FORMAT, group);
2264
2265   timeline->priv->groups = g_list_remove (timeline->priv->groups, group);
2266
2267   timeline->priv->movecontext.needs_move_ctx = TRUE;
2268   ges_timeline_element_set_timeline (GES_TIMELINE_ELEMENT (group), NULL);
2269   gst_object_unref (group);
2270 }
2271
2272 static GPtrArray *
2273 select_tracks_for_object_default (GESTimeline * timeline,
2274     GESClip * clip, GESTrackElement * tr_object, gpointer user_data)
2275 {
2276   GPtrArray *result;
2277   GList *tmp;
2278
2279   result = g_ptr_array_new ();
2280
2281   for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
2282     GESTrack *track = GES_TRACK (tmp->data);
2283
2284     if ((track->type & ges_track_element_get_track_type (tr_object))) {
2285       gst_object_ref (track);
2286       g_ptr_array_add (result, track);
2287     }
2288   }
2289
2290   return result;
2291 }
2292
2293 static void
2294 add_object_to_tracks (GESTimeline * timeline, GESClip * clip, GESTrack * track)
2295 {
2296   gint i;
2297   GList *tmp, *list;
2298   GESTrackType types, visited_type = GES_TRACK_TYPE_UNKNOWN;
2299
2300   GST_DEBUG_OBJECT (timeline, "Creating %" GST_PTR_FORMAT
2301       " trackelements and adding them to our tracks", clip);
2302
2303   types = ges_clip_get_supported_formats (clip);
2304   if (track) {
2305     if ((types & track->type) == 0)
2306       return;
2307     types = track->type;
2308   }
2309
2310   for (i = 0, tmp = timeline->tracks; tmp; tmp = tmp->next, i++) {
2311     GESTrack *track = GES_TRACK (tmp->data);
2312
2313     if (((track->type & types) == 0 || (track->type & visited_type)))
2314       continue;
2315
2316     list = ges_clip_create_track_elements (clip, track->type);
2317     g_list_free (list);
2318   }
2319 }
2320
2321 static void
2322 layer_auto_transition_changed_cb (GESLayer * layer,
2323     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
2324 {
2325   GList *tmp, *clips;
2326
2327   timeline->priv->needs_rollback = FALSE;
2328   _create_transitions_on_layer (timeline, layer, NULL, NULL,
2329       _create_auto_transition_from_transitions);
2330
2331   clips = ges_layer_get_clips (layer);
2332   for (tmp = clips; tmp; tmp = tmp->next) {
2333     if (GES_IS_TRANSITION_CLIP (tmp->data)) {
2334       GList *tmpautotrans;
2335       gboolean found = FALSE;
2336
2337       for (tmpautotrans = timeline->priv->auto_transitions; tmpautotrans;
2338           tmpautotrans = tmpautotrans->next) {
2339         if (GES_AUTO_TRANSITION (tmpautotrans->data)->transition_clip ==
2340             tmp->data) {
2341           found = TRUE;
2342           break;
2343         }
2344       }
2345
2346       if (!found) {
2347         GST_ERROR_OBJECT (timeline,
2348             "Transition %s could not be wrapped into an auto transition"
2349             " REMOVING it", GES_TIMELINE_ELEMENT_NAME (tmp->data));
2350
2351         ges_layer_remove_clip (layer, tmp->data);
2352       }
2353     }
2354   }
2355   g_list_free_full (clips, gst_object_unref);
2356 }
2357
2358 static void
2359 clip_track_element_added_cb (GESClip * clip,
2360     GESTrackElement * track_element, GESTimeline * timeline)
2361 {
2362   guint i;
2363   GESTrack *track;
2364   gboolean is_source;
2365   GPtrArray *tracks = NULL;
2366   GESTrackElement *existing_src = NULL;
2367
2368   if (timeline->priv->ignore_track_element_added == clip) {
2369     GST_DEBUG_OBJECT (timeline, "Ignoring element added (%" GST_PTR_FORMAT
2370         " in %" GST_PTR_FORMAT, track_element, clip);
2371
2372     return;
2373   }
2374
2375   if (ges_track_element_get_track (track_element)) {
2376     GST_WARNING_OBJECT (track_element, "Already in a track");
2377
2378     return;
2379   }
2380
2381   g_signal_emit (G_OBJECT (timeline),
2382       ges_timeline_signals[SELECT_TRACKS_FOR_OBJECT], 0, clip, track_element,
2383       &tracks);
2384
2385   if (!tracks || tracks->len == 0) {
2386     GST_WARNING_OBJECT (timeline, "Got no Track to add %p (type %s), removing"
2387         " from clip (stopping 'child-added' signal emission).",
2388         track_element, ges_track_type_name (ges_track_element_get_track_type
2389             (track_element)));
2390
2391     if (tracks)
2392       g_ptr_array_unref (tracks);
2393
2394     g_signal_stop_emission_by_name (clip, "child-added");
2395     ges_container_remove (GES_CONTAINER (clip),
2396         GES_TIMELINE_ELEMENT (track_element));
2397
2398     return;
2399   }
2400
2401   /* We add the current element to the first track */
2402   track = g_ptr_array_index (tracks, 0);
2403
2404   is_source = g_type_is_a (G_OBJECT_TYPE (track_element), GES_TYPE_SOURCE);
2405   if (is_source)
2406     existing_src = ges_clip_find_track_element (clip, track, GES_TYPE_SOURCE);
2407
2408   if (existing_src == NULL) {
2409     if (!ges_track_add_element (track, track_element)) {
2410       GST_WARNING_OBJECT (clip, "Failed to add track element to track");
2411       ges_container_remove (GES_CONTAINER (clip),
2412           GES_TIMELINE_ELEMENT (track_element));
2413       g_ptr_array_unref (tracks);
2414       return;
2415     }
2416   } else {
2417     GST_INFO_OBJECT (clip, "Already had a Source Element in %" GST_PTR_FORMAT
2418         " of type %s, removing new one. (stopping 'child-added' emission)",
2419         track, G_OBJECT_TYPE_NAME (track_element));
2420     g_signal_stop_emission_by_name (clip, "child-added");
2421     ges_container_remove (GES_CONTAINER (clip),
2422         GES_TIMELINE_ELEMENT (track_element));
2423   }
2424   gst_object_unref (track);
2425   g_clear_object (&existing_src);
2426
2427   /* And create copies to add to other tracks */
2428   timeline->priv->ignore_track_element_added = clip;
2429   for (i = 1; i < tracks->len; i++) {
2430     GESTrack *track;
2431     GESTrackElement *track_element_copy;
2432
2433     track = g_ptr_array_index (tracks, i);
2434     if (is_source)
2435       existing_src = ges_clip_find_track_element (clip, track, GES_TYPE_SOURCE);
2436     if (existing_src == NULL) {
2437       ges_container_remove (GES_CONTAINER (clip),
2438           GES_TIMELINE_ELEMENT (track_element));
2439       gst_object_unref (track);
2440       g_ptr_array_unref (tracks);
2441       continue;
2442     } else {
2443       GST_INFO_OBJECT (clip, "Already had a Source Element in %" GST_PTR_FORMAT
2444           " of type %s, removing new one. (stopping 'child-added' emission)",
2445           track, G_OBJECT_TYPE_NAME (track_element));
2446       g_signal_stop_emission_by_name (clip, "child-added");
2447       ges_container_remove (GES_CONTAINER (clip),
2448           GES_TIMELINE_ELEMENT (track_element));
2449     }
2450     g_clear_object (&existing_src);
2451
2452     track_element_copy =
2453         GES_TRACK_ELEMENT (ges_timeline_element_copy (GES_TIMELINE_ELEMENT
2454             (track_element), TRUE));
2455
2456     GST_LOG_OBJECT (timeline, "Trying to add %p to track %p",
2457         track_element_copy, track);
2458
2459     if (!ges_container_add (GES_CONTAINER (clip),
2460             GES_TIMELINE_ELEMENT (track_element_copy))) {
2461       GST_WARNING_OBJECT (clip, "Failed to add track element to clip");
2462       gst_object_unref (track_element_copy);
2463       g_ptr_array_unref (tracks);
2464       return;
2465     }
2466
2467     if (!ges_track_add_element (track, track_element_copy)) {
2468       GST_WARNING_OBJECT (clip, "Failed to add track element to track");
2469       ges_container_remove (GES_CONTAINER (clip),
2470           GES_TIMELINE_ELEMENT (track_element_copy));
2471       gst_object_unref (track_element_copy);
2472       g_ptr_array_unref (tracks);
2473       return;
2474     }
2475
2476     gst_object_unref (track);
2477   }
2478   timeline->priv->ignore_track_element_added = NULL;
2479   g_ptr_array_unref (tracks);
2480 }
2481
2482 static void
2483 clip_track_element_removed_cb (GESClip * clip,
2484     GESTrackElement * track_element, GESTimeline * timeline)
2485 {
2486   GESTrack *track = ges_track_element_get_track (track_element);
2487
2488   if (track)
2489     ges_track_remove_element (track, track_element);
2490 }
2491
2492 static void
2493 layer_object_added_cb (GESLayer * layer, GESClip * clip, GESTimeline * timeline)
2494 {
2495   GESProject *project;
2496
2497   /* We make sure not to be connected twice */
2498   g_signal_handlers_disconnect_by_func (clip, clip_track_element_added_cb,
2499       timeline);
2500   g_signal_handlers_disconnect_by_func (clip, clip_track_element_removed_cb,
2501       timeline);
2502
2503   /* And we connect to the object */
2504   g_signal_connect (clip, "child-added",
2505       G_CALLBACK (clip_track_element_added_cb), timeline);
2506   g_signal_connect (clip, "child-removed",
2507       G_CALLBACK (clip_track_element_removed_cb), timeline);
2508
2509   if (ges_clip_is_moving_from_layer (clip)) {
2510     GST_DEBUG ("Clip %p moving from one layer to another, not creating "
2511         "TrackElement", clip);
2512     timeline->priv->movecontext.needs_move_ctx = TRUE;
2513     _create_transitions_on_layer (timeline, layer, NULL, NULL,
2514         _find_transition_from_auto_transitions);
2515     return;
2516   }
2517
2518
2519   add_object_to_tracks (timeline, clip, NULL);
2520
2521   GST_DEBUG ("Making sure that the asset is in our project");
2522   project =
2523       GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE (timeline)));
2524   ges_project_add_asset (project,
2525       ges_extractable_get_asset (GES_EXTRACTABLE (clip)));
2526
2527   GST_DEBUG ("Done");
2528 }
2529
2530 static void
2531 layer_priority_changed_cb (GESLayer * layer,
2532     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
2533 {
2534   if (timeline->priv->resyncing_layers)
2535     return;
2536
2537   timeline->layers = g_list_sort (timeline->layers, (GCompareFunc)
2538       sort_layers);
2539 }
2540
2541 static void
2542 layer_object_removed_cb (GESLayer * layer, GESClip * clip,
2543     GESTimeline * timeline)
2544 {
2545   GList *trackelements, *tmp;
2546
2547   if (ges_clip_is_moving_from_layer (clip)) {
2548     GST_DEBUG ("Clip %p is moving from a layer to another, not doing"
2549         " anything on it", clip);
2550     return;
2551   }
2552
2553   GST_DEBUG ("Clip %p removed from layer %p", clip, layer);
2554
2555   /* Go over the clip's track element and figure out which one belongs to
2556    * the list of tracks we control */
2557
2558   trackelements = ges_container_get_children (GES_CONTAINER (clip), FALSE);
2559   for (tmp = trackelements; tmp; tmp = tmp->next) {
2560     GESTrackElement *track_element = (GESTrackElement *) tmp->data;
2561     GESTrack *track = ges_track_element_get_track (track_element);
2562
2563     if (!track)
2564       continue;
2565
2566     GST_DEBUG_OBJECT (timeline, "Trying to remove TrackElement %p",
2567         track_element);
2568
2569     /* FIXME Check if we should actually check that we control the
2570      * track in the new management of TrackElement context */
2571     LOCK_DYN (timeline);
2572     if (G_LIKELY (g_list_find_custom (timeline->priv->priv_tracks, track,
2573                 (GCompareFunc) custom_find_track) || track == NULL)) {
2574       GST_DEBUG ("Belongs to one of the tracks we control");
2575
2576       ges_track_remove_element (track, track_element);
2577     }
2578     UNLOCK_DYN (timeline);
2579   }
2580   g_signal_handlers_disconnect_by_func (clip, clip_track_element_added_cb,
2581       timeline);
2582   g_signal_handlers_disconnect_by_func (clip, clip_track_element_removed_cb,
2583       timeline);
2584
2585   g_list_free_full (trackelements, gst_object_unref);
2586
2587   GST_DEBUG ("Done");
2588 }
2589
2590 static void
2591 trackelement_start_changed_cb (GESTrackElement * child,
2592     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
2593 {
2594   GESTimelinePrivate *priv = timeline->priv;
2595   TrackObjIters *iters = g_hash_table_lookup (priv->obj_iters, child);
2596
2597   if (G_LIKELY (iters->iter_by_layer))
2598     g_sequence_sort_changed (iters->iter_by_layer,
2599         (GCompareDataFunc) element_start_compare, NULL);
2600
2601   if (GES_IS_SOURCE (child)) {
2602     sort_track_elements (timeline, iters);
2603     sort_starts_ends_start (timeline, iters);
2604     sort_starts_ends_end (timeline, iters);
2605
2606     /* If the timeline is set to snap objects together, we
2607      * are sure that all movement of TrackElement-s are done within
2608      * the moving context, so we do not need to recalculate the
2609      * move context as often */
2610     if (timeline->priv->movecontext.ignore_needs_ctx &&
2611         timeline->priv->snapping_distance == 0)
2612       timeline->priv->movecontext.needs_move_ctx = TRUE;
2613
2614     timeline_create_transitions (timeline, child);
2615   }
2616 }
2617
2618 static void
2619 trackelement_priority_changed_cb (GESTrackElement * child,
2620     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
2621 {
2622   GESTimelinePrivate *priv = timeline->priv;
2623
2624   GList *layer_node = g_list_find_custom (timeline->layers,
2625       GINT_TO_POINTER (GES_TIMELINE_ELEMENT_LAYER_PRIORITY (child)),
2626       (GCompareFunc) find_layer_by_prio);
2627   GESLayer *layer = layer_node ? layer_node->data : NULL;
2628   TrackObjIters *iters = g_hash_table_lookup (priv->obj_iters,
2629       child);
2630
2631   if (G_UNLIKELY (layer == NULL)) {
2632     GST_ERROR_OBJECT (timeline,
2633         "Changing a TrackElement prio, which would not "
2634         "land in no layer we are controlling");
2635     if (iters->iter_by_layer)
2636       g_sequence_remove (iters->iter_by_layer);
2637     iters->iter_by_layer = NULL;
2638     iters->layer = NULL;
2639   } else {
2640     /* If it moves from layer, properly change it */
2641     if (layer != iters->layer) {
2642       GSequence *by_layer_sequence =
2643           g_hash_table_lookup (priv->by_layer, layer);
2644
2645       GST_DEBUG_OBJECT (child, "Moved from layer %" GST_PTR_FORMAT
2646           "(prio %d) to" " %" GST_PTR_FORMAT " (prio %d)", layer,
2647           ges_layer_get_priority (layer), iters->layer,
2648           ges_layer_get_priority (iters->layer));
2649
2650       g_sequence_remove (iters->iter_by_layer);
2651       iters->iter_by_layer =
2652           g_sequence_insert_sorted (by_layer_sequence, child,
2653           (GCompareDataFunc) element_start_compare, NULL);
2654       iters->layer = layer;
2655     } else {
2656       g_sequence_sort_changed (iters->iter_by_layer,
2657           (GCompareDataFunc) element_start_compare, NULL);
2658     }
2659   }
2660
2661   if (GES_IS_SOURCE (child))
2662     sort_track_elements (timeline, iters);
2663 }
2664
2665 static void
2666 trackelement_duration_changed_cb (GESTrackElement * child,
2667     GParamSpec * arg G_GNUC_UNUSED, GESTimeline * timeline)
2668 {
2669   GESTimelinePrivate *priv = timeline->priv;
2670   TrackObjIters *iters = g_hash_table_lookup (priv->obj_iters, child);
2671
2672   if (GES_IS_SOURCE (child)) {
2673     sort_starts_ends_end (timeline, iters);
2674
2675     /* If the timeline is set to snap objects together, we
2676      * are sure that all movement of TrackElement-s are done within
2677      * the moving context, so we do not need to recalculate the
2678      * move context as often */
2679     if (timeline->priv->movecontext.ignore_needs_ctx &&
2680         timeline->priv->snapping_distance == 0) {
2681       timeline->priv->movecontext.needs_move_ctx = TRUE;
2682     }
2683
2684     timeline_create_transitions (timeline, child);
2685   }
2686 }
2687
2688 static void
2689 track_element_added_cb (GESTrack * track, GESTrackElement * track_element,
2690     GESTimeline * timeline)
2691 {
2692   /* Auto transition should be updated before we receive the signal */
2693   g_signal_connect_after (GES_TRACK_ELEMENT (track_element), "notify::start",
2694       G_CALLBACK (trackelement_start_changed_cb), timeline);
2695   g_signal_connect_after (GES_TRACK_ELEMENT (track_element),
2696       "notify::duration", G_CALLBACK (trackelement_duration_changed_cb),
2697       timeline);
2698   g_signal_connect_after (GES_TRACK_ELEMENT (track_element),
2699       "notify::priority", G_CALLBACK (trackelement_priority_changed_cb),
2700       timeline);
2701
2702   start_tracking_track_element (timeline, track_element);
2703 }
2704
2705 static void
2706 track_element_removed_cb (GESTrack * track,
2707     GESTrackElement * track_element, GESTimeline * timeline)
2708 {
2709
2710   if (GES_IS_SOURCE (track_element)) {
2711     /* Make sure to reinitialise the moving context next time */
2712     timeline->priv->movecontext.needs_move_ctx = TRUE;
2713   }
2714
2715   /* Disconnect all signal handlers */
2716   g_signal_handlers_disconnect_by_func (track_element,
2717       trackelement_start_changed_cb, timeline);
2718   g_signal_handlers_disconnect_by_func (track_element,
2719       trackelement_duration_changed_cb, timeline);
2720   g_signal_handlers_disconnect_by_func (track_element,
2721       trackelement_priority_changed_cb, timeline);
2722
2723   stop_tracking_track_element (timeline, track_element);
2724 }
2725
2726 static GstPadProbeReturn
2727 _pad_probe_cb (GstPad * mixer_pad, GstPadProbeInfo * info,
2728     GESTimeline * timeline)
2729 {
2730   GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
2731   if (GST_EVENT_TYPE (event) == GST_EVENT_STREAM_START) {
2732     LOCK_DYN (timeline);
2733     if (timeline->priv->group_id == -1) {
2734       if (!gst_event_parse_group_id (event, &timeline->priv->group_id))
2735         timeline->priv->group_id = gst_util_group_id_next ();
2736     }
2737
2738     info->data = gst_event_make_writable (event);
2739     gst_event_set_group_id (GST_PAD_PROBE_INFO_EVENT (info),
2740         timeline->priv->group_id);
2741     UNLOCK_DYN (timeline);
2742
2743     return GST_PAD_PROBE_REMOVE;
2744   }
2745
2746   return GST_PAD_PROBE_OK;
2747 }
2748
2749 static void
2750 _ghost_track_srcpad (TrackPrivate * tr_priv)
2751 {
2752   GstPad *pad;
2753   gchar *padname;
2754   gboolean no_more;
2755   GList *tmp;
2756   GESTrack *track = tr_priv->track;
2757
2758   pad = gst_element_get_static_pad (GST_ELEMENT (track), "src");
2759
2760   GST_DEBUG ("track:%p, pad:%s:%s", track, GST_DEBUG_PAD_NAME (pad));
2761
2762   /* Remember the pad */
2763   LOCK_DYN (tr_priv->timeline);
2764   GST_OBJECT_LOCK (track);
2765   tr_priv->pad = pad;
2766
2767   no_more = TRUE;
2768   for (tmp = tr_priv->timeline->priv->priv_tracks; tmp; tmp = g_list_next (tmp)) {
2769     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
2770
2771     if (!tr_priv->pad) {
2772       GST_LOG ("Found track without pad %p", tr_priv->track);
2773       no_more = FALSE;
2774     }
2775   }
2776   GST_OBJECT_UNLOCK (track);
2777
2778   /* ghost it ! */
2779   GST_DEBUG ("Ghosting pad and adding it to ourself");
2780   padname = g_strdup_printf ("track_%p_src", track);
2781   tr_priv->ghostpad = gst_ghost_pad_new (padname, pad);
2782   g_free (padname);
2783   gst_pad_set_active (tr_priv->ghostpad, TRUE);
2784   gst_element_add_pad (GST_ELEMENT (tr_priv->timeline), tr_priv->ghostpad);
2785
2786   if (no_more) {
2787     GST_DEBUG ("Signaling no-more-pads");
2788     gst_element_no_more_pads (GST_ELEMENT (tr_priv->timeline));
2789   }
2790
2791   tr_priv->probe_id = gst_pad_add_probe (pad,
2792       GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
2793       (GstPadProbeCallback) _pad_probe_cb, tr_priv->timeline, NULL);
2794
2795   UNLOCK_DYN (tr_priv->timeline);
2796 }
2797
2798 gboolean
2799 timeline_add_element (GESTimeline * timeline, GESTimelineElement * element)
2800 {
2801   GESTimelineElement *same_name =
2802       g_hash_table_lookup (timeline->priv->all_elements,
2803       element->name);
2804
2805   GST_DEBUG_OBJECT (timeline, "Adding element: %s", element->name);
2806   if (same_name) {
2807     GST_ERROR_OBJECT (timeline, "%s Already in the timeline %" GST_PTR_FORMAT,
2808         element->name, same_name);
2809     return FALSE;
2810   }
2811
2812   g_hash_table_insert (timeline->priv->all_elements,
2813       ges_timeline_element_get_name (element), gst_object_ref (element));
2814
2815   return TRUE;
2816 }
2817
2818 gboolean
2819 timeline_remove_element (GESTimeline * timeline, GESTimelineElement * element)
2820 {
2821   return g_hash_table_remove (timeline->priv->all_elements, element->name);
2822 }
2823
2824 void
2825 timeline_fill_gaps (GESTimeline * timeline)
2826 {
2827   GList *tmp;
2828
2829   for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
2830     track_resort_and_fill_gaps (tmp->data);
2831   }
2832 }
2833
2834 /**** API *****/
2835 /**
2836  * ges_timeline_new:
2837  *
2838  * Creates a new empty #GESTimeline.
2839  *
2840  * Returns: (transfer floating): The new timeline.
2841  */
2842
2843 GESTimeline *
2844 ges_timeline_new (void)
2845 {
2846   GESProject *project = ges_project_new (NULL);
2847   GESExtractable *timeline = g_object_new (GES_TYPE_TIMELINE, NULL);
2848
2849   ges_extractable_set_asset (timeline, GES_ASSET (project));
2850   gst_object_unref (project);
2851
2852   return GES_TIMELINE (timeline);
2853 }
2854
2855 /**
2856  * ges_timeline_new_from_uri:
2857  * @uri: the URI to load from
2858  * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
2859  *
2860  * Creates a timeline from the given URI.
2861  *
2862  * Returns: (transfer floating) (nullable): A new timeline if the uri was loaded
2863  * successfully, or %NULL if the uri could not be loaded.
2864  */
2865 GESTimeline *
2866 ges_timeline_new_from_uri (const gchar * uri, GError ** error)
2867 {
2868   GESTimeline *ret;
2869   GESProject *project = ges_project_new (uri);
2870
2871   ret = GES_TIMELINE (ges_asset_extract (GES_ASSET (project), error));
2872   gst_object_unref (project);
2873
2874   return ret;
2875 }
2876
2877 /**
2878  * ges_timeline_load_from_uri:
2879  * @timeline: an empty #GESTimeline into which to load the formatter
2880  * @uri: The URI to load from
2881  * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
2882  *
2883  * Loads the contents of URI into the given timeline.
2884  *
2885  * Returns: %TRUE if the timeline was loaded successfully, or %FALSE if the uri
2886  * could not be loaded.
2887  */
2888 gboolean
2889 ges_timeline_load_from_uri (GESTimeline * timeline, const gchar * uri,
2890     GError ** error)
2891 {
2892   GESProject *project;
2893   gboolean ret = FALSE;
2894
2895   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
2896   g_return_val_if_fail ((ges_extractable_get_asset (GES_EXTRACTABLE
2897               (timeline)) == NULL), FALSE);
2898
2899   project = ges_project_new (uri);
2900   ret = ges_project_load (project, timeline, error);
2901   gst_object_unref (project);
2902
2903   return ret;
2904 }
2905
2906 /**
2907  * ges_timeline_save_to_uri:
2908  * @timeline: a #GESTimeline
2909  * @uri: The location to save to
2910  * @formatter_asset: (allow-none): The formatter asset to use or %NULL. If %NULL,
2911  * will try to save in the same format as the one from which the timeline as been loaded
2912  * or default to the formatter with highest rank
2913  * @overwrite: %TRUE to overwrite file if it exists
2914  * @error: (out) (allow-none): An error to be set in case something wrong happens or %NULL
2915  *
2916  * Saves the timeline to the given location
2917  *
2918  * Returns: %TRUE if the timeline was successfully saved to the given location,
2919  * else %FALSE.
2920  */
2921 gboolean
2922 ges_timeline_save_to_uri (GESTimeline * timeline, const gchar * uri,
2923     GESAsset * formatter_asset, gboolean overwrite, GError ** error)
2924 {
2925   GESProject *project;
2926
2927   gboolean ret, created_proj = FALSE;
2928
2929   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
2930   project =
2931       GES_PROJECT (ges_extractable_get_asset (GES_EXTRACTABLE (timeline)));
2932
2933   if (project == NULL) {
2934     project = ges_project_new (NULL);
2935     created_proj = TRUE;
2936   }
2937
2938   ret = ges_project_save (project, timeline, uri, formatter_asset, overwrite,
2939       error);
2940
2941   if (created_proj)
2942     gst_object_unref (project);
2943
2944   return ret;
2945 }
2946
2947 /**
2948  * ges_timeline_get_groups:
2949  * @timeline: a #GESTimeline
2950  *
2951  * Get the list of #GESGroup present in the Timeline.
2952  *
2953  * Returns: (transfer none) (element-type GESGroup): the list of
2954  * #GESGroup that contain clips present in the timeline's layers.
2955  * Must not be changed.
2956  */
2957 GList *
2958 ges_timeline_get_groups (GESTimeline * timeline)
2959 {
2960   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
2961   CHECK_THREAD (timeline);
2962
2963   return timeline->priv->groups;
2964 }
2965
2966 /**
2967  * ges_timeline_append_layer:
2968  * @timeline: a #GESTimeline
2969  *
2970  * Append a newly created #GESLayer to @timeline
2971  * Note that you do not own any reference to the returned layer.
2972  *
2973  * Returns: (transfer none): The newly created #GESLayer, or the last (empty)
2974  * #GESLayer of @timeline.
2975  */
2976 GESLayer *
2977 ges_timeline_append_layer (GESTimeline * timeline)
2978 {
2979   guint32 priority;
2980   GESLayer *layer;
2981
2982   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
2983   CHECK_THREAD (timeline);
2984
2985   layer = ges_layer_new ();
2986   priority = g_list_length (timeline->layers);
2987   ges_layer_set_priority (layer, priority);
2988
2989   ges_timeline_add_layer (timeline, layer);
2990
2991   return layer;
2992 }
2993
2994 /**
2995  * ges_timeline_add_layer:
2996  * @timeline: a #GESTimeline
2997  * @layer: (transfer floating): the #GESLayer to add
2998  *
2999  * Add the layer to the timeline. The reference to the @layer will be stolen
3000  * by the @timeline.
3001  *
3002  * Returns: %TRUE if the layer was properly added, else %FALSE.
3003  */
3004 gboolean
3005 ges_timeline_add_layer (GESTimeline * timeline, GESLayer * layer)
3006 {
3007   gboolean auto_transition;
3008   GList *objects, *tmp;
3009
3010   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3011   g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
3012   CHECK_THREAD (timeline);
3013
3014   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
3015
3016   /* We can only add a layer that doesn't already belong to another timeline */
3017   if (G_UNLIKELY (layer->timeline)) {
3018     GST_WARNING ("Layer belongs to another timeline, can't add it");
3019     gst_object_ref_sink (layer);
3020     gst_object_unref (layer);
3021     return FALSE;
3022   }
3023
3024   /* Add to the list of layers, make sure we don't already control it */
3025   if (G_UNLIKELY (g_list_find (timeline->layers, (gconstpointer) layer))) {
3026     GST_WARNING ("Layer is already controlled by this timeline");
3027     gst_object_ref_sink (layer);
3028     gst_object_unref (layer);
3029     return FALSE;
3030   }
3031
3032   auto_transition = ges_layer_get_auto_transition (layer);
3033
3034   /* If the user doesn't explicitely set layer auto_transition, then set our */
3035   if (!auto_transition) {
3036     auto_transition = ges_timeline_get_auto_transition (timeline);
3037     ges_layer_set_auto_transition (layer, auto_transition);
3038   }
3039
3040   gst_object_ref_sink (layer);
3041   timeline->layers = g_list_insert_sorted (timeline->layers, layer,
3042       (GCompareFunc) sort_layers);
3043
3044   /* Inform the layer that it belongs to a new timeline */
3045   ges_layer_set_timeline (layer, timeline);
3046
3047   g_hash_table_insert (timeline->priv->by_layer, layer, g_sequence_new (NULL));
3048
3049   /* Connect to 'clip-added'/'clip-removed' signal from the new layer */
3050   g_signal_connect_after (layer, "clip-added",
3051       G_CALLBACK (layer_object_added_cb), timeline);
3052   g_signal_connect_after (layer, "clip-removed",
3053       G_CALLBACK (layer_object_removed_cb), timeline);
3054   g_signal_connect (layer, "notify::priority",
3055       G_CALLBACK (layer_priority_changed_cb), timeline);
3056   g_signal_connect (layer, "notify::auto-transition",
3057       G_CALLBACK (layer_auto_transition_changed_cb), timeline);
3058
3059   GST_DEBUG ("Done adding layer, emitting 'layer-added' signal");
3060   g_signal_emit (timeline, ges_timeline_signals[LAYER_ADDED], 0, layer);
3061
3062   /* add any existing clips to the timeline */
3063   objects = ges_layer_get_clips (layer);
3064   for (tmp = objects; tmp; tmp = tmp->next) {
3065     layer_object_added_cb (layer, tmp->data, timeline);
3066     gst_object_unref (tmp->data);
3067     tmp->data = NULL;
3068   }
3069   g_list_free (objects);
3070
3071   timeline->priv->movecontext.needs_move_ctx = TRUE;
3072
3073   return TRUE;
3074 }
3075
3076 /**
3077  * ges_timeline_remove_layer:
3078  * @timeline: a #GESTimeline
3079  * @layer: the #GESLayer to remove
3080  *
3081  * Removes the layer from the timeline. The reference that the @timeline holds on
3082  * the layer will be dropped. If you wish to use the @layer after calling this
3083  * method, you need to take a reference before calling.
3084  *
3085  * Returns: %TRUE if the layer was properly removed, else %FALSE.
3086  */
3087
3088 gboolean
3089 ges_timeline_remove_layer (GESTimeline * timeline, GESLayer * layer)
3090 {
3091   GList *layer_objects, *tmp;
3092
3093   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3094   g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
3095   CHECK_THREAD (timeline);
3096
3097   GST_DEBUG ("timeline:%p, layer:%p", timeline, layer);
3098
3099   if (G_UNLIKELY (!g_list_find (timeline->layers, layer))) {
3100     GST_WARNING ("Layer doesn't belong to this timeline");
3101     return FALSE;
3102   }
3103
3104   /* remove objects from any private data structures */
3105
3106   layer_objects = ges_layer_get_clips (layer);
3107   for (tmp = layer_objects; tmp; tmp = tmp->next) {
3108     layer_object_removed_cb (layer, GES_CLIP (tmp->data), timeline);
3109     gst_object_unref (G_OBJECT (tmp->data));
3110     tmp->data = NULL;
3111   }
3112   g_list_free (layer_objects);
3113
3114   /* Disconnect signals */
3115   GST_DEBUG ("Disconnecting signal callbacks");
3116   g_signal_handlers_disconnect_by_func (layer, layer_object_added_cb, timeline);
3117   g_signal_handlers_disconnect_by_func (layer, layer_object_removed_cb,
3118       timeline);
3119   g_signal_handlers_disconnect_by_func (layer, layer_priority_changed_cb,
3120       timeline);
3121   g_signal_handlers_disconnect_by_func (layer,
3122       layer_auto_transition_changed_cb, timeline);
3123
3124   g_hash_table_remove (timeline->priv->by_layer, layer);
3125   timeline->layers = g_list_remove (timeline->layers, layer);
3126   ges_layer_set_timeline (layer, NULL);
3127
3128   g_signal_emit (timeline, ges_timeline_signals[LAYER_REMOVED], 0, layer);
3129
3130   gst_object_unref (layer);
3131   timeline->priv->movecontext.needs_move_ctx = TRUE;
3132
3133   return TRUE;
3134 }
3135
3136 /**
3137  * ges_timeline_add_track:
3138  * @timeline: a #GESTimeline
3139  * @track: (transfer full): the #GESTrack to add
3140  *
3141  * Add a track to the timeline. The reference to the track will be stolen by the
3142  * pipeline.
3143  *
3144  * Returns: %TRUE if the track was properly added, else %FALSE.
3145  */
3146
3147 /* FIXME: create track elements for clips which have already been
3148  * added to existing layers.
3149  */
3150
3151 gboolean
3152 ges_timeline_add_track (GESTimeline * timeline, GESTrack * track)
3153 {
3154   TrackPrivate *tr_priv;
3155   GList *tmp;
3156
3157   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3158   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
3159   CHECK_THREAD (timeline);
3160
3161   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
3162
3163   /* make sure we don't already control it */
3164   if (G_UNLIKELY (g_list_find (timeline->tracks, (gconstpointer) track))) {
3165     GST_WARNING ("Track is already controlled by this timeline");
3166     return FALSE;
3167   }
3168
3169   /* Add the track to ourself (as a GstBin)
3170    * Reference is stolen ! */
3171   if (G_UNLIKELY (!gst_bin_add (GST_BIN (timeline), GST_ELEMENT (track)))) {
3172     GST_WARNING ("Couldn't add track to ourself (GST)");
3173     return FALSE;
3174   }
3175
3176   tr_priv = g_new0 (TrackPrivate, 1);
3177   tr_priv->timeline = timeline;
3178   tr_priv->track = track;
3179
3180   /* Add the track to the list of tracks we track */
3181   LOCK_DYN (timeline);
3182   timeline->priv->priv_tracks = g_list_append (timeline->priv->priv_tracks,
3183       tr_priv);
3184   UNLOCK_DYN (timeline);
3185   timeline->tracks = g_list_append (timeline->tracks, track);
3186
3187   /* Inform the track that it's currently being used by ourself */
3188   ges_track_set_timeline (track, timeline);
3189
3190   GST_DEBUG ("Done adding track, emitting 'track-added' signal");
3191
3192   _ghost_track_srcpad (tr_priv);
3193
3194   /* emit 'track-added' */
3195   g_signal_emit (timeline, ges_timeline_signals[TRACK_ADDED], 0, track);
3196
3197   /* ensure that each existing clip has the opportunity to create a
3198    * track element for this track*/
3199
3200   /* We connect to the object for the timeline editing mode management */
3201   g_signal_connect (G_OBJECT (track), "track-element-added",
3202       G_CALLBACK (track_element_added_cb), timeline);
3203   g_signal_connect (G_OBJECT (track), "track-element-removed",
3204       G_CALLBACK (track_element_removed_cb), timeline);
3205
3206   for (tmp = timeline->layers; tmp; tmp = tmp->next) {
3207     GList *objects, *obj;
3208     objects = ges_layer_get_clips (tmp->data);
3209
3210     for (obj = objects; obj; obj = obj->next) {
3211       GESClip *clip = obj->data;
3212
3213       add_object_to_tracks (timeline, clip, track);
3214       gst_object_unref (clip);
3215     }
3216     g_list_free (objects);
3217   }
3218
3219   /* FIXME Check if we should rollback if we can't sync state */
3220   gst_element_sync_state_with_parent (GST_ELEMENT (track));
3221   g_object_set (track, "message-forward", TRUE, NULL);
3222
3223   return TRUE;
3224 }
3225
3226 /**
3227  * ges_timeline_remove_track:
3228  * @timeline: a #GESTimeline
3229  * @track: the #GESTrack to remove
3230  *
3231  * Remove the @track from the @timeline. The reference stolen when adding the
3232  * @track will be removed. If you wish to use the @track after calling this
3233  * function you must ensure that you have a reference to it.
3234  *
3235  * Returns: %TRUE if the @track was properly removed, else %FALSE.
3236  */
3237
3238 /* FIXME: release any track elements associated with this layer. currenly this
3239  * will not happen if you remove the track before removing *all*
3240  * clips which have a track element in this track.
3241  */
3242
3243 gboolean
3244 ges_timeline_remove_track (GESTimeline * timeline, GESTrack * track)
3245 {
3246   GList *tmp;
3247   TrackPrivate *tr_priv;
3248   GESTimelinePrivate *priv;
3249
3250   g_return_val_if_fail (GES_IS_TRACK (track), FALSE);
3251   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3252   CHECK_THREAD (timeline);
3253
3254   GST_DEBUG ("timeline:%p, track:%p", timeline, track);
3255
3256   priv = timeline->priv;
3257   LOCK_DYN (timeline);
3258   if (G_UNLIKELY (!(tmp = g_list_find_custom (priv->priv_tracks,
3259                   track, (GCompareFunc) custom_find_track)))) {
3260     GST_WARNING ("Track doesn't belong to this timeline");
3261     UNLOCK_DYN (timeline);
3262     return FALSE;
3263   }
3264
3265   tr_priv = tmp->data;
3266   gst_object_unref (tr_priv->pad);
3267   priv->priv_tracks = g_list_remove (priv->priv_tracks, tr_priv);
3268   UNLOCK_DYN (timeline);
3269   timeline->tracks = g_list_remove (timeline->tracks, track);
3270
3271   ges_track_set_timeline (track, NULL);
3272
3273   /* Remove ghost pad */
3274   if (tr_priv->ghostpad) {
3275     GST_DEBUG ("Removing ghostpad");
3276     gst_pad_set_active (tr_priv->ghostpad, FALSE);
3277     gst_ghost_pad_set_target ((GstGhostPad *) tr_priv->ghostpad, NULL);
3278     gst_element_remove_pad (GST_ELEMENT (timeline), tr_priv->ghostpad);
3279   }
3280
3281   /* Remove pad-added/-removed handlers */
3282   g_signal_handlers_disconnect_by_func (track, track_element_added_cb,
3283       timeline);
3284   g_signal_handlers_disconnect_by_func (track, track_element_removed_cb,
3285       timeline);
3286
3287   /* Signal track removal to all layers/objects */
3288   g_signal_emit (timeline, ges_timeline_signals[TRACK_REMOVED], 0, track);
3289
3290   /* remove track from our bin */
3291   gst_object_ref (track);
3292   if (G_UNLIKELY (!gst_bin_remove (GST_BIN (timeline), GST_ELEMENT (track)))) {
3293     GST_WARNING ("Couldn't remove track to ourself (GST)");
3294     gst_object_unref (track);
3295     return FALSE;
3296   }
3297
3298   /* set track state to NULL */
3299   gst_element_set_state (GST_ELEMENT (track), GST_STATE_NULL);
3300
3301   gst_object_unref (track);
3302
3303   g_free (tr_priv);
3304
3305   return TRUE;
3306 }
3307
3308 /**
3309  * ges_timeline_get_track_for_pad:
3310  * @timeline: The #GESTimeline
3311  * @pad: The #GstPad
3312  *
3313  * Search the #GESTrack corresponding to the given @timeline's @pad.
3314  *
3315  * Returns: (transfer none) (nullable): The corresponding #GESTrack if it is
3316  * found, or %NULL if there is an error.
3317  */
3318
3319 GESTrack *
3320 ges_timeline_get_track_for_pad (GESTimeline * timeline, GstPad * pad)
3321 {
3322   GList *tmp;
3323
3324   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
3325
3326   LOCK_DYN (timeline);
3327   for (tmp = timeline->priv->priv_tracks; tmp; tmp = g_list_next (tmp)) {
3328     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
3329     if (pad == tr_priv->ghostpad) {
3330       UNLOCK_DYN (timeline);
3331       return tr_priv->track;
3332     }
3333   }
3334   UNLOCK_DYN (timeline);
3335
3336   return NULL;
3337 }
3338
3339 /**
3340  * ges_timeline_get_pad_for_track:
3341  * @timeline: The #GESTimeline
3342  * @track: The #GESTrack
3343  *
3344  * Search the #GstPad corresponding to the given @timeline's @track.
3345  *
3346  * Returns: (transfer none) (nullable): The corresponding #GstPad if it is
3347  * found, or %NULL if there is an error.
3348  */
3349
3350 GstPad *
3351 ges_timeline_get_pad_for_track (GESTimeline * timeline, GESTrack * track)
3352 {
3353   GList *tmp;
3354
3355   LOCK_DYN (timeline);
3356   for (tmp = timeline->priv->priv_tracks; tmp; tmp = g_list_next (tmp)) {
3357     TrackPrivate *tr_priv = (TrackPrivate *) tmp->data;
3358
3359     if (track == tr_priv->track) {
3360       if (tr_priv->ghostpad)
3361         gst_object_ref (tr_priv->ghostpad);
3362
3363       UNLOCK_DYN (timeline);
3364       return tr_priv->ghostpad;
3365     }
3366   }
3367   UNLOCK_DYN (timeline);
3368
3369   return NULL;
3370 }
3371
3372 /**
3373  * ges_timeline_get_tracks:
3374  * @timeline: a #GESTimeline
3375  *
3376  * Returns the list of #GESTrack used by the Timeline.
3377  *
3378  * Returns: (transfer full) (element-type GESTrack): A list of #GESTrack.
3379  * The caller should unref each track once he is done with them.
3380  */
3381 GList *
3382 ges_timeline_get_tracks (GESTimeline * timeline)
3383 {
3384   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
3385   CHECK_THREAD (timeline);
3386
3387   return g_list_copy_deep (timeline->tracks, (GCopyFunc) gst_object_ref, NULL);
3388 }
3389
3390 /**
3391  * ges_timeline_get_layers:
3392  * @timeline: a #GESTimeline
3393  *
3394  * Get the list of #GESLayer present in the Timeline.
3395  *
3396  * Returns: (transfer full) (element-type GESLayer): the list of
3397  * #GESLayer present in the Timeline sorted by priority.
3398  * The caller should unref each Layer once he is done with them.
3399  */
3400 GList *
3401 ges_timeline_get_layers (GESTimeline * timeline)
3402 {
3403   GList *tmp, *res = NULL;
3404
3405   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
3406   CHECK_THREAD (timeline);
3407
3408   for (tmp = timeline->layers; tmp; tmp = g_list_next (tmp)) {
3409     res = g_list_insert_sorted (res, gst_object_ref (tmp->data),
3410         (GCompareFunc) sort_layers);
3411   }
3412
3413   return res;
3414 }
3415
3416 static void
3417 track_commited_cb (GESTrack * track, GESTimeline * timeline)
3418 {
3419   gboolean emit_commited = FALSE;
3420   GST_OBJECT_LOCK (timeline);
3421   timeline->priv->expected_commited -= 1;
3422   if (timeline->priv->expected_commited == 0)
3423     emit_commited = TRUE;
3424   g_signal_handlers_disconnect_by_func (track, track_commited_cb, timeline);
3425   GST_OBJECT_UNLOCK (timeline);
3426
3427   if (emit_commited) {
3428     g_signal_emit (timeline, ges_timeline_signals[COMMITED], 0);
3429   }
3430 }
3431
3432 /* Must be called with the timeline's DYN_LOCK */
3433 static gboolean
3434 ges_timeline_commit_unlocked (GESTimeline * timeline)
3435 {
3436   GList *tmp;
3437   gboolean res = TRUE;
3438
3439   GST_DEBUG_OBJECT (timeline, "commiting changes");
3440
3441   for (tmp = timeline->layers; tmp; tmp = tmp->next) {
3442     GESLayer *layer = tmp->data;
3443
3444     _create_transitions_on_layer (timeline, layer, NULL, NULL,
3445         _find_transition_from_auto_transitions);
3446
3447     /* Ensure clip priorities are correct after an edit */
3448     ges_layer_resync_priorities (layer);
3449   }
3450
3451   timeline->priv->expected_commited =
3452       g_list_length (timeline->priv->priv_tracks);
3453
3454   if (timeline->priv->expected_commited == 0) {
3455     g_signal_emit (timeline, ges_timeline_signals[COMMITED], 0);
3456   } else {
3457     for (tmp = timeline->tracks; tmp; tmp = tmp->next) {
3458       g_signal_connect (tmp->data, "commited", G_CALLBACK (track_commited_cb),
3459           timeline);
3460       if (!ges_track_commit (GES_TRACK (tmp->data)))
3461         res = FALSE;
3462     }
3463   }
3464
3465   /* Make sure we reset the context */
3466   timeline->priv->movecontext.needs_move_ctx = TRUE;
3467
3468   return res;
3469 }
3470
3471 /**
3472  * ges_timeline_commit:
3473  * @timeline: a #GESTimeline
3474  *
3475  * Commit all the pending changes of the clips contained in the
3476  * @timeline.
3477  *
3478  * When changes happen in a timeline, they are not
3479  * directly executed in the non-linear engine. Call this method once you are
3480  * done with a set of changes and want it to be executed.
3481  *
3482  * The #GESTimeline::commited signal will be emitted when the (possibly updated)
3483  * #GstPipeline is ready to output data again, except if the state of the
3484  * timeline was #GST_STATE_READY or #GST_STATE_NULL.
3485  *
3486  * Note that all the pending changes will automatically be executed when the
3487  * timeline goes from #GST_STATE_READY to #GST_STATE_PAUSED, which usually is
3488  * triggered by corresponding state changes in a containing #GESPipeline.
3489  *
3490  * You should not try to change the state of the timeline, seek it or add
3491  * tracks to it during a commit operation, that is between a call to this
3492  * function and after receiving the #GESTimeline::commited signal.
3493  *
3494  * See #ges_timeline_commit_sync if you don't want to bother with waiting
3495  * for the signal.
3496  *
3497  * Returns: %TRUE if pending changes were commited or %FALSE if nothing needed
3498  * to be commited
3499  */
3500 gboolean
3501 ges_timeline_commit (GESTimeline * timeline)
3502 {
3503   gboolean ret;
3504
3505   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3506
3507   LOCK_DYN (timeline);
3508   ret = ges_timeline_commit_unlocked (timeline);
3509   UNLOCK_DYN (timeline);
3510
3511   ges_timeline_emit_snappig (timeline, NULL, NULL);
3512   return ret;
3513 }
3514
3515 static void
3516 commited_cb (GESTimeline * timeline)
3517 {
3518   g_mutex_lock (&timeline->priv->commited_lock);
3519   g_cond_signal (&timeline->priv->commited_cond);
3520   g_mutex_unlock (&timeline->priv->commited_lock);
3521 }
3522
3523 /**
3524  * ges_timeline_commit_sync:
3525  * @timeline: a #GESTimeline
3526  *
3527  * Commit all the pending changes of the #GESClips contained in the
3528  * @timeline.
3529  *
3530  * Will return once the update is complete, that is when the
3531  * (possibly updated) #GstPipeline is ready to output data again, or if the
3532  * state of the timeline was #GST_STATE_READY or #GST_STATE_NULL.
3533  *
3534  * This function will wait for any pending state change of the timeline by
3535  * calling #gst_element_get_state with a #GST_CLOCK_TIME_NONE timeout, you
3536  * should not try to change the state from another thread before this function
3537  * has returned.
3538  *
3539  * See #ges_timeline_commit for more information.
3540  *
3541  * Returns: %TRUE if pending changes were commited or %FALSE if nothing needed
3542  * to be commited
3543  */
3544 gboolean
3545 ges_timeline_commit_sync (GESTimeline * timeline)
3546 {
3547   gboolean ret;
3548   gboolean wait_for_signal;
3549
3550   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3551
3552   /* Let's make sure our state is stable */
3553   gst_element_get_state (GST_ELEMENT (timeline), NULL, NULL,
3554       GST_CLOCK_TIME_NONE);
3555
3556   /* Let's make sure no track gets added between now and the actual commiting */
3557   LOCK_DYN (timeline);
3558   wait_for_signal = g_list_length (timeline->priv->priv_tracks) > 0
3559       && GST_STATE (timeline) >= GST_STATE_PAUSED;
3560
3561   if (!wait_for_signal) {
3562     ret = ges_timeline_commit_unlocked (timeline);
3563   } else {
3564     gulong handler_id =
3565         g_signal_connect (timeline, "commited", (GCallback) commited_cb, NULL);
3566
3567     g_mutex_lock (&timeline->priv->commited_lock);
3568
3569     ret = ges_timeline_commit_unlocked (timeline);
3570     g_cond_wait (&timeline->priv->commited_cond,
3571         &timeline->priv->commited_lock);
3572     g_mutex_unlock (&timeline->priv->commited_lock);
3573     g_signal_handler_disconnect (timeline, handler_id);
3574   }
3575
3576   UNLOCK_DYN (timeline);
3577
3578   return ret;
3579 }
3580
3581 /**
3582  * ges_timeline_get_duration:
3583  * @timeline: a #GESTimeline
3584  *
3585  * Get the current duration of @timeline
3586  *
3587  * Returns: The current duration of @timeline
3588  */
3589 GstClockTime
3590 ges_timeline_get_duration (GESTimeline * timeline)
3591 {
3592   g_return_val_if_fail (GES_IS_TIMELINE (timeline), GST_CLOCK_TIME_NONE);
3593   CHECK_THREAD (timeline);
3594
3595   return timeline->priv->duration;
3596 }
3597
3598 /**
3599  * ges_timeline_get_auto_transition:
3600  * @timeline: a #GESTimeline
3601  *
3602  * Gets whether transitions are automatically added when objects
3603  * overlap or not.
3604  *
3605  * Returns: %TRUE if transitions are automatically added, else %FALSE.
3606  */
3607 gboolean
3608 ges_timeline_get_auto_transition (GESTimeline * timeline)
3609 {
3610   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3611   CHECK_THREAD (timeline);
3612
3613   return timeline->priv->auto_transition;
3614 }
3615
3616 /**
3617  * ges_timeline_set_auto_transition:
3618  * @timeline: a #GESLayer
3619  * @auto_transition: whether the auto_transition is active
3620  *
3621  * Sets the layer to the given @auto_transition. See the documentation of the
3622  * property auto_transition for more information.
3623  */
3624 void
3625 ges_timeline_set_auto_transition (GESTimeline * timeline,
3626     gboolean auto_transition)
3627 {
3628   GList *layers;
3629   GESLayer *layer;
3630
3631   g_return_if_fail (GES_IS_TIMELINE (timeline));
3632   CHECK_THREAD (timeline);
3633
3634   timeline->priv->auto_transition = auto_transition;
3635   g_object_notify (G_OBJECT (timeline), "auto-transition");
3636
3637   layers = timeline->layers;
3638   for (; layers; layers = layers->next) {
3639     layer = layers->data;
3640     ges_layer_set_auto_transition (layer, auto_transition);
3641   }
3642 }
3643
3644 /**
3645  * ges_timeline_get_snapping_distance:
3646  * @timeline: a #GESTimeline
3647  *
3648  * Gets the configured snapping distance of the timeline. See
3649  * the documentation of the property snapping_distance for more
3650  * information.
3651  *
3652  * Returns: The @snapping_distance property of the timeline
3653  */
3654 GstClockTime
3655 ges_timeline_get_snapping_distance (GESTimeline * timeline)
3656 {
3657   g_return_val_if_fail (GES_IS_TIMELINE (timeline), GST_CLOCK_TIME_NONE);
3658   CHECK_THREAD (timeline);
3659
3660   return timeline->priv->snapping_distance;
3661
3662 }
3663
3664 /**
3665  * ges_timeline_set_snapping_distance:
3666  * @timeline: a #GESLayer
3667  * @snapping_distance: whether the snapping_distance is active
3668  *
3669  * Sets the @snapping_distance of the timeline. See the documentation of the
3670  * property snapping_distance for more information.
3671  */
3672 void
3673 ges_timeline_set_snapping_distance (GESTimeline * timeline,
3674     GstClockTime snapping_distance)
3675 {
3676   g_return_if_fail (GES_IS_TIMELINE (timeline));
3677   CHECK_THREAD (timeline);
3678
3679   timeline->priv->snapping_distance = snapping_distance;
3680 }
3681
3682 /**
3683  * ges_timeline_get_element:
3684  * @timeline: a #GESTimeline
3685  *
3686  * Gets a #GESTimelineElement contained in the timeline
3687  *
3688  * Returns: (transfer full) (nullable): The #GESTimelineElement or %NULL if
3689  * not found.
3690  */
3691 GESTimelineElement *
3692 ges_timeline_get_element (GESTimeline * timeline, const gchar * name)
3693 {
3694   GESTimelineElement *ret;
3695
3696   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
3697   CHECK_THREAD (timeline);
3698
3699   ret = g_hash_table_lookup (timeline->priv->all_elements, name);
3700
3701   if (ret)
3702     return gst_object_ref (ret);
3703
3704 #ifndef GST_DISABLE_GST_DEBUG
3705   {
3706     GList *element_names, *tmp;
3707     element_names = g_hash_table_get_keys (timeline->priv->all_elements);
3708
3709     GST_INFO_OBJECT (timeline, "Does not contain element %s", name);
3710
3711     for (tmp = element_names; tmp; tmp = tmp->next) {
3712       GST_DEBUG_OBJECT (timeline, "Containes: %s", (gchar *) tmp->data);
3713     }
3714     g_list_free (element_names);
3715   }
3716 #endif
3717
3718   return NULL;
3719 }
3720
3721 /**
3722  * ges_timeline_is_empty:
3723  * @timeline: a #GESTimeline
3724  *
3725  * Check whether a #GESTimeline is empty or not
3726  *
3727  * Returns: %TRUE if the timeline is empty %FALSE otherwize
3728  */
3729 gboolean
3730 ges_timeline_is_empty (GESTimeline * timeline)
3731 {
3732   GHashTableIter iter;
3733   gpointer key, value;
3734
3735   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3736   CHECK_THREAD (timeline);
3737
3738   if (g_hash_table_size (timeline->priv->all_elements) == 0)
3739     return TRUE;
3740
3741   g_hash_table_iter_init (&iter, timeline->priv->all_elements);
3742   while (g_hash_table_iter_next (&iter, &key, &value)) {
3743     if (GES_IS_SOURCE (value) &&
3744         ges_track_element_is_active (GES_TRACK_ELEMENT (value)))
3745       return FALSE;
3746   }
3747
3748   return TRUE;
3749 }
3750
3751 /**
3752  * ges_timeline_get_layer:
3753  * @timeline: The #GESTimeline to retrive a layer from
3754  * @priority: The priority of the layer to find
3755  *
3756  * Retrieve the layer with @priority as a priority
3757  *
3758  * Returns: (transfer full) (nullable): A #GESLayer or %NULL if no layer with
3759  * @priority was found
3760  *
3761  * Since 1.6
3762  */
3763 GESLayer *
3764 ges_timeline_get_layer (GESTimeline * timeline, guint priority)
3765 {
3766   GList *tmp;
3767   GESLayer *layer = NULL;
3768
3769   g_return_val_if_fail (GES_IS_TIMELINE (timeline), NULL);
3770   CHECK_THREAD (timeline);
3771
3772   for (tmp = timeline->layers; tmp; tmp = tmp->next) {
3773     GESLayer *tmp_layer = GES_LAYER (tmp->data);
3774     guint tmp_priority;
3775
3776     g_object_get (tmp_layer, "priority", &tmp_priority, NULL);
3777     if (tmp_priority == priority) {
3778       layer = gst_object_ref (tmp_layer);
3779       break;
3780     }
3781   }
3782
3783   return layer;
3784 }
3785
3786 /**
3787  * ges_timeline_paste_element:
3788  * @timeline: The #GESTimeline onto which the #GESTimelineElement should be pasted
3789  * @element: The #GESTimelineElement to paste
3790  * @position: The position in the timeline the element should
3791  * be pasted to, meaning it will become the start of @element
3792  * @layer_priority: The #GESLayer to which the element should be pasted to.
3793  * -1 means paste to the same layer from which the @element has been copied from.
3794  *
3795  * Paste @element inside the timeline. @element must have been
3796  * created using ges_timeline_element_copy with deep=TRUE set,
3797  * i.e. it must be a deep copy, otherwise it will fail.
3798  *
3799  * Returns: (transfer none): Shallow copy of the @element pasted
3800  */
3801 GESTimelineElement *
3802 ges_timeline_paste_element (GESTimeline * timeline,
3803     GESTimelineElement * element, GstClockTime position, gint layer_priority)
3804 {
3805   GESTimelineElement *res, *copied_from;
3806   GESTimelineElementClass *element_class;
3807
3808   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3809   g_return_val_if_fail (GES_IS_TIMELINE_ELEMENT (element), FALSE);
3810   CHECK_THREAD (timeline);
3811
3812   element_class = GES_TIMELINE_ELEMENT_GET_CLASS (element);
3813   copied_from = ges_timeline_element_get_copied_from (element);
3814
3815   if (!copied_from) {
3816     GST_ERROR_OBJECT (element, "Is not being 'deeply' copied!");
3817
3818     return NULL;
3819   }
3820
3821   if (!element_class->paste) {
3822     GST_ERROR_OBJECT (element, "No paste vmethod implemented");
3823
3824     return NULL;
3825   }
3826
3827   /*
3828    * Currently the API only supports pasting onto the same layer from which
3829    * the @element has been copied from, i.e., @layer_priority needs to be -1.
3830    */
3831   if (layer_priority != -1) {
3832     GST_WARNING_OBJECT (timeline,
3833         "Only -1 value for layer priority is supported");
3834   }
3835
3836   res = element_class->paste (element, copied_from, position);
3837
3838   g_clear_object (&copied_from);
3839
3840   return g_object_ref (res);
3841 }
3842
3843 /**
3844  * ges_timeline_move_layer:
3845  * @timeline: The timeline in which @layer must be
3846  * @layer: The layer to move at @new_layer_priority
3847  * @new_layer_priority: The index at which @layer should land
3848  *
3849  * Moves @layer at @new_layer_priority meaning that @layer
3850  * we land at that position in the stack of layers inside
3851  * the timeline. If @new_layer_priority is superior than the number
3852  * of layers present in the time, it will move to the end of the
3853  * stack of layers.
3854  */
3855 gboolean
3856 ges_timeline_move_layer (GESTimeline * timeline, GESLayer * layer,
3857     guint new_layer_priority)
3858 {
3859   gint current_priority;
3860
3861   g_return_val_if_fail (GES_IS_TIMELINE (timeline), FALSE);
3862   g_return_val_if_fail (GES_IS_LAYER (layer), FALSE);
3863   g_return_val_if_fail (ges_layer_get_timeline (layer) == timeline, FALSE);
3864   CHECK_THREAD (timeline);
3865
3866   current_priority = ges_layer_get_priority (layer);
3867
3868   if (new_layer_priority == current_priority) {
3869     GST_DEBUG_OBJECT (timeline,
3870         "Nothing to do for %" GST_PTR_FORMAT ", same priorities", layer);
3871
3872     return TRUE;
3873   }
3874
3875   timeline->layers = g_list_remove (timeline->layers, layer);
3876   timeline->layers = g_list_insert (timeline->layers, layer,
3877       (gint) new_layer_priority);
3878
3879   _resync_layers (timeline);
3880
3881   return TRUE;
3882 }