MP3-Icecast
view release on metacpan or search on metacpan
package MP3::Icecast;
=head1 NAME
MP3::Icecast - Generate Icecast streams, as well as M3U and PLSv2 playlists.
=head1 SYNOPSIS
use MP3::Icecast;
use MP3::Info;
use IO::Socket;
my $listen_socket = IO::Socket::INET->new(
LocalPort => 8000, #standard Icecast port
Listen => 20,
Proto => 'tcp',
Reuse => 1,
Timeout => 3600);
#create an instance to find all files below /usr/local/mp3
my $finder = MP3::Icecast->new();
$finder->recursive(1);
$finder->add_directory('/usr/local/mp3');
my @files = $finder->files;
#accept TCP 8000 connections
while(1){
next unless my $connection = $listen_socket->accept;
defined(my $child = fork()) or die "Can't fork: $!";
if($child == 0){
$listen_socket->close;
my $icy = MP3::Icecast->new;
#stream files that have an ID3 genre tag of "jazz"
while(@files){
my $file = shift @files;
my $info = new MP3::Info $file;
next unless $info;
next unless $info->genre =~ /jazz/i;
$icy->stream($file,0,$connection);
}
exit 0;
}
#a contrived example to demonstrate that MP3::Icecast
#can generate M3U and PLSv2 media playlists.
print STDERR $icy->m3u, "\n";
print STDERR $icy->pls, "\n";
$connection->close;
}
=head1 ABSTRACT
MP3::Icecast supports streaming Icecast protocol over socket
or other filehandle (including STDIN). This is useful for writing
a streaming media server.
MP3::Icecast also includes support for generating M3U and PLSv2
playlist files. These are common formats supported by most modern
media players, including XMMS, Windows Media Player 9, and Winamp.
=head1 SEE ALSO
The Icecast project
http://www.icecast.org
Namp! (Apache::MP3)
http://namp.sourceforge.net
Unofficial M3U and PLS specifications
http://forums.winamp.com/showthread.php?threadid=65772
=head1 AUTHOR
Allen Day, E<lt>allenday@ucla.eduE<gt>
=head1 COPYRIGHT AND LICENSE
Copyright 2003, Allen Day
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=cut
use strict;
use File::Spec;
use File::Basename 'dirname','basename','fileparse';
use URI::Escape;
use IO::File;
use MP3::Info;
use constant DEBUG => 0;
our $VERSION = '0.02';
our %AUDIO = (
'.mp3' => 'audio/x-mp3',
);
our %FORMAT_FIELDS = (
a => 'artist',
c => 'comment',
d => 'duration',
f => 'filename',
g => 'genre',
l => 'album',
m => 'min',
n => 'track',
q => 'samplerate',
r => 'bitrate',
s => 'sec',
S => 'seconds',
t => 'title',
y => 'year',
);
return $output;
}
=head2 pls
Title : pls
Usage : $pls_text = $icy->pls
Function: generates a PLSv2 string from the
contents of the list returned by files().
files not recognized by MP3::Info are
silently ignored.
Returns : a PLSv2 string
Args : none
=cut
sub pls{
my $self = shift;
my $output = undef;
$output .= "[playlist]$CRLF" if $self->files;
my $c = 0;
foreach my $file ($self->files){
my $info = $self->_get_info($file);
next unless defined($info);
$c++;
$file = $self->_mangle_path($file);
my $time = $info->secs || -1;
my $artist = $info->artist || 'Unknown Artist';
my $album = $info->album || 'Unknown Album';
my $title = $info->title || 'Unknown Title';
$output .= uri_escape(sprintf("File%d=%s${CRLF}Title%d=%s - %s (%s)${CRLF}Length%d=%d$CRLF",$c,$file,$c,$title,$artist,$album,$c,$time));
}
$output .= "NumberOfEntries=$c$CRLF" if $self->files;
$output .= "Version=2$CRLF" if $self->files;
return $output;
}
=head2 stream
Title : streamll: 1 at /raid5a/allenday/projects/MP3/Icecast.pm line 459.
Usage : $icy->stream('/usr/local/mp3/meow.mp3',0);
$icy->stream('/usr/local/mp3/meow.mp3',0,$io_handle);
Function: stream an audio file. prints to STDOUT unless a
third argument is given, in which case ->print() is
called on the second argument. An IO::Handle or
Apache instance will work here.
Returns : true on success, false on failure
Args : 1) system path to the file to stream
2) offset in file to start streaming
3) (optional) object to call ->print() on, rather
than printing to STDOUT
=cut
sub stream{
my ($self,$file,$offset,$handle) = @_;
return undef unless -f $file;
my $info = $self->_get_info($file);
return undef unless defined($info);
my $genre = $info->genre || 'unknown genre';
my $description = $self->description($file) || 'unknown';
my $bitrate = $info->bitrate || 0;
my $size = -s $file || 0;
my $mime = $AUDIO{ lc((fileparse($file,keys(%AUDIO)))[2]) };
my $path = $self->_mangle_path($file);
my $fh = $self->_open_file($file) || die "couldn't open file $file: $!";
binmode($fh);
seek($fh,$offset,0);
my $output = '';
$output .= "ICY ". ($offset ? 206 : 200) ." OK$CRLF";
$output .= "icy-notice1:<BR>This stream requires a shoutcast/icecast compatible player.<BR>$CRLF";
$output .= "icy-notice2:MP3::Icecast<BR>$CRLF";
$output .= "icy-name:$description$CRLF";
$output .= "icy-genre:$genre$CRLF";
$output .= "icy-url: $path$CRLF";
$output .= "icy-pub:1$CRLF";
$output .= "icy-br:$bitrate$CRLF";
$output .= "Accept-Ranges: bytes$CRLF";
if($offset){ $output .= "Content-Range: bytes $offset-" . ($size-1) . "/$size$CRLF" }
$output .= "Content-Length: $size$CRLF";
$output .= "Content-Type: $mime$CRLF";
$output .= "$CRLF";
if(!ref($handle)){
print $output;
} elsif($handle->can('print')) {
$handle->print($output);
} else {
return undef;
}
my $bytes = $size;
while($bytes > 0){
my $data;
my $b = read($fh,$data,2048) || last;
$bytes -= $b;
if(!ref($handle)){
print $data;
} else {
$handle->print($data);
}
}
( run in 0.618 second using v1.01-cache-2.11-cpan-2398b32b56e )