Thanks for all this help!
I found a solution, using the read function instead of recv() - much simpler coding this way, it's not documented well where this function comes from though. Net::HTTP is the child of several large classes...
Another confounding issue was that I suppose Net::HTTP::read_response_haders() just isn't reading the headers "right", it's somehow messing with the byte counts. So I read my own headers now. ;P
Here's a very basic solution. I hope to turn this into a subclass of Net::HTTP, or perhaps instead an instance of IO::Socket::INET and call it Net::Icecast - I'm open to suggestions. You can see also that this nugget is simply of wont for features too:
#!/usr/bin/perl -w
### Written by qbxk for perlmonks
### It is provided as is with no warranties, express or implied, of a
+ny kind. Use posted code at your own risk.
$|++;
use warnings;
use strict;
use Net::HTTP;
use Data::Dumper;
use Carp::Assert;
# use constant USER_AGENT => 'WinampMPEG/2.9'; # I got refused by som
+e public servers unlessen i done it thar way
use constant USER_AGENT => 'Stream-Recorder-0.01';
my %HOST = (
host => 'server.com', port => 8000, mount => '/stream'
);
use constant DEBUG => 1;
sub debug(@) { print STDERR "\n" . join("\n", @_) . "\n"; }
sub debug_raw(@) { print STDERR @_; }
sub open_connection {
my %args = (
host => undef,
port => 80,
mount => '',
user_agent => USER_AGENT,
@_
);
die "Need a host name" unless defined($args{host});
$args{mount} =~ s/^\/+//g;
my $sock = Net::HTTP->new(Host => $args{host}, PeerPort => $args{po
+rt} ) || die $@;
$sock->write_request(GET => "/$args{mount}", 'User-Agent' => $args{
+user_agent}, 'Icy-MetaData' => 1) or die $@;
# my ($code, $mess, %headers) = $sock->read_response_headers( laxed
+ => 1 )
my ($code, $mess, %headers);
while( <$sock> ) {
s/\s*$//g;
last if /^\s*$/;
if( /^(?:HTTP\/1\.[01]|ICY) ([0-9]+) (.+)$/ ) {
($code, $mess) = ($1 +0, $2);
}
else {
my ($h, $v) = split(/:/);
$headers{$h} = $v;
}
}
return ($sock,$code,$mess,%headers);
}
main: {
my ($s,$code, $mess, %headers) = open_connection( %HOST );
debug "$code|$mess\n" . Dumper(\%headers);
# TODO: timeout on $s.
exit if( $code != 200 ); # scream and shout
my ($metaint) = map { (/^icy-metaint$/i && $headers{$_}) or () } ke
+ys %headers;
assert( $metaint > 0 );
open OUT, '>stream-out.mp3';
binmode OUT; # very important
while( 1 ) {
my $buf;
$s->read($buf, $metaint);
print OUT $buf;
my ($metadata, $metalen, $metabyte);
$s->read($metabyte, 1);
$metalen = unpack("C",$metabyte) * 16;
if( $metalen > 0) {
#We have NEW metadata! JOY
$s->read($metadata, $metalen);
$metadata = unpack("A$metalen", $metadata);
assert( $metadata =~ /Stream/, "Not good metadata!" ); #don't
+ dump a lot of BS (binary *#$!), just die.
debug "$metalen - [$metadata]";
}
else {
$metadata = '';
debug_raw "-";
}
}
}
You'll find a clean, "un-meta"ed mp3 file ever growing, called "stream-out.mp3" in your working directory... i've done enough for one night so that's how it stays.
It's not what you look like, when you're doin' what you’re doin'.
It's what you’re doin' when you’re doin' what you look like you’re doin'!
- Charles Wright & the Watts 103rd Street Rhythm Band
-
Are you posting in the right place? Check out Where do I post X? to know for sure.
-
Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
<code> <a> <b> <big>
<blockquote> <br /> <dd>
<dl> <dt> <em> <font>
<h1> <h2> <h3> <h4>
<h5> <h6> <hr /> <i>
<li> <nbsp> <ol> <p>
<small> <strike> <strong>
<sub> <sup> <table>
<td> <th> <tr> <tt>
<u> <ul>
-
Snippets of code should be wrapped in
<code> tags not
<pre> tags. In fact, <pre>
tags should generally be avoided. If they must
be used, extreme care should be
taken to ensure that their contents do not
have long lines (<70 chars), in order to prevent
horizontal scrolling (and possible janitor
intervention).
-
Want more info? How to link
or How to display code and escape characters
are good places to start.