Skip to content

Commit

Permalink
fix(facedetector): added face detector component
Browse files Browse the repository at this point in the history
  • Loading branch information
opensrc0 committed Apr 3, 2024
1 parent 0b654d9 commit 3cd6ab7
Show file tree
Hide file tree
Showing 6 changed files with 406 additions and 19 deletions.
18 changes: 18 additions & 0 deletions __app/component/FaceDetector/FaceDetector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import FaceDetectorInit from './FaceDetectorInit';
import FaceDetectorFlash from './FaceDetectorFlash';
import FaceDetectorClose from './FaceDetectorClose';
import FaceDetectorFacing from './FaceDetectorFacing';

export {
FaceDetectorInit,
FaceDetectorFlash,
FaceDetectorClose,
FaceDetectorFacing,
};

export default {
Init: FaceDetectorInit,
Flash: FaceDetectorFlash,
Close: FaceDetectorClose,
Facing: FaceDetectorFacing,
};
48 changes: 48 additions & 0 deletions __app/component/FaceDetector/FaceDetectorClose.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';

function FaceDetectorClose({
onClose,
children,
zIndex,
top,
bottom,
left,
right,
color,
position,
}) {
return (
<div
style={{
top,
bottom,
left,
right,
zIndex,
color,
position,
}}
onClick={onClose}
onKeyDown={(ev) => ev.key === 'Enter' && onClose()}
role="button"
tabIndex={0}
>
{children || 'Close Button'}
</div>
);
}

FaceDetectorClose.propTypes = {

};

FaceDetectorClose.defaultProps = {
color: 'white',
top: 'auto',
bottom: '50%',
left: 'auto',
right: '72%',
position: 'absolute',
};

export default FaceDetectorClose;
48 changes: 48 additions & 0 deletions __app/component/FaceDetector/FaceDetectorFacing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';

function FaceDetectorFacing({
toggleCamera,
children,
zIndex,
top,
bottom,
left,
right,
color,
position,
}) {
return (
<div
style={{
top,
bottom,
left,
right,
zIndex,
color,
position,
}}
onClick={toggleCamera}
onKeyDown={(ev) => ev.key === 'Enter' && toggleCamera()}
role="button"
tabIndex={0}
>
{children || 'Toggle Camera'}
</div>
);
}

FaceDetectorFacing.propTypes = {

};

FaceDetectorFacing.defaultProps = {
color: 'white',
top: 'auto',
bottom: '50%',
left: 'auto',
right: '37%',
position: 'absolute',
};

export default FaceDetectorFacing;
48 changes: 48 additions & 0 deletions __app/component/FaceDetector/FaceDetectorFlash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';

function FaceDetector({
toggleFlash,
children,
zIndex,
top,
bottom,
left,
right,
color,
position,
}) {
return (
<div
style={{
top,
bottom,
left,
right,
zIndex,
color,
position,
}}
onClick={toggleFlash}
onKeyDown={(ev) => ev.key === 'Enter' && toggleFlash()}
role="button"
tabIndex={0}
>
{children || 'Toggle Flash'}
</div>
);
}

FaceDetector.propTypes = {

};

FaceDetector.defaultProps = {
color: 'white',
top: 'auto',
bottom: '50%',
left: 'auto',
right: '5%',
position: 'absolute',
};

export default FaceDetector;
227 changes: 227 additions & 0 deletions __app/component/FaceDetector/FaceDetectorInit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/* eslint-disable no-inner-declarations */
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { handleError, handleLoading } from '../services/handlerService';
import Wrapper from '../Wrapper/Wrapper';

let mediaStream = null;
let videoUnmount = null;
let unmoutRenderLoop = null;

function FaceDetectorInit({
successCb,
failureCb,
loadingCb,
successMsg,
failureMsg,
cameraType,
zIndex,
children,
}) {
let list = null;
let video = null;
let facingMode;

const [flash, setFlash] = useState(false);
const [isBrowser, setIsBrowser] = useState(false);
const [faces, setFaces] = useState([]);

const stopStreaming = () => {
if (mediaStream) {
mediaStream.getTracks().forEach((track) => {
track.stop();
});
}
};

const allClear = () => {
cancelAnimationFrame(videoUnmount);
stopStreaming();
clearTimeout(unmoutRenderLoop);
};

const detectCodes = async () => {
const WindowFaceDetector = globalThis.FaceDetector;
const faceDetector = new WindowFaceDetector();

// eslint-disable-next-line consistent-return
async function render() {
try {
const getFaces = await faceDetector.detect(video);

if (getFaces[0]) {
setFaces(getFaces);
// cancelAnimationFrame(videoUnmount);
// stopStreaming();
// clearTimeout(unmoutRenderLoop);
}
} catch (error) {
return handleError({ msgType: 'BAR_CODE_DETECTION_FAILED', msg: failureMsg.barCodeDetectionFailed || JSON.stringify(error), failureCb });
}
}

unmoutRenderLoop = setTimeout(() => {
(function renderLoop() {
videoUnmount = requestAnimationFrame(() => {
setTimeout(renderLoop, 1500);
});
render();
}());
}, 100);
};

const createVideo = async (id) => {
document.getElementById('streaming-video')?.remove();

video = document.createElement('video');

video.id = 'streaming-video';
video.srcObject = mediaStream;
video.autoplay = true;
video.play();
// video.style.width = '100vh';
// video.style.height = '100%';
video.style.position = 'absolute';
// video.style.overflow = 'hidden';
// video.style.display = 'block';
video.style.zIndex = zIndex;
video.style.top = '0';
video.style.left = '0';
video.style.objectFit = 'fill';
list = document.getElementById(id);
list.before(video);
};

const startStreaming = async () => {
try {
mediaStream = await navigator.mediaDevices.getUserMedia({
video: {
// deviceId: camera.deviceId,
facingMode,
zoom: true,
resizeMode: true,
focusDistance: true,
focusMode: true,
},
});
} catch (error) {
return handleError({ msgType: 'STREAMING_FAILED', msg: failureMsg.streamingFailed || JSON.stringify(error), failureCb });
}
return mediaStream;
};

const startVideo = async (id = 'camera') => {
mediaStream = await startStreaming();
createVideo(id);
detectCodes();
};

const toggleFlash = async () => {
const track = mediaStream.getVideoTracks()[0];
try {
await track.applyConstraints({
advanced: [{ torch: !flash }],
});
setFlash((s) => !s);
} catch (error) {
return handleError({ msgType: 'FLASH_UPSUPPORTED', msg: failureMsg.flashUnsupported, failureCb });
}
return true;
};

const toggleCamera = () => {
facingMode = facingMode === 'user' ? 'environment' : 'user';

stopStreaming();
cancelAnimationFrame(videoUnmount);
clearTimeout(unmoutRenderLoop);
startVideo();
};

const handleBrowserSupport = () => {
if (FaceDetectorInit.isBrowserSupport()) {
facingMode = cameraType === 'back' ? 'environment' : 'user';
handleLoading({ loadingCb });

startVideo();
} else {
return handleError({ msgType: 'UN_SUPPORTED_FEATURE', msg: failureMsg.unSupported, failureCb });
}

return true;
};

useEffect(() => {
setIsBrowser(true);
handleBrowserSupport();

return () => {
allClear();
};
}, []);

return isBrowser && FaceDetectorInit.isBrowserSupport() && (
<div id="face-detector">
<div id="camera" style={{ position: 'absolute' }} />
{
React.Children.map(children, (child) => React.cloneElement(child, {
zIndex,
toggleCamera,
toggleFlash,
successCb,
successMsg,
failureCb,
failureMsg,
}))
}
{
faces.map((face) => (
<div
className="face"
style={{
width: `${face.boundingBox.width}px`,
height: `${face.boundingBox.height}px`,
left: `${face.boundingBox.left}px`,
top: `${face.boundingBox.top}px`,
position: 'absolute',
border: '2px solid yellow',
zIndex: '12',
}}

/>
))
}
</div>
);
}

FaceDetectorInit.isBrowserSupport = () => navigator?.mediaDevices && globalThis.FaceDetector;

FaceDetectorInit.propTypes = {
successCb: PropTypes.func,
failureCb: PropTypes.func,
loadingCb: PropTypes.func,
successMsg: PropTypes.string,
failureMsg: PropTypes.object,
zIndex: PropTypes.number,
cameraType: PropTypes.oneOf(['back', 'front']),
};

FaceDetectorInit.defaultProps = {
successCb: () => {},
failureCb: () => {},
loadingCb: () => {},
successMsg: '',
failureMsg: {
unSupported: 'QR-Code/Bar-Code/UPI Scanner is not supporting in your device',
streamingFailed: 'Camera streaming failed',
barCodeDetectionFailed: 'Bar code detection failed',
invalidImage: 'Invalid Images',
flashUnsupported: 'Flash is not supporting in your device',
unableToScan: 'Unable to scan',
},
zIndex: 9,
cameraType: 'back',
};

export default Wrapper(FaceDetectorInit);
Loading

0 comments on commit 3cd6ab7

Please sign in to comment.