• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar
  • Skip to footer
webrtcHacks

webrtcHacks

Guides and information for WebRTC developers

  • Home
  • About
    • Chad Hart
    • Philipp Hancke
  • Subscribe
  • Contact
  • Show Search
Hide Search

Guide camera, code, constraints, getUserMedia, resolution, Walkthrough Chad Hart · October 17, 2013

How to Figure Out WebRTC Camera Resolutions

Newer note: February 2016 update here.

Note: Behavior has changed with latest versions of Chrome (v35+). Please see my update to this post here.

{“editor”, “chad“}


movie camera-cropped
Photo courtesy Flickr user Tomás Fano 

I have a confession to make about my WebRTC Motion Detecting Baby Monitor – the video quality was inconsistent and poor on the baby side of my original demo video, so I swapped out my old HTC Thunderbolt for another laptop in the 2nd half of the video. The stream and motion processing consumes a full core on my 2 GHz Intel i7 laptop processor. Fortunately I have more cores there, but this would clearly be a problem for a lot of devices – both in terms of having enough CPU to meet the application’s demands and on battery life. There are also bandwidth concerns in the real world. I was am running everything over WiFi, so I was not too concerned about bandwidth, but there is no reason why one should not be able to run this on a LTE or 3G network where there are bandwidth constraints and data plan usage concerns.

This got me experimenting with getUserMedia() constraints to see what affect adjusting the source resolution and framerate has on CPU utilization and bandwidth consumption. I discovered this is not as straight forward as one would think, so I am posting my observations here.

 

getUserMedia() constraints

As covered in the What happens when there’s missing media sources? post, getUserMedia() takes a constraints object. The object, as defined in the getUserMedia() W3C specification looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
  mandatory: {
    width: { min: 640 },
    height: { min: 480 }
  },
  optional: [
    { width: 650 },
    { width: { min: 650 }},
    { frameRate: 60 },
    { width: { max: 800 }},
    { facingMode: "user" }
  ]
}

The spec allows mandatory and optional constraints for both minimum and maximums on:

Object What it does Value options
Height Specifies the video source height Min and Max of an integer
Width Specifies the video source width Min and Max of an integer
FrameRate specify how many frames to send per second (usually 60 for HD, 30 for SD) Min and Max of an integer
aspectRatio height divided by width – usually 4/3 (1.33333333333) or 16/9 (1.7777777778) Min and Max of an decimal
facingMode Select the front/user facing camera or the rear/environment facing camera if available Which camera to choose – currently user, environment, left, or right

In addition to turning audio and video on/off all together, in practice minHeight, minWidth, maxHeight, and maxWidth variables are the most widely supported:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"audio": true,
"video": {
  "mandatory": {
   "minWidth": 320,
   "maxWidth": 1280,
   "minHeight": 180,
   "maxHeight": 720,
   "minFrameRate": 30
  },
  "optional": []
}
}

I have also seen reference to others that could theoretically be passed down to the libjingle media engine that powers WebRTC. These are out of scope for my camera resolution project, so I did not experiment with them.

Initial Experiments

Adjusting the video resolution seems simple enough using the minHeight, minWidth, maxHeight, and maxWidth constraints. I started by setting various constraints and then using the .videoWidth and .videoHeight properties of my video object to validate the actual values:

Set constraints

1
2
3
4
5
6
7
8
9
10
                var constraints = {
                    video: {
                        mandatory: {
                            maxWidth: 1024,
                            maxHeight: 768,
                            minWidth: 1024,
                            minHeight: 768
                            }
                    }
                };

Call getUserMedia() & assign to a video object on success

1
2
3
4
5
6
navigator.getUserMedia(constraints, onSuccess, onFail);
            function onSuccess(stream) {
                window.stream = stream; // stream available to console
                video.src = window.URL.createObjectURL(stream);
                video.play();
            }

Assign an event listener and report the results

1
2
3
4
5
6
            //Attach to play event and then update dimensions after 500ms
            video.addEventListener("playing", function () {
                setTimeout(function () {
                    console.log("Stream dimensions": " + video.videoWidth + "x" + video.videoHeight);
                }, 500);
            });

Non-obvious Findings

Let’s start out with Chrome (I’m using 30). One would think that requiring an exact width and height would either result in:

  • a stream with that exact width and height, or
  • a failure (call to the error callback – onError in my code above)

Well, that’s not what happened. 1024×768 gave me an error. 1980×1080 gave me 1280×720. Going to 640×480 next gave me 1064×800. This did not make any sense.

To make sure it was not just my code, I checked out Sam Dutton’s simpl.info getUserMedia constraints demo. The VGA button uses 640×360 as max values for constraints – it returned 320×180:

“GetUserMedia() does not always return what you expect”

So much for mandatory.

Mandatory constraints are really suggestions

I did some research and came across this statement by Justin Uberti at Google:

So I was not crazy. It turns out Chrome uses several fixed resolutions:

Width Height Aspect Ratio
1280 720 16:9
960 720 4:3
640 360 16:9
640 480 4:3
320 240 4:3
320 180 16:9

Chrome will return something that is close to the resolution requested among these values. Sometimes it will return something that is outside of your constraints. See this message on the Chromium board for more details.

Kill the stream before applying new constraints

I also noticed it is very important to stop and nullify the stream, if you call getUserMedia() again with new constraints without refreshing in between. If you do not kill the stream, the new constraints are ignored and the previously provided constraints are used again. You can try this here: http://src.chromium.org/svn/trunk/src/chrome/test/data/webrtc/manual/constraints.html

Set 800×600 as a min value and I get 960×720:

1
2
3
4
5
6
7
8
9
10
11
Requesting getUserMedia with constraints: {
"audio": false,
"video": {
  "mandatory": {
   "minWidth": "800",
   "minHeight": "600"
  },
  "optional": []
}
}
Set video tag width and height: 960x720

Move 800×600 to a max value and hit getUserMedia again and you get the same value:

1
2
3
4
5
6
7
8
9
10
11
Requesting getUserMedia with constraints: {
"audio": false,
"video": {
  "mandatory": {
   "maxWidth": "800",
   "maxHeight": "600"
  },
  "optional": []
}
}
Set video tag width and height: 960x720

Fortunately killing the stream is easy and fixes this problem:

1
2
3
4
                    if (!!stream) {
                        video.src = null;
                        stream.stop();
                    }

Use https for a better user experience

Chrome forces the user to ask for permission every time getUserMedia() is called if you server your page using http. Fortunately Chrome desktop remembers your permissions if you use https:

1
Allow: This allows the site to access your camera and microphone at this time and a notification will appear confirming that you've granted access. If you select Allow on a "http" URL your preference will not be remembered in future visits. If you select Allow on a "https" URL, your preference will be remembered in future visits.

Source: https://support.google.com/chrome/answer/2693767?hl=en

In most situations dropping the stream and then the user for permission repeatedly will not generate a good experience. Fortunately the https option allows constraints to be changed more seamlessly, at least in the desktop version. Chrome for Andriod requires permissions for every getUserMedia() call, even on https.

Don’t bother changing constraints in Firefox today

FireFox as of 24.0 does not support the video resolution constraints. You can however manually set the default parameters in the about:config preference selections under media:

Firefox about:config settings for getUserMedia()

This is helpful for testing but has limits the kinds of applications where you could make use of changing video resolutions.

 

Auto-identifying Camera Resolutions

An API for discovering possible camera resolutions sure would be nice. Looking through the W3C mailing list, it looks like this was suggested but dropped due to concerns about using this information to help further fingerprint users based on their hardware. For more on what this fingerprint could look like I suggest checking out this Electronic Frontier Foundation site.

Fortunately using the techniques above it is possible to determine what camera resolutions are available in Chrome by simply trying all six resolution options and seeing what is returned. You can check it out yourself here: https://gumcameraresolutions.azurewebsites.net/ and see the code here: https://github.com/webrtcHacks/WebRTC-Camera-Resolution

In this quick demo I just used the 6 resolutions identified above. Here are the results across my various Chrome supporting devices:

Type Laptop PC+Cam Laptop Mobile Mobile
Brand HP Logitech Lenovo Samsung HTC
Chrome Ver 30.0.1599.69 m 30.0.1599.69 m 30.0.1599.69 m 30.0.1599.82 29.0.1.1547.72
Make Pavilion dv7 WebCam 500 T410 Galaxy S4 Thunderbolt
1280×720 1280×720 1280×720 1280×720 1280×720 1280×720
960×720 960×720 960×720 960×720 960×720 724×544
640×480 640×480 640×480 640×480 640×480 640×480
640×360 320×180 640×360 352×288 640×360 640×360
320×240 320×240 320×240 320×240 320×240 320×240
320×180 160×88 320×180 320×180 320×180 320×180

I did not receive an error back when requesting any of these resolutions, but interestingly in some cases the resolution returned was different than what was requested. It turned out to be less of a problem than I thought, but the results are interesting nonetheless. If image size is important to your application then this kind of test may be needed.

Since both the standard and implementation are in flux, I wanted to verify other resolutions than the 6 above were available. To do this I also created a version that runs through every possible 16:9 and 4:3 resolution combo between 100×56 to 1280×960 here: https://gumcameraresolutions.azurewebsites.net/fullscan.html. All of the constraints that were not the 6 above returned an error and the rest of the results were exactly the same.

These are still early days

If I was a professional developer instead of an inquiring hobbyist then this kind of problem would probably get me upset. In 13% of my test cases I was given a different resolution than I asked for. There was no easy way to discover what resolutions are available. The 6 resolutions provided seem somewhat arbitrary. This implementation may change, but who knows to what. Firefox has no support today. All these problems and unresolved issues and getUserMedia() is the most mature of the three WebRTC API’s!

It is clearly still the early days for WebRTC. Still, 1280×720, 640×480, and 320×240 seemed to work consistently well in all the tests. I would stick to those resolutions if I had to based on my limited dataset.

{“author”, “chad“}

Guide camera, code, constraints, getUserMedia, resolution, Walkthrough

Related Posts

  • Surviving Mandatory HTTPS in Chrome (Xander Dumaine)Surviving Mandatory HTTPS in Chrome (Xander Dumaine)
  • WebRTC Video Resolutions 2 – the Constraints Fight BackWebRTC Video Resolutions 2 – the Constraints Fight Back
  • getUserMedia – What happens when there’s missing media sources?getUserMedia – What happens when there’s missing media sources?
  • Fix Bad Lighting with JavaScript Webcam Exposure Controls (Sebastian Schmid)Fix Bad Lighting with JavaScript Webcam Exposure Controls (Sebastian Schmid)

RSS Feed

Reader Interactions

Comments

  1. Sam Dutton says

    November 5, 2013 at 6:50 am

    Great article — and love the baby monitor

    Reply
  2. Shasak Raina says

    October 20, 2014 at 3:21 am

    Neat article, helped me understand why my app won’t get the right resolutions. Bookmarking this!

    Reply
  3. Gloria says

    February 4, 2015 at 3:12 pm

    Hi!
    I have a question: Firefox is ignoring the constraints y pass to getUserMedia. Do you know why?
    I´m using an HD webcam and i want to limit the resolution.

    Thanks!

    Reply
    • Chad Hart says

      February 7, 2015 at 4:05 pm

      Firefox does not support these constraints. You need to go to Firefox’s about:config and manually adjust the settings if you want to do this (presumably for testing purposes). I have a brief mention of this in the previous gUM constraints post here: https://webrtchacks.com/how-to-figure-out-webrtc-camera-resolutions/

      Reply
  4. Samson T says

    February 6, 2015 at 6:18 pm

    You wrote ‘all 6 resolutions’. To your knowledge, is there any reason that WebRTC won’t support other resolutions?

    Thanks!

    — Samson

    Reply
    • Chad Hart says

      February 7, 2015 at 4:08 pm

      Chrome does allow more than these 6 now. See the update to this post mentioned at the top: http://webrtchacks.com/video-constraints-2/

      Reply
  5. Mouad says

    February 20, 2017 at 6:31 pm

    Awsome

    Reply
  6. SnowTV says

    April 17, 2019 at 2:26 pm

    Hi, I have a question:

    How do you make it that when I open the webpage, it doesn’t ask for my permission to use the camera and just turns it on automatically?

    Thanks!

    Reply
    • Chad Hart says

      April 17, 2019 at 2:51 pm

      The short answer is you don’t – the browser vendors very intentionally require the user to give explicit permission before they share their camera to prevent abuse. All of the major browsers provide a mechanism to whitelist permissions, so often the user only needs to give permission once if they trust the site. Look for device permissions in your browser settings if you want to whitelist a site.

      Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Primary Sidebar

  • Sponsored. Become a webtcHacks sponsor

Email Subscription

Subscribe to our mailing list

* indicates required

Twittering

Tweets by @webRTChacks
webrtcHacksguides and information for WebRTC developers

Footer

SITE

  • Post List
  • About
  • Contact

Categories

  • Guide
  • Other
  • Reverse-Engineering
  • Standards
  • Technology

Tags

apple Blackbox Exploration Brief camera Chrome code computer vision DataChannel debug e2ee Edge extension gateway getUserMedia ICE ims insertable streams ios ip leakage janus jitsi MCU Microsoft NAT opensource Opus ORTC Promo Q&A raspberry pi Safari SDES SDP sfu signaling simulcast standards TURN video vp8 w3c Walkthrough Web Audio webrtc-internals wireshark

Follow

  • Twitter
  • YouTube
  • GitHub
  • RSS

webrtcHacks · copyright © 2023