UNPKG

mighty-webcamjs

Version:

HTML5 Webcam Image Capture Library with Flash Fallback

883 lines (751 loc) 29.6 kB
'use strict'; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _landscape, _portrait; function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /* eslint-disable */ // WebcamJS __VERSION__ // Webcam library for capturing JPEG/PNG images in JavaScript // Attempts getUserMedia, falls back to Flash // Author: Joseph Huckaby: http://github.com/jhuckaby // Based on JPEGCam: http://code.google.com/p/jpegcam/ // Copyright (c) 2012 - 2016 Joseph Huckaby // Licensed under the MIT License require('webrtc-adapter'); var React = require('react'); var ReactDOM = require('react-dom'); var blurMeasureInspectorAsync = require('inspector-bokeh/async'); var helpers = require('./helpers.js'); var model = require('./models/model.js'); var videoRecorder = require('./video-recorder.js'); var UploadFallbackNode = require('./components/UploadFallbackNode'); var WebcamContainer = require('./components/WebcamContainer'); var adjustUploadedPhoto = helpers.adjustUploadedPhoto, createElement = helpers.createElement, debouncePromise = helpers.debouncePromise, _detectFlash = helpers.detectFlash, drawImageScaled = helpers.drawImageScaled, getAndroidVersion = helpers.getAndroidVersion, getIOSVersion = helpers.getIOSVersion, handleImageInput = helpers.handleImageInput, measureBlur = helpers.measureBlur, setPrefixedStyle = helpers.setPrefixedStyle, validateUploadedPhoto = helpers.validateUploadedPhoto, whenDOMReady = helpers.whenDOMReady, isBrowser = helpers.isBrowser, getDeviceOrientation = helpers.getDeviceOrientation; var _userMedia = void 0; var cssPrefix = 'webcamjs'; // prefix for all css classes var URL = global.URL || global.webkitURL || global.mozURL || global.msURL; // Safari fails in cookies disabled mode when even trying to access the localStorage var navigator = global.navigator || {}; var ua = navigator.userAgent || ''; var FLASH_EMBED_ID = 'webcam_movie_embed'; var FLASH_OBJ_ID = 'webcam_movie_obj'; var STANDARD_RESOLUTIONS = [480, 544, 576, 640, 720, 724, 792, 800, 864, 936, 960, 1024, 1080, 1180, 1200, 1280, 1440, 1536, 1600, 1920, 2048, 2160, 2560, 2880, 3032, 3072, 3264]; var isDesktop = !getAndroidVersion() && !getIOSVersion(); // There are specific problems on some devices (Google Pixel, new Samsungs, etc). // Forcing WebRTC detection mode seems to fix NotReadable issue. // // Desktops can't use WebRTC detection mode because facingMode: 'user' / 'environment' always // brings default camera even on Microsoft Surface Pro tablet. var forceWebrtcDetectionMode = !isDesktop; // declare error types // inheritance pattern here: // https://stackoverflow.com/questions/783818/how-do-i-create-a-custom-error-in-javascript function FlashError() { var temp = Error.apply(this, arguments); temp.name = this.name = "FlashError"; this.stack = temp.stack; this.message = temp.message; } function WebcamError() { var temp = Error.apply(this, arguments); temp.name = this.name = "WebcamError"; this.stack = temp.stack; this.message = temp.message; } function IntermediateInheritor() {} IntermediateInheritor.prototype = Error.prototype; FlashError.prototype = new IntermediateInheritor(); WebcamError.prototype = new IntermediateInheritor(); var CAM_BACK = 'CAM_BACK'; var CAM_FRONT = 'CAM_FRONT'; var CAPTURE_MODE_PHOTO = 'CAPTURE_MODE_PHOTO'; var CAPTURE_MODE_VIDEO = 'CAPTURE_MODE_VIDEO'; var DETECTION_MODE_LABELS = 'DETECTION_MODE_LABELS'; var DETECTION_MODE_WEBRTC = 'DETECTION_MODE_WEBRTC'; var Webcam = { constants: { CAM_BACK: CAM_BACK, CAM_FRONT: CAM_FRONT, CAPTURE_MODE_PHOTO: CAPTURE_MODE_PHOTO, CAPTURE_MODE_VIDEO: CAPTURE_MODE_VIDEO, DETECTION_MODE_LABELS: DETECTION_MODE_LABELS, DETECTION_MODE_WEBRTC: DETECTION_MODE_WEBRTC, STANDARD_RESOLUTIONS: STANDARD_RESOLUTIONS, FLASH_EMBED_ID: FLASH_EMBED_ID, FLASH_OBJ_ID: FLASH_OBJ_ID, MODE_ACCEPTS: { CAPTURE_MODE_PHOTO: 'image/*', CAPTURE_MODE_VIDEO: 'video/*' }, WEBRTC_CAMERAS: { CAM_BACK: 'environment', CAM_FRONT: 'user' }, CAMERA_MODE_NONE: 'none', CAMERA_MODE_FLASH: 'flash', CAMERA_MODE_FILE: 'file-fallback', CAMERA_MODE_WEBRTC: 'webrtc' }, errors: { FlashError: FlashError, WebcamError: WebcamError }, hooks: {} // callback hook functions }; var cameraWidthCache = { landscape: (_landscape = {}, _defineProperty(_landscape, CAM_FRONT, 0), _defineProperty(_landscape, CAM_BACK, 0), _landscape), portrait: (_portrait = {}, _defineProperty(_portrait, CAM_FRONT, 0), _defineProperty(_portrait, CAM_BACK, 0), _portrait) }; Object.assign(Webcam, { params: model(Webcam), version: '__VERSION__', init: function init() { var _this = this; // initialize, check for getUserMedia support // Setup getUserMedia, with polyfill for older browsers // Adapted from: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia this.mediaDevices = navigator.mediaDevices || false; var userMedia = this.params.get('userMedia') && !!this.mediaDevices && !!URL; // Older versions of firefox (< 21) apparently claim support but user media does not actually work if (ua.match(/Firefox\D+(\d+)/)) { if (parseInt(RegExp.$1, 10) < 21) userMedia = null; } this.params.set('userMedia', userMedia); // Make sure media stream is closed when navigating away from page if (userMedia) { detectVideoInputs(this.mediaDevices); addEventListener('beforeunload', this.reset.bind(this)); var locked = false; addEventListener('orientationchange', function () { if (locked // prevents multiple attach on rapid orientationchange || !_this.params.get('load') // Webcam not loaded yet || _this.params.get('videoRecording') // Recording in progress ) return; if (_this.params.get('load')) { locked = true; // video needs to be reinitialised after screen rotation so taken pictures aren't // rotated. Webcam.reattach().then(function () { locked = false; }); } }); } }, reattach: function reattach() { return new Promise(function (resolve) { var _this2 = this; var container = this.container; this.reset().then(function () { _this2.attach(container, resolve); }); }.bind(Webcam)); }, checkIfCanCapture: function checkIfCanCapture() { // isWebRTCBrowser: for every other browser we need to provide fallback: file or flash. var isWebRTCBrowser = Webcam.params.get('userMedia'); // isWebRTCCameraWorking: false when camera did not start by some reason - e.g. access not granted. var isWebRTCCameraWorking = !!(isWebRTCBrowser && Webcam.stream && Webcam.stream.active); return !isWebRTCBrowser || isWebRTCCameraWorking; }, switchCamera: whenDOMReady(Webcam, function () { var _this3 = this; for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return new Promise(function (resolve) { if (process.env.NODE_ENV !== 'production' && Webcam.params.get('verbose')) { console.log('switchCamera called with argument', args); } var cam = args[0]; if (!_this3.params.get('cameraDetectionMode') && _this3.params.get('userMedia')) { return detectVideoInputs(_this3.mediaDevices).then(function () { Webcam.switchCamera(cam).then(resolve); }); } // This function is much more reliable than webrtc `facingMode: 'user' / 'environment'` var _cam = cam || (Webcam.params.get('camera') === CAM_FRONT ? CAM_BACK : CAM_FRONT); var resultCameraId = 0; if (Number.isFinite(Webcam.params.get('cameraId')) && _cam === Webcam.params.get('camera')) { return resolve(); } if (!cam) { resultCameraId = (Webcam.params.get('cameraId') + 1) % Webcam.params.get('cameraInfs').length; } resultCameraId = getCameraIdByLabel(_cam) || resultCameraId; if (process.env.NODE_ENV !== 'production' && Webcam.params.get('verbose')) { console.log('Camera switching', { 'camera': _cam, 'cameraId': resultCameraId }); } Webcam.set({ 'camera': _cam, 'cameraId': resultCameraId }); resolve(); }); }), attach: whenDOMReady(Webcam, function (container) { var _this4 = this; var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {}; if (this.container === container) { return this.reattach(); } // create webcam preview and attach to DOM element // pass in actual DOM reference if (!container) { return this.dispatch('error', new WebcamError("Could not locate DOM element to attach to.")); } if (this.params.get('cameraId') === undefined) { return this.switchCamera(this.params.get('camera')).then(function () { Webcam.attach(container, callback); }); } if (process.env.NODE_ENV !== 'production' && Webcam.params.get('verbose')) { console.log('WebcamJS Attaching', container); } // set width/height if not already set this.params.set({ width: this.params.get('width') || container.offsetWidth, height: this.params.get('height') || container.offsetHeight, dest_width: this.params.get('dest_width') || this.params.get('width'), dest_height: this.params.get('dest_height') || this.params.get('height'), fps: this.params.get('fps') * 1 || 30 }); // make sure we have a nonzero width and height at this point if (process.env.NODE_ENV !== 'production' && (!this.params.get('width') || !this.params.get('height'))) { return this.dispatch('error', new WebcamError("No width and/or height for webcam. Please call set() first, or attach to a visible element.")); } var userMedia = _userMedia === undefined ? this.params.get('userMedia') : _userMedia; // if force_flash is set, disable userMedia if (this.params.get('force_flash') || this.params.get('force_file')) { _userMedia = userMedia; userMedia = false; } this.params.set('userMedia', userMedia); var styleSheetURL = this.params.get('webcam_path') + 'webcam.css'; if (!this.styleSheet || this.styleSheet.getAttribute('href') !== styleSheetURL) { this.styleSheet = createElement('link', { href: styleSheetURL, media: 'all', rel: 'stylesheet', type: 'text/css' }); document.head.appendChild(this.styleSheet); } blurMeasureInspectorAsync.setup({ workerURL: this.params.get('webcam_path') + 'measure_blur/measure_blur_worker.js' }); this.container = container; var component = React.createElement(WebcamContainer, { model: this.params, webcam: this, cssPrefix: cssPrefix, onPegRef: function onPegRef(pegNode) { _this4.peg = pegNode; }, onVideoRef: function onVideoRef(videoNode) { _this4.video = videoNode; } }); ReactDOM.render(component, container, callback); }), findBestResolution: function findBestResolution(video, _ref) { var _this5 = this; var width = _ref.width, height = _ref.height; var resolutionsToTest = STANDARD_RESOLUTIONS.reduce(function (result, val) { if (val > width && val <= _this5.params.get('dest_width')) { result.push(val); } return result; }, []); var orientation = getDeviceOrientation(); var cachedWidth = cameraWidthCache[orientation][this.params.get('camera')]; var photoCapabilities = this.params.get('photoCapabilities'); if (photoCapabilities.imageWidth && photoCapabilities.imageWidth.max) { // Use value detected by ImageCapture API. resolutionsToTest.push(photoCapabilities.imageWidth.max); } if (cachedWidth) { // Use value used previously resolutionsToTest.push(cachedWidth); } var ASPECT_LOCK = false; var resolutionFinder = function resolutionFinder(resolver) { var track = video.srcObject.getVideoTracks()[0]; if (!track || !_this5.params.get('load') || !_this5.params.get('best_resolution_finder') || // There is no need to take higher resolution for video, it can // decrease user experience _this5.params.get('capture_mode') === CAPTURE_MODE_VIDEO || // Some browsers (Firefox) can result 0x0 before granting access. // Some browsers (Android) can result 2x2 before granting access. width * height <= 4 || !track || !track.applyConstraints) { return resolver(null); } if (!resolutionsToTest.length) { cameraWidthCache[orientation][_this5.params.get('camera')] = width; return resolver(null); } // checking from the biggest to the smallest var currentWidth = resolutionsToTest.pop(); if (process.env.NODE_ENV !== 'production' && _this5.params.get('verbose')) { console.log('resolution finder', currentWidth); } var constraints = { width: { exact: currentWidth } }; if (ASPECT_LOCK) { constraints.aspectRatio = { exact: width / height }; } track.applyConstraints(constraints).then(function () { cameraWidthCache[orientation][_this5.params.get('camera')] = currentWidth; resolver(currentWidth); }).catch(function () { global.requestAnimationFrame(function () { return resolutionFinder(resolver); }); }); }; return new Promise(function (resolve) { resolutionFinder(resolve); }); }, reset: debouncePromise(function () { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return new Promise(function (resolve) { if (process.env.NODE_ENV !== 'production' && Webcam.params.get('verbose')) { console.log('WebcamJS reset'); } // shutdown camera, reset to potentially attach again if (this.preview_active) this.unfreeze(); // attempt to fix issue #64 this.unflip(); var stream = this.stream; var tracks; if (this.params.get('userMedia')) { if (stream) { if (stream.getVideoTracks) { // get video track to call stop on it tracks = stream.getVideoTracks(); if (tracks && tracks[0] && tracks[0].stop) tracks[0].stop(); } if (stream.getAudioTracks) { tracks = stream.getAudioTracks(); if (tracks && tracks[0] && tracks[0].stop) tracks[0].stop(); } if (stream.stop) { // deprecated, may be removed in future stream.stop(); } } delete this.stream; delete this.video; } else if (this.detectFlash()) { // call for turn off camera in flash var movie = this.getFlashMovie({ silent: config.silent }); if (movie && movie._releaseCamera) movie._releaseCamera(); } if (this.container) { ReactDOM.unmountComponentAtNode(this.container); delete this.container; } if (this.params.get('videoRecording')) { throw new Error('Can\'t reset camera while video is recording.'); } this.params.resetState(); // TODO: remove lines above delete this.fallbackImage; function resolveWhenUnloaded() { setTimeout(function () { if (stream && stream.active) { resolveWhenUnloaded(); } else { resolve(); } }, 150); } resolveWhenUnloaded(); }.bind(Webcam)); }), set: function set() { var _params; (_params = this.params).set.apply(_params, arguments); }, on: function on(name, callback) { // set callback hook name = name.toLowerCase().replace(/^on/, ''); if (!this.hooks[name]) this.hooks[name] = []; this.hooks[name].push(callback); }, off: function off(name, callback) { // remove callback hook name = name.toLowerCase().replace(/^on/, ''); if (this.hooks[name]) { if (callback) { // remove one selected callback from list var idx = this.hooks[name].indexOf(callback); if (idx > -1) this.hooks[name].splice(idx, 1); } else { // no callback specified, so clear all this.hooks[name] = []; } } }, dispatch: function dispatch() { for (var _len2 = arguments.length, dispatchArgs = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { dispatchArgs[_key2] = arguments[_key2]; } if (Webcam.params.get('verbose')) { console.info('webcam dispatch', dispatchArgs); } // fire hook callback, passing optional value to it var name = dispatchArgs[0].toLowerCase().replace(/^on/, ''); var args = Array.prototype.slice.call(dispatchArgs, 1); if (this.hooks[name] && this.hooks[name].length) { for (var idx = 0, len = this.hooks[name].length; idx < len; idx++) { var hook = this.hooks[name][idx]; if (typeof hook == 'function') { // callback is function reference, call directly hook.apply(this, args); } else if ((typeof hook === 'undefined' ? 'undefined' : _typeof(hook)) == 'object' && hook.length == 2) { // callback is PHP-style object instance method // TODO: review if this part is used anywhere? hook[0][hook[1]].apply(hook[0], args); } } // loop return true; } else if (name == 'error') { var message; if (args[0] instanceof FlashError || args[0] instanceof WebcamError) { message = args[0].message; } else { message = "Could not access webcam: " + args[0].name + ": " + args[0].message + " " + args[0].toString(); } // default error handler if no custom one specified alert("Webcam.js Error: " + message); } return false; // no hook defined }, detectFlash: function detectFlash() { return this.params.get('force_file') ? false : _detectFlash(); }, getUploadFallbackNode: function getUploadFallbackNode() { var communicationChannel = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.container.dataset.channel; var extraProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return React.createElement(UploadFallbackNode, _extends({ webcam: this, model: this.params, cssPrefix: cssPrefix, communicationChannel: communicationChannel }, extraProps)); }, blurChecker: function blurChecker(canvas) { return new Promise(function (resolve, reject) { var _this6 = this; if (this.params.get('maximum_blur_index')) { measureBlur(canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height)).then(function (blurScore) { var score = blurScore.avg_edge_width_perc; if (process.env.NODE_ENV !== 'production' && Webcam.params.get('verbose')) { console.log('Blur index:', score); } if (score <= _this6.params.get('maximum_blur_index')) { resolve(); } else { reject('Taken picture is too blurry, please re-take photo.'); } }); } else { resolve(); } }.bind(Webcam)); }, handleImageInput: function (e) { var _this7 = this; var target = e.target; if (!target.files || !target.files.length) { delete this.fallbackImage; return; } var imgFile = target.files[0]; this.fallbackImage = new Promise(function (resolve) { handleImageInput(target.files[0]).then(function (img) { if (target) target.value = ''; var done = function done(exif) { adjustUploadedPhoto(img.data, exif, _this7.params.get()).then(function (canvas) { var cb = function cb() { var imgData = canvas.toDataURL('image/' + _this7.params.get('image_format'), _this7.params.get('jpeg_quality') / 100); var channel = target.dataset.channel ? ':' + target.dataset.channel : ''; _this7.dispatch('imageSelected' + channel, imgData); resolve(imgData); }; _this7.blurChecker(canvas).then(cb, function (errorMsg) { _this7.dispatch('error', errorMsg); }); }); }; var fail = function fail() { _this7.dispatch('error', 'Please try again and select "Take Photo" option.'); }; validateUploadedPhoto(imgFile, _this7.params.get()).then(done, fail); }); }); }.bind(Webcam), getFlashMovie: function getFlashMovie() { var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; // get reference to movie object/embed in DOM if (!this.params.get('load')) return !config.silent && this.dispatch('error', new FlashError("Flash Movie is not loaded yet")); var movie = document.getElementById(FLASH_OBJ_ID); if (!movie || !movie._snap) movie = document.getElementById(FLASH_EMBED_ID); if (!movie) this.dispatch('error', new FlashError("Cannot locate Flash movie in DOM")); return movie; }, freeze: function freeze() { var _this8 = this; // show preview, freeze camera // kill preview if already active if (this.preview_active) this.unfreeze(); // must unflip container as preview canvas will be pre-flipped this.unflip(); // create canvas for holding preview var preview_canvas = this.preview_canvas = createElement('canvas', { className: cssPrefix + '__preview' }); // take snapshot, but fire our own callback this.snap(preview_canvas).then(function () { _this8.container.insertBefore(preview_canvas, _this8.peg); // set flag for user capture (use preview) _this8.preview_active = true; }); }, unfreeze: function unfreeze() { // cancel preview and resume live video feed if (this.preview_active) { // remove preview canvas this.container.removeChild(this.preview_canvas); delete this.preview_canvas; // unflag this.preview_active = false; // re-flip if we unflipped before this.flip(); } }, flip: function flip() { // flip container horiz (mirror mode) if desired if (this.params.get('flip_horiz') && this.video) { if (this.params.get('camera') === CAM_BACK && this.params.get('flip_horiz_back') === false) { return Webcam.unflip(); } setPrefixedStyle(this.video, 'transform', 'scaleX(-1)'); this.video.style.filter = 'FlipH'; this.video.style.msFilter = 'FlipH'; } }, unflip: function unflip() { // unflip container horiz (mirror mode) if desired if (this.params.get('flip_horiz') && this.video) { setPrefixedStyle(this.video, 'transform', 'scaleX(1)'); this.video.style.filter = ''; this.video.style.msFilter = ''; } }, savePreview: function savePreview(user_callback, user_canvas) { // save preview freeze and fire user callback var params = this.params.get(); var canvas = this.preview_canvas; // render to user canvas if desired if (user_canvas) { user_canvas.getContext('2d').drawImage(canvas, 0, 0); } // fire user callback if desired user_callback(user_canvas ? null : canvas.toDataURL('image/' + params.image_format, params.jpeg_quality / 100)); // remove preview if (params.unfreeze_snap) this.unfreeze(); }, snap: function snap(user_canvas) { var _this9 = this; /* TODO: Implement it in the future. As for now (Aug 2018), it was freezing browser and returning "platform error" const imageCapture = new ImageCapture(this.stream.getVideoTracks()[0]); console.log('taking photo start'); imageCapture.takePhoto({fillLightMode: 'flash'}).then((blob) => { console.log('taking photo end'); const img = document.createElement('img'); img.src = URL.createObjectURL(blob); img.setAttribute('style', 'border: 1px solid red'); document.body.appendChild(img); }).catch(console.warn); */ return new Promise(function (resolve, reject) { var params = _this9.params.get(); // take snapshot and return image data uri if (!params.load) { return reject(_this9.dispatch('error', new WebcamError("Webcam is not loaded yet"))); } // if we have an active preview freeze, use that if (_this9.preview_active) { return _this9.savePreview(resolve, user_canvas); } // create offscreen canvas element to hold pixels var canvas = document.createElement('canvas'); // HACK: if we have video (not flash fallback), use video's native width/height canvas.width = _this9.video && _this9.video.videoWidth || params.dest_width; canvas.height = _this9.video && _this9.video.videoHeight || params.dest_height; var context = canvas.getContext('2d'); // flip canvas horizontally if desired if (params.flip_horiz_on_snap) { context.translate(params.dest_width, 0); context.scale(-1, 1); } var self = _this9; // create inline function, called after image load (flash) or immediately (native) var func = function func() { // render image if needed (flash) if (this && this.src && this.width && this.height) { var useDestDimensions = this.width >= params.dest_width; if (useDestDimensions) { context.drawImage(this, 0, 0, params.dest_width, params.dest_height); } else { canvas.width = this.width; canvas.height = this.height; context.drawImage(this, 0, 0, this.width, this.height); } } // render to user canvas if desired if (user_canvas) { user_canvas.width = canvas.width; user_canvas.height = canvas.height; drawImageScaled(canvas, user_canvas); } self.blurChecker(canvas).then(function () { resolve(user_canvas ? null : canvas.toDataURL('image/' + params.image_format, params.jpeg_quality / 100)); }, function (errorMsg) { self.dispatch('error', errorMsg); reject(); }); }; // grab image frame from userMedia or flash movie if (_this9.params.get('userMedia')) { context.drawImage(_this9.video, 0, 0, _this9.video.videoWidth, _this9.video.videoHeight); // fire callback right away func(); } else if (_this9.detectFlash()) { // flash fallback var raw_data = _this9.getFlashMovie()._snap(); // render to image, fire callback when complete var img = new Image(); img.onload = func; img.src = 'data:image/' + params.image_format + ';base64,' + raw_data; } else if (params.enable_file_fallback) { if (_this9.fallbackImage) { _this9.fallbackImage.then(function (image) { var img = new Image(); img.src = image; img.onload = function () { drawImageScaled(img, canvas); func(); }; }); } else { return reject(_this9.dispatch('error', "Select picture first.")); } } else if (params.load) { return reject(_this9.dispatch('error', "Webcam has encountered an unknown error.")); } else { return reject(_this9.dispatch('error', "Webcam is not loaded yet")); } }); }, configure: function configure(panel) { // open flash configuration panel -- specify tab name: // "camera", "privacy", "default", "localStorage", "microphone", "settingsManager" if (!panel) panel = "camera"; this.getFlashMovie()._configure(panel); }, flashNotify: function flashNotify(type, msg) { // receive notification from flash about event switch (type) { case 'flashLoadComplete': // movie loaded successfully this.params.set('load', true); break; case 'cameraLive': // camera is live and ready to snap this.params.set('live', true); break; case 'error': // Flash error this.dispatch('error', new FlashError(msg)); break; default: // catch-all event, just in case // console.log("webcam flash_notify: " + type + ": " + msg); break; } } }); isBrowser && whenDOMReady(Webcam, Webcam.init).call(); var enumerateDevices = debouncePromise(function (mediaDevices) { return mediaDevices.enumerateDevices(); }); function detectVideoInputs(mediaDevices, isGetUserMediaCallback) { return enumerateDevices(mediaDevices).then(function (info) { var labels = ''; var cameraInfs = []; for (var i in info) { var inf = info[i]; if (inf.kind === 'videoinput') { cameraInfs.push(inf); labels += inf.label || ''; } } // cameraDetectionMode works better and produces better picture in webrtc mode var isLabels = labels.length && !getIOSVersion() ? true : false; var cameraDetectionMode = Webcam.params.get('cameraDetectionMode'); var newDetectionMode = isLabels ? DETECTION_MODE_LABELS : DETECTION_MODE_WEBRTC; newDetectionMode = forceWebrtcDetectionMode && DETECTION_MODE_WEBRTC || newDetectionMode; Webcam.params.set({ 'cameraInfs': cameraInfs, 'cameraDetectionMode': newDetectionMode }); // If detection mode changes, this mean user have just granted camera access. We need to find camera, // we are already using by it's label, preventing glitches on switching camera. var detectionModeChangedOverTime = isGetUserMediaCallback && newDetectionMode !== cameraDetectionMode; if (detectionModeChangedOverTime) { var resultCameraId = getCameraIdByLabel(Webcam.params.get('camera')); if (process.env.NODE_ENV !== 'production' && Webcam.params.get('verbose')) { console.log('Setting cameraID after granting access', { 'cameraId': resultCameraId }); } Webcam.set({ cameraId: undefined }); Webcam.switchCamera(Webcam.params.get('camera')).then(Webcam.reattach); } }); } function getCameraIdByLabel(_cam) { var resultCameraId = void 0; Webcam.params.get('cameraInfs').forEach(function (cameraInf, id) { var label = cameraInf.label && cameraInf.label.toLowerCase() || ''; if (_cam === CAM_FRONT && (label.includes('front') || label.includes('face')) || _cam === CAM_BACK && (label.includes('rear') || label.includes('back'))) { resultCameraId = id; } }); return resultCameraId; } Webcam.helpers = Object.assign(helpers, { videoRecorder: videoRecorder.init(Webcam), detectVideoInputs: detectVideoInputs }); module.exports = Webcam;