diff --git a/demos/choose-camera.html b/demos/choose-camera.html new file mode 100644 index 0000000..01c00fd --- /dev/null +++ b/demos/choose-camera.html @@ -0,0 +1,76 @@ + + + + + + WebcamJS Test Page + + + +
Your captured image will appear here...
+ +

WebcamJS Test Page

+

Demonstrates Adobe Flash capture & display

+ +
+ + + + + + + + +
+ +
+ + + + + diff --git a/flash/Webcam.as b/flash/Webcam.as index e9b7421..7a1ea9f 100644 --- a/flash/Webcam.as +++ b/flash/Webcam.as @@ -40,6 +40,7 @@ private var image_format:String; private var fps:int; private var flip_horiz:Boolean; + private var camera_idx:int; public function Webcam() { // class constructor @@ -68,45 +69,25 @@ // Hack to auto-select iSight camera on Mac (JPEGCam Issue #5, submitted by manuel.gonzalez.noriega) // From: http://www.squidder.com/2009/03/09/trick-auto-select-mac-isight-in-flash/ - var cameraIdx:int = -1; + camera_idx = -1; for (var idx = 0, len = Camera.names.length; idx < len; idx++) { if (Camera.names[idx] == "USB Video Class Video") { - cameraIdx = idx; + camera_idx = idx; idx = len; } } - if (cameraIdx > -1) camera = Camera.getCamera( String(cameraIdx) ); + if (camera_idx > -1) camera = Camera.getCamera( String(camera_idx) ); else camera = Camera.getCamera(); if (camera != null) { - camera.addEventListener(ActivityEvent.ACTIVITY, activityHandler); - camera.addEventListener(StatusEvent.STATUS, handleCameraStatus, false, 0, true); - video = new Video( Math.max(video_width, dest_width), Math.max(video_height, dest_height) ); - video.attachCamera(camera); - addChild(video); - - if ((video_width < dest_width) && (video_height < dest_height)) { - video.scaleX = video_width / dest_width; - video.scaleY = video_height / dest_height; - } - - if (flip_horiz) { - video.scaleX *= -1; - video.x = video.width + video.x; - } - - camera.setQuality(0, 100); - camera.setKeyFrameInterval(10); - camera.setMode( Math.max(video_width, dest_width), Math.max(video_height, dest_height), fps ); - - // only detect motion once, to determine when camera is "live" - camera.setMotionLevel( 1 ); - + ExternalInterface.addCallback('_getCameras', getCameras); + ExternalInterface.addCallback('_setCamera', setCamera); + ExternalInterface.addCallback('_initCamera', initCamera); ExternalInterface.addCallback('_snap', snap); ExternalInterface.addCallback('_configure', configure); ExternalInterface.addCallback('_releaseCamera', releaseCamera); - ExternalInterface.call('Webcam.flashNotify', 'flashLoadComplete', true); + ExternalInterface.call('Webcam.flashNotify', 'flashInitComplete', true); } else { trace("You need a camera."); @@ -141,6 +122,56 @@ } } + public function getCameras() { + return Camera.names; + } + + public function setCamera(name: String) { + for (var idx = 0, len = Camera.names.length; idx < len; idx++) { + if (Camera.names[idx] == name) { + camera_idx = idx; + return; + } + } + ExternalInterface.call('Webcam.flashNotify', "error", "camera: " + name + " is not found"); + trace("camera: " + name + " is not found"); + } + + public function initCamera() { + releaseCamera(); + if (camera_idx > -1) camera = Camera.getCamera(String(camera_idx)); + else camera = Camera.getCamera(); + + if (camera != null) { + camera.addEventListener(ActivityEvent.ACTIVITY, activityHandler); + camera.addEventListener(StatusEvent.STATUS, handleCameraStatus, false, 0, true); + video = new Video(Math.max(video_width, dest_width), Math.max(video_height, dest_height)); + video.attachCamera(camera); + addChild(video); + + if ((video_width < dest_width) && (video_height < dest_height)) { + video.scaleX = video_width / dest_width; + video.scaleY = video_height / dest_height; + } + + if (flip_horiz) { + video.scaleX *= -1; + video.x = video.width + video.x; + } + + camera.setQuality(0, 100); + camera.setKeyFrameInterval(10); + camera.setMode(Math.max(video_width, dest_width), Math.max(video_height, dest_height), fps); + + // only detect motion once, to determine when camera is "live" + camera.setMotionLevel(1); + ExternalInterface.call('Webcam.flashNotify', 'flashLoadComplete', true); + } else { + trace("You need a camera."); + ExternalInterface.call('Webcam.flashNotify', "error", "No camera was detected."); + } + } + public function snap() { // take snapshot from camera, and upload if URL was provided trace("in snap(), drawing to bitmap"); @@ -185,13 +216,13 @@ } public function releaseCamera() { - trace("in releaseCamera(), turn off camera"); - video.attachCamera(null); - video.clear(); - camera = null; - removeChild(video); - + if (video != null){ + video.attachCamera(null); + video.clear(); + camera = null; + removeChild(video); + } } } } diff --git a/flash/Webcam.fla b/flash/Webcam.fla index 6f17dd1..d666554 100644 Binary files a/flash/Webcam.fla and b/flash/Webcam.fla differ diff --git a/webcam.js b/webcam.js index dd945d5..afe6889 100644 --- a/webcam.js +++ b/webcam.js @@ -38,6 +38,7 @@ var Webcam = { // globals protocol: location.protocol.match(/https/i) ? 'https' : 'http', + inited: false, // true when webcam privilege is generated loaded: false, // true when webcam movie finishes loading live: false, // true when webcam is initialized and ready to snap userMedia: true, // true when getUserMedia is supported natively @@ -222,7 +223,7 @@ var Webcam = { img.src = origObjURL; }, - attach: function(elem) { + attach: function(elem, notInit) { // create webcam preview and attach to DOM element // pass in actual DOM reference, ID, or CSS selector if (typeof(elem) == 'string') { @@ -291,48 +292,51 @@ var Webcam = { // add video element to dom elem.appendChild( video ); this.video = video; - - // ask user for access to their camera - var self = this; - this.mediaDevices.getUserMedia({ - "audio": false, - "video": this.params.constraints || { - mandatory: { - minWidth: this.params.dest_width, - minHeight: this.params.dest_height + this.inited = true; + this.dispatch("init"); + if(!notInit) { + // ask user for access to their camera + var self = this; + this.mediaDevices.getUserMedia({ + "audio": false, + "video": this.params.constraints || { + mandatory: { + minWidth: this.params.dest_width, + minHeight: this.params.dest_height + } } - } - }) - .then( function(stream) { - // got access, attach stream to video - video.onloadedmetadata = function(e) { - self.stream = stream; - self.loaded = true; - self.live = true; - self.dispatch('load'); - self.dispatch('live'); - self.flip(); - }; - // as window.URL.createObjectURL() is deprecated, adding a check so that it works in Safari. - // older browsers may not have srcObject - if ("srcObject" in video) { - video.srcObject = stream; - } - else { - // using URL.createObjectURL() as fallback for old browsers - video.src = window.URL.createObjectURL(stream); - } - }) - .catch( function(err) { - // JH 2016-07-31 Instead of dispatching error, now falling back to Flash if userMedia fails (thx @john2014) - // JH 2016-08-07 But only if flash is actually installed -- if not, dispatch error here and now. - if (self.params.enable_flash && self.detectFlash()) { - setTimeout( function() { self.params.force_flash = 1; self.attach(elem); }, 1 ); - } - else { - self.dispatch('error', err); - } - }); + }) + .then( function(stream) { + // got access, attach stream to video + video.onloadedmetadata = function(e) { + self.stream = stream; + self.loaded = true; + self.live = true; + self.dispatch('load'); + self.dispatch('live'); + self.flip(); + }; + // as window.URL.createObjectURL() is deprecated, adding a check so that it works in Safari. + // older browsers may not have srcObject + if ("srcObject" in video) { + video.srcObject = stream; + } + else { + // using URL.createObjectURL() as fallback for old browsers + video.src = window.URL.createObjectURL(stream); + } + }) + .catch( function(err) { + // JH 2016-07-31 Instead of dispatching error, now falling back to Flash if userMedia fails (thx @john2014) + // JH 2016-08-07 But only if flash is actually installed -- if not, dispatch error here and now. + if (self.params.enable_flash && self.detectFlash()) { + setTimeout( function() { self.params.force_flash = 1; self.attach(elem); }, 1 ); + } + else { + self.dispatch('error', err); + } + }); + } } else if (this.iOS) { // prepare HTML elements @@ -443,6 +447,12 @@ var Webcam = { var div = document.createElement('div'); div.innerHTML = this.getSWFHTML(); elem.appendChild( div ); + var _this = this; + if(!notInit){ + this.on("init",function() { + _this.getMovie()._initCamera(); + }); + } } else { this.dispatch('error', new WebcamError( this.params.noInterfaceFoundText )); @@ -466,6 +476,138 @@ var Webcam = { elem.style.height = '' + this.params.height + 'px'; } }, + + getCameras: function (success) { + if (this.userMedia) { + this.mediaDevices.enumerateDevices().then(function(devices) { + var data = []; + for(var i = 0;i < devices.length;i++) { + if(devices[i].kind === "videoinput") { + data.push({ + id: devices[i].deviceId, + groupId: devices[i].groupId, + label: devices[i].label + }); + } + } + success(data); + }) + } else if (this.iOS) { + } else if (this.params.enable_flash && this.detectFlash()) { + var devices = this.getMovie()._getCameras(); + var data = []; + for(var i = 0;i < devices.length;i++) { + data.push({ + id: devices[i], + groupId: "", + label: devices[i] + }); + } + success(data); + } + }, + + setAndInitCamera: function (name) { + if (this.userMedia) { + // ask user for access to their camera + var self = this; + var video = this.video; + this.mediaDevices.getUserMedia({ + "audio": false, + "video": { + deviceId: { + exact: name, + } + } + }) + .then( function(stream) { + // got access, attach stream to video + video.onloadedmetadata = function(e) { + self.stream = stream; + self.loaded = true; + self.live = true; + self.dispatch('load'); + self.dispatch('live'); + self.flip(); + }; + // as window.URL.createObjectURL() is deprecated, adding a check so that it works in Safari. + // older browsers may not have srcObject + if ("srcObject" in video) { + video.srcObject = stream; + } + else { + // using URL.createObjectURL() as fallback for old browsers + video.src = window.URL.createObjectURL(stream); + } + }) + .catch( function(err) { + // JH 2016-07-31 Instead of dispatching error, now falling back to Flash if userMedia fails (thx @john2014) + // JH 2016-08-07 But only if flash is actually installed -- if not, dispatch error here and now. + if (self.params.enable_flash && self.detectFlash()) { + setTimeout( function() { self.params.force_flash = 1; self.attach(elem); }, 1 ); + } + else { + self.dispatch('error', err); + } + }); + } else if (this.iOS) { + } else if (this.params.enable_flash && this.detectFlash()) { + var movie = this.getMovie(); + movie._setCamera(name); + movie._initCamera(); + } + }, + + initCamera: function () { + if (this.userMedia) { + // ask user for access to their camera + var self = this; + var video = this.video; + this.mediaDevices.getUserMedia({ + "audio": false, + "video": this.params.constraints || { + mandatory: { + minWidth: this.params.dest_width, + minHeight: this.params.dest_height + } + } + }) + .then( function(stream) { + // got access, attach stream to video + video.onloadedmetadata = function(e) { + self.stream = stream; + self.loaded = true; + self.live = true; + self.dispatch('load'); + self.dispatch('live'); + self.flip(); + }; + // as window.URL.createObjectURL() is deprecated, adding a check so that it works in Safari. + // older browsers may not have srcObject + if ("srcObject" in video) { + video.srcObject = stream; + } + else { + // using URL.createObjectURL() as fallback for old browsers + video.src = window.URL.createObjectURL(stream); + } + }) + .catch( function(err) { + // JH 2016-07-31 Instead of dispatching error, now falling back to Flash if userMedia fails (thx @john2014) + // JH 2016-08-07 But only if flash is actually installed -- if not, dispatch error here and now. + if (self.params.enable_flash && self.detectFlash()) { + setTimeout( function() { self.params.force_flash = 1; self.attach(elem); }, 1 ); + } + else { + self.dispatch('error', err); + } + }); + } else if (this.iOS) { + } else if (this.params.enable_flash && this.detectFlash()) { + var movie = this.getMovie(); + movie._initCamera(); + } + }, reset: function() { // shutdown camera, reset to potentially attach again @@ -670,7 +812,7 @@ var Webcam = { getMovie: function() { // get reference to movie object/embed in DOM - if (!this.loaded) return this.dispatch('error', new FlashError("Flash Movie is not loaded yet")); + if (!this.inited) return this.dispatch('error', new FlashError("Flash Movie is not loaded yet")); var movie = document.getElementById('webcam_movie_obj'); if (!movie || !movie._snap) movie = document.getElementById('webcam_movie_embed'); if (!movie) this.dispatch('error', new FlashError("Cannot locate Flash movie in DOM")); @@ -933,6 +1075,10 @@ var Webcam = { flashNotify: function(type, msg) { // receive notification from flash about event switch (type) { + case "flashInitComplete": + this.inited = true; + this.dispatch("init"); + break; case 'flashLoadComplete': // movie loaded successfully this.loaded = true; diff --git a/webcam.min.js b/webcam.min.js index 03de2f7..9587063 100644 --- a/webcam.min.js +++ b/webcam.min.js @@ -1,2 +1 @@ -// WebcamJS v1.0.25 - http://github.com/jhuckaby/webcamjs - MIT Licensed -(function(e){var t;function a(){var e=Error.apply(this,arguments);e.name=this.name="FlashError";this.stack=e.stack;this.message=e.message}function i(){var e=Error.apply(this,arguments);e.name=this.name="WebcamError";this.stack=e.stack;this.message=e.message}var s=function(){};s.prototype=Error.prototype;a.prototype=new s;i.prototype=new s;var Webcam={version:"1.0.26",protocol:location.protocol.match(/https/i)?"https":"http",loaded:false,live:false,userMedia:true,iOS:/iPad|iPhone|iPod/.test(navigator.userAgent)&&!e.MSStream,params:{width:0,height:0,dest_width:0,dest_height:0,image_format:"jpeg",jpeg_quality:90,enable_flash:true,force_flash:false,flip_horiz:false,fps:30,upload_name:"webcam",constraints:null,swfURL:"",flashNotDetectedText:"ERROR: No Adobe Flash Player detected. Webcam.js relies on Flash for browsers that do not support getUserMedia (like yours).",noInterfaceFoundText:"No supported webcam interface found.",unfreeze_snap:true,iosPlaceholderText:"Click here to open camera.",user_callback:null,user_canvas:null},errors:{FlashError:a,WebcamError:i},hooks:{},init:function(){var t=this;this.mediaDevices=navigator.mediaDevices&&navigator.mediaDevices.getUserMedia?navigator.mediaDevices:navigator.mozGetUserMedia||navigator.webkitGetUserMedia?{getUserMedia:function(e){return new Promise(function(t,a){(navigator.mozGetUserMedia||navigator.webkitGetUserMedia).call(navigator,e,t,a)})}}:null;e.URL=e.URL||e.webkitURL||e.mozURL||e.msURL;this.userMedia=this.userMedia&&!!this.mediaDevices&&!!e.URL;if(navigator.userAgent.match(/Firefox\D+(\d+)/)){if(parseInt(RegExp.$1,10)<21)this.userMedia=null}if(this.userMedia){e.addEventListener("beforeunload",function(e){t.reset()})}},exifOrientation:function(e){var t=new DataView(e);if(t.getUint8(0)!=255||t.getUint8(1)!=216){console.log("Not a valid JPEG file");return 0}var a=2;var i=null;while(a8){console.log("Invalid EXIF orientation value ("+p+")");return 0}return p}}}else{a+=2+t.getUint16(a+2)}}return 0},fixOrientation:function(e,t,a){var i=new Image;i.addEventListener("load",function(e){var s=document.createElement("canvas");var r=s.getContext("2d");if(t<5){s.width=i.width;s.height=i.height}else{s.width=i.height;s.height=i.width}switch(t){case 2:r.transform(-1,0,0,1,i.width,0);break;case 3:r.transform(-1,0,0,-1,i.width,i.height);break;case 4:r.transform(1,0,0,-1,0,i.height);break;case 5:r.transform(0,1,1,0,0,0);break;case 6:r.transform(0,1,-1,0,i.height,0);break;case 7:r.transform(0,-1,-1,0,i.height,i.width);break;case 8:r.transform(0,-1,1,0,0,i.width);break}r.drawImage(i,0,0);a.src=s.toDataURL()},false);i.src=e},attach:function(a){if(typeof a=="string"){a=document.getElementById(a)||document.querySelector(a)}if(!a){return this.dispatch("error",new i("Could not locate DOM element to attach to."))}this.container=a;a.innerHTML="";var s=document.createElement("div");a.appendChild(s);this.peg=s;if(!this.params.width)this.params.width=a.offsetWidth;if(!this.params.height)this.params.height=a.offsetHeight;if(!this.params.width||!this.params.height){return this.dispatch("error",new i("No width and/or height for webcam. Please call set() first, or attach to a visible element."))}if(!this.params.dest_width)this.params.dest_width=this.params.width;if(!this.params.dest_height)this.params.dest_height=this.params.height;this.userMedia=t===undefined?this.userMedia:t;if(this.params.force_flash){t=this.userMedia;this.userMedia=null}if(typeof this.params.fps!=="number")this.params.fps=30;var r=this.params.width/this.params.dest_width;var o=this.params.height/this.params.dest_height;if(this.userMedia){var n=document.createElement("video");n.setAttribute("autoplay","autoplay");n.setAttribute("playsinline","playsinline");n.style.width=""+this.params.dest_width+"px";n.style.height=""+this.params.dest_height+"px";if(r!=1||o!=1){a.style.overflow="hidden";n.style.webkitTransformOrigin="0px 0px";n.style.mozTransformOrigin="0px 0px";n.style.msTransformOrigin="0px 0px";n.style.oTransformOrigin="0px 0px";n.style.transformOrigin="0px 0px";n.style.webkitTransform="scaleX("+r+") scaleY("+o+")";n.style.mozTransform="scaleX("+r+") scaleY("+o+")";n.style.msTransform="scaleX("+r+") scaleY("+o+")";n.style.oTransform="scaleX("+r+") scaleY("+o+")";n.style.transform="scaleX("+r+") scaleY("+o+")"}a.appendChild(n);this.video=n;var l=this;this.mediaDevices.getUserMedia({audio:false,video:this.params.constraints||{mandatory:{minWidth:this.params.dest_width,minHeight:this.params.dest_height}}}).then(function(t){n.onloadedmetadata=function(e){l.stream=t;l.loaded=true;l.live=true;l.dispatch("load");l.dispatch("live");l.flip()};if("srcObject"in n){n.srcObject=t}else{n.src=e.URL.createObjectURL(t)}}).catch(function(e){if(l.params.enable_flash&&l.detectFlash()){setTimeout(function(){l.params.force_flash=1;l.attach(a)},1)}else{l.dispatch("error",e)}})}else if(this.iOS){var h=document.createElement("div");h.id=this.container.id+"-ios_div";h.className="webcamjs-ios-placeholder";h.style.width=""+this.params.width+"px";h.style.height=""+this.params.height+"px";h.style.textAlign="center";h.style.display="table-cell";h.style.verticalAlign="middle";h.style.backgroundRepeat="no-repeat";h.style.backgroundSize="contain";h.style.backgroundPosition="center";var c=document.createElement("span");c.className="webcamjs-ios-text";c.innerHTML=this.params.iosPlaceholderText;h.appendChild(c);var d=document.createElement("img");d.id=this.container.id+"-ios_img";d.style.width=""+this.params.dest_width+"px";d.style.height=""+this.params.dest_height+"px";d.style.display="none";h.appendChild(d);var f=document.createElement("input");f.id=this.container.id+"-ios_input";f.setAttribute("type","file");f.setAttribute("accept","image/*");f.setAttribute("capture","camera");var l=this;var m=this.params;f.addEventListener("change",function(e){if(e.target.files.length>0&&e.target.files[0].type.indexOf("image/")==0){var t=URL.createObjectURL(e.target.files[0]);var a=new Image;a.addEventListener("load",function(e){var t=document.createElement("canvas");t.width=m.dest_width;t.height=m.dest_height;var i=t.getContext("2d");ratio=Math.min(a.width/m.dest_width,a.height/m.dest_height);var s=m.dest_width*ratio;var r=m.dest_height*ratio;var o=(a.width-s)/2;var n=(a.height-r)/2;i.drawImage(a,o,n,s,r,0,0,m.dest_width,m.dest_height);var l=t.toDataURL();d.src=l;h.style.backgroundImage="url('"+l+"')"},false);var i=new FileReader;i.addEventListener("load",function(e){var i=l.exifOrientation(e.target.result);if(i>1){l.fixOrientation(t,i,a)}else{a.src=t}},false);var s=new XMLHttpRequest;s.open("GET",t,true);s.responseType="blob";s.onload=function(e){if(this.status==200||this.status===0){i.readAsArrayBuffer(this.response)}};s.send()}},false);f.style.display="none";a.appendChild(f);h.addEventListener("click",function(e){if(m.user_callback){l.snap(m.user_callback,m.user_canvas)}else{f.style.display="block";f.focus();f.click();f.style.display="none"}},false);a.appendChild(h);this.loaded=true;this.live=true}else if(this.params.enable_flash&&this.detectFlash()){e.Webcam=Webcam;var h=document.createElement("div");h.innerHTML=this.getSWFHTML();a.appendChild(h)}else{this.dispatch("error",new i(this.params.noInterfaceFoundText))}if(this.params.crop_width&&this.params.crop_height){var p=Math.floor(this.params.crop_width*r);var u=Math.floor(this.params.crop_height*o);a.style.width=""+p+"px";a.style.height=""+u+"px";a.style.overflow="hidden";a.scrollLeft=Math.floor(this.params.width/2-p/2);a.scrollTop=Math.floor(this.params.height/2-u/2)}else{a.style.width=""+this.params.width+"px";a.style.height=""+this.params.height+"px"}},reset:function(){if(this.preview_active)this.unfreeze();this.unflip();if(this.userMedia){if(this.stream){if(this.stream.getVideoTracks){var e=this.stream.getVideoTracks();if(e&&e[0]&&e[0].stop)e[0].stop()}else if(this.stream.stop){this.stream.stop()}}delete this.stream;delete this.video}if(this.userMedia!==true&&this.loaded&&!this.iOS){var t=this.getMovie();if(t&&t._releaseCamera)t._releaseCamera()}if(this.container){this.container.innerHTML="";delete this.container}this.loaded=false;this.live=false},set:function(){if(arguments.length==1){for(var e in arguments[0]){this.params[e]=arguments[0][e]}}else{this.params[arguments[0]]=arguments[1]}},on:function(e,t){e=e.replace(/^on/i,"").toLowerCase();if(!this.hooks[e])this.hooks[e]=[];this.hooks[e].push(t)},off:function(e,t){e=e.replace(/^on/i,"").toLowerCase();if(this.hooks[e]){if(t){var a=this.hooks[e].indexOf(t);if(a>-1)this.hooks[e].splice(a,1)}else{this.hooks[e]=[]}}},dispatch:function(){var t=arguments[0].replace(/^on/i,"").toLowerCase();var s=Array.prototype.slice.call(arguments,1);if(this.hooks[t]&&this.hooks[t].length){for(var r=0,o=this.hooks[t].length;rERROR: the Webcam.js Flash fallback does not work from local disk. Please run it from a web server.'}if(!this.detectFlash()){this.dispatch("error",new a("Adobe Flash Player not found. Please install from get.adobe.com/flashplayer and try again."));return'

'+this.params.flashNotDetectedText+"

"}if(!i){var s="";var r=document.getElementsByTagName("script");for(var o=0,n=r.length;o';return t},getMovie:function(){if(!this.loaded)return this.dispatch("error",new a("Flash Movie is not loaded yet"));var e=document.getElementById("webcam_movie_obj");if(!e||!e._snap)e=document.getElementById("webcam_movie_embed");if(!e)this.dispatch("error",new a("Cannot locate Flash movie in DOM"));return e},freeze:function(){var e=this;var t=this.params;if(this.preview_active)this.unfreeze();var a=this.params.width/this.params.dest_width;var i=this.params.height/this.params.dest_height;this.unflip();var s=t.crop_width||t.dest_width;var r=t.crop_height||t.dest_height;var o=document.createElement("canvas");o.width=s;o.height=r;var n=o.getContext("2d");this.preview_canvas=o;this.preview_context=n;if(a!=1||i!=1){o.style.webkitTransformOrigin="0px 0px";o.style.mozTransformOrigin="0px 0px";o.style.msTransformOrigin="0px 0px";o.style.oTransformOrigin="0px 0px";o.style.transformOrigin="0px 0px";o.style.webkitTransform="scaleX("+a+") scaleY("+i+")";o.style.mozTransform="scaleX("+a+") scaleY("+i+")";o.style.msTransform="scaleX("+a+") scaleY("+i+")";o.style.oTransform="scaleX("+a+") scaleY("+i+")";o.style.transform="scaleX("+a+") scaleY("+i+")"}this.snap(function(){o.style.position="relative";o.style.left=""+e.container.scrollLeft+"px";o.style.top=""+e.container.scrollTop+"px";e.container.insertBefore(o,e.peg);e.container.style.overflow="hidden";e.preview_active=true},o)},unfreeze:function(){if(this.preview_active){this.container.removeChild(this.preview_canvas);delete this.preview_context;delete this.preview_canvas;this.preview_active=false;this.flip()}},flip:function(){if(this.params.flip_horiz){var e=this.container.style;e.webkitTransform="scaleX(-1)";e.mozTransform="scaleX(-1)";e.msTransform="scaleX(-1)";e.oTransform="scaleX(-1)";e.transform="scaleX(-1)";e.filter="FlipH";e.msFilter="FlipH"}},unflip:function(){if(this.params.flip_horiz){var e=this.container.style;e.webkitTransform="scaleX(1)";e.mozTransform="scaleX(1)";e.msTransform="scaleX(1)";e.oTransform="scaleX(1)";e.transform="scaleX(1)";e.filter="";e.msFilter=""}},savePreview:function(e,t){var a=this.params;var i=this.preview_canvas;var s=this.preview_context;if(t){var r=t.getContext("2d");r.drawImage(i,0,0)}e(t?null:i.toDataURL("image/"+a.image_format,a.jpeg_quality/100),i,s);if(this.params.unfreeze_snap)this.unfreeze()},snap:function(e,t){if(!e)e=this.params.user_callback;if(!t)t=this.params.user_canvas;var a=this;var s=this.params;if(!this.loaded)return this.dispatch("error",new i("Webcam is not loaded yet"));if(!e)return this.dispatch("error",new i("Please provide a callback function or canvas to snap()"));if(this.preview_active){this.savePreview(e,t);return null}var r=document.createElement("canvas");r.width=this.params.dest_width;r.height=this.params.dest_height;var o=r.getContext("2d");if(this.params.flip_horiz){o.translate(s.dest_width,0);o.scale(-1,1)}var n=function(){if(this.src&&this.width&&this.height){o.drawImage(this,0,0,s.dest_width,s.dest_height)}if(s.crop_width&&s.crop_height){var a=document.createElement("canvas");a.width=s.crop_width;a.height=s.crop_height;var i=a.getContext("2d");i.drawImage(r,Math.floor(s.dest_width/2-s.crop_width/2),Math.floor(s.dest_height/2-s.crop_height/2),s.crop_width,s.crop_height,0,0,s.crop_width,s.crop_height);o=i;r=a}if(t){var n=t.getContext("2d");n.drawImage(r,0,0)}e(t?null:r.toDataURL("image/"+s.image_format,s.jpeg_quality/100),r,o)};if(this.userMedia){o.drawImage(this.video,0,0,this.params.dest_width,this.params.dest_height);n()}else if(this.iOS){var l=document.getElementById(this.container.id+"-ios_div");var h=document.getElementById(this.container.id+"-ios_img");var c=document.getElementById(this.container.id+"-ios_input");iFunc=function(e){n.call(h);h.removeEventListener("load",iFunc);l.style.backgroundImage="none";h.removeAttribute("src");c.value=null};if(!c.value){h.addEventListener("load",iFunc);c.style.display="block";c.focus();c.click();c.style.display="none"}else{iFunc(null)}}else{var d=this.getMovie()._snap();var h=new Image;h.onload=n;h.src="data:image/"+this.params.image_format+";base64,"+d}return null},configure:function(e){if(!e)e="camera";this.getMovie()._configure(e)},flashNotify:function(e,t){switch(e){case"flashLoadComplete":this.loaded=true;this.dispatch("load");break;case"cameraLive":this.live=true;this.dispatch("live");break;case"error":this.dispatch("error",new a(t));break;default:break}},b64ToUint6:function(e){return e>64&&e<91?e-65:e>96&&e<123?e-71:e>47&&e<58?e+4:e===43?62:e===47?63:0},base64DecToArr:function(e,t){var a=e.replace(/[^A-Za-z0-9\+\/]/g,""),i=a.length,s=t?Math.ceil((i*3+1>>2)/t)*t:i*3+1>>2,r=new Uint8Array(s);for(var o,n,l=0,h=0,c=0;c>>(16>>>o&24)&255}l=0}}return r},upload:function(e,t,a){var i=this.params.upload_name||"webcam";var s="";if(e.match(/^data\:image\/(\w+)/))s=RegExp.$1;else throw"Cannot locate image format in Data URI";var r=e.replace(/^data\:image\/\w+\;base64\,/,"");var o=new XMLHttpRequest;o.open("POST",t,true);if(o.upload&&o.upload.addEventListener){o.upload.addEventListener("progress",function(e){if(e.lengthComputable){var t=e.loaded/e.total;Webcam.dispatch("uploadProgress",t,e)}},false)}var n=this;o.onload=function(){if(a)a.apply(n,[o.status,o.responseText,o.statusText]);Webcam.dispatch("uploadComplete",o.status,o.responseText,o.statusText)};var l=new Blob([this.base64DecToArr(r)],{type:"image/"+s});var h=new FormData;h.append(i,l,i+"."+s.replace(/e/,""));o.send(h)}};Webcam.init();if(typeof define==="function"&&define.amd){define(function(){return Webcam})}else if(typeof module==="object"&&module.exports){module.exports=Webcam}else{e.Webcam=Webcam}})(window); \ No newline at end of file +(function(u){var v;function l(){var e=Error.apply(this,arguments);e.name=this.name="FlashError";this.stack=e.stack;this.message=e.message}function g(){var e=Error.apply(this,arguments);e.name=this.name="WebcamError";this.stack=e.stack;this.message=e.message}var e=function(){};e.prototype=Error.prototype;l.prototype=new e;g.prototype=new e;var w={version:"1.0.26",protocol:location.protocol.match(/https/i)?"https":"http",inited:false,loaded:false,live:false,userMedia:true,iOS:/iPad|iPhone|iPod/.test(navigator.userAgent)&&!u.MSStream,params:{width:0,height:0,dest_width:0,dest_height:0,image_format:"jpeg",jpeg_quality:90,enable_flash:true,force_flash:false,flip_horiz:false,fps:30,upload_name:"webcam",constraints:null,swfURL:"",flashNotDetectedText:"ERROR: No Adobe Flash Player detected. Webcam.js relies on Flash for browsers that do not support getUserMedia (like yours).",noInterfaceFoundText:"No supported webcam interface found.",unfreeze_snap:true,iosPlaceholderText:"Click here to open camera.",user_callback:null,user_canvas:null},errors:{FlashError:l,WebcamError:g},hooks:{},init:function(){var t=this;this.mediaDevices=navigator.mediaDevices&&navigator.mediaDevices.getUserMedia?navigator.mediaDevices:navigator.mozGetUserMedia||navigator.webkitGetUserMedia?{getUserMedia:function(a){return new Promise(function(e,t){(navigator.mozGetUserMedia||navigator.webkitGetUserMedia).call(navigator,a,e,t)})}}:null;u.URL=u.URL||u.webkitURL||u.mozURL||u.msURL;this.userMedia=this.userMedia&&!!this.mediaDevices&&!!u.URL;if(navigator.userAgent.match(/Firefox\D+(\d+)/)){if(parseInt(RegExp.$1,10)<21)this.userMedia=null}if(this.userMedia){u.addEventListener("beforeunload",function(e){t.reset()})}},exifOrientation:function(e){var t=new DataView(e);if(t.getUint8(0)!=255||t.getUint8(1)!=216){console.log("Not a valid JPEG file");return 0}var a=2;var i=null;while(a8){console.log("Invalid EXIF orientation value ("+p+")");return 0}return p}}}else{a+=2+t.getUint16(a+2)}}return 0},fixOrientation:function(e,i,s){var r=new Image;r.addEventListener("load",function(e){var t=document.createElement("canvas");var a=t.getContext("2d");if(i<5){t.width=r.width;t.height=r.height}else{t.width=r.height;t.height=r.width}switch(i){case 2:a.transform(-1,0,0,1,r.width,0);break;case 3:a.transform(-1,0,0,-1,r.width,r.height);break;case 4:a.transform(1,0,0,-1,0,r.height);break;case 5:a.transform(0,1,1,0,0,0);break;case 6:a.transform(0,1,-1,0,r.height,0);break;case 7:a.transform(0,-1,-1,0,r.height,r.width);break;case 8:a.transform(0,-1,1,0,0,r.width);break}a.drawImage(r,0,0);s.src=t.toDataURL()},false);r.src=e},attach:function(t,e){if(typeof t=="string"){t=document.getElementById(t)||document.querySelector(t)}if(!t){return this.dispatch("error",new g("Could not locate DOM element to attach to."))}this.container=t;t.innerHTML="";var a=document.createElement("div");t.appendChild(a);this.peg=a;if(!this.params.width)this.params.width=t.offsetWidth;if(!this.params.height)this.params.height=t.offsetHeight;if(!this.params.width||!this.params.height){return this.dispatch("error",new g("No width and/or height for webcam. Please call set() first, or attach to a visible element."))}if(!this.params.dest_width)this.params.dest_width=this.params.width;if(!this.params.dest_height)this.params.dest_height=this.params.height;this.userMedia=v===undefined?this.userMedia:v;if(this.params.force_flash){v=this.userMedia;this.userMedia=null}if(typeof this.params.fps!=="number")this.params.fps=30;var i=this.params.width/this.params.dest_width;var s=this.params.height/this.params.dest_height;if(this.userMedia){var r=document.createElement("video");r.setAttribute("autoplay","autoplay");r.setAttribute("playsinline","playsinline");r.style.width=""+this.params.dest_width+"px";r.style.height=""+this.params.dest_height+"px";if(i!=1||s!=1){t.style.overflow="hidden";r.style.webkitTransformOrigin="0px 0px";r.style.mozTransformOrigin="0px 0px";r.style.msTransformOrigin="0px 0px";r.style.oTransformOrigin="0px 0px";r.style.transformOrigin="0px 0px";r.style.webkitTransform="scaleX("+i+") scaleY("+s+")";r.style.mozTransform="scaleX("+i+") scaleY("+s+")";r.style.msTransform="scaleX("+i+") scaleY("+s+")";r.style.oTransform="scaleX("+i+") scaleY("+s+")";r.style.transform="scaleX("+i+") scaleY("+s+")"}t.appendChild(r);this.video=r;this.inited=true;this.dispatch("init");if(!e){var n=this;this.mediaDevices.getUserMedia({audio:false,video:this.params.constraints||{mandatory:{minWidth:this.params.dest_width,minHeight:this.params.dest_height}}}).then(function(t){r.onloadedmetadata=function(e){n.stream=t;n.loaded=true;n.live=true;n.dispatch("load");n.dispatch("live");n.flip()};if("srcObject"in r){r.srcObject=t}else{r.src=u.URL.createObjectURL(t)}}).catch(function(e){if(n.params.enable_flash&&n.detectFlash()){setTimeout(function(){n.params.force_flash=1;n.attach(t)},1)}else{n.dispatch("error",e)}})}}else if(this.iOS){var l=document.createElement("div");l.id=this.container.id+"-ios_div";l.className="webcamjs-ios-placeholder";l.style.width=""+this.params.width+"px";l.style.height=""+this.params.height+"px";l.style.textAlign="center";l.style.display="table-cell";l.style.verticalAlign="middle";l.style.backgroundRepeat="no-repeat";l.style.backgroundSize="contain";l.style.backgroundPosition="center";var o=document.createElement("span");o.className="webcamjs-ios-text";o.innerHTML=this.params.iosPlaceholderText;l.appendChild(o);var c=document.createElement("img");c.id=this.container.id+"-ios_img";c.style.width=""+this.params.dest_width+"px";c.style.height=""+this.params.dest_height+"px";c.style.display="none";l.appendChild(c);var h=document.createElement("input");h.id=this.container.id+"-ios_input";h.setAttribute("type","file");h.setAttribute("accept","image/*");h.setAttribute("capture","camera");var n=this;var d=this.params;h.addEventListener("change",function(e){if(e.target.files.length>0&&e.target.files[0].type.indexOf("image/")==0){var a=URL.createObjectURL(e.target.files[0]);var h=new Image;h.addEventListener("load",function(e){var t=document.createElement("canvas");t.width=d.dest_width;t.height=d.dest_height;var a=t.getContext("2d");ratio=Math.min(h.width/d.dest_width,h.height/d.dest_height);var i=d.dest_width*ratio;var s=d.dest_height*ratio;var r=(h.width-i)/2;var n=(h.height-s)/2;a.drawImage(h,r,n,i,s,0,0,d.dest_width,d.dest_height);var o=t.toDataURL();c.src=o;l.style.backgroundImage="url('"+o+"')"},false);var t=new FileReader;t.addEventListener("load",function(e){var t=n.exifOrientation(e.target.result);if(t>1){n.fixOrientation(a,t,h)}else{h.src=a}},false);var i=new XMLHttpRequest;i.open("GET",a,true);i.responseType="blob";i.onload=function(e){if(this.status==200||this.status===0){t.readAsArrayBuffer(this.response)}};i.send()}},false);h.style.display="none";t.appendChild(h);l.addEventListener("click",function(e){if(d.user_callback){n.snap(d.user_callback,d.user_canvas)}else{h.style.display="block";h.focus();h.click();h.style.display="none"}},false);t.appendChild(l);this.loaded=true;this.live=true}else if(this.params.enable_flash&&this.detectFlash()){u.Webcam=w;var l=document.createElement("div");l.innerHTML=this.getSWFHTML();t.appendChild(l);var f=this;if(!e){this.on("init",function(){f.getMovie()._initCamera()})}}else{this.dispatch("error",new g(this.params.noInterfaceFoundText))}if(this.params.crop_width&&this.params.crop_height){var m=Math.floor(this.params.crop_width*i);var p=Math.floor(this.params.crop_height*s);t.style.width=""+m+"px";t.style.height=""+p+"px";t.style.overflow="hidden";t.scrollLeft=Math.floor(this.params.width/2-m/2);t.scrollTop=Math.floor(this.params.height/2-p/2)}else{t.style.width=""+this.params.width+"px";t.style.height=""+this.params.height+"px"}},getCameras:function(i){if(this.userMedia){this.mediaDevices.enumerateDevices().then(function(e){var t=[];for(var a=0;a-1)this.hooks[e].splice(a,1)}else{this.hooks[e]=[]}}},dispatch:function(){var e=arguments[0].replace(/^on/i,"").toLowerCase();var t=Array.prototype.slice.call(arguments,1);if(this.hooks[e]&&this.hooks[e].length){for(var a=0,i=this.hooks[e].length;aERROR: the Webcam.js Flash fallback does not work from local disk. Please run it from a web server.'}if(!this.detectFlash()){this.dispatch("error",new l("Adobe Flash Player not found. Please install from get.adobe.com/flashplayer and try again."));return'

'+this.params.flashNotDetectedText+"

"}if(!t){var a="";var i=document.getElementsByTagName("script");for(var s=0,r=i.length;s';return e},getMovie:function(){if(!this.inited)return this.dispatch("error",new l("Flash Movie is not loaded yet"));var e=document.getElementById("webcam_movie_obj");if(!e||!e._snap)e=document.getElementById("webcam_movie_embed");if(!e)this.dispatch("error",new l("Cannot locate Flash movie in DOM"));return e},freeze:function(){var e=this;var t=this.params;if(this.preview_active)this.unfreeze();var a=this.params.width/this.params.dest_width;var i=this.params.height/this.params.dest_height;this.unflip();var s=t.crop_width||t.dest_width;var r=t.crop_height||t.dest_height;var n=document.createElement("canvas");n.width=s;n.height=r;var o=n.getContext("2d");this.preview_canvas=n;this.preview_context=o;if(a!=1||i!=1){n.style.webkitTransformOrigin="0px 0px";n.style.mozTransformOrigin="0px 0px";n.style.msTransformOrigin="0px 0px";n.style.oTransformOrigin="0px 0px";n.style.transformOrigin="0px 0px";n.style.webkitTransform="scaleX("+a+") scaleY("+i+")";n.style.mozTransform="scaleX("+a+") scaleY("+i+")";n.style.msTransform="scaleX("+a+") scaleY("+i+")";n.style.oTransform="scaleX("+a+") scaleY("+i+")";n.style.transform="scaleX("+a+") scaleY("+i+")"}this.snap(function(){n.style.position="relative";n.style.left=""+e.container.scrollLeft+"px";n.style.top=""+e.container.scrollTop+"px";e.container.insertBefore(n,e.peg);e.container.style.overflow="hidden";e.preview_active=true},n)},unfreeze:function(){if(this.preview_active){this.container.removeChild(this.preview_canvas);delete this.preview_context;delete this.preview_canvas;this.preview_active=false;this.flip()}},flip:function(){if(this.params.flip_horiz){var e=this.container.style;e.webkitTransform="scaleX(-1)";e.mozTransform="scaleX(-1)";e.msTransform="scaleX(-1)";e.oTransform="scaleX(-1)";e.transform="scaleX(-1)";e.filter="FlipH";e.msFilter="FlipH"}},unflip:function(){if(this.params.flip_horiz){var e=this.container.style;e.webkitTransform="scaleX(1)";e.mozTransform="scaleX(1)";e.msTransform="scaleX(1)";e.oTransform="scaleX(1)";e.transform="scaleX(1)";e.filter="";e.msFilter=""}},savePreview:function(e,t){var a=this.params;var i=this.preview_canvas;var s=this.preview_context;if(t){var r=t.getContext("2d");r.drawImage(i,0,0)}e(t?null:i.toDataURL("image/"+a.image_format,a.jpeg_quality/100),i,s);if(this.params.unfreeze_snap)this.unfreeze()},snap:function(i,s){if(!i)i=this.params.user_callback;if(!s)s=this.params.user_canvas;var e=this;var r=this.params;if(!this.loaded)return this.dispatch("error",new g("Webcam is not loaded yet"));if(!i)return this.dispatch("error",new g("Please provide a callback function or canvas to snap()"));if(this.preview_active){this.savePreview(i,s);return null}var n=document.createElement("canvas");n.width=this.params.dest_width;n.height=this.params.dest_height;var o=n.getContext("2d");if(this.params.flip_horiz){o.translate(r.dest_width,0);o.scale(-1,1)}var t=function(){if(this.src&&this.width&&this.height){o.drawImage(this,0,0,r.dest_width,r.dest_height)}if(r.crop_width&&r.crop_height){var e=document.createElement("canvas");e.width=r.crop_width;e.height=r.crop_height;var t=e.getContext("2d");t.drawImage(n,Math.floor(r.dest_width/2-r.crop_width/2),Math.floor(r.dest_height/2-r.crop_height/2),r.crop_width,r.crop_height,0,0,r.crop_width,r.crop_height);o=t;n=e}if(s){var a=s.getContext("2d");a.drawImage(n,0,0)}i(s?null:n.toDataURL("image/"+r.image_format,r.jpeg_quality/100),n,o)};if(this.userMedia){o.drawImage(this.video,0,0,this.params.dest_width,this.params.dest_height);t()}else if(this.iOS){var a=document.getElementById(this.container.id+"-ios_div");var h=document.getElementById(this.container.id+"-ios_img");var l=document.getElementById(this.container.id+"-ios_input");iFunc=function(e){t.call(h);h.removeEventListener("load",iFunc);a.style.backgroundImage="none";h.removeAttribute("src");l.value=null};if(!l.value){h.addEventListener("load",iFunc);l.style.display="block";l.focus();l.click();l.style.display="none"}else{iFunc(null)}}else{var c=this.getMovie()._snap();var h=new Image;h.onload=t;h.src="data:image/"+this.params.image_format+";base64,"+c}return null},configure:function(e){if(!e)e="camera";this.getMovie()._configure(e)},flashNotify:function(e,t){switch(e){case"flashInitComplete":this.inited=true;this.dispatch("init");break;case"flashLoadComplete":this.loaded=true;this.dispatch("load");break;case"cameraLive":this.live=true;this.dispatch("live");break;case"error":this.dispatch("error",new l(t));break;default:break}},b64ToUint6:function(e){return e>64&&e<91?e-65:e>96&&e<123?e-71:e>47&&e<58?e+4:e===43?62:e===47?63:0},base64DecToArr:function(e,t){var a=e.replace(/[^A-Za-z0-9\+\/]/g,""),i=a.length,s=t?Math.ceil((i*3+1>>2)/t)*t:i*3+1>>2,r=new Uint8Array(s);for(var n,o,h=0,l=0,c=0;c>>(16>>>n&24)&255}h=0}}return r},upload:function(e,t,a){var i=this.params.upload_name||"webcam";var s="";if(e.match(/^data\:image\/(\w+)/))s=RegExp.$1;else throw"Cannot locate image format in Data URI";var r=e.replace(/^data\:image\/\w+\;base64\,/,"");var n=new XMLHttpRequest;n.open("POST",t,true);if(n.upload&&n.upload.addEventListener){n.upload.addEventListener("progress",function(e){if(e.lengthComputable){var t=e.loaded/e.total;w.dispatch("uploadProgress",t,e)}},false)}var o=this;n.onload=function(){if(a)a.apply(o,[n.status,n.responseText,n.statusText]);w.dispatch("uploadComplete",n.status,n.responseText,n.statusText)};var h=new Blob([this.base64DecToArr(r)],{type:"image/"+s});var l=new FormData;l.append(i,h,i+"."+s.replace(/e/,""));n.send(l)}};w.init();if(typeof define==="function"&&define.amd){define(function(){return w})}else if(typeof module==="object"&&module.exports){module.exports=w}else{u.Webcam=w}})(window); \ No newline at end of file diff --git a/webcam.swf b/webcam.swf index e1d88cd..e65f4cb 100755 Binary files a/webcam.swf and b/webcam.swf differ