2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wtay@chello.be>
5 * gstscheduler.c: Default scheduling code for most cases
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., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
29 #include "cothreads_compat.h"
31 GST_DEBUG_CATEGORY_STATIC (debug_dataflow);
32 GST_DEBUG_CATEGORY_STATIC (debug_scheduler);
33 #define GST_CAT_DEFAULT debug_scheduler
35 typedef struct _GstSchedulerChain GstSchedulerChain;
37 #define GST_ELEMENT_THREADSTATE(elem) (GST_ELEMENT (elem)->sched_private)
38 #define GST_RPAD_BUFPEN(pad) (GST_REAL_PAD(pad)->sched_private)
40 #define GST_ELEMENT_COTHREAD_STOPPING GST_ELEMENT_SCHEDULER_PRIVATE1
41 #define GST_ELEMENT_IS_COTHREAD_STOPPING(element) GST_FLAG_IS_SET((element), GST_ELEMENT_COTHREAD_STOPPING)
43 typedef struct _GstBasicScheduler GstBasicScheduler;
44 typedef struct _GstBasicSchedulerClass GstBasicSchedulerClass;
46 #ifdef _COTHREADS_STANDARD
47 # define _SCHEDULER_NAME "standard"
49 # define _SCHEDULER_NAME "basic"
52 struct _GstSchedulerChain
54 GstBasicScheduler *sched;
63 gint cothreaded_elements;
67 #define GST_TYPE_BASIC_SCHEDULER \
68 (gst_basic_scheduler_get_type())
69 #define GST_BASIC_SCHEDULER(obj) \
70 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BASIC_SCHEDULER,GstBasicScheduler))
71 #define GST_BASIC_SCHEDULER_CLASS(klass) \
72 (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BASIC_SCHEDULER,GstBasicSchedulerClass))
73 #define GST_IS_BASIC_SCHEDULER(obj) \
74 (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BASIC_SCHEDULER))
75 #define GST_IS_BASIC_SCHEDULER_CLASS(obj) \
76 (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BASIC_SCHEDULER))
78 #define SCHED(element) GST_BASIC_SCHEDULER (GST_ELEMENT_SCHEDULER (element))
82 GST_BASIC_SCHEDULER_STATE_NONE,
83 GST_BASIC_SCHEDULER_STATE_STOPPED,
84 GST_BASIC_SCHEDULER_STATE_ERROR,
85 GST_BASIC_SCHEDULER_STATE_RUNNING
87 GstBasicSchedulerState;
91 /* something important has changed inside the scheduler */
92 GST_BASIC_SCHEDULER_CHANGE = GST_SCHEDULER_FLAG_LAST
94 GstBasicSchedulerFlags;
96 struct _GstBasicScheduler
106 GstBasicSchedulerState state;
108 cothread_context *context;
112 struct _GstBasicSchedulerClass
114 GstSchedulerClass parent_class;
117 static GType _gst_basic_scheduler_type = 0;
119 static void gst_basic_scheduler_class_init (GstBasicSchedulerClass * klass);
120 static void gst_basic_scheduler_init (GstBasicScheduler * scheduler);
122 static void gst_basic_scheduler_dispose (GObject * object);
124 static void gst_basic_scheduler_setup (GstScheduler * sched);
125 static void gst_basic_scheduler_reset (GstScheduler * sched);
126 static void gst_basic_scheduler_add_element (GstScheduler * sched,
127 GstElement * element);
128 static void gst_basic_scheduler_remove_element (GstScheduler * sched,
129 GstElement * element);
130 static GstElementStateReturn gst_basic_scheduler_state_transition (GstScheduler
131 * sched, GstElement * element, gint transition);
132 static gboolean gst_basic_scheduler_yield (GstScheduler * sched,
133 GstElement * element);
134 static gboolean gst_basic_scheduler_interrupt (GstScheduler * sched,
135 GstElement * element);
136 static void gst_basic_scheduler_error (GstScheduler * sched,
137 GstElement * element);
138 static void gst_basic_scheduler_pad_link (GstScheduler * sched, GstPad * srcpad,
140 static void gst_basic_scheduler_pad_unlink (GstScheduler * sched,
141 GstPad * srcpad, GstPad * sinkpad);
142 static GstData *gst_basic_scheduler_pad_select (GstScheduler * sched,
143 GstPad ** selected, GstPad ** padlist);
144 static GstSchedulerState gst_basic_scheduler_iterate (GstScheduler * sched);
146 static void gst_basic_scheduler_show (GstScheduler * sched);
148 static GstSchedulerClass *parent_class = NULL;
150 /* for threaded bins, these pre- and post-run functions lock and unlock the
151 * elements. we have to avoid deadlocks, so we make these convenience macros
152 * that will avoid using do_cothread_switch from within the scheduler. */
154 #define do_element_switch(element) G_STMT_START{ \
155 GstElement *from = SCHED (element)->current; \
156 if (from && from->post_run_func) \
157 from->post_run_func (from); \
158 SCHED (element)->current = element; \
159 if (element->pre_run_func) \
160 element->pre_run_func (element); \
161 do_cothread_switch (GST_ELEMENT_THREADSTATE (element)); \
164 #define do_switch_to_main(sched) G_STMT_START{ \
165 GstElement *current = ((GstBasicScheduler*)sched)->current; \
166 if (current && current->post_run_func) \
167 current->post_run_func (current); \
168 ((GstBasicScheduler*) sched)->current = NULL; \
170 (do_cothread_get_main \
171 (((GstBasicScheduler*)sched)->context)); \
174 #define do_switch_from_main(entry) G_STMT_START{ \
175 if (entry->pre_run_func) \
176 entry->pre_run_func (entry); \
177 SCHED (entry)->current = entry; \
178 do_cothread_switch (GST_ELEMENT_THREADSTATE (entry)); \
182 gst_basic_scheduler_get_type (void)
184 if (!_gst_basic_scheduler_type) {
185 static const GTypeInfo scheduler_info = {
186 sizeof (GstBasicSchedulerClass),
189 (GClassInitFunc) gst_basic_scheduler_class_init,
192 sizeof (GstBasicScheduler),
194 (GInstanceInitFunc) gst_basic_scheduler_init,
198 _gst_basic_scheduler_type =
199 g_type_register_static (GST_TYPE_SCHEDULER,
200 "Gst" COTHREADS_NAME_CAPITAL "Scheduler", &scheduler_info, 0);
202 return _gst_basic_scheduler_type;
206 gst_basic_scheduler_class_init (GstBasicSchedulerClass * klass)
208 GObjectClass *gobject_class;
209 GstObjectClass *gstobject_class;
210 GstSchedulerClass *gstscheduler_class;
212 gobject_class = (GObjectClass *) klass;
213 gstobject_class = (GstObjectClass *) klass;
214 gstscheduler_class = (GstSchedulerClass *) klass;
216 parent_class = g_type_class_ref (GST_TYPE_SCHEDULER);
218 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_basic_scheduler_dispose);
220 gstscheduler_class->setup = GST_DEBUG_FUNCPTR (gst_basic_scheduler_setup);
221 gstscheduler_class->reset = GST_DEBUG_FUNCPTR (gst_basic_scheduler_reset);
222 gstscheduler_class->add_element =
223 GST_DEBUG_FUNCPTR (gst_basic_scheduler_add_element);
224 gstscheduler_class->remove_element =
225 GST_DEBUG_FUNCPTR (gst_basic_scheduler_remove_element);
226 gstscheduler_class->state_transition =
227 GST_DEBUG_FUNCPTR (gst_basic_scheduler_state_transition);
228 gstscheduler_class->yield = GST_DEBUG_FUNCPTR (gst_basic_scheduler_yield);
229 gstscheduler_class->interrupt =
230 GST_DEBUG_FUNCPTR (gst_basic_scheduler_interrupt);
231 gstscheduler_class->error = GST_DEBUG_FUNCPTR (gst_basic_scheduler_error);
232 gstscheduler_class->pad_link =
233 GST_DEBUG_FUNCPTR (gst_basic_scheduler_pad_link);
234 gstscheduler_class->pad_unlink =
235 GST_DEBUG_FUNCPTR (gst_basic_scheduler_pad_unlink);
236 gstscheduler_class->pad_select =
237 GST_DEBUG_FUNCPTR (gst_basic_scheduler_pad_select);
238 gstscheduler_class->clock_wait = NULL;
239 gstscheduler_class->iterate = GST_DEBUG_FUNCPTR (gst_basic_scheduler_iterate);
241 gstscheduler_class->show = GST_DEBUG_FUNCPTR (gst_basic_scheduler_show);
243 do_cothreads_init (NULL);
247 gst_basic_scheduler_init (GstBasicScheduler * scheduler)
249 scheduler->elements = NULL;
250 scheduler->num_elements = 0;
251 scheduler->chains = NULL;
252 scheduler->num_chains = 0;
254 GST_FLAG_SET (scheduler, GST_SCHEDULER_FLAG_NEW_API);
258 gst_basic_scheduler_dispose (GObject * object)
260 G_OBJECT_CLASS (parent_class)->dispose (object);
264 plugin_init (GstPlugin * plugin)
266 if (!gst_scheduler_register (plugin, "basic" COTHREADS_NAME,
267 "A basic scheduler using " COTHREADS_NAME " cothreads",
268 gst_basic_scheduler_get_type ()))
271 GST_DEBUG_CATEGORY_INIT (debug_dataflow, "basic_dataflow", 0,
272 "basic scheduler dataflow");
273 GST_DEBUG_CATEGORY_INIT (debug_scheduler, "basic_scheduler", 0,
274 "basic scheduler general information");
279 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
281 "gstbasic" COTHREADS_NAME "scheduler",
282 "a basic scheduler using " COTHREADS_NAME " cothreads",
283 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN)
285 static int gst_basic_scheduler_loopfunc_wrapper (int argc, char **argv)
287 GstElement *element = GST_ELEMENT (argv);
288 G_GNUC_UNUSED const gchar *name = GST_ELEMENT_NAME (element);
290 GST_DEBUG ("entering loopfunc wrapper of %s", name);
292 gst_object_ref (GST_OBJECT (element));
294 GST_CAT_DEBUG (debug_dataflow, "calling loopfunc %s for element %s",
295 GST_DEBUG_FUNCPTR_NAME (element->loopfunc), name);
296 (element->loopfunc) (element);
297 GST_CAT_DEBUG (debug_dataflow, "element %s ended loop function", name);
299 } while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element));
300 GST_FLAG_UNSET (element, GST_ELEMENT_COTHREAD_STOPPING);
302 /* due to oddities in the cothreads code, when this function returns it will
303 * switch to the main cothread. thus, we need to unlock the current element. */
304 if (SCHED (element)) {
305 if (SCHED (element)->current && SCHED (element)->current->post_run_func) {
306 SCHED (element)->current->post_run_func (SCHED (element)->current);
308 SCHED (element)->current = NULL;
311 GST_DEBUG ("leaving loopfunc wrapper of %s", name);
312 gst_object_unref (GST_OBJECT (element));
318 gst_basic_scheduler_chain_wrapper (int argc, char **argv)
320 GSList *already_iterated = NULL;
321 GstElement *element = GST_ELEMENT (argv);
322 G_GNUC_UNUSED const gchar *name = GST_ELEMENT_NAME (element);
324 GST_DEBUG ("entered chain wrapper of element %s", name);
326 GST_CAT_DEBUG (debug_dataflow, "stepping through pads");
328 gst_object_ref (GST_OBJECT (element));
333 pads = element->pads;
336 GstPad *pad = GST_PAD (pads->data);
339 if (!GST_IS_REAL_PAD (pad))
342 realpad = GST_REAL_PAD (pad);
344 if (GST_RPAD_DIRECTION (realpad) == GST_PAD_SINK &&
345 GST_PAD_IS_LINKED (realpad) &&
346 g_slist_find (already_iterated, pad) == NULL) {
349 GST_CAT_DEBUG (debug_dataflow, "pulling data from %s:%s", name,
351 data = gst_pad_pull (pad);
353 if (GST_IS_EVENT (data) && !GST_ELEMENT_IS_EVENT_AWARE (element)) {
354 gst_pad_send_event (pad, GST_EVENT (data));
356 GST_CAT_DEBUG (debug_dataflow,
357 "calling chain function of %s:%s %p", name,
358 GST_PAD_NAME (pad), data);
359 gst_pad_call_chain_function (GST_PAD (realpad), data);
360 GST_CAT_DEBUG (debug_dataflow,
361 "calling chain function of element %s done", name);
364 already_iterated = g_slist_prepend (already_iterated, pad);
367 pads = g_list_next (pads);
369 } while (pads != NULL);
370 if (already_iterated == NULL) {
371 GST_DEBUG_OBJECT (SCHED (element), "nothing to iterate for element %s",
372 GST_ELEMENT_NAME (element));
375 g_slist_free (already_iterated);
376 already_iterated = NULL;
377 } while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element));
379 GST_FLAG_UNSET (element, GST_ELEMENT_COTHREAD_STOPPING);
381 /* due to oddities in the cothreads code, when this function returns it will
382 * switch to the main cothread. thus, we need to unlock the current element. */
383 if (SCHED (element)) {
384 if (SCHED (element)->current && SCHED (element)->current->post_run_func) {
385 SCHED (element)->current->post_run_func (SCHED (element)->current);
387 SCHED (element)->current = NULL;
390 GST_CAT_DEBUG (debug_dataflow, "leaving chain wrapper of element %s", name);
391 gst_object_unref (GST_OBJECT (element));
397 gst_basic_scheduler_src_wrapper (int argc, char **argv)
399 GstElement *element = GST_ELEMENT (argv);
402 GstData *data = NULL;
404 G_GNUC_UNUSED const gchar *name = GST_ELEMENT_NAME (element);
406 GST_DEBUG ("entering src wrapper of element %s", name);
410 pads = element->pads;
413 if (!GST_IS_REAL_PAD (pads->data))
416 realpad = GST_REAL_PAD (pads->data);
418 pads = g_list_next (pads);
419 if (GST_RPAD_DIRECTION (realpad) == GST_PAD_SRC) {
421 GST_CAT_DEBUG (debug_dataflow, "calling _getfunc for %s:%s",
422 GST_DEBUG_PAD_NAME (realpad));
423 data = gst_pad_call_get_function (GST_PAD (realpad));
425 GST_CAT_DEBUG (debug_dataflow, "calling gst_pad_push on pad %s:%s %p",
426 GST_DEBUG_PAD_NAME (realpad), data);
427 gst_pad_push (GST_PAD (realpad), data);
431 } while (!GST_ELEMENT_IS_COTHREAD_STOPPING (element) && !inf_loop);
433 GST_FLAG_UNSET (element, GST_ELEMENT_COTHREAD_STOPPING);
435 /* due to oddities in the cothreads code, when this function returns it will
436 * switch to the main cothread. thus, we need to unlock the current element. */
437 if (SCHED (element)->current->post_run_func)
438 SCHED (element)->current->post_run_func (SCHED (element)->current);
439 SCHED (element)->current = NULL;
441 GST_DEBUG ("leaving src wrapper of element %s", name);
447 gst_basic_scheduler_chainhandler_proxy (GstPad * pad, GstData * data)
449 gint loop_count = 100;
453 parent = GST_PAD_PARENT (pad);
454 peer = GST_RPAD_PEER (pad);
456 GST_CAT_DEBUG (debug_dataflow, "putting buffer %p in peer \"%s:%s\"'s pen",
457 data, GST_DEBUG_PAD_NAME (peer));
460 * loop until the bufferpen is empty so we can fill it up again
462 while (GST_RPAD_BUFPEN (GST_RPAD_PEER (pad)) != NULL && --loop_count) {
463 GST_CAT_DEBUG (debug_dataflow, "switching to %p to empty bufpen %d",
464 GST_ELEMENT_THREADSTATE (parent), loop_count);
466 do_element_switch (parent);
468 /* we may no longer be the same pad, check. */
469 if (GST_RPAD_PEER (peer) != (GstRealPad *) pad) {
470 GST_CAT_DEBUG (debug_dataflow, "new pad in mid-switch!");
471 pad = (GstPad *) GST_RPAD_PEER (peer);
473 parent = GST_PAD_PARENT (pad);
474 peer = GST_RPAD_PEER (pad);
477 if (loop_count == 0) {
478 GST_ELEMENT_ERROR (parent, CORE, SCHEDULER, (NULL),
479 ("(internal error) basic: maximum number of switches exceeded"));
483 g_assert (GST_RPAD_BUFPEN (GST_RPAD_PEER (pad)) == NULL);
485 /* now fill the bufferpen and switch so it can be consumed */
486 GST_RPAD_BUFPEN (GST_RPAD_PEER (pad)) = data;
487 GST_CAT_DEBUG (debug_dataflow, "switching to %p to consume buffer %p",
488 GST_ELEMENT_THREADSTATE (GST_PAD_PARENT (pad)), data);
490 do_element_switch (parent);
492 GST_CAT_DEBUG (debug_dataflow, "leaving chainhandler proxy of %s:%s",
493 GST_DEBUG_PAD_NAME (pad));
497 gst_basic_scheduler_select_proxy (GstPad * pad, GstData * data)
501 parent = GST_PAD_PARENT (pad);
503 GST_CAT_DEBUG (debug_dataflow, "putting buffer %p in peer's pen of pad %s:%s",
504 data, GST_DEBUG_PAD_NAME (pad));
506 g_assert (GST_RPAD_BUFPEN (GST_RPAD_PEER (pad)) == NULL);
507 /* now fill the bufferpen and switch so it can be consumed */
508 GST_RPAD_BUFPEN (GST_RPAD_PEER (pad)) = data;
509 GST_CAT_DEBUG (debug_dataflow, "switching to %p",
510 GST_ELEMENT_THREADSTATE (parent));
511 /* FIXME temporarily diabled */
512 /* parent->select_pad = pad; */
514 do_element_switch (parent);
516 GST_CAT_DEBUG (debug_dataflow, "done switching");
521 gst_basic_scheduler_gethandler_proxy (GstPad * pad)
527 GST_CAT_DEBUG (debug_dataflow, "entering gethandler proxy of %s:%s",
528 GST_DEBUG_PAD_NAME (pad));
530 parent = GST_PAD_PARENT (pad);
531 peer = GST_RPAD_PEER (pad);
533 /* FIXME this should be bounded */
534 /* we will loop switching to the peer until it's filled up the bufferpen */
535 while (GST_RPAD_BUFPEN (pad) == NULL) {
537 GST_CAT_DEBUG (debug_dataflow, "switching to \"%s\": %p to fill bufpen",
538 GST_ELEMENT_NAME (parent), GST_ELEMENT_THREADSTATE (parent));
540 do_element_switch (parent);
542 /* we may no longer be the same pad, check. */
543 if (GST_RPAD_PEER (peer) != (GstRealPad *) pad) {
544 GST_CAT_DEBUG (debug_dataflow, "new pad in mid-switch!");
545 pad = (GstPad *) GST_RPAD_PEER (peer);
547 GST_ELEMENT_ERROR (parent, CORE, PAD, (NULL), ("pad unlinked"));
549 parent = GST_PAD_PARENT (pad);
550 peer = GST_RPAD_PEER (pad);
553 GST_CAT_DEBUG (debug_dataflow, "done switching");
555 /* now grab the buffer from the pen, clear the pen, and return the buffer */
556 data = GST_RPAD_BUFPEN (pad);
557 GST_RPAD_BUFPEN (pad) = NULL;
559 GST_CAT_DEBUG (debug_dataflow, "leaving gethandler proxy of %s:%s",
560 GST_DEBUG_PAD_NAME (pad));
566 gst_basic_scheduler_eventhandler_proxy (GstPad * srcpad, GstEvent * event)
570 GST_CAT_INFO (debug_dataflow, "intercepting event %d on pad %s:%s",
571 GST_EVENT_TYPE (event), GST_DEBUG_PAD_NAME (srcpad));
573 /* figure out if we need to flush */
574 switch (GST_EVENT_TYPE (event)) {
575 case GST_EVENT_FLUSH:
579 case GST_EVENT_SEEK_SEGMENT:
580 flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH;
588 GstData *data = GST_RPAD_BUFPEN (srcpad);
590 GST_CAT_INFO (debug_dataflow, "event is flush");
593 GST_CAT_INFO (debug_dataflow, "need to clear some buffers");
595 gst_data_unref (data);
596 GST_RPAD_BUFPEN (srcpad) = NULL;
599 return GST_RPAD_EVENTFUNC (srcpad) (srcpad, event);
603 gst_basic_scheduler_cothreaded_chain (GstBin * bin, GstSchedulerChain * chain)
607 cothread_func wrapper_function;
611 GST_DEBUG ("chain is using COTHREADS");
613 g_assert (chain->sched->context != NULL);
615 /* walk through all the chain's elements */
616 elements = chain->elements;
620 element = GST_ELEMENT (elements->data);
621 elements = g_list_next (elements);
623 decoupled = GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED);
625 /* start out without a wrapper function, we select it later */
626 wrapper_function = NULL;
628 /* if the element has a loopfunc... */
629 if (element->loopfunc != NULL) {
631 GST_DEBUG_FUNCPTR (gst_basic_scheduler_loopfunc_wrapper);
632 GST_DEBUG ("element '%s' is a loop-based", GST_ELEMENT_NAME (element));
634 /* otherwise we need to decide what kind of cothread
635 * if it's not DECOUPLED, we decide based on
636 * whether it's a source or not */
638 /* if it doesn't have any sinks, it must be a source (duh) */
639 if (element->numsinkpads == 0) {
641 GST_DEBUG_FUNCPTR (gst_basic_scheduler_src_wrapper);
642 GST_DEBUG ("element '%s' is a source, using _src_wrapper",
643 GST_ELEMENT_NAME (element));
646 GST_DEBUG_FUNCPTR (gst_basic_scheduler_chain_wrapper);
647 GST_DEBUG ("element '%s' is a filter, using _chain_wrapper",
648 GST_ELEMENT_NAME (element));
653 /* now we have to walk through the pads to set up their state */
654 pads = element->pads;
658 pad = GST_PAD (pads->data);
659 pads = g_list_next (pads);
661 if (!GST_IS_REAL_PAD (pad))
664 peerpad = GST_PAD_PEER (pad);
666 GstElement *peerelement = GST_ELEMENT (GST_PAD_PARENT (peerpad));
667 gboolean different_sched =
668 (peerelement->sched != GST_SCHEDULER (chain->sched));
669 gboolean peer_decoupled =
670 GST_FLAG_IS_SET (peerelement, GST_ELEMENT_DECOUPLED);
672 GST_DEBUG ("inspecting pad %s:%s", GST_DEBUG_PAD_NAME (peerpad));
674 /* we don't need to check this for decoupled elements */
676 /* if the peer element is in another schedule,
677 * it's not decoupled and we are not decoupled
678 * either, we have an error */
679 if (different_sched && !peer_decoupled) {
680 GST_ELEMENT_ERROR (element, CORE, SCHEDULER, (NULL),
681 ("element \"%s\" is not decoupled but has pads in different schedulers",
682 GST_ELEMENT_NAME (element)));
685 /* ok, the peer is in a different scheduler and is decoupled,
687 * handlers so we can talk with it */
688 else if (different_sched) {
689 if (GST_RPAD_DIRECTION (peerpad) == GST_PAD_SINK) {
690 GST_DEBUG ("copying chain func into push proxy for peer %s:%s",
691 GST_DEBUG_PAD_NAME (peerpad));
692 GST_RPAD_CHAINHANDLER (peerpad) = gst_pad_call_chain_function;
694 GST_DEBUG ("copying get func into pull proxy for peer %s:%s",
695 GST_DEBUG_PAD_NAME (peerpad));
696 GST_RPAD_GETHANDLER (peerpad) = gst_pad_call_get_function;
700 /* in any case we need to copy the eventfunc into the handler */
701 GST_RPAD_EVENTHANDLER (peerpad) = GST_RPAD_EVENTFUNC (peerpad);
704 /* if the element is DECOUPLED or outside the manager, we have to chain */
706 /* set the chain proxies */
707 if (GST_RPAD_DIRECTION (pad) == GST_PAD_SINK) {
708 GST_DEBUG ("copying chain function into push proxy for %s:%s",
709 GST_DEBUG_PAD_NAME (pad));
710 GST_RPAD_CHAINHANDLER (pad) = gst_pad_call_chain_function;
712 GST_DEBUG ("copying get function into pull proxy for %s:%s",
713 GST_DEBUG_PAD_NAME (pad));
714 GST_RPAD_GETHANDLER (pad) = gst_pad_call_get_function;
717 /* otherwise we really are a cothread */
719 if (GST_RPAD_DIRECTION (pad) == GST_PAD_SINK) {
720 GST_DEBUG ("setting cothreaded push proxy for sinkpad %s:%s",
721 GST_DEBUG_PAD_NAME (pad));
722 GST_RPAD_CHAINHANDLER (pad) =
723 GST_DEBUG_FUNCPTR (gst_basic_scheduler_chainhandler_proxy);
724 GST_RPAD_EVENTHANDLER (pad) = GST_RPAD_EVENTFUNC (pad);
726 GST_DEBUG ("setting cothreaded pull proxy for srcpad %s:%s",
727 GST_DEBUG_PAD_NAME (pad));
728 GST_RPAD_GETHANDLER (pad) =
729 GST_DEBUG_FUNCPTR (gst_basic_scheduler_gethandler_proxy);
730 /* the gethandler proxy function can queue a buffer in the bufpen, we need
731 * to remove this buffer when a flush event is sent on the pad */
732 GST_RPAD_EVENTHANDLER (pad) =
733 GST_DEBUG_FUNCPTR (gst_basic_scheduler_eventhandler_proxy);
738 /* need to set up the cothread now */
739 if (wrapper_function != NULL) {
740 if (GST_ELEMENT_THREADSTATE (element) == NULL) {
741 GST_DEBUG ("about to create a cothread, wrapper for '%s' is &%s",
742 GST_ELEMENT_NAME (element),
743 GST_DEBUG_FUNCPTR_NAME (wrapper_function));
744 do_cothread_create (GST_ELEMENT_THREADSTATE (element),
745 chain->sched->context, wrapper_function, 0, (char **) element);
746 if (GST_ELEMENT_THREADSTATE (element) == NULL) {
747 GST_ELEMENT_ERROR (element, RESOURCE, TOO_LAZY, (NULL),
748 ("could not create cothread for \"%s\"",
749 GST_ELEMENT_NAME (element)));
752 GST_DEBUG ("created cothread %p for '%s'",
753 GST_ELEMENT_THREADSTATE (element), GST_ELEMENT_NAME (element));
755 /* set the cothread wrapper function */
756 GST_DEBUG ("about to set the wrapper function for '%s' to &%s",
757 GST_ELEMENT_NAME (element),
758 GST_DEBUG_FUNCPTR_NAME (wrapper_function));
759 do_cothread_setfunc (GST_ELEMENT_THREADSTATE (element),
760 chain->sched->context, wrapper_function, 0, (char **) element);
761 GST_DEBUG ("set wrapper function for '%s' to &%s",
762 GST_ELEMENT_NAME (element),
763 GST_DEBUG_FUNCPTR_NAME (wrapper_function));
771 static GstSchedulerChain *
772 gst_basic_scheduler_chain_new (GstBasicScheduler * sched)
774 GstSchedulerChain *chain = g_new (GstSchedulerChain, 1);
776 /* initialize the chain with sane values */
777 chain->sched = sched;
778 chain->disabled = NULL;
779 chain->elements = NULL;
780 chain->num_elements = 0;
782 chain->cothreaded_elements = 0;
783 chain->schedule = FALSE;
785 /* add the chain to the schedulers' list of chains */
786 sched->chains = g_list_prepend (sched->chains, chain);
789 /* notify the scheduler that something changed */
790 GST_FLAG_SET (sched, GST_BASIC_SCHEDULER_CHANGE);
792 GST_INFO ("created new chain %p, now are %d chains in sched %p",
793 chain, sched->num_chains, sched);
799 gst_basic_scheduler_chain_destroy (GstSchedulerChain * chain)
801 GstBasicScheduler *sched = chain->sched;
803 /* remove the chain from the schedulers' list of chains */
804 sched->chains = g_list_remove (sched->chains, chain);
807 /* destroy the chain */
808 g_list_free (chain->disabled); /* should be empty... */
809 g_list_free (chain->elements); /* ditto */
811 GST_INFO ("destroyed chain %p, now are %d chains in sched %p", chain,
812 sched->num_chains, sched);
816 /* notify the scheduler that something changed */
817 GST_FLAG_SET (sched, GST_BASIC_SCHEDULER_CHANGE);
821 gst_basic_scheduler_chain_add_element (GstSchedulerChain * chain,
822 GstElement * element)
824 /* set the sched pointer for the element */
825 element->sched = GST_SCHEDULER (chain->sched);
827 /* add the element to either the main list or the disabled list */
828 if (GST_STATE (element) == GST_STATE_PLAYING) {
829 GST_INFO ("adding element \"%s\" to chain %p enabled",
830 GST_ELEMENT_NAME (element), chain);
831 chain->elements = g_list_prepend (chain->elements, element);
833 GST_INFO ("adding element \"%s\" to chain %p disabled",
834 GST_ELEMENT_NAME (element), chain);
835 chain->disabled = g_list_prepend (chain->disabled, element);
837 chain->num_elements++;
839 /* notify the scheduler that something changed */
840 GST_FLAG_SET (chain->sched, GST_BASIC_SCHEDULER_CHANGE);
844 gst_basic_scheduler_chain_enable_element (GstSchedulerChain * chain,
845 GstElement * element)
847 GST_INFO ("enabling element \"%s\" in chain %p",
848 GST_ELEMENT_NAME (element), chain);
850 /* remove from disabled list */
851 chain->disabled = g_list_remove (chain->disabled, element);
853 /* add to elements list */
854 chain->elements = g_list_prepend (chain->elements, element);
856 /* notify the scheduler that something changed */
857 GST_FLAG_SET (chain->sched, GST_BASIC_SCHEDULER_CHANGE);
858 /* GST_FLAG_UNSET(element, GST_ELEMENT_COTHREAD_STOPPING); */
860 /* reschedule the chain */
861 return gst_basic_scheduler_cothreaded_chain (GST_BIN (GST_SCHEDULER (chain->
862 sched)->parent), chain);
866 gst_basic_scheduler_chain_disable_element (GstSchedulerChain * chain,
867 GstElement * element)
869 GST_INFO ("disabling element \"%s\" in chain %p",
870 GST_ELEMENT_NAME (element), chain);
872 /* remove from elements list */
873 chain->elements = g_list_remove (chain->elements, element);
875 /* add to disabled list */
876 chain->disabled = g_list_prepend (chain->disabled, element);
878 /* notify the scheduler that something changed */
879 GST_FLAG_SET (chain->sched, GST_BASIC_SCHEDULER_CHANGE);
880 GST_FLAG_SET (element, GST_ELEMENT_COTHREAD_STOPPING);
882 /* reschedule the chain */
883 /* FIXME this should be done only if manager state != NULL */
884 /* gst_basic_scheduler_cothreaded_chain(GST_BIN(chain->sched->parent),chain); */
888 gst_basic_scheduler_chain_remove_element (GstSchedulerChain * chain,
889 GstElement * element)
891 GST_INFO ("removing element \"%s\" from chain %p", GST_ELEMENT_NAME (element),
894 /* if it's active, deactivate it */
895 if (g_list_find (chain->elements, element)) {
896 gst_basic_scheduler_chain_disable_element (chain, element);
898 /* we have to check for a threadstate here because a queue doesn't have one */
899 if (GST_ELEMENT_THREADSTATE (element)) {
900 do_cothread_destroy (GST_ELEMENT_THREADSTATE (element));
901 GST_ELEMENT_THREADSTATE (element) = NULL;
904 /* remove the element from the list of elements */
905 chain->disabled = g_list_remove (chain->disabled, element);
906 chain->num_elements--;
908 /* notify the scheduler that something changed */
909 GST_FLAG_SET (chain->sched, GST_BASIC_SCHEDULER_CHANGE);
911 /* if there are no more elements in the chain, destroy the chain */
912 if (chain->num_elements == 0)
913 gst_basic_scheduler_chain_destroy (chain);
918 gst_basic_scheduler_chain_elements (GstBasicScheduler * sched,
919 GstElement * element1, GstElement * element2)
922 GstSchedulerChain *chain;
923 GstSchedulerChain *chain1 = NULL, *chain2 = NULL;
926 /* first find the chains that hold the two */
927 chains = sched->chains;
929 chain = (GstSchedulerChain *) (chains->data);
930 chains = g_list_next (chains);
932 if (g_list_find (chain->disabled, element1))
934 else if (g_list_find (chain->elements, element1))
937 if (g_list_find (chain->disabled, element2))
939 else if (g_list_find (chain->elements, element2))
943 /* first check to see if they're in the same chain, we're done if that's the case */
944 if ((chain1 != NULL) && (chain1 == chain2)) {
945 GST_INFO ("elements are already in the same chain");
949 /* now, if neither element has a chain, create one */
950 if ((chain1 == NULL) && (chain2 == NULL)) {
951 GST_INFO ("creating new chain to hold two new elements");
952 chain = gst_basic_scheduler_chain_new (sched);
953 gst_basic_scheduler_chain_add_element (chain, element1);
954 gst_basic_scheduler_chain_add_element (chain, element2);
955 /* FIXME chain changed here */
956 /* gst_basic_scheduler_cothreaded_chain(chain->sched->parent,chain); */
958 /* otherwise if both have chains already, join them */
959 } else if ((chain1 != NULL) && (chain2 != NULL)) {
960 GST_INFO ("merging chain %p into chain %p", chain2, chain1);
961 /* take the contents of chain2 and merge them into chain1 */
963 g_list_concat (chain1->disabled, g_list_copy (chain2->disabled));
965 g_list_concat (chain1->elements, g_list_copy (chain2->elements));
966 chain1->num_elements += chain2->num_elements;
967 gst_basic_scheduler_chain_destroy (chain2);
970 gst_basic_scheduler_cothreaded_chain (GST_BIN (GST_SCHEDULER (chain1->
971 sched)->parent), chain1);
973 /* otherwise one has a chain already, the other doesn't */
975 /* pick out which one has the chain, and which doesn't */
977 chain = chain1, element = element2;
979 chain = chain2, element = element1;
981 GST_INFO ("adding element to existing chain");
982 gst_basic_scheduler_chain_add_element (chain, element);
983 /* FIXME chain changed here */
984 /* gst_basic_scheduler_cothreaded_chain(chain->sched->parent,chain); */
990 /* find the chain within the scheduler that holds the element, if any */
991 static GstSchedulerChain *
992 gst_basic_scheduler_find_chain (GstBasicScheduler * sched, GstElement * element)
995 GstSchedulerChain *chain;
997 GST_INFO ("searching for element \"%s\" in chains",
998 GST_ELEMENT_NAME (element));
1000 chains = sched->chains;
1002 chain = (GstSchedulerChain *) (chains->data);
1003 chains = g_list_next (chains);
1005 if (g_list_find (chain->elements, element))
1007 if (g_list_find (chain->disabled, element))
1015 gst_basic_scheduler_chain_recursive_add (GstSchedulerChain * chain,
1016 GstElement * element, gboolean remove)
1020 GstElement *peerelement;
1021 GstSchedulerChain *prevchain;
1023 /* check to see if it's in a chain already */
1024 prevchain = gst_basic_scheduler_find_chain (chain->sched, element);
1025 /* if it's already in another chain, either remove or punt */
1026 if (prevchain != NULL) {
1028 gst_basic_scheduler_chain_remove_element (prevchain, element);
1033 /* add it to this one */
1034 gst_basic_scheduler_chain_add_element (chain, element);
1036 GST_DEBUG ("recursing on element \"%s\"", GST_ELEMENT_NAME (element));
1037 /* now go through all the pads and see which peers can be added */
1038 pads = element->pads;
1040 pad = GST_PAD (pads->data);
1041 pads = g_list_next (pads);
1043 GST_DEBUG ("have pad %s:%s, checking for valid peer",
1044 GST_DEBUG_PAD_NAME (pad));
1045 /* if the peer exists and could be in the same chain */
1046 if (GST_PAD_PEER (pad)) {
1047 GST_DEBUG ("has peer %s:%s", GST_DEBUG_PAD_NAME (GST_PAD_PEER (pad)));
1048 peerelement = GST_PAD_PARENT (GST_PAD_PEER (pad));
1049 if (GST_ELEMENT_SCHEDULER (GST_PAD_PARENT (pad)) ==
1050 GST_ELEMENT_SCHEDULER (peerelement)) {
1051 GST_DEBUG ("peer \"%s\" is valid for same chain",
1052 GST_ELEMENT_NAME (peerelement));
1053 gst_basic_scheduler_chain_recursive_add (chain, peerelement, remove);
1060 * Entry points for this scheduler.
1063 gst_basic_scheduler_setup (GstScheduler * sched)
1065 /* first create thread context */
1066 if (GST_BASIC_SCHEDULER (sched)->context == NULL) {
1067 GST_DEBUG ("initializing cothread context");
1068 GST_BASIC_SCHEDULER (sched)->context = do_cothread_context_init ();
1073 gst_basic_scheduler_reset (GstScheduler * sched)
1075 cothread_context *ctx;
1076 GList *elements = GST_BASIC_SCHEDULER (sched)->elements;
1079 GstElement *element = GST_ELEMENT (elements->data);
1081 if (GST_ELEMENT_THREADSTATE (element)) {
1082 do_cothread_destroy (GST_ELEMENT_THREADSTATE (element));
1083 GST_ELEMENT_THREADSTATE (element) = NULL;
1085 elements = g_list_next (elements);
1088 ctx = GST_BASIC_SCHEDULER (sched)->context;
1090 do_cothread_context_destroy (ctx);
1092 GST_BASIC_SCHEDULER (sched)->context = NULL;
1096 gst_basic_scheduler_add_element (GstScheduler * sched, GstElement * element)
1098 GstSchedulerChain *chain;
1099 GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched);
1101 GST_INFO ("adding element \"%s\" to scheduler", GST_ELEMENT_NAME (element));
1103 /* only deal with elements after this point, not bins */
1104 /* exception is made for Bin's that are schedulable, like the autoplugger */
1105 if (GST_IS_BIN (element)
1106 && !GST_FLAG_IS_SET (element, GST_BIN_SELF_SCHEDULABLE))
1109 /* first add it to the list of elements that are to be scheduled */
1110 bsched->elements = g_list_prepend (bsched->elements, element);
1111 bsched->num_elements++;
1113 /* create a chain to hold it, and add */
1114 chain = gst_basic_scheduler_chain_new (bsched);
1115 gst_basic_scheduler_chain_add_element (chain, element);
1119 gst_basic_scheduler_remove_element (GstScheduler * sched, GstElement * element)
1121 GstSchedulerChain *chain;
1122 GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched);
1124 if (g_list_find (bsched->elements, element)) {
1125 GST_INFO ("removing element \"%s\" from scheduler",
1126 GST_ELEMENT_NAME (element));
1128 /* if we are removing the currently scheduled element */
1129 if (bsched->current == element) {
1130 GST_FLAG_SET (element, GST_ELEMENT_COTHREAD_STOPPING);
1131 if (element->post_run_func)
1132 element->post_run_func (element);
1133 bsched->current = NULL;
1135 /* find what chain the element is in */
1136 chain = gst_basic_scheduler_find_chain (bsched, element);
1138 /* remove it from its chain */
1139 if (chain != NULL) {
1140 gst_basic_scheduler_chain_remove_element (chain, element);
1143 /* remove it from the list of elements */
1144 bsched->elements = g_list_remove (bsched->elements, element);
1145 bsched->num_elements--;
1147 /* unset the scheduler pointer in the element */
1151 static GstElementStateReturn
1152 gst_basic_scheduler_state_transition (GstScheduler * sched,
1153 GstElement * element, gint transition)
1155 GstSchedulerChain *chain;
1156 GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched);
1158 /* check if our parent changed state */
1159 if (GST_SCHEDULER_PARENT (sched) == element) {
1160 GST_INFO ("parent \"%s\" changed state", GST_ELEMENT_NAME (element));
1161 if (transition == GST_STATE_PLAYING_TO_PAUSED) {
1162 GST_INFO ("setting scheduler state to stopped");
1163 GST_SCHEDULER_STATE (sched) = GST_SCHEDULER_STATE_STOPPED;
1164 } else if (transition == GST_STATE_PAUSED_TO_PLAYING) {
1165 GST_INFO ("setting scheduler state to running");
1166 GST_SCHEDULER_STATE (sched) = GST_SCHEDULER_STATE_RUNNING;
1168 GST_INFO ("no interesting state change, doing nothing");
1170 } else if (transition == GST_STATE_PLAYING_TO_PAUSED ||
1171 transition == GST_STATE_PAUSED_TO_PLAYING) {
1172 /* find the chain the element is in */
1173 chain = gst_basic_scheduler_find_chain (bsched, element);
1175 /* remove it from the chain */
1177 if (transition == GST_STATE_PLAYING_TO_PAUSED) {
1178 gst_basic_scheduler_chain_disable_element (chain, element);
1179 } else if (transition == GST_STATE_PAUSED_TO_PLAYING) {
1180 if (!gst_basic_scheduler_chain_enable_element (chain, element)) {
1181 GST_INFO ("could not enable element \"%s\"",
1182 GST_ELEMENT_NAME (element));
1183 return GST_STATE_FAILURE;
1187 GST_INFO ("element \"%s\" not found in any chain, no state change",
1188 GST_ELEMENT_NAME (element));
1192 return GST_STATE_SUCCESS;
1196 gst_basic_scheduler_yield (GstScheduler * sched, GstElement * element)
1198 if (GST_ELEMENT_IS_COTHREAD_STOPPING (element)) {
1200 do_switch_to_main (sched);
1202 /* no need to do a pre_run, the cothread is stopping */
1208 gst_basic_scheduler_interrupt (GstScheduler * sched, GstElement * element)
1211 GST_FLAG_SET (element, GST_ELEMENT_COTHREAD_STOPPING);
1212 do_switch_to_main (sched);
1218 gst_basic_scheduler_error (GstScheduler * sched, GstElement * element)
1220 GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched);
1222 if (GST_ELEMENT_THREADSTATE (element)) {
1223 GstSchedulerChain *chain;
1225 chain = gst_basic_scheduler_find_chain (bsched, element);
1227 gst_basic_scheduler_chain_disable_element (chain, element);
1229 GST_SCHEDULER_STATE (sched) = GST_SCHEDULER_STATE_ERROR;
1231 do_switch_to_main (sched);
1236 gst_basic_scheduler_pad_link (GstScheduler * sched, GstPad * srcpad,
1239 GstElement *srcelement, *sinkelement;
1240 GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched);
1242 srcelement = GST_PAD_PARENT (srcpad);
1243 g_return_if_fail (srcelement != NULL);
1244 sinkelement = GST_PAD_PARENT (sinkpad);
1245 g_return_if_fail (sinkelement != NULL);
1247 GST_INFO ("have pad linked callback on %s:%s to %s:%s",
1248 GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
1249 GST_DEBUG ("srcpad sched is %p, sinkpad sched is %p",
1250 GST_ELEMENT_SCHEDULER (srcelement), GST_ELEMENT_SCHEDULER (sinkelement));
1252 gst_basic_scheduler_chain_elements (bsched, srcelement, sinkelement);
1256 gst_basic_scheduler_pad_unlink (GstScheduler * sched, GstPad * srcpad,
1259 GstElement *element1, *element2;
1260 GstSchedulerChain *chain1, *chain2;
1261 GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched);
1263 GST_INFO ("unlinking pads %s:%s and %s:%s",
1264 GST_DEBUG_PAD_NAME (srcpad), GST_DEBUG_PAD_NAME (sinkpad));
1266 /* we need to have the parent elements of each pad */
1267 element1 = GST_ELEMENT (GST_PAD_PARENT (srcpad));
1268 element2 = GST_ELEMENT (GST_PAD_PARENT (sinkpad));
1270 /* first task is to remove the old chain they belonged to.
1271 * this can be accomplished by taking either of the elements,
1272 * since they are guaranteed to be in the same chain
1273 * FIXME is it potentially better to make an attempt at splitting cleaner??
1275 chain1 = gst_basic_scheduler_find_chain (bsched, element1);
1276 chain2 = gst_basic_scheduler_find_chain (bsched, element2);
1278 /* FIXME: The old code still works in most cases, but does not deal with
1279 * the problem of screwed up sched chains in some autoplugging cases.
1280 * The new code has an infinite recursion bug during pipeline shutdown,
1281 * which must be fixed before it can be enabled again.
1284 if (chain1 != chain2) {
1285 /* elements not in the same chain don't need to be separated */
1286 GST_INFO ("elements not in the same chain");
1291 GST_INFO ("destroying chain");
1292 gst_basic_scheduler_chain_destroy (chain1);
1294 /* now create a new chain to hold element1 and build it from scratch */
1295 chain1 = gst_basic_scheduler_chain_new (bsched);
1296 gst_basic_scheduler_chain_recursive_add (chain1, element1, FALSE);
1299 /* check the other element to see if it landed in the newly created chain */
1300 if (gst_basic_scheduler_find_chain (bsched, element2) == NULL) {
1301 /* if not in chain, create chain and build from scratch */
1302 chain2 = gst_basic_scheduler_chain_new (bsched);
1303 gst_basic_scheduler_chain_recursive_add (chain2, element2, FALSE);
1307 /* if they're both in the same chain, move second set of elements to a new chain */
1308 if (chain1 && (chain1 == chain2)) {
1309 GST_INFO ("creating new chain for second element and peers");
1310 chain2 = gst_basic_scheduler_chain_new (bsched);
1311 gst_basic_scheduler_chain_recursive_add (chain2, element2, TRUE);
1317 gst_basic_scheduler_pad_select (GstScheduler * sched, GstPad ** selected,
1320 GstData *data = NULL;
1323 GST_INFO ("performing select");
1325 while (padlist[i]) {
1326 GstPad *pad = padlist[i];
1328 GST_RPAD_CHAINHANDLER (pad) =
1329 GST_DEBUG_FUNCPTR (gst_basic_scheduler_select_proxy);
1332 do_element_switch (GST_PAD_PARENT (GST_PAD_PEER (padlist[0])));
1335 while (padlist[i]) {
1336 GstPad *pad = padlist[i];
1338 if (GST_RPAD_BUFPEN (pad)) {
1340 data = GST_RPAD_BUFPEN (pad);
1341 GST_RPAD_BUFPEN (pad) = NULL;
1344 GST_RPAD_CHAINHANDLER (pad) =
1345 GST_DEBUG_FUNCPTR (gst_basic_scheduler_chainhandler_proxy);
1348 g_assert (data != NULL);
1352 static GstSchedulerState
1353 gst_basic_scheduler_iterate (GstScheduler * sched)
1356 GstSchedulerChain *chain;
1360 GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched);
1362 GST_CAT_LOG_OBJECT (debug_dataflow, sched,
1363 "starting iteration in bin %s", GST_ELEMENT_NAME (sched->parent));
1365 /* clear the changes flag */
1366 GST_FLAG_UNSET (bsched, GST_BASIC_SCHEDULER_CHANGE);
1368 /* step through all the chains */
1369 chains = bsched->chains;
1372 return GST_SCHEDULER_STATE_STOPPED;
1375 chain = (GstSchedulerChain *) (chains->data);
1376 chains = g_list_next (chains);
1378 /* all we really have to do is switch to the first child */
1379 /* FIXME this should be lots more intelligent about where to start */
1380 GST_CAT_DEBUG (debug_dataflow,
1381 "starting iteration via cothreads using %s scheduler", _SCHEDULER_NAME);
1383 if (chain->elements) {
1384 entry = NULL; /*MattH ADDED? */
1385 GST_DEBUG ("there are %d elements in this chain", chain->num_elements);
1386 elements = chain->elements;
1388 entry = GST_ELEMENT (elements->data);
1389 elements = g_list_next (elements);
1390 if (GST_FLAG_IS_SET (entry, GST_ELEMENT_DECOUPLED)) {
1391 GST_DEBUG ("entry \"%s\" is DECOUPLED, skipping",
1392 GST_ELEMENT_NAME (entry));
1394 } else if (GST_FLAG_IS_SET (entry, GST_ELEMENT_INFINITE_LOOP)) {
1395 GST_DEBUG ("entry \"%s\" is not valid, skipping",
1396 GST_ELEMENT_NAME (entry));
1402 GstSchedulerState state;
1404 GST_FLAG_SET (entry, GST_ELEMENT_COTHREAD_STOPPING);
1406 GST_CAT_DEBUG (debug_dataflow,
1407 "set COTHREAD_STOPPING flag on \"%s\"(@%p)",
1408 GST_ELEMENT_NAME (entry), entry);
1409 if (GST_ELEMENT_THREADSTATE (entry)) {
1411 do_switch_from_main (entry);
1413 state = GST_SCHEDULER_STATE (sched);
1414 /* if something changed, return - go on else */
1415 if (GST_FLAG_IS_SET (bsched, GST_BASIC_SCHEDULER_CHANGE) &&
1416 state != GST_SCHEDULER_STATE_ERROR)
1417 return GST_SCHEDULER_STATE_RUNNING;
1419 GST_CAT_DEBUG (debug_dataflow,
1420 "cothread switch not possible, element has no threadstate");
1421 return GST_SCHEDULER_STATE_ERROR;
1424 /* following is a check to see if the chain was interrupted due to a
1425 * top-half state_change(). (i.e., if there's a pending state.)
1427 * if it was, return to gstthread.c::gst_thread_main_loop() to
1428 * execute the state change.
1430 GST_CAT_DEBUG (debug_dataflow, "cothread switch ended or interrupted");
1432 if (state != GST_SCHEDULER_STATE_RUNNING) {
1433 GST_CAT_INFO (debug_dataflow, "scheduler is not running, in state %d",
1440 GST_CAT_INFO (debug_dataflow,
1441 "no entry in this chain, trying the next one");
1444 GST_CAT_INFO (debug_dataflow,
1445 "no enabled elements in this chain, trying the next one");
1449 GST_CAT_LOG_OBJECT (debug_dataflow, sched, "leaving (%s)",
1450 GST_ELEMENT_NAME (sched->parent));
1451 if (scheduled == 0) {
1452 GST_CAT_INFO (debug_dataflow, "nothing was scheduled, return STOPPED");
1453 return GST_SCHEDULER_STATE_STOPPED;
1455 GST_CAT_INFO (debug_dataflow, "scheduler still running, return RUNNING");
1456 return GST_SCHEDULER_STATE_RUNNING;
1462 gst_basic_scheduler_show (GstScheduler * sched)
1464 GList *chains, *elements;
1465 GstElement *element;
1466 GstSchedulerChain *chain;
1467 GstBasicScheduler *bsched = GST_BASIC_SCHEDULER (sched);
1469 if (sched == NULL) {
1470 g_print ("scheduler doesn't exist for this element\n");
1474 g_return_if_fail (GST_IS_SCHEDULER (sched));
1476 g_print ("SCHEDULER DUMP FOR MANAGING BIN \"%s\"\n",
1477 GST_ELEMENT_NAME (sched->parent));
1479 g_print ("scheduler has %d elements in it: ", bsched->num_elements);
1480 elements = bsched->elements;
1482 element = GST_ELEMENT (elements->data);
1483 elements = g_list_next (elements);
1485 g_print ("%s, ", GST_ELEMENT_NAME (element));
1489 g_print ("scheduler has %d chains in it\n", bsched->num_chains);
1490 chains = bsched->chains;
1492 chain = (GstSchedulerChain *) (chains->data);
1493 chains = g_list_next (chains);
1495 g_print ("%p: ", chain);
1497 elements = chain->disabled;
1499 element = GST_ELEMENT (elements->data);
1500 elements = g_list_next (elements);
1502 g_print ("!%s, ", GST_ELEMENT_NAME (element));
1505 elements = chain->elements;
1507 element = GST_ELEMENT (elements->data);
1508 elements = g_list_next (elements);
1510 g_print ("%s, ", GST_ELEMENT_NAME (element));