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>
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.
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.
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.
30 * SECTION:element-gnlcomposition
32 * A GnlComposition contains GnlObjects such as GnlSources and GnlOperations,
33 * and connects them dynamically to create a composition timeline.
36 static GstStaticPadTemplate gnl_composition_src_template =
37 GST_STATIC_PAD_TEMPLATE ("src",
42 GST_DEBUG_CATEGORY_STATIC (gnlcomposition_debug);
43 #define GST_CAT_DEFAULT gnlcomposition_debug
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,
55 PROP_DEACTIVATED_ELEMENTS_STATE,
59 /* Properties from GnlObject */
64 GNLOBJECT_PROP_DURATION,
77 typedef struct _GnlCompositionEntry GnlCompositionEntry;
91 struct _GnlCompositionPrivate
93 gboolean dispose_has_run;
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
102 GList *objects_start;
104 GHashTable *objects_hash;
107 /* List of GnlObject to be inserted or removed from the composition on the
109 GHashTable *pending_io;
110 GMutex pending_io_lock;
113 thread-safe Seek handling.
114 flushing_lock : mutex to access flushing and pending_idle
117 GMutex flushing_lock;
120 /* source top-level ghostpad, probe and entry */
121 gulong ghosteventprobe;
122 GnlCompositionEntry *toplevelentry;
124 /* current stack, list of GnlObject* */
127 /* List of GnlObject whose start/duration will be the same as the composition */
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 */
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
141 GstClockTime segment_start;
142 GstClockTime segment_stop;
144 /* Seek segment handler */
146 GstSegment *outside_segment;
148 /* Next running base_time to set on outgoing segment */
149 guint64 next_base_time;
152 OUR sync_handler on the child_bus
153 We are called before gnl_object_sync_handler
155 GstPadEventFunction gnl_event_pad_func;
156 gboolean send_stream_start;
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;
167 gboolean initialized;
169 GstState deactivated_elements_state;
171 GstElement *current_bin;
173 gboolean seeking_itself;
174 gint real_eos_seqnum;
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;
183 static guint _signals[LAST_SIGNAL] = { 0 };
185 static GParamSpec *gnlobject_properties[GNLOBJECT_PROP_LAST];
186 static GParamSpec *_properties[PROP_LAST];
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))
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);
200 static gboolean gnl_composition_add_object (GstBin * bin, GstElement * element);
202 static void gnl_composition_handle_message (GstBin * bin, GstMessage * message);
205 gnl_composition_remove_object (GstBin * bin, GstElement * element);
207 static GstStateChangeReturn
208 gnl_composition_change_state (GstElement * element, GstStateChange transition);
210 static inline void gnl_composition_reset_target_pad (GnlComposition * comp);
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);
218 static gboolean update_pipeline (GnlComposition * comp,
219 GstClockTime currenttime, gboolean initial, gboolean modify);
220 static gboolean gnl_composition_commit_func (GnlObject * object,
222 static void update_start_stop_duration (GnlComposition * comp);
225 gnl_composition_event_handler (GstPad * ghostpad, GstObject * parent,
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);
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);
238 _gnl_composition_add_entry (GnlComposition * comp, GnlObject * object);
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 *
245 _set_real_eos_seqnum_from_seek (GnlComposition * comp, GstEvent * event);
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)))
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))
257 #define COMP_ENTRY(comp, object) \
258 (g_hash_table_lookup (comp->priv->objects_hash, (gconstpointer) object))
260 #define COMP_OBJECTS_LOCK(comp) G_STMT_START { \
261 GST_LOG_OBJECT (comp, "locking objects_lock from thread %p", \
263 g_mutex_lock (&comp->priv->objects_lock); \
264 GST_LOG_OBJECT (comp, "locked objects_lock from thread %p", \
268 #define COMP_OBJECTS_UNLOCK(comp) G_STMT_START { \
269 GST_LOG_OBJECT (comp, "unlocking objects_lock from thread %p", \
271 g_mutex_unlock (&comp->priv->objects_lock); \
274 #define COMP_PENDING_IO_LOCK(comp) G_STMT_START { \
275 GST_LOG_OBJECT (comp, "locking pending_io_lock from thread %p", \
277 g_mutex_lock (&comp->priv->pending_io_lock); \
278 GST_LOG_OBJECT (comp, "locked pending_io_lock from thread %p", \
282 #define COMP_FLUSHING_LOCK(comp) G_STMT_START { \
283 GST_LOG_OBJECT (comp, "locking flushing_lock from thread %p", \
285 g_mutex_lock (&comp->priv->flushing_lock); \
286 GST_LOG_OBJECT (comp, "locked flushing_lock from thread %p", \
289 #define COMP_FLUSHING_UNLOCK(comp) G_STMT_START { \
290 GST_LOG_OBJECT (comp, "unlocking flushing_lock from thread %p", \
292 g_mutex_unlock (&comp->priv->flushing_lock); \
295 #define MAIN_CONTEXT_LOCK(comp) G_STMT_START { \
296 GST_LOG_OBJECT (comp, "Getting MAIN_CONTEXT_LOCK in thread %p", \
298 g_mutex_lock(&((GnlComposition*)comp)->priv->mcontext_lock); \
299 GST_LOG_OBJECT (comp, "Got MAIN_CONTEXT_LOCK in thread %p", \
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", \
309 #define GET_TASK_LOCK(comp) (&(GNL_COMPOSITION(comp)->task_rec_lock))
311 struct _GnlCompositionEntry
314 GnlComposition *comp;
316 /* handler id for block probe */
324 _remove_all_sources (GnlComposition * comp)
328 MAIN_CONTEXT_LOCK (comp);
330 g_main_context_find_source_by_user_data (comp->priv->mcontext,
332 g_source_destroy (source);
334 MAIN_CONTEXT_UNLOCK (comp);
338 iterate_main_context_func (GnlComposition * comp)
340 if (comp->priv->running == FALSE) {
341 GST_DEBUG_OBJECT (comp, "Not running anymore");
346 g_main_context_iteration (comp->priv->mcontext, TRUE);
350 _start_task (GnlComposition * comp)
354 comp->priv->running = TRUE;
356 GST_OBJECT_LOCK (comp);
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);
367 gst_task_set_state (task, GST_TASK_STARTED);
368 GST_OBJECT_UNLOCK (comp);
372 _stop_task (GnlComposition * comp)
377 GST_INFO_OBJECT (comp, "Stoping children management task");
379 comp->priv->running = FALSE;
381 /* Clean the stack of GSource set on the MainContext */
382 g_main_context_wakeup (comp->priv->mcontext);
383 _remove_all_sources (comp);
385 GST_DEBUG_OBJECT (comp, "stop task");
387 GST_OBJECT_LOCK (comp);
392 res = gst_task_set_state (task, GST_TASK_STOPPED);
393 GST_OBJECT_UNLOCK (comp);
395 if (!gst_task_join (task))
398 gst_object_unref (task);
404 GST_OBJECT_UNLOCK (comp);
406 /* this is not an error */
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)
419 GST_OBJECT_UNLOCK (comp);
428 _seek_pipeline_func (SeekData * seekd)
433 GstSeekType cur_type, stop_type;
435 GnlCompositionPrivate *priv = seekd->comp->priv;
437 gst_event_parse_seek (seekd->event, &rate, &format, &flags,
438 &cur_type, &cur, &stop_type, &stop);
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);
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);
449 GST_DEBUG_OBJECT (seekd->comp, "Segment now has flags:%d",
450 priv->segment->flags);
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!");
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));
469 priv->next_base_time = 0;
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)");
475 priv->reset_time = TRUE;
476 seek_handling (seekd->comp, TRUE, FALSE);
477 priv->reset_time = FALSE;
480 gst_event_unref (seekd->event);
481 g_slice_free (SeekData, seekd);
483 return G_SOURCE_REMOVE;
489 _add_update_gsource (GnlComposition * comp)
491 GST_DEBUG_OBJECT (comp, "Adding GSource");
493 MAIN_CONTEXT_LOCK (comp);
494 g_main_context_invoke (comp->priv->mcontext,
495 (GSourceFunc) update_pipeline_func, comp);
496 MAIN_CONTEXT_UNLOCK (comp);
500 _add_commit_gsource (GnlComposition * comp)
502 GST_DEBUG_OBJECT (comp, "Adding GSource");
504 MAIN_CONTEXT_LOCK (comp);
505 g_main_context_invoke (comp->priv->mcontext,
506 (GSourceFunc) _commit_func, comp);
507 MAIN_CONTEXT_UNLOCK (comp);
511 _add_seek_gsource (GnlComposition * comp, GstEvent * event)
513 SeekData *seekd = g_slice_new0 (SeekData);
515 GST_DEBUG_OBJECT (comp, "Adding GSource");
518 seekd->event = event;
520 MAIN_CONTEXT_LOCK (comp);
521 g_main_context_invoke (comp->priv->mcontext,
522 (GSourceFunc) _seek_pipeline_func, seekd);
523 MAIN_CONTEXT_UNLOCK (comp);
528 _initialize_stack_func (GnlComposition * comp)
530 GnlCompositionPrivate *priv = comp->priv;
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");
538 return G_SOURCE_REMOVE;
540 COMP_OBJECTS_UNLOCK (comp);
542 priv->initialized = TRUE;
544 return G_SOURCE_REMOVE;
548 _add_initialize_stack_gsource (GnlComposition * comp)
550 GST_DEBUG_OBJECT (comp, "Adding GSource");
552 MAIN_CONTEXT_LOCK (comp);
553 g_main_context_invoke (comp->priv->mcontext,
554 (GSourceFunc) _initialize_stack_func, comp);
555 MAIN_CONTEXT_UNLOCK (comp);
559 _free_child_io_data (gpointer childio)
561 g_slice_free (ChildIOData, childio);
565 _remove_object_func (ChildIOData * childio)
567 GnlComposition *comp = childio->comp;
568 GnlObject *object = childio->object;
570 GnlCompositionPrivate *priv = comp->priv;
571 GnlCompositionEntry *entry;
572 GnlObject *in_pending_io;
574 COMP_OBJECTS_LOCK (comp);
575 entry = COMP_ENTRY (comp, object);
576 in_pending_io = g_hash_table_lookup (priv->pending_io, object);
580 GST_INFO_OBJECT (comp, "Object %" GST_PTR_FORMAT " was marked"
581 " for addition, removing it from the addition list", object);
583 g_hash_table_remove (priv->pending_io, object);
584 COMP_OBJECTS_UNLOCK (comp);
588 GST_ERROR_OBJECT (comp, "Object %" GST_PTR_FORMAT " is "
589 " not in the composition", object);
591 COMP_OBJECTS_UNLOCK (comp);
596 GST_WARNING_OBJECT (comp, "Object %" GST_PTR_FORMAT " is already marked"
597 " for removal", object);
599 COMP_OBJECTS_UNLOCK (comp);
604 g_hash_table_add (priv->pending_io, object);
605 COMP_OBJECTS_UNLOCK (comp);
611 _add_remove_object_gsource (GnlComposition * comp, GnlObject * object)
613 ChildIOData *childio = g_slice_new0 (ChildIOData);
615 GST_DEBUG_OBJECT (comp, "Adding GSource");
617 childio->comp = comp;
618 childio->object = object;
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);
627 remove_object_handler (GnlComposition * comp, GnlObject * object)
629 g_return_val_if_fail (GNL_IS_OBJECT (object), FALSE);
631 _add_remove_object_gsource (comp, object);
637 _add_object_func (ChildIOData * childio)
639 GnlComposition *comp = childio->comp;
640 GnlObject *object = childio->object;
641 GnlCompositionPrivate *priv = comp->priv;
642 GnlCompositionEntry *entry;
643 GnlObject *in_pending_io;
645 COMP_OBJECTS_LOCK (comp);
646 entry = COMP_ENTRY (comp, object);
647 in_pending_io = g_hash_table_lookup (priv->pending_io, object);
650 GST_ERROR_OBJECT (comp, "Object %" GST_PTR_FORMAT " is "
651 " already in the composition", object);
653 COMP_OBJECTS_UNLOCK (comp);
658 GST_WARNING_OBJECT (comp, "Object %" GST_PTR_FORMAT " is already marked"
659 " for addition", object);
661 COMP_OBJECTS_UNLOCK (comp);
666 g_hash_table_add (priv->pending_io, object);
668 COMP_OBJECTS_UNLOCK (comp);
673 _add_add_object_gsource (GnlComposition * comp, GnlObject * object)
675 ChildIOData *childio = g_slice_new0 (ChildIOData);
677 GST_DEBUG_OBJECT (comp, "Adding GSource");
679 childio->comp = comp;
680 childio->object = object;
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);
689 add_object_handler (GnlComposition * comp, GnlObject * object)
691 g_return_val_if_fail (GNL_IS_OBJECT (object), FALSE);
693 _add_add_object_gsource (comp, object);
699 gnl_composition_class_init (GnlCompositionClass * klass)
701 GObjectClass *gobject_class;
702 GstElementClass *gstelement_class;
703 GstBinClass *gstbin_class;
704 GnlObjectClass *gnlobject_class;
706 gobject_class = (GObjectClass *) klass;
707 gstelement_class = (GstElementClass *) klass;
708 gstbin_class = (GstBinClass *) klass;
709 gnlobject_class = (GnlObjectClass *) klass;
711 g_type_class_add_private (klass, sizeof (GnlCompositionPrivate));
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>");
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);
724 gstelement_class->change_state = gnl_composition_change_state;
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);
732 gst_element_class_add_pad_template (gstelement_class,
733 gst_static_pad_template_get (&gnl_composition_src_template));
735 /* Get the paramspec of the GnlObject klass so we can do
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");
745 * GnlComposition:deactivated-elements-state
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.
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);
761 g_object_class_install_properties (gobject_class, PROP_LAST, _properties);
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
771 * Action signal to commit all the pending changes of the composition and
772 * its children timing properties
774 * Returns: %TRUE if changes have been commited, %FALSE if nothing had to
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);
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,
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);
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);
800 gnlobject_class->commit = gnl_composition_commit_func;
801 klass->remove_object_handler = remove_object_handler;
802 klass->add_object_handler = add_object_handler;
806 hash_value_destroy (GnlCompositionEntry * entry)
809 GstElement *element = GST_ELEMENT (entry->object);
811 srcpad = GNL_OBJECT_SRC (element);
812 if (entry->probeid) {
813 gst_pad_remove_probe (srcpad, entry->probeid);
817 if (entry->dataprobeid) {
818 gst_pad_remove_probe (srcpad, entry->dataprobeid);
819 entry->dataprobeid = 0;
822 g_slice_free (GnlCompositionEntry, entry);
826 gnl_composition_init (GnlComposition * comp)
828 GnlCompositionPrivate *priv;
830 GST_OBJECT_FLAG_SET (comp, GNL_OBJECT_SOURCE);
831 GST_OBJECT_FLAG_SET (comp, GNL_OBJECT_COMPOSITION);
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;
839 g_mutex_init (&priv->flushing_lock);
840 priv->flushing = FALSE;
842 priv->segment = gst_segment_new ();
843 priv->outside_segment = gst_segment_new ();
845 g_rec_mutex_init (&comp->task_rec_lock);
847 priv->reset_time = FALSE;
849 priv->objects_hash = g_hash_table_new_full
851 g_direct_equal, NULL, (GDestroyNotify) hash_value_destroy);
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
858 g_direct_equal, NULL, (GDestroyNotify) hash_value_destroy);
860 g_mutex_init (&priv->pending_io_lock);
861 priv->pending_io = g_hash_table_new (g_direct_hash, g_direct_equal);
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");
870 gnl_composition_reset (comp);
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));
880 gnl_composition_dispose (GObject * object)
882 GnlComposition *comp = GNL_COMPOSITION (object);
883 GnlCompositionPrivate *priv = comp->priv;
885 if (priv->dispose_has_run)
888 priv->dispose_has_run = TRUE;
891 g_node_destroy (priv->current);
892 priv->current = NULL;
895 if (priv->expandables) {
898 iter = priv->expandables;
900 g_print ("ITER IS: %p\n", iter);
902 GList *next = iter->next;
904 _gnl_composition_remove_entry (comp, iter->data);
908 priv->expandables = NULL;
910 gnl_composition_reset_target_pad (comp);
912 G_OBJECT_CLASS (parent_class)->dispose (object);
916 gnl_composition_finalize (GObject * object)
919 GnlComposition *comp = GNL_COMPOSITION (object);
920 GnlCompositionPrivate *priv = comp->priv;
922 COMP_OBJECTS_LOCK (comp);
923 iter = priv->objects_start;
925 GList *next = iter->next;
927 _gnl_composition_remove_entry (comp, iter->data);
931 g_list_free (priv->objects_stop);
933 g_node_destroy (priv->current);
934 g_hash_table_destroy (priv->objects_hash);
935 COMP_OBJECTS_UNLOCK (comp);
937 gst_segment_free (priv->segment);
938 gst_segment_free (priv->outside_segment);
940 g_mutex_clear (&priv->objects_lock);
941 g_mutex_clear (&priv->flushing_lock);
942 g_mutex_clear (&priv->pending_io_lock);
945 g_rec_mutex_clear (&comp->task_rec_lock);
947 G_OBJECT_CLASS (parent_class)->finalize (object);
949 g_mutex_clear (&priv->mcontext_lock);
953 gnl_composition_set_property (GObject * object, guint prop_id,
954 const GValue * value, GParamSpec * pspec)
956 GnlComposition *comp = GNL_COMPOSITION (object);
959 case PROP_DEACTIVATED_ELEMENTS_STATE:
960 comp->priv->deactivated_elements_state = g_value_get_enum (value);
963 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
969 gnl_composition_get_property (GObject * object, guint prop_id,
970 GValue * value, GParamSpec * pspec)
972 GnlComposition *comp = GNL_COMPOSITION (object);
975 case PROP_DEACTIVATED_ELEMENTS_STATE:
976 g_value_set_enum (value, comp->priv->deactivated_elements_state);
979 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
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.
989 signal_duration_change (GnlComposition * comp)
991 gst_element_post_message (GST_ELEMENT_CAST (comp),
992 gst_message_new_duration_changed (GST_OBJECT_CAST (comp)));
996 unblock_child_pads (GValue * item, GValue * ret G_GNUC_UNUSED,
997 GnlComposition * comp)
1000 GstElement *child = g_value_get_object (item);
1001 GnlCompositionEntry *entry = COMP_ENTRY (comp, child);
1003 GST_DEBUG_OBJECT (child, "unblocking pads");
1005 pad = GNL_OBJECT_SRC (child);
1006 if (entry->probeid) {
1007 gst_pad_remove_probe (pad, entry->probeid);
1014 unblock_children (GnlComposition * comp)
1016 GstIterator *children;
1018 children = gst_bin_iterate_elements (GST_BIN (comp->priv->current_bin));
1021 if (G_UNLIKELY (gst_iterator_fold (children,
1022 (GstIteratorFoldFunction) unblock_child_pads, NULL,
1023 comp) == GST_ITERATOR_RESYNC)) {
1024 gst_iterator_resync (children);
1027 gst_iterator_free (children);
1032 reset_child (GValue * item, GValue * ret G_GNUC_UNUSED, gpointer user_data)
1034 GnlCompositionEntry *entry;
1035 GstElement *child = g_value_get_object (item);
1036 GnlComposition *comp = GNL_COMPOSITION (user_data);
1038 GstPad *srcpad, *peerpad;
1040 GST_DEBUG_OBJECT (child, "unlocking state");
1041 gst_element_set_locked_state (child, FALSE);
1043 entry = COMP_ENTRY (comp, child);
1044 object = entry->object;
1045 srcpad = object->srcpad;
1046 peerpad = gst_pad_get_peer (srcpad);
1048 gst_pad_unlink (srcpad, peerpad);
1049 gst_object_unref (peerpad);
1056 lock_child_state (GValue * item, GValue * ret G_GNUC_UNUSED,
1057 gpointer udata G_GNUC_UNUSED)
1059 GstElement *child = g_value_get_object (item);
1061 GST_DEBUG_OBJECT (child, "locking state");
1062 gst_element_set_locked_state (child, TRUE);
1068 reset_children (GnlComposition * comp)
1070 GstIterator *children;
1072 children = gst_bin_iterate_elements (GST_BIN (comp->priv->current_bin));
1075 if (G_UNLIKELY (gst_iterator_fold (children,
1076 (GstIteratorFoldFunction) reset_child, NULL,
1077 comp) == GST_ITERATOR_RESYNC)) {
1078 gst_iterator_resync (children);
1081 gst_iterator_free (children);
1085 _remove_child (GValue * item, GValue * ret G_GNUC_UNUSED, GstBin * bin)
1087 GstElement *child = g_value_get_object (item);
1089 if (GNL_IS_OPERATION (child))
1090 gnl_operation_hard_cleanup (GNL_OPERATION (child));
1093 gst_bin_remove (bin, child);
1099 _empty_bin (GstBin * bin)
1101 GstIterator *children;
1103 children = gst_bin_iterate_elements (bin);
1105 while (G_UNLIKELY (gst_iterator_fold (children,
1106 (GstIteratorFoldFunction) _remove_child, NULL,
1107 bin) == GST_ITERATOR_RESYNC)) {
1108 gst_iterator_resync (children);
1111 gst_iterator_free (children);
1115 gnl_composition_reset (GnlComposition * comp)
1117 GnlCompositionPrivate *priv = comp->priv;
1119 GST_DEBUG_OBJECT (comp, "resetting");
1121 priv->segment_start = GST_CLOCK_TIME_NONE;
1122 priv->segment_stop = GST_CLOCK_TIME_NONE;
1123 priv->next_base_time = 0;
1125 gst_segment_init (priv->segment, GST_FORMAT_TIME);
1126 gst_segment_init (priv->outside_segment, GST_FORMAT_TIME);
1129 g_node_destroy (priv->current);
1130 priv->current = NULL;
1132 priv->stackvalid = FALSE;
1134 gnl_composition_reset_target_pad (comp);
1136 reset_children (comp);
1138 COMP_FLUSHING_LOCK (comp);
1140 priv->flushing = FALSE;
1142 COMP_FLUSHING_UNLOCK (comp);
1144 priv->reset_time = FALSE;
1145 priv->initialized = FALSE;
1146 priv->send_stream_start = TRUE;
1147 priv->real_eos_seqnum = 0;
1149 _empty_bin (GST_BIN_CAST (priv->current_bin));
1151 GST_DEBUG_OBJECT (comp, "Composition now resetted");
1154 static GstPadProbeReturn
1155 ghost_event_probe_handler (GstPad * ghostpad G_GNUC_UNUSED,
1156 GstPadProbeInfo * info, GnlComposition * comp)
1158 GstPadProbeReturn retval = GST_PAD_PROBE_OK;
1159 GnlCompositionPrivate *priv = comp->priv;
1160 GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info);
1162 GST_DEBUG_OBJECT (comp, "event: %s", GST_EVENT_TYPE_NAME (event));
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",
1169 GST_PAD_PROBE_INFO_DATA (info) =
1170 gst_event_new_flush_stop (priv->reset_time);
1171 gst_event_unref (event);
1173 case GST_EVENT_STREAM_START:
1174 if (g_atomic_int_compare_and_exchange (&priv->send_stream_start, TRUE,
1176 /* FIXME: Do we want to create a new stream ID here? */
1177 GST_DEBUG_OBJECT (comp, "forward stream-start %p", event);
1179 GST_DEBUG_OBJECT (comp, "dropping stream-start %p", event);
1180 retval = GST_PAD_PROBE_DROP;
1183 case GST_EVENT_SEGMENT:
1185 guint64 rstart, rstop;
1186 const GstSegment *segment;
1189 /* next_base_time */
1191 COMP_FLUSHING_LOCK (comp);
1193 priv->flushing = FALSE;
1194 COMP_FLUSHING_UNLOCK (comp);
1196 gst_event_parse_segment (event, &segment);
1197 gst_segment_copy_into (segment, ©);
1200 gst_segment_to_running_time (segment, GST_FORMAT_TIME,
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;
1211 event2 = gst_event_new_segment (©);
1212 GST_EVENT_SEQNUM (event2) = GST_EVENT_SEQNUM (event);
1214 if (GNL_OBJECT (comp)->seqnum == 0)
1215 GNL_OBJECT (comp)->seqnum = GST_EVENT_SEQNUM (event);
1217 GST_PAD_PROBE_INFO_DATA (info) = event2;
1218 gst_event_unref (event);
1223 gint seqnum = gst_event_get_seqnum (event);
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;
1232 COMP_FLUSHING_UNLOCK (comp);
1234 GST_ERROR_OBJECT (comp, "Got EOS, last EOS seqnum id : %i current "
1235 "seq num is: %i", comp->priv->real_eos_seqnum, seqnum);
1237 if (priv->commited_probeid && comp->priv->awaited_segment_seqnum == 0) {
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);
1247 if (g_atomic_int_compare_and_exchange (&comp->priv->real_eos_seqnum,
1250 GST_INFO_OBJECT (comp, "Got EOS for real, fowarding it");
1251 GST_ERROR_OBJECT (comp, "GOGOGO EOS -- Seq ID is %i", seqnum);
1253 return GST_PAD_PROBE_OK;
1256 _add_update_gsource (comp);
1257 retval = GST_PAD_PROBE_DROP;
1269 /* Warning : Don't take the objects lock in this method */
1271 gnl_composition_handle_message (GstBin * bin, GstMessage * message)
1273 GnlComposition *comp = (GnlComposition *) bin;
1274 gboolean dropit = FALSE;
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)) :
1281 switch (GST_MESSAGE_TYPE (message)) {
1282 case GST_MESSAGE_ERROR:
1283 case GST_MESSAGE_WARNING:
1286 * There is a massive issue with reverse negotiation and dynamic pipelines.
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.
1294 * In order to avoid those issues, we just ignore error messages from elements
1295 * which aren't in the currently configured stack
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 !");
1309 gst_message_unref (message);
1311 GST_BIN_CLASS (parent_class)->handle_message (bin, message);
1315 priority_comp (GnlObject * a, GnlObject * b)
1317 if (a->priority < b->priority)
1320 if (a->priority > b->priority)
1326 static inline gboolean
1327 have_to_update_pipeline (GnlComposition * comp)
1329 GnlCompositionPrivate *priv = comp->priv;
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));
1338 if (priv->segment->start < priv->segment_start)
1341 if (priv->segment->start >= priv->segment_stop)
1348 gnl_composition_commit_func (GnlObject * object, gboolean recurse)
1350 GST_ERROR ("Adding commit gsource");
1351 _add_commit_gsource (GNL_COMPOSITION (object));
1356 * get_new_seek_event:
1358 * Returns a seek event for the currently configured segment
1359 * and start/stop values
1361 * The GstSegment and segment_start|stop must have been configured
1362 * before calling this function.
1365 get_new_seek_event (GnlComposition * comp, gboolean initial,
1366 gboolean updatestoponly)
1368 GstSeekFlags flags = GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH;
1370 GstSeekType starttype = GST_SEEK_TYPE_SET;
1371 GnlCompositionPrivate *priv = comp->priv;
1373 GST_DEBUG_OBJECT (comp, "initial:%d", initial);
1374 /* remove the seek flag */
1376 flags |= (GstSeekFlags) priv->segment->flags;
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));
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));
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;
1393 if (updatestoponly) {
1394 starttype = GST_SEEK_TYPE_NONE;
1395 start = GST_CLOCK_TIME_NONE;
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);
1403 return gst_event_new_seek (priv->segment->rate,
1404 priv->segment->format, flags, starttype, start, GST_SEEK_TYPE_SET, stop);
1407 /* OBJECTS LOCK must be taken when calling this ! */
1409 get_current_position (GnlComposition * comp)
1413 GnlCompositionPrivate *priv = comp->priv;
1415 gint64 value = GST_CLOCK_TIME_NONE;
1417 GstPad *peer = gst_pad_get_peer (GNL_OBJECT (comp)->srcpad);
1419 /* Try querying position downstream */
1422 res = gst_pad_query_position (peer, GST_FORMAT_TIME, &value);
1423 gst_object_unref (peer);
1426 GST_LOG_OBJECT (comp,
1427 "Successfully got downstream position %" GST_TIME_FORMAT,
1428 GST_TIME_ARGS ((guint64) value));
1433 GST_DEBUG_OBJECT (comp, "Downstream position query failed");
1435 /* resetting format/value */
1436 value = GST_CLOCK_TIME_NONE;
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");
1444 obj = (GnlObject *) priv->current->data;
1446 pad = GNL_OBJECT_SRC (obj);
1447 res = gst_pad_query_position (pad, GST_FORMAT_TIME, &value);
1449 if (G_UNLIKELY (res == FALSE)) {
1450 GST_WARNING_OBJECT (comp, "query position failed");
1451 value = GST_CLOCK_TIME_NONE;
1453 GST_LOG_OBJECT (comp, "Query returned %" GST_TIME_FORMAT,
1454 GST_TIME_ARGS ((guint64) value));
1459 if (GST_CLOCK_TIME_IS_VALID (comp->priv->segment_start)) {
1460 GST_INFO_OBJECT (comp, "Current position is unknown, " "setting it to 0");
1465 return (guint64) value;
1469 update_base_time (GNode * node, GstClockTime * timestamp)
1471 if (GNL_IS_OPERATION (node->data))
1472 gnl_operation_update_base_time (GNL_OPERATION (node->data), *timestamp);
1477 /* WITH OBJECTS LOCK TAKEN */
1479 update_operations_base_time (GnlComposition * comp, gboolean reverse)
1481 GstClockTime timestamp;
1484 timestamp = comp->priv->segment->stop;
1486 timestamp = comp->priv->segment->start;
1488 g_node_traverse (comp->priv->current, G_IN_ORDER, G_TRAVERSE_ALL, -1,
1489 (GNodeTraverseFunc) update_base_time, ×tamp);
1494 _seek_current_stack (GnlComposition * comp, GstEvent * event)
1497 GnlCompositionPrivate *priv = comp->priv;
1498 GstPad *peer = gst_pad_get_peer (GNL_OBJECT_SRC (comp));
1500 GST_INFO_OBJECT (comp, "Seeking itself %" GST_PTR_FORMAT, event);
1502 priv->seeking_itself = TRUE;
1503 res = gst_pad_push_event (peer, event);
1504 priv->seeking_itself = FALSE;
1505 gst_object_unref (peer);
1507 GST_DEBUG_OBJECT (comp, "Done seeking");
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
1518 initial : FIXME : ???? Always seems to be TRUE
1519 update : TRUE from EOS, FALSE from seek
1523 seek_handling (GnlComposition * comp, gboolean initial, gboolean update)
1525 GST_DEBUG_OBJECT (comp, "initial:%d, update:%d", initial, update);
1527 COMP_FLUSHING_LOCK (comp);
1528 GST_DEBUG_OBJECT (comp, "Setting flushing to TRUE");
1529 comp->priv->flushing = TRUE;
1530 COMP_FLUSHING_UNLOCK (comp);
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);
1537 update_pipeline (comp, comp->priv->segment->stop, initial, !update);
1539 GstEvent *toplevel_seek = get_new_seek_event (comp, FALSE, FALSE);
1541 _set_real_eos_seqnum_from_seek (comp, toplevel_seek);
1543 _seek_current_stack (comp, toplevel_seek);
1544 update_operations_base_time (comp, !(comp->priv->segment->rate >= 0.0));
1546 COMP_OBJECTS_UNLOCK (comp);
1552 gnl_composition_event_handler (GstPad * ghostpad, GstObject * parent,
1555 GnlComposition *comp = (GnlComposition *) parent;
1556 GnlCompositionPrivate *priv = comp->priv;
1557 gboolean res = TRUE;
1559 GST_DEBUG_OBJECT (comp, "event type:%s", GST_EVENT_TYPE_NAME (event));
1561 switch (GST_EVENT_TYPE (event)) {
1562 case GST_EVENT_SEEK:
1564 if (!priv->seeking_itself) {
1565 _add_seek_gsource (comp, event);
1567 GST_FIXME_OBJECT (comp, "HANDLE seeking errors!");
1572 GNL_OBJECT (comp)->wanted_seqnum = gst_event_get_seqnum (event);
1579 GstClockTimeDiff diff;
1580 GstClockTime timestamp;
1582 gst_event_parse_qos (event, &qostype, &prop, &diff, ×tamp);
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));
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
1599 * The problem is that the QoS running timestamps we receive from
1600 * downstream will not have taken into account those flush.
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.
1605 * The generic algorithm for the incoming running timestamp translation
1607 * (original_seek_time : original seek position received from usptream)
1608 * (current_segment_start : Start position of the currently configured
1611 * difference = original_seek_time - current_segment_start
1612 * new_qos_position = upstream_qos_position - difference
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
1620 if (GST_CLOCK_TIME_IS_VALID (priv->outside_segment->start)) {
1621 GstClockTimeDiff curdiff;
1623 /* We'll either create a new event or discard it */
1624 gst_event_unref (event);
1626 if (priv->segment->rate < 0.0)
1627 curdiff = priv->outside_segment->stop - priv->segment_stop;
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 */
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);
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);
1666 gnl_composition_reset_target_pad (GnlComposition * comp)
1668 GnlCompositionPrivate *priv = comp->priv;
1670 GST_DEBUG_OBJECT (comp, "Removing ghostpad");
1672 if (priv->ghosteventprobe) {
1675 target = gst_ghost_pad_get_target ((GstGhostPad *) GNL_OBJECT_SRC (comp));
1677 gst_pad_remove_probe (target, priv->ghosteventprobe);
1678 priv->ghosteventprobe = 0;
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;
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
1693 gnl_composition_ghost_pad_set_target (GnlComposition * comp, GstPad * target,
1694 GnlCompositionEntry * entry)
1697 GnlCompositionPrivate *priv = comp->priv;
1700 GST_DEBUG_OBJECT (comp, "target:%s:%s", GST_DEBUG_PAD_NAME (target));
1702 GST_DEBUG_OBJECT (comp, "Removing target");
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);
1714 /* Actually set the target */
1715 gnl_object_ghost_pad_set_target ((GnlObject *) comp,
1716 GNL_OBJECT (comp)->srcpad, target);
1718 /* Set top-level entry (will be NULL if unsetting) */
1719 priv->toplevelentry = entry;
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);
1729 GST_DEBUG_OBJECT (comp, "END");
1733 refine_start_stop_in_region_above_priority (GnlComposition * composition,
1734 GstClockTime timestamp, GstClockTime start,
1736 GstClockTime * rstart, GstClockTime * rstop, guint32 priority)
1740 GstClockTime nstart = start, nstop = stop;
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);
1747 for (tmp = composition->priv->objects_start; tmp; tmp = tmp->next) {
1748 object = (GnlObject *) tmp->data;
1750 GST_LOG_OBJECT (object, "START %" GST_TIME_FORMAT "--%" GST_TIME_FORMAT,
1751 GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop));
1753 if ((object->priority >= priority) || (!object->active))
1756 if (object->start <= timestamp)
1759 if (object->start >= nstop)
1762 nstop = object->start;
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));
1772 for (tmp = composition->priv->objects_stop; tmp; tmp = tmp->next) {
1773 object = (GnlObject *) tmp->data;
1775 GST_LOG_OBJECT (object, "STOP %" GST_TIME_FORMAT "--%" GST_TIME_FORMAT,
1776 GST_TIME_ARGS (object->start), GST_TIME_ARGS (object->stop));
1778 if ((object->priority >= priority) || (!object->active))
1781 if (object->stop >= timestamp)
1784 if (object->stop <= nstart)
1787 nstart = object->stop;
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));
1806 * Converts a sorted list to a tree
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.
1814 convert_list_to_tree (GList ** stack, GstClockTime * start,
1815 GstClockTime * stop, guint32 * highprio)
1823 if (!stack || !*stack)
1826 object = (GnlObject *) (*stack)->data;
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);
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;
1838 *stop = object->stop;
1841 if (GST_CLOCK_TIME_IS_VALID (*start)) {
1842 if (GST_CLOCK_TIME_IS_VALID (object->start) && (*start < object->start))
1843 *start = object->start;
1845 *start = object->start;
1848 if (GNL_OBJECT_IS_SOURCE (object)) {
1849 *stack = g_list_next (*stack);
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;
1856 ret = g_node_new (object);
1861 GnlOperation *oper = (GnlOperation *) object;
1863 GST_LOG_OBJECT (oper, "operation, num_sinks:%d", oper->num_sinks);
1865 ret = g_node_new (object);
1866 limit = (oper->dynamicsinks == FALSE);
1867 nbsinks = oper->num_sinks;
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));
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);
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
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.
1901 * WITH OBJECTS LOCK TAKEN
1904 get_stack_list (GnlComposition * comp, GstClockTime timestamp,
1905 guint32 priority, gboolean activeonly, GstClockTime * start,
1906 GstClockTime * stop, guint * highprio)
1909 GList *stack = 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);
1917 GST_DEBUG_OBJECT (comp,
1918 "timestamp:%" GST_TIME_FORMAT ", priority:%u, activeonly:%d",
1919 GST_TIME_ARGS (timestamp), priority, activeonly);
1921 GST_LOG ("objects_start:%p objects_stop:%p", comp->priv->objects_start,
1922 comp->priv->objects_stop);
1925 for (tmp = comp->priv->objects_stop; tmp; tmp = g_list_next (tmp)) {
1926 GnlObject *object = (GnlObject *) tmp->data;
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);
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);
1946 GST_LOG_OBJECT (comp, "too far, stopping iteration");
1947 first_out_of_stack = object->stop;
1952 for (tmp = comp->priv->objects_start; tmp; tmp = g_list_next (tmp)) {
1953 GnlObject *object = (GnlObject *) tmp->data;
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),
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);
1973 GST_LOG_OBJECT (comp, "too far, stopping iteration");
1974 first_out_of_stack = object->start;
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);
1991 /* convert that list to a 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;
2001 GST_DEBUG ("nstart:%" GST_TIME_FORMAT ", nstop:%" GST_TIME_FORMAT,
2002 GST_TIME_ARGS (nstart), GST_TIME_ARGS (nstop));
2009 *highprio = highest;
2011 g_list_free (stack);
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
2023 * Returns: The new current stack for the given #GnlComposition and @timestamp.
2025 * WITH OBJECTS LOCK TAKEN
2028 get_clean_toplevel_stack (GnlComposition * comp, GstClockTime * timestamp,
2029 GstClockTime * start_time, GstClockTime * stop_time)
2031 GNode *stack = NULL;
2032 GstClockTime start = G_MAXUINT64;
2033 GstClockTime stop = G_MAXUINT64;
2035 gboolean reverse = (comp->priv->segment->rate < 0.0);
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));
2042 stack = get_stack_list (comp, *timestamp, 0, TRUE, &start, &stop, &highprio);
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"));
2057 GST_DEBUG ("start:%" GST_TIME_FORMAT ", stop:%" GST_TIME_FORMAT,
2058 GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
2061 guint32 top_priority = GNL_OBJECT_PRIORITY (stack->data);
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);
2077 *start_time = start;
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));
2093 set_child_caps (GValue * item, GValue * ret G_GNUC_UNUSED, GnlObject * comp)
2095 GstElement *child = g_value_get_object (item);
2097 gnl_object_set_caps ((GnlObject *) child, comp->caps);
2102 /* Must be called with OBJECTS_LOCK taken */
2104 _set_current_bin_to_ready (GnlComposition * comp)
2107 GnlCompositionPrivate *priv = comp->priv;
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)));
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));
2118 gst_object_unref (ptarget);
2122 gst_element_set_locked_state (priv->current_bin, TRUE);
2123 gst_element_set_state (priv->current_bin, GST_STATE_READY);
2126 /* Must be called with OBJECTS_LOCK taken */
2128 _process_pending_entries (GnlComposition * comp)
2131 GHashTableIter iter;
2132 gboolean deactivated_stack = FALSE;
2134 GnlCompositionPrivate *priv = comp->priv;
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);
2142 if (GST_OBJECT_PARENT (object) == GST_OBJECT_CAST (priv->current_bin) &&
2143 deactivated_stack == FALSE) {
2144 deactivated_stack = TRUE;
2146 _deactivate_stack (comp);
2149 _gnl_composition_remove_entry (comp, object);
2151 _gnl_composition_add_entry (comp, object);
2155 g_hash_table_remove_all (priv->pending_io);
2159 _emit_commited_signal_func (GnlComposition * comp)
2161 GST_INFO_OBJECT (comp, "Emiting COMMITED now that the stack " "is ready");
2163 g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE);
2165 return G_SOURCE_REMOVE;
2168 static GstPadProbeReturn
2169 _add_emit_commited_and_restart_task (GnlComposition * comp)
2171 GST_ERROR_OBJECT (comp, "Setup commit and restart task!");
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);
2179 comp->priv->awaited_segment_seqnum = 0;
2180 comp->priv->commited_probeid = 0;
2182 gst_task_start (comp->task);
2184 return GST_PAD_PROBE_REMOVE;
2187 static GstPadProbeReturn
2188 _commit_done_cb (GstPad * pad, GstPadProbeInfo * info, GnlComposition * comp)
2190 if (comp->priv->awaited_segment_seqnum) {
2191 if (GST_IS_EVENT (info->data)) {
2192 gint seqnum = gst_event_get_seqnum (info->data);
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);
2199 if (seqnum == comp->priv->awaited_segment_seqnum) {
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");
2205 return _add_emit_commited_and_restart_task (comp);
2207 } else if (GST_EVENT_TYPE (info->data) == GST_EVENT_SEGMENT) {
2209 GST_INFO_OBJECT (comp, "Got segment event with right seqnum"
2210 " now waiting for a buffer to restart playing with our "
2213 comp->priv->awaited_segment_seqnum = 0;
2218 return GST_PAD_PROBE_OK;
2219 } else if (GST_IS_BUFFER (info->data)) {
2221 GST_INFO_OBJECT (comp, "Got %" GST_PTR_FORMAT " concidering commit "
2222 "as done", info->data);
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);
2229 GST_INFO_OBJECT (comp, "Got %" GST_PTR_FORMAT " still waiting for a buffer",
2232 return GST_PAD_PROBE_OK;
2235 static inline gboolean
2236 _commit_values (GnlComposition * comp)
2239 gboolean commited = FALSE;
2240 GnlCompositionPrivate *priv = comp->priv;
2242 for (tmp = priv->objects_start; tmp; tmp = tmp->next) {
2243 if (gnl_object_commit (tmp->data, TRUE))
2247 GST_DEBUG_OBJECT (comp, "Linking up commit vmethod");
2248 commited |= GNL_OBJECT_CLASS (parent_class)->commit (GNL_OBJECT (comp), TRUE);
2254 _commit_func (GnlComposition * comp)
2256 GstClockTime curpos;
2257 GnlCompositionPrivate *priv = comp->priv;
2259 GST_INFO_OBJECT (comp, "Commiting state");
2261 COMP_OBJECTS_LOCK (comp);
2263 /* Get current so that it represent the duration it was
2264 * before commiting children */
2265 curpos = get_current_position (comp);
2267 _process_pending_entries (comp);
2269 if (_commit_values (comp) == FALSE) {
2270 COMP_OBJECTS_UNLOCK (comp);
2271 GST_INFO_OBJECT (comp, "Nothing to commit, leaving");
2273 g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, FALSE);
2275 return G_SOURCE_REMOVE;
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);
2284 if (priv->initialized == FALSE) {
2285 GST_DEBUG_OBJECT (comp, "Not initialized yet, just updating values");
2287 update_start_stop_duration (comp);
2288 COMP_OBJECTS_UNLOCK (comp);
2290 g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE);
2293 /* And update the pipeline at current position if needed */
2295 update_start_stop_duration (comp);
2296 update_pipeline (comp, curpos, TRUE, TRUE);
2298 if (!priv->current) {
2299 COMP_OBJECTS_UNLOCK (comp);
2301 GST_INFO_OBJECT (comp, "No new stack set, we can go and keep acting on"
2304 g_signal_emit (comp, _signals[COMMITED_SIGNAL], 0, TRUE);
2306 COMP_OBJECTS_UNLOCK (comp);
2310 return G_SOURCE_REMOVE;
2314 update_pipeline_func (GnlComposition * comp)
2316 GnlCompositionPrivate *priv;
2319 /* Set up a non-initial seek on segment_stop */
2321 reverse = (priv->segment->rate < 0.0);
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;
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;
2334 seek_handling (comp, TRUE, TRUE);
2336 /* Post segment done if last seek was a segment seek */
2337 if (!priv->current && (priv->segment->flags & GST_SEEK_FLAG_SEGMENT)) {
2340 if (GST_CLOCK_TIME_IS_VALID (priv->segment->stop))
2341 epos = (MIN (priv->segment->stop, GNL_OBJECT_STOP (comp)));
2343 epos = GNL_OBJECT_STOP (comp);
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));
2355 return G_SOURCE_REMOVE;
2359 _set_all_children_state (GnlComposition * comp, GstState state)
2363 GST_DEBUG_OBJECT (comp, "Setting all children state to %s",
2364 gst_element_state_get_name (state));
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);
2371 for (tmp = comp->priv->expandables; tmp; tmp = tmp->next)
2372 gst_element_set_state (tmp->data, state);
2374 COMP_OBJECTS_UNLOCK (comp);
2377 static GstStateChangeReturn
2378 gnl_composition_change_state (GstElement * element, GstStateChange transition)
2380 GstIterator *children;
2381 GnlComposition *comp = (GnlComposition *) element;
2382 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
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)));
2388 switch (transition) {
2389 case GST_STATE_CHANGE_READY_TO_PAUSED:
2390 gnl_composition_reset (comp);
2392 /* state-lock all elements */
2393 GST_DEBUG_OBJECT (comp,
2394 "Setting all children to READY and locking their state");
2396 children = gst_bin_iterate_elements (GST_BIN (comp->priv->current_bin));
2398 while (G_UNLIKELY (gst_iterator_fold (children,
2399 (GstIteratorFoldFunction) lock_child_state, NULL,
2400 NULL) == GST_ITERATOR_RESYNC)) {
2401 gst_iterator_resync (children);
2403 gst_iterator_free (children);
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));
2409 while (G_UNLIKELY (gst_iterator_fold (children,
2410 (GstIteratorFoldFunction) set_child_caps, NULL,
2411 comp) == GST_ITERATOR_RESYNC)) {
2412 gst_iterator_resync (children);
2414 gst_iterator_free (children);
2417 _add_initialize_stack_gsource (comp);
2419 case GST_STATE_CHANGE_PAUSED_TO_READY:
2420 _set_all_children_state (comp, GST_STATE_READY);
2421 gnl_composition_reset (comp);
2423 case GST_STATE_CHANGE_READY_TO_NULL:
2424 _set_all_children_state (comp, GST_STATE_NULL);
2425 gnl_composition_reset (comp);
2431 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2433 if (ret == GST_STATE_CHANGE_FAILURE)
2436 switch (transition) {
2437 case GST_STATE_CHANGE_PAUSED_TO_READY:
2438 case GST_STATE_CHANGE_READY_TO_NULL:
2439 unblock_children (comp);
2449 objects_start_compare (GnlObject * a, GnlObject * b)
2451 if (a->start == b->start) {
2452 if (a->priority < b->priority)
2454 if (a->priority > b->priority)
2458 if (a->start < b->start)
2460 if (a->start > b->start)
2466 objects_stop_compare (GnlObject * a, GnlObject * b)
2468 if (a->stop == b->stop) {
2469 if (a->priority < b->priority)
2471 if (a->priority > b->priority)
2475 if (b->stop < a->stop)
2477 if (b->stop > a->stop)
2482 /* WITH OBJECTS LOCK TAKEN */
2484 update_start_stop_duration (GnlComposition * comp)
2487 GnlObject *cobj = (GnlObject *) comp;
2488 GnlCompositionPrivate *priv = comp->priv;
2490 if (!priv->objects_start) {
2491 GST_LOG ("no objects, resetting everything to 0");
2494 cobj->start = cobj->pending_start = 0;
2495 g_object_notify_by_pspec (G_OBJECT (cobj),
2496 gnlobject_properties[GNLOBJECT_PROP_START]);
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);
2508 g_object_notify_by_pspec (G_OBJECT (cobj),
2509 gnlobject_properties[GNLOBJECT_PROP_STOP]);
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");
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]);
2528 /* Else it's the first object's start value */
2529 obj = (GnlObject *) priv->objects_start->data;
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]);
2541 obj = (GnlObject *) priv->objects_stop->data;
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));
2547 if (priv->expandables) {
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);
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]);
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);
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));
2578 static inline gboolean
2579 _parent_or_priority_changed (GnlObject * obj, GNode * oldnode,
2580 GnlObject * newparent, GNode * node)
2582 GnlObject *oldparent = NULL;
2586 G_NODE_IS_ROOT (oldnode) ? NULL : (GnlObject *) oldnode->parent->data;
2588 if (oldparent != newparent)
2591 if (oldparent == NULL || newparent == NULL)
2594 return (g_node_child_index (node, obj) != g_node_child_index (oldnode, obj));
2598 _link_to_parent (GnlComposition * comp, GnlObject * newobj,
2599 GnlObject * newparent)
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)));
2608 sinkpad = get_unlinked_sink_ghost_pad ((GnlOperation *) newparent);
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));
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));
2621 gst_object_unref (sinkpad);
2626 _relink_children_recursively (GnlComposition * comp,
2627 GnlObject * newobj, GNode * node, GstEvent * toplevel_seek)
2630 guint nbchildren = g_node_n_children (node);
2631 GnlOperation *oper = (GnlOperation *) newobj;
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);
2640 for (child = node->children; child; child = child->next)
2641 _relink_single_node (comp, child, toplevel_seek);
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);
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 */
2653 * recursive depth-first relink stack function on new stack
2655 * _ relink nodes with changed parent/order
2656 * _ links new nodes with parents
2657 * _ unblocks available source pads (except for toplevel)
2659 * WITH OBJECTS LOCK TAKEN
2662 _relink_single_node (GnlComposition * comp, GNode * node,
2663 GstEvent * toplevel_seek)
2666 GnlObject *newparent;
2667 GstPad *srcpad = NULL, *sinkpad = NULL;
2668 GstEvent *translated_seek;
2670 if (G_UNLIKELY (!node))
2673 newparent = G_NODE_IS_ROOT (node) ? NULL : (GnlObject *) node->parent->data;
2674 newobj = (GnlObject *) node->data;
2676 GST_DEBUG_OBJECT (comp, "newobj:%s",
2677 GST_ELEMENT_NAME ((GstElement *) newobj));
2679 srcpad = GNL_OBJECT_SRC (newobj);
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));
2684 translated_seek = gnl_object_translate_incoming_seek (newobj, toplevel_seek);
2686 gst_element_send_event (GST_ELEMENT (newobj), translated_seek);
2688 /* link to parent if needed. */
2690 _link_to_parent (comp, newobj, newparent);
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);
2699 /* Handle children */
2700 if (GNL_IS_OPERATION (newobj))
2701 _relink_children_recursively (comp, newobj, node, toplevel_seek);
2703 GST_LOG_OBJECT (comp, "done with object %s",
2704 GST_ELEMENT_NAME (GST_ELEMENT (newobj)));
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.
2715 * Compares the given stack to the current one and relinks it if needed.
2717 * WITH OBJECTS LOCK TAKEN
2719 * Returns: The #GList of #GnlObject no longer used
2723 _deactivate_stack (GnlComposition * comp)
2727 _set_current_bin_to_ready (comp);
2729 ptarget = gst_ghost_pad_get_target (GST_GHOST_PAD (GNL_OBJECT_SRC (comp)));
2730 _empty_bin (GST_BIN_CAST (comp->priv->current_bin));
2732 if (comp->priv->ghosteventprobe) {
2733 GST_INFO_OBJECT (comp, "Removing old ghost pad probe");
2735 gst_pad_remove_probe (ptarget, comp->priv->ghosteventprobe);
2736 comp->priv->ghosteventprobe = 0;
2740 gst_object_unref (ptarget);
2742 /* priv->current = NULL;
2747 _relink_new_stack (GnlComposition * comp, GNode * stack,
2748 GstEvent * toplevel_seek)
2750 GnlCompositionPrivate *priv = comp->priv;
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);
2755 _relink_single_node (comp, stack, toplevel_seek);
2757 gst_event_unref (toplevel_seek);
2759 gst_element_set_locked_state (priv->current_bin, FALSE);
2760 gst_element_sync_state_with_parent (priv->current_bin);
2764 * unlock_activate_stack (GnlComposition * comp, GNode * node, GstState state)
2768 * GST_LOG_OBJECT (comp, "object:%s",
2769 * GST_ELEMENT_NAME ((GstElement *) (node->data)));
2771 * gst_element_set_locked_state ((GstElement *) (node->data), FALSE);
2772 * gst_element_set_state (GST_ELEMENT (node->data), state);
2774 * for (child = node->children; child; child = child->next)
2775 * unlock_activate_stack (comp, child, state);
2780 are_same_stacks (GNode * stack1, GNode * stack2)
2782 gboolean res = FALSE;
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))
2789 if (stack1 && stack2) {
2790 GNode *child1, *child2;
2792 /* if they don't contain the same source, not equal */
2793 if (!(stack1->data == stack2->data))
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)))
2800 child1 = stack1->children;
2801 child2 = stack2->children;
2802 while (child1 && child2) {
2803 if (!(are_same_stacks (child1, child2)))
2805 child1 = g_node_next_sibling (child1);
2806 child2 = g_node_next_sibling (child2);
2809 /* if there's a difference in child number, stacks are not equal */
2810 if (child1 || child2)
2814 /* if stack1 AND stack2 are NULL, then they're equal (both empty) */
2818 GST_LOG ("Stacks are equal : %d", res);
2823 static inline gboolean
2824 _activate_new_stack (GnlComposition * comp)
2827 GstElement *topelement;
2828 GnlCompositionEntry *topentry;
2830 GnlCompositionPrivate *priv = comp->priv;
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;
2839 GST_ERROR_OBJECT (comp, "Nothing else in the composition"
2840 ", update 'worked'");
2844 priv->stackvalid = TRUE;
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);
2852 GST_ERROR_OBJECT (comp,
2853 "We have a valid toplevel element pad %s:%s", GST_DEBUG_PAD_NAME (pad));
2855 gnl_composition_ghost_pad_set_target (comp, pad, topentry);
2857 GST_ERROR_OBJECT (comp, "New stack activated!");
2861 /* WITH OBJECTS LOCK TAKEN */
2863 _set_real_eos_seqnum_from_seek (GnlComposition * comp, GstEvent * event)
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);
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;
2877 if (should_check_objects) {
2878 for (tmp = priv->objects_stop; tmp; tmp = g_list_next (tmp)) {
2879 GnlObject *object = (GnlObject *) tmp->data;
2881 if (!GNL_IS_SOURCE (object))
2884 if ((!reverse && priv->segment_stop < object->stop) ||
2885 (reverse && priv->segment_start > object->start)) {
2887 g_atomic_int_set (&priv->real_eos_seqnum, 0);
2893 g_atomic_int_set (&priv->real_eos_seqnum, stack_seqnum);
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.
2906 * Updates the internal pipeline and properties. If @currenttime is
2907 * GST_CLOCK_TIME_NONE, it will not modify the current pipeline
2909 * Returns: FALSE if there was an error updating the pipeline.
2911 * WITH OBJECTS LOCK TAKEN
2914 update_pipeline (GnlComposition * comp, GstClockTime currenttime,
2915 gboolean initial, gboolean modify)
2919 GstEvent *toplevel_seek;
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;
2929 gboolean startchanged, stopchanged;
2931 GstState nextstate = (GST_STATE_NEXT (comp) == GST_STATE_VOID_PENDING) ?
2932 GST_STATE (comp) : GST_STATE_NEXT (comp);
2934 GST_ERROR_OBJECT (comp,
2935 "currenttime:%" GST_TIME_FORMAT
2936 " initial:%d , modify:%d", GST_TIME_ARGS (currenttime), initial, modify);
2938 if (!GST_CLOCK_TIME_IS_VALID (currenttime))
2941 if (state == GST_STATE_NULL && nextstate == GST_STATE_NULL) {
2942 GST_DEBUG_OBJECT (comp, "STATE_NULL: not updating pipeline");
2947 GST_DEBUG_OBJECT (comp,
2948 "now really updating the pipeline, current-state:%s",
2949 gst_element_state_get_name (state));
2951 /* Get new stack and compare it to current one */
2952 stack = get_clean_toplevel_stack (comp, ¤ttime, &new_start, &new_stop);
2953 samestack = are_same_stacks (priv->current, stack);
2955 /* invalidate the stack while modifying it */
2956 priv->stackvalid = FALSE;
2958 if (priv->segment->rate >= 0.0) {
2959 startchanged = priv->segment_start != currenttime;
2960 stopchanged = priv->segment_stop != new_stop;
2962 startchanged = priv->segment_start != new_start;
2963 stopchanged = priv->segment_stop != currenttime;
2966 /* set new segment_start/stop (the current zone over which the new stack
2968 if (priv->segment->rate >= 0.0) {
2969 priv->segment_start = currenttime;
2970 priv->segment_stop = new_stop;
2972 priv->segment_start = new_start;
2973 priv->segment_stop = currenttime;
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;
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);
2988 /* If stacks are different, unlink/relink objects */
2990 _deactivate_stack (comp);
2991 _relink_new_stack (comp, stack, toplevel_seek);
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;
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"
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);
3010 gst_task_pause (comp->task);
3013 /* Activate stack */
3015 return _activate_new_stack (comp);
3017 return _seek_current_stack (comp, toplevel_seek);
3021 gnl_composition_add_object (GstBin * bin, GstElement * element)
3023 GnlComposition *comp = (GnlComposition *) bin;
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);
3030 g_assert_not_reached ();
3036 _gnl_composition_add_entry (GnlComposition * comp, GnlObject * object)
3038 gboolean ret = TRUE;
3040 GnlCompositionEntry *entry;
3041 GnlCompositionPrivate *priv = comp->priv;
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)));
3048 g_object_ref_sink (object);
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");
3059 gnl_object_set_caps (object, GNL_OBJECT (comp)->caps);
3060 gnl_object_set_commit_needed (GNL_OBJECT (comp));
3063 GST_WARNING_OBJECT (comp, "couldn't add object");
3067 /* lock state of child ! */
3068 GST_LOG_OBJECT (comp, "Locking state of %s", GST_ELEMENT_NAME (object));
3070 /* wrap new element in a GnlCompositionEntry ... */
3071 entry = g_slice_new0 (GnlCompositionEntry);
3072 entry->object = (GnlObject *) object;
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);
3082 GST_INFO_OBJECT (object, "Used as expandable, commiting now");
3083 gnl_object_commit (GNL_OBJECT (object), FALSE);
3086 /* ...and add it to the hash table */
3087 g_hash_table_insert (priv->objects_hash, object, entry);
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);
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);
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);
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)));
3112 priv->objects_stop = g_list_insert_sorted
3113 (priv->objects_stop, object, (GCompareFunc) objects_stop_compare);
3115 /* Now the object is ready to be commited and then used */
3122 update_start_stop_duration (comp);
3128 gnl_composition_remove_object (GstBin * bin, GstElement * element)
3130 GnlComposition *comp = (GnlComposition *) bin;
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);
3137 g_assert_not_reached ();
3143 _gnl_composition_remove_entry (GnlComposition * comp, GnlObject * object)
3145 gboolean ret = FALSE;
3146 GnlCompositionEntry *entry;
3147 GnlCompositionPrivate *priv = comp->priv;
3149 GST_ERROR_OBJECT (comp, "object %s", GST_OBJECT_NAME (object));
3151 /* we only accept GnlObject */
3152 entry = COMP_ENTRY (comp, object);
3153 if (entry == NULL) {
3157 gst_element_set_locked_state (GST_ELEMENT (object), FALSE);
3158 gst_element_set_state (GST_ELEMENT (object), GST_STATE_NULL);
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);
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");
3171 if (priv->current && GNL_OBJECT (priv->current->data) == GNL_OBJECT (object))
3172 gnl_composition_reset_target_pad (comp);
3174 g_hash_table_remove (priv->objects_hash, object);
3176 GST_LOG_OBJECT (object, "Done removing from the composition, now updating");
3178 /* Make it possible to reuse the same object later */
3179 gnl_object_reset (GNL_OBJECT (object));
3180 gst_object_unref (object);