blib/lib/MP3/Icecast/Simple.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 18 | 79 | 22.7 |
branch | 0 | 14 | 0.0 |
condition | 0 | 17 | 0.0 |
subroutine | 6 | 11 | 54.5 |
pod | 5 | 5 | 100.0 |
total | 29 | 126 | 23.0 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package MP3::Icecast::Simple; | ||||||
2 | |||||||
3 | =head1 NAME | ||||||
4 | |||||||
5 | MP3::Icecast::Simple - Simple MP3::Icecast wrapper | ||||||
6 | |||||||
7 | =head1 SYNOPSIS | ||||||
8 | |||||||
9 | use MP3::Icecast::Simple; | ||||||
10 | |||||||
11 | $icy = MP3::Icecast::Simple->new( | ||||||
12 | description => "Station", | ||||||
13 | server => '127.0.0.1:8000', | ||||||
14 | password => 'password', | ||||||
15 | local_port => 1234, | ||||||
16 | bitrate => 96 | ||||||
17 | ); | ||||||
18 | $icy->play("/path/to/files"); | ||||||
19 | |||||||
20 | =head1 ABSTRACT | ||||||
21 | |||||||
22 | MP3::Icecast::Simple is a simple MP3::Icecast wrapper, that can be | ||||||
23 | used to create a SHOUTcast/Icecast broadcast source easy. | ||||||
24 | |||||||
25 | =head1 SEE ALSO | ||||||
26 | |||||||
27 | MP3::Icecast module by Allen Day (MP3::Icecast) | ||||||
28 | |||||||
29 | Nullsoft SHOUTcast DNAS home | ||||||
30 | http://www.shoutcast.com | ||||||
31 | |||||||
32 | =cut | ||||||
33 | |||||||
34 | 1 | 1 | 33751 | use strict; | |||
1 | 2 | ||||||
1 | 37 | ||||||
35 | 1 | 1 | 4 | use base 'MP3::Icecast'; | |||
1 | 3 | ||||||
1 | 677 | ||||||
36 | 1 | 1 | 1245 | use Time::HiRes qw(sleep); | |||
1 | 2060 | ||||||
1 | 4 | ||||||
37 | 1 | 1 | 1343 | use IO::Socket; | |||
1 | 30171 | ||||||
1 | 5 | ||||||
38 | 1 | 1 | 2074 | use LWP::UserAgent; | |||
1 | 71972 | ||||||
1 | 39 | ||||||
39 | 1 | 1 | 12 | use vars qw(@ISA $VERSION); | |||
1 | 2 | ||||||
1 | 6982 | ||||||
40 | |||||||
41 | $VERSION = "0.2"; | ||||||
42 | |||||||
43 | =head1 METHODS | ||||||
44 | |||||||
45 | =head2 new | ||||||
46 | |||||||
47 | Title : new | ||||||
48 | Usage : $icy = MP3::Icecast::Simple->new(%arg) | ||||||
49 | Function: Create a new MP3::Icecast::Simple instance | ||||||
50 | Returns : MP3::Icecast::Simple object | ||||||
51 | Args : description Name of the radiostation | ||||||
52 | server Address and port of SHOUTcast server | ||||||
53 | password Password to SHOUTcast server | ||||||
54 | local_port Local port | ||||||
55 | bitrate Initial bitrate | ||||||
56 | |||||||
57 | =cut | ||||||
58 | |||||||
59 | sub new { | ||||||
60 | 0 | 0 | 1 | my ($class, %arg) = @_; | |||
61 | 0 | my $self = bless {%arg}, $class; | |||||
62 | |||||||
63 | 0 | return $self; | |||||
64 | } | ||||||
65 | |||||||
66 | =head2 play | ||||||
67 | |||||||
68 | Title : play | ||||||
69 | Usage : $icy->play($dir, $resursive); | ||||||
70 | Function: Play a directory of .mp3 files | ||||||
71 | Returns : | ||||||
72 | Args : dirname Path to direactory with .mp3 files | ||||||
73 | recursive Flag determining whether a directory is recursively searched for files (optional) | ||||||
74 | |||||||
75 | =cut | ||||||
76 | |||||||
77 | sub play { | ||||||
78 | 0 | 0 | 1 | my $self = shift; | |||
79 | 0 | my $dir = shift; | |||||
80 | 0 | 0 | my $recursive = shift || 0; | ||||
81 | |||||||
82 | 0 | my $listen_socket = IO::Socket::INET->new( | |||||
83 | LocalPort => $self->{local_port}, | ||||||
84 | Listen => 20, | ||||||
85 | Proto => 'tcp', | ||||||
86 | Reuse => 0, | ||||||
87 | Timeout => 3600 | ||||||
88 | ); | ||||||
89 | |||||||
90 | 0 | $self->recursive($recursive); | |||||
91 | 0 | $self->add_directory($dir); | |||||
92 | |||||||
93 | 0 | my @files = $self->files; | |||||
94 | 0 | while(1) { | |||||
95 | 0 | 0 | next unless my $connection = $listen_socket->accept; | ||||
96 | 0 | 0 | defined(my $child = fork()) or die "Can't fork: $!"; | ||||
97 | 0 | 0 | if($child == 0) { | ||||
98 | 0 | $listen_socket->close; | |||||
99 | 0 | $connection->print($self->header); | |||||
100 | 0 | 0 | $self->stream($_, $connection) || last for(@files); | ||||
101 | } | ||||||
102 | 0 | $connection->close; | |||||
103 | } | ||||||
104 | 0 | exit 0; | |||||
105 | } | ||||||
106 | |||||||
107 | =head2 stream | ||||||
108 | |||||||
109 | Title : stream (rewrited from original MP3::Icecast package with improvements) | ||||||
110 | Usage : $icy->stream($file, $handle); | ||||||
111 | Function: Play a file via socket | ||||||
112 | Returns : 1 if file was transmitted successfully, | ||||||
113 | undef if an error occured | ||||||
114 | Args : file File to stream | ||||||
115 | handle Socket handler | ||||||
116 | |||||||
117 | =cut | ||||||
118 | |||||||
119 | sub stream { | ||||||
120 | 0 | 0 | 1 | my ($self, $file, $handle) = @_; | |||
121 | 0 | 0 | return undef unless -f $file; | ||||
122 | |||||||
123 | 0 | my $info = $self->_get_info($file); | |||||
124 | 0 | 0 | return undef unless defined($info); | ||||
125 | |||||||
126 | 0 | 0 | my $size = -s $file || 0; | ||||
127 | 0 | 0 | my $bitrate = $info->bitrate || 1; | ||||
128 | 0 | 0 | my $description = $self->description($file) || 'unknown'; | ||||
129 | 0 | 0 | my $fh = $self->_open_file($file) || die "couldn't open file $file: $!"; | ||||
130 | |||||||
131 | 0 | binmode $fh; | |||||
132 | |||||||
133 | 0 | 0 | 0 | if(ref($handle) and $handle->can('print')) { | |||
134 | 0 | my $bytes = $size; | |||||
135 | 0 | print $description."\n"; | |||||
136 | 0 | $self->updinfo($description); | |||||
137 | 0 | while($bytes > 0) { | |||||
138 | 0 | my $data; | |||||
139 | 0 | 0 | my $b = read($fh, $data, $bitrate * 128) || last; | ||||
140 | 0 | $bytes -= $b; | |||||
141 | 0 | $handle->print($data); | |||||
142 | 0 | sleep $b / ($bitrate * 128); | |||||
143 | } | ||||||
144 | 0 | return 1; | |||||
145 | } | ||||||
146 | 0 | return undef; | |||||
147 | } | ||||||
148 | |||||||
149 | =head2 updinfo | ||||||
150 | |||||||
151 | Title : updinfo | ||||||
152 | Usage : Not a publick method | ||||||
153 | Function: Update current song title on the SHOUTcast server | ||||||
154 | Returns : 1 if song title updated successfully, | ||||||
155 | undef if an error occured | ||||||
156 | Args : description Name of current song | ||||||
157 | |||||||
158 | =cut | ||||||
159 | |||||||
160 | sub updinfo { | ||||||
161 | 0 | 0 | 1 | my ($self, $songname) = @_; | |||
162 | 0 | my $ua = LWP::UserAgent->new; | |||||
163 | 0 | $ua->timeout(10); | |||||
164 | 0 | $ua->env_proxy; | |||||
165 | 0 | $ua->agent('Mozilla/5.0'); | |||||
166 | 0 | my $response = $ua->get("http://$self->{server}/admin.cgi?mode=updinfo&pass=$self->{password}&song=" . $songname); | |||||
167 | 0 | 0 | return undef unless ($response->is_success); | ||||
168 | 0 | return 1; | |||||
169 | } | ||||||
170 | |||||||
171 | =head2 header | ||||||
172 | |||||||
173 | Title : header | ||||||
174 | Usage : Not a publick method | ||||||
175 | Function: Create a ICY response header | ||||||
176 | Returns : ICY response header | ||||||
177 | Args : none | ||||||
178 | |||||||
179 | =cut | ||||||
180 | |||||||
181 | sub header { | ||||||
182 | 0 | 0 | 1 | my $self = shift; | |||
183 | 0 | my $output = ''; | |||||
184 | 0 | my $CRLF = "\015\012"; | |||||
185 | |||||||
186 | 0 | $output .= "ICY 200 OK$CRLF"; | |||||
187 | 0 | $output .= "icy-notice1: This stream requires a shoutcast/icecast compatible player. $CRLF"; |
|||||
188 | 0 | $output .= "icy-notice2:MP3::Icecast::Simple $CRLF"; |
|||||
189 | 0 | $output .= "icy-name:" . $self->{description} . $CRLF; | |||||
190 | 0 | $output .= "icy-pub:1$CRLF"; | |||||
191 | 0 | $output .= "icy-br:" . $self->{bitrate} . $CRLF; | |||||
192 | 0 | $output .= "Accept-Ranges: bytes$CRLF"; | |||||
193 | 0 | $output .= "Content-Type: audio/x-mp3$CRLF"; | |||||
194 | 0 | $output .= "$CRLF"; | |||||
195 | |||||||
196 | 0 | return $output; | |||||
197 | } | ||||||
198 | |||||||
199 | 1; | ||||||
200 | |||||||
201 | =head1 AUTHOR | ||||||
202 | |||||||
203 | Gregory A. Rozanoff, rozanoff@gmail.com | ||||||
204 | |||||||
205 | =head1 COPYRIGHT AND LICENSE | ||||||
206 | |||||||
207 | Copyright 2006, Gregory A. Rozanoff | ||||||
208 | |||||||
209 | This library is free software; you can redistribute it and/or modify | ||||||
210 | it under the same terms as Perl itself. | ||||||
211 | |||||||
212 | =cut |