gstreamer/docs/design/part-block.txt
Edward Hervey 8923a57a6f docs/design/part-block.txt: Further explain the use of flushing on blocked pads.
Original commit message from CVS:
* docs/design/part-block.txt:
Further explain the use of flushing on blocked pads.
* docs/gst/gstreamer-sections.txt:
* gst/gstpad.c: (gst_pad_is_blocking), (handle_pad_block),
(gst_pad_push_event):
* gst/gstpad.h:
Added new GstPadFlag : GST_PAD_BLOCKING.
Adds the notion of pads really blocking, which enables to properly
handle FLUSH_START/FLUSH_STOP events on blocked pads.
Fixes #358999
API: gst_pad_is_blocking()
API: GST_PAD_IS_BLOCKING() macro
API: GST_PAD_BLOCKING GstPadFlag
2006-10-02 16:01:54 +00:00

164 lines
6.1 KiB
Text

Pad block
---------
The purpose of blocking a pad is to be notified of downstream dataflow
and events. The notification can be used for
- (Re)connecting/disconnecting the pad.
- performing a seek
- inspecting the data/events on the pad
The pad block is performed on a source pad (push based) or sink pad (pull based)
and will succeed when the following events happen on the pad:
- gst_pad_push()
- gst_pad_alloc_buffer()
- gst_pad_push_event() except for FLUSH_START and FLUSH_STOP events.
- gst_pad_pull_range () (on a sinkpad)
Flushing
--------
The flushing event is used to clear any data out of the
downstream elements.
* Generic case
Consider the following pipeline:
.-----. .-------. .-------.
| src | | elem1 |\/ | elem2 |
| src -> sink src -> sink src ....
'-----' '-------'/\ '-------'
Where elem1.src is blocked. If the pad block is taken (the callback
is called or the sync block returned) no data is flowing in elem2.sink.
In this situation, the streaming thread is blocked on a GCond and is
waiting to be unblocked.
When sending a flushing seek upstream on elem1.src, the FLUSH_START and
will temporary unblock the streaming thread and make all pad functions that
triggers a block (_push/_alloc_buffer/_push_event/_pull_range) return
GST_FLOW_WRONG_STATE. This will then eventually pause the streaming thread
and release the STREAM_LOCK.
Since no STREAM lock is taken after the pad block it is not needed to send
the FLUSH_START event further downstream.
The FLUSH_STOP will set the srcpad to non-flushing again and is dropped
fr the same reason. From then on, the new data after the flushing seek
will be queued when the pad block is taken again.
* Case where the stream is blocking downstream
The example above is only valid if the elem1.src pad is really blocking
(callback called or sync block returned).
In the case where the stream is blocking further downstream (on elem2.src
for example, or on a blocking queue), extra care has to be taken.
Consider the following pipeline:
.-----. .-------. .-------.
| src | | elem1 |\/ | elem2 |
| src -> sink src -> sink src .... Blocking somewhere downstream
'-----' '-------'/\ '-------'
A pad block has been requested by the user on elem1.src , but since the stream
is blocking somewhere downstream, the callback is not called or the sync block
does not return.
In order for the block to happen, a FLUSH_START needs to be:
_ either sent directly on the downstream blocking element/pad so that it
release the stream lock, and it gives a chance for the elem1.src pad to
block.
_ A FLUSH_START is sent downstream from an upstream element, causing all the
pads down to the blocking element/pad (including elem1.src) to go to the
FLUSHING state. A FLUSH_STOP can now be sent downstream, causing all the
pads down to the previously blocking element to unset their FLUSHING state.
The next push will then properly block on elem1.src.
In this case, the pads have to be careful when handling the FLUSH events
forwarding. Those events should only be forwarded downstream is the BLOCKED
pad was previously BLOCKING.
Use cases:
----------
* Prerolling a partial pipeline
.---------. .---------. .----------.
| filesrc | | demuxer | .-----. | decoder1 |
| src -> sink src1 ->|queue|-> sink src
'---------' | | '-----' '----------' X
| | .----------.
| | .-----. | decoder2 |
| src2 ->|queue|-> sink src
'---------' '-----' '----------' X
The purpose is to create the pipeline dynamically up to the
decoders but not yet connect them to a sink and without losing
any data.
To do this, the source pads of the decoders is blocked so that no
events or buffers can escape and we don't interrupt the stream.
When all of the dynamic pad are created (no-more-pads emited by the
branching point, ie, the demuxer or the queues filled) and the pads
are blocked (blocked callback received) the pipeline is completely
prerolled.
It should then be possible to perform the following actions on the
prerolled pipeline:
- query duration/position
- perform a flushing seek to preroll a new position
- connect other elements and unblock the blocked pads.
* dynamically switching an element in a PLAYING pipeline.
.----------. .----------. .----------.
| element1 | | element2 | | element3 |
... src -> sink src -> sink ...
'----------' '----------' '----------'
.----------.
| element4 |
sink src
'----------'
The purpose is to replace element2 with element4 in the PLAYING
pipeline.
1) block element1 src pad. This can be done async.
2) wait for block to happen. at this point nothing flowing between
element1 and element2 and nothing will flow until unblocked.
3) unlink element1 and element2
4) optional step: make sure data is flushed out of element2:
4a) pad event probe on element2 src
4b) send EOS to element2, this makes sure that element2 flushes
out the last bits of data it holds.
4c) wait for EOS to appear in the probe, drop the EOS.
5) unlink element2 and element3
5a) optionally element2 can now be set to NULL.
6) link element4 and element3
7) link element1 and element4 (FIXME, how about letting element4 know
about the currently running segment?)
8) unblock element1 src
The same flow can be used to replace an element in a PAUSED pipeline. Only
special care has to be taken when performing step 2) which has to be done
async or it might deadlock. In the async callback one can then perform the
steps from 3). In a playing pipeline one can of course use the async block
as well, so that there is a generic method for both PAUSED and PLAYING.
The same flow works as well for any chain of multiple elements and might
be implemented with a helper function in the future.