Tinkerlog header image 2

Tinkerlog

Alex’ blog

Webcam snapshots with Flex3

November 3rd, 2007 · 11 Comments · flex

This time no hardware hacks. At least no selfmade hardware.

Lately I saw websites that used Adobe flash to get access to my local webcam. I thought it would be fun to have someone take a snapshot of himself and post it on my site.

And it is a fun project to learn some Adobe flex.

snapshotr.jpg

To be honest, I am new to flex and php as well. I am sure that it contains bugs and security issues. If you find some, then please let me know. This is a proof of concept, if you read on and try it on your own site, you do it on your own risk. You have been warned ;-).

Source

Here are some source code snippets. There are three parts:

  • camcomment.mxml, the flex application to take the snapshot
  • upload.php, the script to receive the image
  • a php snippet to include in wordpress to display the snapshot and the comment

The flex application consists of main application, a class for the webcam access and a class for base64 encoding.

The main application is described in an article on how to upload binaries with an Adobe Air application. It works great, except that I was not able to transfer the image to the server, as I could not get hold of the data with php. And php was the only thing available on my blog site. So I modified the code to encode the image with base64. That way I could use simple form processing on the php side.

[...]
      private function init():void {
        webCam = new WebCam(160, 120);
        var ref:UIComponent = new UIComponent();
        preview.removeAllChildren();
        preview.addChild(ref);
        ref.addChild(webCam);
      }
      private function enableSubmit():void {
        if ((nameField.text.length > 0) && (commentField.text.length > 0) &&
          (imageViewer.getChildren().length > 0)) {
            submit.enabled = true;
          }
          else {
            submit.enabled = false;
          }
      }
      private function takeSnapshot():void {
        imageViewer.visible = true;
        imageViewer.width = preview.width;
        imageViewer.height = preview.height;
        var uiComponent : UIComponent = new UIComponent();
        uiComponent.width = webCam.width;
        uiComponent.height = webCam.height;
        var photoData:Bitmap = webCam.getSnapshot();
        var photoBitmap:BitmapData = photoData.bitmapData;
        uiComponent.addChild(photoData);
        imageViewer.removeAllChildren();
        imageViewer.addChild(uiComponent);
        state.text = "";
        enableSubmit();
      }
      private function uploadSnapshot():void {
        if (imageViewer.getChildren().length > 0) {
          var uic:UIComponent = imageViewer.getChildAt(0) as UIComponent;
          var bitmap:Bitmap = uic.getChildAt(0) as Bitmap;
          var jpgEncoder:JPEGEncoder = new JPEGEncoder(75);
          var jpgBytes:ByteArray = jpgEncoder.encode(bitmap.bitmapData);
          state.text = "starting upload ...";
          uploadPhoto(jpgBytes);
          state.text = "upload done";
        }
      }
      private function deleteSnapshot():void {
        imageViewer.removeAllChildren();
        enableSubmit();
        state.text = "";
      }
      private function uploadPhoto(imageData:ByteArray):void {
        var request:URLRequest = new URLRequest("http://tinkerlog.com/uploads/upload.php");
        var vars:URLVariables = new URLVariables();
        vars.name = nameField.text;
        vars.comment = commentField.text;
        vars.bindata = Base64.encodeByteArray(imageData);
        request.method = "POST";
        var loader:URLLoader = new URLLoader();
        loader.addEventListener(Event.COMPLETE, uploadPhotoHandler);
        request.data = vars;
        loader.load(request);
      }
      private function uploadPhotoHandler(event:Event):void {
        trace("server: " + event.target.data);
        state.text = "Response from server: " + event.target.data;
      }
[...]

The following is the code to receive the image data and the comment. There are only basic checks in place. As already noted, the binary data is transmitted as base64 encoded string and has to be decoded. If the image is smaller then 10.000 bytes, it gets stored in a file named snapshot.jpg. Comments are saved to a file named comments.txt. The script returns the filesize which is then displayed in the Snapshot application.

<?php
  if ($_REQUEST["bindata"] === NULL) {
    echo "missing parameter.";
  }
  else {
    $img_data = base64_decode($_REQUEST["bindata"]);
    $name = $_REQUEST["name"] === NULL ? "anonymousn" : $_REQUEST["name"] ."n";
    $name = strip_tags($name);
    $comment = strip_tags($_REQUEST["comment"]) . "n";
    $img_size = strlen($img_data);
    if ($img_size < 10000) {
      $img_filename = "data/snapshot.jpg";
      $comment_filename = "data/comment.txt";
      unlink($img_filename);
      unlink($comment_filename);
      $img_file = fopen($img_filename, "w") or die("can't open file");
      fwrite($img_file, $img_data);
      fclose($img_file);
      echo "$img_size bytes uploaded.";
      // write comments
      var_dump($comment);
      $comment_lines = explode("r", $comment);
      var_dump($comment_lines);
      $comment_file = fopen($comment_filename, "w");
      fwrite($comment_file, $name);
      foreach ($comment_lines as $line) {
        fwrite($comment_file, $line . "n");
     }
     fclose($comment_file);
    }
    else {
      echo "image too big.";
    }
  }
?>

The following snippet is directly hacked into the sidebar.php. Search for the line <?php endif; ?> and paste it below. It just renders a static link to the snapshot.jpg and pulls the lines out of the comments file to display them.

  <li>
    <h2 class="widgettitle">Snapshot</h2>
    <div class="textwidget">
      <ul>
        <img src="/uploads/data/snapshot.jpg"/>
      <?php
        $comment_filename = ABSPATH . "/uploads/data/comment.txt";
        $fh = fopen($comment_filename, "r");
        if ($fh) {
          $contents = fread($fh, filesize($comment_filename));
          fclose($fh);
          $lines = explode("n", $contents, 2);
          echo "$lines[0] says: ";
          echo "$lines[1]";
        }

      ?>
      </ul>
    </div>
  </li>

Demo

Ok, time for some demo.

Insert name and comment and take a snapshot. Reload the page and see, if it is displayed in the sidebar. The comments and picture are overwritten by the next one who tries this, so nothing is archived.

Note: You have to have a web cam attached to your PC or Mac and you have to allow the access to the camera.

Conclusion

Posting so much source code into the post is really a pain. Wordpress users know what I am talking about. But over all, it is always nice to have something new on the blog to play with. If someone has improvements, be it security fixes, bug fixes or a nice skin for the application, just let me know.

And please stay well-dressed when you take a snapshot ;-)

Links

Downloads

Add this to:Del.icio.us Del.icio.us Digg! Digg

11 responses so far ↓

Leave a Comment