mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-03 16:09:39 +00:00
93d0c4b2e7
Original commit message from CVS: Better write ideas down
537 lines
14 KiB
Text
537 lines
14 KiB
Text
Element types
|
|
-------------
|
|
|
|
SOURCES
|
|
-------
|
|
|
|
* never chain-based
|
|
* have no sinkpads
|
|
|
|
1) get based src
|
|
|
|
(----------)
|
|
! fakesrc !
|
|
! src- (get based)
|
|
(----------)
|
|
|
|
* no sinkpads
|
|
* srcpad(s) that are get-based
|
|
|
|
2) loop based src
|
|
|
|
(----------)
|
|
! fakesrc !
|
|
! src- (loop based)
|
|
(----------)
|
|
|
|
* no sinkpads
|
|
* element is loop-based
|
|
* data is pushed on the srcpad(s)
|
|
|
|
FILTERS
|
|
-------
|
|
|
|
3) chain based filter
|
|
|
|
(----------)
|
|
! identity !
|
|
-sink src-
|
|
(----------)
|
|
|
|
* sinkpad(s) have a chain function
|
|
* srcpad(s) push data
|
|
|
|
4) loop-based filter
|
|
|
|
(----------)
|
|
! identity !
|
|
-sink src-
|
|
(----------)
|
|
|
|
* element is loop-based
|
|
* data is pushed on the srcpads
|
|
* data is pulled from sinkpad(s)
|
|
|
|
|
|
SINKS
|
|
-----
|
|
|
|
5) chain based sink
|
|
|
|
(----------)
|
|
! fakesink !
|
|
-sink !
|
|
(----------)
|
|
|
|
* sinkpad(s) have a chain function
|
|
* no srcpads
|
|
|
|
6) loop-based sink
|
|
|
|
(----------)
|
|
! fakesink !
|
|
-sink !
|
|
(----------)
|
|
|
|
* element is loop-based
|
|
* data is pulled from sinkpad(s)
|
|
|
|
|
|
DECOUPLED
|
|
---------
|
|
|
|
7) decoupled element
|
|
|
|
|
|
(----------)
|
|
! queue !
|
|
-sink src-
|
|
(----------)
|
|
|
|
* sinkpad(s) have chain function
|
|
* srcpad(s) have get function
|
|
* never loop-based
|
|
* always acts like a chain-based sink for upstream elements
|
|
* always acts like a get-based src for downstream elements
|
|
* are not added to a group, but marked as an entry point in
|
|
case it acts as a src element
|
|
|
|
|
|
Connection types
|
|
----------------
|
|
|
|
1) get based src
|
|
2) loop based src
|
|
3) chain based filter
|
|
4) loop-based filter
|
|
5) chain based sink
|
|
6) loop-based sink
|
|
7) decoupled element
|
|
|
|
|
|
! 1 ! 2 ! 3 ! 4 ! 5 ! 6 ! 7 !
|
|
---+-----+-----+-----+-----+-----+-----+-----+
|
|
1 ! X ! X ! A ! C ! A ! C ! A !
|
|
! ! ! ! ! ! ! !
|
|
2 ! X ! X ! B ! F ! B ! F ! B !
|
|
! ! ! ! ! ! ! !
|
|
3 ! X ! X ! D ! E ! D ! E ! D !
|
|
! ! ! ! ! ! ! !
|
|
4 ! X ! X ! B ! F ! B ! F ! B !
|
|
! ! ! ! ! ! ! !
|
|
5 ! X ! X ! X ! X ! X ! X ! X !
|
|
! ! ! ! ! ! ! !
|
|
6 ! X ! X ! X ! X ! X ! X ! X !
|
|
! ! ! ! ! ! ! !
|
|
7 ! X ! X ! A ! C ! A ! C ! X !
|
|
! ! ! ! ! ! ! !
|
|
|
|
|
|
A)
|
|
|
|
src -> sink
|
|
src -> filter
|
|
src -> decoupled
|
|
decoupled -> sink
|
|
decoupled -> filter
|
|
|
|
- get based source
|
|
- chain based sink
|
|
|
|
* one group
|
|
* src at start of group and entry point
|
|
* _get from src, push to sink
|
|
|
|
(-group1---------------)
|
|
! !
|
|
! *fakesrc -> fakesink !
|
|
(----------------------)
|
|
|
|
|
|
B)
|
|
|
|
src -> sink
|
|
src -> filter
|
|
src -> decoupled
|
|
filter -> sink
|
|
filter -> filter
|
|
filter -> decoupled
|
|
|
|
- loop based source/filter
|
|
- chain based sink/filter/decoupled
|
|
|
|
* one group
|
|
* src/filter at start of group and entry point
|
|
* loop on src, chainhandler set to chain function
|
|
|
|
(-group1----------------)
|
|
! !
|
|
! %*fakesrc -> fakesink !
|
|
(-----------------------)
|
|
|
|
|
|
C)
|
|
|
|
src -> sink
|
|
src -> filter
|
|
decoupled -> sink
|
|
decoupled -> filter
|
|
|
|
- get based source/decoupled
|
|
- loop based sink/filter
|
|
|
|
* one group
|
|
* loop based element is entry point
|
|
* loop on sink/filter, gethandler set to getfunction
|
|
|
|
(-group1----------------)
|
|
! !
|
|
! fakesrc -> %*fakesink !
|
|
(-----------------------)
|
|
|
|
D)
|
|
|
|
filter -> filter
|
|
filter -> sink
|
|
filter -> decoupled
|
|
|
|
- chain based filter
|
|
- chain based filter/sink/decoupled
|
|
|
|
* one group is created to hold the two elements
|
|
* no entry point
|
|
* chainhandler set to peer chainfunction
|
|
|
|
(-group1----------------)
|
|
! !
|
|
! identity -> identity !
|
|
(-----------------------)
|
|
|
|
E)
|
|
|
|
filter -> filter
|
|
filter -> sink
|
|
|
|
- chain based filter
|
|
- loop based filter/sink
|
|
|
|
* two groups
|
|
* group is created for src element if needed
|
|
* chainhandler of loop based element set to loop wrapper, control is
|
|
handed to the peer group
|
|
* gethandler of loop based element set to get wrapper
|
|
|
|
(-group1---) (-group2------)
|
|
! ! ! !
|
|
! identity ---> %*identity !
|
|
(----------) (-------------)
|
|
|
|
|
|
F)
|
|
|
|
src -> filter
|
|
src -> sink
|
|
filter -> filter
|
|
filter -> sink
|
|
|
|
- loop based filter/src
|
|
- loop based filter/sink
|
|
|
|
* two groups
|
|
* two entry points
|
|
* chainhandler set to loop wrapper
|
|
* gethandler set to get wrapper
|
|
|
|
(-group1-----) (-group2------)
|
|
! ! ! !
|
|
! %*identity ---> %*identity !
|
|
(------------) (-------------)
|
|
|
|
|
|
Grouping
|
|
--------
|
|
|
|
* a group has at most one loop based element
|
|
* elements in a group are sorted, src elements first (not mandatory)
|
|
* a group has one cothread
|
|
* a group is created immediatly for loop based elements, all other elements
|
|
are added to a group when a pad connection is made
|
|
* get-based plugins are put in the same group as a peer loop based element
|
|
* chain based elements are put in the same group as sink peer elements
|
|
* entry point in the group is:
|
|
- loopbased element
|
|
- first src element if no loopbased element exists in the group
|
|
|
|
Result: you end up with a group of connected elements with either:
|
|
- a loop based plugin as the entry point
|
|
- a get based plugin as the entry point
|
|
|
|
Scheduling the group is a matter of starting the cothread and calling
|
|
the loop function or doing a _get/_push on a srcpad.
|
|
|
|
|
|
other examples of groups:
|
|
-------------------------
|
|
|
|
% = loop based
|
|
* = entry point of group
|
|
|
|
.
|
|
(-group1---------------)
|
|
! !
|
|
! *fakesrc -> fakesink !
|
|
(----------------------)
|
|
|
|
.
|
|
(-group1---------------------------------------)
|
|
! !
|
|
! *fakesrc -> identity -> identity -> fakesink !
|
|
(----------------------------------------------)
|
|
|
|
.
|
|
(-group1-----------------------------------------)
|
|
! !
|
|
! fakesrc -> %*identity -> identity -> fakesink !
|
|
(------------------------------------------------)
|
|
|
|
.
|
|
(-group1---------------) (-group2-----------------)
|
|
! ! ! !
|
|
! *fakesrc -> identity --> *%identity -> fakesink !
|
|
(----------------------) (------------------------)
|
|
|
|
.
|
|
(-group1------------------------------------)
|
|
! !
|
|
! *fakesrc -> tee --> identity -> fakesink !
|
|
! --> identity -> fakesink !
|
|
(-------------------------------------------)
|
|
|
|
.
|
|
(-group1------------------------------------)
|
|
! !
|
|
! *fakesrc -> tee --> identity -> fakesink !
|
|
(--------------!----------------------------)
|
|
v
|
|
(-group2-----------------)
|
|
! !
|
|
! *%identity -> fakesink !
|
|
(------------------------)
|
|
|
|
.
|
|
(-group1----------) (-group2-----------------)
|
|
! ! ! !
|
|
! *fakesrc -> tee --> *%identity -> fakesink !
|
|
(--------------!--) (------------------------)
|
|
v
|
|
(-group3-----------------)
|
|
! !
|
|
! *%identity -> fakesink !
|
|
(------------------------)
|
|
|
|
.
|
|
(-group1-----------------------) (-group2----------------------)
|
|
! ! ! !
|
|
! filesrc -> *%mpegdemux --> queue* -> mpeg2dec -> xvideosink !
|
|
! ! (-----------------------------)
|
|
! ! (-group3----------------------)
|
|
! ! ! !
|
|
! --> queue* -> mad -> osssink !
|
|
(------------------------------) (-----------------------------)
|
|
|
|
|
|
Chaining
|
|
--------
|
|
|
|
* groups that are connected end up in the same chain
|
|
* a group always belongs to a chain
|
|
* updating the chain is only needed when two groups are
|
|
connected with a connection of type E/F. for other
|
|
connection types, the group itself is updated.
|
|
* a chain is scheduled by scheduling a random group in the chain.
|
|
|
|
|
|
Wrapper functions
|
|
-----------------
|
|
|
|
|
|
|
|
iterating without cothreads
|
|
---------------------------
|
|
|
|
A cothread for each group is the easiest way to schedule most of
|
|
the pipelines. Some pipelines are however schedulable without
|
|
any cothreads.
|
|
|
|
Each group is schedulable without cothreads, one can call the
|
|
group schedule function and be done with it. Problems arise
|
|
one the group boundaries of connected elements, which are always
|
|
of type E and F (chain->loop, loop->loop)
|
|
|
|
We always have a producer group and a provider group in this case.
|
|
|
|
chain->loop
|
|
-----------
|
|
|
|
|
|
|
|
Scheduler algorithm
|
|
-------------------
|
|
|
|
1: select (random?) group in chain
|
|
2: schedule group
|
|
3: on E/F connections, the get/chain wrapper is called
|
|
- get wrapper puts the peer element on the runqueue and
|
|
recursively invokes the scheduler.
|
|
- chain wrapper puts the buffer in the bufpen and puts
|
|
the peer element in the runqueue
|
|
4: when the group is scheduled, take group from the runqueue
|
|
and goto 2:
|
|
5: no more groups on the runqueue, iteration ends
|
|
|
|
|
|
NOTES:
|
|
|
|
- We need a GList instead of a single bufpen to hold buffers
|
|
for multi-out elements.
|
|
|
|
- We probably need to set a limit on the maximum number of
|
|
recursions and size of the bufpen list.
|
|
|
|
- elements run non-preemptively, a group is done scheduling when all
|
|
elements in that group complete their chain/loop function.
|
|
|
|
- can we only have a stack overflow when there is a loop in the
|
|
pipeline? I think so.
|
|
|
|
- putting groups twice on the runqueue is not a good idea, we
|
|
need to check a flag or something, maybe give the group a
|
|
higher priority?
|
|
|
|
- what about starvation? We'll probably have to put the group
|
|
at the end of the runqueue.
|
|
|
|
- multi-out elements can introduce significant latency. consider:
|
|
|
|
(-group1----------) (-group2-----------------)
|
|
! ! ! !
|
|
! *filesrc -> mad --> *%identity -> osssink !
|
|
(-----------------) (------------------------)
|
|
|
|
mad produces N output buffers, they are queued in the bufpen, when
|
|
group1 is done, group2 is scheduled to empty the bufpen queue.
|
|
|
|
The time it takes for the first buffer to arrive at osssink equals
|
|
the time needed to decode the N-1 other buffers.
|
|
|
|
Of course, given the right buffersize for filesrc, N can be reduced
|
|
to 1. mad can also suggest a buffersize to filesrc with the
|
|
BUFFER_SIZE event.
|
|
|
|
|
|
|
|
Ungrouping
|
|
----------
|
|
|
|
Ungrouping might be the hardest part. especially in the case where
|
|
an element is still running (state changes and pipeline modifications
|
|
in element callbacks).
|
|
|
|
Ungrouping involves two tasks:
|
|
|
|
- breaking up groups. This can happen when a pad connection is broken
|
|
so that the group contains two clusters of unconnected elements.
|
|
- breaking up chains. This happens when a pad connection is broken
|
|
so that the chains contains two clusters of unconnected groups.
|
|
|
|
|
|
case1
|
|
-----
|
|
|
|
The most simple case is where two elements are disconnected and one
|
|
of the elements does not belong to a group. In this case, we don't need
|
|
to do anything at all.
|
|
|
|
case2
|
|
-----
|
|
|
|
if the elements are part of different groups, we need to check if the
|
|
chain needs to be broken. A chain is broken if it contains two sets
|
|
of unconnected groups.
|
|
|
|
To test this case, we will create a new chain and recursively move
|
|
one of the groups with all of its connected groups to it. If the
|
|
origial chain is empty after this operation, there was still a connection
|
|
and the new chain replaces the old one, else we end up with two chains.
|
|
|
|
case3
|
|
-----
|
|
|
|
When the elements are part of the same group we check if both elements
|
|
still have a connection to some other element in that group. The elements
|
|
without connections are removed from the group.
|
|
|
|
It is possible that when an element still has a connection with some other
|
|
element in the group, the group has to be split up anyway. This can happen
|
|
in fakesrc ! indentity ! identity ! fakesink when we break the connection
|
|
between the two identity elements. We have to be careful here in the cothread
|
|
case that we don't take away the running cothread from under the elements.
|
|
In the non-cothread case we can just move the elements to another new group.
|
|
|
|
|
|
Interrupt with cothreadless optimal scheduler
|
|
---------------------------------------------
|
|
|
|
Interrupts are usually performed when a blocking _get based source or
|
|
decoupled element is unlocked for a state change. The idea of the
|
|
interrupt scheduler call is to return to the main execution stack frame
|
|
ASAP so that the state change can take place. For cothread based
|
|
implementations of the scheduler this is not a problem as one can jump
|
|
to the main cothread context without problems. For non cothread based
|
|
schedulers we need to follow this scheme:
|
|
|
|
get <-> chain based connection.
|
|
|
|
- enter get group scheduler
|
|
- call _get function on source, this can block internally
|
|
- app performs state change, element is told to unlock itself
|
|
- lock inside the get based function unlocks and _get function
|
|
return GST_EVENT_INTERRUPT
|
|
- group scheduler notices the event, unrefs it and jumps out of the
|
|
get_group_scheduler function, a scheduler INTERRUPTED flag is set.
|
|
|
|
get <-> loop based connection
|
|
|
|
- enter loop group scheduler
|
|
- loop based element does pull on sinkpad
|
|
- _get blocks
|
|
- app performs state change, _get is unlocked and returns
|
|
GST_EVENT_INTERRUPT.
|
|
- loop based function receives INTERRUPT event and exits its loop
|
|
ASAP using gst_element_interrupt.
|
|
|
|
loop/chain <-> loop based connection
|
|
|
|
- when returning from the recursive call to the scheduler, the state
|
|
of the scheduler is checked, if it was interrupted, a
|
|
GST_EVENT_INTERRUPTED event is returned to the loop based element.
|
|
- the loop based element exits its loop ASAP.
|
|
|
|
|
|
This technique will unwind the stack of scheduled groups ASAP and returns
|
|
to the main execution stack frame where the iterate() function can return
|
|
and the state change can take place.
|
|
|
|
Another alternative could be implemented when the _push and _pull functions
|
|
would return a result code...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|