composition: Set update to TRUE when updating the stack because of EOS
[platform/upstream/gstreamer.git] / gnl / gnlcomposition.c
1 /* GStreamer
2  * Copyright (C) 2001 Wim Taymans <wim.taymans@gmail.com>
3  *               2004-2008 Edward Hervey <bilboed@bilboed.com>
4  *               2014 Mathieu Duponchelle <mathieu.duponchelle@opencreed.com>
5  *               2014 Thibault Saunier <tsaunier@gnome.org>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "gnl.h"
28
29 /**
30  * SECTION:element-gnlcomposition
31  *
32  * A GnlComposition contains GnlObjects such as GnlSources and GnlOperations,
33  * and connects them dynamically to create a composition timeline.
34  */
35
36 static GstStaticPadTemplate gnl_composition_src_template =
37 GST_STATIC_PAD_TEMPLATE ("src",
38     GST_PAD_SRC,
39     GST_PAD_ALWAYS,
40     GST_STATIC_CAPS_ANY);
41
42 GST_DEBUG_CATEGORY_STATIC (gnlcomposition_debug);
43 #define GST_CAT_DEFAULT gnlcomposition_debug
44
45 #define _do_init              \
46   GST_DEBUG_CATEGORY_INIT (gnlcomposition_debug,"gnlcomposition", GST_DEBUG_FG_BLUE | GST_DEBUG_BOLD, "GNonLin Composition");
47 #define gnl_composition_parent_class parent_class
48 G_DEFINE_TYPE_WITH_CODE (GnlComposition, gnl_composition, GNL_TYPE_OBJECT,
49     _do_init);
50
51
52 enum
53 {
54   PROP_0,
55   PROP_DEACTIVATED_ELEMENTS_STATE,
56   PROP_LAST,
57 };
58
59 /* Properties from GnlObject */
60 enum
61 {
62   GNLOBJECT_PROP_START,
63   GNLOBJECT_PROP_STOP,
64   GNLOBJECT_PROP_DURATION,
65   GNLOBJECT_PROP_LAST
66 };
67
68 enum
69 {
70   COMMIT_SIGNAL,
71   COMMITED_SIGNAL,
72   ADD_OBJECT_SIGNAL,
73   REMOVE_OBJECT_SIGNAL,
74   LAST_SIGNAL
75 };
76
77 typedef struct _GnlCompositionEntry GnlCompositionEntry;
78
79 typedef struct
80 {
81   GnlComposition *comp;
82   GstEvent *event;
83 } SeekData;
84
85 typedef struct
86 {
87   GnlComposition *comp;
88   GnlObject *object;
89 } ChildIOData;
90
91 struct _GnlCompositionPrivate
92 {
93   gboolean dispose_has_run;
94
95   /*
96      Sorted List of GnlObjects , ThreadSafe
97      objects_start : sorted by start-time then priority
98      objects_stop : sorted by stop-time then priority
99      objects_hash : contains signal handlers id for controlled objects
100      objects_lock : mutex to acces/modify any of those lists/hashtable
101    */
102   GList *objects_start;
103   GList *objects_stop;
104   GHashTable *objects_hash;
105   GMutex objects_lock;
106
107   /* List of GnlObject to be inserted or removed from the composition on the
108    * next commit */
109   GHashTable *pending_io;
110   GMutex pending_io_lock;
111
112   /*
113      thread-safe Seek handling.
114      flushing_lock : mutex to access flushing and pending_idle
115      flushing :
116    */
117   GMutex flushing_lock;
118   gboolean flushing;
119
120   /* source top-level ghostpad, probe and entry */
121   gulong ghosteventprobe;
122   GnlCompositionEntry *toplevelentry;
123
124   /* current stack, list of GnlObject* */
125   GNode *current;
126
127   /* List of GnlObject whose start/duration will be the same as the composition */
128   GList *expandables;
129
130   /* TRUE if the stack is valid.
131    * This is meant to prevent the top-level pad to be unblocked before the stack
132    * is fully done. Protected by OBJECTS_LOCK */
133   gboolean stackvalid;
134
135   /*
136      current segment seek start/stop time.
137      Reconstruct pipeline ONLY if seeking outside of those values
138      FIXME : segment_start isn't always the earliest time before which the
139      timeline doesn't need to be modified
140    */
141   GstClockTime segment_start;
142   GstClockTime segment_stop;
143
144   /* Seek segment handler */
145   GstSegment *segment;
146   GstSegment *outside_segment;
147
148   /* Next running base_time to set on outgoing segment */
149   guint64 next_base_time;
150
151   /*
152      OUR sync_handler on the child_bus
153      We are called before gnl_object_sync_handler
154    */
155   GstPadEventFunction gnl_event_pad_func;
156   gboolean send_stream_start;
157
158   GMainContext *mcontext;
159   /* Ensure that when we remove all sources from the maincontext
160    * we can not add any source, avoiding:
161    * "g_source_attach: assertion '!SOURCE_DESTROYED (source)' failed" */
162   GMutex mcontext_lock;
163
164   gboolean reset_time;
165
166   gboolean running;
167   gboolean initialized;
168
169   GstState deactivated_elements_state;
170
171   GstElement *current_bin;
172
173   gboolean seeking_itself;
174   gint real_eos_seqnum;
175
176   /* While we do not get a buffer on our srcpad,
177    * we are not commited */
178   gulong commited_probeid;
179   /* 0 means that we already received the right segment */
180   gint awaited_segment_seqnum;
181 };
182
183 static guint _signals[LAST_SIGNAL] = { 0 };
184
185 static GParamSpec *gnlobject_properties[GNLOBJECT_PROP_LAST];
186 static GParamSpec *_properties[PROP_LAST];
187
188 #define OBJECT_IN_ACTIVE_SEGMENT(comp,element)      \
189   ((GNL_OBJECT_START(element) < comp->priv->segment_stop) &&  \
190    (GNL_OBJECT_STOP(element) >= comp->priv->segment_start))
191
192 static void gnl_composition_dispose (GObject * object);
193 static void gnl_composition_finalize (GObject * object);
194 static void gnl_composition_set_property (GObject * object, guint prop_id,
195     const GValue * value, GParamSpec * pspsec);
196 static void gnl_composition_get_property (GObject * object, guint prop_id,
197     GValue * value, GParamSpec * pspsec);
198 static void gnl_composition_reset (GnlComposition * comp);
199
200 static gboolean gnl_composition_add_object (GstBin * bin, GstElement * element);
201
202 static void gnl_composition_handle_message (GstBin * bin, GstMessage * message);
203
204 static gboolean
205 gnl_composition_remove_object (GstBin * bin, GstElement * element);
206
207 static GstStateChangeReturn
208 gnl_composition_change_state (GstElement * element, GstStateChange transition);
209
210 static inline void gnl_composition_reset_target_pad (GnlComposition * comp);
211
212 static gboolean
213 seek_handling (GnlComposition * comp, gboolean initial, gboolean update);
214 static gint objects_start_compare (GnlObject * a, GnlObject * b);
215 static gint objects_stop_compare (GnlObject * a, GnlObject * b);
216 static GstClockTime get_current_position (GnlComposition * comp);
217
218 static gboolean update_pipeline (GnlComposition * comp,
219     GstClockTime currenttime, gboolean initial, gboolean modify);
220 static gboolean gnl_composition_commit_func (GnlObject * object,
221     gboolean recurse);
222 static void update_start_stop_duration (GnlComposition * comp);
223
224 static gboolean
225 gnl_composition_event_handler (GstPad * ghostpad, GstObject * parent,
226     GstEvent * event);
227 static void _relink_single_node (GnlComposition * comp, GNode * node,
228     GstEvent * toplevel_seek);
229 static gboolean update_pipeline_func (GnlComposition * comp);
230 static gboolean _commit_func (GnlComposition * comp);
231 static gboolean lock_child_state (GValue * item, GValue * ret,
232     gpointer udata G_GNUC_UNUSED);
233 static gboolean
234 set_child_caps (GValue * item, GValue * ret G_GNUC_UNUSED, GnlObject * comp);
235 static GstEvent *get_new_seek_event (GnlComposition * comp, gboolean initial,
236     gboolean updatestoponly);
237 static gboolean
238 _gnl_composition_add_entry (GnlComposition * comp, GnlObject * object);
239 static gboolean
240 _gnl_composition_remove_entry (GnlComposition * comp, GnlObject * object);
241 static void _deactivate_stack (GnlComposition * comp);
242 static GstPadProbeReturn _add_emit_commited_and_restart_task (GnlComposition *
243     comp);
244 static gboolean
245 _set_real_eos_seqnum_from_seek (GnlComposition * comp, GstEvent * event);
246
247
248 /* COMP_REAL_START: actual position to start current playback at. */
249 #define COMP_REAL_START(comp)                                                  \
250   (MAX (comp->priv->segment->start, GNL_OBJECT_START (comp)))
251
252 #define COMP_REAL_STOP(comp)                                                   \
253   (GST_CLOCK_TIME_IS_VALID (comp->priv->segment->stop) ?                       \
254    (MIN (comp->priv->segment->stop, GNL_OBJECT_STOP (comp))) :                 \
255    GNL_OBJECT_STOP (comp))
256
257 #define COMP_ENTRY(comp, object)                                               \
258   (g_hash_table_lookup (comp->priv->objects_hash, (gconstpointer) object))
259
260 #define COMP_OBJECTS_LOCK(comp) G_STMT_START {                                 \
261     GST_LOG_OBJECT (comp, "locking objects_lock from thread %p",               \
262         g_thread_self());                                                      \
263     g_mutex_lock (&comp->priv->objects_lock);                                  \
264     GST_LOG_OBJECT (comp, "locked objects_lock from thread %p",                \
265         g_thread_self());                                                      \
266   } G_STMT_END
267
268 #define COMP_OBJECTS_UNLOCK(comp) G_STMT_START {                               \
269     GST_LOG_OBJECT (comp, "unlocking objects_lock from thread %p",             \
270         g_thread_self());                                                      \
271     g_mutex_unlock (&comp->priv->objects_lock);                                \
272   } G_STMT_END
273
274 #define COMP_PENDING_IO_LOCK(comp) G_STMT_START {                                 \
275     GST_LOG_OBJECT (comp, "locking pending_io_lock from thread %p",               \
276         g_thread_self());                                                      \
277     g_mutex_lock (&comp->priv->pending_io_lock);                                  \
278     GST_LOG_OBJECT (comp, "locked pending_io_lock from thread %p",                \
279         g_thread_self());                                                      \
280   } G_STMT_END
281
282 #define COMP_FLUSHING_LOCK(comp) G_STMT_START {                                \
283     GST_LOG_OBJECT (comp, "locking flushing_lock from thread %p",              \
284         g_thread_self());                                                      \
285     g_mutex_lock (&comp->priv->flushing_lock);                                 \
286     GST_LOG_OBJECT (comp, "locked flushing_lock from thread %p",               \
287         g_thread_self());                                                      \
288   } G_STMT_END
289 #define COMP_FLUSHING_UNLOCK(comp) G_STMT_START {                              \
290     GST_LOG_OBJECT (comp, "unlocking flushing_lock from thread %p",            \
291         g_thread_self());                                                      \
292     g_mutex_unlock (&comp->priv->flushing_lock);                               \
293   } G_STMT_END
294
295 #define MAIN_CONTEXT_LOCK(comp) G_STMT_START {                       \
296   GST_LOG_OBJECT (comp, "Getting MAIN_CONTEXT_LOCK in thread %p",    \
297         g_thread_self());                                            \
298   g_mutex_lock(&((GnlComposition*)comp)->priv->mcontext_lock);    \
299   GST_LOG_OBJECT (comp, "Got MAIN_CONTEXT_LOCK in thread %p",        \
300         g_thread_self());                                            \
301 } G_STMT_END
302
303 #define MAIN_CONTEXT_UNLOCK(comp) G_STMT_START {                     \
304   g_mutex_unlock(&((GnlComposition*)comp)->priv->mcontext_lock);  \
305   GST_LOG_OBJECT (comp, "Unlocked MAIN_CONTEXT_LOCK in thread %p",   \
306         g_thread_self());                                            \
307 } G_STMT_END
308
309 #define GET_TASK_LOCK(comp)    (&(GNL_COMPOSITION(comp)->task_rec_lock))
310
311 struct _GnlCompositionEntry
312 {
313   GnlObject *object;
314   GnlComposition *comp;
315
316   /* handler id for block probe */
317   gulong probeid;
318   gulong dataprobeid;
319
320   gboolean seeked;
321 };
322
323 static void
324 _remove_all_sources (GnlComposition * comp)
325 {
326   GSource *source;
327
328   MAIN_CONTEXT_LOCK (comp);
329   while ((source =
330           g_main_context_find_source_by_user_data (comp->priv->mcontext,
331               comp))) {
332     g_source_destroy (source);
333   }
334   MAIN_CONTEXT_UNLOCK (comp);
335 }
336
337 static void
338 iterate_main_context_func (GnlComposition * comp)
339 {
340   if (comp->priv->running == FALSE) {
341     GST_DEBUG_OBJECT (comp, "Not running anymore");
342
343     return;
344   }
345
346   g_main_context_iteration (comp->priv->mcontext, TRUE);
347 }
348
349 static void
350 _start_task (GnlComposition * comp)
351 {
352   GstTask *task;
353
354   comp->priv->running = TRUE;
355
356   GST_OBJECT_LOCK (comp);
357
358   task = comp->task;
359   if (task == NULL) {
360     task =
361         gst_task_new ((GstTaskFunction) iterate_main_context_func, comp, NULL);
362     gst_task_set_lock (task, GET_TASK_LOCK (comp));
363     GST_INFO_OBJECT (comp, "created task %p", task);
364     comp->task = task;
365   }
366
367   gst_task_set_state (task, GST_TASK_STARTED);
368   GST_OBJECT_UNLOCK (comp);
369 }
370
371 static gboolean
372 _stop_task (GnlComposition * comp)
373 {
374   gboolean res = TRUE;
375   GstTask *task;
376
377   GST_INFO_OBJECT (comp, "Stoping children management task");
378
379   comp->priv->running = FALSE;
380
381   /*  Clean the stack of GSource set on the MainContext */
382   g_main_context_wakeup (comp->priv->mcontext);
383   _remove_all_sources (comp);
384
385   GST_DEBUG_OBJECT (comp, "stop task");
386
387   GST_OBJECT_LOCK (comp);
388   task = comp->task;
389   if (task == NULL)
390     goto no_task;
391   comp->task = NULL;
392   res = gst_task_set_state (task, GST_TASK_STOPPED);
393   GST_OBJECT_UNLOCK (comp);
394
395   if (!gst_task_join (task))
396     goto join_failed;
397
398   gst_object_unref (task);
399
400   return res;
401
402 no_task:
403   {
404     GST_OBJECT_UNLOCK (comp);
405
406     /* this is not an error */
407     return TRUE;
408   }
409 join_failed:
410   {
411     /* this is bad, possibly the application tried to join the task from
412      * the task's thread. We install the task again so that it will be stopped
413      * again from the right thread next time hopefully. */
414     GST_OBJECT_LOCK (comp);
415     GST_DEBUG_OBJECT (comp, "join failed");
416     /* we can only install this task if there was no other task */
417     if (comp->task == NULL)
418       comp->task = task;
419     GST_OBJECT_UNLOCK (comp);
420
421     return FALSE;
422   }
423
424   return res;
425 }
426
427 static gboolean
428 _seek_pipeline_func (SeekData * seekd)
429 {
430   gdouble rate;
431   GstFormat format;
432   GstSeekFlags flags;
433   GstSeekType cur_type, stop_type;
434   gint64 cur, stop;
435   GnlCompositionPrivate *priv = seekd->comp->priv;
436
437   gst_event_parse_seek (seekd->event, &rate, &format, &flags,
438       &cur_type, &cur, &stop_type, &stop);
439
440   GST_DEBUG_OBJECT (seekd->comp,
441       "start:%" GST_TIME_FORMAT " -- stop:%" GST_TIME_FORMAT "  flags:%d",
442       GST_TIME_ARGS (cur), GST_TIME_ARGS (stop), flags);
443
444   gst_segment_do_seek (priv->segment,
445       rate, format, flags, cur_type, cur, stop_type, stop, NULL);
446   gst_segment_do_seek (priv->outside_segment,
447       rate, format, flags, cur_type, cur, stop_type, stop, NULL);
448
449   GST_DEBUG_OBJECT (seekd->comp, "Segment now has flags:%d",
450       priv->segment->flags);
451
452   if (priv->segment->start >= GNL_OBJECT_STOP (seekd->comp)) {
453     GST_INFO_OBJECT (seekd->comp,
454         "Start %" GST_TIME_FORMAT " > comp->stop: %" GST_TIME_FORMAT
455         " Not seeking", GST_TIME_ARGS (priv->segment->start),
456         GST_TIME_ARGS (GNL_OBJECT_STOP (seekd->comp)));
457     GST_FIXME_OBJECT (seekd->comp, "HANDLE error async!");
458     goto beach;
459   }
460
461   /* crop the segment start/stop values */
462   /* Only crop segment start value if we don't have a default object */
463   if (priv->expandables == NULL)
464     priv->segment->start =
465         MAX (priv->segment->start, GNL_OBJECT_START (seekd->comp));
466   priv->segment->stop =
467       MIN (priv->segment->stop, GNL_OBJECT_STOP (seekd->comp));
468
469   priv->next_base_time = 0;
470
471   GST_FIXME_OBJECT (seekd->comp,
472       "BE smarter and do not force pipeline update on"
473       " seek (though it just does basic comparision and not full rebuild)");
474
475   priv->reset_time = TRUE;
476   seek_handling (seekd->comp, TRUE, FALSE);
477   priv->reset_time = FALSE;
478
479 beach:
480   gst_event_unref (seekd->event);
481   g_slice_free (SeekData, seekd);
482
483   return G_SOURCE_REMOVE;
484 }
485
486
487
488 static void
489 _add_update_gsource (GnlComposition * comp)
490 {
491   GST_DEBUG_OBJECT (comp, "Adding GSource");
492
493   MAIN_CONTEXT_LOCK (comp);
494   g_main_context_invoke (comp->priv->mcontext,
495       (GSourceFunc) update_pipeline_func, comp);
496   MAIN_CONTEXT_UNLOCK (comp);
497 }
498
499 static void
500 _add_commit_gsource (GnlComposition * comp)
501 {
502   GST_DEBUG_OBJECT (comp, "Adding GSource");
503
504   MAIN_CONTEXT_LOCK (comp);
505   g_main_context_invoke (comp->priv->mcontext,
506       (GSourceFunc) _commit_func, comp);
507   MAIN_CONTEXT_UNLOCK (comp);
508 }
509
510 static void
511 _add_seek_gsource (GnlComposition * comp, GstEvent * event)
512 {
513   SeekData *seekd = g_slice_new0 (SeekData);
514
515   GST_DEBUG_OBJECT (comp, "Adding GSource");
516
517   seekd->comp = comp;
518   seekd->event = event;
519
520   MAIN_CONTEXT_LOCK (comp);
521   g_main_context_invoke (comp->priv->mcontext,
522       (GSourceFunc) _seek_pipeline_func, seekd);
523   MAIN_CONTEXT_UNLOCK (comp);
524 }
525
526
527 static gboolean
528 _initialize_stack_func (GnlComposition * comp)
529 {
530   GnlCompositionPrivate *priv = comp->priv;
531
532   /* set ghostpad target */
533   COMP_OBJECTS_LOCK (comp);
534   if (!(update_pipeline (comp, COMP_REAL_START (comp), TRUE, TRUE))) {
535     COMP_OBJECTS_UNLOCK (comp);
536     GST_FIXME_OBJECT (comp, "PLEASE signal state change failure ASYNC");
537
538     return G_SOURCE_REMOVE;
539   }
540   COMP_OBJECTS_UNLOCK (comp);
541
542   priv->initialized = TRUE;
543
544   return G_SOURCE_REMOVE;
545 }
546
547 static void
548 _add_initialize_stack_gsource (GnlComposition * comp)
549 {
550   GST_DEBUG_OBJECT (comp, "Adding GSource");
551
552   MAIN_CONTEXT_LOCK (comp);
553   g_main_context_invoke (comp->priv->mcontext,
554       (GSourceFunc) _initialize_stack_func, comp);
555   MAIN_CONTEXT_UNLOCK (comp);
556 }
557
558 static void
559 _free_child_io_data (gpointer childio)
560 {
561   g_slice_free (ChildIOData, childio);
562 }
563
564 static void
565 _remove_object_func (ChildIOData * childio)
566 {
567   GnlComposition *comp = childio->comp;
568   GnlObject *object = childio->object;
569
570   GnlCompositionPrivate *priv = comp->priv;
571   GnlCompositionEntry *entry;
572   GnlObject *in_pending_io;
573
574   COMP_OBJECTS_LOCK (comp);
575   entry = COMP_ENTRY (comp, object);
576   in_pending_io = g_hash_table_lookup (priv->pending_io, object);
577
578   if (!entry) {
579     if (in_pending_io) {
580       GST_INFO_OBJECT (comp, "Object %" GST_PTR_FORMAT " was marked"
581           " for addition, removing it from the addition list", object);
582
583       g_hash_table_remove (priv->pending_io, object);
584       COMP_OBJECTS_UNLOCK (comp);
585       return;
586     }
587
588     GST_ERROR_OBJECT (comp, "Object %" GST_PTR_FORMAT " is "
589         " not in the composition", object);
590
591     COMP_OBJECTS_UNLOCK (comp);
592     return;
593   }
594
595   if (in_pending_io) {
596     GST_WARNING_OBJECT (comp, "Object %" GST_PTR_FORMAT " is already marked"
597         " for removal", object);
598
599     COMP_OBJECTS_UNLOCK (comp);
600     return;
601   }
602
603
604   g_hash_table_add (priv->pending_io, object);
605   COMP_OBJECTS_UNLOCK (comp);
606
607   return;
608 }
609
610 static void
611 _add_remove_object_gsource (GnlComposition * comp, GnlObject * object)
612 {
613   ChildIOData *childio = g_slice_new0 (ChildIOData);
614
615   GST_DEBUG_OBJECT (comp, "Adding GSource");
616
617   childio->comp = comp;
618   childio->object = object;
619
620   MAIN_CONTEXT_LOCK (comp);
621   g_main_context_invoke_full (comp->priv->mcontext, G_PRIORITY_DEFAULT,
622       (GSourceFunc) _remove_object_func, childio, _free_child_io_data);
623   MAIN_CONTEXT_UNLOCK (comp);
624 }
625
626 static gboolean
627 remove_object_handler (GnlComposition * comp, GnlObject * object)
628 {
629   g_return_val_if_fail (GNL_IS_OBJECT (object), FALSE);
630
631   _add_remove_object_gsource (comp, object);
632
633   return TRUE;
634 }
635
636 static void
637 _add_object_func (ChildIOData * childio)
638 {
639   GnlComposition *comp = childio->comp;
640   GnlObject *object = childio->object;
641   GnlCompositionPrivate *priv = comp->priv;
642   GnlCompositionEntry *entry;
643   GnlObject *in_pending_io;
644
645   COMP_OBJECTS_LOCK (comp);
646   entry = COMP_ENTRY (comp, object);
647   in_pending_io = g_hash_table_lookup (priv->pending_io, object);
648
649   if (entry) {
650     GST_ERROR_OBJECT (comp, "Object %" GST_PTR_FORMAT " is "
651         " already in the composition", object);
652
653     COMP_OBJECTS_UNLOCK (comp);
654     return;
655   }
656
657   if (in_pending_io) {
658     GST_WARNING_OBJECT (comp, "Object %" GST_PTR_FORMAT " is already marked"
659         " for addition", object);
660
661     COMP_OBJECTS_UNLOCK (comp);
662     return;
663   }
664
665
666   g_hash_table_add (priv->pending_io, object);
667
668   COMP_OBJECTS_UNLOCK (comp);
669   return;
670 }
671
672 static void
673 _add_add_object_gsource (GnlComposition * comp, GnlObject * object)
674 {
675   ChildIOData *childio = g_slice_new0 (ChildIOData);
676
677   GST_DEBUG_OBJECT (comp, "Adding GSource");
678
679   childio->comp = comp;
680   childio->object = object;
681
682   MAIN_CONTEXT_LOCK (comp);
683   g_main_context_invoke_full (comp->priv->mcontext, G_PRIORITY_DEFAULT,
684       (GSourceFunc) _add_object_func, childio, _free_child_io_data);
685   MAIN_CONTEXT_UNLOCK (comp);
686 }
687
688 static gboolean
689 add_object_handler (GnlComposition * comp, GnlObject * object)
690 {
691   g_return_val_if_fail (GNL_IS_OBJECT (object), FALSE);
692
693   _add_add_object_gsource (comp, object);
694
695   return TRUE;
696 }
697
698 static void
699 gnl_composition_class_init (GnlCompositionClass * klass)
700 {
701   GObjectClass *gobject_class;
702   GstElementClass *gstelement_class;
703   GstBinClass *gstbin_class;
704   GnlObjectClass *gnlobject_class;
705
706   gobject_class = (GObjectClass *) klass;
707   gstelement_class = (GstElementClass *) klass;
708   gstbin_class = (GstBinClass *) klass;
709   gnlobject_class = (GnlObjectClass *) klass;
710
711   g_type_class_add_private (klass, sizeof (GnlCompositionPrivate));
712
713   gst_element_class_set_static_metadata (gstelement_class,
714       "GNonLin Composition", "Filter/Editor", "Combines GNL objects",
715       "Wim Taymans <wim.taymans@gmail.com>, Edward Hervey <bilboed@bilboed.com>");
716
717   gobject_class->dispose = GST_DEBUG_FUNCPTR (gnl_composition_dispose);
718   gobject_class->finalize = GST_DEBUG_FUNCPTR (gnl_composition_finalize);
719   gobject_class->set_property =
720       GST_DEBUG_FUNCPTR (gnl_composition_set_property);
721   gobject_class->get_property =
722       GST_DEBUG_FUNCPTR (gnl_composition_get_property);
723
724   gstelement_class->change_state = gnl_composition_change_state;
725
726   gstbin_class->add_element = GST_DEBUG_FUNCPTR (gnl_composition_add_object);
727   gstbin_class->remove_element =
728       GST_DEBUG_FUNCPTR (gnl_composition_remove_object);
729   gstbin_class->handle_message =
730       GST_DEBUG_FUNCPTR (gnl_composition_handle_message);
731
732   gst_element_class_add_pad_template (gstelement_class,
733       gst_static_pad_template_get (&gnl_composition_src_template));
734
735   /* Get the paramspec of the GnlObject klass so we can do
736    * fast notifies */
737   gnlobject_properties[GNLOBJECT_PROP_START] =
738       g_object_class_find_property (gobject_class, "start");
739   gnlobject_properties[GNLOBJECT_PROP_STOP] =
740       g_object_class_find_property (gobject_class, "stop");
741   gnlobject_properties[GNLOBJECT_PROP_DURATION] =
742       g_object_class_find_property (gobject_class, "duration");
743
744   /**
745    * GnlComposition:deactivated-elements-state
746    *
747    * Get or set the #GstState in which elements that are not used
748    * in the currently configured pipeline should be set.
749    * By default the state is GST_STATE_READY to lower memory usage and avoid
750    * using all the avalaible threads from the kernel but that means that in
751    * certain case gapless will be more 'complicated' than if the state was set
752    * to GST_STATE_PAUSED.
753    */
754   _properties[PROP_DEACTIVATED_ELEMENTS_STATE] =
755       g_param_spec_enum ("deactivated-elements-state",
756       "Deactivate elements state", "The state in which elements"
757       " not used in the currently configured pipeline should"
758       " be set", GST_TYPE_STATE, GST_STATE_READY,
759       G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
760
761   g_object_class_install_properties (gobject_class, PROP_LAST, _properties);
762
763   /**
764    * GnlComposition::commit
765    * @comp: a #GnlComposition
766    * @recurse: Whether to commit recursiverly into (GnlComposition) children of
767    *           @object. This is used in case we have composition inside
768    *           a gnlsource composition, telling it to commit the included
769    *           composition state.
770    *
771    * Action signal to commit all the pending changes of the composition and
772    * its children timing properties
773    *
774    * Returns: %TRUE if changes have been commited, %FALSE if nothing had to
775    * be commited
776    */
777   _signals[COMMIT_SIGNAL] = g_signal_new ("commit", G_TYPE_FROM_CLASS (klass),
778       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
779       G_STRUCT_OFFSET (GnlObjectClass, commit_signal_handler), NULL, NULL, NULL,
780       G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN);
781
782   _signals[COMMITED_SIGNAL] =
783       g_signal_new ("commited", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST,
784       0, NULL, NULL, g_cclosure_marshal_generic, G_TYPE_NONE, 1,
785       G_TYPE_BOOLEAN);
786
787   _signals[REMOVE_OBJECT_SIGNAL] =
788       g_signal_new ("remove-object", G_TYPE_FROM_CLASS (klass),
789       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
790       G_STRUCT_OFFSET (GnlCompositionClass, remove_object_handler), NULL, NULL,
791       NULL, G_TYPE_BOOLEAN, 1, GNL_TYPE_OBJECT);
792
793   _signals[ADD_OBJECT_SIGNAL] =
794       g_signal_new ("add-object", G_TYPE_FROM_CLASS (klass),
795       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
796       G_STRUCT_OFFSET (GnlCompositionClass, add_object_handler), NULL, NULL,
797       NULL, G_TYPE_BOOLEAN, 1, GNL_TYPE_OBJECT);
798
799
800   gnlobject_class->commit = gnl_composition_commit_func;
801   klass->remove_object_handler = remove_object_handler;
802   klass->add_object_handler = add_object_handler;
803 }
804
805 static void
806 hash_value_destroy (GnlCompositionEntry * entry)
807 {
808   GstPad *srcpad;
809   GstElement *element = GST_ELEMENT (entry->object);
810
811   srcpad = GNL_OBJECT_SRC (element);
812   if (entry->probeid) {
813     gst_pad_remove_probe (srcpad, entry->probeid);
814     entry->probeid = 0;
815   }
816
817   if (entry->dataprobeid) {
818     gst_pad_remove_probe (srcpad, entry->dataprobeid);
819     entry->dataprobeid = 0;
820   }
821
822   g_slice_free (GnlCompositionEntry, entry);
823 }
824
825 static void
826 gnl_composition_init (GnlComposition * comp)
827 {
828   GnlCompositionPrivate *priv;
829
830   GST_OBJECT_FLAG_SET (comp, GNL_OBJECT_SOURCE);
831   GST_OBJECT_FLAG_SET (comp, GNL_OBJECT_COMPOSITION);
832
833   priv = G_TYPE_INSTANCE_GET_PRIVATE (comp, GNL_TYPE_COMPOSITION,
834       GnlCompositionPrivate);
835   g_mutex_init (&priv->objects_lock);
836   priv->objects_start = NULL;
837   priv->objects_stop = NULL;
838
839   g_mutex_init (&priv->flushing_lock);
840   priv->flushing = FALSE;
841
842   priv->segment = gst_segment_new ();
843   priv->outside_segment = gst_segment_new ();
844
845   g_rec_mutex_init (&comp->task_rec_lock);
846
847   priv->reset_time = FALSE;
848
849   priv->objects_hash = g_hash_table_new_full
850       (g_direct_hash,
851       g_direct_equal, NULL, (GDestroyNotify) hash_value_destroy);
852
853   priv->deactivated_elements_state = GST_STATE_READY;
854   priv->mcontext = g_main_context_new ();
855   g_mutex_init (&priv->mcontext_lock);
856   priv->objects_hash = g_hash_table_new_full
857       (g_direct_hash,
858       g_direct_equal, NULL, (GDestroyNotify) hash_value_destroy);
859
860   g_mutex_init (&priv->pending_io_lock);
861   priv->pending_io = g_hash_table_new (g_direct_hash, g_direct_equal);
862
863   comp->priv = priv;
864
865   GST_ERROR_OBJECT (comp, "HERE");
866   priv->current_bin = gst_bin_new ("current-bin");
867   gst_bin_add (GST_BIN (comp), priv->current_bin);
868   GST_ERROR_OBJECT (comp, "There");
869
870   gnl_composition_reset (comp);
871
872   priv->gnl_event_pad_func = GST_PAD_EVENTFUNC (GNL_OBJECT_SRC (comp));
873   gst_pad_set_event_function (GNL_OBJECT_SRC (comp),
874       GST_DEBUG_FUNCPTR (gnl_composition_event_handler));
875
876   _start_task (comp);
877 }
878
879 static void
880 gnl_composition_dispose (GObject * object)
881 {
882   GnlComposition *comp = GNL_COMPOSITION (object);
883   GnlCompositionPrivate *priv = comp->priv;
884
885   if (priv->dispose_has_run)
886     return;
887
888   priv->dispose_has_run = TRUE;
889
890   if (priv->current) {
891     g_node_destroy (priv->current);
892     priv->current = NULL;
893   }
894
895   if (priv->expandables) {
896     GList *iter;
897
898     iter = priv->expandables;
899
900     g_print ("ITER IS: %p\n", iter);
901     while (iter) {
902       GList *next = iter->next;
903
904       _gnl_composition_remove_entry (comp, iter->data);
905       iter = next;
906     }
907
908     priv->expandables = NULL;
909   }
910   gnl_composition_reset_target_pad (comp);
911
912   G_OBJECT_CLASS (parent_class)->dispose (object);
913 }
914
915 static void
916 gnl_composition_finalize (GObject * object)
917 {
918   GList *iter;
919   GnlComposition *comp = GNL_COMPOSITION (object);
920   GnlCompositionPrivate *priv = comp->priv;
921
922   COMP_OBJECTS_LOCK (comp);
923   iter = priv->objects_start;
924   while (iter) {
925     GList *next = iter->next;
926
927     _gnl_composition_remove_entry (comp, iter->data);
928     iter = next;
929   }
930
931   g_list_free (priv->objects_stop);
932   if (priv->current)
933     g_node_destroy (priv->current);
934   g_hash_table_destroy (priv->objects_hash);
935   COMP_OBJECTS_UNLOCK (comp);
936
937   gst_segment_free (priv->segment);
938   gst_segment_free (priv->outside_segment);
939
940   g_mutex_clear (&priv->objects_lock);
941   g_mutex_clear (&priv->flushing_lock);
942   g_mutex_clear (&priv->pending_io_lock);
943
944   _stop_task (comp);
945   g_rec_mutex_clear (&comp->task_rec_lock);
946
947   G_OBJECT_CLASS (parent_class)->finalize (object);
948
949   g_mutex_clear (&priv->mcontext_lock);
950 }
951
952 static void
953 gnl_composition_set_property (GObject * object, guint prop_id,
954     const GValue * value, GParamSpec * pspec)
955 {
956   GnlComposition *comp = GNL_COMPOSITION (object);
957
958   switch (prop_id) {
959     case PROP_DEACTIVATED_ELEMENTS_STATE:
960       comp->priv->deactivated_elements_state = g_value_get_enum (value);
961       break;
962     default:
963       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
964       break;
965   }
966 }
967
968 static void
969 gnl_composition_get_property (GObject * object, guint prop_id,
970     GValue * value, GParamSpec * pspec)
971 {
972   GnlComposition *comp = GNL_COMPOSITION (object);
973
974   switch (prop_id) {
975     case PROP_DEACTIVATED_ELEMENTS_STATE:
976       g_value_set_enum (value, comp->priv->deactivated_elements_state);
977       break;
978     default:
979       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
980       break;
981   }
982 }
983
984 /* signal_duration_change
985  * Creates a new GST_MESSAGE_DURATION_CHANGED with the currently configured
986  * composition duration and sends that on the bus.
987  */
988 static inline void
989 signal_duration_change (GnlComposition * comp)
990 {
991   gst_element_post_message (GST_ELEMENT_CAST (comp),
992       gst_message_new_duration_changed (GST_OBJECT_CAST (comp)));
993 }
994
995 static gboolean
996 unblock_child_pads (GValue * item, GValue * ret G_GNUC_UNUSED,
997     GnlComposition * comp)
998 {
999   GstPad *pad;
1000   GstElement *child = g_value_get_object (item);
1001   GnlCompositionEntry *entry = COMP_ENTRY (comp, child);
1002
1003   GST_DEBUG_OBJECT (child, "unblocking pads");
1004
1005   pad = GNL_OBJECT_SRC (child);
1006   if (entry->probeid) {
1007     gst_pad_remove_probe (pad, entry->probeid);
1008     entry->probeid = 0;
1009   }
1010   return TRUE;
1011 }
1012
1013 static void
1014 unblock_children (GnlComposition * comp)
1015 {
1016   GstIterator *children;
1017
1018   children = gst_bin_iterate_elements (GST_BIN (comp->priv->current_bin));
1019
1020 retry:
1021   if (G_UNLIKELY (gst_iterator_fold (children,
1022               (GstIteratorFoldFunction) unblock_child_pads, NULL,
1023               comp) == GST_ITERATOR_RESYNC)) {
1024     gst_iterator_resync (children);
1025     goto retry;
1026   }
1027   gst_iterator_free (children);
1028 }
1029
1030
1031 static gboolean
1032 reset_child (GValue * item, GValue * ret G_GNUC_UNUSED, gpointer user_data)
1033 {
1034   GnlCompositionEntry *entry;
1035   GstElement *child = g_value_get_object (item);
1036   GnlComposition *comp = GNL_COMPOSITION (user_data);
1037   GnlObject *object;
1038   GstPad *srcpad, *peerpad;
1039
1040   GST_DEBUG_OBJECT (child, "unlocking state");
1041   gst_element_set_locked_state (child, FALSE);
1042
1043   entry = COMP_ENTRY (comp, child);
1044   object = entry->object;
1045   srcpad = object->srcpad;
1046   peerpad = gst_pad_get_peer (srcpad);
1047   if (peerpad) {
1048     gst_pad_unlink (srcpad, peerpad);
1049     gst_object_unref (peerpad);
1050   }
1051
1052   return TRUE;
1053 }
1054
1055 static gboolean
1056 lock_child_state (GValue * item, GValue * ret G_GNUC_UNUSED,
1057     gpointer udata G_GNUC_UNUSED)
1058 {
1059   GstElement *child = g_value_get_object (item);
1060
1061   GST_DEBUG_OBJECT (child, "locking state");
1062   gst_element_set_locked_state (child, TRUE);
1063
1064   return TRUE;
1065 }
1066
1067 static void
1068 reset_children (GnlComposition * comp)
1069 {
1070   GstIterator *children;
1071
1072   children = gst_bin_iterate_elements (GST_BIN (comp->priv->current_bin));
1073
1074 retry:
1075   if (G_UNLIKELY (gst_iterator_fold (children,
1076               (GstIteratorFoldFunction) reset_child, NULL,
1077               comp) == GST_ITERATOR_RESYNC)) {
1078     gst_iterator_resync (children);
1079     goto retry;
1080   }
1081   gst_iterator_free (children);
1082 }
1083
1084 static gboolean
1085 _remove_child (GValue * item, GValue * ret G_GNUC_UNUSED, GstBin * bin)
1086 {
1087   GstElement *child = g_value_get_object (item);
1088
1089   if (GNL_IS_OPERATION (child))
1090     gnl_operation_hard_cleanup (GNL_OPERATION (child));
1091
1092
1093   gst_bin_remove (bin, child);
1094
1095   return TRUE;
1096 }
1097
1098 static void
1099 _empty_bin (GstBin * bin)
1100 {
1101   GstIterator *children;
1102
1103   children = gst_bin_iterate_elements (bin);
1104
1105   while (G_UNLIKELY (gst_iterator_fold (children,
1106               (GstIteratorFoldFunction) _remove_child, NULL,
1107               bin) == GST_ITERATOR_RESYNC)) {
1108     gst_iterator_resync (children);
1109   }
1110
1111   gst_iterator_free (children);
1112 }
1113
1114 static void
1115 gnl_composition_reset (GnlComposition * comp)
1116 {
1117   GnlCompositionPrivate *priv = comp->priv;
1118
1119   GST_DEBUG_OBJECT (comp, "resetting");
1120
1121   priv->segment_start = GST_CLOCK_TIME_NONE;
1122   priv->segment_stop = GST_CLOCK_TIME_NONE;
1123   priv->next_base_time = 0;
1124
1125   gst_segment_init (priv->segment, GST_FORMAT_TIME);
1126   gst_segment_init (priv->outside_segment, GST_FORMAT_TIME);
1127
1128   if (priv->current)
1129     g_node_destroy (priv->current);
1130   priv->current = NULL;
1131
1132   priv->stackvalid = FALSE;
1133
1134   gnl_composition_reset_target_pad (comp);
1135
1136   reset_children (comp);
1137
1138   COMP_FLUSHING_LOCK (comp);
1139
1140   priv->flushing = FALSE;
1141
1142   COMP_FLUSHING_UNLOCK (comp);
1143
1144   priv->reset_time = FALSE;
1145   priv->initialized = FALSE;
1146   priv->send_stream_start = TRUE;
1147   priv->real_eos_seqnum = 0;
1148
1149   _empty_bin (GST_BIN_CAST (priv->current_bin));
1150
1151   GST_DEBUG_OBJECT (comp, "Composition now resetted");
1152 }
1153
1154 static GstPadProbeReturn
1155 ghost_event_probe_handler (GstPad * ghostpad G_GNUC_UNUSED,
1156     GstPadProbeInfo * info, GnlComposition * comp)
1157 {
1158   GstPadProbeReturn retval = GST_PAD_PROBE_OK;
1159   GnlCompositionPrivate *priv = comp->priv;
1160   GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
1161
1162   GST_DEBUG_OBJECT (comp, "event: %s", GST_EVENT_TYPE_NAME (event));
1163
1164   switch (GST_EVENT_TYPE (event)) {
1165     case GST_EVENT_FLUSH_STOP:
1166       GST_DEBUG_OBJECT (comp,
1167           "replacing flush stop event with a flush stop event with 'reset_time' = %d",
1168           priv->reset_time);
1169       GST_PAD_PROBE_INFO_DATA (info) =
1170           gst_event_new_flush_stop (priv->reset_time);
1171       gst_event_unref (event);
1172       break;
1173     case GST_EVENT_STREAM_START:
1174       if (g_atomic_int_compare_and_exchange (&priv->send_stream_start, TRUE,
1175               FALSE)) {
1176         /* FIXME: Do we want to create a new stream ID here? */
1177         GST_DEBUG_OBJECT (comp, "forward stream-start %p", event);
1178       } else {
1179         GST_DEBUG_OBJECT (comp, "dropping stream-start %p", event);
1180         retval = GST_PAD_PROBE_DROP;
1181       }
1182       break;
1183     case GST_EVENT_SEGMENT:
1184     {
1185       guint64 rstart, rstop;
1186       const GstSegment *segment;
1187       GstSegment copy;
1188       GstEvent *event2;
1189       /* next_base_time */
1190
1191       COMP_FLUSHING_LOCK (comp);
1192
1193       priv->flushing = FALSE;
1194       COMP_FLUSHING_UNLOCK (comp);
1195
1196       gst_event_parse_segment (event, &segment);
1197       gst_segment_copy_into (segment, &copy);
1198
1199       rstart =
1200           gst_segment_to_running_time (segment, GST_FORMAT_TIME,
1201           segment->start);
1202       rstop =
1203           gst_segment_to_running_time (segment, GST_FORMAT_TIME, segment->stop);
1204       copy.base = comp->priv->next_base_time;
1205       GST_DEBUG_OBJECT (comp,
1206           "Updating base time to %" GST_TIME_FORMAT ", next:%" GST_TIME_FORMAT,
1207           GST_TIME_ARGS (comp->priv->next_base_time),
1208           GST_TIME_ARGS (comp->priv->next_base_time + rstop - rstart));
1209       comp->priv->next_base_time += rstop - rstart;
1210
1211       event2 = gst_event_new_segment (&copy);
1212       GST_EVENT_SEQNUM (event2) = GST_EVENT_SEQNUM (event);
1213
1214       if (GNL_OBJECT (comp)->seqnum == 0)
1215         GNL_OBJECT (comp)->seqnum = GST_EVENT_SEQNUM (event);
1216
1217       GST_PAD_PROBE_INFO_DATA (info) = event2;
1218       gst_event_unref (event);
1219     }
1220       break;
1221     case GST_EVENT_EOS:
1222     {
1223       gint seqnum = gst_event_get_seqnum (event);
1224
1225       COMP_FLUSHING_LOCK (comp);
1226       if (priv->flushing) {
1227         GST_DEBUG_OBJECT (comp, "flushing, bailing out");
1228         COMP_FLUSHING_UNLOCK (comp);
1229         retval = GST_PAD_PROBE_DROP;
1230         break;
1231       }
1232       COMP_FLUSHING_UNLOCK (comp);
1233
1234       GST_ERROR_OBJECT (comp, "Got EOS, last EOS seqnum id : %i current "
1235           "seq num is: %i", comp->priv->real_eos_seqnum, seqnum);
1236
1237       if (priv->commited_probeid && comp->priv->awaited_segment_seqnum == 0) {
1238
1239         GST_INFO_OBJECT (comp, "We got an EOS right after seeing the right"
1240             " segment, restarting task");
1241         GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (comp),
1242             GST_DEBUG_GRAPH_SHOW_ALL, "eos-after-segment");
1243         gst_pad_remove_probe (GNL_OBJECT_SRC (comp), priv->commited_probeid);
1244         _add_emit_commited_and_restart_task (comp);
1245       }
1246
1247       if (g_atomic_int_compare_and_exchange (&comp->priv->real_eos_seqnum,
1248               seqnum, 1)) {
1249
1250         GST_INFO_OBJECT (comp, "Got EOS for real, fowarding it");
1251         GST_ERROR_OBJECT (comp, "GOGOGO EOS -- Seq ID is %i", seqnum);
1252
1253         return GST_PAD_PROBE_OK;
1254       }
1255
1256       _add_update_gsource (comp);
1257       retval = GST_PAD_PROBE_DROP;
1258     }
1259       break;
1260     default:
1261       break;
1262   }
1263
1264   return retval;
1265 }
1266
1267
1268
1269 /* Warning : Don't take the objects lock in this method */
1270 static void
1271 gnl_composition_handle_message (GstBin * bin, GstMessage * message)
1272 {
1273   GnlComposition *comp = (GnlComposition *) bin;
1274   gboolean dropit = FALSE;
1275
1276   GST_DEBUG_OBJECT (comp, "message:%s from %s",
1277       gst_message_type_get_name (GST_MESSAGE_TYPE (message)),
1278       GST_MESSAGE_SRC (message) ? GST_ELEMENT_NAME (GST_MESSAGE_SRC (message)) :
1279       "UNKNOWN");
1280
1281   switch (GST_MESSAGE_TYPE (message)) {
1282     case GST_MESSAGE_ERROR:
1283     case GST_MESSAGE_WARNING:
1284     {
1285       /* FIXME / HACK
1286        * There is a massive issue with reverse negotiation and dynamic pipelines.
1287        *
1288        * Since we're not waiting for the pads of the previous stack to block before
1289        * re-switching, we might end up switching sources in the middle of a downstrea
1290        * negotiation which will do reverse negotiation... with the new source (which
1291        * is no longer the one that issues the request). That negotiation will fail
1292        * and the original source will emit an ERROR message.
1293        *
1294        * In order to avoid those issues, we just ignore error messages from elements
1295        * which aren't in the currently configured stack
1296        */
1297       if (GST_MESSAGE_SRC (message) && GNL_IS_OBJECT (GST_MESSAGE_SRC (message))
1298           && !OBJECT_IN_ACTIVE_SEGMENT (comp, GST_MESSAGE_SRC (message))) {
1299         GST_DEBUG_OBJECT (comp,
1300             "HACK Dropping error message from object not in currently configured stack !");
1301         dropit = TRUE;
1302       }
1303     }
1304     default:
1305       break;
1306   }
1307
1308   if (dropit)
1309     gst_message_unref (message);
1310   else
1311     GST_BIN_CLASS (parent_class)->handle_message (bin, message);
1312 }
1313
1314 static gint
1315 priority_comp (GnlObject * a, GnlObject * b)
1316 {
1317   if (a->priority < b->priority)
1318     return -1;
1319
1320   if (a->priority > b->priority)
1321     return 1;
1322
1323   return 0;
1324 }
1325
1326 static inline gboolean
1327 have_to_update_pipeline (GnlComposition * comp)
1328 {
1329   GnlCompositionPrivate *priv = comp->priv;
1330
1331   GST_DEBUG_OBJECT (comp,
1332       "segment[%" GST_TIME_FORMAT "--%" GST_TIME_FORMAT "] current[%"
1333       GST_TIME_FORMAT "--%" GST_TIME_FORMAT "]",
1334       GST_TIME_ARGS (priv->segment->start),
1335       GST_TIME_ARGS (priv->segment->stop),
1336       GST_TIME_ARGS (priv->segment_start), GST_TIME_ARGS (priv->segment_stop));
1337
1338   if (priv->segment->start < priv->segment_start)
1339     return TRUE;
1340
1341   if (priv->segment->start >= priv->segment_stop)
1342     return TRUE;
1343
1344   return FALSE;
1345 }
1346
1347 static gboolean
1348 gnl_composition_commit_func (GnlObject * object, gboolean recurse)
1349 {
1350   GST_ERROR ("Adding commit gsource");
1351   _add_commit_gsource (GNL_COMPOSITION (object));
1352   return TRUE;
1353 }
1354
1355 /*
1356  * get_new_seek_event:
1357  *
1358  * Returns a seek event for the currently configured segment
1359  * and start/stop values
1360  *
1361  * The GstSegment and segment_start|stop must have been configured
1362  * before calling this function.
1363  */
1364 static GstEvent *
1365 get_new_seek_event (GnlComposition * comp, gboolean initial,
1366     gboolean updatestoponly)
1367 {
1368   GstSeekFlags flags = GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH;
1369   gint64 start, stop;
1370   GstSeekType starttype = GST_SEEK_TYPE_SET;
1371   GnlCompositionPrivate *priv = comp->priv;
1372
1373   GST_DEBUG_OBJECT (comp, "initial:%d", initial);
1374   /* remove the seek flag */
1375   if (!initial)
1376     flags |= (GstSeekFlags) priv->segment->flags;
1377
1378   GST_DEBUG_OBJECT (comp,
1379       "private->segment->start:%" GST_TIME_FORMAT " segment_start%"
1380       GST_TIME_FORMAT, GST_TIME_ARGS (priv->segment->start),
1381       GST_TIME_ARGS (priv->segment_start));
1382
1383   GST_DEBUG_OBJECT (comp,
1384       "private->segment->stop:%" GST_TIME_FORMAT " segment_stop%"
1385       GST_TIME_FORMAT, GST_TIME_ARGS (priv->segment->stop),
1386       GST_TIME_ARGS (priv->segment_stop));
1387
1388   start = MAX (priv->segment->start, priv->segment_start);
1389   stop = GST_CLOCK_TIME_IS_VALID (priv->segment->stop)
1390       ? MIN (priv->segment->stop, priv->segment_stop)
1391       : priv->segment_stop;
1392
1393   if (updatestoponly) {
1394     starttype = GST_SEEK_TYPE_NONE;
1395     start = GST_CLOCK_TIME_NONE;
1396   }
1397
1398   GST_DEBUG_OBJECT (comp,
1399       "Created new seek event. Flags:%d, start:%" GST_TIME_FORMAT ", stop:%"
1400       GST_TIME_FORMAT ", rate:%lf", flags, GST_TIME_ARGS (start),
1401       GST_TIME_ARGS (stop), priv->segment->rate);
1402
1403   return gst_event_new_seek (priv->segment->rate,
1404       priv->segment->format, flags, starttype, start, GST_SEEK_TYPE_SET, stop);
1405 }
1406
1407 /* OBJECTS LOCK must be taken when calling this ! */
1408 static GstClockTime
1409 get_current_position (GnlComposition * comp)
1410 {
1411   GstPad *pad;
1412   GnlObject *obj;
1413   GnlCompositionPrivate *priv = comp->priv;
1414   gboolean res;
1415   gint64 value = GST_CLOCK_TIME_NONE;
1416
1417   GstPad *peer = gst_pad_get_peer (GNL_OBJECT (comp)->srcpad);
1418
1419   /* Try querying position downstream */
1420
1421   if (peer) {
1422     res = gst_pad_query_position (peer, GST_FORMAT_TIME, &value);
1423     gst_object_unref (peer);
1424
1425     if (res) {
1426       GST_LOG_OBJECT (comp,
1427           "Successfully got downstream position %" GST_TIME_FORMAT,
1428           GST_TIME_ARGS ((guint64) value));
1429       goto beach;
1430     }
1431   }
1432
1433   GST_DEBUG_OBJECT (comp, "Downstream position query failed");
1434
1435   /* resetting format/value */
1436   value = GST_CLOCK_TIME_NONE;
1437
1438   /* If downstream fails , try within the current stack */
1439   if (!priv->current) {
1440     GST_DEBUG_OBJECT (comp, "No current stack, can't send query");
1441     goto beach;
1442   }
1443
1444   obj = (GnlObject *) priv->current->data;
1445
1446   pad = GNL_OBJECT_SRC (obj);
1447   res = gst_pad_query_position (pad, GST_FORMAT_TIME, &value);
1448
1449   if (G_UNLIKELY (res == FALSE)) {
1450     GST_WARNING_OBJECT (comp, "query position failed");
1451     value = GST_CLOCK_TIME_NONE;
1452   } else {
1453     GST_LOG_OBJECT (comp, "Query returned %" GST_TIME_FORMAT,
1454         GST_TIME_ARGS ((guint64) value));
1455   }
1456
1457 beach:
1458
1459   if (GST_CLOCK_TIME_IS_VALID (comp->priv->segment_start)) {
1460     GST_INFO_OBJECT (comp, "Current position is unknown, " "setting it to 0");
1461
1462     value = 0;
1463   }
1464
1465   return (guint64) value;
1466 }
1467
1468 static gboolean
1469 update_base_time (GNode * node, GstClockTime * timestamp)
1470 {
1471   if (GNL_IS_OPERATION (node->data))
1472     gnl_operation_update_base_time (GNL_OPERATION (node->data), *timestamp);
1473
1474   return FALSE;
1475 }
1476
1477 /* WITH OBJECTS LOCK TAKEN */
1478 static void
1479 update_operations_base_time (GnlComposition * comp, gboolean reverse)
1480 {
1481   GstClockTime timestamp;
1482
1483   if (reverse)
1484     timestamp = comp->priv->segment->stop;
1485   else
1486     timestamp = comp->priv->segment->start;
1487
1488   g_node_traverse (comp->priv->current, G_IN_ORDER, G_TRAVERSE_ALL, -1,
1489       (GNodeTraverseFunc) update_base_time, &timestamp);
1490 }
1491
1492
1493 static gboolean
1494 _seek_current_stack (GnlComposition * comp, GstEvent * event)
1495 {
1496   gboolean res;
1497   GnlCompositionPrivate *priv = comp->priv;
1498   GstPad *peer = gst_pad_get_peer (GNL_OBJECT_SRC (comp));
1499
1500   GST_INFO_OBJECT (comp, "Seeking itself %" GST_PTR_FORMAT, event);
1501
1502   priv->seeking_itself = TRUE;
1503   res = gst_pad_push_event (peer, event);
1504   priv->seeking_itself = FALSE;
1505   gst_object_unref (peer);
1506
1507   GST_DEBUG_OBJECT (comp, "Done seeking");
1508
1509   return res;
1510 }
1511
1512 /*
1513   Figures out if pipeline needs updating.
1514   Updates it and sends the seek event.
1515   Sends flush events downstream if needed.
1516   can be called by user_seek or segment_done
1517
1518   initial : FIXME : ???? Always seems to be TRUE
1519   update : TRUE from EOS, FALSE from seek
1520 */
1521
1522 static gboolean
1523 seek_handling (GnlComposition * comp, gboolean initial, gboolean update)
1524 {
1525   GST_DEBUG_OBJECT (comp, "initial:%d, update:%d", initial, update);
1526
1527   COMP_FLUSHING_LOCK (comp);
1528   GST_DEBUG_OBJECT (comp, "Setting flushing to TRUE");
1529   comp->priv->flushing = TRUE;
1530   COMP_FLUSHING_UNLOCK (comp);
1531
1532   COMP_OBJECTS_LOCK (comp);
1533   if (update || have_to_update_pipeline (comp)) {
1534     if (comp->priv->segment->rate >= 0.0)
1535       update_pipeline (comp, comp->priv->segment->start, initial, !update);
1536     else
1537       update_pipeline (comp, comp->priv->segment->stop, initial, !update);
1538   } else {
1539     GstEvent *toplevel_seek = get_new_seek_event (comp, FALSE, FALSE);
1540
1541     _set_real_eos_seqnum_from_seek (comp, toplevel_seek);
1542
1543     _seek_current_stack (comp, toplevel_seek);
1544     update_operations_base_time (comp, !(comp->priv->segment->rate >= 0.0));
1545   }
1546   COMP_OBJECTS_UNLOCK (comp);
1547
1548   return TRUE;
1549 }
1550
1551 static gboolean
1552 gnl_composition_event_handler (GstPad * ghostpad, GstObject * parent,
1553     GstEvent * event)
1554 {
1555   GnlComposition *comp = (GnlComposition *) parent;
1556   GnlCompositionPrivate *priv = comp->priv;
1557   gboolean res = TRUE;
1558
1559   GST_DEBUG_OBJECT (comp, "event type:%s", GST_EVENT_TYPE_NAME (event));
1560
1561   switch (GST_EVENT_TYPE (event)) {
1562     case GST_EVENT_SEEK:
1563     {
1564       if (!priv->seeking_itself) {
1565         _add_seek_gsource (comp, event);
1566         event = NULL;
1567         GST_FIXME_OBJECT (comp, "HANDLE seeking errors!");
1568
1569         return TRUE;
1570       }
1571
1572       GNL_OBJECT (comp)->wanted_seqnum = gst_event_get_seqnum (event);
1573       break;
1574     }
1575     case GST_EVENT_QOS:
1576     {
1577       gdouble prop;
1578       GstQOSType qostype;
1579       GstClockTimeDiff diff;
1580       GstClockTime timestamp;
1581
1582       gst_event_parse_qos (event, &qostype, &prop, &diff, &timestamp);
1583
1584       GST_INFO_OBJECT (comp,
1585           "timestamp:%" GST_TIME_FORMAT " segment.start:%" GST_TIME_FORMAT
1586           " segment.stop:%" GST_TIME_FORMAT " segment_start%" GST_TIME_FORMAT
1587           " segment_stop:%" GST_TIME_FORMAT, GST_TIME_ARGS (timestamp),
1588           GST_TIME_ARGS (priv->outside_segment->start),
1589           GST_TIME_ARGS (priv->outside_segment->stop),
1590           GST_TIME_ARGS (priv->segment_start),
1591           GST_TIME_ARGS (priv->segment_stop));
1592
1593       /* The problem with QoS events is the following:
1594        * At each new internal segment (i.e. when we re-arrange our internal
1595        * elements) we send flushing seeks to those elements (to properly
1596        * configure their playback range) but don't let the FLUSH events get
1597        * downstream.
1598        *
1599        * The problem is that the QoS running timestamps we receive from
1600        * downstream will not have taken into account those flush.
1601        *
1602        * What we need to do is to translate to our internal running timestamps
1603        * which for each configured segment starts at 0 for those elements.
1604        *
1605        * The generic algorithm for the incoming running timestamp translation
1606        * is therefore:
1607        *     (original_seek_time : original seek position received from usptream)
1608        *     (current_segment_start : Start position of the currently configured
1609        *                              timeline segment)
1610        *
1611        *     difference = original_seek_time - current_segment_start
1612        *     new_qos_position = upstream_qos_position - difference
1613        *
1614        * The new_qos_position is only valid when:
1615        *    * it applies to the current segment (difference > 0)
1616        *    * The QoS difference + timestamp is greater than the difference
1617        *
1618        */
1619
1620       if (GST_CLOCK_TIME_IS_VALID (priv->outside_segment->start)) {
1621         GstClockTimeDiff curdiff;
1622
1623         /* We'll either create a new event or discard it */
1624         gst_event_unref (event);
1625
1626         if (priv->segment->rate < 0.0)
1627           curdiff = priv->outside_segment->stop - priv->segment_stop;
1628         else
1629           curdiff = priv->segment_start - priv->outside_segment->start;
1630         GST_DEBUG ("curdiff %" GST_TIME_FORMAT, GST_TIME_ARGS (curdiff));
1631         if ((curdiff != 0) && ((timestamp < curdiff)
1632                 || (curdiff > timestamp + diff))) {
1633           GST_DEBUG_OBJECT (comp,
1634               "QoS event outside of current segment, discarding");
1635           /* The QoS timestamp is before the currently set-up pipeline */
1636           goto beach;
1637         }
1638
1639         /* Substract the amount of running time we've already outputted
1640          * until the currently configured pipeline from the QoS timestamp.*/
1641         timestamp -= curdiff;
1642         GST_INFO_OBJECT (comp,
1643             "Creating new QoS event with timestamp %" GST_TIME_FORMAT,
1644             GST_TIME_ARGS (timestamp));
1645         event = gst_event_new_qos (qostype, prop, diff, timestamp);
1646       }
1647       break;
1648     }
1649     default:
1650       break;
1651   }
1652
1653   if (res) {
1654     GST_DEBUG_OBJECT (comp, "About to call gnl_event_pad_func: %p",
1655         priv->gnl_event_pad_func);
1656     res = priv->gnl_event_pad_func (GNL_OBJECT (comp)->srcpad, parent, event);
1657     priv->reset_time = FALSE;
1658     GST_DEBUG_OBJECT (comp, "Done calling gnl_event_pad_func() %d", res);
1659   }
1660
1661 beach:
1662   return res;
1663 }
1664
1665 static inline void
1666 gnl_composition_reset_target_pad (GnlComposition * comp)
1667 {
1668   GnlCompositionPrivate *priv = comp->priv;
1669
1670   GST_DEBUG_OBJECT (comp, "Removing ghostpad");
1671
1672   if (priv->ghosteventprobe) {
1673     GstPad *target;
1674
1675     target = gst_ghost_pad_get_target ((GstGhostPad *) GNL_OBJECT_SRC (comp));
1676     if (target)
1677       gst_pad_remove_probe (target, priv->ghosteventprobe);
1678     priv->ghosteventprobe = 0;
1679   }
1680
1681   gnl_object_ghost_pad_set_target (GNL_OBJECT (comp),
1682       GNL_OBJECT_SRC (comp), NULL);
1683   priv->toplevelentry = NULL;
1684   GST_ERROR ("NEED STRAM START");
1685   priv->send_stream_start = TRUE;
1686 }
1687
1688 /* gnl_composition_ghost_pad_set_target:
1689  * target: The target #GstPad. The refcount will be decremented (given to the ghostpad).
1690  * entry: The GnlCompositionEntry to which the pad belongs
1691  */
1692 static void
1693 gnl_composition_ghost_pad_set_target (GnlComposition * comp, GstPad * target,
1694     GnlCompositionEntry * entry)
1695 {
1696   GstPad *ptarget;
1697   GnlCompositionPrivate *priv = comp->priv;
1698
1699   if (target)
1700     GST_DEBUG_OBJECT (comp, "target:%s:%s", GST_DEBUG_PAD_NAME (target));
1701   else
1702     GST_DEBUG_OBJECT (comp, "Removing target");
1703
1704
1705   ptarget =
1706       gst_ghost_pad_get_target (GST_GHOST_PAD (GNL_OBJECT (comp)->srcpad));
1707   if (ptarget && ptarget == target) {
1708     GST_DEBUG_OBJECT (comp,
1709         "Target of srcpad is the same as existing one, not changing");
1710     gst_object_unref (ptarget);
1711     return;
1712   }
1713
1714   /* Actually set the target */
1715   gnl_object_ghost_pad_set_target ((GnlObject *) comp,
1716       GNL_OBJECT (comp)->srcpad, target);
1717
1718   /* Set top-level entry (will be NULL if unsetting) */
1719   priv->toplevelentry = entry;
1720
1721   if (target && (priv->ghosteventprobe == 0)) {
1722     priv->ghosteventprobe =
1723         gst_pad_add_probe (target,
1724         GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GST_PAD_PROBE_TYPE_EVENT_FLUSH,
1725         (GstPadProbeCallback) ghost_event_probe_handler, comp, NULL);
1726     GST_DEBUG_OBJECT (comp, "added event probe %lu", priv->ghosteventprobe);
1727   }
1728
1729   GST_DEBUG_OBJECT (comp, "END");
1730 }
1731
1732 static void
1733 refine_start_stop_in_region_above_priority (GnlComposition * composition,
1734     GstClockTime timestamp, GstClockTime start,
1735     GstClockTime stop,
1736     GstClockTime * rstart, GstClockTime * rstop, guint32 priority)
1737 {
1738   GList *tmp;
1739   GnlObject *object;
1740   GstClockTime nstart = start, nstop = stop;
1741
1742   GST_DEBUG_OBJECT (composition,
1743       "timestamp:%" GST_TIME_FORMAT " start: %" GST_TIME_FORMAT " stop: %"
1744       GST_TIME_FORMAT " priority:%u", GST_TIME_ARGS (timestamp),
1745       GST_TIME_ARGS (start), GST_TIME_ARGS (stop), priority);
1746
1747   for (tmp = composition->priv->objects_start; tmp; tmp = tmp->next) {
1748     object = (GnlObject *) tmp->data;
1749
1750     GST_LOG_OBJECT (object, "START %" GST_TIME_FORMAT "--%" GST_TIME_FORMAT,
1751         GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop));
1752
1753     if ((object->priority >= priority) || (!object->active))
1754       continue;
1755
1756     if (object->start <= timestamp)
1757       continue;
1758
1759     if (object->start >= nstop)
1760       continue;
1761
1762     nstop = object->start;
1763
1764     GST_DEBUG_OBJECT (composition,
1765         "START Found %s [prio:%u] at %" GST_TIME_FORMAT,
1766         GST_OBJECT_NAME (object), object->priority,
1767         GST_TIME_ARGS (object->start));
1768
1769     break;
1770   }
1771
1772   for (tmp = composition->priv->objects_stop; tmp; tmp = tmp->next) {
1773     object = (GnlObject *) tmp->data;
1774
1775     GST_LOG_OBJECT (object, "STOP %" GST_TIME_FORMAT "--%" GST_TIME_FORMAT,
1776         GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop));
1777
1778     if ((object->priority >= priority) || (!object->active))
1779       continue;
1780
1781     if (object->stop >= timestamp)
1782       continue;
1783
1784     if (object->stop <= nstart)
1785       continue;
1786
1787     nstart = object->stop;
1788
1789     GST_DEBUG_OBJECT (composition,
1790         "STOP Found %s [prio:%u] at %" GST_TIME_FORMAT,
1791         GST_OBJECT_NAME (object), object->priority,
1792         GST_TIME_ARGS (object->start));
1793
1794     break;
1795   }
1796
1797   if (*rstart)
1798     *rstart = nstart;
1799
1800   if (*rstop)
1801     *rstop = nstop;
1802 }
1803
1804
1805 /*
1806  * Converts a sorted list to a tree
1807  * Recursive
1808  *
1809  * stack will be set to the next item to use in the parent.
1810  * If operations number of sinks is limited, it will only use that number.
1811  */
1812
1813 static GNode *
1814 convert_list_to_tree (GList ** stack, GstClockTime * start,
1815     GstClockTime * stop, guint32 * highprio)
1816 {
1817   GNode *ret;
1818   guint nbsinks;
1819   gboolean limit;
1820   GList *tmp;
1821   GnlObject *object;
1822
1823   if (!stack || !*stack)
1824     return NULL;
1825
1826   object = (GnlObject *) (*stack)->data;
1827
1828   GST_DEBUG ("object:%s , *start:%" GST_TIME_FORMAT ", *stop:%"
1829       GST_TIME_FORMAT " highprio:%d",
1830       GST_ELEMENT_NAME (object), GST_TIME_ARGS (*start),
1831       GST_TIME_ARGS (*stop), *highprio);
1832
1833   /* update earliest stop */
1834   if (GST_CLOCK_TIME_IS_VALID (*stop)) {
1835     if (GST_CLOCK_TIME_IS_VALID (object->stop) && (*stop > object->stop))
1836       *stop = object->stop;
1837   } else {
1838     *stop = object->stop;
1839   }
1840
1841   if (GST_CLOCK_TIME_IS_VALID (*start)) {
1842     if (GST_CLOCK_TIME_IS_VALID (object->start) && (*start < object->start))
1843       *start = object->start;
1844   } else {
1845     *start = object->start;
1846   }
1847
1848   if (GNL_OBJECT_IS_SOURCE (object)) {
1849     *stack = g_list_next (*stack);
1850
1851     /* update highest priority.
1852      * We do this here, since it's only used with sources (leafs of the tree) */
1853     if (object->priority > *highprio)
1854       *highprio = object->priority;
1855
1856     ret = g_node_new (object);
1857
1858     goto beach;
1859   } else {
1860     /* GnlOperation */
1861     GnlOperation *oper = (GnlOperation *) object;
1862
1863     GST_LOG_OBJECT (oper, "operation, num_sinks:%d", oper->num_sinks);
1864
1865     ret = g_node_new (object);
1866     limit = (oper->dynamicsinks == FALSE);
1867     nbsinks = oper->num_sinks;
1868
1869     /* FIXME : if num_sinks == -1 : request the proper number of pads */
1870     for (tmp = g_list_next (*stack); tmp && (!limit || nbsinks);) {
1871       g_node_append (ret, convert_list_to_tree (&tmp, start, stop, highprio));
1872       if (limit)
1873         nbsinks--;
1874     }
1875
1876     *stack = tmp;
1877   }
1878
1879 beach:
1880   GST_DEBUG_OBJECT (object,
1881       "*start:%" GST_TIME_FORMAT " *stop:%" GST_TIME_FORMAT
1882       " priority:%u", GST_TIME_ARGS (*start), GST_TIME_ARGS (*stop), *highprio);
1883
1884   return ret;
1885 }
1886
1887 /*
1888  * get_stack_list:
1889  * @comp: The #GnlComposition
1890  * @timestamp: The #GstClockTime to look at
1891  * @priority: The priority level to start looking from
1892  * @activeonly: Only look for active elements if TRUE
1893  * @start: The biggest start time of the objects in the stack
1894  * @stop: The smallest stop time of the objects in the stack
1895  * @highprio: The highest priority in the stack
1896  *
1897  * Not MT-safe, you should take the objects lock before calling it.
1898  * Returns: A tree of #GNode sorted in priority order, corresponding
1899  * to the given search arguments. The returned value can be #NULL.
1900  *
1901  * WITH OBJECTS LOCK TAKEN
1902  */
1903 static GNode *
1904 get_stack_list (GnlComposition * comp, GstClockTime timestamp,
1905     guint32 priority, gboolean activeonly, GstClockTime * start,
1906     GstClockTime * stop, guint * highprio)
1907 {
1908   GList *tmp;
1909   GList *stack = NULL;
1910   GNode *ret = NULL;
1911   GstClockTime nstart = GST_CLOCK_TIME_NONE;
1912   GstClockTime nstop = GST_CLOCK_TIME_NONE;
1913   GstClockTime first_out_of_stack = GST_CLOCK_TIME_NONE;
1914   guint32 highest = 0;
1915   gboolean reverse = (comp->priv->segment->rate < 0.0);
1916
1917   GST_DEBUG_OBJECT (comp,
1918       "timestamp:%" GST_TIME_FORMAT ", priority:%u, activeonly:%d",
1919       GST_TIME_ARGS (timestamp), priority, activeonly);
1920
1921   GST_LOG ("objects_start:%p objects_stop:%p", comp->priv->objects_start,
1922       comp->priv->objects_stop);
1923
1924   if (reverse) {
1925     for (tmp = comp->priv->objects_stop; tmp; tmp = g_list_next (tmp)) {
1926       GnlObject *object = (GnlObject *) tmp->data;
1927
1928       GST_LOG_OBJECT (object,
1929           "start: %" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT " , duration:%"
1930           GST_TIME_FORMAT ", priority:%u, active:%d",
1931           GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop),
1932           GST_TIME_ARGS (object->duration), object->priority, object->active);
1933
1934       if (object->stop >= timestamp) {
1935         if ((object->start < timestamp) &&
1936             (object->priority >= priority) &&
1937             ((!activeonly) || (object->active))) {
1938           GST_LOG_OBJECT (comp, "adding %s: sorted to the stack",
1939               GST_OBJECT_NAME (object));
1940           stack = g_list_insert_sorted (stack, object,
1941               (GCompareFunc) priority_comp);
1942           if (GNL_IS_OPERATION (object))
1943             gnl_operation_update_base_time (GNL_OPERATION (object), timestamp);
1944         }
1945       } else {
1946         GST_LOG_OBJECT (comp, "too far, stopping iteration");
1947         first_out_of_stack = object->stop;
1948         break;
1949       }
1950     }
1951   } else {
1952     for (tmp = comp->priv->objects_start; tmp; tmp = g_list_next (tmp)) {
1953       GnlObject *object = (GnlObject *) tmp->data;
1954
1955       GST_LOG_OBJECT (object,
1956           "start: %" GST_TIME_FORMAT " , stop:%" GST_TIME_FORMAT " , duration:%"
1957           GST_TIME_FORMAT ", priority:%u", GST_TIME_ARGS (object->start),
1958           GST_TIME_ARGS (object->stop), GST_TIME_ARGS (object->duration),
1959           object->priority);
1960
1961       if (object->start <= timestamp) {
1962         if ((object->stop > timestamp) &&
1963             (object->priority >= priority) &&
1964             ((!activeonly) || (object->active))) {
1965           GST_LOG_OBJECT (comp, "adding %s: sorted to the stack",
1966               GST_OBJECT_NAME (object));
1967           stack = g_list_insert_sorted (stack, object,
1968               (GCompareFunc) priority_comp);
1969           if (GNL_IS_OPERATION (object))
1970             gnl_operation_update_base_time (GNL_OPERATION (object), timestamp);
1971         }
1972       } else {
1973         GST_LOG_OBJECT (comp, "too far, stopping iteration");
1974         first_out_of_stack = object->start;
1975         break;
1976       }
1977     }
1978   }
1979
1980   /* Insert the expandables */
1981   if (G_LIKELY (timestamp < GNL_OBJECT_STOP (comp)))
1982     for (tmp = comp->priv->expandables; tmp; tmp = tmp->next) {
1983       GST_DEBUG_OBJECT (comp, "Adding expandable %s sorted to the list",
1984           GST_OBJECT_NAME (tmp->data));
1985       stack = g_list_insert_sorted (stack, tmp->data,
1986           (GCompareFunc) priority_comp);
1987       if (GNL_IS_OPERATION (tmp->data))
1988         gnl_operation_update_base_time (GNL_OPERATION (tmp->data), timestamp);
1989     }
1990
1991   /* convert that list to a stack */
1992   tmp = stack;
1993   ret = convert_list_to_tree (&tmp, &nstart, &nstop, &highest);
1994   if (GST_CLOCK_TIME_IS_VALID (first_out_of_stack)) {
1995     if (reverse && nstart < first_out_of_stack)
1996       nstart = first_out_of_stack;
1997     else if (!reverse && nstop > first_out_of_stack)
1998       nstop = first_out_of_stack;
1999   }
2000
2001   GST_DEBUG ("nstart:%" GST_TIME_FORMAT ", nstop:%" GST_TIME_FORMAT,
2002       GST_TIME_ARGS (nstart), GST_TIME_ARGS (nstop));
2003
2004   if (*stop)
2005     *stop = nstop;
2006   if (*start)
2007     *start = nstart;
2008   if (highprio)
2009     *highprio = highest;
2010
2011   g_list_free (stack);
2012
2013   return ret;
2014 }
2015
2016 /*
2017  * get_clean_toplevel_stack:
2018  * @comp: The #GnlComposition
2019  * @timestamp: The #GstClockTime to look at
2020  * @stop_time: Pointer to a #GstClockTime for min stop time of returned stack
2021  * @start_time: Pointer to a #GstClockTime for greatest start time of returned stack
2022  *
2023  * Returns: The new current stack for the given #GnlComposition and @timestamp.
2024  *
2025  * WITH OBJECTS LOCK TAKEN
2026  */
2027 static GNode *
2028 get_clean_toplevel_stack (GnlComposition * comp, GstClockTime * timestamp,
2029     GstClockTime * start_time, GstClockTime * stop_time)
2030 {
2031   GNode *stack = NULL;
2032   GstClockTime start = G_MAXUINT64;
2033   GstClockTime stop = G_MAXUINT64;
2034   guint highprio;
2035   gboolean reverse = (comp->priv->segment->rate < 0.0);
2036
2037   GST_DEBUG_OBJECT (comp, "timestamp:%" GST_TIME_FORMAT,
2038       GST_TIME_ARGS (*timestamp));
2039   GST_DEBUG ("start:%" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT,
2040       GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2041
2042   stack = get_stack_list (comp, *timestamp, 0, TRUE, &start, &stop, &highprio);
2043
2044   if (!stack &&
2045       ((reverse && (*timestamp > COMP_REAL_START (comp))) ||
2046           (!reverse && (*timestamp < COMP_REAL_STOP (comp))))) {
2047     GST_ELEMENT_ERROR (comp, STREAM, WRONG_TYPE,
2048         ("Gaps ( at %" GST_TIME_FORMAT
2049             ") in the stream is not supported, the application is responsible"
2050             " for filling them", GST_TIME_ARGS (*timestamp)),
2051         ("Gap in the composition this should never"
2052             "append, make sure to fill them"));
2053
2054     return NULL;
2055   }
2056
2057   GST_DEBUG ("start:%" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT,
2058       GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2059
2060   if (stack) {
2061     guint32 top_priority = GNL_OBJECT_PRIORITY (stack->data);
2062
2063     /* Figure out if there's anything blocking us with smaller priority */
2064     refine_start_stop_in_region_above_priority (comp, *timestamp, start,
2065         stop, &start, &stop, (highprio == 0) ? top_priority : highprio);
2066   }
2067
2068   if (*stop_time) {
2069     if (stack)
2070       *stop_time = stop;
2071     else
2072       *stop_time = 0;
2073   }
2074
2075   if (*start_time) {
2076     if (stack)
2077       *start_time = start;
2078     else
2079       *start_time = 0;
2080   }
2081
2082   GST_DEBUG_OBJECT (comp,
2083       "Returning timestamp:%" GST_TIME_FORMAT " , start_time:%"
2084       GST_TIME_FORMAT " , stop_time:%" GST_TIME_FORMAT,
2085       GST_TIME_ARGS (*timestamp), GST_TIME_ARGS (*start_time),
2086       GST_TIME_ARGS (*stop_time));
2087
2088   return stack;
2089 }
2090
2091
2092 static gboolean
2093 set_child_caps (GValue * item, GValue * ret G_GNUC_UNUSED, GnlObject * comp)
2094 {
2095   GstElement *child = g_value_get_object (item);
2096
2097   gnl_object_set_caps ((GnlObject *) child, comp->caps);
2098
2099   return TRUE;
2100 }
2101
2102 /*  Must be called with OBJECTS_LOCK taken */
2103 static void
2104 _set_current_bin_to_ready (GnlComposition * comp)
2105 {
2106   GstPad *ptarget;
2107   GnlCompositionPrivate *priv = comp->priv;
2108
2109   /* FIXME Check how to guarantee some thread safety here */
2110   if (priv->current && GST_STATE (comp) != GST_STATE_PLAYING) {
2111     ptarget = gst_ghost_pad_get_target (GST_GHOST_PAD (GNL_OBJECT_SRC (comp)));
2112
2113     if (ptarget) {
2114       gst_element_send_event (priv->current_bin, gst_event_new_flush_start ());
2115       gst_element_send_event (priv->current_bin,
2116           gst_event_new_flush_stop (TRUE));
2117
2118       gst_object_unref (ptarget);
2119     }
2120   }
2121
2122   gst_element_set_locked_state (priv->current_bin, TRUE);
2123   gst_element_set_state (priv->current_bin, GST_STATE_READY);
2124 }
2125
2126 /*  Must be called with OBJECTS_LOCK taken */
2127 static void
2128 _process_pending_entries (GnlComposition * comp)
2129 {
2130   GnlObject *object;
2131   GHashTableIter iter;
2132   gboolean deactivated_stack = FALSE;
2133
2134   GnlCompositionPrivate *priv = comp->priv;
2135
2136   g_hash_table_iter_init (&iter, priv->pending_io);
2137   while (g_hash_table_iter_next (&iter, (gpointer *) & object, NULL)) {
2138     GnlCompositionEntry *entry = COMP_ENTRY (comp, object);
2139
2140     if (entry) {
2141
2142       if (GST_OBJECT_PARENT (object) == GST_OBJECT_CAST (priv->current_bin) &&
2143           deactivated_stack == FALSE) {
2144         deactivated_stack = TRUE;
2145
2146         _deactivate_stack (comp);
2147       }
2148
2149       _gnl_composition_remove_entry (comp, object);
2150     } else {
2151       _gnl_composition_add_entry (comp, object);
2152     }
2153   }
2154
2155   g_hash_table_remove_all (priv->pending_io);
2156 }
2157
2158 static gboolean
2159 _emit_commited_signal_func (GnlComposition * comp)
2160 {
2161   GST_INFO_OBJECT (comp, "Emiting COMMITED now that the stack " "is ready");
2162
2163   g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE);
2164
2165   return G_SOURCE_REMOVE;
2166 }
2167
2168 static GstPadProbeReturn
2169 _add_emit_commited_and_restart_task (GnlComposition * comp)
2170 {
2171   GST_ERROR_OBJECT (comp, "Setup commit and restart task!");
2172
2173   MAIN_CONTEXT_LOCK (comp);
2174   g_main_context_invoke_full (comp->priv->mcontext, G_PRIORITY_HIGH,
2175       (GSourceFunc) _emit_commited_signal_func, comp, NULL);
2176   MAIN_CONTEXT_UNLOCK (comp);
2177
2178
2179   comp->priv->awaited_segment_seqnum = 0;
2180   comp->priv->commited_probeid = 0;
2181
2182   gst_task_start (comp->task);
2183
2184   return GST_PAD_PROBE_REMOVE;
2185 }
2186
2187 static GstPadProbeReturn
2188 _commit_done_cb (GstPad * pad, GstPadProbeInfo * info, GnlComposition * comp)
2189 {
2190   if (comp->priv->awaited_segment_seqnum) {
2191     if (GST_IS_EVENT (info->data)) {
2192       gint seqnum = gst_event_get_seqnum (info->data);
2193
2194       GST_DEBUG_OBJECT (comp, "Got event %s -- with seqnum: %i "
2195           "(awaited_segment_seqnum: %i)",
2196           GST_EVENT_TYPE_NAME (info->data), seqnum,
2197           comp->priv->awaited_segment_seqnum);
2198
2199       if (seqnum == comp->priv->awaited_segment_seqnum) {
2200
2201         if (GST_EVENT_TYPE (info->data) == GST_EVENT_EOS) {
2202           GST_INFO_OBJECT (comp, "Received EOS even before"
2203               " receiving SEGMENT with proper seqnum -> we are done");
2204
2205           return _add_emit_commited_and_restart_task (comp);
2206
2207         } else if (GST_EVENT_TYPE (info->data) == GST_EVENT_SEGMENT) {
2208
2209           GST_INFO_OBJECT (comp, "Got segment event with right seqnum"
2210               " now waiting for a buffer to restart playing with our "
2211               " children");
2212
2213           comp->priv->awaited_segment_seqnum = 0;
2214         }
2215       }
2216     }
2217
2218     return GST_PAD_PROBE_OK;
2219   } else if (GST_IS_BUFFER (info->data)) {
2220
2221     GST_INFO_OBJECT (comp, "Got %" GST_PTR_FORMAT " concidering commit "
2222         "as done", info->data);
2223
2224     GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (comp),
2225         GST_DEBUG_GRAPH_SHOW_ALL, "new-stack");
2226     return _add_emit_commited_and_restart_task (comp);
2227   }
2228
2229   GST_INFO_OBJECT (comp, "Got %" GST_PTR_FORMAT " still waiting for a buffer",
2230       info->data);
2231
2232   return GST_PAD_PROBE_OK;
2233 }
2234
2235 static inline gboolean
2236 _commit_values (GnlComposition * comp)
2237 {
2238   GList *tmp;
2239   gboolean commited = FALSE;
2240   GnlCompositionPrivate *priv = comp->priv;
2241
2242   for (tmp = priv->objects_start; tmp; tmp = tmp->next) {
2243     if (gnl_object_commit (tmp->data, TRUE))
2244       commited = TRUE;
2245   }
2246
2247   GST_DEBUG_OBJECT (comp, "Linking up commit vmethod");
2248   commited |= GNL_OBJECT_CLASS (parent_class)->commit (GNL_OBJECT (comp), TRUE);
2249
2250   return commited;
2251 }
2252
2253 static gboolean
2254 _commit_func (GnlComposition * comp)
2255 {
2256   GstClockTime curpos;
2257   GnlCompositionPrivate *priv = comp->priv;
2258
2259   GST_INFO_OBJECT (comp, "Commiting state");
2260
2261   COMP_OBJECTS_LOCK (comp);
2262
2263   /* Get current so that it represent the duration it was
2264    * before commiting children */
2265   curpos = get_current_position (comp);
2266
2267   _process_pending_entries (comp);
2268
2269   if (_commit_values (comp) == FALSE) {
2270     COMP_OBJECTS_UNLOCK (comp);
2271     GST_INFO_OBJECT (comp, "Nothing to commit, leaving");
2272
2273     g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, FALSE);
2274
2275     return G_SOURCE_REMOVE;
2276   }
2277
2278   /* The topology of the composition might have changed, update the lists */
2279   priv->objects_start = g_list_sort
2280       (priv->objects_start, (GCompareFunc) objects_start_compare);
2281   priv->objects_stop = g_list_sort
2282       (priv->objects_stop, (GCompareFunc) objects_stop_compare);
2283
2284   if (priv->initialized == FALSE) {
2285     GST_DEBUG_OBJECT (comp, "Not initialized yet, just updating values");
2286
2287     update_start_stop_duration (comp);
2288     COMP_OBJECTS_UNLOCK (comp);
2289
2290     g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE);
2291
2292   } else {
2293     /* And update the pipeline at current position if needed */
2294
2295     update_start_stop_duration (comp);
2296     update_pipeline (comp, curpos, TRUE, TRUE);
2297
2298     if (!priv->current) {
2299       COMP_OBJECTS_UNLOCK (comp);
2300
2301       GST_INFO_OBJECT (comp, "No new stack set, we can go and keep acting on"
2302           " our children");
2303
2304       g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE);
2305     } else {
2306       COMP_OBJECTS_UNLOCK (comp);
2307     }
2308   }
2309
2310   return G_SOURCE_REMOVE;
2311 }
2312
2313 static gboolean
2314 update_pipeline_func (GnlComposition * comp)
2315 {
2316   GnlCompositionPrivate *priv;
2317   gboolean reverse;
2318
2319   /* Set up a non-initial seek on segment_stop */
2320   priv = comp->priv;
2321   reverse = (priv->segment->rate < 0.0);
2322   if (!reverse) {
2323     GST_DEBUG_OBJECT (comp,
2324         "Setting segment->start to segment_stop:%" GST_TIME_FORMAT,
2325         GST_TIME_ARGS (priv->segment_stop));
2326     priv->segment->start = priv->segment_stop;
2327   } else {
2328     GST_DEBUG_OBJECT (comp,
2329         "Setting segment->stop to segment_start:%" GST_TIME_FORMAT,
2330         GST_TIME_ARGS (priv->segment_start));
2331     priv->segment->stop = priv->segment_start;
2332   }
2333
2334   seek_handling (comp, TRUE, TRUE);
2335
2336   /* Post segment done if last seek was a segment seek */
2337   if (!priv->current && (priv->segment->flags & GST_SEEK_FLAG_SEGMENT)) {
2338     gint64 epos;
2339
2340     if (GST_CLOCK_TIME_IS_VALID (priv->segment->stop))
2341       epos = (MIN (priv->segment->stop, GNL_OBJECT_STOP (comp)));
2342     else
2343       epos = GNL_OBJECT_STOP (comp);
2344
2345     GST_LOG_OBJECT (comp, "Emitting segment done pos %" GST_TIME_FORMAT,
2346         GST_TIME_ARGS (epos));
2347     gst_element_post_message (GST_ELEMENT_CAST (comp),
2348         gst_message_new_segment_done (GST_OBJECT (comp),
2349             priv->segment->format, epos));
2350     gst_pad_push_event (GNL_OBJECT (comp)->srcpad,
2351         gst_event_new_segment_done (priv->segment->format, epos));
2352   }
2353
2354
2355   return G_SOURCE_REMOVE;
2356 }
2357
2358 static void
2359 _set_all_children_state (GnlComposition * comp, GstState state)
2360 {
2361   GList *tmp;
2362
2363   GST_DEBUG_OBJECT (comp, "Setting all children state to %s",
2364       gst_element_state_get_name (state));
2365
2366   COMP_OBJECTS_LOCK (comp);
2367   gst_element_set_state (comp->priv->current_bin, state);
2368   for (tmp = comp->priv->objects_start; tmp; tmp = tmp->next)
2369     gst_element_set_state (tmp->data, state);
2370
2371   for (tmp = comp->priv->expandables; tmp; tmp = tmp->next)
2372     gst_element_set_state (tmp->data, state);
2373
2374   COMP_OBJECTS_UNLOCK (comp);
2375 }
2376
2377 static GstStateChangeReturn
2378 gnl_composition_change_state (GstElement * element, GstStateChange transition)
2379 {
2380   GstIterator *children;
2381   GnlComposition *comp = (GnlComposition *) element;
2382   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2383
2384   GST_DEBUG_OBJECT (comp, "%s => %s",
2385       gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
2386       gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
2387
2388   switch (transition) {
2389     case GST_STATE_CHANGE_READY_TO_PAUSED:
2390       gnl_composition_reset (comp);
2391
2392       /* state-lock all elements */
2393       GST_DEBUG_OBJECT (comp,
2394           "Setting all children to READY and locking their state");
2395
2396       children = gst_bin_iterate_elements (GST_BIN (comp->priv->current_bin));
2397
2398       while (G_UNLIKELY (gst_iterator_fold (children,
2399                   (GstIteratorFoldFunction) lock_child_state, NULL,
2400                   NULL) == GST_ITERATOR_RESYNC)) {
2401         gst_iterator_resync (children);
2402       }
2403       gst_iterator_free (children);
2404
2405       /* Set caps on all objects */
2406       if (G_UNLIKELY (!gst_caps_is_any (GNL_OBJECT (comp)->caps))) {
2407         children = gst_bin_iterate_elements (GST_BIN (comp->priv->current_bin));
2408
2409         while (G_UNLIKELY (gst_iterator_fold (children,
2410                     (GstIteratorFoldFunction) set_child_caps, NULL,
2411                     comp) == GST_ITERATOR_RESYNC)) {
2412           gst_iterator_resync (children);
2413         }
2414         gst_iterator_free (children);
2415       }
2416
2417       _add_initialize_stack_gsource (comp);
2418       break;
2419     case GST_STATE_CHANGE_PAUSED_TO_READY:
2420       _set_all_children_state (comp, GST_STATE_READY);
2421       gnl_composition_reset (comp);
2422       break;
2423     case GST_STATE_CHANGE_READY_TO_NULL:
2424       _set_all_children_state (comp, GST_STATE_NULL);
2425       gnl_composition_reset (comp);
2426       break;
2427     default:
2428       break;
2429   }
2430
2431   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2432
2433   if (ret == GST_STATE_CHANGE_FAILURE)
2434     return ret;
2435
2436   switch (transition) {
2437     case GST_STATE_CHANGE_PAUSED_TO_READY:
2438     case GST_STATE_CHANGE_READY_TO_NULL:
2439       unblock_children (comp);
2440       break;
2441     default:
2442       break;
2443   }
2444
2445   return ret;
2446 }
2447
2448 static gint
2449 objects_start_compare (GnlObject * a, GnlObject * b)
2450 {
2451   if (a->start == b->start) {
2452     if (a->priority < b->priority)
2453       return -1;
2454     if (a->priority > b->priority)
2455       return 1;
2456     return 0;
2457   }
2458   if (a->start < b->start)
2459     return -1;
2460   if (a->start > b->start)
2461     return 1;
2462   return 0;
2463 }
2464
2465 static gint
2466 objects_stop_compare (GnlObject * a, GnlObject * b)
2467 {
2468   if (a->stop == b->stop) {
2469     if (a->priority < b->priority)
2470       return -1;
2471     if (a->priority > b->priority)
2472       return 1;
2473     return 0;
2474   }
2475   if (b->stop < a->stop)
2476     return -1;
2477   if (b->stop > a->stop)
2478     return 1;
2479   return 0;
2480 }
2481
2482 /* WITH OBJECTS LOCK TAKEN */
2483 static void
2484 update_start_stop_duration (GnlComposition * comp)
2485 {
2486   GnlObject *obj;
2487   GnlObject *cobj = (GnlObject *) comp;
2488   GnlCompositionPrivate *priv = comp->priv;
2489
2490   if (!priv->objects_start) {
2491     GST_LOG ("no objects, resetting everything to 0");
2492
2493     if (cobj->start) {
2494       cobj->start = cobj->pending_start = 0;
2495       g_object_notify_by_pspec (G_OBJECT (cobj),
2496           gnlobject_properties[GNLOBJECT_PROP_START]);
2497     }
2498
2499     if (cobj->duration) {
2500       cobj->pending_duration = cobj->duration = 0;
2501       g_object_notify_by_pspec (G_OBJECT (cobj),
2502           gnlobject_properties[GNLOBJECT_PROP_DURATION]);
2503       signal_duration_change (comp);
2504     }
2505
2506     if (cobj->stop) {
2507       cobj->stop = 0;
2508       g_object_notify_by_pspec (G_OBJECT (cobj),
2509           gnlobject_properties[GNLOBJECT_PROP_STOP]);
2510     }
2511
2512     return;
2513   }
2514
2515   /* If we have a default object, the start position is 0 */
2516   if (priv->expandables) {
2517     GST_LOG_OBJECT (cobj,
2518         "Setting start to 0 because we have a default object");
2519
2520     if (cobj->start != 0) {
2521       cobj->pending_start = cobj->start = 0;
2522       g_object_notify_by_pspec (G_OBJECT (cobj),
2523           gnlobject_properties[GNLOBJECT_PROP_START]);
2524     }
2525
2526   } else {
2527
2528     /* Else it's the first object's start value */
2529     obj = (GnlObject *) priv->objects_start->data;
2530
2531     if (obj->start != cobj->start) {
2532       GST_LOG_OBJECT (obj, "setting start from %s to %" GST_TIME_FORMAT,
2533           GST_OBJECT_NAME (obj), GST_TIME_ARGS (obj->start));
2534       cobj->pending_start = cobj->start = obj->start;
2535       g_object_notify_by_pspec (G_OBJECT (cobj),
2536           gnlobject_properties[GNLOBJECT_PROP_START]);
2537     }
2538
2539   }
2540
2541   obj = (GnlObject *) priv->objects_stop->data;
2542
2543   if (obj->stop != cobj->stop) {
2544     GST_LOG_OBJECT (obj, "setting stop from %s to %" GST_TIME_FORMAT,
2545         GST_OBJECT_NAME (obj), GST_TIME_ARGS (obj->stop));
2546
2547     if (priv->expandables) {
2548       GList *tmp;
2549
2550       GST_INFO_OBJECT (comp, "RE-setting all expandables duration and commit");
2551       for (tmp = priv->expandables; tmp; tmp = tmp->next) {
2552         g_object_set (tmp->data, "duration", obj->stop, NULL);
2553         gnl_object_commit (GNL_OBJECT (tmp->data), FALSE);
2554       }
2555     }
2556
2557     priv->segment->stop = obj->stop;
2558     cobj->stop = obj->stop;
2559     g_object_notify_by_pspec (G_OBJECT (cobj),
2560         gnlobject_properties[GNLOBJECT_PROP_STOP]);
2561   }
2562
2563   if ((cobj->stop - cobj->start) != cobj->duration) {
2564     cobj->pending_duration = cobj->duration = cobj->stop - cobj->start;
2565     g_object_notify_by_pspec (G_OBJECT (cobj),
2566         gnlobject_properties[GNLOBJECT_PROP_DURATION]);
2567     signal_duration_change (comp);
2568   }
2569
2570   GST_LOG_OBJECT (comp,
2571       "start:%" GST_TIME_FORMAT
2572       " stop:%" GST_TIME_FORMAT
2573       " duration:%" GST_TIME_FORMAT,
2574       GST_TIME_ARGS (cobj->start),
2575       GST_TIME_ARGS (cobj->stop), GST_TIME_ARGS (cobj->duration));
2576 }
2577
2578 static inline gboolean
2579 _parent_or_priority_changed (GnlObject * obj, GNode * oldnode,
2580     GnlObject * newparent, GNode * node)
2581 {
2582   GnlObject *oldparent = NULL;
2583
2584   if (oldnode)
2585     oldparent =
2586         G_NODE_IS_ROOT (oldnode) ? NULL : (GnlObject *) oldnode->parent->data;
2587
2588   if (oldparent != newparent)
2589     return TRUE;
2590
2591   if (oldparent == NULL || newparent == NULL)
2592     return FALSE;
2593
2594   return (g_node_child_index (node, obj) != g_node_child_index (oldnode, obj));
2595 }
2596
2597 static void
2598 _link_to_parent (GnlComposition * comp, GnlObject * newobj,
2599     GnlObject * newparent)
2600 {
2601   GstPad *sinkpad;
2602
2603   /* relink to new parent in required order */
2604   GST_LOG_OBJECT (comp, "Linking %s and %s",
2605       GST_ELEMENT_NAME (GST_ELEMENT (newobj)),
2606       GST_ELEMENT_NAME (GST_ELEMENT (newparent)));
2607
2608   sinkpad = get_unlinked_sink_ghost_pad ((GnlOperation *) newparent);
2609
2610   if (G_UNLIKELY (sinkpad == NULL)) {
2611     GST_WARNING_OBJECT (comp,
2612         "Couldn't find an unlinked sinkpad from %s",
2613         GST_ELEMENT_NAME (newparent));
2614   } else {
2615     if (G_UNLIKELY (gst_pad_link_full (GNL_OBJECT_SRC (newobj), sinkpad,
2616                 GST_PAD_LINK_CHECK_NOTHING) != GST_PAD_LINK_OK)) {
2617       GST_WARNING_OBJECT (comp, "Failed to link pads %s:%s - %s:%s",
2618           GST_DEBUG_PAD_NAME (GNL_OBJECT_SRC (newobj)),
2619           GST_DEBUG_PAD_NAME (sinkpad));
2620     }
2621     gst_object_unref (sinkpad);
2622   }
2623 }
2624
2625 static void
2626 _relink_children_recursively (GnlComposition * comp,
2627     GnlObject * newobj, GNode * node, GstEvent * toplevel_seek)
2628 {
2629   GNode *child;
2630   guint nbchildren = g_node_n_children (node);
2631   GnlOperation *oper = (GnlOperation *) newobj;
2632
2633   GST_INFO_OBJECT (newobj, "is a %s operation, analyzing the %d children",
2634       oper->dynamicsinks ? "dynamic" : "regular", nbchildren);
2635   /* Update the operation's number of sinks, that will make it have the proper
2636    * number of sink pads to connect the children to. */
2637   if (oper->dynamicsinks)
2638     g_object_set (G_OBJECT (newobj), "sinks", nbchildren, NULL);
2639
2640   for (child = node->children; child; child = child->next)
2641     _relink_single_node (comp, child, toplevel_seek);
2642
2643   if (G_UNLIKELY (nbchildren < oper->num_sinks))
2644     GST_ERROR ("Not enough sinkpads to link all objects to the operation ! "
2645         "%d / %d", oper->num_sinks, nbchildren);
2646
2647   if (G_UNLIKELY (nbchildren == 0))
2648     GST_ERROR ("Operation has no child objects to be connected to !!!");
2649   /* Make sure we have enough sinkpads */
2650 }
2651
2652 /*
2653  * recursive depth-first relink stack function on new stack
2654  *
2655  * _ relink nodes with changed parent/order
2656  * _ links new nodes with parents
2657  * _ unblocks available source pads (except for toplevel)
2658  *
2659  * WITH OBJECTS LOCK TAKEN
2660  */
2661 static void
2662 _relink_single_node (GnlComposition * comp, GNode * node,
2663     GstEvent * toplevel_seek)
2664 {
2665   GnlObject *newobj;
2666   GnlObject *newparent;
2667   GstPad *srcpad = NULL, *sinkpad = NULL;
2668   GstEvent *translated_seek;
2669
2670   if (G_UNLIKELY (!node))
2671     return;
2672
2673   newparent = G_NODE_IS_ROOT (node) ? NULL : (GnlObject *) node->parent->data;
2674   newobj = (GnlObject *) node->data;
2675
2676   GST_DEBUG_OBJECT (comp, "newobj:%s",
2677       GST_ELEMENT_NAME ((GstElement *) newobj));
2678
2679   srcpad = GNL_OBJECT_SRC (newobj);
2680
2681   gst_bin_add (GST_BIN (comp->priv->current_bin), gst_object_ref (newobj));
2682   gst_element_sync_state_with_parent (GST_ELEMENT_CAST (newobj));
2683
2684   translated_seek = gnl_object_translate_incoming_seek (newobj, toplevel_seek);
2685
2686   gst_element_send_event (GST_ELEMENT (newobj), translated_seek);
2687
2688   /* link to parent if needed.  */
2689   if (newparent) {
2690     _link_to_parent (comp, newobj, newparent);
2691
2692     /* If there's an operation, inform it about priority changes */
2693     sinkpad = gst_pad_get_peer (srcpad);
2694     gnl_operation_signal_input_priority_changed ((GnlOperation *)
2695         newparent, sinkpad, newobj->priority);
2696     gst_object_unref (sinkpad);
2697   }
2698
2699   /* Handle children */
2700   if (GNL_IS_OPERATION (newobj))
2701     _relink_children_recursively (comp, newobj, node, toplevel_seek);
2702
2703   GST_LOG_OBJECT (comp, "done with object %s",
2704       GST_ELEMENT_NAME (GST_ELEMENT (newobj)));
2705 }
2706
2707
2708
2709 /*
2710  * compare_relink_stack:
2711  * @comp: The #GnlComposition
2712  * @stack: The new stack
2713  * @modify: TRUE if the timeline has changed and needs downstream flushes.
2714  *
2715  * Compares the given stack to the current one and relinks it if needed.
2716  *
2717  * WITH OBJECTS LOCK TAKEN
2718  *
2719  * Returns: The #GList of #GnlObject no longer used
2720  */
2721
2722 static void
2723 _deactivate_stack (GnlComposition * comp)
2724 {
2725   GstPad *ptarget;
2726
2727   _set_current_bin_to_ready (comp);
2728
2729   ptarget = gst_ghost_pad_get_target (GST_GHOST_PAD (GNL_OBJECT_SRC (comp)));
2730   _empty_bin (GST_BIN_CAST (comp->priv->current_bin));
2731
2732   if (comp->priv->ghosteventprobe) {
2733     GST_INFO_OBJECT (comp, "Removing old ghost pad probe");
2734
2735     gst_pad_remove_probe (ptarget, comp->priv->ghosteventprobe);
2736     comp->priv->ghosteventprobe = 0;
2737   }
2738
2739   if (ptarget)
2740     gst_object_unref (ptarget);
2741
2742 /*   priv->current = NULL;
2743  */
2744 }
2745
2746 static void
2747 _relink_new_stack (GnlComposition * comp, GNode * stack,
2748     GstEvent * toplevel_seek)
2749 {
2750   GnlCompositionPrivate *priv = comp->priv;
2751
2752   GST_ERROR ("Reseting seqnum to %i", gst_event_get_seqnum (toplevel_seek));
2753   GNL_OBJECT (comp)->wanted_seqnum = gst_event_get_seqnum (toplevel_seek);
2754
2755   _relink_single_node (comp, stack, toplevel_seek);
2756
2757   gst_event_unref (toplevel_seek);
2758
2759   gst_element_set_locked_state (priv->current_bin, FALSE);
2760   gst_element_sync_state_with_parent (priv->current_bin);
2761 }
2762
2763 /* static void
2764  * unlock_activate_stack (GnlComposition * comp, GNode * node, GstState state)
2765  * {
2766  *   GNode *child;
2767  * 
2768  *   GST_LOG_OBJECT (comp, "object:%s",
2769  *       GST_ELEMENT_NAME ((GstElement *) (node->data)));
2770  * 
2771  *   gst_element_set_locked_state ((GstElement *) (node->data), FALSE);
2772  *   gst_element_set_state (GST_ELEMENT (node->data), state);
2773  * 
2774  *   for (child = node->children; child; child = child->next)
2775  *     unlock_activate_stack (comp, child, state);
2776  * }
2777  */
2778
2779 static gboolean
2780 are_same_stacks (GNode * stack1, GNode * stack2)
2781 {
2782   gboolean res = FALSE;
2783
2784   /* TODO : FIXME : we should also compare start/inpoint */
2785   /* stacks are not equal if one of them is NULL but not the other */
2786   if ((!stack1 && stack2) || (stack1 && !stack2))
2787     goto beach;
2788
2789   if (stack1 && stack2) {
2790     GNode *child1, *child2;
2791
2792     /* if they don't contain the same source, not equal */
2793     if (!(stack1->data == stack2->data))
2794       goto beach;
2795
2796     /* if they don't have the same number of children, not equal */
2797     if (!(g_node_n_children (stack1) == g_node_n_children (stack2)))
2798       goto beach;
2799
2800     child1 = stack1->children;
2801     child2 = stack2->children;
2802     while (child1 && child2) {
2803       if (!(are_same_stacks (child1, child2)))
2804         goto beach;
2805       child1 = g_node_next_sibling (child1);
2806       child2 = g_node_next_sibling (child2);
2807     }
2808
2809     /* if there's a difference in child number, stacks are not equal */
2810     if (child1 || child2)
2811       goto beach;
2812   }
2813
2814   /* if stack1 AND stack2 are NULL, then they're equal (both empty) */
2815   res = TRUE;
2816
2817 beach:
2818   GST_LOG ("Stacks are equal : %d", res);
2819
2820   return res;
2821 }
2822
2823 static inline gboolean
2824 _activate_new_stack (GnlComposition * comp)
2825 {
2826   GstPad *pad;
2827   GstElement *topelement;
2828   GnlCompositionEntry *topentry;
2829
2830   GnlCompositionPrivate *priv = comp->priv;
2831
2832   if (!priv->current) {
2833     if ((!priv->objects_start)) {
2834       gnl_composition_reset_target_pad (comp);
2835       priv->segment_start = 0;
2836       priv->segment_stop = GST_CLOCK_TIME_NONE;
2837     }
2838
2839     GST_ERROR_OBJECT (comp, "Nothing else in the composition"
2840         ", update 'worked'");
2841     return TRUE;
2842   }
2843
2844   priv->stackvalid = TRUE;
2845
2846   /* The stack is entirely ready, send seek out synchronously */
2847   topelement = GST_ELEMENT (priv->current->data);
2848   /* Get toplevel object source pad */
2849   pad = GNL_OBJECT_SRC (topelement);
2850   topentry = COMP_ENTRY (comp, topelement);
2851
2852   GST_ERROR_OBJECT (comp,
2853       "We have a valid toplevel element pad %s:%s", GST_DEBUG_PAD_NAME (pad));
2854
2855   gnl_composition_ghost_pad_set_target (comp, pad, topentry);
2856
2857   GST_ERROR_OBJECT (comp, "New stack activated!");
2858   return TRUE;
2859 }
2860
2861 /* WITH OBJECTS LOCK TAKEN */
2862 static gboolean
2863 _set_real_eos_seqnum_from_seek (GnlComposition * comp, GstEvent * event)
2864 {
2865   GList *tmp;
2866
2867   gboolean should_check_objects = FALSE;
2868   GnlCompositionPrivate *priv = comp->priv;
2869   gboolean reverse = (priv->segment->rate < 0);
2870   gint stack_seqnum = gst_event_get_seqnum (event);
2871
2872   if (reverse && GST_CLOCK_TIME_IS_VALID (priv->segment_start))
2873     should_check_objects = TRUE;
2874   else if (!reverse && GST_CLOCK_TIME_IS_VALID (priv->segment_stop))
2875     should_check_objects = TRUE;
2876
2877   if (should_check_objects) {
2878     for (tmp = priv->objects_stop; tmp; tmp = g_list_next (tmp)) {
2879       GnlObject *object = (GnlObject *) tmp->data;
2880
2881       if (!GNL_IS_SOURCE (object))
2882         continue;
2883
2884       if ((!reverse && priv->segment_stop < object->stop) ||
2885           (reverse && priv->segment_start > object->start)) {
2886
2887         g_atomic_int_set (&priv->real_eos_seqnum, 0);
2888         return FALSE;
2889       }
2890     }
2891   }
2892
2893   g_atomic_int_set (&priv->real_eos_seqnum, stack_seqnum);
2894
2895   return TRUE;
2896 }
2897
2898 /*
2899  * update_pipeline:
2900  * @comp: The #GnlComposition
2901  * @currenttime: The #GstClockTime to update at, can be GST_CLOCK_TIME_NONE.
2902  * @initial: TRUE if this is the first setup
2903  * @change_state: Change the state of the (de)activated objects if TRUE.
2904  * @modify: Flush downstream if TRUE. Needed for modified timelines.
2905  *
2906  * Updates the internal pipeline and properties. If @currenttime is
2907  * GST_CLOCK_TIME_NONE, it will not modify the current pipeline
2908  *
2909  * Returns: FALSE if there was an error updating the pipeline.
2910  *
2911  * WITH OBJECTS LOCK TAKEN
2912  */
2913 static gboolean
2914 update_pipeline (GnlComposition * comp, GstClockTime currenttime,
2915     gboolean initial, gboolean modify)
2916 {
2917
2918   gint stack_seqnum;
2919   GstEvent *toplevel_seek;
2920
2921   GNode *stack = NULL;
2922   gboolean samestack = FALSE;
2923   gboolean updatestoponly = FALSE;
2924   GstState state = GST_STATE (comp);
2925   GnlCompositionPrivate *priv = comp->priv;
2926   GstClockTime new_stop = GST_CLOCK_TIME_NONE;
2927   GstClockTime new_start = GST_CLOCK_TIME_NONE;
2928
2929   gboolean startchanged, stopchanged;
2930
2931   GstState nextstate = (GST_STATE_NEXT (comp) == GST_STATE_VOID_PENDING) ?
2932       GST_STATE (comp) : GST_STATE_NEXT (comp);
2933
2934   GST_ERROR_OBJECT (comp,
2935       "currenttime:%" GST_TIME_FORMAT
2936       " initial:%d , modify:%d", GST_TIME_ARGS (currenttime), initial, modify);
2937
2938   if (!GST_CLOCK_TIME_IS_VALID (currenttime))
2939     return FALSE;
2940
2941   if (state == GST_STATE_NULL && nextstate == GST_STATE_NULL) {
2942     GST_DEBUG_OBJECT (comp, "STATE_NULL: not updating pipeline");
2943     return FALSE;
2944   }
2945
2946
2947   GST_DEBUG_OBJECT (comp,
2948       "now really updating the pipeline, current-state:%s",
2949       gst_element_state_get_name (state));
2950
2951   /* Get new stack and compare it to current one */
2952   stack = get_clean_toplevel_stack (comp, &currenttime, &new_start, &new_stop);
2953   samestack = are_same_stacks (priv->current, stack);
2954
2955   /* invalidate the stack while modifying it */
2956   priv->stackvalid = FALSE;
2957
2958   if (priv->segment->rate >= 0.0) {
2959     startchanged = priv->segment_start != currenttime;
2960     stopchanged = priv->segment_stop != new_stop;
2961   } else {
2962     startchanged = priv->segment_start != new_start;
2963     stopchanged = priv->segment_stop != currenttime;
2964   }
2965
2966   /* set new segment_start/stop (the current zone over which the new stack
2967    * is valid) */
2968   if (priv->segment->rate >= 0.0) {
2969     priv->segment_start = currenttime;
2970     priv->segment_stop = new_stop;
2971   } else {
2972     priv->segment_start = new_start;
2973     priv->segment_stop = currenttime;
2974   }
2975
2976   if (samestack) {
2977     if (startchanged || stopchanged) {
2978       /* Update seek events need to be flushing if not in PLAYING,
2979        * else we will encounter deadlocks. */
2980       updatestoponly = (state == GST_STATE_PLAYING) ? FALSE : TRUE;
2981     }
2982   }
2983
2984   toplevel_seek = get_new_seek_event (comp, TRUE, updatestoponly);
2985   stack_seqnum = gst_event_get_seqnum (toplevel_seek);
2986   _set_real_eos_seqnum_from_seek (comp, toplevel_seek);
2987
2988   /* If stacks are different, unlink/relink objects */
2989   if (!samestack) {
2990     _deactivate_stack (comp);
2991     _relink_new_stack (comp, stack, toplevel_seek);
2992   }
2993
2994   /* Unlock all elements in new stack */
2995   GST_INFO_OBJECT (comp, "Setting current stack [%" GST_TIME_FORMAT " - %"
2996       GST_TIME_FORMAT "]", GST_TIME_ARGS (priv->segment_start),
2997       GST_TIME_ARGS (priv->segment_stop));
2998   priv->current = stack;
2999
3000   if (priv->current) {
3001     GST_INFO_OBJECT (comp, "New stack set and ready to run, probing src pad"
3002         " and stopping children thread until we are actually ready with"
3003         " that new stack");
3004
3005     comp->priv->awaited_segment_seqnum = stack_seqnum;
3006     priv->commited_probeid = gst_pad_add_probe (GNL_OBJECT_SRC (comp),
3007         GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
3008         (GstPadProbeCallback) _commit_done_cb, comp, NULL);
3009
3010     gst_task_pause (comp->task);
3011   }
3012
3013   /* Activate stack */
3014   if (!samestack)
3015     return _activate_new_stack (comp);
3016   else
3017     return _seek_current_stack (comp, toplevel_seek);
3018 }
3019
3020 static gboolean
3021 gnl_composition_add_object (GstBin * bin, GstElement * element)
3022 {
3023   GnlComposition *comp = (GnlComposition *) bin;
3024
3025   if (element == comp->priv->current_bin) {
3026     GST_ERROR_OBJECT (comp, "Adding internal bin");
3027     return GST_BIN_CLASS (parent_class)->add_element (bin, element);
3028   }
3029
3030   g_assert_not_reached ();
3031
3032   return FALSE;
3033 }
3034
3035 static gboolean
3036 _gnl_composition_add_entry (GnlComposition * comp, GnlObject * object)
3037 {
3038   gboolean ret = TRUE;
3039
3040   GnlCompositionEntry *entry;
3041   GnlCompositionPrivate *priv = comp->priv;
3042
3043   GST_DEBUG_OBJECT (comp, "element %s", GST_OBJECT_NAME (object));
3044   GST_DEBUG_OBJECT (object, "%" GST_TIME_FORMAT "--%" GST_TIME_FORMAT,
3045       GST_TIME_ARGS (GNL_OBJECT_START (object)),
3046       GST_TIME_ARGS (GNL_OBJECT_STOP (object)));
3047
3048   g_object_ref_sink (object);
3049
3050   if ((GNL_OBJECT_IS_EXPANDABLE (object)) &&
3051       g_list_find (priv->expandables, object)) {
3052     GST_WARNING_OBJECT (comp,
3053         "We already have an expandable, remove it before adding new one");
3054     ret = FALSE;
3055
3056     goto chiringuito;
3057   }
3058
3059   gnl_object_set_caps (object, GNL_OBJECT (comp)->caps);
3060   gnl_object_set_commit_needed (GNL_OBJECT (comp));
3061
3062   if (!ret) {
3063     GST_WARNING_OBJECT (comp, "couldn't add object");
3064     goto chiringuito;
3065   }
3066
3067   /* lock state of child ! */
3068   GST_LOG_OBJECT (comp, "Locking state of %s", GST_ELEMENT_NAME (object));
3069
3070   /* wrap new element in a GnlCompositionEntry ... */
3071   entry = g_slice_new0 (GnlCompositionEntry);
3072   entry->object = (GnlObject *) object;
3073   entry->comp = comp;
3074
3075   if (GNL_OBJECT_IS_EXPANDABLE (object)) {
3076     /* Only react on non-default objects properties */
3077     g_object_set (object,
3078         "start", (GstClockTime) 0,
3079         "inpoint", (GstClockTime) 0,
3080         "duration", (GstClockTimeDiff) GNL_OBJECT_STOP (comp), NULL);
3081
3082     GST_INFO_OBJECT (object, "Used as expandable, commiting now");
3083     gnl_object_commit (GNL_OBJECT (object), FALSE);
3084   }
3085
3086   /* ...and add it to the hash table */
3087   g_hash_table_insert (priv->objects_hash, object, entry);
3088
3089   /* Set the caps of the composition */
3090   if (G_UNLIKELY (!gst_caps_is_any (((GnlObject *) comp)->caps)))
3091     gnl_object_set_caps ((GnlObject *) object, ((GnlObject *) comp)->caps);
3092
3093   /* Special case for default source. */
3094   if (GNL_OBJECT_IS_EXPANDABLE (object)) {
3095     /* It doesn't get added to objects_start and objects_stop. */
3096     priv->expandables = g_list_prepend (priv->expandables, object);
3097     goto beach;
3098   }
3099
3100   /* add it sorted to the objects list */
3101   priv->objects_start = g_list_insert_sorted
3102       (priv->objects_start, object, (GCompareFunc) objects_start_compare);
3103
3104   if (priv->objects_start)
3105     GST_LOG_OBJECT (comp,
3106         "Head of objects_start is now %s [%" GST_TIME_FORMAT "--%"
3107         GST_TIME_FORMAT "]",
3108         GST_OBJECT_NAME (priv->objects_start->data),
3109         GST_TIME_ARGS (GNL_OBJECT_START (priv->objects_start->data)),
3110         GST_TIME_ARGS (GNL_OBJECT_STOP (priv->objects_start->data)));
3111
3112   priv->objects_stop = g_list_insert_sorted
3113       (priv->objects_stop, object, (GCompareFunc) objects_stop_compare);
3114
3115   /* Now the object is ready to be commited and then used */
3116
3117 beach:
3118   return ret;
3119
3120 chiringuito:
3121   {
3122     update_start_stop_duration (comp);
3123     goto beach;
3124   }
3125 }
3126
3127 static gboolean
3128 gnl_composition_remove_object (GstBin * bin, GstElement * element)
3129 {
3130   GnlComposition *comp = (GnlComposition *) bin;
3131
3132   if (element == comp->priv->current_bin) {
3133     GST_ERROR_OBJECT (comp, "Adding internal bin");
3134     return GST_BIN_CLASS (parent_class)->remove_element (bin, element);
3135   }
3136
3137   g_assert_not_reached ();
3138
3139   return FALSE;
3140 }
3141
3142 static gboolean
3143 _gnl_composition_remove_entry (GnlComposition * comp, GnlObject * object)
3144 {
3145   gboolean ret = FALSE;
3146   GnlCompositionEntry *entry;
3147   GnlCompositionPrivate *priv = comp->priv;
3148
3149   GST_ERROR_OBJECT (comp, "object %s", GST_OBJECT_NAME (object));
3150
3151   /* we only accept GnlObject */
3152   entry = COMP_ENTRY (comp, object);
3153   if (entry == NULL) {
3154     goto out;
3155   }
3156
3157   gst_element_set_locked_state (GST_ELEMENT (object), FALSE);
3158   gst_element_set_state (GST_ELEMENT (object), GST_STATE_NULL);
3159
3160   /* handle default source */
3161   if (GNL_OBJECT_IS_EXPANDABLE (object)) {
3162     /* Find it in the list */
3163     priv->expandables = g_list_remove (priv->expandables, object);
3164   } else {
3165     /* remove it from the objects list and resort the lists */
3166     priv->objects_start = g_list_remove (priv->objects_start, object);
3167     priv->objects_stop = g_list_remove (priv->objects_stop, object);
3168     GST_LOG_OBJECT (object, "Removed from the objects start/stop list");
3169   }
3170
3171   if (priv->current && GNL_OBJECT (priv->current->data) == GNL_OBJECT (object))
3172     gnl_composition_reset_target_pad (comp);
3173
3174   g_hash_table_remove (priv->objects_hash, object);
3175
3176   GST_LOG_OBJECT (object, "Done removing from the composition, now updating");
3177
3178   /* Make it possible to reuse the same object later */
3179   gnl_object_reset (GNL_OBJECT (object));
3180   gst_object_unref (object);
3181
3182 out:
3183   return ret;
3184 }