docs: improve clock chapter

This commit is contained in:
Wim Taymans 2012-10-08 13:22:30 +02:00
parent 402f0166c1
commit d36377ba72
2 changed files with 288 additions and 70 deletions

View file

@ -1,27 +1,176 @@
<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 </para>
according to the element exposing the clock, based on its own playback <para>
rate. If no clock provider is available in a pipeline, the system clock &GStreamer; provides support for the following use cases:
is used instead. <itemizedlist>
<listitem>
<para>
Non-live sources with access faster than playback rate. This is
the case where one is reading media from a file and playing it
back in a synchronized fashion. In this case, multiple streams need
to be synchronized, like audio, video and subtitles.
</para>
</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>
<sect1 id="section-clock-time-types" xreflabel="Clock running-time">
<title>Clock running-time </title>
<para> <para>
&GStreamer; derives several <emphasis>time value</emphasis> from the clock In a typical computer, there are many sources that can be used as a
and the playback state. time source, e.g., the system time, soundcards, CPU performance
It is important to note, that a <emphasis>clock-time</emphasis> is counters, ... For this reason, there are many
monotonically rising, but the value itself is not meaningful. <classname>GstClock</classname> implementations available in &GStreamer;.
Subtracting the <emphasis>base-time</emphasis> yields the The clock time doesn't always start from 0 or from some known value.
<emphasis>running-time</emphasis>. It is the same as the Some clocks start counting from some known start date, other clocks start
<emphasis>stream-time</emphasis> if one plays from start to end at original counting since last reboot, etc...
rate. The <emphasis>stream-time</emphasis> indicates the position in the </para>
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. 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> </para>
<figure float="1" id="chapter-clock-img"> <figure float="1" id="chapter-clock-img">
@ -33,59 +182,114 @@
</mediaobject> </mediaobject>
</figure> </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
clock is now >= 1 second, the sink will drop this buffer because it is
too late. Without any latency compensation in the sink, all buffers will
be dropped.
</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>
<para> <para>
For more information on how to write elements that conform to this All sink elements will delay playback by the value in the LATENCY event.
required behaviour, see the Plugin Writer's Guide. Since all sinks delay with the same amount of time, they will be
relative in sync.
</para> </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>

View file

@ -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>
@ -140,7 +143,17 @@
</para> </para>
</sect2> </sect2>
<sect2> <title> Sink elements </title> <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>
<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>