| A new concept in scheduling is that of chains of elements that need to be schedule separately, even in the same set of |
| managed elements (which is the set of elements that the Bin in question [a pipeline or thread] is responsible for). |
| An example would by anywhere you have a non-blocking queue in place for buffering. This kind of element might be |
| useful in cases where the scheduling on a buffer level is tight enough that deadlocks might occur. |
| |
| The scheduler will find chains by traversing the pipeline through the list of managed elements. A chain boundary is |
| anywhere you have a 'DECOUPLED' element. A DECOUPLED element is one where there is no direct correlation between the |
| activities of the various pads. A source fits this description, although the normal single-pad source is the |
| degenerate case. A queue more properly fits the bill, since pushing a buffer at the sink pad doesn't trigger anything |
| on the src pad, and vice versa. A multi-src async source is probably the best example, since you want to leave the |
| scheduling up to the elements connected to it. |
| |
| Anyway, first the simple case: |
| |
| fakesrc -> fakesink |
| |
| Both of them should probably have the DECOUPLED bit set, at least to be true to the nature of the actual fake elements. |
| These two end up being a chain, and scheduling has to be set up for the chain. There are no cothreaded elements in the |
| chain, which means it's relatively easy. The goal is to find a single entry into the chain, which can be called in a |
| loop to get things done. Since the fakesrc is DECOUPLED, and we'd be messing with the source pad, it has lower |
| priority than a DECOUPLED sink pad, so the fakesrc's sink pad is the ideal entry into the chain. This can be |
| simplified into saying that the fakesink is the entry. |
| |
| In the end, the code to do this boils down to: |
| |
| buf = gst_pad_pull (fakesink->sinkpad); |
| gst_pad_push (fakesink->sinkpad, buf); |
| |
| Because of the way things are no implemented for scheduling, turning it around and making the source the entry has no |
| effect as far as the efficiency. That's because _get no longer directly calls gst_pad_push(), so we have to do it |
| outside. No big deal, it boils down to the same thing I think, modulo a cache-line of stack (i.e. one or two fewer |
| this way). |
| |
| If we put an identity in the middle: |
| |
| fakesrc -> identity -> fakesink |
| |
| then we have the same thing, except that there's now an element that isn't DECOUPLED, so it gets higher priority. That |
| means the identity is now the entry, and when we push the buffer into its chain function, the fakesink gets called. |
| |
| Now, we can make this much more complex, with the following elementary echo meta-filter: |
| |
| |=====| -> delay1 -> |=====| |
| | | | | |
| -> queue -> | tee | -> delay2 -> | mix | -> queue -> |
| | | | | |
| |=====| -> delay3 -> |=====| |
| |
| The tee takes a buffer in and spits three out, delay shifts the timestamps around and possibly reframes things to be |
| friendly. mix takes the three buffers and simply sums them (they're all audio). The tee element takes one buffer in |
| and promptly spits three out, one after another. Delay takes an element and immediately spits out a buffer (it |
| zero-pads at the beginning [the duration of the delay] for the sake of argument). Mix in this case is chained, but |
| assumes that buffer will arrive in order. On the last chain, it does a push of the newly mixed audio buffer. |
| |
| The queues are both DECOUPLED, so they have lower weight. That leaves a bunch of other elements sitting there ripe for |
| entry. But if we were to take delay1, what would happen? Well we can't, since there's no _get function on the tee's |
| src pads. |
| |
| This just re-enforces the idea that the left-most (closest to the source, for you right-to-left people) element should |
| get to be the entry. But what if we have multiple left-most elements?: |
| |
| -> queue -> eq1 -> |=====| |
| | mix | -> queue |
| -> queue -> eq2 -> |=====| |
| |
| If eq1 is the only entry, we call pull on the queue, then chain over to mix. Mix then doesn't do anything with it, |
| since it's waiting for another buffer before doing anything. That means we have to do the same with eq2, and have it |
| chain to mix, at which point mix will do its magic and chain out to the right-hand side. Figure out to actually use |
| both entries is hard, because the idea at this point is that there's only a single entry to a chain. |
| |
| Does this mean that we should make mix a DECOUPLED element? That would fix it to some extent, giving us three chains |
| in the above case. Each eq chain would be driven by the eq element, pulling from the queue and pushing into the mixer. |
| The mixer -> queue chain is problematic, because there is no possibly entry. The mixer side has no _get function |
| (since the push always happens upon the receipt of a buffer from the second sink pad), which means that those two |
| pads have no possible entrance. |
| |
| Cothreads make this case much easier, since the mix element would drive things, forcing the eq elements to pull and |
| process buffers in order as needed. It may be that the best option in the case where there are any multi-sinkpad |
| elements is to turn them into cothreads. |
| |
| |
| Now, on to cothreaded cases. The simplest possible is to turn all the elements into cothreads. I may punt on this and |
| do just that for the moment, but there's still the question of what to do at the ends of the chain, where the DECOUPLED |
| elements are. The easiest is to simply always make then chained, so there's never any worry about who owns the |
| cothread context for the element, simply because there never will be one. |
| |
| fakesrc -> queue -> @identity -> fakesink |
| |
| We just set it up so both ends of the queue are chained, and all is well. |