├── Makefile ├── README.md ├── bower.json ├── jquery.webcam.js ├── jquery.webcam.min.js ├── jscam.swf ├── jscam_canvas_only.swf ├── src ├── BitString.as ├── JPGEncoder.as ├── jscam.as └── jscam.xml └── webcam.jquery.json /Makefile: -------------------------------------------------------------------------------- 1 | 2 | SWFMILL := swfmill 3 | MTASC := mtasc 4 | 5 | MTASCSTDLIB := /usr/share/mtasc/std 6 | 7 | main: 8 | $(SWFMILL) simple src/jscam.xml jscam.swf 9 | $(MTASC) -v -swf jscam.swf -main jscam.as -version 8 -cp src -cp $(MTASCSTDLIB) 10 | 11 | clean: 12 | rm -f jscam.swf 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jQuery Webcam Plugin 2 | ==================== 3 | [![CDNJS version](https://img.shields.io/cdnjs/v/jQuery-webcam.svg)](https://cdnjs.com/libraries/jQuery-webcam) 4 | 5 | 6 | Description 7 | ----------- 8 | A small wrapper library to be able to communicate with a Flash webcam via JavaScript. 9 | 10 | 11 | Example 12 | ------ 13 | 14 | Please note: The camera doesn't work if you have any dom-errors on your page! 15 | 16 | The Flash object will be embedded into the following Div: 17 | 18 | ```html 19 |
20 | ``` 21 | 22 | ```javascript 23 | 24 | jQuery("#webcam").webcam({ 25 | 26 | width: 320, 27 | height: 240, 28 | mode: "callback", 29 | swffile: "/jscam_canvas_only.swf", // canvas only doesn't implement a jpeg encoder, so the file is much smaller 30 | 31 | onTick: function(remain) { 32 | 33 | if (0 == remain) { 34 | jQuery("#status").text("Cheese!"); 35 | } else { 36 | jQuery("#status").text(remain + " seconds remaining..."); 37 | } 38 | }, 39 | 40 | onSave: function(data) { 41 | 42 | var col = data.split(";"); 43 | // Work with the picture. Picture-data is encoded as an array of arrays... Not really nice, though =/ 44 | }, 45 | 46 | onCapture: function () { 47 | webcam.save(); 48 | 49 | // Show a flash for example 50 | }, 51 | 52 | debug: function (type, string) { 53 | // Write debug information to console.log() or a div, ... 54 | }, 55 | 56 | onLoad: function () { 57 | // Page load 58 | var cams = webcam.getCameraList(); 59 | for(var i in cams) { 60 | jQuery("#cams").append("
  • " + cams[i] + "
  • "); 61 | } 62 | } 63 | }); 64 | 65 | 66 | ``` 67 | 68 | If you want to draw the picture onto a canvas, have a look at the source of the page on my blog, linked below. 69 | 70 | 71 | Further examples and documentation 72 | ========================== 73 | For further details and code examples take a look at the demonstration and documentation page on: 74 | 75 | [https://raw.org/article/camara-support-in-html5-and-javascript/](https://raw.org/article/camara-support-in-html5-and-javascript/) 76 | 77 | License 78 | ====== 79 | Copyright (c) 2013, [Robert Eisele](https://raw.org/) 80 | Dual licensed under the MIT or GPL Version 2 licenses. 81 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webcam", 3 | "main": "jquery.webcam.js", 4 | "version": "1.0.0", 5 | "homepage": "http://www.xarg.org/project/jquery-webcam-plugin/", 6 | "description": "A webcam wrapper plugin for jQuery.", 7 | "keywords": [ 8 | "webcam", 9 | "camera", 10 | "flash" 11 | ], 12 | "authors": [ 13 | "Robert Eisele " 14 | ], 15 | "dependencies": { 16 | "jquery": ">=1.5" 17 | }, 18 | "license": [ 19 | "MIT", 20 | "GPL" 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "git://github.com/infusion/jQuery-webcam.git" 25 | } 26 | } -------------------------------------------------------------------------------- /jquery.webcam.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license jQuery webcam plugin v1.0.0 09/12/2010 3 | * http://www.xarg.org/project/jquery-webcam-plugin/ 4 | * 5 | * Copyright (c) 2010, Robert Eisele (robert@xarg.org) 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | **/ 8 | 9 | (function ($) { 10 | 11 | var webcam = { 12 | 13 | "extern": null, // external select token to support jQuery dialogs 14 | "append": true, // append object instead of overwriting 15 | 16 | "width": 320, 17 | "height": 240, 18 | 19 | "mode": "callback", // callback | save | stream 20 | 21 | "swffile": "jscam.swf", 22 | "quality": 85, 23 | 24 | "debug": function () {}, 25 | "onCapture": function () {}, 26 | "onTick": function () {}, 27 | "onSave": function () {}, 28 | "onLoad": function () {} 29 | }; 30 | 31 | window["webcam"] = webcam; 32 | 33 | $["fn"]["webcam"] = function(options) { 34 | 35 | if (typeof options === "object") { 36 | for (var ndx in webcam) { 37 | if (options[ndx] !== undefined) { 38 | webcam[ndx] = options[ndx]; 39 | } 40 | } 41 | } 42 | 43 | var source = ''; 44 | 45 | if (null !== webcam["extern"]) { 46 | $(webcam["extern"])[webcam["append"] ? "append" : "html"](source); 47 | } else { 48 | this[webcam["append"] ? "append" : "html"](source); 49 | } 50 | 51 | var run = 3; 52 | (_register = function() { 53 | var cam = document.getElementById('XwebcamXobjectX'); 54 | 55 | if (cam && cam["capture"] !== undefined) { 56 | 57 | /* Simple callback methods are not allowed :-/ */ 58 | webcam["capture"] = function(x) { 59 | try { 60 | return cam["capture"](x); 61 | } catch(e) {} 62 | } 63 | webcam["save"] = function(x) { 64 | try { 65 | return cam["save"](x); 66 | } catch(e) {} 67 | } 68 | webcam["setCamera"] = function(x) { 69 | try { 70 | return cam["setCamera"](x); 71 | } catch(e) {} 72 | } 73 | webcam["getCameraList"] = function() { 74 | try { 75 | return cam["getCameraList"](); 76 | } catch(e) {} 77 | } 78 | webcam["pauseCamera"] = function() { 79 | try { 80 | return cam["pauseCamera"](); 81 | } catch(e) {} 82 | } 83 | webcam["resumeCamera"] = function() { 84 | try { 85 | return cam["resumeCamera"](); 86 | } catch(e) {} 87 | } 88 | webcam["onLoad"](); 89 | } else if (0 == run) { 90 | webcam["debug"]("error", "Flash movie not yet registered!"); 91 | } else { 92 | /* Flash interface not ready yet */ 93 | run--; 94 | window.setTimeout(_register, 1000 * (4 - run)); 95 | } 96 | })(); 97 | } 98 | 99 | })(jQuery); 100 | -------------------------------------------------------------------------------- /jquery.webcam.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | jQuery webcam plugin v1.0.0 09/12/2010 3 | http://www.xarg.org/project/jquery-webcam-plugin/ 4 | 5 | Copyright (c) 2010, Robert Eisele (robert@xarg.org) 6 | Dual licensed under the MIT or GPL Version 2 licenses. 7 | */ 8 | (function(f){var a={extern:null,append:!0,width:320,height:240,mode:"callback",swffile:"jscam.swf",quality:85,debug:function(){},onCapture:function(){},onTick:function(){},onSave:function(){},onLoad:function(){}};window.webcam=a;f.fn.webcam=function(b){if("object"===typeof b)for(var d in a)void 0!==b[d]&&(a[d]=b[d]);b='';if(null!==a.extern)f(a.extern)[a.append?"append":"html"](b);else this[a.append?"append":"html"](b);var e=3;(_register=function(){var c=document.getElementById("XwebcamXobjectX");c&&void 0!==c.capture?(a.capture=function(a){try{return c.capture(a)}catch(b){}},a.save=function(a){try{return c.save(a)}catch(b){}},a.setCamera=function(a){try{return c.setCamera(a)}catch(b){}},a.getCameraList=function(){try{return c.getCameraList()}catch(a){}}, 10 | a.pauseCamera=function(){try{return c.pauseCamera()}catch(a){}},a.resumeCamera=function(){try{return c.resumeCamera()}catch(a){}},a.onLoad()):0==e?a.debug("error","Flash movie not yet registered!"):(e--,window.setTimeout(_register,1E3*(4-e)))})()}})(jQuery); 11 | -------------------------------------------------------------------------------- /jscam.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infusion/jQuery-webcam/96fff101c8bd0346ce7ea42c67fde2ba9e33278a/jscam.swf -------------------------------------------------------------------------------- /jscam_canvas_only.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infusion/jQuery-webcam/96fff101c8bd0346ce7ea42c67fde2ba9e33278a/jscam_canvas_only.swf -------------------------------------------------------------------------------- /src/BitString.as: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2008, Adobe Systems Incorporated 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | * Neither the name of Adobe Systems Incorporated nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 21 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | /** 34 | * AS2 Port 35 | * Copyright (c) 2010, Robert Eisele (robert@xarg.org) 36 | */ 37 | 38 | class BitString 39 | { 40 | public var len:Number = 0; 41 | public var val:Number = 0; 42 | } 43 | -------------------------------------------------------------------------------- /src/JPGEncoder.as: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2008, Adobe Systems Incorporated 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | * Neither the name of Adobe Systems Incorporated nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 21 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | /** 34 | * AS2 Port 35 | * Copyright (c) 2010, Robert Eisele (robert@xarg.org) 36 | */ 37 | 38 | import flash.display.BitmapData; 39 | import BitString; 40 | import JPEGEncoder; 41 | 42 | /** 43 | * Class that converts BitmapData into a valid JPEG 44 | */ 45 | class JPGEncoder 46 | { 47 | 48 | // Static table initialization 49 | 50 | private var ZigZag:Array = [ 51 | 0, 1, 5, 6,14,15,27,28, 52 | 2, 4, 7,13,16,26,29,42, 53 | 3, 8,12,17,25,30,41,43, 54 | 9,11,18,24,31,40,44,53, 55 | 10,19,23,32,39,45,52,54, 56 | 20,22,33,38,46,51,55,60, 57 | 21,34,37,47,50,56,59,61, 58 | 35,36,48,49,57,58,62,63 59 | ]; 60 | 61 | private var YTable:Array = new Array(64); 62 | private var UVTable:Array = new Array(64); 63 | private var fdtbl_Y:Array = new Array(64); 64 | private var fdtbl_UV:Array = new Array(64); 65 | 66 | private function initQuantTables(sf:Number):Void 67 | { 68 | var i:Number; 69 | var t:Number; 70 | var YQT:Array = [ 71 | 16, 11, 10, 16, 24, 40, 51, 61, 72 | 12, 12, 14, 19, 26, 58, 60, 55, 73 | 14, 13, 16, 24, 40, 57, 69, 56, 74 | 14, 17, 22, 29, 51, 87, 80, 62, 75 | 18, 22, 37, 56, 68,109,103, 77, 76 | 24, 35, 55, 64, 81,104,113, 92, 77 | 49, 64, 78, 87,103,121,120,101, 78 | 72, 92, 95, 98,112,100,103, 99 79 | ]; 80 | for (i = 0; i < 64; i++) { 81 | t = Math.floor((YQT[i]*sf+50)/100); 82 | if (t < 1) { 83 | t = 1; 84 | } else if (t > 255) { 85 | t = 255; 86 | } 87 | YTable[ZigZag[i]] = t; 88 | } 89 | var UVQT:Array = [ 90 | 17, 18, 24, 47, 99, 99, 99, 99, 91 | 18, 21, 26, 66, 99, 99, 99, 99, 92 | 24, 26, 56, 99, 99, 99, 99, 99, 93 | 47, 66, 99, 99, 99, 99, 99, 99, 94 | 99, 99, 99, 99, 99, 99, 99, 99, 95 | 99, 99, 99, 99, 99, 99, 99, 99, 96 | 99, 99, 99, 99, 99, 99, 99, 99, 97 | 99, 99, 99, 99, 99, 99, 99, 99 98 | ]; 99 | for (i = 0; i < 64; i++) { 100 | t = Math.floor((UVQT[i]*sf+50)/100); 101 | if (t < 1) { 102 | t = 1; 103 | } else if (t > 255) { 104 | t = 255; 105 | } 106 | UVTable[ZigZag[i]] = t; 107 | } 108 | var aasf:Array = [ 109 | 1.0, 1.387039845, 1.306562965, 1.175875602, 110 | 1.0, 0.785694958, 0.541196100, 0.275899379 111 | ]; 112 | i = 0; 113 | for (var row:Number = 0; row < 8; row++) 114 | { 115 | for (var col:Number = 0; col < 8; col++) 116 | { 117 | fdtbl_Y[i] = (1.0 / (YTable [ZigZag[i]] * aasf[row] * aasf[col] * 8.0)); 118 | fdtbl_UV[i] = (1.0 / (UVTable[ZigZag[i]] * aasf[row] * aasf[col] * 8.0)); 119 | i++; 120 | } 121 | } 122 | } 123 | 124 | private var YDC_HT:Array; 125 | private var UVDC_HT:Array; 126 | private var YAC_HT:Array; 127 | private var UVAC_HT:Array; 128 | 129 | private function computeHuffmanTbl(nrcodes:Array, std_table:Array):Array 130 | { 131 | var codevalue:Number = 0; 132 | var pos_in_table:Number = 0; 133 | var HT:Array = new Array(); 134 | for (var k:Number=1; k<=16; k++) { 135 | for (var j:Number=1; j<=nrcodes[k]; j++) { 136 | HT[std_table[pos_in_table]] = new BitString(); 137 | HT[std_table[pos_in_table]].val = codevalue; 138 | HT[std_table[pos_in_table]].len = k; 139 | pos_in_table++; 140 | codevalue++; 141 | } 142 | codevalue*=2; 143 | } 144 | return HT; 145 | } 146 | 147 | private var std_dc_luminance_nrcodes:Array = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0]; 148 | private var std_dc_luminance_values:Array = [0,1,2,3,4,5,6,7,8,9,10,11]; 149 | private var std_ac_luminance_nrcodes:Array = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d]; 150 | private var std_ac_luminance_values:Array = [ 151 | 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12, 152 | 0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07, 153 | 0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, 154 | 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0, 155 | 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16, 156 | 0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, 157 | 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39, 158 | 0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49, 159 | 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, 160 | 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69, 161 | 0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79, 162 | 0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, 163 | 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98, 164 | 0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7, 165 | 0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, 166 | 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5, 167 | 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4, 168 | 0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, 169 | 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea, 170 | 0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 171 | 0xf9,0xfa 172 | ]; 173 | 174 | private var std_dc_chrominance_nrcodes:Array = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0]; 175 | private var std_dc_chrominance_values:Array = [0,1,2,3,4,5,6,7,8,9,10,11]; 176 | private var std_ac_chrominance_nrcodes:Array = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77]; 177 | private var std_ac_chrominance_values:Array = [ 178 | 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21, 179 | 0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71, 180 | 0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, 181 | 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0, 182 | 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34, 183 | 0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, 184 | 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38, 185 | 0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48, 186 | 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, 187 | 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68, 188 | 0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78, 189 | 0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, 190 | 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96, 191 | 0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5, 192 | 0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, 193 | 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3, 194 | 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2, 195 | 0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, 196 | 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9, 197 | 0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8, 198 | 0xf9,0xfa 199 | ]; 200 | 201 | private function initHuffmanTbl():Void 202 | { 203 | YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values); 204 | UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values); 205 | YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values); 206 | UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values); 207 | } 208 | 209 | private var bitcode:Array = new Array(65535); 210 | private var category:Array = new Array(65535); 211 | 212 | private function initCategoryNumber():Void 213 | { 214 | var nrlower:Number = 1; 215 | var nrupper:Number = 2; 216 | var nr:Number; 217 | for (var cat:Number=1; cat<=15; cat++) { 218 | //Positive numbers 219 | for (nr=nrlower; nr= 0 ) { 248 | if (value & (1 << posval) ) { 249 | bytenew |= (1 << bytepos); 250 | } 251 | posval--; 252 | bytepos--; 253 | if (bytepos < 0) { 254 | if (bytenew == 0xFF) { 255 | writeByte(0xFF); 256 | writeByte(0); 257 | } 258 | else { 259 | writeByte(bytenew); 260 | } 261 | bytepos=7; 262 | bytenew=0; 263 | } 264 | } 265 | } 266 | 267 | private function writeByte(value:Number):Void 268 | { 269 | var c:Array = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' ]; 270 | 271 | byteout+= c[(value >> 4) & 0xf]; 272 | byteout+= c[ value & 0xf]; 273 | 274 | //byteout+= "\t" + chr(value) + "\n"; 275 | 276 | 277 | 278 | } 279 | 280 | private function writeWord(value:Number):Void 281 | { 282 | writeByte((value>>8)&0xFF); 283 | writeByte((value )&0xFF); 284 | } 285 | 286 | // DCT & quantization core 287 | 288 | private function fDCTQuant(data:Array, fdtbl:Array):Array 289 | { 290 | var tmp0:Number, tmp1:Number, tmp2:Number, tmp3:Number, tmp4:Number, tmp5:Number, tmp6:Number, tmp7:Number; 291 | var tmp10:Number, tmp11:Number, tmp12:Number, tmp13:Number; 292 | var z1:Number, z2:Number, z3:Number, z4:Number, z5:Number, z11:Number, z13:Number; 293 | var i:Number; 294 | /* Pass 1: process rows. */ 295 | var dataOff:Number=0; 296 | for (i=0; i<8; i++) { 297 | tmp0 = data[dataOff+0] + data[dataOff+7]; 298 | tmp7 = data[dataOff+0] - data[dataOff+7]; 299 | tmp1 = data[dataOff+1] + data[dataOff+6]; 300 | tmp6 = data[dataOff+1] - data[dataOff+6]; 301 | tmp2 = data[dataOff+2] + data[dataOff+5]; 302 | tmp5 = data[dataOff+2] - data[dataOff+5]; 303 | tmp3 = data[dataOff+3] + data[dataOff+4]; 304 | tmp4 = data[dataOff+3] - data[dataOff+4]; 305 | 306 | /* Even part */ 307 | tmp10 = tmp0 + tmp3; /* phase 2 */ 308 | tmp13 = tmp0 - tmp3; 309 | tmp11 = tmp1 + tmp2; 310 | tmp12 = tmp1 - tmp2; 311 | 312 | data[dataOff+0] = tmp10 + tmp11; /* phase 3 */ 313 | data[dataOff+4] = tmp10 - tmp11; 314 | 315 | z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */ 316 | data[dataOff+2] = tmp13 + z1; /* phase 5 */ 317 | data[dataOff+6] = tmp13 - z1; 318 | 319 | /* Odd part */ 320 | tmp10 = tmp4 + tmp5; /* phase 2 */ 321 | tmp11 = tmp5 + tmp6; 322 | tmp12 = tmp6 + tmp7; 323 | 324 | /* The rotator is modified from fig 4-8 to avoid extra negations. */ 325 | z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */ 326 | z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */ 327 | z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */ 328 | z3 = tmp11 * 0.707106781; /* c4 */ 329 | 330 | z11 = tmp7 + z3; /* phase 5 */ 331 | z13 = tmp7 - z3; 332 | 333 | data[dataOff+5] = z13 + z2; /* phase 6 */ 334 | data[dataOff+3] = z13 - z2; 335 | data[dataOff+1] = z11 + z4; 336 | data[dataOff+7] = z11 - z4; 337 | 338 | dataOff += 8; /* advance pointer to next row */ 339 | } 340 | 341 | /* Pass 2: process columns. */ 342 | dataOff = 0; 343 | for (i=0; i<8; i++) { 344 | tmp0 = data[dataOff+ 0] + data[dataOff+56]; 345 | tmp7 = data[dataOff+ 0] - data[dataOff+56]; 346 | tmp1 = data[dataOff+ 8] + data[dataOff+48]; 347 | tmp6 = data[dataOff+ 8] - data[dataOff+48]; 348 | tmp2 = data[dataOff+16] + data[dataOff+40]; 349 | tmp5 = data[dataOff+16] - data[dataOff+40]; 350 | tmp3 = data[dataOff+24] + data[dataOff+32]; 351 | tmp4 = data[dataOff+24] - data[dataOff+32]; 352 | 353 | /* Even part */ 354 | tmp10 = tmp0 + tmp3; /* phase 2 */ 355 | tmp13 = tmp0 - tmp3; 356 | tmp11 = tmp1 + tmp2; 357 | tmp12 = tmp1 - tmp2; 358 | 359 | data[dataOff+ 0] = tmp10 + tmp11; /* phase 3 */ 360 | data[dataOff+32] = tmp10 - tmp11; 361 | 362 | z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */ 363 | data[dataOff+16] = tmp13 + z1; /* phase 5 */ 364 | data[dataOff+48] = tmp13 - z1; 365 | 366 | /* Odd part */ 367 | tmp10 = tmp4 + tmp5; /* phase 2 */ 368 | tmp11 = tmp5 + tmp6; 369 | tmp12 = tmp6 + tmp7; 370 | 371 | /* The rotator is modified from fig 4-8 to avoid extra negations. */ 372 | z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */ 373 | z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */ 374 | z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */ 375 | z3 = tmp11 * 0.707106781; /* c4 */ 376 | 377 | z11 = tmp7 + z3; /* phase 5 */ 378 | z13 = tmp7 - z3; 379 | 380 | data[dataOff+40] = z13 + z2; /* phase 6 */ 381 | data[dataOff+24] = z13 - z2; 382 | data[dataOff+ 8] = z11 + z4; 383 | data[dataOff+56] = z11 - z4; 384 | 385 | dataOff++; /* advance pointer to next column */ 386 | } 387 | 388 | // Quantize/descale the coefficients 389 | for (i=0; i<64; i++) { 390 | // Apply the quantization and scaling factor & Round to nearest integer 391 | data[i] = Math.round((data[i]*fdtbl[i])); 392 | } 393 | return data; 394 | } 395 | 396 | // Chunk writing 397 | 398 | private function writeAPP0():Void 399 | { 400 | writeWord(0xFFE0); // marker 401 | writeWord(16); // length 402 | writeByte(0x4A); // J 403 | writeByte(0x46); // F 404 | writeByte(0x49); // I 405 | writeByte(0x46); // F 406 | writeByte(0); // = "JFIF",'\0' 407 | writeByte(1); // versionhi 408 | writeByte(1); // versionlo 409 | writeByte(0); // xyunits 410 | writeWord(1); // xdensity 411 | writeWord(1); // ydensity 412 | writeByte(0); // thumbnwidth 413 | writeByte(0); // thumbnheight 414 | } 415 | 416 | private function writeSOF0(width:Number, height:Number):Void 417 | { 418 | writeWord(0xFFC0); // marker 419 | writeWord(17); // length, truecolor YUV JPG 420 | writeByte(8); // precision 421 | writeWord(height); 422 | writeWord(width); 423 | writeByte(3); // nrofcomponents 424 | writeByte(1); // IdY 425 | writeByte(0x11); // HVY 426 | writeByte(0); // QTY 427 | writeByte(2); // IdU 428 | writeByte(0x11); // HVU 429 | writeByte(1); // QTU 430 | writeByte(3); // IdV 431 | writeByte(0x11); // HVV 432 | writeByte(1); // QTV 433 | } 434 | 435 | private function writeDQT():Void 436 | { 437 | writeWord(0xFFDB); // marker 438 | writeWord(132); // length 439 | writeByte(0); 440 | var i:Number; 441 | for (i=0; i<64; i++) { 442 | writeByte(YTable[i]); 443 | } 444 | writeByte(1); 445 | for (i=0; i<64; i++) { 446 | writeByte(UVTable[i]); 447 | } 448 | } 449 | 450 | private function writeDHT():Void 451 | { 452 | writeWord(0xFFC4); // marker 453 | writeWord(0x01A2); // length 454 | var i:Number; 455 | 456 | writeByte(0); // HTYDCinfo 457 | for (i=0; i<16; i++) { 458 | writeByte(std_dc_luminance_nrcodes[i+1]); 459 | } 460 | for (i=0; i<=11; i++) { 461 | writeByte(std_dc_luminance_values[i]); 462 | } 463 | 464 | writeByte(0x10); // HTYACinfo 465 | for (i=0; i<16; i++) { 466 | writeByte(std_ac_luminance_nrcodes[i+1]); 467 | } 468 | for (i=0; i<=161; i++) { 469 | writeByte(std_ac_luminance_values[i]); 470 | } 471 | 472 | writeByte(1); // HTUDCinfo 473 | for (i=0; i<16; i++) { 474 | writeByte(std_dc_chrominance_nrcodes[i+1]); 475 | } 476 | for (i=0; i<=11; i++) { 477 | writeByte(std_dc_chrominance_values[i]); 478 | } 479 | 480 | writeByte(0x11); // HTUACinfo 481 | for (i=0; i<16; i++) { 482 | writeByte(std_ac_chrominance_nrcodes[i+1]); 483 | } 484 | for (i=0; i<=161; i++) { 485 | writeByte(std_ac_chrominance_values[i]); 486 | } 487 | } 488 | 489 | private function writeSOS():Void 490 | { 491 | writeWord(0xFFDA); // marker 492 | writeWord(12); // length 493 | writeByte(3); // nrofcomponents 494 | writeByte(1); // IdY 495 | writeByte(0); // HTY 496 | writeByte(2); // IdU 497 | writeByte(0x11); // HTU 498 | writeByte(3); // IdV 499 | writeByte(0x11); // HTV 500 | writeByte(0); // Ss 501 | writeByte(0x3f); // Se 502 | writeByte(0); // Bf 503 | } 504 | 505 | // Core processing 506 | private var DU:Array = new Array(64); 507 | 508 | private function processDU(CDU:Array, fdtbl:Array, DC:Number, HTDC:Array, HTAC:Array):Number 509 | { 510 | var EOB:BitString = HTAC[0x00]; 511 | var M16zeroes:BitString = HTAC[0xF0]; 512 | var i:Number; 513 | 514 | var DU_DCT:Array = fDCTQuant(CDU, fdtbl); 515 | //ZigZag reorder 516 | for (i=0;i<64;i++) { 517 | DU[ZigZag[i]]=DU_DCT[i]; 518 | } 519 | var Diff:Number = DU[0] - DC; DC = DU[0]; 520 | //Encode DC 521 | if (Diff==0) { 522 | writeBits(HTDC[0]); // Diff might be 0 523 | } else { 524 | writeBits(HTDC[category[32767+Diff]]); 525 | writeBits(bitcode[32767+Diff]); 526 | } 527 | //Encode ACs 528 | var end0pos:Number = 63; 529 | for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) { 530 | }; 531 | //end0pos = first element in reverse order !=0 532 | if ( end0pos == 0) { 533 | writeBits(EOB); 534 | return DC; 535 | } 536 | i = 1; 537 | while ( i <= end0pos ) { 538 | var startpos:Number = i; 539 | for (; (DU[i]==0) && (i<=end0pos); i++) { 540 | } 541 | var nrzeroes:Number = i-startpos; 542 | if ( nrzeroes >= 16 ) { 543 | for (var nrmarker:Number=1; nrmarker <= nrzeroes/16; nrmarker++) { 544 | writeBits(M16zeroes); 545 | } 546 | nrzeroes = int(nrzeroes&0xF); 547 | } 548 | writeBits(HTAC[nrzeroes*16+category[32767+DU[i]]]); 549 | writeBits(bitcode[32767+DU[i]]); 550 | i++; 551 | } 552 | if ( end0pos != 63 ) { 553 | writeBits(EOB); 554 | } 555 | return DC; 556 | } 557 | 558 | private var YDU:Array = new Array(64); 559 | private var UDU:Array = new Array(64); 560 | private var VDU:Array = new Array(64); 561 | 562 | private function RGB2YUV(img:BitmapData, xpos:Number, ypos:Number):Void 563 | { 564 | var pos:Number=0; 565 | for (var y:Number=0; y<8; y++) { 566 | for (var x:Number=0; x<8; x++) { 567 | var P:Number = img.getPixel32(xpos+x,ypos+y); 568 | var R:Number = Number((P>>16)&0xFF); 569 | var G:Number = Number((P>> 8)&0xFF); 570 | var B:Number = Number((P )&0xFF); 571 | YDU[pos]=((( 0.29900)*R+( 0.58700)*G+( 0.11400)*B))-128; 572 | UDU[pos]=(((-0.16874)*R+(-0.33126)*G+( 0.50000)*B)); 573 | VDU[pos]=((( 0.50000)*R+(-0.41869)*G+(-0.08131)*B)); 574 | pos++; 575 | } 576 | } 577 | } 578 | 579 | /** 580 | * Constructor for JPEGEncoder class 581 | * 582 | * @param quality The quality level between 1 and 100 that detrmines the 583 | * level of compression used in the generated JPEG 584 | * @langversion ActionScript 3.0 585 | * @playerversion Flash 9.0 586 | * @tiptext 587 | */ 588 | public function JPGEncoder(quality:Number) 589 | { 590 | if (!quality) { 591 | quality = 50; 592 | } 593 | 594 | if (quality <= 0) { 595 | quality = 1; 596 | } 597 | if (quality > 100) { 598 | quality = 100; 599 | } 600 | var sf:Number = 0; 601 | if (quality < 50) { 602 | sf = int(5000 / quality); 603 | } else { 604 | sf = int(200 - quality*2); 605 | } 606 | // Create tables 607 | initHuffmanTbl(); 608 | initCategoryNumber(); 609 | initQuantTables(sf); 610 | } 611 | 612 | /** 613 | * Created a JPEG image from the specified BitmapData 614 | * 615 | * @param image The BitmapData that will be converted into the JPEG format. 616 | * @return a ByteArray representing the JPEG encoded image data. 617 | * @langversion ActionScript 3.0 618 | * @playerversion Flash 9.0 619 | * @tiptext 620 | */ 621 | public function encode(image:BitmapData):String 622 | { 623 | // Initialize bit writer 624 | byteout = ""; 625 | bytenew=0; 626 | bytepos=7; 627 | 628 | // Add JPEG headers 629 | writeWord(0xFFD8); // SOI 630 | writeAPP0(); 631 | writeDQT(); 632 | writeSOF0(image.width,image.height); 633 | writeDHT(); 634 | writeSOS(); 635 | 636 | 637 | // Encode 8x8 macroblocks 638 | var DCY:Number=0; 639 | var DCU:Number=0; 640 | var DCV:Number=0; 641 | bytenew=0; 642 | bytepos=7; 643 | for (var ypos:Number=0; ypos= 0 ) { 654 | var fillbits:BitString = new BitString(); 655 | fillbits.len = bytepos+1; 656 | fillbits.val = (1<<(bytepos+1))-1; 657 | writeBits(fillbits); 658 | } 659 | 660 | writeWord(0xFFD9); //EOI 661 | return byteout; 662 | } 663 | } 664 | -------------------------------------------------------------------------------- /src/jscam.as: -------------------------------------------------------------------------------- 1 | /** 2 | * jQuery webcam 3 | * Copyright (c) 2010, Robert Eisele (robert@xarg.org) 4 | * Dual licensed under the MIT or GPL Version 2 licenses. 5 | * Date: 09/12/2010 6 | * 7 | * @author Robert Eisele 8 | * @version 1.0 9 | * 10 | * @see http://www.xarg.org/project/jquery-webcam-plugin/ 11 | **/ 12 | 13 | import flash.system.Security; 14 | import flash.external.ExternalInterface; 15 | import flash.display.BitmapData; 16 | import JPGEncoder; 17 | 18 | class JSCam { 19 | 20 | private static var camera:Camera = null; 21 | private static var buffer:BitmapData = null; 22 | private static var quality:Number = 85; 23 | private static var interval = null; 24 | private static var stream = null; 25 | private static var mode = "callback"; 26 | 27 | public static function main():Void { 28 | 29 | System.security.allowDomain("*"); 30 | 31 | if (_root.mode) { 32 | mode = _root.mode; 33 | } else { 34 | ExternalInterface.call('webcam.debug', "error", "No camera mode present, falling back..."); 35 | } 36 | 37 | if (_root.quality) { 38 | quality = _root.quality; 39 | } 40 | 41 | // From: http://www.squidder.com/2009/03/09/trick-auto-select-mac-isight-in-flash/ 42 | var id:Number = -1; 43 | for (var i = 0, l = Camera.names.length; i < l; i++) { 44 | if (Camera.names[i] == "USB Video Class Video") { 45 | id = i; 46 | break; 47 | } 48 | } 49 | if (id > -1) { 50 | camera = Camera.get(id); 51 | } else { 52 | camera = Camera.get(); 53 | } 54 | 55 | if (null != camera) { 56 | 57 | // http://www.adobe.com/support/flash/action_scripts/actionscript_dictionary/actionscript_dictionary133.html 58 | camera.onStatus = function(info:Object) { 59 | 60 | switch (info.code) { 61 | case 'Camera.Muted': 62 | ExternalInterface.call('webcam.debug', "notify", "Camera stopped"); 63 | break; 64 | case 'Camera.Unmuted' : 65 | ExternalInterface.call('webcam.debug', "notify", "Camera started"); 66 | break; 67 | } 68 | } 69 | 70 | camera.setQuality(0, 100); 71 | camera.setMode(Stage.width, Stage.height, 24, false); 72 | 73 | ExternalInterface.addCallback("capture", null, capture); 74 | 75 | ExternalInterface.addCallback("save", null, save); 76 | 77 | ExternalInterface.addCallback("pauseCamera", null, pauseCamera); 78 | ExternalInterface.addCallback("resumeCamera", null, resumeCamera); 79 | 80 | ExternalInterface.addCallback("setCamera", null, setCamera); 81 | ExternalInterface.addCallback("getCameraList", null, getCameraList); 82 | 83 | _root.attachMovie("clip", "video", 1); 84 | _root.video.attachVideo(camera); 85 | _root.video._x = 0; 86 | _root.video._y = 0; 87 | 88 | } else { 89 | ExternalInterface.call('webcam.debug', "error", "No camera was detected."); 90 | } 91 | } 92 | public static function pauseCamera():Void { 93 | _root.video.attachVideo(null); 94 | } 95 | public static function resumeCamera():Void { 96 | _root.video.attachVideo(camera); 97 | } 98 | 99 | 100 | public static function capture(time:Number):Boolean { 101 | 102 | if (null != camera) { 103 | 104 | if (null != buffer) { 105 | return false; 106 | } 107 | 108 | buffer = new BitmapData(Stage.width, Stage.height); 109 | ExternalInterface.call('webcam.debug', "notify", "Capturing started."); 110 | 111 | if ("stream" == mode) { 112 | _stream(); 113 | return true; 114 | } 115 | 116 | if (!time) { 117 | time = -1; 118 | } else if (time > 10) { 119 | time = 10; 120 | } 121 | 122 | _capture(time + 1); 123 | 124 | return true; 125 | } 126 | return false; 127 | } 128 | 129 | private static function _capture(time:Number):Void { 130 | 131 | if (null != interval) { 132 | clearInterval(interval); 133 | } 134 | 135 | if (0 == time) { 136 | buffer.draw(_root.video); 137 | ExternalInterface.call('webcam.onCapture'); 138 | ExternalInterface.call('webcam.debug', "notify", "Capturing finished."); 139 | } else { 140 | ExternalInterface.call('webcam.onTick', time - 1); 141 | interval = setInterval(_capture, 1000, time - 1); 142 | } 143 | } 144 | 145 | public static function getCameraList():Array { 146 | 147 | var list = new Array(); 148 | 149 | for (var i = 0, l = Camera.names.length; i < l; i++) { 150 | list[i] = Camera.names[i]; 151 | } 152 | return list; 153 | } 154 | 155 | public static function setCamera(id:Number):Boolean { 156 | 157 | if (0 <= id && id < Camera.names.length) { 158 | camera = Camera.get(id); 159 | camera.setQuality(0, 100); 160 | camera.setMode(Stage.width, Stage.height, 24, false); 161 | 162 | _root.removeMovieClip(); 163 | _root.attachMovie("clip", "video", 1); 164 | _root.video.attachVideo(camera); 165 | _root.video._x = 0; 166 | _root.video._y = 0; 167 | 168 | return true; 169 | 170 | } 171 | return false; 172 | } 173 | 174 | public static function save(file:String):Boolean { 175 | 176 | if ("stream" == mode) { 177 | 178 | return true; 179 | 180 | } else if (null != buffer) { 181 | 182 | if ("callback" == mode) { 183 | 184 | for (var i = 0; i < 240; ++i) { 185 | 186 | var row = ""; 187 | for (var j=0; j < 320; ++j) { 188 | row+= buffer.getPixel(j, i); 189 | row+= ";"; 190 | } 191 | ExternalInterface.call("webcam.onSave", row); 192 | } 193 | 194 | } else if ("save" == mode) { 195 | 196 | if (file) { 197 | 198 | var e = new JPGEncoder(quality); 199 | 200 | var sal = {}; 201 | sal.sendAndLoad = XML.prototype.sendAndLoad; 202 | sal.contentType = "image/jpeg"; 203 | sal.toString = function() { 204 | return e.encode(JSCam.buffer); 205 | } 206 | 207 | var doc = new XML(); 208 | doc.onLoad = function(success) { 209 | ExternalInterface.call("webcam.onSave", "done", this.toString()); 210 | } 211 | 212 | sal.sendAndLoad(file, doc); 213 | /* 214 | ExternalInterface.call('webcam.debug', "error", "No save mode compiled in."); 215 | return false; 216 | */ 217 | } else { 218 | ExternalInterface.call('webcam.debug', "error", "No file name specified."); 219 | return false; 220 | } 221 | 222 | } else { 223 | ExternalInterface.call('webcam.debug', "error", "Unsupported storage mode."); 224 | } 225 | 226 | buffer = null; 227 | return true; 228 | } 229 | return false; 230 | } 231 | 232 | private static function _stream():Void { 233 | 234 | buffer.draw(_root.video); 235 | 236 | if (null != stream) { 237 | clearInterval(stream); 238 | } 239 | 240 | 241 | for (var i = 0; i < 240; ++i) { 242 | 243 | var row = ""; 244 | for (var j=0; j < 320; ++j) { 245 | row+= buffer.getPixel(j, i); 246 | row+= ";"; 247 | } 248 | ExternalInterface.call("webcam.onSave", row); 249 | } 250 | 251 | stream = setInterval(_stream, 10); 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/jscam.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /webcam.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webcam", 3 | "title": "jQuery Webcam Plugin", 4 | "description": "A webcam wrapper plugin for jQuery.", 5 | "version": "1.0.0", 6 | "keywords": [ 7 | "webcam", 8 | "camera", 9 | "flash" 10 | ], 11 | "author": { 12 | "name": "Robert Eisele", 13 | "url": "http://www.xarg.org/" 14 | }, 15 | "licenses": [{ 16 | "type": "MIT", 17 | "url": "http://www.opensource.org/licenses/MIT" 18 | }, { 19 | "type": "GPLv2", 20 | "url": "http://www.opensource.org/licenses/GPL-2.0" 21 | }], 22 | "dependencies": { 23 | "jquery": ">=1.5" 24 | }, 25 | "homepage": "https://github.com/infusion/jQuery-webcam", 26 | "demo": "http://www.xarg.org/project/jquery-webcam-plugin/" 27 | } --------------------------------------------------------------------------------