17

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?

lennon310
  • 12,503
  • 11
  • 43
  • 61
sidewaiise
  • 1,445
  • 3
  • 16
  • 27
  • Which version of Firefox? – Black Sheep Oct 17 '14 at 00:34
  • @aldanux Firefox 33.0 on Mac OS X 10.9.5 – sidewaiise Oct 17 '14 at 01:47
  • have you tried setting the video element's height and width – mido Oct 18 '14 at 04:27
  • Yes. It just changes the total area of the video element. The video it's self simply scales to fit inside. (without altering proportions) – sidewaiise Oct 18 '14 at 05:32
  • look at [this answer](http://stackoverflow.com/a/17529034/3702797) in that question : http://stackoverflow.com/questions/17502205/webrtc-resolution-limit it seems it is a firefox lack in features – Kaiido Nov 03 '14 at 22:12
  • @Kaiido, Yes I thought this was the case. However, I was thinking it would be possible to distort the image to match that of Chrome. Any ideas on how to do this? (I'd stopped working on this for couple months and only just now getting back to it...) – sidewaiise Jan 08 '15 at 03:37
  • (HURRY UP MOZILLA... GET YOUR FEATURES UP TO DATE!!) thx – sidewaiise Jan 08 '15 at 03:38
  • Do you mean for every FF user? Because you could for your personal one modify , in `about:config`the value `media.navigator.video.default_height` to 180 and `media.navigator.video.default_width` to 320. Or a workaround (probably a bad one) could be to draw it in a canvas after modifying the h/w of every images – Kaiido Jan 08 '15 at 09:21
  • Ps: at least FF supports `getUserMedia` (IE & Safari still don't) – Kaiido Jan 08 '15 at 09:22
  • That last point is very valid :). I actually found the solution to this.. I'll post it soon. – sidewaiise Jan 08 '15 at 09:39
  • Firefox *does* support resolution constraints (though not the `aspectRatio` one). See my answer http://stackoverflow.com/questions/28282385/webrtc-firefox-constraints/28911694#28911694 - though your aspect being wrong sounds more like a bug. What camera/OS do you have? – jib Mar 07 '15 at 05:56

2 Answers2

27

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.

Community
  • 1
  • 1
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • Very good answer, thanks for the full explanation. Your running code snippet is very good :) – sidewaiise Jan 08 '15 at 09:56
  • Just to clear up this misconception: Constraints work just fine in Firefox provided you use the standard syntax rather than the outdated Chrome syntax. It should be `width: { min: 640 }` not `minWidth: 640`. Also skip `mandatory`. See [this answer](http://stackoverflow.com/questions/28282385/webrtc-firefox-constraints/28911694#28911694). – jib Apr 02 '15 at 19:07
  • @jib yeah I saw this answer, (Look at my comment on the accepted answer) I'll update the answer, But I'm not sure your Idea of skipping Chrome syntax is a good one since Chrome throws an error, with w3's syntax and won't display anything. However, I'll put kinda *polyfill* I wrote in an edit in few minutes. Therefore, I'm not sure when `require: ["width", "height"]` was integrated in FF. I'll have to try with older versions, so I'll let my answer for now. – Kaiido Apr 03 '15 at 00:17
  • Ps: look at this answer from FF guy, back in july 13 http://stackoverflow.com/questions/17502205/webrtc-resolution-limit/17529034#17529034 – Kaiido Apr 03 '15 at 00:20
  • 2
    @Kaiido That answer is almost three years old. I added the `require` syntax in FF32 last year, an unfortunate detour in the spec as the new syntax was being worked out (I work with jesup). - I agree it is unfortunate that Chome throws an error, but I suggest filing an issue with them, as we must converge on the standard, not on something else. I'm trying to fix this in *adapter.js*, the cross-browser shim, and I have a [PR](https://github.com/webrtc/samples/pull/493) for a constraints-polyfill pending there that makes spec constraints work with Chrome and older versions of Firefox. – jib Apr 03 '15 at 00:53
  • Two years old since I can't count. – jib Apr 03 '15 at 01:06
  • 4
    **Update:** [adapter.js](https://github.com/webrtc/adapter) now lets you use spec-constraints with both Chrome and Firefox! – jib Jun 08 '15 at 15:10
  • snippet don't works! – JRichardsz Jan 15 '23 at 16:55
  • @JRichardsz yes this question is about an interop issue that occurred in 2015 (8 years ago). The interop issue has been resolved since then. Are you really using these old browsers? The `navigator.getUserMedia` API has been replaced by `navigator.mediaDevices.getUserMedia`. And StackSnippets have been modified in a way they don't support gUM requests anymore. Honestly I'm not sure how I should update this answer as the question itself is out of date. – Kaiido Jan 16 '23 at 02:11
1

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/

sidewaiise
  • 1,445
  • 3
  • 16
  • 27
  • doesn't seem to work anymore ... I've been having the same problem. When there is a chat between a chrome client and a firefox client, the video coming from firefox is always 4:3 no matter which version I've tried. I'm now on firefox 35 and you're fiddle doesn't work either... – Ted Jan 20 '15 at 10:40
  • If all you wanted with your question was to resize the video playing on the page, then yes resizing the video playback element is the right answer. Constraints access different camera modes, and since people have different cameras with different abilities, one should be careful not to over-constrain on hardware abilities for risk of excluding people unintentionally. Sending video over a peerConnection is a different matter however. Even though peerConnection adjusts bitrate dynamically, avoiding high resolutions and frame rates when you don't need them helps. Fiddle worked for me FWIW. – jib Apr 02 '15 at 19:23