Tag Archives: webcam

Accessing JPG from IP Cameras

I use off-the-shelf IP cameras but monitor them from a central server instead of looking at each camera’s own interface. In this setup it can be better to PULL images off the camera from the server instead of being PUSHED from the the camera to the server. Pushing images requires setting up each camera with the server information and a schedule to upload pictures, which can be sparse in options depending on the camera.

By pulling images on demand, the server can control the time interval and no special setup is required for each camera other than possibly setting up a user name and password for access. Most will require Basic Auth to access in image.

Here’s the JPG access URLs for cameras I’ve tested:

Panasonic cameras (C30A, C131A, C20A, C1A)

http://your.camera.ip.addr/SnapshotJPEG?Resolution=640x480&Quality=Standard&View=Normal&Count=1

Foscam (FI8918W, FI8910W, FI8904W, FI8905W, etc)

http://your.camera.ip.addr/snapshot.cgi

Trendnet (TV-IP110W)

These cameras are not good for this purpose. Only an ActiveX viewer is available for the web or FTP for single images to a server.

Windows PCs with webcams running Yawcam.

Enabling the web access port 8888 for example.

http://your.camera.ip.addr:8888/out.jpg

Variable range scrollback for archive images

For a given image of class=’camview’  this jQuery javascript will scroll back a variable amount depending on the mouse location over the image.

The mouse over right edge of the image represents the most recent image from the archive. The bottom-left corner represents 24 hours ago. But to have finer granularity at reviewing more recent images, scrolling the mouse over the top of the image will scroll back one time-increment every 10 pixels. Moving the mouse from right to left over the bottom of the image will scroll back over a full 24 hours (1440 one-minute images) skipping frames as needed to fit the mouse resolution. Mouse heights between the top and bottom edges will have a linearly scaled proportion of archive history.

$('.camview').mousemove(function(event) {
 var x = event.pageX - $(this).offset().left;
 var y = event.pageY - $(this).offset().top;
 var mindx = 0.1;
 var maxdx = 1440.0 / $(this).width();
 var dy = 1.0 - y / $(this).height();
 var dx = dy * mindx + (1.0-dy) * maxdx;
 var arc = ($(this).width() - x) * dx;
 $(this).find('img').attr('src', 'camjpg.php?cam=' + $(this).attr('id') + '&arc=' + arc.toFixed(0));
});

Http proxy for accessing local area network images

To access multiple cameras on a local area network from a single web page, you need to proxy the requests from your web server. Since many netcams require BasicAuth to access images,

A PHP script to handle the proxy request can be found here: http://www.troywolf.com/articles/php/class_http/
To process a set of cameras, store the camera access data in an array such as this

$cams = array(
 "c30a" => array(
 "name" => "c30a",
 "type" => "http",
 "http" => "http://192.168.3.71/SnapshotJPEG?Resolution=640x480&Quality=Standard&Count=1",
 "http_user" => "<user>",
 "http_pass" => "<password>",
 "title" => "Backyard",
 "live" => "//192.168.3.71/CgiStart?page=Single&Language=0"
 ),
 "c20a" => array (
 "name" => "c20a",
 "type" => "http",
 "http" => "http://192.168.3.73/SnapshotJPEG?Resolution=640x480&Quality=Standard&Count=1",
 "http_user" => "<user>",
 "http_pass" => "<password>",
 "title" => "Garage",
 "live" => "//192.168.3.73/CgiStart?page=Single&Language=0"
 ),
}
?>

Include the above config.php in the php script that will handle the proxy request. It will look up the local area network destination from the ‘http’ element of the array, and pass the request along the camera, then copy the image returned from the camera to the caller.

url = $cam['http'];
 $h->postvars = $_POST;
 if (!$h->fetch($h->url, 0, "", $http_user, $http_pass)) {
   header("HTTP/1.0 501 Script Error");
   echo "proxy.php had an error attempting to query the url";
   exit();
 }

 $http_user = "";
 $http_pass = "";
 if (isset($cam['http_user'])) $http_user = $cam['http_user'];
 if (isset($cam['http_pass'])) $http_pass = $cam['http_pass'];

 $h->url = $cam['http'];
 $h->postvars = $_POST;
 if (!$h->fetch($h->url, 0, "", $http_user, $http_pass)) {
 header("HTTP/1.0 501 Script Error");
 echo "proxy.php had an error attempting to query the url";
 exit();
 }

 // Forward the headers to the client.
 $ary_headers = explode("\n", $h->header);
 foreach($ary_headers as $hdr) { header($hdr); }

 // Send the response body to the client.
 echo $h->body;
?>

To use this proxy from a web page, the javascript with jQuery would request an image from the same page as the main web site.

 $('#c30a img').attr('src', 'proxy.php?cam=c30a');

Using a Trendnet IP110W camera with a fixed FTP location

The TRENDnet TV-IP110W has few options when setting up an FTP transfer. It will always place the image in sub-folder based on the date such as 20110308/230117_9.jpg for the image taken on March 8, 2011 at 23:01:17.9. This makes it difficult to use the image on a static html webcam page.

To move the last picture taken to a fixed file location, a cron job is needed to find the JPG file and move/rename it to a fixed name. In this case the camera is set to take an image every 60 seconds and FTP it to the server. The following script is also set to run as a cron job every 60 seconds.

In this example trendnet is the Filename Prefix set in the Event Configuration >> General Setting tab and /path/to/ftp is the location set in Event Server

# move current picture to *_old
mv /path/to/ftp/trendnet.jpg /path/to/ftp/trendnet_old.jpg

# find where the trendnet put the image and move it to the ftp root location
find /path/to/ftp/trendnet -name '*.jpg' -exec mv {} /path/to/ftp/trendnet.jpg \;

Chumby Webcam Image Cycling


Sample Actionscript for showing 4 webcams on a single Chumby display and cycling through 9 webcams. Every second replacing one of the images with the next one in the sequence.

<br />
class CamView.Main {&lt;br&gt;<br />
  static var urls:Array = [&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam1.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam2.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam3.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam4.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam5.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam6.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam7.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam8.jpg&quot;,&lt;br&gt;<br />
  &quot;http://192.168.2.10/cam9.jpg&quot; ];&lt;br&gt;<br />
  static var numurls = 9;&lt;br&gt;<br />
  private var parent:MovieClip;&lt;br&gt;<br />
  private var tf:TextField;&lt;br&gt;<br />
  private var mcTL:MovieClip;&lt;br&gt;<br />
  private var mcTR:MovieClip;&lt;br&gt;<br />
  private var mcBL:MovieClip;&lt;br&gt;<br />
  private var mcBR:MovieClip;&lt;br&gt;<br />
  static var count:Number = 0;&lt;br&gt;<br />
  static var urlidx:Number = 4;</p>
<p>  function UpdateText(mc:MovieClip) {&lt;br&gt;<br />
    // Add time based url extension to force non cached images&lt;br&gt;<br />
    var time:Date = new Date();&lt;br&gt;<br />
    var sec:String = String(time.getSeconds());&lt;br&gt;<br />
    var cnt:String = String(time.getMinutes()) + sec;&lt;br&gt;<br />
    var urlext:String = &quot;?t=&quot; + cnt;</p>
<p>    var mlMyLoader:MovieClipLoader = new MovieClipLoader();</p>
<p>    // rotate images and urls&lt;br&gt;<br />
    switch (count) {&lt;br&gt;<br />
      case 0: mlMyLoader.loadClip(urls[urlidx] + urlext, &quot;mCam1&quot;); break;&lt;br&gt;<br />
      case 1: mlMyLoader.loadClip(urls[urlidx] + urlext, &quot;mCam2&quot;); break;&lt;br&gt;<br />
      case 3: mlMyLoader.loadClip(urls[urlidx] + urlext, &quot;mCam3&quot;); break;&lt;br&gt;<br />
      case 2: mlMyLoader.loadClip(urls[urlidx] + urlext, &quot;mCam4&quot;); break;&lt;br&gt;<br />
    }&lt;br&gt;</p>
<p>    if (++count == 4) count = 0;&lt;br&gt;<br />
    if (++urlidx == numurls) urlidx = 0;</p>
<p>    delete mlMyLoader;&lt;br&gt;<br />
  }</p>
<p>  function Main(mc:MovieClip) {&lt;br&gt;<br />
    // save reference to main movieclip&lt;br&gt;<br />
    this.parent = mc;</p>
<p>    var mlMyLoader:MovieClipLoader = new MovieClipLoader();</p>
<p>    mcTL = mc.createEmptyMovieClip(&quot;mCam1&quot;, mc.getNextHighestDepth());&lt;br&gt;<br />
    mcTR = mc.createEmptyMovieClip(&quot;mCam2&quot;, mc.getNextHighestDepth());&lt;br&gt;<br />
    mcBL = mc.createEmptyMovieClip(&quot;mCam3&quot;, mc.getNextHighestDepth());&lt;br&gt;<br />
    mcBR = mc.createEmptyMovieClip(&quot;mCam4&quot;, mc.getNextHighestDepth());&lt;/p&gt;</p>
<p>    // assume cam images are 640x480, scale by 1/4 for Chumby display&lt;br&gt;<br />
    mcTL._xscale = 25;&lt;br&gt;<br />
    mcTL._yscale = 25;&lt;br&gt;<br />
    mlMyLoader.loadClip(urls[0], &quot;mCam1&quot;);</p>
<p>    mcTR._x = 160;&lt;br&gt;<br />
    mcTR._xscale = 25;&lt;br&gt;<br />
    mcTR._yscale = 25;&lt;br&gt;<br />
    mlMyLoader.loadClip(urls[1], &quot;mCam2&quot;);</p>
<p>    mcBL._y = 120;&lt;br&gt;<br />
    mcBL._xscale = 25;&lt;br&gt;<br />
    mcBL._yscale = 25;&lt;br&gt;<br />
    mlMyLoader.loadClip(urls[2], &quot;mCam3&quot;);</p>
<p>    mcBR._x = 160;&lt;br&gt;<br />
    mcBR._y = 120;&lt;br&gt;<br />
    mcBR._xscale = 25;&lt;br&gt;<br />
    mcBR._yscale = 25;&lt;br&gt;<br />
    mlMyLoader.loadClip(urls[3], &quot;mCam4&quot;);</p>
<p>    delete mlMyLoader;</p>
<p>    var mytimer = setInterval(UpdateText, 1000, mc);<br />
  }</p>
<p>  static function main(mc:MovieClip) {&lt;br&gt;<br />
    var app = new Main(mc);&lt;br&gt;<br />
  }&lt;br&gt;<br />
}&lt;br&gt;<br />