mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-12 12:21:30 +00:00
85 lines
4.9 KiB
Text
85 lines
4.9 KiB
Text
|
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.
|