mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
Added docs with proposals for major rewrite for 0.9.
Original commit message from CVS: Added docs with proposals for major rewrite for 0.9.
This commit is contained in:
parent
a9bad8aa6e
commit
1d4aeab404
3 changed files with 1089 additions and 0 deletions
328
docs/random/wtay/negotiation3
Normal file
328
docs/random/wtay/negotiation3
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
Caps Negotiation
|
||||||
|
================
|
||||||
|
|
||||||
|
|
||||||
|
Definitions
|
||||||
|
-----------
|
||||||
|
|
||||||
|
- GstCaps is a structure that holds a mimetype and a set of properties. The properties are
|
||||||
|
either fixed or non-fixed, they contain ranges or lists.
|
||||||
|
|
||||||
|
- filter caps: caps set by the application to constrain a link to a specific GstCaps.
|
||||||
|
|
||||||
|
- allowed caps: caps calculated when connecting elements, these are the types that are
|
||||||
|
possible on this connection. If no types are possible, the link is refused.
|
||||||
|
|
||||||
|
- pad template caps: caps put on padtemplates to describe the possible media types of
|
||||||
|
the pad.
|
||||||
|
|
||||||
|
- pad caps: the caps of the pad as it is currently negotiated.
|
||||||
|
|
||||||
|
|
||||||
|
General overview
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Caps negotiation is the process of two pads agreeing on a common fixed GstCaps structure
|
||||||
|
that describes the media type that will be exchanged between those pads.
|
||||||
|
|
||||||
|
We define that caps negotiation only happens in the PLAYING state, when there is actual
|
||||||
|
dataflow and elements thus know what kind of data they are dealing with, or when
|
||||||
|
linking elements.
|
||||||
|
|
||||||
|
Caps negotiation is first performed when linking elements. If the elements can agree
|
||||||
|
on a media type at link time then negotiation will not have to be performed at runtime.
|
||||||
|
Usually this is not possible, so the pads remain unnegotiated until at runtime.
|
||||||
|
|
||||||
|
Caps negotiation is initiated by an element that wants to send data on a non-negotiated
|
||||||
|
pad, thus the source pad. The core only provides a policy and convenience methods to aid
|
||||||
|
the element in performing the negotiation. The core does not keep track of what pads
|
||||||
|
are negotiated or not, this is a task of the pads.
|
||||||
|
|
||||||
|
Caps negotiation is normally started by the source element right before it sends out
|
||||||
|
data (case 1).
|
||||||
|
Caps negotiation can also be redone by a sink element that wants to receive another
|
||||||
|
format from a downstream element (case 2).
|
||||||
|
|
||||||
|
There are two functions handling the negotiation, the link function and the do_link
|
||||||
|
function. The link function is always called first and must eventually call do_link on
|
||||||
|
the peer pad. The link function must figure out a compatible format for the connection
|
||||||
|
and then call the do_link function on the peer pad. The do_link function can only
|
||||||
|
accept or refuse the provided caps.
|
||||||
|
|
||||||
|
For autopluggers it is important to know when the pad is ready to start the negotiation.
|
||||||
|
It is also inportant to know when the negotiation failed and it must be possible to
|
||||||
|
restart the negotiation with another element. This functionality will be provided
|
||||||
|
with signals.
|
||||||
|
|
||||||
|
Pad functions
|
||||||
|
-------------
|
||||||
|
|
||||||
|
!
|
||||||
|
! const GstCaps* gst_pad_iterate_caps (GstPad *pad, gint position);
|
||||||
|
!
|
||||||
|
Returns the nth possible caps that describes the media type that can flow over
|
||||||
|
this pad. This function should not return static caps but caps that are
|
||||||
|
dependent of the media type and the peer connections of the element.
|
||||||
|
|
||||||
|
This function can be called at any time so that an autoplugger can know the
|
||||||
|
exact types of the pads at any time.
|
||||||
|
|
||||||
|
!
|
||||||
|
! gboolean gst_pad_accept_caps (GstPad *pad, GstCaps *caps);
|
||||||
|
!
|
||||||
|
Checks that a given caps is acceptable for this pad. Returns FALSE if the
|
||||||
|
pad cannot handle the caps.
|
||||||
|
|
||||||
|
!
|
||||||
|
! gboolean gst_pad_link (GstPad *pad, GstPad *peer, GstCaps *caps);
|
||||||
|
!
|
||||||
|
Tells the pad to start negotiation with the given filtercaps. The
|
||||||
|
caps need not be fixed and serves as a filter for performing the negotiation
|
||||||
|
with the peerpad.
|
||||||
|
|
||||||
|
!
|
||||||
|
! gboolean gst_pad_do_link (GstPad *pad, GstPad *peer, GstCaps *caps);
|
||||||
|
!
|
||||||
|
Configures the pad to accept data with the given caps. The caps must be fixed.
|
||||||
|
|
||||||
|
!
|
||||||
|
! const GstCaps* gst_pad_get_negotiated_caps (GstPad *pad);
|
||||||
|
!
|
||||||
|
Get the negotiated caps of a pad or NULL if the pad is not negotiated.
|
||||||
|
|
||||||
|
|
||||||
|
Linking Elements
|
||||||
|
----------------
|
||||||
|
|
||||||
|
When linking elements with the gst_pad_link function, the core will call
|
||||||
|
the link function on the srcpad. If that pad returns GST_PAD_LINK_OK,
|
||||||
|
the link is established, else the link fails.
|
||||||
|
|
||||||
|
Since the link function of the pad will call the do_link function of
|
||||||
|
the peerpad, the caps will be set on both pads.
|
||||||
|
|
||||||
|
It is not required to decide on a caps at link time, a plugin can choose to
|
||||||
|
dynamically renegotiate at runtime.
|
||||||
|
|
||||||
|
When a link fails, the core emits a signal link_failed, which the application
|
||||||
|
can catch to try to establish a new link with another element, for example.
|
||||||
|
|
||||||
|
!
|
||||||
|
! def gst_pad_link_filtered (pad1, pad2, filtercaps):
|
||||||
|
!
|
||||||
|
! ... get srcpad and sinkpad ...
|
||||||
|
! srcpad = (pad1 == GST_PAD_SRC ? pad1 : pad2);
|
||||||
|
! sinkpad = (pad1 == GST_PAD_SINK ? pad1 : pad2);
|
||||||
|
!
|
||||||
|
! ... more checks to see if the pads are of different types and
|
||||||
|
! ... that they live in the same scheduler etc...
|
||||||
|
!
|
||||||
|
! res = gst_pad_link (srcpad, sinkpad, filtercaps)
|
||||||
|
! if (res == GST_PAD_LINK_OK)
|
||||||
|
! ... perform other setup ...
|
||||||
|
! else
|
||||||
|
! res = signal_emit (srcpad, "link_failed")
|
||||||
|
!
|
||||||
|
! return res
|
||||||
|
!
|
||||||
|
|
||||||
|
Pad link is just a convenience function that passes a NULL filtercaps:
|
||||||
|
!
|
||||||
|
! def gst_pad_link (pad1, pad2):
|
||||||
|
! gst_pad_link_filtered (pad1, pad2, NULL)
|
||||||
|
!
|
||||||
|
|
||||||
|
|
||||||
|
Dynamic renegotiation
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Dynamic renegotiation happens at runtime when the element knows the exact media
|
||||||
|
type it is handling.
|
||||||
|
|
||||||
|
The element that wants to redecide on the data type of a connection just calls
|
||||||
|
gst_pad_relink on one of it's pads. The core will call unlink and link on the
|
||||||
|
pads again as if this were a new connection. Since the iterate_caps function
|
||||||
|
returns other values while linking, the new link will renegotiate to a new
|
||||||
|
format or the link will fail.
|
||||||
|
|
||||||
|
Prototype of the relink function:
|
||||||
|
!
|
||||||
|
! def gst_pad_relink_filtered (pad, filtercaps):
|
||||||
|
! gst_pad_unlink (pad, peerpad)
|
||||||
|
! gst_pad_link_filtered (pad, peerpad, filtercaps)
|
||||||
|
!
|
||||||
|
|
||||||
|
Again the relink function is a convenience function for not having to pass
|
||||||
|
filtercaps.
|
||||||
|
!
|
||||||
|
! def gst_pad_relink (pad):
|
||||||
|
! gst_pad_relink_filtered (pad, NULL)
|
||||||
|
!
|
||||||
|
|
||||||
|
The core, however, should optimize the relink function so that it does a minimal
|
||||||
|
amount of work, like not informing the scheduler about the unlink/link call in
|
||||||
|
case of success.
|
||||||
|
|
||||||
|
Error recovery
|
||||||
|
--------------
|
||||||
|
|
||||||
|
When a pad is ready to negotiate and it has no peer pad, it fires the "need-link"
|
||||||
|
signal. Autopluggers can use this signal to start plugging elements to the pad.
|
||||||
|
|
||||||
|
When a link fails because of a renegotiation, the "link-failed" signal is fired
|
||||||
|
so that autopluggers can try other elements.
|
||||||
|
|
||||||
|
|
||||||
|
Default implementations
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
!
|
||||||
|
! gst_pad_src_link_func (pad, peerpad, filtercaps)
|
||||||
|
! {
|
||||||
|
! srcpad->negotiated_caps = NULL;
|
||||||
|
! gboolean possible = FALSE;
|
||||||
|
!
|
||||||
|
! for (i=0; peercaps = gst_pad_iterate_caps (peerpad, i); i++)
|
||||||
|
! {
|
||||||
|
! for (j=0; srccaps = gst_pad_iterate_caps (srcpad, j); j++)
|
||||||
|
! {
|
||||||
|
! test = gst_caps_intersect (srccaps, peercaps, filtercaps);
|
||||||
|
! if (test == GST_CAPS_EMPTY)
|
||||||
|
! continue;
|
||||||
|
!
|
||||||
|
! /* non empty caps, something is possible */
|
||||||
|
! possible = TRUE;
|
||||||
|
!
|
||||||
|
! if (!gst_caps_is_fixed (caps))
|
||||||
|
! continue;
|
||||||
|
!
|
||||||
|
! if (gst_pad_accepts_caps (peerpad, test))
|
||||||
|
! {
|
||||||
|
! if (gst_pad_do_link (peerpad, srcpad, test))
|
||||||
|
! {
|
||||||
|
! srcpad->negotiated_caps = test;
|
||||||
|
! goto done;
|
||||||
|
! }
|
||||||
|
! else
|
||||||
|
! {
|
||||||
|
! /* weird, it accepted but did not want to link */
|
||||||
|
! }
|
||||||
|
! }
|
||||||
|
! else
|
||||||
|
! {
|
||||||
|
! /* caps are not accepted by peer, try next */
|
||||||
|
! }
|
||||||
|
! }
|
||||||
|
! }
|
||||||
|
! done:
|
||||||
|
! if (!possible)
|
||||||
|
! return GST_PAD_LINK_FAILED;
|
||||||
|
! else
|
||||||
|
! return GST_PAD_LINK_OK;
|
||||||
|
! }
|
||||||
|
!
|
||||||
|
|
||||||
|
gst_pad_iterate_caps returns caps in the following order:
|
||||||
|
|
||||||
|
1) prefered caps (if any)
|
||||||
|
2) negotiated caps (if any)
|
||||||
|
3) profile caps (if any, filtered against caps of current media type)
|
||||||
|
4) padtemplate caps (filtered against caps of current media type)
|
||||||
|
|
||||||
|
1 = a caps describing the media type that would result in optimal
|
||||||
|
processing of the current media type
|
||||||
|
2 = a caps that it is currently handling
|
||||||
|
3 = a caps describing the user configured default values
|
||||||
|
4 = a caps describing all other possibilities that this pad can
|
||||||
|
produce or consume given the current media type if any.
|
||||||
|
|
||||||
|
|
||||||
|
generic flow 1:
|
||||||
|
|
||||||
|
Linkage
|
||||||
|
|
||||||
|
src sink
|
||||||
|
| |
|
||||||
|
| <--- link(sink, A) |
|
||||||
|
check if | |
|
||||||
|
A is ok | |
|
||||||
|
or filter | |
|
||||||
|
against | |
|
||||||
|
allowed | |
|
||||||
|
src caps | |
|
||||||
|
| iterate_caps |
|
||||||
|
if A not +-------------------------> |
|
||||||
|
fixed | |
|
||||||
|
| <-------------------------+
|
||||||
|
filter | |
|
||||||
|
against A | |
|
||||||
|
to get A' | |
|
||||||
|
| can_accept(A') |
|
||||||
|
if A' fix +-------------------------> |
|
||||||
|
| | check if A' is ok
|
||||||
|
| yes |
|
||||||
|
| <-------------------------+
|
||||||
|
| |
|
||||||
|
if A' not | |
|
||||||
|
fixed, | |
|
||||||
|
A'=null | |
|
||||||
|
| do_link(src, A') |
|
||||||
|
+-------------------------> |
|
||||||
|
| ok | store src, A'
|
||||||
|
| <-------------------------+
|
||||||
|
store | |
|
||||||
|
sink, A' | |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Unlink
|
||||||
|
|
||||||
|
src sink
|
||||||
|
| |
|
||||||
|
| <--- unlink() |
|
||||||
|
| |
|
||||||
|
| unlink() |
|
||||||
|
+-------------------------> |
|
||||||
|
unref | | unref src, format
|
||||||
|
sink, fmt | |
|
||||||
|
|
||||||
|
|
||||||
|
Dynamic negotiation of B
|
||||||
|
|
||||||
|
src sink
|
||||||
|
| can_accept(B) |
|
||||||
|
+-------------------------> |
|
||||||
|
| | check if B is ok
|
||||||
|
| yes |
|
||||||
|
| <-------------------------+
|
||||||
|
| |
|
||||||
|
| do_link(B) |
|
||||||
|
+-------------------------> |
|
||||||
|
| ok | store B
|
||||||
|
| <-------------------------+
|
||||||
|
store | |
|
||||||
|
B | |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
TODO
|
||||||
|
-----
|
||||||
|
|
||||||
|
Write test objects to simulate different behaviour.
|
||||||
|
|
||||||
|
|
||||||
|
tests (in python):
|
||||||
|
|
||||||
|
vts -> colorspace -> ximagsink
|
||||||
|
|
||||||
|
relink with different colorspaces while running
|
||||||
|
|
||||||
|
|
||||||
|
vts -> videoscale -> ximagesink
|
||||||
|
|
||||||
|
relink to different sizes while running, scaling vts or ximagesink.
|
||||||
|
|
||||||
|
sinesrc -> audiorate -> audiosink
|
||||||
|
|
||||||
|
where audiosink only has one possible fixed caps.
|
||||||
|
|
85
docs/random/wtay/performance
Normal file
85
docs/random/wtay/performance
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
Fixing Performance
|
||||||
|
------------------
|
||||||
|
|
||||||
|
1) Observations
|
||||||
|
|
||||||
|
The following observations are made when considering the current (17/11/2004)
|
||||||
|
problems in gstreamer.
|
||||||
|
|
||||||
|
- startup time.
|
||||||
|
Most of the time is spend in reading the xml registry. Several methods exist
|
||||||
|
for not using a registry (see testsuite/plugin/).
|
||||||
|
Since registries are pluggable, one can also write a binary registry which
|
||||||
|
should significantly reduce the size and the load time.
|
||||||
|
|
||||||
|
- Performance in state changes
|
||||||
|
State changes are pretty heavy currently because negotiation happens at
|
||||||
|
each state change. The PAUSED->PLAYING state change in particular is
|
||||||
|
too heavy and should be made more 'snappy'.
|
||||||
|
|
||||||
|
- Performance in data passing.
|
||||||
|
Too much checking is done in the push/pull functions. Most of these checks
|
||||||
|
can be performed more efficiently in the plugins, like checking if the
|
||||||
|
element is sufficiently negotiated. The discont event 'invent' hack used
|
||||||
|
for fixing the synchronisation has to go away.
|
||||||
|
|
||||||
|
We also propose a get_range based method for pads so that random-access
|
||||||
|
in file-based sources can happen more efficiently (typical for muxers).
|
||||||
|
|
||||||
|
- Scheduling overhead.
|
||||||
|
The current default opt scheduler has extensive data-structures and does
|
||||||
|
uses fairly complicated algorithms for grouping and scheduling elements.
|
||||||
|
This has performance impact on pipeline constructions and pipeline
|
||||||
|
execution.
|
||||||
|
|
||||||
|
- Events in dataflow.
|
||||||
|
Because events are put inside the dataflow, a typical chain function has
|
||||||
|
to check if the GstData object is an event of a buffer.
|
||||||
|
|
||||||
|
|
||||||
|
2) Proposed solutions
|
||||||
|
|
||||||
|
- startup time.
|
||||||
|
Volunteers to implement a binary registry?
|
||||||
|
|
||||||
|
- The changes listed in the separate document 'threading' will greatly
|
||||||
|
reduce the number of negotiation steps during state changes. It will
|
||||||
|
also reduce the latency between PAUSED<->PLAYING since it basically
|
||||||
|
involves unlocking a mutex in the sinks.
|
||||||
|
|
||||||
|
- adding return values to push/pull will offload some checks to the plugins
|
||||||
|
that can more efficiently and more accuratly handle most error cases.
|
||||||
|
With the new sync model in 'threading' it is also not needed to have
|
||||||
|
the 'invented' events in the pull function.
|
||||||
|
|
||||||
|
- The following scheduling policies should be made possible for pads:
|
||||||
|
|
||||||
|
1) get_range <-> pull-range-based
|
||||||
|
2) get <-> pull based
|
||||||
|
3) push <-> chain-based
|
||||||
|
|
||||||
|
The scheduling policies are listed in order of preference. A pad should
|
||||||
|
export what scheduling it supports and the core will select what
|
||||||
|
scheduling method to use. It is possible for a pad to support more than
|
||||||
|
one scheduling method.
|
||||||
|
|
||||||
|
It is possible that two elements cannot connect when they do not support
|
||||||
|
compatible scheduling policies. We require that pull-based pads also
|
||||||
|
support the chain based methods, at minimal, using a helper function to
|
||||||
|
queue a buffer and that get-based pads also support a push based implementation.
|
||||||
|
|
||||||
|
- With the changes listed in 'threading' the scheduler will be a lot more
|
||||||
|
simple since it does not have to keep track of groups and connected
|
||||||
|
elements. Starting and scheduling the pipeline will be a matter of
|
||||||
|
starting the source/loop GstTasks and they will from there on take
|
||||||
|
over.
|
||||||
|
|
||||||
|
- move events out of the dataflow. Different methods will be used to
|
||||||
|
send/receive events. This will also remove some of the checks in
|
||||||
|
the push/pull functions. Note that plugins are still able to serialize
|
||||||
|
buffers and events because they hold the streaming lock.
|
||||||
|
|
||||||
|
3) detailed explanation
|
||||||
|
|
||||||
|
TO BE WRITTEN
|
||||||
|
|
676
docs/random/wtay/threading
Normal file
676
docs/random/wtay/threading
Normal file
|
@ -0,0 +1,676 @@
|
||||||
|
Fixing Threading
|
||||||
|
----------------
|
||||||
|
|
||||||
|
1) Observations
|
||||||
|
|
||||||
|
The following observations are made when considering the current (17/11/2004)
|
||||||
|
problems in gstreamer.
|
||||||
|
|
||||||
|
- Bin state changes.
|
||||||
|
Currently the state of a bin is determined by the highest state of the
|
||||||
|
children, This is in particular a problem for GstThread because a thread
|
||||||
|
should start/stop spinning at any time depending on the state of a child.
|
||||||
|
|
||||||
|
ex 1:
|
||||||
|
|
||||||
|
+-------------------------------------+
|
||||||
|
| GstThread |
|
||||||
|
| +--------+ +---------+ +------+ |
|
||||||
|
| | src | | decoder | | sink | |
|
||||||
|
| | src-sink src-sink | |
|
||||||
|
| +--------+ +---------+ +------+ |
|
||||||
|
+-------------------------------------+
|
||||||
|
|
||||||
|
When performing the state change on the GstThread to PLAYING, one of the
|
||||||
|
children (at random) will go to PLAYING first, this will trigger a method
|
||||||
|
in GstThread that will start spinning the thread. Some elements are not yet
|
||||||
|
in the PLAYING state when the scheduler starts iterating elements. This
|
||||||
|
is not a clean way to start the data passing.
|
||||||
|
|
||||||
|
State changes also trigger negotiation and scheduling (in the other thread)
|
||||||
|
can do too. This creates races in negotiation.
|
||||||
|
|
||||||
|
- ERROR and EOS conditions triggering a state change
|
||||||
|
|
||||||
|
A typical problem is also that since scheduling starts while the state change
|
||||||
|
happens, it is possible that the elements go to EOS or ERROR before the
|
||||||
|
state change completes. Currently this makes the elements go to PAUSED again,
|
||||||
|
creating races with the state change in progress. This also gives the
|
||||||
|
impression to the core that the state change failed.
|
||||||
|
|
||||||
|
- no locking whatsoever
|
||||||
|
|
||||||
|
When an element does a state change, it is possible for another thread to
|
||||||
|
perform a conflicting state change.
|
||||||
|
|
||||||
|
- negotiation is not designed to work over multithread boundaries.
|
||||||
|
|
||||||
|
negotiation over a queue is not possible. There is no method or policy of
|
||||||
|
discovering a media type and then commiting it. It is also not possible to
|
||||||
|
tie the negotiated media to the relevant buffer.
|
||||||
|
|
||||||
|
ex1:
|
||||||
|
it Should be possible to queue the old and the new formats in a queue.
|
||||||
|
The element connected to the sinkpad of the queue should be able to
|
||||||
|
find out that the new format will be accepted by the element connected
|
||||||
|
on the srcpad of the queue, even if that element is streaming the old
|
||||||
|
format.
|
||||||
|
|
||||||
|
+------------------------------+
|
||||||
|
| GstQueue |
|
||||||
|
| +++++++++++++++++++++++++ |
|
||||||
|
-sink |B|B|B|B|B|B|A|A|A|A|A|A| src-
|
||||||
|
| +++++++++++++++++++++++++ |
|
||||||
|
+------------------------------+
|
||||||
|
+----------+ +----------+
|
||||||
|
buffers in buffers in
|
||||||
|
new format old format
|
||||||
|
|
||||||
|
- element properties are not threadsafe
|
||||||
|
|
||||||
|
When setting an element property while streaming, the element does no
|
||||||
|
locking whatsoever to guarantee its internal consistency.
|
||||||
|
|
||||||
|
- No control over streaming.
|
||||||
|
|
||||||
|
When some GstThread is iterating and you want to reconnect a pad, there
|
||||||
|
is no way to block the pad, perform the actions and then unblock it
|
||||||
|
again. This leads to thread problems where a pad is negotiation at the
|
||||||
|
same time that it is passing data.
|
||||||
|
|
||||||
|
This is currently solved by PAUSING the pipeline or performing the actions
|
||||||
|
in the same threadcontext as the iterate loop.
|
||||||
|
|
||||||
|
- race conditions in synchronizing the clocks and spinning up the pipeline.
|
||||||
|
Currently the clock is started as soon as the pipeline is set to playing.
|
||||||
|
Because some time elaspes before the elements are negotiated, autoplugged
|
||||||
|
and streaming, the first frame/sample almost always arrives late at the
|
||||||
|
sinks. Hacks exist to adjust the element base time to compensate for the
|
||||||
|
delay but this clearly is not clean.
|
||||||
|
|
||||||
|
- race conditions when performing seeks in the pipeline. Since the elements
|
||||||
|
have no control over the streaming threads, they cannot block them or
|
||||||
|
resync them to the new seek position. It is also hard to synchronize them
|
||||||
|
with the clock.
|
||||||
|
|
||||||
|
- race conditions when sending tags and error messages up the pipeline
|
||||||
|
hierarchy. These races are either caused by glib refcounting problems and
|
||||||
|
by not properly locking.
|
||||||
|
|
||||||
|
- more as changes are implemented and testcases are written
|
||||||
|
|
||||||
|
2) possible solutions
|
||||||
|
|
||||||
|
- not allowing threading at all
|
||||||
|
|
||||||
|
Run the complete pipeline in a single thread. Complications to solve include
|
||||||
|
handling of blocking operations like source elements blocking in kernel
|
||||||
|
space, sink elements blocking on the clock or kernel space, etc.. In practice,
|
||||||
|
all operations should be made non-blocking because a blocking element can
|
||||||
|
cause the rest of the pipeline to block as well and cause it to miss a deadline.
|
||||||
|
A non-blocking model needs cooperation from the kernel (with callbacks) or
|
||||||
|
requires the use of a polling mechanism, both of which are either impractical
|
||||||
|
or too CPU intensive and in general not achievable for a general purpose
|
||||||
|
Multimedia framework. For this reason we will not go further with this
|
||||||
|
solution.
|
||||||
|
|
||||||
|
- Allow threading.
|
||||||
|
|
||||||
|
To make this work, We propose the following changes:
|
||||||
|
|
||||||
|
- Remove GstThread, it does not add anything useful in a sense that you cannot
|
||||||
|
arbitrarily place the thread element, it needs decoupled elements around the
|
||||||
|
borders.
|
||||||
|
|
||||||
|
- Simplify the state changes of bins elements. A bin or element never changes
|
||||||
|
state automatically on EOS and ERROR.
|
||||||
|
|
||||||
|
- Introduce the concept of the application and the streaming thread. All data
|
||||||
|
passing is done in the streaming thread. This also means that all operations
|
||||||
|
either are performed in the application thread or streaming thread and that
|
||||||
|
they should be protected against competing operations in other threads.
|
||||||
|
This would define a policy for adding appropriate locking.
|
||||||
|
|
||||||
|
- Move the creation of threads into source and loop-based elements. This will
|
||||||
|
make it possible for the elements in control of the threads to perform the
|
||||||
|
locking when needed. One particular instance is for example the state changes,
|
||||||
|
by creating the threads in the element, it is possible to sync the streaming
|
||||||
|
and the application thread (which does the state change).
|
||||||
|
|
||||||
|
- Remove negotiation from state changes. This will remove the conflict between
|
||||||
|
streaming and negotiating elements.
|
||||||
|
|
||||||
|
- add locks around pad operations like negotiation, streaming, linking, etc. This
|
||||||
|
will remove races between these conflicting operations. This will also make it
|
||||||
|
possible to un/block dataflow.
|
||||||
|
|
||||||
|
- add locks around bin operations like add/removing elements.
|
||||||
|
|
||||||
|
- add locks around element operations like state changes and property changes.
|
||||||
|
|
||||||
|
- add a 2-phase directed negotiation process. The source pad queries and figures
|
||||||
|
out what the sinkpad can take in the first phase. In the second phase it sends
|
||||||
|
the new format change as an event to the peer element. This event can be
|
||||||
|
interleaved with the buffers and can travel over queues inbetween the buffers.
|
||||||
|
Need to rethink this wrt bufferpools (see DShow and old bufferpool implementation)
|
||||||
|
|
||||||
|
- add a preroll phase that will be used to spin up the pipeline and align frames/samples
|
||||||
|
in the sinks. This phase will happen in the PAUSED state. This also means that
|
||||||
|
dataflow will happen in the PAUSED state. Sinks will not sink samples in the PAUSED
|
||||||
|
state but will complete their state change asynchronously. This will allow
|
||||||
|
us to have perfect synchronisation with the clock.
|
||||||
|
|
||||||
|
- a two phase seek policy. First the event travels upstream, putting all elements in
|
||||||
|
the seeking phase and making them synchronize to the new position. In the
|
||||||
|
second phase the DISCONT event signals the end of the seek and all filters can
|
||||||
|
continue with the new position.
|
||||||
|
|
||||||
|
- Error messages, EOS, tags and other events in the pipeline should be sent to a
|
||||||
|
mainloop. The app then has an in-thread mechanism for getting information about
|
||||||
|
the pipeline. It should also be possible to get the messages directly from the
|
||||||
|
elements itself, like signals. The application programmer has to know that
|
||||||
|
working these events come from another thread and should handle them accordingly.
|
||||||
|
|
||||||
|
- Add return values to push/pull so that errors upstream or downstream can be noted
|
||||||
|
by other elements so that they can disable themselves or propagate the error.
|
||||||
|
|
||||||
|
|
||||||
|
3) detailed explanation
|
||||||
|
|
||||||
|
a) Pipeline construction
|
||||||
|
|
||||||
|
Pipeline construction includes:
|
||||||
|
|
||||||
|
- adding/removing elements to the bin
|
||||||
|
- finding elements in a bin by name and interface
|
||||||
|
- setting the clock on a pipeline.
|
||||||
|
- setting properties on objects
|
||||||
|
- linking/unlinking pads
|
||||||
|
|
||||||
|
These operations should take the object lock to make sure it can be
|
||||||
|
executed from different threads.
|
||||||
|
|
||||||
|
When connecting pads to other pads from elements inside another bin,
|
||||||
|
we require that the bin has a ghostpad for the pad. This is needed so
|
||||||
|
that the bin looks like a self-contained element.
|
||||||
|
|
||||||
|
not allowed:
|
||||||
|
+---------------------+
|
||||||
|
| GstBin |
|
||||||
|
+---------+ | +--------+ |
|
||||||
|
| element | | | src | |
|
||||||
|
sink src------sink src- ... |
|
||||||
|
+---------+ | +--------+ |
|
||||||
|
+---------------------+
|
||||||
|
|
||||||
|
allowed:
|
||||||
|
+-----------------------+
|
||||||
|
| GstBin |
|
||||||
|
| +--------+ |
|
||||||
|
+---------+ | | src | |
|
||||||
|
| element | | sink src- ... |
|
||||||
|
sink src---sink/ +--------+ |
|
||||||
|
+---------+ +-----------------------+
|
||||||
|
|
||||||
|
This requirement is important when we need to sort the elements in the
|
||||||
|
bin to perfrom the state change.
|
||||||
|
|
||||||
|
|
||||||
|
testcases:
|
||||||
|
|
||||||
|
- create a bin, add/remove elements from it
|
||||||
|
- add/remove from different threads and check the bin integrity.
|
||||||
|
|
||||||
|
b) state changes
|
||||||
|
|
||||||
|
An element can be in one of the four states NULL, READY, PAUSED, PLAYING.
|
||||||
|
|
||||||
|
NULL: starting state of the element
|
||||||
|
READY: element is ready to start running.
|
||||||
|
PAUSED: element is streaming data, has opened devices etc.
|
||||||
|
PLAYING: element is streaming data and clock is running
|
||||||
|
|
||||||
|
Note that data starts streaming even in the PAUSED state. The only difference
|
||||||
|
between the PAUSED and PLAYING state is that the clock is running in the
|
||||||
|
PLAYING state. This mostly has an effect on the renderers which will block on
|
||||||
|
the first sample they receive when in PAUSED mode. The transition from
|
||||||
|
READY->PAUSED is called the preroll state. During that transition, media is
|
||||||
|
queued in the pipeline and autoplugging is done.
|
||||||
|
|
||||||
|
Elements are put in a new state using the _set_state function. This function
|
||||||
|
can return the following return values:
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GST_STATE_FAILURE = 0,
|
||||||
|
GST_STATE_PARTIAL = 1,
|
||||||
|
GST_STATE_SUCCESS = 2,
|
||||||
|
GST_STATE_ASYNC = 3
|
||||||
|
} GstElementStateReturn;
|
||||||
|
|
||||||
|
GST_STATE_FAILURE is returned when the element failed to go to the
|
||||||
|
required state. When dealing with a bin, this is returned when one
|
||||||
|
of the elements failed to go to the required state. The other elements
|
||||||
|
in the bin might have changed their states succesfully. This return
|
||||||
|
value means that the element did _not_ change state, for bins this
|
||||||
|
means that not all children have changed their state.
|
||||||
|
|
||||||
|
GST_STATE_PARTIAL is returned when some elements in a bin where in the
|
||||||
|
locked state and therefore did not change their state. Note that the
|
||||||
|
state of the bin will be changed regardless of this PARTIAL return value.
|
||||||
|
|
||||||
|
GST_STATE_SUCCES is returned when all the elements successfully changed their
|
||||||
|
states.
|
||||||
|
|
||||||
|
GST_STATE_ASYNC is returned when an element is going to report the success
|
||||||
|
or failure of a state change later.
|
||||||
|
|
||||||
|
The state of a bin is not related to the state of its children but only to
|
||||||
|
the last state change directly performed on the bin or on a parent bin. This
|
||||||
|
means that changing the state of an element inside the bin does not affect
|
||||||
|
the state of the bin.
|
||||||
|
|
||||||
|
Setting the state on a bin that is already in the correct state will
|
||||||
|
perform the requested state change on the children.
|
||||||
|
|
||||||
|
Elements are not allowed to change their own state. For bins, it is allowed
|
||||||
|
to change the state of its children. This means that the application
|
||||||
|
can only know about the states of the elements it has explicitly set.
|
||||||
|
|
||||||
|
There is a difference in the way a pipeline and a bin handles the state
|
||||||
|
change of its children:
|
||||||
|
|
||||||
|
- a bin returns GST_STATE_ASYNC when one of its children returns an
|
||||||
|
ASYNC reply.
|
||||||
|
|
||||||
|
- a pipeline never returns GST_STATE_ASYNC but returns from the state
|
||||||
|
change function after all ASYNC elements completed the state change.
|
||||||
|
This is done by polling the ASYNC elements until they return their
|
||||||
|
final state.
|
||||||
|
|
||||||
|
The state change function must be fast an cannot block. If a blocking behaviour
|
||||||
|
is unavoidable, the state change function must perform an async state change.
|
||||||
|
Sink elements therefore always use async state changes since they need to
|
||||||
|
wait before the first buffer arrives at the sink.
|
||||||
|
|
||||||
|
A bin has to change the state of its children elements from the sink to the
|
||||||
|
source elements. This makes sure that a sink element is always ready to
|
||||||
|
receive data from a source element in the case of a READY->PAUSED state change.
|
||||||
|
In the case of a PAUSED->READY state, the sink element will be set to READY
|
||||||
|
first so that the source element will receive an error when it tries to push
|
||||||
|
data to this element so that it will shut down as well.
|
||||||
|
|
||||||
|
For loop based elements we have to be careful since they can pull a buffer
|
||||||
|
from the peer element before it has been put in the right state.
|
||||||
|
The state of a loop based element is therefore only changed after the source
|
||||||
|
element has been put in the new state.
|
||||||
|
|
||||||
|
c) Element state change functions
|
||||||
|
|
||||||
|
The core will call the change_state function of an element with the element
|
||||||
|
lock held. The element is responsible for starting any streaming tasks/threads
|
||||||
|
and making sure that it synchronizes them to the state change function if
|
||||||
|
needed.
|
||||||
|
|
||||||
|
This means that no other thread is allowed to change the state of the element
|
||||||
|
at that time and for bins, it is not possible to add/remove elements.
|
||||||
|
|
||||||
|
When an element is busy doing the ASYNC state change, it is possible that another
|
||||||
|
state change happens. The elements should be prepared for this.
|
||||||
|
|
||||||
|
An element can receive a state change for the same state it is in. This
|
||||||
|
is not a problem, some elements (like bins) use this to resynchronize their
|
||||||
|
children. Other elements should ignore this state change and return SUCCESS.
|
||||||
|
|
||||||
|
When performing a state change on an element that returns ASYNC on one of
|
||||||
|
the state changes, ASYNC is returned and you can only proceed to the next
|
||||||
|
state change change when this ASYNC state change completed. Use the
|
||||||
|
gst_element_get_state function to know when the state change completed.
|
||||||
|
An example of this behaviour is setting a videosink to PLAYING, it will
|
||||||
|
return ASYNC in the state change from READY->PAUSED. You can only set
|
||||||
|
it to PLAYING when this state change completes.
|
||||||
|
|
||||||
|
Bins will perform the state change code listed in d).
|
||||||
|
|
||||||
|
For performing the state change, two variables are used: the current state
|
||||||
|
of the element and the pending state. When the element is not performing a
|
||||||
|
state change, the pending state == None. The state change variables are
|
||||||
|
protected by the element lock. The pending state != None as long as the
|
||||||
|
state change is performed or when an ASYNC state change is running.
|
||||||
|
|
||||||
|
The core provides the following function for applications and bins to
|
||||||
|
get the current state of an element:
|
||||||
|
|
||||||
|
bool gst_element_get_state(&state, &pending, timeout);
|
||||||
|
|
||||||
|
This function will block while the state change function is running inside
|
||||||
|
the element because it grabs the element lock.
|
||||||
|
When the element did not perform an async state change, this function returns
|
||||||
|
TRUE immediatly with the state updated to reflect the current state of the
|
||||||
|
element and pending set to None.
|
||||||
|
When the element performed an async state change, this function will block
|
||||||
|
for the value of timeout and will return TRUE if the element completed the
|
||||||
|
async state change within that timeout, otherwise it returns FALSE, with
|
||||||
|
the current and pending state filled in.
|
||||||
|
|
||||||
|
The algorithm is like this:
|
||||||
|
|
||||||
|
bool gst_element_get_state(elem, &state, &pending, timeout)
|
||||||
|
{
|
||||||
|
g_mutex_lock (ELEMENT_LOCK);
|
||||||
|
if (elem->pending != none) {
|
||||||
|
if (!g_mutex_cond_wait(STATE, ELEMENT_LOCK, timeout) {
|
||||||
|
/* timeout triggered */
|
||||||
|
*state = elem->state;
|
||||||
|
*pending = elem->pending;
|
||||||
|
ret = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (elem->pending == none) {
|
||||||
|
*state = elem->state;
|
||||||
|
*pending = none;
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
g_mutex_unlock (ELEMENT_LOCK);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
For plugins the following function is provided to commit the pending state,
|
||||||
|
the ELEMENT_LOCK should be held when calling this function:
|
||||||
|
|
||||||
|
gst_element_commit_state(element)
|
||||||
|
{
|
||||||
|
if (pending != none) {
|
||||||
|
state = pending;
|
||||||
|
pending = none;
|
||||||
|
}
|
||||||
|
g_cond_broadcast (STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
For bins the gst_element_get_state() works slightly different. It will run
|
||||||
|
the function on all of its children, as soon as one of the children returns
|
||||||
|
FALSE, the method returns FALSE with the state set to the current bin state
|
||||||
|
and the pending set to pending state.
|
||||||
|
|
||||||
|
For bins with elements that did an ASYNC state change, the _commit_state()
|
||||||
|
is only executed when actively calling _get_state(). The reason for this is
|
||||||
|
that when a child of the bin commits its state, this is not automatically
|
||||||
|
reported to the bin. This is not a problem since the _get_state() function
|
||||||
|
is the only way to get the current and pending state of the bin and is always
|
||||||
|
consistent.
|
||||||
|
|
||||||
|
d) bin state change algorithm
|
||||||
|
|
||||||
|
In order to perform the sink to source state change a bin must be able to sort
|
||||||
|
the elements. To make this easier we require that elements are connected to
|
||||||
|
bins using ghostpads on the bin.
|
||||||
|
|
||||||
|
The algoritm goes like this:
|
||||||
|
|
||||||
|
d = [ ] # list of delayed elements
|
||||||
|
p = [ ] # list of pending async elements
|
||||||
|
q = [ elements without srcpads ] # sinks
|
||||||
|
while q not empty do
|
||||||
|
e = dequeue q
|
||||||
|
s = [ all elements connected to e on the sinkpads ]
|
||||||
|
q = q append s
|
||||||
|
if e is entry point
|
||||||
|
d = d append e
|
||||||
|
else
|
||||||
|
r = state change e
|
||||||
|
if r is ASYNC
|
||||||
|
p = p append e
|
||||||
|
done
|
||||||
|
while d not empty do
|
||||||
|
e = dequeue d
|
||||||
|
r = state change e
|
||||||
|
if r is ASYNC
|
||||||
|
p = p append e
|
||||||
|
done
|
||||||
|
# a bin would return ASYNC here if p is not empty
|
||||||
|
|
||||||
|
# this last part is only performed by a pipeline
|
||||||
|
while p not empty do
|
||||||
|
e = peek p
|
||||||
|
if state completed e
|
||||||
|
dequeue e from p
|
||||||
|
done
|
||||||
|
|
||||||
|
The algorithm first tries to find the sink elements, ie. ones without
|
||||||
|
sinkpads. Then it changes the state of each sink elements and queues
|
||||||
|
the elements connected to the sinkpads.
|
||||||
|
|
||||||
|
The entry points (loopbased and getbased elements) are delayed as we
|
||||||
|
first need to change the state of the other elements before we can activate
|
||||||
|
the entry points in the pipeline.
|
||||||
|
|
||||||
|
The pipeline will poll the async children before returning.
|
||||||
|
|
||||||
|
e) The GstTask infrastructure
|
||||||
|
|
||||||
|
A new component: GstTask is added to the core. A task is created by
|
||||||
|
an instance of the abstract GstScheduler class.
|
||||||
|
|
||||||
|
Each schedulable element (when added to a pipeline) is handed a
|
||||||
|
reference to a GstScheduler. It can use this object to create
|
||||||
|
a GstTask, which is basically a managed wrapper around a threading
|
||||||
|
library like GThread. It should be possible to write a GstScheduler
|
||||||
|
instance that uses other means of scheduling, like one that does not
|
||||||
|
use threads but implements task switching based on mutex locking.
|
||||||
|
|
||||||
|
When source and loopbased elements want to create the streaming thread
|
||||||
|
they create an instance of a GstTask, which they pass a pointer to
|
||||||
|
a loop-function. This function will be called as soon as the element
|
||||||
|
performs GstTask.start(). The element can stop and uses mutexes to
|
||||||
|
pause the GstTask from, for example, the state change function or the
|
||||||
|
event functions.
|
||||||
|
|
||||||
|
The GstTasks implement the streaming threads.
|
||||||
|
|
||||||
|
f) the preroll phase
|
||||||
|
|
||||||
|
Element start the streaming threads in the READY->PAUSED state. Since
|
||||||
|
the elements that start the threads are put in the PAUSED state last,
|
||||||
|
after their connected elements, they will be able to deliver data to
|
||||||
|
their peers without problems.
|
||||||
|
|
||||||
|
Sink elements like audio and videosinks will return an async state change
|
||||||
|
reply and will only commit the state change after receiving the first
|
||||||
|
buffer. This will implement the preroll phase.
|
||||||
|
|
||||||
|
The following pseudo code shows an algorithm for commiting the state
|
||||||
|
change in the streaming method.
|
||||||
|
|
||||||
|
GST_LOCK (element);
|
||||||
|
/* if we are going to PAUSED, we can commit the state change */
|
||||||
|
if (GST_STATE_TRANSITION (element) == GST_STATE_READY_TO_PAUSED) {
|
||||||
|
gst_element_commit_state (element);
|
||||||
|
}
|
||||||
|
/* if we are paused we need to wait for playing to continue */
|
||||||
|
if (GST_STATE (element) == GST_STATE_PAUSED) {
|
||||||
|
|
||||||
|
/* here we wait for the next state change */
|
||||||
|
do {
|
||||||
|
g_cond_wait (element->state_cond, GST_GET_LOCK (element));
|
||||||
|
} while (GST_STATE (element) == GST_STATE_PAUSED);
|
||||||
|
|
||||||
|
/* check if we got playing */
|
||||||
|
if (GST_STATE (element) != GST_STATE_PLAYING) {
|
||||||
|
/* not playing, we can't accept the buffer */
|
||||||
|
GST_UNLOCK (element);
|
||||||
|
gst_buffer_unref (buf);
|
||||||
|
return GST_FLOW_WRONG_STATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GST_UNLOCK (element);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
g) return values for push/pull
|
||||||
|
|
||||||
|
To recover from pipeline errors in a more elegant manner than just
|
||||||
|
shutting down the pipeline, we need more finegrained error messages
|
||||||
|
in the data transport. The plugins should be able to know what goes
|
||||||
|
wrong when interacting with their outside environment. This means
|
||||||
|
that gst_pad_push/gst_pad_pull and gst_event_send should return a
|
||||||
|
result code.
|
||||||
|
|
||||||
|
Possible return values include:
|
||||||
|
|
||||||
|
- GST_OK
|
||||||
|
- GST_ERROR
|
||||||
|
- GST_NOT_CONNECTED
|
||||||
|
- GST_NOT_NEGOTIATED
|
||||||
|
- GST_WRONG_STATE
|
||||||
|
- GST_UNEXPECTED
|
||||||
|
- GST_NOT_SUPPORTED
|
||||||
|
|
||||||
|
GST_OK
|
||||||
|
Data transport was successful
|
||||||
|
|
||||||
|
GST_ERROR
|
||||||
|
An error occured during transport, such as a fatal decoding error,
|
||||||
|
the pad should not be used again.
|
||||||
|
|
||||||
|
GST_NOT_CONNECTED
|
||||||
|
The pad was not connected
|
||||||
|
|
||||||
|
GST_NOT_NEGOTIATED
|
||||||
|
The peer does not know what datatype is going over the pipeline.
|
||||||
|
|
||||||
|
GST_WRONG_STATE
|
||||||
|
The peer pad is not in the correct state.
|
||||||
|
|
||||||
|
GST_UNEXPECTED
|
||||||
|
The peer pad did not expect the data because it was flushing or
|
||||||
|
received an eos.
|
||||||
|
|
||||||
|
GST_NOT_SUPPORTED
|
||||||
|
The operation is not supported.
|
||||||
|
|
||||||
|
The signatures of the functions will become:
|
||||||
|
|
||||||
|
GstFlowReturn gst_pad_push (GstPad *pad, GstBuffer *buffer);
|
||||||
|
GstFlowReturn gst_pad_pull (GstPad *pad, GstBuffer **buffer);
|
||||||
|
|
||||||
|
GstResult gst_pad_push_event (GstPad *pad, GstEvent *event);
|
||||||
|
|
||||||
|
- push_event will send the event to the connected pad.
|
||||||
|
|
||||||
|
For sending events from the application:
|
||||||
|
|
||||||
|
GstResult gst_pad_send_event (GstPad *pad, GstEvent *event);
|
||||||
|
|
||||||
|
h) Negotiation
|
||||||
|
|
||||||
|
Implement a simple two phase negotiation. First the source queries the
|
||||||
|
sink if it accepts a certain format, then it sends the new format
|
||||||
|
as an event. Sink pads can also trigger a state change by requesting
|
||||||
|
a renegotiation.
|
||||||
|
|
||||||
|
i) Mainloop integration/GstBus
|
||||||
|
|
||||||
|
All error, warning and EOS messages from the plugins are sent to an event
|
||||||
|
queue. The pipeline reads the messages from the queue and will either
|
||||||
|
handle them or forward them to the main event queue that is read by the
|
||||||
|
application.
|
||||||
|
|
||||||
|
Specific pipelines can be written that deal with negotiation messages and
|
||||||
|
errors in the pipeline intelligently. The basic pipeline will stop the
|
||||||
|
pipeline when an error occurs.
|
||||||
|
|
||||||
|
Whenever an element posts a message on the event queue, a signal is also
|
||||||
|
fired that can be catched by the application. When dealing with those
|
||||||
|
signals the application has to be aware that they come from the streaming
|
||||||
|
threads and need to make sure they use proper locking to protect their
|
||||||
|
own data structures.
|
||||||
|
|
||||||
|
The messages will be implemented using a GstBus object that allows
|
||||||
|
plugins to post messages and allows the application to read messages either
|
||||||
|
synchronous or asynchronous. It is also possible to integrate the bus in
|
||||||
|
the mainloop.
|
||||||
|
|
||||||
|
The messages will derive from GstData to make them a lightweight refcounted
|
||||||
|
object. Need to figure out how we can extend this method to encapsulate
|
||||||
|
generic signals in messages too.
|
||||||
|
|
||||||
|
This decouples the streaming thread from the application thread and should
|
||||||
|
avoid race conditions and pipeline stalling due to application interaction.
|
||||||
|
|
||||||
|
It is still possible to receive the messages in the streaming thread context
|
||||||
|
if an application wants to. When doing this, special care has to be taken
|
||||||
|
when performing state changes.
|
||||||
|
|
||||||
|
j) EOS
|
||||||
|
|
||||||
|
When an element goes to EOS, it sends the EOS event to the peer plugin
|
||||||
|
and stops sending data on that pad. The peer element that received an EOS
|
||||||
|
event on a pad can refuse any buffers on that pad.
|
||||||
|
|
||||||
|
All elements without source pads must post the EOS message on the message
|
||||||
|
queue. When the pipeline receives an EOS event from all sinks, it will
|
||||||
|
post the EOS message on the application message queue so that the application
|
||||||
|
knows the pipeline is in EOS. Elements without any connected sourcepads
|
||||||
|
should also post the EOS message. This makes sure that all "dead-ends"
|
||||||
|
signalled the EOS.
|
||||||
|
|
||||||
|
No state change happens when elements go to EOS but the elements with the
|
||||||
|
GstTask will stop their tasks and so stop producing data.
|
||||||
|
|
||||||
|
An application can issue a seek operation which makes all tasks running
|
||||||
|
again so that they can start streaming from the new location.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
A) threads and lowlatency
|
||||||
|
|
||||||
|
People often think it is a sin to use threads in low latency applications. This is true
|
||||||
|
when using the data has to pass thread boundaries but false when it doesn't. Since
|
||||||
|
source and loop based elements create a thread, it is possible to construct a pipeline
|
||||||
|
where data passing has to cross thread boundaries, consider this case:
|
||||||
|
|
||||||
|
+-----------------------------------+
|
||||||
|
| +--------+ +--------+ |
|
||||||
|
| |element1| |element2| |
|
||||||
|
| .. -sink src-sink src- .. |
|
||||||
|
| +--------+ +--------+ |
|
||||||
|
+-----------------------------------+
|
||||||
|
|
||||||
|
The two elements are loop base and thus create a thread to drive the pipeline. At the
|
||||||
|
border between the two elements there is a mutex to pass the data between the two
|
||||||
|
threads. When using these kinds of element in a pipeline, low-latency will not be
|
||||||
|
possible. For low-latency apps, don't use these constructs!
|
||||||
|
|
||||||
|
Note that in a typical pipeline with one get-based element and two chain-based
|
||||||
|
elements (decoder/sink) there is only one thread, no data is crossing thread
|
||||||
|
boundaries and thus this pipeline can be low-latency. Also note that while this
|
||||||
|
pipeline is streaming no interaction or locking is done between it and the main
|
||||||
|
application.
|
||||||
|
|
||||||
|
+-------------------------------------+
|
||||||
|
| +--------+ +---------+ +------+ |
|
||||||
|
| | src | | decoder | | sink | |
|
||||||
|
| | src-sink src-sink | |
|
||||||
|
| +--------+ +---------+ +------+ |
|
||||||
|
+-------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
|
B) howto make non-threaded pipelines
|
||||||
|
|
||||||
|
For low latency it is required to not have datapassing cross any thread
|
||||||
|
borders. Here are some pointers for making sure this requirement is met:
|
||||||
|
|
||||||
|
- never connect a loop or chain based element to a loop based element, this
|
||||||
|
will create a new thread for the sink loop element.
|
||||||
|
|
||||||
|
- do not use queues or any other decoupled element, as they implicitly
|
||||||
|
create a thread boundary.
|
||||||
|
|
||||||
|
- At least one thread will be created for any source element (either in the
|
||||||
|
connected loop-based element or in the source itself) unless the source
|
||||||
|
elements are connected to the same loop based element.
|
||||||
|
|
||||||
|
- when designing sinks, make them non-blocking, use the async clock callbacks
|
||||||
|
to schedule media rendering in the same thread (if any) as the clock. Sinks that
|
||||||
|
provide the clock can be made blocking.
|
||||||
|
|
Loading…
Reference in a new issue