Updated the API docs.
[platform/upstream/gstreamer.git] / gst / gstscheduler.c
1 /* Gnome-Streamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 //#define GST_DEBUG_ENABLED
21
22 #include "gstscheduler.h"
23 #include "gstdebug.h"
24
25
26 static int
27 gst_bin_loopfunc_wrapper (int argc,char *argv[])
28 {
29   GstElement *element = GST_ELEMENT (argv);
30   G_GNUC_UNUSED const gchar *name = gst_element_get_name (element);
31
32   DEBUG_ENTER("(%d,'%s')",argc,name);
33
34   do {
35     DEBUG("calling loopfunc %s for element %s\n",
36           GST_DEBUG_FUNCPTR_NAME (element->loopfunc),name);
37     (element->loopfunc) (element);
38     DEBUG("element %s ended loop function\n", name);
39   } while (!GST_ELEMENT_IS_COTHREAD_STOPPING(element));
40   GST_FLAG_UNSET(element,GST_ELEMENT_COTHREAD_STOPPING);
41
42   DEBUG_LEAVE("(%d,'%s')",argc,name);
43   return 0;
44 }
45
46 static int
47 gst_bin_chain_wrapper (int argc,char *argv[])
48 {
49   GstElement *element = GST_ELEMENT (argv);
50   G_GNUC_UNUSED const gchar *name = gst_element_get_name (element);
51   GList *pads;
52   GstPad *pad;
53   GstBuffer *buf;
54         
55   DEBUG_ENTER("(\"%s\")",name);
56   DEBUG("stepping through pads\n");
57   do {
58     pads = element->pads;
59     while (pads) {
60       pad = GST_PAD (pads->data);
61       pads = g_list_next (pads);   
62       if (pad->direction == GST_PAD_SINK) {
63         DEBUG("pulling a buffer from %s:%s\n", name, gst_pad_get_name (pad));
64         buf = gst_pad_pull (pad);
65         DEBUG("calling chain function of %s:%s\n", name, gst_pad_get_name (pad));
66         (pad->chainfunc) (pad,buf);
67         DEBUG("calling chain function of %s:%s done\n", name, gst_pad_get_name (pad));
68       }
69     }
70   } while (!GST_ELEMENT_IS_COTHREAD_STOPPING(element));
71   GST_FLAG_UNSET(element,GST_ELEMENT_COTHREAD_STOPPING);
72   
73   DEBUG_LEAVE("(%d,'%s')",argc,name);
74   return 0;
75 }
76
77 static int
78 gst_bin_src_wrapper (int argc,char *argv[])
79 {
80   GstElement *element = GST_ELEMENT (argv);
81   GList *pads;
82   GstPad *pad;
83   GstBuffer *buf;
84   G_GNUC_UNUSED const gchar *name = gst_element_get_name (element);
85   
86   DEBUG_ENTER("(%d,\"%s\")",argc,name);
87
88   do {
89     pads = element->pads;
90     while (pads) {
91       pad = GST_PAD (pads->data);   
92       if (pad->direction == GST_PAD_SRC) {
93 //        region_struct *region = cothread_get_data (element->threadstate, "region");
94         DEBUG("calling _getfunc for %s:%s\n",GST_DEBUG_PAD_NAME(pad));
95 //        if (region) {
96           //gst_src_push_region (GST_SRC (element), region->offset, region->size);
97 //          if (pad->getregionfunc == NULL)
98 //            fprintf(stderr,"error, no getregionfunc in \"%s\"\n", name);
99 //          buf = (pad->getregionfunc)(pad, region->offset, region->size);
100 //        } else {
101           if (pad->getfunc == NULL)
102             fprintf(stderr,"error, no getfunc in \"%s\"\n", name);
103           buf = (pad->getfunc)(pad);
104 //        }
105  
106         DEBUG("calling gst_pad_push on pad %s:%s\n",GST_DEBUG_PAD_NAME(pad));
107         gst_pad_push (pad, buf);
108       }
109       pads = g_list_next(pads);
110     }
111   } while (!GST_ELEMENT_IS_COTHREAD_STOPPING(element));
112   GST_FLAG_UNSET(element,GST_ELEMENT_COTHREAD_STOPPING);
113
114   DEBUG_LEAVE("");
115   return 0;
116 }
117                                 
118 static void
119 gst_bin_pushfunc_proxy (GstPad *pad, GstBuffer *buf)
120 {
121   cothread_state *threadstate = GST_ELEMENT(pad->parent)->threadstate;
122   DEBUG_ENTER("(%s:%s)",GST_DEBUG_PAD_NAME(pad));
123   DEBUG("putting buffer %p in peer's pen\n",buf);
124   pad->peer->bufpen = buf;
125   DEBUG("switching to %p (@%p)\n",threadstate,&(GST_ELEMENT(pad->parent)->threadstate));
126   cothread_switch (threadstate);
127   DEBUG("done switching\n");
128 }
129
130 static GstBuffer*
131 gst_bin_pullfunc_proxy (GstPad *pad)
132 {       
133   GstBuffer *buf;
134
135   cothread_state *threadstate = GST_ELEMENT(pad->parent)->threadstate;
136   DEBUG_ENTER("(%s:%s)",GST_DEBUG_PAD_NAME(pad));
137   if (pad->bufpen == NULL) {
138     DEBUG("switching to %p (@%p)\n",threadstate,&(GST_ELEMENT(pad->parent)->threadstate));
139     cothread_switch (threadstate);
140   }
141   DEBUG("done switching\n");
142   buf = pad->bufpen;  
143   pad->bufpen = NULL;
144   return buf; 
145 }
146   
147 static GstBuffer *
148 gst_bin_chainfunc_proxy (GstPad *pad)
149 {
150 // FIXME!!
151 //  GstBuffer *buf;
152   return NULL;
153 }
154   
155 // FIXME!!!
156 static void
157 gst_bin_pullregionfunc_proxy (GstPad *pad,
158                                 gulong offset,
159                                 gulong size)
160 {
161 //  region_struct region;
162   cothread_state *threadstate;
163     
164   DEBUG_ENTER("%s:%s,%ld,%ld",GST_DEBUG_PAD_NAME(pad),offset,size);
165       
166 //  region.offset = offset;
167 //  region.size = size;
168   
169 //  threadstate = GST_ELEMENT(pad->parent)->threadstate;
170 //  cothread_set_data (threadstate, "region", &region);
171   cothread_switch (threadstate);
172 //  cothread_set_data (threadstate, "region", NULL);
173 }
174
175
176 static void
177 gst_schedule_cothreaded_chain (GstBin *bin, _GstBinChain *chain) {
178   GList *elements;
179   GstElement *element;
180   cothread_func wrapper_function;
181   GList *pads;
182   GstPad *pad;
183
184   DEBUG("chain is using cothreads\n");
185
186   // first create thread context
187   if (bin->threadcontext == NULL) {
188     DEBUG("initializing cothread context\n");
189     bin->threadcontext = cothread_init ();   
190   }
191
192   // walk through all the chain's elements
193   elements = chain->elements;
194   while (elements) {
195     element = GST_ELEMENT (elements->data);
196     elements = g_list_next (elements);
197
198     // start out without a wrapper function, we select it later
199     wrapper_function = NULL;
200
201     // if the element has a loopfunc...
202     if (element->loopfunc != NULL) {
203       wrapper_function = GST_DEBUG_FUNCPTR(gst_bin_loopfunc_wrapper);
204       DEBUG("\nelement '%s' is a loop-based\n",gst_element_get_name(element));
205     } else {
206       // otherwise we need to decide what kind of cothread
207       // if it's not DECOUPLED, we decide based on whether it's a source or not
208       if (!GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) {
209         // if it doesn't have any sinks, it must be a source (duh)
210         if (element->numsinkpads == 0) {
211           wrapper_function = GST_DEBUG_FUNCPTR(gst_bin_src_wrapper);
212           DEBUG("\nelement '%s' is a source, using _src_wrapper\n",gst_element_get_name(element));
213         } else {
214           wrapper_function = GST_DEBUG_FUNCPTR(gst_bin_chain_wrapper);
215           DEBUG("\nelement '%s' is a filter, using _chain_wrapper\n",gst_element_get_name(element));
216         }
217       }
218     }
219
220     // now we have to walk through the pads to set up their state
221     pads = gst_element_get_pad_list (element);
222     while (pads) {
223       pad = GST_PAD (pads->data);
224       pads = g_list_next (pads);
225
226       // if the element is DECOUPLED or outside the manager, we have to chain
227       if ((wrapper_function == NULL) ||
228           (GST_ELEMENT(pad->peer->parent)->manager != GST_ELEMENT(bin))) {
229         // set the chain proxies
230         if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
231           DEBUG("copying chain function into push proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad));
232           pad->pushfunc = pad->chainfunc;
233         } else {
234           DEBUG("copying get function into pull proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad));
235           pad->pullfunc = pad->getfunc;
236           pad->pullregionfunc = pad->getregionfunc;
237         }
238
239       // otherwise we really are a cothread
240       } else {
241         if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
242           DEBUG("setting cothreaded push proxy for sinkpad %s:%s\n",GST_DEBUG_PAD_NAME(pad));
243           pad->pushfunc = GST_DEBUG_FUNCPTR(gst_bin_pushfunc_proxy);
244         } else {
245           DEBUG("setting cothreaded pull proxy for srcpad %s:%s\n",GST_DEBUG_PAD_NAME(pad));
246           pad->pullfunc = GST_DEBUG_FUNCPTR(gst_bin_pullfunc_proxy);
247         }
248       }
249     }
250
251     // need to set up the cothread now
252     if (wrapper_function != NULL) {
253       if (element->threadstate == NULL) {
254         element->threadstate = cothread_create (bin->threadcontext);
255         DEBUG("created cothread %p for '%s'\n",element->threadstate,gst_element_get_name(element));
256       }
257       cothread_setfunc (element->threadstate, wrapper_function, 0, (char **)element);
258       DEBUG("set wrapper function for '%s' to &%s\n",gst_element_get_name(element),
259             GST_DEBUG_FUNCPTR_NAME(wrapper_function));
260     }
261   }
262 }
263
264 static void
265 gst_schedule_chained_chain (GstBin *bin, _GstBinChain *chain) {
266   GList *elements;
267   GstElement *element;
268   GList *pads;
269   GstPad *pad;
270
271   DEBUG("chain entered\n");
272   // walk through all the elements
273   elements = chain->elements;
274   while (elements) {
275     element = GST_ELEMENT (elements->data);
276     elements = g_list_next (elements);
277
278     // walk through all the pads
279     pads = gst_element_get_pad_list (element);
280     while (pads) {
281       pad = GST_PAD (pads->data);
282       pads = g_list_next (pads);
283
284       if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
285         DEBUG("copying chain function into push proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad));
286         pad->pushfunc = pad->chainfunc; 
287       } else {
288         DEBUG("copying get function into pull proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad));
289         pad->pullfunc = pad->getfunc;
290         pad->pullregionfunc = pad->getregionfunc;
291       }
292     }
293   }
294 }
295
296 static void gst_bin_schedule_cleanup(GstBin *bin) {
297   GList *chains;
298   _GstBinChain *chain;
299
300   chains = bin->chains;
301   while (chains) {
302     chain = (_GstBinChain *)(chains->data);
303     chains = g_list_next(chains);
304
305     g_list_free(chain->elements);
306     g_list_free(chain->entries);
307
308     g_free(chain);
309   }
310   g_list_free(bin->chains);
311
312   bin->chains = NULL;
313 }
314
315 void gst_bin_schedule_func(GstBin *bin) {
316 //  GstElement *manager;
317   GList *elements;
318   GstElement *element;
319 //  const gchar *elementname;
320   GSList *pending = NULL;
321 //  GstBin *pending_bin;
322   GList *pads;
323   GstPad *pad;
324 //  GstElement *peer_manager;
325   GList *chains;
326   _GstBinChain *chain;
327
328   DEBUG_SET_STRING("(\"%s\")",gst_element_get_name (GST_ELEMENT (bin)));
329   DEBUG_ENTER_STRING;
330
331   gst_bin_schedule_cleanup(bin);
332
333   // next we have to find all the separate scheduling chains
334   DEBUG("\nattempting to find scheduling chains...\n");
335   // first make a copy of the managed_elements we can mess with
336   elements = g_list_copy (bin->managed_elements);
337   // we have to repeat until the list is empty to get all chains
338   while (elements) {
339     element = GST_ELEMENT (elements->data);
340
341     // if this is a DECOUPLED element
342     if (GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) {
343       // skip this element entirely
344       DEBUG("skipping '%s' because it's decoupled\n",gst_element_get_name(element));
345       elements = g_list_next (elements);
346       continue;
347     }
348
349     DEBUG("starting with element '%s'\n",gst_element_get_name(element));
350
351     // prime the pending list with the first element off the top
352     pending = g_slist_prepend (NULL, element);
353     // and remove that one from the main list
354     elements = g_list_remove (elements, element);
355
356     // create a chain structure
357     chain = g_new0 (_GstBinChain, 1);
358
359     // for each pending element, walk the pipeline
360     do {
361       // retrieve the top of the stack and pop it
362       element = GST_ELEMENT (pending->data);
363       pending = g_slist_remove (pending, element);
364
365       // add ourselves to the chain's list of elements
366       DEBUG("adding '%s' to chain\n",gst_element_get_name(element));
367       chain->elements = g_list_prepend (chain->elements, element);
368       chain->num_elements++;
369       // set the cothreads flag as appropriate
370       if (GST_FLAG_IS_SET (element, GST_ELEMENT_USE_COTHREAD))
371         chain->need_cothreads = TRUE;
372       if (bin->use_cothreads == TRUE)
373         chain->need_cothreads = TRUE;
374
375       // if we're managed by the current bin, and we're not decoupled,
376       // go find all the peers and add them to the list of elements to check
377       if ((element->manager == GST_ELEMENT(bin)) && 
378           !GST_FLAG_IS_SET (element, GST_ELEMENT_DECOUPLED)) {
379         // remove ourselves from the outer list of all managed elements
380 //        DEBUG("removing '%s' from list of possible elements\n",gst_element_get_name(element));
381         elements = g_list_remove (elements, element);
382
383         // if this element is a source, add it as an entry
384         if (element->numsinkpads == 0) {
385           chain->entries = g_list_prepend (chain->entries, element);
386           DEBUG("added '%s' as SRC entry into the chain\n",gst_element_get_name(element));
387         }
388
389         // now we have to walk the pads to find peers
390         pads = gst_element_get_pad_list (element);
391         while (pads) {
392           pad = GST_PAD (pads->data);
393           pads = g_list_next (pads);
394           DEBUG("have pad %s:%s\n",GST_DEBUG_PAD_NAME(pad));
395
396           DEBUG("peer pad %p\n", pad->peer);
397           // only bother with if the pad's peer's parent is this bin or it's DECOUPLED
398           // only add it if it's in the list of un-visited elements still
399           if ((g_list_find (elements, pad->peer->parent) != NULL) ||
400               GST_FLAG_IS_SET (pad->peer->parent, GST_ELEMENT_DECOUPLED)) {
401             // add the peer element to the pending list
402             DEBUG("adding '%s' to list of pending elements\n",gst_element_get_name(GST_ELEMENT(pad->peer->parent)));
403             pending = g_slist_prepend (pending, GST_ELEMENT(pad->peer->parent));
404
405             // if this is a sink pad, then the element on the other side is an entry
406             if ((gst_pad_get_direction (pad) == GST_PAD_SINK) &&
407                 (GST_FLAG_IS_SET (pad->peer->parent, GST_ELEMENT_DECOUPLED))) {
408               chain->entries = g_list_prepend (chain->entries, pad->peer->parent);
409               DEBUG("added '%s' as DECOUPLED entry into the chain\n",gst_element_get_name(GST_ELEMENT(pad->peer->parent))); 
410             }
411           } else
412             DEBUG("element '%s' has already been dealt with\n",gst_element_get_name(GST_ELEMENT(pad->peer->parent)));
413         }
414       }
415     } while (pending);
416
417     // add the chain to the bin
418     DEBUG("have chain with %d elements: ",chain->num_elements);
419     { GList *elements = chain->elements;
420       while (elements) {
421         element = GST_ELEMENT (elements->data);
422         elements = g_list_next(elements);
423         DEBUG_NOPREFIX("%s, ",gst_element_get_name(element));
424       }
425     }
426     DEBUG_NOPREFIX("\n");
427     bin->chains = g_list_prepend (bin->chains, chain);
428     bin->num_chains++;
429   }
430   // free up the list in case it's full of DECOUPLED elements
431   g_list_free (elements);
432
433   DEBUG("\nwe have %d chains to schedule\n",bin->num_chains);
434
435   // now we have to go through all the chains and schedule them
436   chains = bin->chains;
437   while (chains) {
438     chain = (_GstBinChain *)(chains->data);
439     chains = g_list_next (chains);
440
441     // schedule as appropriate
442     if (chain->need_cothreads) {
443       gst_schedule_cothreaded_chain (bin,chain);
444     } else {
445       gst_schedule_chained_chain (bin,chain);
446     }
447   }
448
449   DEBUG_LEAVE("(\"%s\")",gst_element_get_name(GST_ELEMENT(bin)));
450 }
451
452
453 /*
454         // ***** check for possible connections outside
455         // get the pad's peer
456         peer = gst_pad_get_peer (pad);
457         // FIXME this should be an error condition, if not disabled
458         if (!peer) break;
459         // get the parent of the peer of the pad
460         outside = GST_ELEMENT (gst_pad_get_parent (peer));
461         // FIXME this should *really* be an error condition
462         if (!outside) break;
463         // if it's a source or connection and it's not ours...
464         if ((GST_IS_SRC (outside) || GST_IS_CONNECTION (outside)) &&
465             (gst_object_get_parent (GST_OBJECT (outside)) != GST_OBJECT (bin))) {
466           if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
467             DEBUG("dealing with outside source element %s\n",gst_element_get_name(outside));
468 //            DEBUG("PUNT: copying pullfunc ptr from %s:%s to %s:%s (@ %p)\n",
469 //GST_DEBUG_PAD_NAME(pad->peer),GST_DEBUG_PAD_NAME(pad),&pad->pullfunc);
470 //            pad->pullfunc = pad->peer->pullfunc;
471 //            DEBUG("PUNT: setting pushfunc proxy to fake proxy on %s:%s\n",GST_DEBUG_PAD_NAME(pad->peer));
472 //            pad->peer->pushfunc = GST_DEBUG_FUNCPTR(gst_bin_pushfunc_fake_proxy);
473             pad->pullfunc = GST_DEBUG_FUNCPTR(gst_bin_pullfunc_proxy);
474           }
475         } else {
476 */
477
478
479
480
481
482 /*
483       } else if (GST_IS_SRC (element)) {
484         DEBUG("adding '%s' as entry point, because it's a source\n",gst_element_get_name (element));
485         bin->entries = g_list_prepend (bin->entries,element);
486         bin->num_entries++;
487         cothread_setfunc(element->threadstate,gst_bin_src_wrapper,0,(char **)element);
488       }
489
490       pads = gst_element_get_pad_list (element);
491       while (pads) {
492         pad = GST_PAD(pads->data);
493
494         if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
495           DEBUG("setting push proxy for sinkpad %s:%s\n",GST_DEBUG_PAD_NAME(pad));
496           // set the proxy functions
497           pad->pushfunc = GST_DEBUG_FUNCPTR(gst_bin_pushfunc_proxy);
498           DEBUG("pushfunc %p = gst_bin_pushfunc_proxy %p\n",&pad->pushfunc,gst_bin_pushfunc_proxy);
499         } else if (gst_pad_get_direction (pad) == GST_PAD_SRC) {
500           DEBUG("setting pull proxies for srcpad %s:%s\n",GST_DEBUG_PAD_NAME(pad));
501           // set the proxy functions
502           pad->pullfunc = GST_DEBUG_FUNCPTR(gst_bin_pullfunc_proxy);
503           DEBUG("pad->pullfunc(@%p) = gst_bin_pullfunc_proxy(@%p)\n",
504                 &pad->pullfunc,gst_bin_pullfunc_proxy);
505           pad->pullregionfunc = GST_DEBUG_FUNCPTR(gst_bin_pullregionfunc_proxy);
506         }
507         pads = g_list_next (pads);
508       }
509       elements = g_list_next (elements);
510
511       // if there are no entries, we have to pick one at random
512       if (bin->num_entries == 0)
513         bin->entries = g_list_prepend (bin->entries, GST_ELEMENT(bin->children->data));
514     }
515   } else {
516     DEBUG("don't need cothreads, looking for entry points\n");
517     // we have to find which elements will drive an iteration
518     elements = bin->children;
519     while (elements) {
520       element = GST_ELEMENT (elements->data);
521       DEBUG("found element \"%s\"\n", gst_element_get_name (element));
522       if (GST_IS_BIN (element)) {
523         gst_bin_create_plan (GST_BIN (element));
524       }
525       if (GST_IS_SRC (element)) {
526         DEBUG("adding '%s' as entry point, because it's a source\n",gst_element_get_name (element));
527         bin->entries = g_list_prepend (bin->entries, element);
528         bin->num_entries++;
529       }
530
531       // go through all the pads, set pointers, and check for connections
532       pads = gst_element_get_pad_list (element);
533       while (pads) {
534         pad = GST_PAD (pads->data);
535
536         if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
537           DEBUG("found SINK pad %s:%s\n", GST_DEBUG_PAD_NAME(pad));
538
539           // copy the peer's chain function, easy enough
540           DEBUG("copying peer's chainfunc to %s:%s's pushfunc\n",GST_DEBUG_PAD_NAME(pad));
541           pad->pushfunc = GST_DEBUG_FUNCPTR(pad->peer->chainfunc);
542
543           // need to walk through and check for outside connections
544 //FIXME need to do this for all pads
545           // get the pad's peer
546           peer = gst_pad_get_peer (pad);
547           if (!peer) {
548             DEBUG("found SINK pad %s has no peer\n", gst_pad_get_name (pad));
549             break;
550           }
551           // get the parent of the peer of the pad
552           outside = GST_ELEMENT (gst_pad_get_parent (peer));
553           if (!outside) break;
554           // if it's a connection and it's not ours...
555           if (GST_IS_CONNECTION (outside) &&
556                (gst_object_get_parent (GST_OBJECT (outside)) != GST_OBJECT (bin))) {
557             gst_info("gstbin: element \"%s\" is the external source Connection "
558                                     "for internal element \"%s\"\n",
559                           gst_element_get_name (GST_ELEMENT (outside)),
560                           gst_element_get_name (GST_ELEMENT (element)));
561             bin->entries = g_list_prepend (bin->entries, outside);
562             bin->num_entries++;
563           }
564         }
565         else {
566           DEBUG("found pad %s\n", gst_pad_get_name (pad));
567         }
568         pads = g_list_next (pads);
569
570       }
571       elements = g_list_next (elements);
572     }
573 */
574
575
576
577
578 /*
579   // If cothreads are needed, we need to not only find elements but
580   // set up cothread states and various proxy functions.
581   if (bin->need_cothreads) {
582     DEBUG("bin is using cothreads\n");
583
584     // first create thread context
585     if (bin->threadcontext == NULL) {
586       DEBUG("initializing cothread context\n");
587       bin->threadcontext = cothread_init ();
588     }
589
590     // walk through all the children
591     elements = bin->managed_elements;
592     while (elements) {
593       element = GST_ELEMENT (elements->data);
594       elements = g_list_next (elements);
595
596       // start out with a NULL warpper function, we'll set it if we want a cothread
597       wrapper_function = NULL;
598
599       // have to decide if we need to or can use a cothreads, and if so which wrapper
600       // first of all, if there's a loopfunc, the decision's already made
601       if (element->loopfunc != NULL) {
602         wrapper_function = GST_DEBUG_FUNCPTR(gst_bin_loopfunc_wrapper);
603         DEBUG("element %s is a loopfunc, must use a cothread\n",gst_element_get_name(element));
604       } else {
605         // otherwise we need to decide if it needs a cothread
606         // if it's complex, or cothreads are preferred and it's *not* decoupled, cothread it
607         if (GST_FLAG_IS_SET (element,GST_ELEMENT_COMPLEX) ||
608             (GST_FLAG_IS_SET (bin,GST_BIN_FLAG_PREFER_COTHREADS) &&
609              !GST_FLAG_IS_SET (element,GST_ELEMENT_DECOUPLED))) {
610           // base it on whether we're going to loop through source or sink pads
611           if (element->numsinkpads == 0)
612             wrapper_function = GST_DEBUG_FUNCPTR(gst_bin_src_wrapper);
613           else
614             wrapper_function = GST_DEBUG_FUNCPTR(gst_bin_chain_wrapper);
615         }
616       }
617
618       // walk through the all the pads for this element, setting proxy functions
619       // the selection of proxy functions depends on whether we're in a cothread or not
620       pads = gst_element_get_pad_list (element);
621       while (pads) {
622         pad = GST_PAD (pads->data);
623         pads = g_list_next (pads);
624
625         // check to see if someone else gets to set up the element
626         peer_manager = GST_ELEMENT((pad)->peer->parent)->manager;
627         if (peer_manager != GST_ELEMENT(bin)) {
628           DEBUG("WARNING: pad %s:%s is connected outside of bin\n",GST_DEBUG_PAD_NAME(pad));
629         }
630
631         // if the wrapper_function is set, we need to use the proxy functions
632         if (wrapper_function != NULL) {
633           // set up proxy functions
634           if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
635             DEBUG("setting push proxy for sinkpad %s:%s\n",GST_DEBUG_PAD_NAME(pad));
636             pad->pushfunc = GST_DEBUG_FUNCPTR(gst_bin_pushfunc_proxy);
637           } else if (gst_pad_get_direction (pad) == GST_PAD_SRC) {
638             DEBUG("setting pull proxy for srcpad %s:%s\n",GST_DEBUG_PAD_NAME(pad));
639             pad->pullfunc = GST_DEBUG_FUNCPTR(gst_bin_pullfunc_proxy);
640           }
641         } else {
642           // otherwise we need to set up for 'traditional' chaining
643           if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
644             // we can just copy the chain function, since it shares the prototype
645             DEBUG("copying chain function into push proxy for %s:%s\n",
646                   GST_DEBUG_PAD_NAME(pad));
647             pad->pushfunc = pad->chainfunc;
648           } else if (gst_pad_get_direction (pad) == GST_PAD_SRC) {
649             // we can just copy the get function, since it shares the prototype
650             DEBUG("copying get function into pull proxy for %s:%s\n",
651                   GST_DEBUG_PAD_NAME(pad));
652             pad->pullfunc = pad->getfunc;
653           }
654         }
655       }
656
657       // if a loopfunc has been specified, create and set up a cothread
658       if (wrapper_function != NULL) {
659         if (element->threadstate == NULL) {
660           element->threadstate = cothread_create (bin->threadcontext);
661           DEBUG("created cothread %p (@%p) for \"%s\"\n",element->threadstate,
662                 &element->threadstate,gst_element_get_name(element));
663         }
664         cothread_setfunc (element->threadstate, wrapper_function, 0, (char **)element);
665         DEBUG("set wrapper function for \"%s\" to &%s\n",gst_element_get_name(element),
666               GST_DEBUG_FUNCPTR_NAME(wrapper_function));
667       }
668
669 //      // HACK: if the element isn't decoupled, it's an entry
670 //      if (!GST_FLAG_IS_SET(element,GST_ELEMENT_DECOUPLED))
671 //        bin->entries = g_list_append(bin->entries, element);
672     }
673
674   // otherwise, cothreads are not needed
675   } else {
676     DEBUG("bin is chained, no cothreads needed\n");
677
678     elements = bin->managed_elements;
679     while (elements) {
680       element = GST_ELEMENT (elements->data);
681       elements = g_list_next (elements);
682
683       pads = gst_element_get_pad_list (element);
684       while (pads) {
685         pad = GST_PAD (pads->data);
686         pads = g_list_next (pads);
687
688         if (gst_pad_get_direction (pad) == GST_PAD_SINK) {
689           DEBUG("copying chain function into push proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad));
690           pad->pushfunc = pad->chainfunc;
691         } else {
692           DEBUG("copying get function into pull proxy for %s:%s\n",GST_DEBUG_PAD_NAME(pad));
693           pad->pullfunc = pad->getfunc;
694         }
695       }
696     }
697   }
698 */
699
700