| OUTDATED |
| -------- |
| |
| Plan generation happens at transition from NULL to READY (and PLAYING to READY right now, need to fix |
| that). By way of some logic in gst_bin_change_state(), gst_bin_create_plan() is only called for the |
| outer Bin, usually a Pipeline. This keeps things from getting nasty later on. |
| |
| A major new concept in plan generation is that of the 'manager'. This is the element that is reponsible |
| for running a given element. In general, Pipelines and Threads are the only managing-capable elements |
| (have the MANAGER flag set), since they are the only ones with real scheduling authority (because they |
| have a process context to play with, basically). |
| |
| gst_bin_set_manager() is called to set the manager element of the bin and all it's children and their |
| children. However, there's one important trick: it won't recurse into child Bins that have the MANAGER |
| flag set. This avoids some highly redundant recursion. |
| |
| When create_plan() is called on the outside Pipeline, the first thing it does is call |
| set_manager(self,self). As noted above, this recursion will not proceed into child Bins that have the |
| MANAGER flag set. |
| |
| The next step is to recursively generate the plan (yes, head-recursive). This gives child Bins the |
| opportunity to generate their plan first, causing a inside-to-outside sequence. This matches the way |
| the scheduling is arranged now, where the plan for a Src/Connection outside a Bin is handled by that |
| Bin, not it's parent. But we must be very careful not to stomp on that plan in the parent Bin. |
| |
| Because create_plan() is called on all Bins, but we can only set up scheduling state in MANAGER bins, |
| create_plan() must perform create_plan() recursion, but not do anything else *unless* the MANAGER bit is |
| set. It shouldn't even call set_manager() unless it's a MANAGER itself, because calling it otherwise |
| would waste time doing the work again. Basically, from the standpoing of setting the manager, |
| create_plan() recursion starts it when the current Bin is a MANAGER, and set_manager() stops when it |
| finds the next one. create_plan()'s further recursion eventually starts the process back up again |
| furtuer down the hierarchy, until everything is covered. |
| |
| For all MANAGER Bins, the last step is to actually create the scheduling plan. This is still one of the |
| nastiest chunks of code in the whole project, and probably will do nothing but get worse from now on (it |
| got better recently, but only because I took a chainsaw to the code and broke everthing...). It will |
| remain similar to what it is now, but with some definite differences. |
| |
| First task is now to find all the elements that we're responsible for. This is normally a recursive |
| process, because the structure is an arbitrary tree. However, something like the following should work |
| (bin is self): |
| |
| GSList *elements = NULL; |
| GList *children; |
| GSList *waiting_bins = NULL; |
| GstBin *waiting_bin; |
| |
| waiting_bins = g_slist_prepend (waiting_bins,bin); |
| |
| while (waiting_bins) { |
| // retrieve the top of the stack and pop it |
| waiting_bin = GST_BIN (waiting_bins->data); |
| waiting_bins = g_slist_remove (waiting_bins,waiting_bin); |
| |
| // walk the list of elements, and find bins |
| children = waiting_bin->children; |
| while (children) { |
| // add it to the list of elements |
| elements = g_slist_prepend (elements, children->data); |
| |
| // if it's a bin and it's not a managing bin, |
| // shove it on the list of bins to recurse into |
| if (GST_IS_BIN (children->data) && |
| !GST_FLAG_IS_SET (GST_ELEMENT (children->data))) |
| waiting_bins = g_slist_prepend (waiting_bins,children->data); |
| |
| children = g_list_next (children); |
| } |
| } |
| |
| The code makes the assumption that the manager of every element is the same until such time as a |
| different managing parent appears in the hierarchy. This is the result of the aforementioned nested |
| recursion of create_plan() and set_manager(), but may not remain the case forever. The above loop |
| should probably be slightly re-written to work solely on whether or not the Bin in question is the |
| element's manager. This means that the child Bins are *always* recursed into, in case there's a rogue |
| element inside of one of them that's supposed to be managed. |
| |
| At the same time all the elements managed by this bin are found (i.e. in the inner loop), we can |
| determine some useful bits of information, such as testing for several cases that require the use of |
| cothreads. The availability of manager information at this point may aid significantly in this |
| decision. |
| |
| Finally, the scheduling plan is generated, based on all the elements to be managed by the Bin (the list |
| of which may span several 'generations' of Bins and elements). Elements which have peers in child |
| self-managed Bins are left alone on for the pad in that makes that connection. This should keep the |
| parent Bins from stepping all over state set up by the child Bins, by establishing clear implicit |
| ownership on the pad level, based on the managing Bins' relationship to the pad. |