secret
Last active

Normalizing getUserMedia between Opera's and Chrome's implementation. Neither of which are 100% spec complaint.

  • Download Gist
demo.html
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
<!DOCTYPE html>
<video id="video" autoplay>
<source type="video/webm" src="http://miketaylr.com/misc/dontstop.webm">
//real demo should have real source elements
//for all your favorite codec flavors
</video>
<script>
//normalize window.URL
window.URL || (window.URL = window.webkitURL || window.msURL || window.oURL);
 
//normalize navigator.getUserMedia
navigator.getUserMedia || (navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
//detect if {video: true} or "video" style options
//by creating an iframe and blowing it up
//style jacked from @kangax
var optionStyle = (function(win){
//only test if there's something to test
if (!navigator.getUserMedia) return;
 
var el = document.createElement('iframe'),
root = document.body || document.documentElement,
string = true, object = true, nop = function(){};
root.appendChild(el);
var f = win.frames[win.frames.length-1];
 
f.navigator.getUserMedia || (f.navigator.getUserMedia = f.navigator.webkitGetUserMedia || f.navigator.mozGetuserMedia || f.navigator.msGetUserMedia);
 
try { //try it with spec syntax
f.navigator.getUserMedia({video: true}, nop);
} catch (e) {
object = false;
try { //try it with old spec string syntax
f.navigator.getUserMedia("video", nop);
} catch (e) { //neither is supported
string = false;
}
} finally { //clean up
root.removeChild(el);
el = null;
}
return {string: string, object: object}
})(window),
//normalize the options object to a string
//if that's the only thing supported
norm = function(opts){ // has to be {video: false, audio: true}. caveat emptor.
var stringOptions = [];
 
if (optionStyle.string && !optionStyle.object) {
//pluck the "true"s
for (var o in opts) {
if (opts[o] == true) {
stringOptions.push(o);
}
}
return stringOptions.join(" ");
} else {
 
//really, this will blow up if you pass it {video: true, rofl: "copter"}. so don't.
return opts;
}
},
 
hollaback = function(stream) {
video.src = (window.URL && window.URL.createObjectURL) ? window.URL.createObjectURL(stream) : stream;
},
errback = function() {
//doSomethingUsefulHere()
},
 
video = document.getElementById('video');
if (navigator.getUserMedia) {
navigator.getUserMedia(norm({video: true, audio: true}), hollaback, errback);
}
</script>

By line number:

9: reference any prefixed window.URL object as window.URL to make our lives nicer. Note there's no mozURL because it doesn't exist, AFAIK.

12: do the same for navigator.getUserMedia

17: optionsStyle is an immediately invoked function expression (IIFE) that does sort of a feature test. Basically it creates some iframes and tries to use getUserMedia with the new object ({video: true, audio: false}) syntax. Then it tries with the old string ("video audio") syntax. Failing that, it throws a NoDiceError.

42: optionsStyle returns an object that looks like this: {string: true, object: false}.

47: norm is a helper function to normalize the spec object syntax into whatever the UA can handle, based on what gets returned from optionsStyle. In a nutshell, it takes the object argument, looks for the trues and makes a string out of those--if the string, but not the object, syntax is supported.

66: if window.URL.createObjectURL exists, pass it a stream and set the src of the video. Otherwise, set the raw stream to the video src.

76: navigator.getUserMedia(norm({video: true, audio: true}), hollaback, errback); this is how you use it. MAGICK™.

Removed throwing the error and added an early bail from the optionsStyle IIFE. Less nice, but less errors thrown in production. ;)

Suggest we call it "gUM shield" ?

A similar effort, inspired by gUM Shield https://gist.github.com/2004018

Fantastic – thanks!

One minor point...

With the changes to getUserMedia() in Chrome Canary a few days ago, permission is now requested when getUserMedia() is called.

This means that with guMShield, permission is requested twice: once in the try block, and once when navigator.getUserMedia() is called.

Maybe the code could be restructured just to use the try block?

Ack, just deleted my comment. >_<

I think @paulirish mentioned to me that Chrome Canary added support for the spec syntax--so I wonder if we can get away without using this at all...

If not, yeah this should be fixed.

mr @agektmr found a clever trick for handling both init cases (object and string), that's worth considering:

navigator.webkitGetUserMedia(
  {audio : true, video : true, toString : function(){return "video,audio";}},
  onSuccess,
  onError
);

Ha! That's evil, in a nice way :^).
On May 22, 2012 5:27 PM, "Paul Irish" <
reply@reply.github.com>
wrote:

mr @agektmr found a clever trick for handling both init cases (object and
string), that's worth considering:

navigator.webkitGetUserMedia(
 {audio : true, video : true, toString : function(){return
"video,audio";}},
 onSuccess,
 onError
);

Reply to this email directly or view it on GitHub:
https://gist.github.com/f2ac64ed7fc467ccdfe3

I'm going to be implementing "native" audio recording soon using no plugins (strictly HTML5 API / extensions). I'd love to see more comments here and activity -- I'll contribute when I have something. As far as the context of this thread goes... Paul and Mike, please post any resources you have that are related since this topic is still evolving. Thank you Mike for creating this page!

@say2joe I believe getUserMedia + Web Audio API can achieve this

window.URL.createObjectURL(stream) chokes on Firefox 18... looks like this works:

  hollaback = function(stream) {
    if (navigator.mozGetUserMedia) {
      // HACK for ff
      video.mozSrcObject = stream;
      video.play();
    } else {
      video.src = (window.URL && window.URL.createObjectURL) ? window.URL.createObjectURL(stream) : stream;
    }
  }, 

http://people.mozilla.com/~anarayanan/webrtc/gum_test.html

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.