2

I use the following code from (https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia):

navigator.mediaDevices = navigator.mediaDevices || ((navigator.mozGetUserMedia || navigator.webkitGetUserMedia) ? {
   getUserMedia: function(c) {
     return new Promise(function(y, n) {
       (navigator.mozGetUserMedia || navigator.webkitGetUserMedia).call(navigator, c, y, n);
     });
   }
} : null);

to setup the microphone for use. This works great in Chrome (v45) and Firefox (v36), but in Firefox (v41) I get the following error in the console:

Error: setting a property that has only a getter
RecorderSvc.initAudio@http://fakewebsite.com/js/services/recorder.js:61:1

I can solve the problem by doing:

if (navigator.mozGetUserMedia || navigator.webkitGetUserMedia) {
    navigator.mediaDevices.getUserMedia = function(c) {
        return new Promise(function(y, n) {
            (navigator.mozGetUserMedia || navigator.webkitGetUserMedia).call(navigator, c, y, n);
        });
    }
}

but this doesn't work in Chrome or Firefox (v36).

  • In Chrome, only navigator.webkitGetUserMedia is defined.
  • In Firefox (v36), only navigator.mozGetUserMedia is defined.
  • In Firefox (v41), both navigator.mozGetUserMedia AND navigator.mediaDevices are defined.

I can't figure out how to fix this without breaking one of the browsers. Any ideas?

JulieMarie
  • 400
  • 1
  • 6
  • 21

2 Answers2

2

The code you've copied (which I wrote incidentally) does a lousy job trying to polyfill navigator.mediaDevices.getUserMedia into browsers that don't have it natively yet. It has since been removed from where you found it. Thanks for spotting that it's broken.

Polyfilling is tricky business, so I highly recommend using adapter.js, the official WebRTC polyfill, instead of attempting to shim it manually. You can use a recent version of adapter.js, or link to the always latest one directly, like this:

<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>

See https://jsfiddle.net/srn9db4h/ for an example of this that works in the browsers you mention.

I wont try to correct the code you mention, because polyfilling navigator.mediaDevices correctly got quite complicated.

It's also insufficient, because the location of getUserMedia isn't the only thing that has changed in the specification. The format of the constraints argument has changed as well, which is why navigator.mediaDevices.getUsermedia is still behind an experimental flag in Chrome 45.

adapter.js takes care of all this until the browsers catch up.

Community
  • 1
  • 1
jib
  • 40,579
  • 17
  • 100
  • 158
  • Sweet! Thanks @jib! It worked until we tried it on Firefox v41! I will definitely look into adapter.js. Trying to get a handle on WebRTC for our needs is giving me a headache in Firefox! – JulieMarie Oct 14 '15 at 20:02
  • I agree it is frustrating when APIs change. Ironically Firefox is much closer to spec on the APIs [at this moment](http://iswebrtcreadyyet.com/) than Chrome is. adapter.js is maintained by Google though, and is a good stopgap. The Media Capture and Streams spec is finally stable, but the PeerConnection spec is still undergoing last-minute changes, so for production you might want to snapshot the latest adapter.js rather than linking to the latest one which may change with spec changes. – jib Oct 14 '15 at 20:29
  • I'll have to read into the specs a little more. When the permission request for audio/video is made in FF and you dismiss it, there's no notification whatsoever. Is that spec? At least Chrome responds and lets you know the request was dismissed! – JulieMarie Oct 14 '15 at 21:40
  • I bet it's still there, minimized in the URL bar. See https://bugzil.la/1004055 and related issues. The spec leaves permission models to the browsers. Chrome's has different tradeoffs. – jib Oct 14 '15 at 23:25
  • I'm guessing you're talking about the grey microphone? Yep okay, I know about that and by reminding me of that, I just discovered another flaw with my logic! I pop up a window right after the gUM is displayed requesting that the user "Always Share", but if they dismiss the gUM by x'ing it out for whatever the reason, the user will be lost! Thank you for pointing me to the bug you posted...I've looked through the bugs often. I just wish that FF would return "PermissionDeniedError" and "PermissionDismissedError" like Chrome does. – JulieMarie Oct 15 '15 at 15:51
  • As mentioned in https://bugzil.la/947266#c28 that is not to spec unfortunately. Also, `PermissionDeniedError` is now [`SecurityError`](https://bugzilla.mozilla.org/show_bug.cgi?id=1206982). – jib Oct 16 '15 at 12:24
1

If you surround your statement in a Try Catch block, it'll work.

if (navigator.mediaDevices || (navigator.mozGetUserMedia || navigator.webkitGetUserMedia)) {
  try {
    navigator.mediaDevices = {
      getUserMedia: function(c) {
        return new Promise(function(y, n) {
          (navigator.mozGetUserMedia || navigator.webkitGetUserMedia).call(navigator, c, y, n);
        });
      }
    };
  }
  catch(err) {
    navigator.mediaDevices.getUserMedia = function(c) {
      return new Promise(function(y, n) {
        (navigator.mozGetUserMedia || navigator.webkitGetUserMedia).call(navigator, c, y, n);
      });
    }
  }
} else {
  navigator.mediaDevices = null;
}
Cory
  • 26
  • 2
  • 2
    This code overrides the native `navigator.mediaDevices.getUserMedia` function on browsers that have it, which is not great. Also, beware that `navigator.mediaDevices.getUserMedia` isn't going to work in Chrome as described in https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia just from this. You should use [adapter.js](https://github.com/webrtc/adapter/blob/master/adapter.js) instead. – jib Oct 13 '15 at 15:24