#!/usr/bin/perl -w # # # Simple script to dump raw PCM audio from SkyIPCam 747/747W to STDOUT # # Original version by Brian Rudy (brudyNO@SPAMpraecogito.com) # # To dump raw audio out to ALSA # ./test-audio-dump.pl --camip mycam --username admin --password admin --debug | aplay -c 1 -f S16_LE -r 8000 --nonblock # v0.03 3/28/2010 # Update incorporating basic header parsing based on Fitivision CGI spec. # # v0.02 9/18/2008 # Update to remove the 28 byte header in each frame. # # v0.01 9/12/2008 # First working version. Has prolblem with periodic 'beat' noise in samples. # use strict; require LWP; require LWP::Debug; use Getopt::Long; my ($debug,$username,$password,$camip); my $optresults = GetOptions ( "debug" => \$debug, # If debugging messages are desired "username=s" => \$username, "password=s" => \$password, "camip=s" => \$camip # IP address/hostname of camera ); #LWP::Debug::level('+'); # We make our own specialization of LWP::UserAgent that provides # user/password. { package RequestAgent; our @ISA = qw(LWP::UserAgent); sub new { my $self = LWP::UserAgent::new(@_); $self->agent("lwp-request/sets-grabber"); $self; } sub get_basic_credentials { return ($username, $password); } } my $ua = RequestAgent->new; my $URL = 'http://' . $camip . '/cgi/audio/audio.cgi?type=PCM'; my $expected_length; my $bytes_received = 0; my $chunk_buffer = ""; # The chunk buffer is used for removal of MIME chunk info print STDERR "Sending request for $URL\n" if $debug; my $res = $ua->request(HTTP::Request->new(GET => $URL), sub { my($chunk, $res) = @_; $bytes_received += length($chunk); unless (defined $expected_length) { $expected_length = $res->content_length || 0; } if ($expected_length) { if ($debug) { printf STDERR "%d%% - ", 100 * $bytes_received / $expected_length; } } print STDERR "$bytes_received bytes received\n" if $debug; $chunk_buffer = clean_stream($chunk_buffer . $chunk); }); if ($debug) { print $res->status_line, "\n"; } # Clean the MIME chunk info from the stream sub clean_stream { my ($dirty_chunk) = @_; my $minsize = 60; print STDERR "Dirty chunk is " . length($dirty_chunk) . " bytes\n" if $debug; # Verify that the chunk we are searching is at least as large as the search parameter if (length($dirty_chunk) >= $minsize) { # Our chunk is big enough to search # Search all but only dump up to the the last $minsize bytes my $clean_chunk = $dirty_chunk; $clean_chunk =~ s/--myboundary\r\n//gs; # Remove the Content Length and the 28 byte header my $header; if (($header) = $clean_chunk =~ m/Content-Length: \d+\r\n\r\n(.{28})/gs) { my ($frame_number,$size,$type,$second,$microsecond,$format,$status) = $header =~ m/(.{4})(.{4})(.{4})(.{4})(.{4})(.{4})(.{4})/gs; $clean_chunk =~ s/Content-Length: \d+\r\n\r\n.{28}//gs; if (defined $frame_number) { print STDERR "Found frame " . unpack("%L*", $frame_number) . "\n" if $debug; print STDERR "Found size " . unpack("%L*", $size) . "\n" if $debug; print STDERR "Found type " . chr(unpack("%L*", $type)) . "\n" if $debug; print STDERR "Found second " . unpack("%L*", $second) . "\n" if $debug; print STDERR "Found microsecond " . unpack("%L*", $microsecond) . "\n" if $debug; print STDERR "Found format " . sprintf("%b",unpack("%L*", $format)) . "\n" if $debug; print STDERR "Found status " . sprintf("%b",unpack("%L*", $status)) . "\n" if $debug; } } my $remaining_dirty = substr($clean_chunk, -$minsize); print STDERR "There are " . length($remaining_dirty) . " bytes left to search\n" if $debug; # Dump the known clean part to STDOUT $clean_chunk = substr($clean_chunk, 0, -$minsize); print STDERR "Found " . length($clean_chunk) . " bytes of clean data\n" if $debug; print STDOUT $clean_chunk; # Return the remaining dirty part return $remaining_dirty; } else { print STDERR "There are " . length($dirty_chunk) . " bytes left to search\n" if $debug; return $dirty_chunk; } }