/usr/share/doc/liquidsoap/html/clocks.html is in liquidsoap 1.0.0-4build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | <?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML \
1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type" />
<title>Liquidsoap :: Clocks</title>
<link href="style.css" type="text/css" rel="stylesheet" />
<link href="homepage.css" type="text/css" rel="stylesheet" />
</head>
<body>
<div id="wrapper">
<div id="header">
<div id="logo">
<h1>Liquidsoap</h1>
<h2>audio stream generation</h2>
</div>
<div>
<ul id="menu">
<li id="menu-about">
<a href="index.html">about</a></li>
<li id="menu-doc-index">
<a href="documentation.html">documentation</a></li>
<li id="menu-doc-api">
<a href="reference.html">API</a></li>
<li id="menu-doc-snippets">
<a href="scripts/index.html">snippets</a></li>
<li id="menu-developers">
<a href="http://savonet.rastageeks.org/">developers</a></li>
</ul>
</div>
</div>
<div id="content"><div>
<h3>Clocks</h3>
<p>
In the <a href="quick_start.html">quickstart</a> and in the introduction to liquidsoap
<a href="sources.html">sources</a>, we have described a simple world in which sources
communicate with each other, creating and transforming data that
composes multimedia streams.
In this simple view, all sources produce data at the same rate,
animated by a single clock: at every cycle of the clock,
a fixed amount of data is produced.
</p>
<p>
While this simple picture is useful to get a fair idea of what's going on
in liquidsoap, the full picture is more complex: in fact, a streaming
system might involve <em>multiple clocks</em>, or in other words several
time flows.
</p>
<p>
It is only in very particular cases that liquidsoap scripts
need to mention clocks explicitly. Otherwise, you won't even notice
how many clocks are involved in your setup: indeed, liquidsoap can figure
out the clocks by itself, much like it infers types.
Nevertheless, there will sometimes be cases where your script cannot
be assigned clocks in a correct way, in which case liquidsoap will
complain. For that reason, every user should eventually get a minimum
understanding of clocks.
</p>
<p>
In the following, we first describe why we need clocks.
Then we go through the possible errors that any user might encounter
regarding clocks.
Finally, we describe how to explicitly use clocks,
and show a few striking examples of what can be achieved that way.
</p>
<h4>Why multiple clocks</h4>
<p>
The first reason is <b>external</b> to liquidsoap: there is simply
not a unique notion of time in the real world.
Your computer has an internal clock which indicates
a slightly different time than your watch or another computer's clock.
Moreover, when communicating with a remote computer, network
latency causes extra time distortions.
Even within a single computer there are several clocks: notably, each
soundcard has its own clock, which will tick at a slightly different
rate than the main clock of the computer.
Since liquidsoap communicates with soundcards and remote computers,
it has to take those mismatches into account.
</p>
<p>
There are also some reasons that are purely <b>internal</b> to liquidsoap:
in order to produce a stream at a given speed,
a source might need to obtain data from another source at
a different rate. This is obvious for an operator that speeds up or
slows down audio (<code>stretch</code>). But it also holds more subtly
for <code>cross</code>, <code>smart_cross</code> as well as the
derived operators: during the lapse of time where the operator combines
data from an end of track with the beginning of the other other,
the crossing operator needs twice as much stream data. After ten tracks,
with a crossing duration of six seconds, one more minute will have
passed for the source compared to the time of the crossing operator.
</p>
<p>
In order to avoid inconsistencies caused by time differences,
while maintaining a simple and efficient execution model for
its sources, liquidsoap works under the restriction that
one source belongs to a unique clock,
fixed once for all when the source is created.
</p>
<p>
The graph representation of streaming systems can be adapted
into a good representation of what clocks mean.
One simply needs to add boxes representing clocks:
a source can belong to only one box,
and all sources of a box produce streams at the same rate.
For example,
<code>output.icecast(fallback([crossfade(playlist(...)),jingles]))</code>
yields the following graph:
</p>
<img alt="Graph representation with clocks" src="./images/graph_clocks.png" /><p>
Here, clock_2 was created specifically for the crossfading
operator; the rate of that clock is controlled by that operator,
which can hence accelerate it around track changes without any
risk of inconsistency.
The other clock is simply a wallclock, so that the main stream
is produced following the “real” time rate.
</p>
<h4>Error messages</h4>
<p>
Most of the time you won't have to do anything special about clocks:
operators that have special requirements regarding clocks will do
what's necessary themselves, and liquidsoap will check that everything is
fine. But if the check fails, you'll need to understand the error,
which is what this section is for.
</p>
<h5>Disjoint clocks</h5>
<p>
On the following example, liquidsoap will issue the fatal error
<code>a source cannot belong to two clocks</code>:
</p>
<pre>
s = playlist("~/media/audio")
output.alsa(s) # perhaps for monitoring
output.icecast(mount="radio.ogg",%vorbis,crossfade(s))
</pre>
<p>
Here, the source <code>s</code> is first assigned the ALSA clock,
because it is tied to an ALSA output.
Then, we attempt to build a <code>crossfade</code> over <code>s</code>.
But this operator requires its source to belong to a dedicated
internal clock (because crossfading requires control over the flow
of the of the source, to accelerate it around track changes).
The error expresses this conflict:
<code>s</code> must belong at the same time to the ALSA clock
and <code>crossfade</code>'s clock.
</p>
<h5>Nested clocks</h5>
<p>
On the following example, liquidsoap will issue the fatal error
<code>cannot unify two nested clocks</code>:
</p>
<pre>
jingles = playlist("jingles.lst")
music = rotate([1,10],[jingles,playlist("remote.lst")])
safe = rotate([1,10],[jingles,single("local.ogg")])
q = fallback([crossfade(music),safe])
</pre>
<p>
Let's see what happened.
The <code>rotate</code> operator, like most operators, operates
within a single clock, which means that <code>jingles</code>
and our two <code>playlist</code> instances must belong to the same clock.
Similarly, <code>music</code> and <code>safe</code> must belong to that
same clock.
When we applied crossfading to <code>music</code>,
the <code>crossfade</code> operator created its own internal clock,
call it <code>cross_clock</code>,
to signify that it needs the ability to accelerate at will the
streaming of <code>music</code>.
So, <code>music</code> is attached to <code>cross_clock</code>,
and all sources built above come along.
Finally, we build the fallback, which requires that all of its
sources belong to the same clock.
In other words, <code>crossfade(music)</code> must belong
to <code>cross_clock</code> just like <code>safe</code>.
The error message simply says that this is forbidden: the internal
clock of our crossfade cannot be its external clock – otherwise
it would not have exclusive control over its internal flow of time.
</p>
<p>
The same error also occurs on <code>add([crossfade(s),s])</code>,
the simplest example of conflicting time flows, described above.
However, you won't find yourself writing this obviously problematic
piece of code. On the other hand, one would sometimes like to
write things like our first example.
</p>
<p>
The key to the error with our first example is that the same
<code>jingles</code> source is used in combination with <code>music</code>
and <code>safe</code>. As a result, liquidsoap sees a potentially
nasty situation, which indeed could be turned into a real mess
by adding just a little more complexity. To obtain the desired effect
without requiring illegal clock assignments, it suffices to
create two jingle sources, one for each clock:
</p>
<pre>
music = rotate([1,10],[playlist("jingles.lst"),
playlist("remote.lst")])
safe = rotate([1,10],[playlist("jingles.lst"),
single("local.ogg")])
q = fallback([crossfade(music),safe])
</pre>
<p>
There is no problem anymore: <code>music</code> belongs to
<code>crossfade</code>'s internal clock, and <code>crossfade(music)</code>,
<code>safe</code> and the <code>fallback</code> belong to another clock.
</p>
<h4>The clock API</h4>
<p>
There are only a couple of operations dealing explicitly with clocks.
</p>
<p>
The function <code>clock.assign_new(l)</code> creates a new clock
and assigns it to all sources from the list <code>l</code>.
For convenience, we also provide a wrapper, <code>clock(s)</code>
which does the same with a single source instead of a list,
and returns that source.
With both functions, the new clock will follow (the computer's idea of)
real time, unless <code>sync=false</code> is passed, in which case
it will run as fast as possible.
</p>
<p>
The old (pre-1.0.0) setting <code>root.sync</code> is superseded
by <code>clock.assign_new()</code>.
If you want to run an output as fast as your CPU allows,
just attach it to a new clock without synchronization:
</p>
<pre>clock.assign_new(sync=false,[output.file(%vorbis,"audio.ogg",source)])
</pre>
<p>
This will automatically attach the appropriate sources to that clock.
However, you may need to do it for other operators if they are totally
unrelated to the first one.
</p>
<p>
The <code>buffer()</code> operator can be used to communicate between
any two clocks: it takes a source in one clock and builds a source
in another. The trick is that it uses a buffer: if one clock
happens to run too fast or too slow, the buffer may empty or overflow.
</p>
<p>
Finally, <code>get_clock_status</code> provides information on
existing clocks and track their respective times:
it returns a list containing for each clock a pair
<code>(name,time)</code> indicating
the clock id its current time in <em>clock cycles</em> –
a cycle corresponds to the duration of a frame,
which is given in ticks, displayed on startup in the logs.
The helper function <code>log_clocks</code> built
around <code>get_clock_status</code> can be used to directly
obtain a simple log file, suitable for graphing with gnuplot.
Those functions are useful to debug latency issues.
</p>
<h4>External clocks: decoupling latencies</h4>
<p>
The first reason to explicitly assign clocks is to precisely handle
the various latencies that might occur in your setup.
</p>
<p>
Most input/output operators (ALSA, AO, Jack, OSS, etc)
require their own clocks. Indeed, their processing rate is constrained
by external sound APIs or by the hardware itself.
Sometimes, it is too much of an inconvenience,
in which case one can set <code>clock_safe=false</code> to allow
another clock assignment –
use at your own risk, as this might create bad latency interferences.
</p>
<p>
Currently, <code>output.icecast</code> does not require to belong
to any particular clock. This allows to stream according to the
soundcard's internal clock, like in most other tools:
in <code>output.icecast(%vorbis,mount="live.ogg",input.alsa())</code>,
the ALSA clock will drive the streaming of the soundcard input via
icecast.
</p>
<p>
Sometimes, the external factors tied to Icecast output cannot be
disregarded: the network may lag. If you stream a soundcard input
to Icecast and the network lags, there will be a glitch in the
soundcard input – a long enough lag will cause a disconnection,
in which case there won't be anybody to notice the huge glitch.
This might be undesirable, and is certainly disappointing if you
are recording a backup of your precious soundcard input using
<code>output.file</code>: by default it will suffer the same
latencies and glitches, while in theory it could be perfect.
To fix this you can explicitly separate Icecast (high latency,
low quality acceptable) from the backup and soundcard input (low latency,
high quality wanted):
</p>
<pre>
input = input.oss()
clock.assign_new(id="icecast",
[output.icecast(%mp3,mount="blah",mksafe(buffer(input)))])
output.file(
%mp3,"record-%Y-%m-%d-%H-%M-%S.mp3",
input)
</pre>
<p>
Here, the soundcard input and file output end up in the OSS
clock. The icecast output
goes to the explicitly created <code>"icecast"</code> clock,
and a buffer is used to
connect it to the soundcard input. Small network lags will be
absorbed by the buffer. Important lags and possible disconnections
will result in an overflow of the buffer.
In any case, the OSS input and file output won't be affected
by those latencies, and the recording should be perfect.
The Icecast quality is also better with that setup,
since small lags are absorbed by the buffer and do not create
a glitch in the OSS capture, so that Icecast listeners won't
notice the lag at all.
</p>
<p>
Note that explicitly assigning a new clock for the Icecast
output was unnecessary: liquidsoap would have chosen the default
wallclock otherwise. However, it is better practice to force
it manually, to make to obtain the wanted clock assignment.
It is especially important since you might change your script
later, and you want to avoid an unnoticed change of the
clock assignment.
</p>
<h4>Internal clocks: exploiting multiple cores</h4>
<p>
Clocks can also be useful even when external factors are not an issue.
Indeed, several clocks run in several threads, which creates an opportunity
to exploit multiple CPU cores.
The story is a bit complex because OCaml has some limitations on
exploiting multiple cores, but in many situations most of the computing
is done in C code (typically decoding and encoding) so it parallelizes
quite well.
</p>
<p>
Typically, if you run several outputs that do not share much (any) code,
you can put each of them in a separate clock.
For example the following script takes one file and encodes it as MP3
twice. You should run it as <code>liquidsoap EXPR -- FILE</code>
and observe that it fully exploits two cores:
</p>
<pre>
def one()
clock.assign_new(sync=false,
[output.file(%mp3,"/dev/null",single(argv(1)))])
end
one()
one()
</pre>
</div></div>
<div>
<div id="footer"> 2003-2011 Savonet team</div>
</div>
</div>
</body></html>
|