#!/usr/bin/perl # demo test case to collect mjpeg frames from a Linksys WVC54G # # The Linksys WVC54G claims to only support MPEG4 (ASF) but is in also # support MJPEG # # Assuming the NetCam's IP address is set the default # you can change formats these (minimal) URLs # # MPEG4(ASF): "http://192.168.1.115/adm/file.cgi?h_videotype=mpeg4&todo=Save" # MJPEG: "http://192.168.1.115/adm/file.cgi?h_videotype=mjpeg&todo=save" # # # In MJPEG "mode" there are two methods / access URLs to acquire the Jpeg stream # # 1: http server-push or JPEG images # and # 2: cgi stream of images. # # the "first" way is to access via the URL path # "http://192.168.1.115/img/mjpeg.jpg # which provides a server-push'ed series of Jpegs # # HTTP/1.0 200 OK # Content-type: multipart/x-mixed-replace; boundary=--ServerPush # # --ServerPush # Content-type: image/jpeg # # \xff\xd8\..... # --ServerPush # Content-type: image/jpeg # # # Image size is not given, thus next boundary maker "--ServerPush" # must be located to Identify end of the image. # # Errors in the stream are handed by scanning to the next instance of the # boundary marker "--ServerPush" # Any read delays of a few seconds or more between frames should indicate # a dead or dropped connection and the network should be reestablished. # # # the "second" method is accessed via the URL path: # "http://192.168.1.115/img/mjpeg.cgi # providing a streamed series of JPegs. # The format is a 12 character numeric string followed by the image data. # the 11 character numeric string consists of 6 digits for the image size # three digits for Width and three digits for Height # # eg: # SSSSSSWWWHHHJPEG_IMAGE_DATA #or # 003316320240\xff\xd8\.......... # # in the above case the image size is 3316, width=320, height=240. # Note that no HTTP codes are issued before the data ( eg: HTTP/1.0 200 OK ) # instead the video data stream starts immediately # # Errors are handled by closing the TCP connection and establishing the link # use strict; use IO::Socket; use Getopt::Long; our $F; my $iphost="192.168.1.115:80"; my $target_path="/img/mjpeg.jpg"; my $icount=90; my $nowrite=0; my $image_count=0; my ($data, $jpeg_data); my ($verbose, $cgimethod); my ($stime, $rtime); select((select(STDOUT), $| = 1)[0]); select((select(STDERR), $| = 1)[0]); sub main { GetOptions ('verbose+' => \$verbose, 'iphost=s' => \$iphost, 'cgi!' => \$cgimethod, 'nowrite+' => \$nowrite, 'path=s' => \$target_path, 'count=i' => \$icount, ); $target_path="/" unless $target_path; if ( $#ARGV == 0 ) { if ($ARGV[0] =~ m#([^/]+\.[^/]+)(/?.*?)$# ) { $iphost=$1; $target_path=$2; } } $iphost .= ":80" unless ( $iphost =~ /:/ ); $target_path="/img/mjpeg.cgi" if ( $cgimethod ); print STDERR "Not saving images\n" if $nowrite; &connect_socket(); $stime=time(); if ( $cgimethod ) { &get_mjpeg_cgi(); } else { &get_mjpeg_jpg(); } $rtime = time() - $^T; print STDERR "Stats: $image_count frames in $rtime seconds\n\t", int( $image_count / $rtime ), " fps\n"; } sub get_mjpeg_cgi { my ($jpeg_length, $jpeg_width, $jpeg_height); while(1) { my ($j); last if ( $image_count > $icount ); RC: $j = read($F, $data, 12); if (!$j) { print "read error : image $image_count ($j) : $! \n"; if ( ! $! ) { print "retrying...\n"; sleep(1); } &reset_socket; goto RC; } if( $j != 12 ) { print "header to short ", length($data), "\n"; &reset_socket; next; } if ($data =~ /(\d\d\d\d\d\d)(\d\d\d)(\d\d\d)/) { $jpeg_length=$1; $jpeg_width=$2; $jpeg_height=$3; } else { print "failed to parse head ($data)\n"; &reset_socket; next; } $j = read($F, $jpeg_data, $jpeg_length); if( $j != $jpeg_length ) { print "data read to short", length($jpeg_data), "\n"; &reset_socket; next; } dump_dat() unless ( $nowrite ); } } sub get_mjpeg_jpg { $/="--ServerPush"; #split on this marker. RJ: while ( $data = <$F> ) { return if ( $image_count > $icount ); $data =~ /jpeg..(.*)/s; $jpeg_data = $1; $jpeg_data =~ s/\r\n$//; next if ( length($jpeg_data) < 1000); # a dumb test dump_dat() unless ( $nowrite ); } if (!eof($F)) { print "not EOF : $! \n"; sleep(2); goto RJ; } } sub dump_dat { open(JP, ">/tmp/jpeg_$image_count.jpg"); print JP $jpeg_data; close(JP); print "jpeg_$image_count.jpg: ", -s "jpeg_$image_count.jpg", "\n"; return; } sub reset_socket { close($F); &connect_socket; } sub connect_socket { my $retry=0; print "connecting: $iphost $target_path\n"; while( $retry++ < 5 ) { $F = IO::Socket::INET->new( PeerAddr => $iphost, Proto => "tcp", Type => SOCK_STREAM, Timeout => 10) and last; sleep 2; } die "Failed to open Socket : retry = $retry : error = $!\n" if ($F == undef); print "connected: $iphost $target_path\n"; # tell it to the server. print $F "GET $target_path HTTP/1.0\r\n" . "Accept: */*\r\n" . "Keep-Alive: 300" . "Connection: keep-alive" . "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n", "\r\n"; } &main; exit;