mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 10:11:08 +00:00
docs: improve clock chapter
This commit is contained in:
parent
402f0166c1
commit
d36377ba72
2 changed files with 288 additions and 70 deletions
|
@ -1,91 +1,295 @@
|
||||||
<chapter id="chapter-clocks">
|
<chapter id="chapter-clocks">
|
||||||
<title>Clocks in GStreamer</title>
|
<title>Clocks and synchronization in &GStreamer;</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
To maintain sync in pipeline playback (which is the only case where this
|
When playing complex media, each sound and video sample must be played in a
|
||||||
really matters), &GStreamer; uses <emphasis>clocks</emphasis>. Clocks
|
specific order at a specific time. For this purpose, GStreamer provides a
|
||||||
are exposed by some elements, whereas other elements are merely clock
|
synchronization mechanism.
|
||||||
slaves. The primary task of a clock is to represent the time progress
|
|
||||||
according to the element exposing the clock, based on its own playback
|
|
||||||
rate. If no clock provider is available in a pipeline, the system clock
|
|
||||||
is used instead.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
&GStreamer; derives several <emphasis>time value</emphasis> from the clock
|
&GStreamer; provides support for the following use cases:
|
||||||
and the playback state.
|
<itemizedlist>
|
||||||
It is important to note, that a <emphasis>clock-time</emphasis> is
|
<listitem>
|
||||||
monotonically rising, but the value itself is not meaningful.
|
<para>
|
||||||
Subtracting the <emphasis>base-time</emphasis> yields the
|
Non-live sources with access faster than playback rate. This is
|
||||||
<emphasis>running-time</emphasis>. It is the same as the
|
the case where one is reading media from a file and playing it
|
||||||
<emphasis>stream-time</emphasis> if one plays from start to end at original
|
back in a synchronized fashion. In this case, multiple streams need
|
||||||
rate. The <emphasis>stream-time</emphasis> indicates the position in the
|
to be synchronized, like audio, video and subtitles.
|
||||||
media. The <emphasis>running-time</emphasis> is (re-)set to 0 when the
|
</para>
|
||||||
pipeline starts to play and also after <emphasis>flushing</emphasis> seeks.
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Capture and synchronized muxing/mixing of media from multiple live
|
||||||
|
sources. This is a typical use case where you record audio and
|
||||||
|
video from a microphone/camera and mux it into a file for
|
||||||
|
storage.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Streaming from (slow) network streams with buffering. This is the
|
||||||
|
typical web streaming case where you access content from a streaming
|
||||||
|
server with http.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Capture from live source and and playback to live source with
|
||||||
|
configurable latency. This is used when, for example, capture from
|
||||||
|
a camera, apply an effect and display the result. It is also used
|
||||||
|
when streaming low latency content over a network with UDP.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Simultaneous live capture and playback from prerecorded content.
|
||||||
|
This is used in audio recording cases where you play a previously
|
||||||
|
recorded audio and record new samples, the purpose is to have the
|
||||||
|
new audio perfectly in sync with the previously recorded data.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
&GStreamer; uses a <classname>GstClock</classname> object, buffer
|
||||||
|
timestamps and a SEGMENT event to synchronize streams in a pipeline
|
||||||
|
as we will see in the next sections.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<figure float="1" id="chapter-clock-img">
|
<sect1 id="section-clock-time-types" xreflabel="Clock running-time">
|
||||||
<title>&GStreamer; clock and various times</title>
|
<title>Clock running-time </title>
|
||||||
<mediaobject>
|
<para>
|
||||||
<imageobject>
|
In a typical computer, there are many sources that can be used as a
|
||||||
<imagedata scale="75" fileref="images/clocks.ℑ" format="&IMAGE;" />
|
time source, e.g., the system time, soundcards, CPU performance
|
||||||
</imageobject>
|
counters, ... For this reason, there are many
|
||||||
</mediaobject>
|
<classname>GstClock</classname> implementations available in &GStreamer;.
|
||||||
</figure>
|
The clock time doesn't always start from 0 or from some known value.
|
||||||
|
Some clocks start counting from some known start date, other clocks start
|
||||||
|
counting since last reboot, etc...
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
A <classname>GstClock</classname> returns the
|
||||||
|
<emphasis role="strong">absolute-time</emphasis>
|
||||||
|
according to that clock with <function>gst_clock_get_time ()</function>.
|
||||||
|
The absolute-time (or clock time) of a clock is monotonically increasing.
|
||||||
|
From the absolute-time is a <emphasis role="strong">running-time</emphasis>
|
||||||
|
calculated, which is simply the difference between a previous snapshot
|
||||||
|
of the absolute-time called the <emphasis role="strong">base-time</emphasis>.
|
||||||
|
So:
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
running-time = absolute-time - base-time
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
A &GStreamer; <classname>GstPipeline</classname> object maintains a
|
||||||
|
<classname>GstClock</classname> object and a base-time when it goes
|
||||||
|
to the PLAYING state. The pipeline gives a handle to the selected
|
||||||
|
<classname>GstClock</classname> to each element in the pipeline along
|
||||||
|
with selected base-time. The pipeline will select a base-time in such
|
||||||
|
a way that the running-time reflects the total time spent in the
|
||||||
|
PLAYING state. As a result, when the pipeline is PAUSED, the
|
||||||
|
running-time stands still.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Because all objects in the pipeline have the same clock and base-time,
|
||||||
|
they can thus all calculate the running-time according to the pipeline
|
||||||
|
clock.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="section-buffer-running-time" xreflabel="Buffer running-time">
|
||||||
|
<title>Buffer running-time</title>
|
||||||
|
<para>
|
||||||
|
To calculate a buffer running-time, we need a buffer timestamp and
|
||||||
|
the SEGMENT event that preceeded the buffer. First we can convert
|
||||||
|
the SEGMENT event into a <classname>GstSegment</classname> object
|
||||||
|
and then we can use the
|
||||||
|
<function>gst_segment_to_running_time ()</function> function to
|
||||||
|
perform the calculation of the buffer running-time.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Synchronization is now a matter of making sure that a buffer with a
|
||||||
|
certain running-time is played when the clock reaches the same
|
||||||
|
running-time. Usually this task is done by sink elements. Sink also
|
||||||
|
have to take into account the latency configured in the pipeline and
|
||||||
|
add this to the buffer running-time before synchronizing to the
|
||||||
|
pipeline clock.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Non-live sources timestamp buffers with a running-time starting
|
||||||
|
from 0. After a flushing seek, they will produce buffers again
|
||||||
|
from a running-time of 0.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Live sources need to timestamp buffers with a running-time matching
|
||||||
|
the pipeline running-time when the first byte of the buffer was
|
||||||
|
captured.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="section-buffer-stream-time" xreflabel="Buffer stream-time">
|
||||||
|
<title>Buffer stream-time</title>
|
||||||
|
<para>
|
||||||
|
The buffer stream-time, also known as the position in the stream,
|
||||||
|
is calculated from the buffer timestamps and the preceeding SEGMENT
|
||||||
|
event. It represents the time inside the media as a value between
|
||||||
|
0 and the total duration of the media.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The stream-time is used in:
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Report the current position in the stream with the POSITION
|
||||||
|
query.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The position used in the seek events and queries.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
The position used to synchronize controlled values.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The stream-time is never used to synchronize streams, this is only
|
||||||
|
done with the running-time.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
|
<sect1 id="section-time-overview" xreflabel="Time overview">
|
||||||
|
<title>Time overview</title>
|
||||||
|
<para>
|
||||||
|
Here is an overview of the various timelines used in &GStreamer;.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The image below represents the different times in the pipeline when
|
||||||
|
playing a 100ms sample and repeating the part between 50ms and
|
||||||
|
100ms.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<figure float="1" id="chapter-clock-img">
|
||||||
|
<title>&GStreamer; clock and various times</title>
|
||||||
|
<mediaobject>
|
||||||
|
<imageobject>
|
||||||
|
<imagedata scale="75" fileref="images/clocks.ℑ" format="&IMAGE;" />
|
||||||
|
</imageobject>
|
||||||
|
</mediaobject>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
You can see how the running-time of a buffer always increments
|
||||||
|
monotonically along with the clock-time. Buffers are played when their
|
||||||
|
running-time is equal to the clock-time - base-time. The stream-time
|
||||||
|
represents the position in the stream and jumps backwards when
|
||||||
|
repeating.
|
||||||
|
</para>
|
||||||
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="section-clocks-providers">
|
<sect1 id="section-clocks-providers">
|
||||||
<title>Clock providers</title>
|
<title>Clock providers</title>
|
||||||
|
<para>
|
||||||
|
A clock provider is an element in the pipeline that can provide
|
||||||
|
a <classname>GstClock</classname> object. The clock object needs to
|
||||||
|
report an absoulute-time that is monotonocally increasing when the
|
||||||
|
element is in the PLAYING state. It is allowed to pause the clock
|
||||||
|
while the element is PAUSED.
|
||||||
|
</para>
|
||||||
<para>
|
<para>
|
||||||
Clock providers exist because they play back media at some rate, and
|
Clock providers exist because they play back media at some rate, and
|
||||||
this rate is not necessarily the same as the system clock rate. For
|
this rate is not necessarily the same as the system clock rate. For
|
||||||
example, a soundcard may playback at 44,1 kHz, but that doesn't mean
|
example, a soundcard may playback at 44,1 kHz, but that doesn't mean
|
||||||
that after <emphasis>exactly</emphasis> 1 second <emphasis>according
|
that after <emphasis>exactly</emphasis> 1 second <emphasis>according
|
||||||
to the system clock</emphasis>, the soundcard has played back 44.100
|
to the system clock</emphasis>, the soundcard has played back 44.100
|
||||||
samples. This is only true by approximation. Therefore, generally,
|
samples. This is only true by approximation. In fact, the audio
|
||||||
pipelines with an audio output use the audiosink as clock provider.
|
device has an internal clock based on the number of samples played
|
||||||
This ensures that one second of video will be played back at the same
|
that we can expose.
|
||||||
rate as that the soundcard plays back 1 second of audio.
|
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
Whenever some part of the pipeline requires to know the current clock
|
If an element with an internal clock needs to synchronize, it needs
|
||||||
time, it will be requested from the clock through
|
to estimate when a time according to the pipeline clock will take
|
||||||
<function>gst_clock_get_time ()</function>. The clock-time does not
|
place according to the internal clock. To estimate this, it needs
|
||||||
need to start at 0. The pipeline, which contains the global clock that
|
to slave its clock to the pipeline clock.
|
||||||
all elements in the pipeline will use, in addition has a <quote>base
|
|
||||||
time</quote>, which is the clock time at the point where the
|
|
||||||
pipeline went to the PLAYING state. Each element can subtract the
|
|
||||||
<quote>base time</quote> from the clock-time to know the current
|
|
||||||
running time.
|
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
The clock provider is responsible for making sure that the clock time
|
If the pipeline clock is exactly the internal clock of an element,
|
||||||
always represents the current media time as closely as possible; it
|
the element can skip the slaving step and directly use the pipeline
|
||||||
has to take care of things such as playback latencies, buffering in
|
clock to schedule playback. This can be both faster and more
|
||||||
audio-kernel modules, and so on, since all those could affect a/v sync
|
accurate.
|
||||||
and thus decrease the user experience.
|
Therefore, generally, elements with an internal clock like audio
|
||||||
|
input or output devices will be a clock provider for the pipeline.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
When the pipeline goes to the PLAYING state, it will go over all
|
||||||
|
elements in the pipeline from sink to source and ask each element
|
||||||
|
if they can provide a clock. The last element that can provide a
|
||||||
|
clock will be used as the clock provider in the pipeline.
|
||||||
|
This algorithm prefers a clock from an audio sink in a typical
|
||||||
|
playback pipeline and a clock from source elements in a typical
|
||||||
|
capture pipeline.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
There exist some bus messages to let you know about the clock and
|
||||||
|
clock providers in the pipeline. You can see what clock is selected
|
||||||
|
in the pipeline by looking at the NEW_CLOCK message on the bus.
|
||||||
|
When a clock provider is removed from the pipeline, a CLOCK_LOST
|
||||||
|
message is posted and the application should go to PAUSED and back
|
||||||
|
to PLAYING to select a new clock.
|
||||||
</para>
|
</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
<sect1 id="section-clocks-slaves">
|
<sect1 id="section-clocks-latency">
|
||||||
<title>Clock slaves</title>
|
<title>Latency</title>
|
||||||
<para>
|
<para>
|
||||||
Clock slaves get assigned a clock by their containing pipeline. Their
|
The latency is the time it takes for a sample captured at timestamp X
|
||||||
task is to make sure that media playback follows the time progress as
|
to reach the sink. This time is measured against the clock in the
|
||||||
represented by this clock as closely as possible. For most elements,
|
pipeline. For pipelines where the only elements that synchronize against
|
||||||
that will simply mean to wait until the buffer running-time is reached
|
the clock are the sinks, the latency is always 0 since no other element
|
||||||
before playing back their current sample.
|
is delaying the buffer.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
The buffer running-time is derived from the buffer timestamp and the
|
For pipelines with live sources, a latency is introduced, mostly because
|
||||||
newsegment event preceeding the buffer. A buffer is played synchronized
|
of the way a live source works. Consider an audio source, it will start
|
||||||
with the clock when the clock's running-time has reached exactly the
|
capturing the first sample at time 0. If the source pushes buffers with
|
||||||
buffer running-time; this can be done with the function
|
44100 samples at a time at 44100Hz it will have collected the buffer at
|
||||||
<function>gst_clock_id_wait ()</function>.
|
second 1. Since the timestamp of the buffer is 0 and the time of the
|
||||||
</para>
|
clock is now >= 1 second, the sink will drop this buffer because it is
|
||||||
<para>
|
too late. Without any latency compensation in the sink, all buffers will
|
||||||
For more information on how to write elements that conform to this
|
be dropped.
|
||||||
required behaviour, see the Plugin Writer's Guide.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<sect2 id="section-latency-compensation">
|
||||||
|
<title>Latency compensation</title>
|
||||||
|
<para>
|
||||||
|
Before the pipeline goes to the PLAYING state, it will, in addition to
|
||||||
|
selecting a clock and calculating a base-time, calculate the latency
|
||||||
|
in the pipeline. It does this by doing a LATENCY query on all the sinks
|
||||||
|
in the pipeline. The pipeline then selects the maximum latency in the
|
||||||
|
pipeline and configures this with a LATENCY event.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
All sink elements will delay playback by the value in the LATENCY event.
|
||||||
|
Since all sinks delay with the same amount of time, they will be
|
||||||
|
relative in sync.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
|
<sect2 id="section-latency-dynamic">
|
||||||
|
<title>Dynamic Latency</title>
|
||||||
|
<para>
|
||||||
|
Adding/removing elements to/from a pipeline or changing element
|
||||||
|
properties can change the latency in a pipeline. An element can
|
||||||
|
request a latency change in the pipeline by posting a LATENCY
|
||||||
|
message on the bus. The application can then decide to query and
|
||||||
|
redistribute a new latency or not. Changing the latency in a
|
||||||
|
pipeline might cause visual or audible glitches and should
|
||||||
|
therefore only be done by the application when it is allowed.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
</sect1>
|
</sect1>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
|
@ -73,7 +73,10 @@
|
||||||
<para>
|
<para>
|
||||||
Synchronization is now a matter of making sure that a buffer with a
|
Synchronization is now a matter of making sure that a buffer with a
|
||||||
certain running-time is played when the clock reaches the same
|
certain running-time is played when the clock reaches the same
|
||||||
running-time. Usually this task is done by sink elements.
|
running-time. Usually this task is done by sink elements. Sink also
|
||||||
|
have to take into account the latency configured in the pipeline and
|
||||||
|
add this to the buffer running-time before synchronizing to the
|
||||||
|
pipeline clock.
|
||||||
</para>
|
</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
|
|
||||||
|
@ -120,11 +123,11 @@
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
<sect2>
|
<sect2>
|
||||||
<title>Parser elements </title>
|
<title>Parser/Decoder/Encoder elements </title>
|
||||||
<para>
|
<para>
|
||||||
Parser elements must use the incomming timestamps and transfer those
|
Parser/Decoder elements must use the incomming timestamps and transfer
|
||||||
to the resulting output buffers. They are allowed to interpolate or
|
those to the resulting output buffers. They are allowed to interpolate
|
||||||
reconstruct timestamps on missing input buffers when they can.
|
or reconstruct timestamps on missing input buffers when they can.
|
||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
@ -139,8 +142,18 @@
|
||||||
buffer timestamps.
|
buffer timestamps.
|
||||||
</para>
|
</para>
|
||||||
</sect2>
|
</sect2>
|
||||||
|
|
||||||
|
<sect2>
|
||||||
|
<title>Muxer elements</title>
|
||||||
|
<para>
|
||||||
|
Muxer elements should use the incomming buffer running-time to mux the
|
||||||
|
different streams together. They should copy the incomming running-time
|
||||||
|
to the outgoing buffers.
|
||||||
|
</para>
|
||||||
|
</sect2>
|
||||||
|
|
||||||
<sect2> <title> Sink elements </title>
|
<sect2>
|
||||||
|
<title>Sink elements</title>
|
||||||
<para>
|
<para>
|
||||||
If the element is intended to emit samples at a specific time (real time
|
If the element is intended to emit samples at a specific time (real time
|
||||||
playing), the element should require a clock, and thus implement the
|
playing), the element should require a clock, and thus implement the
|
||||||
|
@ -148,8 +161,9 @@
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
The sink should then make sure that the sample with running-time is played
|
The sink should then make sure that the sample with running-time is played
|
||||||
exactly when the pipeline clock reaches that running-time. Some elements
|
exactly when the pipeline clock reaches that running-time + latency.
|
||||||
might use the clock API such as <function>gst_clock_id_wait()</function>
|
Some elements might use the clock API such as
|
||||||
|
<function>gst_clock_id_wait()</function>
|
||||||
to perform this action. Other sinks might need to use other means of
|
to perform this action. Other sinks might need to use other means of
|
||||||
scheduling timely playback of the data.
|
scheduling timely playback of the data.
|
||||||
</para>
|
</para>
|
||||||
|
|
Loading…
Reference in a new issue