getUserMedia() video size in Firefox & Chrome differs

sidewaiise picture sidewaiise · Oct 17, 2014 · Viewed 21.6k times · Source

I'm using getUserMedia(), and when implementing constraints (see below) they only work in Chrome and not Mozilla. The size in mozilla always appears stretched and ends up bigger than the one in chome.

var vid_constraints = {
    mandatory: {
        maxHeight: 180,
        maxWidth: 320
    }
}
var constraints = { audio: false, video: vid_constraints };
navigator.getUserMedia(constraints, successCallback, errorCallback);

After reading some, it appears that MozGetUserMedia() doesn't support resolution constraints. Is there a way to display the video that ensures it has the same size/res in both Firefox and Chrome?

Thanks

Edit I've modified the script to take snapshots. I've saved snapshots in Moz and in Chrome - the result is what follows:

Chrome Mozilla

(left = Chrome, right = Mozilla)

Thought this may clarify the problem. It looks like aspect ratio.

Edit (take 2)

Yes - the aspect ratio for the chrome one is 16:9, whereas for Moz its 4:3. How can I modify this?

Answer

Kaiido picture Kaiido · Jan 8, 2015

Edit April 15

As noted by @jib in his awesome answer,

Firefox [38+] does support a subset of constraints with getUserMedia(), but not the outdated syntax that Chrome and Opera are using. The mandatory / optional syntax was deprecated a year ago, and minWidth and minHeight the year before that.

So the new syntax, approved by specs is :

var constraints = {
    audio: false,
    video: {
        width: { min: 1024, ideal: 1280, max: 1920 },
        height: { min: 576, ideal: 720, max: 1080 },
    }
};

However this syntax throws an error in Chrome. As noted in comments, a PR to adapter.js has been made, including a polyfill for older FF and chrome.

Here is my attempt, for chrome only (but newer version of FF seem to accept the magical and hidden require:['width', 'height'].

	var video_constraints = {
		width: { min: 320, max: 480 },
		height: { min: 240, max: 360 },
		require: ["width", "height"] // needed pre-Firefox 38 (non-spec)
		};
		

	function getMedia(){
     
        if(navigator.webkitGetUserMedia){
				var wkConstraints={mandatory: {} };
				var c = video_constraints;
				for(var i in c){
					switch(i){
					 case 'width': for(j in c[i]){
						  switch(j){
							 case 'max': wkConstraints.mandatory.maxWidth = c[i][j]; break;
							 case 'min': wkConstraints.mandatory.minWidth = c[i][j]; break;
							 case 'exact': wkConstraints.mandatory.minWidth = wkConstraints.mandatory.maxWidth = c[i][j]; break;
							 }
						}; break;

					 case 'height': for(var j in c[i]){
						  switch(j){
							 case 'max': wkConstraints.mandatory.maxHeight = c[i][j]; break;
							 case 'min': wkConstraints.mandatory.minHeight = c[i][j]; break;
							 case 'exact': wkConstraints.mandatory.minHeight = wkConstraints.mandatory.maxHeight = c[i][j]; break;
							 }
						}; break;
					 default: break;
					}
				}
				video_constraints = wkConstraints;
				}

		navigator.getUserMedia = ( 	navigator.getUserMedia ||
							   	navigator.webkitGetUserMedia ||
								navigator.mozGetUserMedia);

        if(!navigator.getUserMedia){
			alert("your browser doesn't support getUserMedia")
			}

		
		navigator.getUserMedia(
                {
				video: video_constraints,
				audio: false,
				},
			
				function(stream) {
					if (navigator.mozGetUserMedia) {
						video.mozSrcObject = stream;
						} 
					else {
						var URL = window.URL || window.webkitURL;
						video.src = URL.createObjectURL(stream);
						}
					video.play();
					},

				function(err) {
					console.log(err);
					}
				);
		}

	  var video= document.querySelector('video');
	  video.addEventListener('playing', loopStart, false);			   
	  function loopStart(){
		  this.removeEventListener('playing', loopStart);
		  if(this.videoHeight === 0){
			window.setTimeout(function() {
			  this.pause();
			  this.play();
			  loopStart();
			}, 100);
			}
			else {
			  this.setAttribute('width', this.videoWidth);
			  this.setAttribute('height', this.videoHeight);
			  }
		 }
	  getMedia();
<video/>

First Answer

So I started to write you this before you answered your own question.

As noted in the comments, I'm drawing each frame of the video to fit a resized canvas.

var video, canvas, streaming = false,
  constrainedWidth = 320,
  constrainedHeight = 180;

function streamCam() {
  navigator.getMedia = (navigator.getUserMedia ||
    navigator.webkitGetUserMedia ||
    navigator.mozGetUserMedia ||
    navigator.msGetUserMedia);

  //**Deprecated Now, see the update**
  var video_constraints = {
    "mandatory": {
      "minWidth": constrainedWidth,
      "minHeight": constrainedHeight,
      "minFrameRate": "30"
    },
    "optional": []
  }

  navigator.getMedia({
      video: video_constraints,
      audio: false
    },
    function(stream) {
      if (navigator.mozGetUserMedia) {
        video.mozSrcObject = stream;
      } else {
        var vendorURL = window.URL || window.webkitURL;
        video.src = vendorURL.createObjectURL(stream);
      }
      video.play();
    },
    function(err) {
      console.log("An error occured! " + err);
      streamCam();
    }
  );


}

function FFResize() {
  canvas.width = constrainedWidth;
  canvas.height = constrainedHeight;
  canvas.getContext('2d').drawImage(video, 0, 0, constrainedWidth, constrainedHeight);
  setTimeout(function() {
    requestAnimationFrame(FFResize)
  }, 10);
}


window.onload = function() {
  video = document.querySelector('#video'),
    canvas = document.querySelector('#canvas');

  streamCam();

  video.addEventListener('playing', function(ev) {
    if (!streaming) {
      if (video.videoHeight === 0) {
        window.setTimeout(function() {
          video.pause();
          video.play();
        }, 100);
      } else {
        video.setAttribute('width', video.videoWidth);
        video.setAttribute('height', video.videoHeight);
        canvas.setAttribute('width', constrainedWidth);
        canvas.setAttribute('height', constrainedHeight);
        streaming = true;
        requestAnimationFrame(FFResize);
      }
    }
  }, false);

};
#canvas {
  position: fixed;
  top: 0;
}
<video id="video"></video>
<canvas id="canvas"></canvas>

This does its job but as you noted constraints are still in dev and the only way to have them to work with Firefox is to manually set every browser's media.navigator.video.default_ in about:config.