Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getUserMedia() video size in Firefox & Chrome differs

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:

ChromeMozilla

(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?

like image 208
sidewaiise Avatar asked Oct 16 '14 23:10

sidewaiise


People also ask

What does getUserMedia return?

It returns a Promise that resolves to a MediaStream object. If the user denies permission, or matching media is not available, then the promise is rejected with NotAllowedError or NotFoundError DOMException respectively.

Which browsers support getUserMedia?

getUserMedia one. Get User Media/Stream API is supported by Mozilla Firefox browser version 36 to 61.

What is getUserMedia?

getUserMedia() method prompts the user for permission to use up to one video input device (such as a camera or shared screen) and up to one audio input device (such as a microphone) as the source for a MediaStream .

Is getUserMedia a part of WebRTC?

getUserMedia() is a part of the webRTC media capture API and is used to get access to the camera and the microphone connected to the user device (user computer, smartphone, etc.) from the browser.

How do I enable getUserMedia in Firefox?

getUserMedia has to be enabled in Firefox by setting the media.peerconnection.enabled option to true in about:config. There are a few more differences once we start coding so let’s walk through it. Our recipe for getUserMedia success will be broken down into the following easy steps:

What's the difference between getUserMedia in Firefox and edge?

Use adapter.jsto simplify the many differences that exist for getUserMedia and its constraints between browsers (the same is true for PeerConnection too) Firefox only works within a set of fixed resolutions Edge only works within a set of fixed resolutions Device orientation matters when you are requesting fixed resolutions on a mobile

Does Firefox support getUserMedia ()?

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.

How to change video resolution constraints in Firefox?

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:configpreference selections under media: Firefox about:config settings for getUserMedia()


2 Answers

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.

like image 53
Kaiido Avatar answered Sep 21 '22 04:09

Kaiido


Ok. Long time coming.

I found that the following code:

var vid_constraints = {
    mandatory: {
        maxHeight: 180,
        maxWidth: 320
    }
}

Is actually useless. What works better, is to simply use

navigator.getUserMedia({audio: true, video: true}, function(stream){..},function(e){...});

And you just set the size of the video through CSS or inline html. For my application this worked fine, as I just had to copy the video content to a canvas. Worked great in the end.

So in conclusion, the problem seemed to be the constraint options. Stay away form them if your trying to make it work in multiple browsers.

This may not work for you if you require better control over your camera resolution. If you need finite control over the res, perhaps try conditional (ie. if(navigator.mozGetUserMedia){... //compatible resolutions etc ...}).

Praying there will be a standard getUserMedia function handled by all browsers soon! :)

Just a reference. I forked some fiddleJS code and modified it to test in Chrome and Mozilla:http://jsfiddle.net/sidewaiise/Laq5txvq/

like image 22
sidewaiise Avatar answered Sep 21 '22 04:09

sidewaiise