⏯ Play a stream on the web with HTML5 Cam API

The list of available Web API is growing every month and so the browser’s support. The goal of the Web-Consortium is certainly to provide as much device API than what’s available to you when using native apps on your smartphone.

What is the feature you use the most on your mobile ? Probably your Camera. Today everyone as a photo camera in his pocket and can even use it to make video-calls, to scan bar-codes or document, or to play with Virtual-Reality (VR) and Augmented-Reality (AR).

Today we’ll see how we can use the camera from a webpage with the HTML5 Cam API.

Photo by Banter Snaps on Unsplash

The prequel of the Cam API 🎥

First thing first, we should know what’s the Cam API ? But well, the name says it all. What the name doesn’t say is how we came to where we are today, as it was not so straightforward as we could think.

The first path to the Cam API was just finding a way to give access to the cam to capture a “file”. I say a “file”, because if you think simple, the picture or the video taken by your camera is just a jpeg or a mp4 (or other filetypes). The only difference is that you don’t access a ready made file, but you use the camera to generate it. At the end, the result is the same, you upload a file to the web…

As one of the most used interaction element of the web is the form, it was normal to reuse it and that’s why the first attempt was with the input type="file" :

<!-- for images -->
<input type="file" accept="image/*;capture=camera">
<!-- for video or audio -->
<input type="file" accept="video/*;capture=camcorder">
<input type="file" accept="audio/*;capture=microphone">

The object related to this resource is a HTMLMediaCapture object.

You’ve probably already seen the limit of this implementation as I wrote many times about a “file”. Yes, exactly, what about media streaming ?

You can’t stream media from a form input of type file.

You’ve seen the spoiler, the answer is “You can’t !”. So the working group had to find another way for streaming data. They first tried an implementation with a new html element name <device> but this never emerged into the browsers. So they moved on to the API we’ll see next.

Today’s Cam API 📹

The API is available through a JavaScript object. An important thing to know is that this API was first accessed directly from the navigator object in you browser. But like all the new spec, it evolved and was then stored as part of the navigator.MediaDevices object.

Deprecation warning : navigator.getUserMedia() is deprecated.
Instead use navigator.MediaDevices.getUserMedia()

For the old and the new implementation, a requirement is the https. Like all new HTML5 API’s, the W3C choosed the security and the API will fail with an appropriate exception if were the web page executing the script is not on https.

Photo by Mink Mingle on Unsplash

Let’s code ☕️

Capturing the stream from your webcam to your webpage is as simple as:

navigator.mediaDevices.getUserMedia(constraint)
.then(handleSuccess)
.catch(handleError);

The Promise<MediaStream> getUserMedia(constraint: object) method will accept a constraint object as a parameter. This object will contain the settings that we want to use to define what and how the stream should be configured. The method returns a Promise that will contain the media stream. If your not comfortable with promises yet, it’s probably a good time to learn what they are, as a lot of new HTML5 API are using this pattern for the async actions.

So, once you call this method, the browser will read the constraint and try to access your device’s camera. At this step, the following errors can occurs :

  • You’ve not configured correctly the constraint object to pass to the method
  • You’re using http and not the securised https protocol of the site and therefor your browser won’t let you use the API
  • NotAllowedError : You’ve not given the permission to the webpage to use the cam device. If you refused the permission by mistake, you can change this from your browser settings.
  • No cam was found on your current device (mobile or desktop)

If none of those exception were thrown, then

The constraint object

The constraint object is a descriptor of the media. We’ll need to define what we want to capture (video and sound, video only,…), with which camera (front or rear), but also other more technical options like the size of the video, the codec to use, the frame-rate, etc.

{
audio: false,
video: {
facingMode: “user”,
frameRate: { ideal: 10, max: 15 },
width: { ideal: 1280 },
height: { ideal: 720 }
}
}

To test the example you can keep it very simple and just define the channel you want to capture.

{ audio: true, video: true }

The success callback

The success callback will define what you will do with the captured stream. Here our goal is to display the stream directly in the page in a video element which will act as a player.

handleSuccess = (mediaStream) => {
player.classList.remove('hidden');
// Older browsers may not have srcObject
if ("srcObject" in player) {
player.srcObject = mediaStream;
} else {
// Avoid using this in new browsers, as it is deprecated and will be removed
player.src = window.URL.createObjectURL(mediaStream);
}
player.onloadedmetadata = e => player.play();
};

The callback will receive a single argument which is a stream object defined as MediaStream type.

The condition will check the maturity state of the video player element in your browser. Like a lot of feature in HTML5, the player object has evolved and a feature detection is required to use it properly. It’s not directly related to our camera api, but as we want to use it we need to be sure to call the implemented version of the feature.

If the latest version of the player is implemented, we can directly affect the MediaStream instance to the srcObject attribute.

On the previous version, you will have to create a url object from that stream and affect it to the src attribute.

The error callback

This is the easiest part of our code snippet : the exception handling. If an error occurs we just display a message to the user.

err => {
noDeviceMessageBox.classList.remove('hidden');
}

I heard you at the back of the classroom. You’re right this is simple only because our example is simple. In a real app, this will be more complete as you should manage all the possible errors and return an appropriate message to the user according to what really happened. But let’s keep it simple for this “tryout”.

Working example 🤖

Here is a working example that stream the media from you webcam. If you can’t see it, probably that the feature is not implemented in your browser yet. Go check https://caniuse.com/#feat=stream for the current support.

If it’s supported but still not working, be sure that you allowed the web page to use your cam (browser permission settings).

CamAPI working example on https://codepen.io/miam84/pen/xEWvQN

Go further with those links 🔗

You like this article ? ❤️

Let me know with some claps 👏 and share this article with other people 👨‍💻