Say Cheese! Take and Upload Pictures in Chrome 21

The Media Capture and Streams specification , commonly referred to as GetUserMedia(), adds the ability to tap into web cams and microphones to the web browsers without the need for proprietary plugins like Adobe Flash. In this article, you will learn how to connect to the user's web cam using only JavaScript to take a picture and upload it to your web site using GetUserMedia() in Chrome 21+.

Support

Chrome 21 is the first of the big three web browsers to support this standard in its general release build. Support is coming soon in Firefox (in the nightly builds now) and IE10. Because Chrome 21 is the only one of the big 3 to be in final release at the time of this writing, this article will focus exclusively on Chrome.

Because the specification is still a draft specification, each browser is currently supporting the standard with browser specific prefixes. The GetUserMedia function is a property of the navigator object and can be detected using simple object detection.

if
(!navigator.webkitGetUserMedia) {
alert('Not in Chrome 21+');
}

How It Looks To the User

When your web application calls GetUserMedia, Chrome will prompt the user at the top of the window to allow or deny access to the camera and microphone as seen in Figure 1. If the user has more than one device, they can choose between the devices by clicking the "Options" button on the far right of this bar.

Chrome

If your application is being used on an intranet or in a situation where you have control over the user's browser and would like to always automatically allow, go to Settings -> Privacy -> Content Settings and at the very bottom is an option for Camera and Microphone where you can choose your site to always allow camera and microphone.

Starting the Stream

Related Articles

GetUserMedia() has three parameters. The first parameter defines weather you are connecting to the user's audio, video or both. In this example, our goal is to only tap into the user's camera to take a picture so we don't need audio. As with all things where we have to request security permissions, it is best to ask for the minimum permissions necessary to do what you need to do so in this example you are just requesting video.

The second parameter defines a callback function that will be called if the user has the devices you've requested and has granted access.

In the success callback, it passes a reference to a LocalMediaStream object that you can then create a web worker using window.webkitURL.createObjectURL() to assign to a HTML5 video object on your web page. The createObjectURL function is part of the new multi-threaded web worker support found in Chrome, which creates a reference to an asynchronously running web worker to process each frame of video as it comes in.

The web worker approach is critical to processor-intensive javascript such as video processing. If you tried to write javascript that did this kind of heavy lifting in the old-style synchronous method, or using a setTimeout, it would likely pop a dialog suggesting to the user to cancel the script from running. The web worker allows the video to be processed asynchronously without blocking the rest of the page from running normally.

The third parameter is a callback function that will be called if the user either doesn't have the device you've requested or has denied access. This will allow you to gracefully handle the scenario that the user is using Chrome 21 and they have either denied you access to the web cam or they do not have one configured.

If you want to provide graceful fallback for users of older browsers or that don't have a web cam, you should display a file upload widget by default and only display the video tag and canvas once you've connected to the user's web cam successfully. If you code your page like this, you will not need the onfail handler at all.

Grabbing a Frame of Video

In order to grab a frame of video and encode it in a form that can be uploaded to a web server, you need to draw the frame to a canvas first. The HTML5 video object can be passed to any of the canvas' methods that call for an image and will render the currently displayed video frame. In order to grab the frame, call drawImage passing in the video frame along with the scaling information (source x/y/width/height and destination x/y/width/height).

In order to freeze the frame in the code example included in this article, when the user clicks "Take Picture" the current frame of video is drawn to the canvas object then the video object is hidden so the user will see only the frozen frame and not the live video. This allows the user to make sure this is the frame they want before uploading the picture to the server.

Encoding The Picture To Upload

Handling binary data types in the web world is always tricky. Fortunately the browser gives us some good options for converting the binary types into character encoding friendly base64 by using the toDataURL function on the canvas object. This converts what the canvas is currently displaying into a base64 encoded version of the image intended to be used as an inline-defined image, defined in the data URL specification. In order for the server-side .NET to be able to decode this, you have to remove the data URL portion (data:image/png;base64,) from the beginning of the string.

The example below uses jQuery to do an HTTP post to the UploadWebCamPic controller action. Because you are passing a base64 file that could be longer than the maximum URL allowed for HTTP GET, using a POST is critical. Also note that the data portion is crafted as a string to avoid the base64 encoded image string being passed isn't accidentally sniffed as a different data type.

ASP.NET MVC3 Controller Action

Handling files in web pages is always tricky business due to the complexities of multi-part form encoding, which has never been handled gracefully by the browser or server side technologies including MVC3. You would typically use the Request object to get a stream of the uploaded file outside of MVC's normal routing scheme to handle a file upload but in this case since we have base64 encoded the file on the client, from ASP.NET MVC's point of view it is just receiving a large string as a parameter. Because it is getting the parameter as a string, you will need to convert this string back into bytes from the base64 encoding.

Once it has been converted to bytes, you could just store it without doing any further processing. In the example below, we go to the next step and convert this byte array into a .NET WebImage object that you can crop, store, change file format and more. The image is converted back into PNG bytes which can then be stored in an Image or varbinary column of a SQL database.

[HttpPost]
public ActionResult UploadWebCamPic(string ImageData)
{
byte[] upImg = null;
WebImage wi = new WebImage(Convert.FromBase64String(ImageData));
upImg = wi.GetBytes("png");
//Get a data Context and you now have the image in PNG format
//in the upImg byte[] that you can store in your DB.
return Json("OK");
}

Conclusion

It's quick and easy to add web cam support to your site to liven up your user profile page. By making it easier for your users to provide pictures of themselves more profiles will be completed with fewer generic avatars.

Over the next year, Firefox and IE10 will also support GetUserMedia(), substantially increasing the number of users that will be able to use this functionality. You can get ahead of the curve now by adding a web photo booth to your site now!

About the Author:

David Talbot has over 14 years of experience in the software industry with experience ranging from license plate recognition using neural networks to television set-top boxes to highly scalable web applications. His main focus is building rich user interface web applications on the .NET platform. He is also the author of Applied ADO.NET and numerous articles on technology.