Webcam snapshots with Flex3

Posted by on Nov 3, 2007 in flex | 19 Comments

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

19 Comments

  1. 98clouds » Webcam snapshots with Flex3
    4. November 2007

    [...] You can read the full story here [...]

  2.   Webcam snapshots with Flex3 by rssdreams
    9. November 2007

    [...] here to [...]

  3. Prendre une photo avec une webcam sous Flex 2 - Formation Flex, exemples, tuto, news et conseils en développement Adobe Flex / AIR
    7. February 2008

    [...] application AIR, je vous conseille le très bon tutoriel (en anglais) dont je me suis inspiré sur tinkerlog.com (Flex 3 propose un encodeur JPEG intégré, et les applications AIR peuvent enregistrer des [...]

  4. Boris
    24. April 2008

    Very good tutorial. Thank you! Keep up the good work. I’ve used flash and fms for months to do that. Time to dig into flex.

  5. Zane
    27. May 2008

    Can you put a C# asp.net code for the upload?

    Thanks,

    Zane

  6. José Antonio
    12. June 2008

    Excelent tutorial. File uploading from Flex has some important limitations. After having read a lot of ideas from blogs and forums, I finally reached your blog.

    You seem to have combined the necessary pieces in order to achieve the best solution. Excelent work!

  7. Will
    22. July 2008

    Great blog, just one question. I used your code but i can’t find where is the snapshot.jpg pic is saved. I actually think it is not saved at all, but how can i save it?

  8. Alex
    22. July 2008

    Will,
    the snapshot is sent to the server. On the server side the php script is used to receive it and save it to disk.
    Cheers,
    Alex

  9. webcam motion
    2. April 2009

    Here is a list of motion detection resources resources for Flash developers, with sources, theory, samples, etc… http://www.ugolog.com/pages/webcam-motion-detection-for-flash-flex-and-csharp

  10. Edit Profile « LogField
    16. April 2009

    [...] 8 Webcam snapshots with Flex3 [...]

  11. How to save files from Flex to your server with PHP | Arno Manders
    24. April 2009

    [...] I found the solution at thinkerlog.com. I think it is the most straight forward solution. This are the [...]

  12. Danish
    13. August 2009

    hi,
    how can i save image on local hard drive

  13. Toby Liddicoat
    10. September 2009

    Actually, contrary to the main post remark of “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” – you can!

    I guess not many of you guys out there that PHP nuts like myself, but if you want to get a data piped into a PHP script just access it like so:

    No need for hacking around with converting stuff to Base64, just take the original Adobe tutorial that posts to a Perl script and update your code to use ‘php://input’ … and if you want to throw name/value pairs, then append then to your URL in the querystring.

  14. Wensheng Wang
    28. October 2009

    Thank you for this, by far the most straight forward flex webcam solution.

    I am using it for scan qrcode:
    http://blog.wensheng.com/2009/10/poc-online-barcode-qrcode-scan-with.html

  15. hugo armando
    13. January 2010

    hi!!
    nice job with the webcam but i still have a cuestion:
    it apears that the webcam initialization obtains every installed twain source (screen capture driver, webcam, etc.) similar to a webcam. in my case it doesnt let me choose which one i want to use. i there a way to select a device from the source code or get a list to choose from?
    tnks and keep the good work.

  16. Taniguchi, J.T.
    25. January 2010

    Thanks alot for posting this man, my stuff got going thanks to ya :)

  17. Albert
    19. February 2010

    Here a update to cofigure easely the image size an quality…

    as3 code:

    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);
    uiComponent.scaleX = uiComponent.scaleY = scaleIt;
    imageViewer.removeAllChildren();
    imageViewer.addChild(uiComponent);
    state.text = “”;
    enableSubmit();
    deleteButton.enabled = true;

    }
    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(jpegQual);
    var jpgBytes:ByteArray = jpgEncoder.encode(bitmap.bitmapData);
    state.text = “starting upload …”;
    uploadPhoto(jpgBytes);

    }
    }
    private function deleteSnapshot():void {
    imageViewer.removeAllChildren();
    enableSubmit();
    state.text = “”;
    }
    private function uploadPhoto(imageData:ByteArray):void {
    var request:URLRequest = new URLRequest(“./script/php/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 = “Thank you / Danke: ” + event.target.data;
    }
    ]]>

    PHP code:

    <?php
    if ($_REQUEST["bindata"] === NULL) {
    echo "missing parameter.";
    }
    else {
    $img_data = base64_decode($_REQUEST["bindata"]);
    $name = $_REQUEST["name"] === NULL ? "anonymous\n" : $_REQUEST["name"] ."\n";
    $name = strip_tags($name);
    $comment = strip_tags($_REQUEST["comment"]) . "\n";
    $img_size = strlen($img_data);
    if ($img_size

    maybe u like it
    greetings from Bonn, ger

  18. kay
    20. June 2011

    the download does not have the complete files in them….pls add them

  19. sandy
    6. April 2012

    great tutorial. But we are ignoring that the stream and image is mirror image. How can we solve this issue. I tried
    webCam.scaleX = -1;
    webCam.x = webCam.width + webCam.x;
    to solve it. But every time the image width is coming double the orginal. Is there any other way to do it. Please help .