Friday, January 21, 2011

Your Own Private Radio (Station)

"Radio Knobs" by Joe Lencioni
I had to add a streaming radio station to a client's web site last year.
This particular installation was a for a music library. We already had all the tracks in multiple formats including mp3. We simply wanted to push out a random stream of content from the library.
The web server was running Debian "Lenny" on Intel.

The process was pretty straightforward except for having the streamer launch when the server restarted.

If all is working, you  can listen to the Lalela.com radio station using your favorite streaming music player.

Choosing the Tech
We need a basic mp3 stream that will work with popular music players.
icecast2 is the most popular Open Source system for publishing streams. That one's a no-brainer.
Unfortunately Ices, the program from the Icecast group that creates those streams no longer supports the mp3 format. And iTunes doesn't play Ogg. So we need another solution for creating the original stream.

The ezstream application does exactly what need. But the Debian package didn't include a script to run when the server boots.

Installation
Use your favorite method of installing Debian packages.
Both "icecast2" and "ezstream" are in the Stable distribution.

Configuration
Create a playlist for ezstream (instructions are in the README), and configure icecast by editing /etc/icecast2/icecast.xml


The icecast2 package provided a launch script, but ezstream did not.
While I'm not an expert on launch scripts, I was able cobble something together.
Here's my init.d script for ezstream:


#! /bin/sh
### BEGIN INIT INFO
# Provides:          ezstream
# Required-Start:    $remote_fs
# Required-Stop:     $remote_fs
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Example initscript
# Description:       This file should be used to construct scripts to be
#                    placed in /etc/init.d.
### END INIT INFO

# Author: Lon Koenig

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="EZstream streaming audio source for icecast"
NAME=ezstream
DAEMON=/usr/bin/$NAME
DAEMON_ARGS="-c /var/www/ezstream/ezstream_mp3.xml"
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

# Read configuration variable file if it is present
#[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Not using a "default." Configuration file is defined above in DAEMON_ARGS

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
. /lib/lsb/init-functions

#
# Function that starts the daemon/service
#
do_start()
{
# Return
#   0 if daemon has been started
#   1 if daemon was already running
#   2 if daemon could not be started
start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --background --quiet --pidfile $PIDFILE --exec $DAEMON -- \
$DAEMON_ARGS \
|| return 2
# Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend
# on this one.  As a last resort, sleep for some time.
}

#
# Function that stops the daemon/service
#
do_stop()
{
# Return
#   0 if daemon has been stopped
#   1 if daemon was already stopped
#   2 if daemon could not be stopped
#   other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently.  A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}

#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
return 0
}

case "$1" in
  start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
  stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
 reload|force-reload)

# If do_reload() is not implemented then leave this commented out
# and leave 'force-reload' as an alias for 'restart'.

log_daemon_msg "Reloading $DESC" "$NAME"
do_reload
log_end_msg $?
;;
  restart|force-reload)
#
# If the "reload" option is implemented then remove the
# 'force-reload' alias
#
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
 0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
 *)
  # Failed to stop
log_end_msg 1
;;
esac
;;
  *)
echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
#echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
exit 3
;;
esac

:

That's it! Log into the server at http://{your_host_here}:8000/admin/ to see the management panel.
Pull up http://{your_host_here}:8000/live.nsv.3mu in your favorite streamer to hear the tunes.

No comments:

Post a Comment

Please leave your comment here.