react-vision-camera
Version:
Camera component for React. We can use this component for computer vision tasks like barcode scanning, text recognition, etc.
200 lines (199 loc) • 7.9 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = __importDefault(require("react"));
const VisionCamera = (props) => {
const devices = react_1.default.useRef(null);
const localStream = react_1.default.useRef(null);
const camera = react_1.default.useRef(null);
const mounted = react_1.default.useRef(false);
react_1.default.useEffect(() => {
const init = () => __awaiter(void 0, void 0, void 0, function* () {
if (!devices.current) {
yield loadDevices(); // load the camera devices list when the component is mounted
}
if (props.isActive === true) {
playWithDesired();
}
mounted.current = true;
});
init();
}, []);
react_1.default.useEffect(() => {
if (mounted.current === true) {
if (props.isActive === true) {
playWithDesired();
}
else {
stop();
}
}
}, [props.isActive]);
react_1.default.useEffect(() => {
console.log("pause");
console.log(props.isPause);
if (mounted.current === true) {
if (camera.current && props.isActive === true) {
if (props.isPause === true) {
camera.current.pause();
}
else {
camera.current.play();
}
}
}
}, [props.isPause]);
react_1.default.useEffect(() => {
if (props.isActive === true && localStream.current && mounted.current === true) {
playWithDesired();
}
}, [props.desiredCamera, props.desiredResolution, props.facingMode]);
const playWithDesired = () => __awaiter(void 0, void 0, void 0, function* () {
if (!devices.current) {
yield loadDevices(); // load the camera devices list if it hasn't been loaded
}
let desiredDevice = getDesiredDevice(devices.current);
if (desiredDevice) {
let options = {};
options.deviceId = desiredDevice;
if (props.desiredResolution) {
options.desiredResolution = props.desiredResolution;
}
if (props.facingMode) {
options.facingMode = props.facingMode;
}
play(options);
}
else {
throw new Error("No camera detected");
}
});
const getDesiredDevice = (devices) => {
var count = 0;
var desiredIndex = 0;
for (var i = 0; i < devices.length; i++) {
var device = devices[i];
var label = device.label || `Camera ${count++}`;
if (props.desiredCamera) {
if (label.toLowerCase().indexOf(props.desiredCamera.toLowerCase()) != -1) {
desiredIndex = i;
break;
}
}
}
if (devices.length > 0) {
return devices[desiredIndex].deviceId; // return the device id
}
else {
return null;
}
};
const play = (options) => {
stop(); // close before play
var constraints = {};
if (options.deviceId) {
constraints = {
video: { deviceId: options.deviceId },
audio: false
};
}
else {
constraints = {
video: { width: 1280, height: 720 },
audio: false
};
}
if (options.facingMode) {
delete constraints["video"]["deviceId"];
constraints["video"]["facingMode"] = { exact: options.facingMode };
}
if (options.desiredResolution) {
constraints["video"]["width"] = options.desiredResolution.width;
constraints["video"]["height"] = options.desiredResolution.height;
}
navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
localStream.current = stream;
// Attach local stream to video element
camera.current.srcObject = stream;
}).catch(function (err) {
if (options.facingMode) { // facing mode not supported on desktop Chrome
delete options["facingMode"];
play(options);
}
else {
console.error('getUserMediaError', err, err.stack);
}
});
};
const stop = () => {
try {
if (localStream.current) {
const stream = localStream.current;
const tracks = stream.getTracks();
for (let index = 0; index < tracks.length; index++) {
const track = tracks[index];
track.stop();
}
if (props.onClosed) {
props.onClosed();
}
}
}
catch (e) {
console.log(e);
}
};
const loadDevices = () => __awaiter(void 0, void 0, void 0, function* () {
const constraints = { video: true, audio: false };
const stream = yield navigator.mediaDevices.getUserMedia(constraints); // ask for permission
const mediaDevices = yield navigator.mediaDevices.enumerateDevices();
let cameraDevices = [];
for (let i = 0; i < mediaDevices.length; i++) {
let device = mediaDevices[i];
if (device.kind == 'videoinput') { // filter out audio devices
cameraDevices.push(device);
}
}
devices.current = cameraDevices;
const tracks = stream.getTracks();
for (let i = 0; i < tracks.length; i++) {
const track = tracks[i];
track.stop(); // stop the opened camera
}
if (props.onDeviceListLoaded) {
props.onDeviceListLoaded(cameraDevices);
}
});
const onCameraOpened = () => {
console.log("onCameraOpened");
if (props.onOpened) {
props.onOpened(camera.current, getCurrentCameraLabel());
}
};
const getCurrentCameraLabel = () => {
try {
if (localStream.current) {
const stream = localStream.current;
return stream.getTracks()[0].label;
}
}
catch (error) {
return "";
}
};
return (react_1.default.createElement("div", { style: { position: "relative", width: "100%", height: "100%", left: 0, top: 0 } },
react_1.default.createElement("video", { style: { position: "absolute", objectFit: "cover", width: "100%", height: "100%", left: 0, top: 0 }, ref: camera, muted: true, autoPlay: true, playsInline: true, onLoadedData: onCameraOpened }),
props.children));
};
exports.default = VisionCamera;