mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-11 11:51:34 +00:00
290 lines
7.7 KiB
Markdown
290 lines
7.7 KiB
Markdown
# Sink elements
|
|
|
|
Sink elements consume data and normally have no source pads.
|
|
|
|
Typical sink elements include:
|
|
|
|
- audio/video renderers
|
|
|
|
- network sinks
|
|
|
|
- filesinks
|
|
|
|
Sinks are harder to construct than other element types as they are
|
|
treated specially by the GStreamer core.
|
|
|
|
## state changes
|
|
|
|
A sink always returns ASYNC from the state change to PAUSED, this
|
|
includes a state change from READY→PAUSED and PLAYING→PAUSED. The reason
|
|
for this is that this way we can detect when the first buffer or event
|
|
arrives in the sink when the state change completes.
|
|
|
|
A sink should block on the first EOS event or buffer received in the
|
|
READY→PAUSED state before commiting the state to PAUSED.
|
|
|
|
FLUSHING events have to be handled out of sync with the buffer flow and
|
|
take no part in the preroll procedure.
|
|
|
|
Events other than EOS do not complete the preroll stage.
|
|
|
|
## sink overview
|
|
|
|
- TODO: `PREROLL_LOCK` can be removed and we can safely use the `STREAM_LOCK`.
|
|
|
|
``````
|
|
# Commit the state. We return TRUE if we can continue
|
|
# streaming, FALSE in the case we go to a READY or NULL state.
|
|
# if we go to PLAYING, we don't need to block on preroll.
|
|
commit
|
|
{
|
|
LOCK
|
|
switch (pending)
|
|
case PLAYING:
|
|
need_preroll = FALSE
|
|
break
|
|
case PAUSED:
|
|
break
|
|
case READY:
|
|
case NULL:
|
|
return FALSE
|
|
case VOID:
|
|
return TRUE
|
|
|
|
# update state
|
|
state = pending
|
|
next = VOID
|
|
pending = VOID
|
|
UNLOCK
|
|
return TRUE
|
|
}
|
|
|
|
# Sync an object. We have to wait for the element to reach
|
|
# the PLAYING state before we can wait on the clock.
|
|
# Some items do not need synchronisation (most events) so the
|
|
# get_times method returns FALSE (not syncable)
|
|
# need_preroll indicates that we are not in the PLAYING state
|
|
# and therefore need to commit and potentially block on preroll
|
|
# if our clock_wait got interrupted we commit and block again.
|
|
# The reason for this is that the current item being rendered is
|
|
# not yet finished and we can use that item to finish preroll.
|
|
do_sync (obj)
|
|
{
|
|
# get timing information for this object
|
|
syncable = get_times (obj, &start, &stop)
|
|
if (!syncable)
|
|
return OK;
|
|
again:
|
|
while (need_preroll)
|
|
if (need_commit)
|
|
need_commit = FALSE
|
|
if (!commit)
|
|
return FLUSHING
|
|
|
|
if (need_preroll)
|
|
# release PREROLL_LOCK and wait. prerolled can be observed
|
|
# and will be TRUE
|
|
prerolled = TRUE
|
|
PREROLL_WAIT (releasing PREROLL_LOCK)
|
|
prerolled = FALSE
|
|
if (flushing)
|
|
return FLUSHING
|
|
|
|
if (valid (start || stop))
|
|
PREROLL_UNLOCK
|
|
end_time = stop
|
|
ret = wait_clock (obj,start)
|
|
PREROLL_LOCK
|
|
if (flushing)
|
|
return FLUSHING
|
|
# if the clock was unscheduled, we redo the
|
|
# preroll
|
|
if (ret == UNSCHEDULED)
|
|
goto again
|
|
}
|
|
|
|
# render a prerollable item (EOS or buffer). It is
|
|
# always called with the PREROLL_LOCK helt.
|
|
render_object (obj)
|
|
{
|
|
ret = do_sync (obj)
|
|
if (ret != OK)
|
|
return ret;
|
|
|
|
# preroll and syncing done, now we can render
|
|
render(obj)
|
|
}
|
|
| # sinks that sync on buffer contents do like this
|
|
| while (more_to_render)
|
|
| ret = render
|
|
| if (ret == interrupted)
|
|
| prerolled = TRUE
|
|
render (buffer) ----->| PREROLL_WAIT (releasing PREROLL_LOCK)
|
|
| prerolled = FALSE
|
|
| if (flushing)
|
|
| return FLUSHING
|
|
|
|
|
|
|
# queue a prerollable item (EOS or buffer). It is
|
|
# always called with the PREROLL_LOCK helt.
|
|
# This function will commit the state when receiving the
|
|
# first prerollable item.
|
|
# items are then added to the rendering queue or rendered
|
|
# right away if no preroll is needed.
|
|
queue (obj, prerollable)
|
|
{
|
|
if (need_preroll)
|
|
if (prerollable)
|
|
queuelen++
|
|
|
|
# first item in the queue while we need preroll
|
|
# will complete state change and call preroll
|
|
if (queuelen == 1)
|
|
preroll (obj)
|
|
if (need_commit)
|
|
need_commit = FALSE
|
|
if (!commit)
|
|
return FLUSHING
|
|
|
|
# then see if we need more preroll items before we
|
|
# can block
|
|
if (need_preroll)
|
|
if (queuelen <= maxqueue)
|
|
queue.add (obj)
|
|
return OK
|
|
|
|
# now clear the queue and render each item before
|
|
# rendering the current item.
|
|
while (queue.hasItem)
|
|
render_object (queue.remove())
|
|
|
|
render_object (obj)
|
|
queuelen = 0
|
|
}
|
|
|
|
# various event functions
|
|
event
|
|
EOS:
|
|
# events must complete preroll too
|
|
STREAM_LOCK
|
|
PREROLL_LOCK
|
|
if (flushing)
|
|
return FALSE
|
|
ret = queue (event, TRUE)
|
|
if (ret == FLUSHING)
|
|
return FALSE
|
|
PREROLL_UNLOCK
|
|
STREAM_UNLOCK
|
|
break
|
|
SEGMENT:
|
|
# the segment must be used to clip incoming
|
|
# buffers. Then then go into the queue as non-prerollable
|
|
# items used for syncing the buffers
|
|
STREAM_LOCK
|
|
PREROLL_LOCK
|
|
if (flushing)
|
|
return FALSE
|
|
set_clip
|
|
ret = queue (event, FALSE)
|
|
if (ret == FLUSHING)
|
|
return FALSE
|
|
PREROLL_UNLOCK
|
|
STREAM_UNLOCK
|
|
break
|
|
FLUSH_START:
|
|
# set flushing and unblock all that is waiting
|
|
event ----> subclasses can interrupt render
|
|
PREROLL_LOCK
|
|
flushing = TRUE
|
|
unlock_clock
|
|
PREROLL_SIGNAL
|
|
PREROLL_UNLOCK
|
|
STREAM_LOCK
|
|
lost_state
|
|
STREAM_UNLOCK
|
|
break
|
|
FLUSH_END:
|
|
# unset flushing and clear all data and eos
|
|
STREAM_LOCK
|
|
event
|
|
PREROLL_LOCK
|
|
queue.clear
|
|
queuelen = 0
|
|
flushing = FALSE
|
|
eos = FALSE
|
|
PREROLL_UNLOCK
|
|
STREAM_UNLOCK
|
|
break
|
|
|
|
# the chain function checks the buffer falls within the
|
|
# configured segment and queues the buffer for preroll and
|
|
# rendering
|
|
chain
|
|
STREAM_LOCK
|
|
PREROLL_LOCK
|
|
if (flushing)
|
|
return FLUSHING
|
|
if (clip)
|
|
queue (buffer, TRUE)
|
|
PREROLL_UNLOCK
|
|
STREAM_UNLOCK
|
|
|
|
state
|
|
switch (transition)
|
|
READY_PAUSED:
|
|
# no datapassing is going on so we always return ASYNC
|
|
ret = ASYNC
|
|
need_commit = TRUE
|
|
eos = FALSE
|
|
flushing = FALSE
|
|
need_preroll = TRUE
|
|
prerolled = FALSE
|
|
break
|
|
PAUSED_PLAYING:
|
|
# we grab the preroll lock. This we can only do if the
|
|
# chain function is either doing some clock sync, we are
|
|
# waiting for preroll or the chain function is not being called.
|
|
PREROLL_LOCK
|
|
if (prerolled || eos)
|
|
ret = OK
|
|
need_commit = FALSE
|
|
need_preroll = FALSE
|
|
if (eos)
|
|
post_eos
|
|
else
|
|
PREROLL_SIGNAL
|
|
else
|
|
need_preroll = TRUE
|
|
need_commit = TRUE
|
|
ret = ASYNC
|
|
PREROLL_UNLOCK
|
|
break
|
|
PLAYING_PAUSED:
|
|
---> subclass can interrupt render
|
|
# we grab the preroll lock. This we can only do if the
|
|
# chain function is either doing some clock sync
|
|
# or the chain function is not being called.
|
|
PREROLL_LOCK
|
|
need_preroll = TRUE
|
|
unlock_clock
|
|
if (prerolled || eos)
|
|
ret = OK
|
|
else
|
|
ret = ASYNC
|
|
PREROLL_UNLOCK
|
|
break
|
|
PAUSED_READY:
|
|
---> subclass can interrupt render
|
|
# we grab the preroll lock. Set to flushing and unlock
|
|
# everything. This should exit the chain functions and stop
|
|
# streaming.
|
|
PREROLL_LOCK
|
|
flushing = TRUE
|
|
unlock_clock
|
|
queue.clear
|
|
queuelen = 0
|
|
PREROLL_SIGNAL
|
|
ret = OK
|
|
PREROLL_UNLOCK
|
|
break
|
|
```
|