Streaming to shoutcast with libshout

After setting up a shoutcast server I tried to find a way to stream my music through it. When I was a windows user winamp with the shoutcast DSP plugin was a nice solution. But now I wanted to stream music that resides on my linux home server. The Shoutcast Transcoder that can be used for this purpose requires a paid license for mp3 streaming. So libshout from Icecast with a perl binding comes to rescue!

Requirements:

  • installed and working shoutcast DNAS
  • lame (for mp3 transcoding)
  • libshout (if not available through your repos you can get it here)
  • shout-perl
  • MP3::Tag perl module
  • Encode perl module (to make your id3 tags utf8 if a non-latin and non-utf8 charset is used)
To install the Shout.pm perl module after uncompressing the tar.gz you have to do this (as root):
perl Makefile.PL
make
make install

More information is provided here. If you face any problems make sure that you have libvorbislibogg and libtheora libraries as well as their corresponding devel packages installed.

Install the MP3::Tag perl module through your repos (available in Centos rpmforge repo) or through CPAN otherwise (perl -MCPAN -e ‘install MP3::Tag’).

Before installing the Encode perl module check if it is already installed by trying to use it! In centos it is part of the perl installation so there is no need to install it!

Now for the nice part! I edited the example2.pl script that was included with Shout.pm installation files. You can see my script here:

#!/usr/bin/perl -w
use strict;
use bytes;
use Shout;
use MP3::Tag;
use Encode;

###############################################################################
###	C O N F I G U R A T I O N
###############################################################################
use vars qw{$Debug $Lame};
chomp( $Lame = `which lame` );
my $Bitrate = 64;
my $Samplerate = 44100;

### Create a new streaming object
my $streamer = new Shout
	host		=> 'localhost',
	port		=> 8000,
	mount		=> '',
	password	=> 'my password',
	name		=> 'my radio name',
	url			=> 'http://myurl.example.com/',
	genre		=> 'my genre',
	description	=> 'my description',
	bitrate		=> $Bitrate,
	format		=> SHOUT_FORMAT_MP3,
	protocol	=> SHOUT_PROTOCOL_ICY;

$streamer->set_audio_info(SHOUT_AI_BITRATE => $Bitrate, SHOUT_AI_SAMPLERATE => $Samplerate);

###############################################################################
###	M A I N   P R O G R A M
###############################################################################

### Try to connect, aborting on failure
if ( $streamer->open ) {
	printf "Connected to %s port %d...\n", $streamer->host, $streamer->port;
	printf "Will stream to mountpoint '%s'.\n", $streamer->mount;
} else {
	printf "couldn't connect: %s\n", $streamer->get_error;
	exit $streamer->get_errno;
}

### Stream each file specified in the playlist file on the command line

open(PLSFILE, '<', $ARGV[0]) or die $!;

while (<PLSFILE>) {

	my $file = $_;
	chomp($file);

	print STDERR "Can't read '$file': $!\n" unless -r $file;
	print "Sending $file...\n";

	my $mp3file = MP3::Tag->new($file);
	my $mp3title = $mp3file->title();
	my $mp3artist = $mp3file->artist();

	eval {
		$mp3title = Encode::encode("utf8", Encode::decode("iso-8859-7", $mp3title));
		$mp3artist = Encode::encode("utf8", Encode::decode("iso-8859-7", $mp3artist));
	};

	print "$mp3title - $mp3artist\n";

        $streamer->set_metadata(song => "$mp3title - $mp3artist");

	### Run lame in downsampling mode on the file we're going to send
	open( LAME, "-|" ) || exec $Lame, qw{--mp3input -t -b}, $Bitrate, qw{-m j -f -S}, $file, "-";

	my $buff;
	READ: while ((my $len = sysread(LAME, $buff, 4096)) > 0) {
		print STDERR "Read $len bytes...\n" if $Debug;
		$streamer->send( $buff ) && next;

		warn( "send failed: ", $streamer->get_error, "\n" );
		last READ;
	} continue {
		$streamer->sync;
	}

	close LAME;
}

close (PLSFILE);

### Disconnect from the server
$streamer->close;

Copy and paste the above code in a file named streamer.pl and then make it executable (chmod +x streamer.pl).

Edit the fields in the configuration section to suit your needs, especially the host, port and password fields.

On lines 61-64 is the code for converting you id3 tags to utf8 if they are in some other charset. Many of my MP3s have greek characters so I used iso-8859-7 as the source encoding. You may have to edit this if you have id3 tags in other languages with non-latin characters. Or you can comment out (with #) these lines if you don’t need the conversion. Keep in mind that the webpage from shoutcast server defaults its encoding to a latin only charset. So to display your utf8 id3 tags you have to change the encoding manually in your browser. Nevertheless winamp displays utf8 id3 tags correctly so you listeners will be able to read the song title and artist without problems.

You can comment out (with #) lines 55 and 66 if you don’t want anything displayed on the command line while your music is streamed to the server.

You may see the error “fatal error: can’t update LAME-tag frame!” after each song ends. This is a bug in lame and you cannot disable it. I hope it will be fixed in a future version of lame.

Now create a playlist file that will be streamed to the server by doing this:

find / -name *.mp3 > myplaylist

That’s it! You are ready to stream your music by doing this:

./streamer.pl myplaylist

Happy streaming!

15 thoughts on “Streaming to shoutcast with libshout

  1. Hello,
    Thanks for your blog , really appreciate the scripts and infrmation . I have been testing the setup with the latest libshout and shout perl using your streamer script , works very well for me . Sound quality is very good as you can adjust the lame parameters to suit . I have one small problem though, with the metadata. Since I use iso8859-2 charset, there are 4 non standard-latin letters there . The streamer prints the metadata ok , but when it arrives at the shoutcast server , the non-standard chars get mangled . Is there anything we can do when sending the metadata to inform the server that a non-standard charset is used ?
    Best Regards
    Mo

    1. Hello,
      Please see the part of this blog post regarding lines 61-64 of the script. I believe that if you change my “iso-8859-7” to your “iso8859-2” your problem will be solved.
      Also please see my comment regarding the webpage that shoutcast server shows. It uses latin charset by default and you have to change your browser settings to display the utf8 charset.

  2. Hello Atrias,
    Appreciate your reply , I have tried that suggestion already . Did runs with and without the conversion . It does not seem to make any difference . When I compared the utf-8 and iso8859-2 code pages the 4 characters that are non-latin-1 are mapped with the same hex code . Had a bit of a better look with wireshark , on the input into the dnas server , and I can see that the server is being fed with mangled metadata . So the problem lies with the libshout library . I can see that the original character in the metadata which is h’c48d is being sent in ASCII as 25 63 34 25 38 which is %c4%8 . It has lost the last half a byte and gained a % . My libshout version is 2.3.1 , but in the server web page it comes up as 2.2.2 .

    I guess in the mean time it may be the only chance to use the ‘xml method’ and write to dnas directly bypassing libshout . Do you have a small perl script snippet to start me off , which I can use to send the song/artist metadata from the source and into dnas ? something that does the job of :
    $streamer->set_metadata(song => “$mp3artist – $mp3title”)

    but sends it via perl directly in the form :

    http://serverip:port/admin.cgi?mode=updinfo&pass=xxxxxxx&song=$mp3artist – $mp3title

    Best Regards
    Mo

  3. serverip:port/admin.cgi?mode=updinfo&pass=xxxxxxx&song=$mp3artist – mp3title

    Left out the http bit , as it was stuffing around with the line

  4. I give up , can not seem to post the line , without half of it going missing , but it does not look like that , it is missing a whole xml string with charset and metadata definition , and I do not know how to post it so it comes out .

  5. Hi Atrias,

    I got it working finally . The trick was to rewrite the line $streamer->set_metadata(song => “$mp3artist – $mp3title”);

    As is, it is being treated as latin-1 , and the 2byte special characters were getting mangled. So to avoid that the two variables mp3artist and mp3title need to be encapsulated within an xml string which specifies utf-8 encoding of the metadata . The above bit about sending it directly to servr:port is not needed as libshout is capable of doing that fine . So it goes a bit like this (i will leave the xml tags out , as the web page here messes with it)

    $streamer->set_metadata(song => ” $mp3artist – $mp3title”);

    Thanks for all your help
    Mo

  6. Take two :

    $streamer->set_metadata(song => ” ?xml version=/”1.0/” encoding=/”UTF-8/” ? metadata TIT2 $mp3artist – $mp3title TIT2 metadata “);

      1. ./streamer.pl playlist && ./streamer.pl playlist && ./streamer.pl playlist && ./streamer.pl playlist && ./streamer.pl playlist && ./streamer.pl playlist && ./streamer.pl playlist && ./streamer.pl playlist && ./streamer.pl playlist

        also works. another thing that i heard last night while listening on my radio sometimes the sound went like in slowmotion or went fast, same thing if you change playback speed on – then you reconnect on radio and playback speed is with + (plus) then third time when i reconnected it went back on normal just wanted to let you know

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s