mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-28 12:41:05 +00:00
570 lines
22 KiB
Markdown
570 lines
22 KiB
Markdown
# Overview
|
||
|
||
This part gives an overview of the design of GStreamer with references
|
||
to the more detailed explanations of the different topics.
|
||
|
||
This document is intented for people that want to have a global overview
|
||
of the inner workings of GStreamer.
|
||
|
||
## Introduction
|
||
|
||
GStreamer is a set of libraries and plugins that can be used to
|
||
implement various multimedia applications ranging from desktop players,
|
||
audio/video recorders, multimedia servers, transcoders, etc.
|
||
|
||
Applications are built by constructing a pipeline composed of elements.
|
||
An element is an object that performs some action on a multimedia stream
|
||
such as:
|
||
|
||
- read a file
|
||
- decode or encode between formats
|
||
- capture from a hardware device
|
||
- render to a hardware device
|
||
- mix or multiplex multiple streams
|
||
|
||
Elements have input and output pads called sink and source pads in
|
||
GStreamer. An application links elements together on pads to construct a
|
||
pipeline. Below is an example of an ogg/vorbis playback pipeline.
|
||
|
||
```
|
||
+-----------------------------------------------------------+
|
||
| ----------> downstream -------------------> |
|
||
| |
|
||
| pipeline |
|
||
| +---------+ +----------+ +-----------+ +----------+ |
|
||
| | filesrc | | oggdemux | | vorbisdec | | alsasink | |
|
||
| | src-sink src-sink src-sink | |
|
||
| +---------+ +----------+ +-----------+ +----------+ |
|
||
| |
|
||
| <---------< upstream <-------------------< |
|
||
+-----------------------------------------------------------+
|
||
```
|
||
|
||
The filesrc element reads data from a file on disk. The oggdemux element
|
||
parses the data and sends the compressed audio data to the vorbisdec
|
||
element. The vorbisdec element decodes the compressed data and sends it
|
||
to the alsasink element. The alsasink element sends the samples to the
|
||
audio card for playback.
|
||
|
||
Downstream and upstream are the terms used to describe the direction in
|
||
the Pipeline. From source to sink is called "downstream" and "upstream"
|
||
is from sink to source. Dataflow always happens downstream.
|
||
|
||
The task of the application is to construct a pipeline as above using
|
||
existing elements. This is further explained in the pipeline building
|
||
topic.
|
||
|
||
The application does not have to manage any of the complexities of the
|
||
actual dataflow/decoding/conversions/synchronisation etc. but only calls
|
||
high level functions on the pipeline object such as PLAY/PAUSE/STOP.
|
||
|
||
The application also receives messages and notifications from the
|
||
pipeline such as metadata, warning, error and EOS messages.
|
||
|
||
If the application needs more control over the graph it is possible to
|
||
directly access the elements and pads in the pipeline.
|
||
|
||
## Design overview
|
||
|
||
GStreamer design goals include:
|
||
|
||
- Process large amounts of data quickly
|
||
- Allow fully multithreaded processing
|
||
- Ability to deal with multiple formats
|
||
- Synchronize different dataflows
|
||
- Ability to deal with multiple devices
|
||
|
||
The capabilities presented to the application depends on the number of
|
||
elements installed on the system and their functionality.
|
||
|
||
The GStreamer core is designed to be media agnostic but provides many
|
||
features to elements to describe media formats.
|
||
|
||
## Elements
|
||
|
||
The smallest building blocks in a pipeline are elements. An element
|
||
provides a number of pads which can be source or sinkpads. Sourcepads
|
||
provide data and sinkpads consume data. Below is an example of an ogg
|
||
demuxer element that has one pad that takes (sinks) data and two source
|
||
pads that produce data.
|
||
|
||
```
|
||
+-----------+
|
||
| oggdemux |
|
||
| src0
|
||
sink src1
|
||
+-----------+
|
||
```
|
||
|
||
An element can be in four different states: NULL, READY, PAUSED,
|
||
PLAYING. In the NULL and READY state, the element is not processing any
|
||
data. In the PLAYING state it is processing data. The intermediate
|
||
PAUSED state is used to preroll data in the pipeline. A state change can
|
||
be performed with `gst_element_set_state()`.
|
||
|
||
An element always goes through all the intermediate state changes. This
|
||
means that when en element is in the READY state and is put to PLAYING,
|
||
it will first go through the intermediate PAUSED state.
|
||
|
||
An element state change to PAUSED will activate the pads of the element.
|
||
First the source pads are activated, then the sinkpads. When the pads
|
||
are activated, the pad activate function is called. Some pads will start
|
||
a thread (GstTask) or some other mechanism to start producing or
|
||
consuming data.
|
||
|
||
The PAUSED state is special as it is used to preroll data in the
|
||
pipeline. The purpose is to fill all connected elements in the pipeline
|
||
with data so that the subsequent PLAYING state change happens very
|
||
quickly. Some elements will therefore not complete the state change to
|
||
PAUSED before they have received enough data. Sink elements are required
|
||
to only complete the state change to PAUSED after receiving the first
|
||
data.
|
||
|
||
Normally the state changes of elements are coordinated by the pipeline
|
||
as explained in [states](design/states.md).
|
||
|
||
Different categories of elements exist:
|
||
|
||
- *source elements*: these are elements that do not consume data but
|
||
only provide data for the pipeline.
|
||
|
||
- *sink elements*: these are elements that do not produce data but
|
||
renders data to an output device.
|
||
|
||
- *transform elements*: these elements transform an input stream in a
|
||
certain format into a stream of another format.
|
||
Encoder/decoder/converters are examples.
|
||
|
||
- *demuxer elements*: these elements parse a stream and produce several
|
||
output streams.
|
||
|
||
- *mixer/muxer elements*: combine several input streams into one output
|
||
stream.
|
||
|
||
Other categories of elements can be constructed (see [klass](design/draft-klass.md)).
|
||
|
||
## Bins
|
||
|
||
A bin is an element subclass and acts as a container for other elements
|
||
so that multiple elements can be combined into one element.
|
||
|
||
A bin coordinates its children’s state changes as explained later. It
|
||
also distributes events and various other functionality to elements.
|
||
|
||
A bin can have its own source and sinkpads by ghostpadding one or more
|
||
of its children’s pads to itself.
|
||
|
||
Below is a picture of a bin with two elements. The sinkpad of one
|
||
element is ghostpadded to the bin.
|
||
|
||
```
|
||
+---------------------------+
|
||
| bin |
|
||
| +--------+ +-------+ |
|
||
| | | | | |
|
||
| /sink src-sink | |
|
||
sink +--------+ +-------+ |
|
||
+---------------------------+
|
||
```
|
||
|
||
## Pipeline
|
||
|
||
A pipeline is a special bin subclass that provides the following
|
||
features to its children:
|
||
|
||
- Select and manage a global clock for all its children.
|
||
- Manage running\_time based on the selected clock. Running\_time is
|
||
the elapsed time the pipeline spent in the PLAYING state and is used
|
||
for synchronisation.
|
||
- Manage latency in the pipeline.
|
||
- Provide means for elements to comunicate with the application by the
|
||
GstBus.
|
||
- Manage the global state of the elements such as Errors and
|
||
end-of-stream.
|
||
|
||
Normally the application creates one pipeline that will manage all the
|
||
elements in the application.
|
||
|
||
## Dataflow and buffers
|
||
|
||
GStreamer supports two possible types of dataflow, the push and pull
|
||
model. In the push model, an upstream element sends data to a downstream
|
||
element by calling a method on a sinkpad. In the pull model, a
|
||
downstream element requests data from an upstream element by calling a
|
||
method on a source pad.
|
||
|
||
The most common dataflow is the push model. The pull model can be used
|
||
in specific circumstances by demuxer elements. The pull model can also
|
||
be used by low latency audio applications.
|
||
|
||
The data passed between pads is encapsulated in Buffers. The buffer
|
||
contains pointers to the actual memory and also metadata describing the
|
||
memory. This metadata includes:
|
||
|
||
- timestamp of the data, this is the time instance at which the data
|
||
was captured or the time at which the data should be played back.
|
||
|
||
- offset of the data: a media specific offset, this could be samples
|
||
for audio or frames for video.
|
||
|
||
- the duration of the data in time.
|
||
|
||
- additional flags describing special properties of the data such as
|
||
discontinuities or delta units.
|
||
|
||
- additional arbitrary metadata
|
||
|
||
When an element whishes to send a buffer to another element is does this
|
||
using one of the pads that is linked to a pad of the other element. In
|
||
the push model, a buffer is pushed to the peer pad with
|
||
`gst_pad_push()`. In the pull model, a buffer is pulled from the peer
|
||
with the `gst_pad_pull_range()` function.
|
||
|
||
Before an element pushes out a buffer, it should make sure that the peer
|
||
element can understand the buffer contents. It does this by querying the
|
||
peer element for the supported formats and by selecting a suitable
|
||
common format. The selected format is then first sent to the peer
|
||
element with a CAPS event before pushing the buffer (see
|
||
[negotiation](design/negotiation.md)).
|
||
|
||
When an element pad receives a CAPS event, it has to check if it
|
||
understand the media type. The element must refuse following buffers if
|
||
the media type preceding it was not accepted.
|
||
|
||
Both `gst_pad_push()` and `gst_pad_pull_range()` have a return value
|
||
indicating whether the operation succeeded. An error code means that no
|
||
more data should be sent to that pad. A source element that initiates
|
||
the data flow in a thread typically pauses the producing thread when
|
||
this happens.
|
||
|
||
A buffer can be created with `gst_buffer_new()` or by requesting a
|
||
usable buffer from a buffer pool using
|
||
`gst_buffer_pool_acquire_buffer()`. Using the second method, it is
|
||
possible for the peer element to implement a custom buffer allocation
|
||
algorithm.
|
||
|
||
The process of selecting a media type is called caps negotiation.
|
||
|
||
## Caps
|
||
|
||
A media type (Caps) is described using a generic list of key/value
|
||
pairs. The key is a string and the value can be a single/list/range of
|
||
int/float/string.
|
||
|
||
Caps that have no ranges/list or other variable parts are said to be
|
||
fixed and can be used to put on a buffer.
|
||
|
||
Caps with variables in them are used to describe possible media types
|
||
that can be handled by a pad.
|
||
|
||
## Dataflow and events
|
||
|
||
Parallel to the dataflow is a flow of events. Unlike the buffers, events
|
||
can pass both upstream and downstream. Some events only travel upstream
|
||
others only downstream.
|
||
|
||
The events are used to denote special conditions in the dataflow such as
|
||
EOS or to inform plugins of special events such as flushing or seeking.
|
||
|
||
Some events must be serialized with the buffer flow, others don’t.
|
||
Serialized events are inserted between the buffers. Non serialized
|
||
events jump in front of any buffers current being processed.
|
||
|
||
An example of a serialized event is a TAG event that is inserted between
|
||
buffers to mark metadata for those buffers.
|
||
|
||
An example of a non serialized event is the FLUSH event.
|
||
|
||
## Pipeline construction
|
||
|
||
The application starts by creating a Pipeline element using
|
||
`gst_pipeline_new()`. Elements are added to and removed from the
|
||
pipeline with `gst_bin_add()` and `gst_bin_remove()`.
|
||
|
||
After adding the elements, the pads of an element can be retrieved with
|
||
`gst_element_get_pad()`. Pads can then be linked together with
|
||
`gst_pad_link()`.
|
||
|
||
Some elements create new pads when actual dataflow is happening in the
|
||
pipeline. With `g_signal_connect()` one can receive a notification when
|
||
an element has created a pad. These new pads can then be linked to other
|
||
unlinked pads.
|
||
|
||
Some elements cannot be linked together because they operate on
|
||
different incompatible data types. The possible datatypes a pad can
|
||
provide or consume can be retrieved with `gst_pad_get_caps()`.
|
||
|
||
Below is a simple mp3 playback pipeline that we constructed. We will use
|
||
this pipeline in further examples.
|
||
|
||
```
|
||
+-------------------------------------------+
|
||
| pipeline |
|
||
| +---------+ +----------+ +----------+ |
|
||
| | filesrc | | mp3dec | | alsasink | |
|
||
| | src-sink src-sink | |
|
||
| +---------+ +----------+ +----------+ |
|
||
+-------------------------------------------+
|
||
```
|
||
|
||
## Pipeline clock
|
||
|
||
One of the important functions of the pipeline is to select a global
|
||
clock for all the elements in the pipeline.
|
||
|
||
The purpose of the clock is to provide a stricly increasing value at the
|
||
rate of one `GST_SECOND` per second. Clock values are expressed in
|
||
nanoseconds. Elements use the clock time to synchronize the playback of
|
||
data.
|
||
|
||
Before the pipeline is set to PLAYING, the pipeline asks each element if
|
||
they can provide a clock. The clock is selected in the following order:
|
||
|
||
- If the application selected a clock, use that one.
|
||
|
||
- If a source element provides a clock, use that clock.
|
||
|
||
- Select a clock from any other element that provides a clock, start
|
||
with the sinks.
|
||
|
||
- If no element provides a clock a default system clock is used for
|
||
the pipeline.
|
||
|
||
In a typical playback pipeline this algorithm will select the clock
|
||
provided by a sink element such as an audio sink.
|
||
|
||
In capture pipelines, this will typically select the clock of the data
|
||
producer, which in most cases can not control the rate at which it
|
||
produces data.
|
||
|
||
## Pipeline states
|
||
|
||
When all the pads are linked and signals have been connected, the
|
||
pipeline can be put in the PAUSED state to start dataflow.
|
||
|
||
When a bin (and hence a pipeline) performs a state change, it will
|
||
change the state of all its children. The pipeline will change the state
|
||
of its children from the sink elements to the source elements, this to
|
||
make sure that no upstream element produces data to an element that is
|
||
not yet ready to accept it.
|
||
|
||
In the mp3 playback pipeline, the state of the elements is changed in
|
||
the order alsasink, mp3dec, filesrc.
|
||
|
||
All intermediate states are traversed for each element resulting in the
|
||
following chain of state changes:
|
||
|
||
* alsasink to READY: the audio device is probed
|
||
|
||
* mp3dec to READY: nothing happens.
|
||
|
||
* filesrc to READY: the file is probed
|
||
|
||
* alsasink to PAUSED: the audio device is opened. alsasink is a sink and returns ASYNC because it did not receive data yet. mp3dec to PAUSED: the decoding library is initialized
|
||
|
||
* filesrc to PAUSED: the file is opened and a thread is started to push data to mp3dec
|
||
|
||
At this point data flows from filesrc to mp3dec and alsasink. Since
|
||
mp3dec is PAUSED, it accepts the data from filesrc on the sinkpad and
|
||
starts decoding the compressed data to raw audio samples.
|
||
|
||
The mp3 decoder figures out the samplerate, the number of channels and
|
||
other audio properties of the raw audio samples and sends out a caps
|
||
event with the media type.
|
||
|
||
Alsasink then receives the caps event, inspects the caps and
|
||
reconfigures itself to process the media type.
|
||
|
||
mp3dec then puts the decoded samples into a Buffer and pushes this
|
||
buffer to the next element.
|
||
|
||
Alsasink receives the buffer with samples. Since it received the first
|
||
buffer of samples, it completes the state change to the PAUSED state. At
|
||
this point the pipeline is prerolled and all elements have samples.
|
||
Alsasink is now also capable of providing a clock to the pipeline.
|
||
|
||
Since alsasink is now in the PAUSED state it blocks while receiving the
|
||
first buffer. This effectively blocks both mp3dec and filesrc in their
|
||
`gst_pad_push()`.
|
||
|
||
Since all elements now return SUCCESS from the
|
||
`gst_element_get_state()` function, the pipeline can be put in the
|
||
PLAYING state.
|
||
|
||
Before going to PLAYING, the pipeline select a clock and samples the
|
||
current time of the clock. This is the base\_time. It then distributes
|
||
this time to all elements. Elements can then synchronize against the
|
||
clock using the buffer running\_time
|
||
base\_time (See also [synchronisation](design/synchronisation.md)).
|
||
|
||
The following chain of state changes then takes place:
|
||
|
||
* alsasink to PLAYING: the samples are played to the audio device
|
||
|
||
* mp3dec to PLAYING: nothing happens
|
||
|
||
* filesrc to PLAYING: nothing happens
|
||
|
||
## Pipeline status
|
||
|
||
The pipeline informs the application of any special events that occur in
|
||
the pipeline with the bus. The bus is an object that the pipeline
|
||
provides and that can be retrieved with `gst_pipeline_get_bus()`.
|
||
|
||
The bus can be polled or added to the glib mainloop.
|
||
|
||
The bus is distributed to all elements added to the pipeline. The
|
||
elements use the bus to post messages on. Various message types exist
|
||
such as ERRORS, WARNINGS, EOS, `STATE_CHANGED`, etc..
|
||
|
||
The pipeline handles EOS messages received from elements in a special
|
||
way. It will only forward the message to the application when all sink
|
||
elements have posted an EOS message.
|
||
|
||
Other methods for obtaining the pipeline status include the Query
|
||
functionality that can be performed with `gst_element_query()` on the
|
||
pipeline. This type of query is useful for obtaining information about
|
||
the current position and total time of the pipeline. It can also be used
|
||
to query for the supported seeking formats and ranges.
|
||
|
||
## Pipeline EOS
|
||
|
||
When the source filter encounters the end of the stream, it sends an EOS
|
||
event to the peer element. This event will then travel downstream to all
|
||
of the connected elements to inform them of the EOS. The element is not
|
||
supposed to accept any more data after receiving an EOS event on a
|
||
sinkpad.
|
||
|
||
The element providing the streaming thread stops sending data after
|
||
sending the EOS event.
|
||
|
||
The EOS event will eventually arrive in the sink element. The sink will
|
||
then post an EOS message on the bus to inform the pipeline that a
|
||
particular stream has finished. When all sinks have reported EOS, the
|
||
pipeline forwards the EOS message to the application. The EOS message is
|
||
only forwarded to the application in the PLAYING state.
|
||
|
||
When in EOS, the pipeline remains in the PLAYING state, it is the
|
||
applications responsability to PAUSE or READY the pipeline. The
|
||
application can also issue a seek, for example.
|
||
|
||
## Pipeline READY
|
||
|
||
When a running pipeline is set from the PLAYING to READY state, the
|
||
following actions occur in the pipeline:
|
||
|
||
* alsasink to PAUSED: alsasink blocks and completes the state change on the
|
||
next sample. If the element was EOS, it does not wait for a sample to complete
|
||
the state change.
|
||
* mp3dec to PAUSED: nothing
|
||
* filesrc to PAUSED: nothing
|
||
|
||
Going to the intermediate PAUSED state will block all elements in the
|
||
`_push()` functions. This happens because the sink element blocks on the
|
||
first buffer it receives.
|
||
|
||
Some elements might be performing blocking operations in the PLAYING
|
||
state that must be unblocked when they go into the PAUSED state. This
|
||
makes sure that the state change happens very fast.
|
||
|
||
In the next PAUSED to READY state change the pipeline has to shut down
|
||
and all streaming threads must stop sending data. This happens in the
|
||
following sequence:
|
||
|
||
* alsasink to READY: alsasink unblocks from the `_chain()` function and returns
|
||
a FLUSHING return value to the peer element. The sinkpad is deactivated and
|
||
becomes unusable for sending more data.
|
||
* mp3dec to READY: the pads are deactivated and the state change completes
|
||
when mp3dec leaves its `_chain()` function.
|
||
* filesrc to READY: the pads are deactivated and the thread is paused.
|
||
|
||
The upstream elements finish their `_chain()` function because the
|
||
downstream element returned an error code (FLUSHING) from the `_push()`
|
||
functions. These error codes are eventually returned to the element that
|
||
started the streaming thread (filesrc), which pauses the thread and
|
||
completes the state change.
|
||
|
||
This sequence of events ensure that all elements are unblocked and all
|
||
streaming threads stopped.
|
||
|
||
## Pipeline seeking
|
||
|
||
Seeking in the pipeline requires a very specific order of operations to
|
||
make sure that the elements remain synchronized and that the seek is
|
||
performed with a minimal amount of latency.
|
||
|
||
An application issues a seek event on the pipeline using
|
||
`gst_element_send_event()` on the pipeline element. The event can be a
|
||
seek event in any of the formats supported by the elements.
|
||
|
||
The pipeline first pauses the pipeline to speed up the seek operations.
|
||
|
||
The pipeline then issues the seek event to all sink elements. The sink
|
||
then forwards the seek event upstream until some element can perform the
|
||
seek operation, which is typically the source or demuxer element. All
|
||
intermediate elements can transform the requested seek offset to another
|
||
format, this way a decoder element can transform a seek to a frame
|
||
number to a timestamp, for example.
|
||
|
||
When the seek event reaches an element that will perform the seek
|
||
operation, that element performs the following steps.
|
||
|
||
1) send a FLUSH_START event to all downstream and upstream peer elements.
|
||
2) make sure the streaming thread is not running. The streaming thread will
|
||
always stop because of step 1).
|
||
3) perform the seek operation
|
||
4) send a FLUSH done event to all downstream and upstream peer elements.
|
||
5) send SEGMENT event to inform all elements of the new position and to complete
|
||
the seek.
|
||
|
||
In step 1) all downstream elements have to return from any blocking
|
||
operations and have to refuse any further buffers or events different
|
||
from a FLUSH done.
|
||
|
||
The first step ensures that the streaming thread eventually unblocks and
|
||
that step 2) can be performed. At this point, dataflow is completely
|
||
stopped in the pipeline.
|
||
|
||
In step 3) the element performs the seek to the requested position.
|
||
|
||
In step 4) all peer elements are allowed to accept data again and
|
||
streaming can continue from the new position. A FLUSH done event is sent
|
||
to all the peer elements so that they accept new data again and restart
|
||
their streaming threads.
|
||
|
||
Step 5) informs all elements of the new position in the stream. After
|
||
that the event function returns back to the application. and the
|
||
streaming threads start to produce new data.
|
||
|
||
Since the pipeline is still PAUSED, this will preroll the next media
|
||
sample in the sinks. The application can wait for this preroll to
|
||
complete by performing a `_get_state()` on the pipeline.
|
||
|
||
The last step in the seek operation is then to adjust the stream
|
||
running_time of the pipeline to 0 and to set the pipeline back to
|
||
PLAYING.
|
||
|
||
The sequence of events in our mp3 playback example.
|
||
|
||
```
|
||
| a) seek on pipeline
|
||
| b) PAUSE pipeline
|
||
+----------------------------------V--------+
|
||
| pipeline | c) seek on sink
|
||
| +---------+ +----------+ +---V------+ |
|
||
| | filesrc | | mp3dec | | alsasink | |
|
||
| | src-sink src-sink | |
|
||
| +---------+ +----------+ +----|-----+ |
|
||
+-----------------------------------|-------+
|
||
<------------------------+
|
||
d) seek travels upstream
|
||
|
||
--------------------------> 1) FLUSH event
|
||
| 2) stop streaming
|
||
| 3) perform seek
|
||
--------------------------> 4) FLUSH done event
|
||
--------------------------> 5) SEGMENT event
|
||
|
||
| e) update running_time to 0
|
||
| f) PLAY pipeline
|
||
```
|