mighty-webcamjs
Version:
HTML5 Webcam Image Capture Library with Flash Fallback
862 lines (739 loc) • 26.5 kB
JavaScript
/* 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');
const React = require('react');
const ReactDOM = require('react-dom');
var blurMeasureInspectorAsync = require('inspector-bokeh/async');
const helpers = require('./helpers.js');
const model = require('./models/model.js');
var videoRecorder = require('./video-recorder.js');
const UploadFallbackNode = require('./components/UploadFallbackNode');
const WebcamContainer = require('./components/WebcamContainer');
const {
adjustUploadedPhoto,
createElement,
debouncePromise,
detectFlash,
drawImageScaled,
getAndroidVersion,
getIOSVersion,
handleImageInput,
measureBlur,
setPrefixedStyle,
validateUploadedPhoto,
whenDOMReady,
isBrowser,
getDeviceOrientation,
} = helpers;
let _userMedia;
const cssPrefix = 'webcamjs'; // prefix for all css classes
const URL = global.URL || global.webkitURL || global.mozURL || global.msURL;
// Safari fails in cookies disabled mode when even trying to access the localStorage
const navigator = global.navigator || {};
const ua = navigator.userAgent || '';
const FLASH_EMBED_ID = 'webcam_movie_embed';
const FLASH_OBJ_ID = 'webcam_movie_obj';
const 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();
const CAM_BACK = 'CAM_BACK';
const CAM_FRONT = 'CAM_FRONT';
const CAPTURE_MODE_PHOTO = 'CAPTURE_MODE_PHOTO';
const CAPTURE_MODE_VIDEO = 'CAPTURE_MODE_VIDEO';
const DETECTION_MODE_LABELS = 'DETECTION_MODE_LABELS';
const DETECTION_MODE_WEBRTC = 'DETECTION_MODE_WEBRTC';
const Webcam = {
constants: {
CAM_BACK,
CAM_FRONT,
CAPTURE_MODE_PHOTO,
CAPTURE_MODE_VIDEO,
DETECTION_MODE_LABELS,
DETECTION_MODE_WEBRTC,
STANDARD_RESOLUTIONS,
FLASH_EMBED_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
};
const cameraWidthCache = {
landscape: {
[CAM_FRONT]: 0,
[CAM_BACK]: 0,
},
portrait: {
[CAM_FRONT]: 0,
[CAM_BACK]: 0,
}
};
Object.assign(Webcam, {
params: model(Webcam),
version: '__VERSION__',
init() {
// 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;
let 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', () => {
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(() => { locked = false; });
}
});
}
},
reattach: () => new Promise(function (resolve) {
const container = this.container;
this.reset().then(() => {
this.attach(container, resolve);
});
}.bind(Webcam)),
checkIfCanCapture() {
// isWebRTCBrowser: for every other browser we need to provide fallback: file or flash.
const isWebRTCBrowser = Webcam.params.get('userMedia');
// isWebRTCCameraWorking: false when camera did not start by some reason - e.g. access not granted.
const isWebRTCCameraWorking = !!(isWebRTCBrowser && Webcam.stream && Webcam.stream.active);
return !isWebRTCBrowser || isWebRTCCameraWorking;
},
switchCamera: whenDOMReady(Webcam, function (...args) {
return new Promise((resolve) => {
if (process.env.NODE_ENV !== 'production' && Webcam.params.get('verbose')) {
console.log('switchCamera called with argument', args);
}
const cam = args[0];
if (!this.params.get('cameraDetectionMode') && this.params.get('userMedia')) {
return detectVideoInputs(this.mediaDevices).then(() => {
Webcam.switchCamera(cam).then(resolve);
});
}
// This function is much more reliable than webrtc `facingMode: 'user' / 'environment'`
const _cam = cam || (Webcam.params.get('camera') === CAM_FRONT ? CAM_BACK : CAM_FRONT);
let 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, callback = () => {}) {
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(() => {
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."));
}
let 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);
const 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;
const component = (
<WebcamContainer
model={ this.params }
webcam={ this }
cssPrefix={ cssPrefix }
onPegRef={(pegNode) => { this.peg = pegNode; }}
onVideoRef={(videoNode) => { this.video = videoNode; }}
/>
);
ReactDOM.render(component, container, callback);
}),
findBestResolution: function (video, { width, height }) {
const resolutionsToTest = STANDARD_RESOLUTIONS.reduce((result, val) => {
if (val > width && val <= this.params.get('dest_width')) {
result.push(val);
}
return result;
}, []);
const orientation = getDeviceOrientation();
const cachedWidth = cameraWidthCache[orientation][this.params.get('camera')];
const 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);
}
const ASPECT_LOCK = false;
const resolutionFinder = (resolver) => {
const track = video.srcObject.getVideoTracks()[0];
if (
!track ||
!this.params.get('load') ||
!this.params.get('best_resolution_finder') ||
// There is no need to take higher resolution for video, it can
// decrease user experience
this.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][this.params.get('camera')] = width;
return resolver(null);
}
// checking from the biggest to the smallest
const currentWidth = resolutionsToTest.pop();
if (process.env.NODE_ENV !== 'production' && this.params.get('verbose')) {
console.log('resolution finder', currentWidth);
}
let constraints = { width: { exact: currentWidth } };
if (ASPECT_LOCK) {
constraints.aspectRatio = { exact: width / height };
}
track.applyConstraints(constraints).then(() => {
cameraWidthCache[orientation][this.params.get('camera')] = currentWidth;
resolver(currentWidth);
}).catch(() => {
global.requestAnimationFrame(() => resolutionFinder(resolver));
});
};
return new Promise((resolve) => {
resolutionFinder(resolve);
});
},
reset: debouncePromise((config = {}) => {
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(() => {
if (stream && stream.active) {
resolveWhenUnloaded();
} else {
resolve();
}
}, 150);
}
resolveWhenUnloaded();
}.bind(Webcam));
}),
set(...args) { this.params.set(...args); },
on(name, callback) {
// set callback hook
name = name.toLowerCase().replace(/^on/, '');
if (!this.hooks[name]) this.hooks[name] = [];
this.hooks[name].push( callback );
},
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(...dispatchArgs) {
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 == '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() {
return this.params.get('force_file') ? false : detectFlash();
},
getUploadFallbackNode(communicationChannel = this.container.dataset.channel, extraProps = {}) {
return (
<UploadFallbackNode
webcam={ this }
model={ this.params }
cssPrefix={ cssPrefix }
communicationChannel={ communicationChannel }
{ ...extraProps }
/>
);
},
blurChecker: (canvas) => new Promise(function (resolve, reject) {
if (this.params.get('maximum_blur_index')) {
measureBlur(canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height))
.then((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 <= this.params.get('maximum_blur_index')) {
resolve();
} else {
reject('Taken picture is too blurry, please re-take photo.');
}
});
} else {
resolve();
}
}.bind(Webcam)),
handleImageInput: function (e) {
const target = e.target;
if (!target.files || !target.files.length) {
delete this.fallbackImage;
return;
}
var imgFile = target.files[0];
this.fallbackImage = new Promise((resolve) => {
handleImageInput(target.files[0]).then((img) => {
if (target) target.value = '';
var done = (exif) => {
adjustUploadedPhoto(img.data, exif, this.params.get())
.then((canvas) => {
var cb = () => {
var imgData = canvas.toDataURL('image/' + this.params.get('image_format'), this.params.get('jpeg_quality') / 100);
var channel = target.dataset.channel ? ':' + target.dataset.channel : '';
this.dispatch('imageSelected' + channel, imgData);
resolve(imgData);
};
this.blurChecker(canvas).then(cb, (errorMsg) => {
this.dispatch('error', errorMsg);
});
});
};
var fail = () => {
this.dispatch('error', 'Please try again and select "Take Photo" option.');
};
validateUploadedPhoto(imgFile, this.params.get()).then(done, fail);
});
});
}.bind(Webcam),
getFlashMovie(config = {}) {
// 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() {
// 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(() => {
this.container.insertBefore( preview_canvas, this.peg );
// set flag for user capture (use preview)
this.preview_active = true;
});
},
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() {
// 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() {
// 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(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(user_canvas) {
/*
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((resolve, reject) => {
const params = this.params.get();
// take snapshot and return image data uri
if (!params.load) {
return reject(
this.dispatch('error', new WebcamError("Webcam is not loaded yet"))
);
}
// if we have an active preview freeze, use that
if (this.preview_active) {
return this.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 = this.video && this.video.videoWidth || params.dest_width;
canvas.height = this.video && this.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 = this;
// create inline function, called after image load (flash) or immediately (native)
var func = function () {
// render image if needed (flash)
if (this && this.src && this.width && this.height) {
const 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 (this.params.get('userMedia')) {
context.drawImage(this.video, 0, 0, this.video.videoWidth, this.video.videoHeight);
// fire callback right away
func();
} else if (this.detectFlash()) {
// flash fallback
var raw_data = this.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 (this.fallbackImage) {
this.fallbackImage.then(function (image) {
var img = new Image();
img.src = image;
img.onload = () => {
drawImageScaled(img, canvas);
func();
};
});
} else {
return reject(this.dispatch('error', "Select picture first."));
}
} else if (params.load) {
return reject(this.dispatch('error', "Webcam has encountered an unknown error."));
} else {
return reject(this.dispatch('error', "Webcam is not loaded yet"));
}
});
},
configure(panel) {
// open flash configuration panel -- specify tab name:
// "camera", "privacy", "default", "localStorage", "microphone", "settingsManager"
if (!panel) panel = "camera";
this.getFlashMovie()._configure(panel);
},
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();
const enumerateDevices = debouncePromise((mediaDevices) => mediaDevices.enumerateDevices());
function detectVideoInputs(mediaDevices, isGetUserMediaCallback) {
return enumerateDevices(mediaDevices).then(function (info) {
var labels = '';
const 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
const isLabels = labels.length && !getIOSVersion() ? true : false;
const cameraDetectionMode = Webcam.params.get('cameraDetectionMode');
let 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.
const detectionModeChangedOverTime = isGetUserMediaCallback && newDetectionMode !== cameraDetectionMode;
if (detectionModeChangedOverTime) {
const 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) {
let resultCameraId;
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,
});
module.exports = Webcam;