The plot
Following many user requests , we were considering adding this format to the list of supported encoders.
As for mp3 encoding, this support was subject to some legal issues. These issues were indeed the initial motivation for implementing icecast2 using the patent-free ogg codecs and container for streaming data.
However, it seems that many users and net-radios are still asking for these formats. There can be several motivations for that, among which I can list the following major ones:
- These formats are supported by more players and, in particular, embedded devices, which are the main target for net-radios
- For the case of AAC+, the codec supports a very good audio quality for low bandwidths, such as 64 and 32kb/s.
The philosophy
There is a constant complain from the open source communities about the use of patent-encumbered formats. In particular, those formats are not allowed in the Wikimedia commons and the corresponding encoders, in particular mp3 encoders, are not supported by for instance the Debian distribution.
In a similar intent to push for a wider usage of patent-free codecs, the icecast project reimplemented its streaming server in order to use the ogg container and codecs.
All these actions are very important. In particular, the support for ogg/theora video files in firefox, and hopefully the major HTML 5 browser is mainly the result of wikipedia refusing other video formats. Indeed, wikipedia is now a major website, and it would be insane to not support the videos there.
However, I for myself do not like when these actions become too restrictive to the user. For this matter, I personally prefer freedom of choice among other considerations. Also, as a matter of fact, when trying to change the situation about these issues, one has to take into account the relative importance of each actor. It is because wikipedia is already huge that it has managed to change the things.
Hence, in the case of net-radios, a majority of potential users are asking for AAC+ and other patent-encumbered formats. Although this is not the ideal situation we would like to see, there comes a point were either the free-software projects have something to propose, or the users will just go for a proprietary software, which is even worse to the situation.
For this purpose, I believed it was important to add such support into our software. However, as you will see later, we do not implement ourself the encoding algorithm, but only added support for external encoders. That way, we simply let the user choose what he wants to use, but remain a patent-free and open-source software.
The implementation
Since mp3 was, and still is, the major target for net-radios, icecast2 still has the possibility to stream this format. In fact, this format, as well as AAC+, does not have headers, such that it is possible to decode a stream at any point in it. Hence, in this case, icecast only acts as a proxy, and the only constraint is to send it the encoded data at real rate.
Hence, the main library for streaming to icecast, libshout, parses the audio data that it receives, and schedules the streaming according to real rate. Additionally, it also supports a raw mechanism, for which there would be not such mangling.
In the case of liquidsoap, we did not need this feature of the libshout since the software is already working in real time. Furthermore, the source connection protocol provides a content-type header field, which allows icecast to display the correct mime for the data format that is streamed.
All in all, in order to send AAC+ data to icecast, one needs to send the correct mime-type in the source connection headers, and then send audio data at the correct rate.
However, the libshout did not allow to set an arbitrary content-type header. Hence, I proposed a patch. It was proposed for an inclusion into main code, since I believe it is interesting not only for our specific need, but also for any other generic data format support. Although the patch was accepted, it was never committed.
So, we decided to go on: since liquidsoap need not the real rate mangling feature of the libhsout, and since the source protocol is mainly a basic HTTP, we decided it was worth a custom implementation. This implementation was quite easy, thanks to the powerful capabilities of the OCaml language, and resulted in a custom implementation, ocaml-cry, that does the same as libshout, except that it does not control the rate of the data flow sent to the icecast server.
Then, we also needed to support encoding data to the AAC format. After looking at the only available open-source encoder, aacplusenc, I tried to propose some changes in order to export a library and be able to use it in liquidsoap. However, these propositions did not make it into upstream’s code, so I decided to also move on and use directly the encoder binary.
Hence, using only the good old unix standard input/output mechanism, it is possible to start an encoder as a child process, send it the raw PCM data to its standard input and read the encoded data from its standard output. This lead to the development of the external output operators, which include output.icecast.external.
Although this implementation was not easy, it now appears to work quite well in the current SVN version. In particular, the encoder process exits correctly for the FLAC encoder, which is needed in order to stream correctly, since the ogg container needs a restart for every track. Hence, we also gained support for encoding to the FLAC audio format, which was not binded for OCaml (yet ?) because its callback-driven API makes it tricky to do.
Additionally, the support for external processes as encoders allows to use the lame binary for encoding mp3 data. Hence, liquidsoap can now encode mp3 data without the need of a built-in support for it. And, as always, the idea of using external processes spread over several other operators, including the support for external file and stream decoders, enabling support for many audio format (in fact almost all supported in linux, using mplayer).
In conclusion, liquidsoap now has a fairly good support for AAC+ audio streaming, as well as a many new formats for decoding files and streams. It also support arbitrary formats and encoders using a custom variant of the output.icecast.external. Hence, provided your format can be decoded from any point in the stream, you should be able to use liquidsoap to stream data to icecast as soon as you have a binary which is able to receive RAW or WAV PCM data to its standard input and send encoded data to its standard output.
As an illustration, here is an implementation of the output.icecast.aacplusenc:
def output.icecast.aacplusenc(
~id="output.icecast.aacplusenc",
~start=true,
~restart=true,
~restart_delay=3,
~host="localhost",
~port=8000,
~user="source",
~password="hackme",
~genre="Misc",
~url="http://savonet.sf.net/",
~description="OCaml Radio!",
~public=true,
~dumpfile="",
~mount="Use [name]",
~name="Use [mount]",
~protocol="http",
~aacplusenc="aacplusenc",
~bitrate=64,
~restart_on_crash=false,
~restart_on_new_track=false,
~restart_encoder_delay=3600,
~headers=[],
s)
# Metadata update is set by ICY with icecast
def aacplusenc_p(m)
"#{aacplusenc} - - #{bitrate}"
end
output.icecast.external(id=id,
process=aacplusenc_p,
bitrate=bitrate,
start=start,
restart=restart,
restart_delay=restart_delay,
host=host,
port=port,
user=user,
password=password,
genre=genre,
url=url,
description=description,
public=public,
dumpfile=dumpfile,
name=name,
mount=mount,
protocol=protocol,
header=true,
restart_on_crash=restart_on_crash,
restart_on_new_track=restart_on_new_track,
headers=headers,
restart_encoder_delay=restart_encoder_delay,
format="audio/aacp",
s)
end
blog.rastageeks.org
