ctx->guides->icecast
Using icecast and mpd for web radio on OpenBSD
The goal of this guide is to show a simple yet powerful web radio setup. There is a radio stream server (typically with high bandwidth) and a number of music players that are the main source of music. At most one player can be live at any given moment. The server broadcasts the stream of the live music source while at the same time providing a fallback stream when no source is live. The fallback stream can be an always-on random playlist. This way the radio listeners are transferred to the live stream when it becomes available and back to the fallback when it stops.
Configuring the radio server
First things first, on the radio server you will need:
$ pkg_add icecast mpd mpc
Then, copy the sample configuration file that ships with icecast:
$ cp /usr/local/share/examples/icecast/icecast.xml.dist \
/var/icecast/icecast.xml
Edit the configuration file to your liking, but make sure you include
the following, because this is the part that implements the fallback
playlist logic. The no-mount
option blocks client connections to the
fallback stream; so this setup allows listeners only on the /live.ogg
mount.
<mount>
<mount-name>/play.ogg</mount-name>
<no-mount>1</no-mount>
</mount>
<mount>
<mount-name>/live.ogg</mount-name>
<fallback-mount>/play.ogg</fallback-mount>
<fallback-override>1</fallback-override>
</mount>
To run icecast do /etc/rc.d/icecast start
.
Now lets configure the mpd music player to be the never-ending loop
playlist on the server that feeds the fallback stream. The important
thing in /etc/mpd.conf
is the following part:
audio_output {
type "shout"
encoding "ogg"
name "myradio"
host "localhost"
port "8000"
mount "/play.ogg"
password "hackme"
bitrate "128"
format "44100:16:2"
}
To run the music player daemon do /etc/rc.d/mpd start
.
All is ready at the server side. To automate the starting of programs
on boot add the following to your /etc/rc.conf.local
configuration:
pkg_scripts="icecast mpd"
Now, simply create playlists using an mpd client like mpc
and put the player in random and repeat mode. You can also use
a simple 10-second crossfade effect:
$ mpc random on
$ mpc repeat on
$ mpc crossfade 10
To make all these settings persistent accross mpd restarts (and reboots)
set the state_file
option in mpd.conf
:
state_file "/var/spool/mpd/mpdstate"
By now you should be able to listen to the /play.ogg
stream by using
the /live.ogg
mount point that is located at
http://myradio.example.com:8000/live.ogg
. You can test it from a
client host with mplayer installed like this:
$ mplayer "http://myradio.example.com:8000/live.ogg"
If you experience connectivity problems from the outside world, the
easiest way to resolve them is adding the wildcard IP address to the
icecast.xml
configuration:
<listen-socket>
<port>8000</port>
<bind-address>0.0.0.0</bind-address>
</listen-socket>
Otherwise, you can create an additional listen-socket
node and specify
the IP address of your public network interface.
Configuring the radio sources
A music source only needs a player with icecast support, so simply install mpd and ncmpc, a nice curses mpd client.
$ pkg_add mpd ncmpc
Again, the relevant portion of the configuration file is shown here:
audio_output {
type "shout"
encoding "ogg"
name "myuser@myradio"
host "localhost"
port "8000"
mount "/live.ogg"
password "hackme"
bitrate "128"
format "44100:16:2"
}
Notice that the only difference with the server is the mount point and
the name. By convention the name includes the user that is currently
live. It is crucial for the bitrate
and format
options to be the
same with the /play.ogg
source for the fallback trick to work.
Furthermore, you see that the host
option is still set to localhost
,
but no icecast is running on the source machines. This is on purpose,
and the idea is to use an ssh tunnel between the radio server and the
music source to avoid transmitting plaintext passwords. This is done
like this, where myradio.example.com
is the hostname of the server:
$ ssh -N -L8000:localhost:8000 myradio.example.com
The above command forwards port 8000 on localhost
to port 8000 on the
myradio.example.com
machine. If you do not want an interactive
session you can also use the -N
switch, but that's optional. Once the
tunnel is created and mpd is in playback state it becomes the live
playing source that is streaming on the /live.ogg
mount point. No
other source can take the lock while the live stream is active.
Final considerations and metadata handling
If all worked-out well so far you have a working radio setup that supports both live broadcasting and a fallback loop playlist. You should only publish the location of the live mount point because listeners that use the fallback mount directly will continue to listen to this stream even if the live one comes up. That is:
http://myradio.example.com:8000/live.ogg
In order to use metadata in other services, icecast provides an XSLT
interface to write scripts. For a list of all available options you can
put the following code in /var/icecast/web/all.xsl
:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="*">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
This dynamically generated XML document is visible at
http://myradio.example.com:8000/all.xsl
. For example, if you need a
text-only representation of the currently playing song you could use
something like the following in a file of its own, and access it
likewise:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/icestats">
<!-- only show the first source -->
<xsl:apply-templates select="source[1]"/>
</xsl:template>
<xsl:template match="source">
<xsl:choose>
<xsl:when test="title or artist">
<xsl:if test="artist"><xsl:value-of select="artist" /> - </xsl:if>
<xsl:value-of select="title" />
</xsl:when>
<xsl:otherwise>Unknown</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This is a rather complex example that tests the existence of artist and
title metadata in order to construct a valid string at all times. The
important thing here is the use of the <xsl:value-of select="title" />
construct. Furthermore, the source[1]
hack ensures that only one (the
first) mount point is selected, so it displays either the live or the
fallback stream's information and not both.
Cheers!
lostd@