├── .eslintignore ├── docs ├── CNAME ├── favicon.ico ├── icons │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── mstile-70x70.png │ ├── apple-touch-icon.png │ ├── mstile-144x144.png │ ├── mstile-150x150.png │ ├── mstile-310x150.png │ ├── mstile-310x310.png │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ └── safari-pinned-tab.svg ├── browserconfig.xml ├── manifest.json ├── index.css ├── index.html ├── index.js └── bundle.js ├── .gitignore ├── .travis.yml ├── .npmignore ├── src ├── favicon.svg └── imagecapture.js ├── webpack.config.js ├── .eslintrc.js ├── CONTRIBUTING.md ├── package.json ├── README.md └── LICENSE /.eslintignore: -------------------------------------------------------------------------------- 1 | docs/bundle.js 2 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | imagecapture.js.org 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | lib 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/imagecapture-polyfill/master/docs/favicon.ico -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '9' 4 | - '8' 5 | - '7' 6 | - '6' 7 | - '5' 8 | - '4' 9 | -------------------------------------------------------------------------------- /docs/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/imagecapture-polyfill/master/docs/icons/favicon-16x16.png -------------------------------------------------------------------------------- /docs/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/imagecapture-polyfill/master/docs/icons/favicon-32x32.png -------------------------------------------------------------------------------- /docs/icons/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/imagecapture-polyfill/master/docs/icons/mstile-70x70.png -------------------------------------------------------------------------------- /docs/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/imagecapture-polyfill/master/docs/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /docs/icons/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/imagecapture-polyfill/master/docs/icons/mstile-144x144.png -------------------------------------------------------------------------------- /docs/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/imagecapture-polyfill/master/docs/icons/mstile-150x150.png -------------------------------------------------------------------------------- /docs/icons/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/imagecapture-polyfill/master/docs/icons/mstile-310x150.png -------------------------------------------------------------------------------- /docs/icons/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/imagecapture-polyfill/master/docs/icons/mstile-310x310.png -------------------------------------------------------------------------------- /docs/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/imagecapture-polyfill/master/docs/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /docs/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoogleChromeLabs/imagecapture-polyfill/master/docs/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower.json 2 | node_modules 3 | .idea 4 | .eslintrc.js 5 | build 6 | demo 7 | tests 8 | .travis.yml 9 | *.html 10 | **/*.bak 11 | src/favicon.svg 12 | yarn.lock 13 | CONTRIBUTING.md 14 | -------------------------------------------------------------------------------- /docs/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | context: path.join(__dirname, 'docs'), 5 | entry: './index.js', 6 | 7 | devServer: { 8 | contentBase: './docs', 9 | inline: true, 10 | }, 11 | 12 | output: { 13 | path: path.join(__dirname, 'docs'), 14 | filename: './bundle.js', 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /docs/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ImageCapture polyfill demo", 3 | "icons": [ 4 | { 5 | "src": "icons/android-chrome-192x192.png", 6 | "sizes": "192x192", 7 | "type": "image/png" 8 | }, 9 | { 10 | "src": "icons/android-chrome-512x512.png", 11 | "sizes": "512x512", 12 | "type": "image/png" 13 | } 14 | ], 15 | "theme_color": "#ffffff", 16 | "display": "standalone" 17 | } 18 | -------------------------------------------------------------------------------- /docs/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #photo { 18 | filter: hue-rotate(180deg); 19 | } 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true 5 | }, 6 | parserOptions: { 7 | sourceType: 'module', 8 | }, 9 | extends: 'google', 10 | rules: { 11 | 'max-len': [ 'warn', 12 | { code: 130 } // 130 on GitHub, 80 on npmjs.org for README.md code blocks 13 | ], 14 | 'arrow-parens': ['error', 'as-needed'], 15 | 'space-before-function-paren': [ 16 | 'error', 17 | { 18 | anonymous: 'always', 19 | named: 'never' 20 | } 21 | ], 22 | 'no-negated-condition': 'warn', 23 | 'spaced-comment': ['error', 'always', { exceptions: ['/'] }] 24 | }, 25 | globals: { 26 | DOMException: false, 27 | MediaStream: false, 28 | Polymer: false, 29 | URL: false, 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MediaStream ImageCapture polyfill demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |

Capturing every 300 milliseconds

19 |

grabFrame()

20 |

21 |

takePhoto() with post-processing ("Avatar" mix)

22 |

23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement] 6 | (https://cla.developers.google.com/about/google-individual) 7 | (CLA), which you can do online. The CLA is necessary mainly because you own the 8 | copyright to your changes, even after your contribution becomes part of our 9 | codebase, so we need your permission to use and distribute your code. We also 10 | need to be sure of various other things—for instance that you'll tell us if you 11 | know that your code infringes on other people's patents. You don't have to sign 12 | the CLA until after you've submitted your code for review and a member has 13 | approved it, but you must do it before we can put your code into our codebase. 14 | Before you start working on a larger contribution, you should get in touch with 15 | us first through the issue tracker with your idea so that we can help out and 16 | possibly guide you. Coordinating up front makes it much easier to avoid 17 | frustration later on. 18 | 19 | ### Code reviews 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. 22 | 23 | ### The small print 24 | Contributions made by corporations are covered by a different agreement than 25 | the one above, the 26 | [Software Grant and Corporate Contributor License Agreement] 27 | (https://cla.developers.google.com/about/google-corporate). 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "image-capture", 3 | "version": "0.4.0", 4 | "description": "MediaStream ImageCapture polyfill: takePhoto(), grabFrame() and more", 5 | "main": "lib/imagecapture.js", 6 | "module": "src/imagecapture.js", 7 | "jsnext:main": "src/imagecapture.js", 8 | "scripts": { 9 | "lint": "eslint src/*.js docs/index.js", 10 | "prepare": "babel -d lib src/ && uglifyjs lib/imagecapture.js --compress --mangle --comments -o lib/imagecapture.min.js", 11 | "dev": "webpack-dev-server --progress", 12 | "lt": "lt --port 8080 --subdomain imagecapture", 13 | "docs": "webpack" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/GoogleChrome/imagecapture-polyfill.git" 18 | }, 19 | "keywords": [ 20 | "camera", 21 | "webcam", 22 | "photo", 23 | "picture", 24 | "image", 25 | "capture", 26 | "ImageCapture", 27 | "MediaStream", 28 | "getUserMedia" 29 | ], 30 | "author": "Dan Dascalescu (dandv)", 31 | "license": "MIT", 32 | "bugs": { 33 | "url": "https://github.com/GoogleChrome/imagecapture-polyfill/issues" 34 | }, 35 | "homepage": "https://github.com/GoogleChrome/imagecapture-polyfill#readme", 36 | "dependencies": {}, 37 | "devDependencies": { 38 | "babel-cli": "^6.26.0", 39 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", 40 | "babel-preset-es2015": "^6.24.1", 41 | "eslint": "^3.19.0", 42 | "eslint-config-google": "^0.7.1", 43 | "localtunnel": "^1.8.3", 44 | "uglify-js": "^3.3.5", 45 | "webpack": "^2.7.0", 46 | "webpack-dev-server": "^2.10.1" 47 | }, 48 | "babel": { 49 | "presets": [ 50 | [ 51 | "es2015", 52 | { 53 | "modules": "umd" 54 | } 55 | ] 56 | ] 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /docs/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 27 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /docs/index.js: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | // Demo for the MediaStream ImageCapture polyfill 18 | 19 | 'use strict'; 20 | 21 | const logElement = document.querySelector('#log'); 22 | 23 | /** 24 | * Log messages to the #log element and the console 25 | * @param {*[]} messages - list of messages 26 | */ 27 | function log(...messages) { 28 | console.log(...messages); 29 | const p = document.createElement('p'); 30 | p.innerText = messages.join(' '); 31 | logElement.appendChild(p); 32 | } 33 | 34 | /** 35 | * Log messages to the #log element and the console as errors 36 | * @param {*} messages - list of messages 37 | */ 38 | function err(...messages) { 39 | console.error(...messages); 40 | const p = document.createElement('p'); 41 | p.innerText = messages.join(' '); 42 | p.style = 'color: red'; 43 | logElement.appendChild(p); 44 | } 45 | 46 | import {ImageCapture} from '../src/imagecapture'; 47 | 48 | let interval; 49 | const canvas = document.getElementById('frame'); 50 | 51 | const photo = document.getElementById('photo'); 52 | photo.addEventListener('load', function () { 53 | // After the image loads, discard the image object to release the memory 54 | window.URL.revokeObjectURL(photo.src); 55 | }); 56 | 57 | let videoDevice; 58 | document.getElementById('stop').addEventListener('click', stopFunction); 59 | 60 | // Use navigator.mediaDevices.getUserMedia instead of navigator.getUserMedia, per 61 | // https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getUserMedia and https://webrtc.org/web-apis/interop/ 62 | // For cross-platform compatibility, we'll use the WebRTC adapter.js library 63 | navigator.mediaDevices.getUserMedia({video: true}).then(gotMedia).catch(failedToGetMedia); 64 | 65 | /** 66 | * We have a video capture device. Exercise various capturing modes. 67 | * @param {MediaStream} mediaStream 68 | */ 69 | function gotMedia(mediaStream) { 70 | // Extract video track. 71 | videoDevice = mediaStream.getVideoTracks()[0]; 72 | log('Using camera', videoDevice.label); 73 | 74 | const captureDevice = new ImageCapture(videoDevice, mediaStream); 75 | interval = setInterval(function () { 76 | captureDevice.grabFrame().then(processFrame).catch(error => { 77 | err((new Date()).toISOString(), 'Error while grabbing frame:', error); 78 | }); 79 | 80 | captureDevice.takePhoto().then(processPhoto).catch(error => { 81 | err((new Date()).toISOString(), 'Error while taking photo:', error); 82 | }); 83 | }, 300); 84 | } 85 | 86 | /** 87 | * Draw the imageBitmap returned by grabFrame() onto a canvas 88 | * @param {ImageBitmap} imageBitmap 89 | */ 90 | function processFrame(imageBitmap) { 91 | canvas.width = imageBitmap.width; 92 | canvas.height = imageBitmap.height; 93 | canvas.getContext('2d').drawImage(imageBitmap, 0, 0); 94 | } 95 | 96 | /** 97 | * Set the source of the 'photo' to the blob returned by takePhoto() 98 | * @param {Blob} blob 99 | */ 100 | function processPhoto(blob) { 101 | photo.src = window.URL.createObjectURL(blob); 102 | } 103 | 104 | /** 105 | * Stop frame grabbing and video capture 106 | */ 107 | function stopFunction() { 108 | if (interval) clearInterval(interval); // stop frame grabbing 109 | if (videoDevice) videoDevice.stop(); // turn off the camera 110 | } 111 | 112 | /** 113 | * Handle errors 114 | * @param {Error} error 115 | */ 116 | function failedToGetMedia(error) { 117 | err('getUserMedia failed:', error); 118 | stopFunction(); 119 | } 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ImageCapture polyfill 2 | 3 | [![Build Status](https://travis-ci.org/GoogleChromeLabs/imagecapture-polyfill.svg?branch=master)](https://travis-ci.org/GoogleChromeLabs/imagecapture-polyfill) [![Dependency Status](https://david-dm.org/GoogleChromeLabs/imagecapture-polyfill.svg)](https://david-dm.org/GoogleChromeLabs/imagecapture-polyfill) [![devDependency Status](https://david-dm.org/GoogleChromeLabs/imagecapture-polyfill/dev-status.svg)](https://david-dm.org/GoogleChromeLabs/imagecapture-polyfill#info=devDependencies) 4 | 5 | ImageCapture is a polyfill for the [MediaStream Image Capture API](https://w3c.github.io/mediacapture-image/). 6 | 7 | ## Status 8 | 9 | As of June 2017, the ImageCapture spec is [relatively stable](https://github.com/w3c/mediacapture-image/issues). Chrome supports the API starting with M59 (earlier versions require setting a flag) and Firefox has partial support behind a flag. See the [ImageCapture browser support](https://github.com/w3c/mediacapture-image/blob/gh-pages/implementation-status.md) page for details. 10 | 11 | ## Prior art 12 | 13 | Prior to this API, in order to take a still picture from the device camera, two approaches have been used: 14 | 15 | 1. Set the source of a `