From a6471785fd4f2c68ccd3ed3bc925fce5924ae41f Mon Sep 17 00:00:00 2001 From: salembream Date: Tue, 28 Jan 2025 22:56:01 +0300 Subject: [PATCH 1/2] force highes resolution --- src/components/Camera/Camera.tsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/components/Camera/Camera.tsx b/src/components/Camera/Camera.tsx index 3e79223..8d63969 100644 --- a/src/components/Camera/Camera.tsx +++ b/src/components/Camera/Camera.tsx @@ -259,9 +259,9 @@ const initCameraStream = async ( if (navigator?.mediaDevices?.getUserMedia) { navigator.mediaDevices .getUserMedia(constraints) - .then((stream) => { + .then(async (stream) => { if (isMounted) { - setStream(handleSuccess(stream, setNumberOfCameras)); + setStream(await handleSuccess(stream, setNumberOfCameras)); } }) .catch((err) => { @@ -279,7 +279,7 @@ const initCameraStream = async ( constraints, async (stream) => { if (isMounted) { - setStream(handleSuccess(stream, setNumberOfCameras)); + setStream(await handleSuccess(stream, setNumberOfCameras)); } }, (err) => { @@ -292,7 +292,17 @@ const initCameraStream = async ( } }; -const handleSuccess = (stream: MediaStream, setNumberOfCameras: SetNumberOfCameras) => { +const handleSuccess = async (stream: MediaStream, setNumberOfCameras: SetNumberOfCameras) => { + + const tr = stream.getVideoTracks()[0]; + + const cp = tr.getCapabilities(); + await tr.applyConstraints({ + + width: cp.width?.max, + height: cp.height?.max, + }); + navigator.mediaDevices .enumerateDevices() .then((r) => setNumberOfCameras(r.filter((i) => i.kind === 'videoinput').length)); From 140a357a1961032f4388496f56f982572405630d Mon Sep 17 00:00:00 2001 From: salembream Date: Tue, 28 Jan 2025 23:51:14 +0300 Subject: [PATCH 2/2] dist --- dist/components/Camera/types.d.ts | 4 +- dist/index.cjs.js | 286 ++++++++++++++++++++++++------ dist/index.esm.js | 286 ++++++++++++++++++++++++------ example/src/Camera/index.js | 81 +++++++-- 4 files changed, 525 insertions(+), 132 deletions(-) diff --git a/dist/components/Camera/types.d.ts b/dist/components/Camera/types.d.ts index 975bb80..eaacf5b 100644 --- a/dist/components/Camera/types.d.ts +++ b/dist/components/Camera/types.d.ts @@ -20,7 +20,9 @@ export interface CameraProps { videoReadyCallback?(): void; } export declare type CameraType = React.ForwardRefExoticComponent> & { - takePhoto(): string; + takePhoto(type?: 'base64url' | 'imgData'): string | ImageData; switchCamera(): FacingMode; getNumberOfCameras(): number; + toggleTorch(): boolean; + torchSupported: boolean; }; diff --git a/dist/index.cjs.js b/dist/index.cjs.js index f38882e..2421e94 100644 --- a/dist/index.cjs.js +++ b/dist/index.cjs.js @@ -23,6 +23,43 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ +function __awaiter(thisArg, _arguments, P, generator) { + 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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +} + +function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +} + function __makeTemplateObject(cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; @@ -43,6 +80,7 @@ var Cam = styled.video(templateObject_4 || (templateObject_4 = __makeTemplateObj }); var Canvas = styled.canvas(templateObject_5 || (templateObject_5 = __makeTemplateObject(["\n display: none;\n"], ["\n display: none;\n"]))); var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5; +//# sourceMappingURL=styles.js.map var Camera = React__default.forwardRef(function (_a, ref) { var _b = _a.facingMode, facingMode = _b === void 0 ? 'user' : _b, _c = _a.aspectRatio, aspectRatio = _c === void 0 ? 'cover' : _c, _d = _a.numberOfCamerasCallback, numberOfCamerasCallback = _d === void 0 ? function () { return null; } : _d, _e = _a.videoSourceDeviceId, videoSourceDeviceId = _e === void 0 ? undefined : _e, _f = _a.errorMessages, errorMessages = _f === void 0 ? { @@ -53,18 +91,57 @@ var Camera = React__default.forwardRef(function (_a, ref) { } : _f, _g = _a.videoReadyCallback, videoReadyCallback = _g === void 0 ? function () { return null; } : _g; var player = React.useRef(null); var canvas = React.useRef(null); + var context = React.useRef(null); var container = React.useRef(null); var _h = React.useState(0), numberOfCameras = _h[0], setNumberOfCameras = _h[1]; var _j = React.useState(null), stream = _j[0], setStream = _j[1]; var _k = React.useState(facingMode), currentFacingMode = _k[0], setFacingMode = _k[1]; var _l = React.useState(false), notSupported = _l[0], setNotSupported = _l[1]; var _m = React.useState(false), permissionDenied = _m[0], setPermissionDenied = _m[1]; + var _o = React.useState(false), torchSupported = _o[0], setTorchSupported = _o[1]; + var _p = React.useState(false), torch = _p[0], setTorch = _p[1]; + var mounted = React.useRef(false); + React.useEffect(function () { + mounted.current = true; + return function () { + mounted.current = false; + }; + }, []); React.useEffect(function () { numberOfCamerasCallback(numberOfCameras); }, [numberOfCameras]); + var switchTorch = function (on) { + if (on === void 0) { on = false; } + return __awaiter(void 0, void 0, void 0, function () { + var supportedConstraints, track, _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!(stream && (navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) && !!mounted.current)) return [3 /*break*/, 4]; + supportedConstraints = navigator.mediaDevices.getSupportedConstraints(); + track = stream.getTracks()[0]; + if (!(supportedConstraints && 'torch' in supportedConstraints && track)) return [3 /*break*/, 4]; + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + return [4 /*yield*/, track.applyConstraints({ advanced: [{ torch: on }] })]; + case 2: + _b.sent(); + return [2 /*return*/, true]; + case 3: + _a = _b.sent(); + return [2 /*return*/, false]; + case 4: return [2 /*return*/, false]; + } + }); + }); + }; + React.useEffect(function () { + switchTorch(torch); + }, [torch]); React.useImperativeHandle(ref, function () { return ({ - takePhoto: function () { - var _a, _b, _c, _d; + takePhoto: function (type) { + var _a, _b, _c, _d, _e; if (numberOfCameras < 1) { throw new Error(errorMessages.noCameraAccessible); } @@ -75,7 +152,7 @@ var Camera = React__default.forwardRef(function (_a, ref) { var canvasWidth = ((_c = container === null || container === void 0 ? void 0 : container.current) === null || _c === void 0 ? void 0 : _c.offsetWidth) || 1280; var canvasHeight = ((_d = container === null || container === void 0 ? void 0 : container.current) === null || _d === void 0 ? void 0 : _d.offsetHeight) || 1280; var canvasAR = canvasWidth / canvasHeight; - var sX = void 0, sY = void 0, sW = void 0, sH = void 0; + var sX = void 0, sY = void 0, sW = void 0, sH = void 0, imgData = void 0; if (playerAR > canvasAR) { sH = playerHeight; sW = playerHeight * canvasAR; @@ -90,11 +167,20 @@ var Camera = React__default.forwardRef(function (_a, ref) { } canvas.current.width = sW; canvas.current.height = sH; - var context = canvas.current.getContext('2d'); - if (context && (player === null || player === void 0 ? void 0 : player.current)) { - context.drawImage(player.current, sX, sY, sW, sH, 0, 0, sW, sH); + if (!context.current) { + context.current = canvas.current.getContext('2d', { willReadFrequently: true }); + } + if (context.current && (player === null || player === void 0 ? void 0 : player.current)) { + context.current.drawImage(player.current, sX, sY, sW, sH, 0, 0, sW, sH); + } + switch (type) { + case 'imgData': + imgData = (_e = context.current) === null || _e === void 0 ? void 0 : _e.getImageData(0, 0, sW, sH); + break; + default: /* base64url */ + imgData = canvas.current.toDataURL('image/jpeg'); + break; } - var imgData = canvas.current.toDataURL('image/jpeg'); return imgData; } else { @@ -115,11 +201,18 @@ var Camera = React__default.forwardRef(function (_a, ref) { getNumberOfCameras: function () { return numberOfCameras; }, + toggleTorch: function () { + var torchVal = !torch; + setTorch(torchVal); + return torchVal; + }, + torchSupported: torchSupported, }); }); React.useEffect(function () { - initCameraStream(stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied); + initCameraStream(stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied, !!mounted.current); }, [currentFacingMode, videoSourceDeviceId]); React.useEffect(function () { + switchTorch(false).then(function (success) { return setTorchSupported(success); }); if (stream && player && player.current) { player.current.srcObject = stream; } @@ -141,57 +234,137 @@ var Camera = React__default.forwardRef(function (_a, ref) { React__default.createElement(Canvas, { ref: canvas })))); }); Camera.displayName = 'Camera'; -var initCameraStream = function (stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied) { +var shouldSwitchToCamera = function (currentFacingMode) { return __awaiter(void 0, void 0, void 0, function () { + var cameras; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + cameras = []; + if (!(currentFacingMode === 'environment')) return [3 /*break*/, 2]; + return [4 /*yield*/, navigator.mediaDevices.enumerateDevices().then(function (devices) { + var videoDevices = devices.filter(function (i) { return i.kind == 'videoinput'; }); + videoDevices.forEach(function (device) { + var capabilities = device.getCapabilities(); + if (capabilities.facingMode && capabilities.facingMode.indexOf('environment') >= 0 && capabilities.deviceId) { + cameras.push(capabilities.deviceId); + } + }); + })]; + case 1: + _a.sent(); + _a.label = 2; + case 2: + if (cameras.length > 1) { + return [2 /*return*/, cameras.pop()]; + } + return [2 /*return*/, undefined]; + } + }); +}); }; +var initCameraStream = function (stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied, isMounted) { return __awaiter(void 0, void 0, void 0, function () { + var cameraDeviceId, switchToCamera, constraints, getWebcam; var _a; - // stop any active streams in the window - if (stream) { - stream.getTracks().forEach(function (track) { - track.stop(); - }); - } - var constraints = { - audio: false, - video: { - deviceId: videoSourceDeviceId ? { exact: videoSourceDeviceId } : undefined, - facingMode: currentFacingMode, - width: { ideal: 1920 }, - height: { ideal: 1920 }, - }, - }; - if ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) { - navigator.mediaDevices - .getUserMedia(constraints) - .then(function (stream) { - setStream(handleSuccess(stream, setNumberOfCameras)); - }) - .catch(function (err) { - handleError(err, setNotSupported, setPermissionDenied); - }); - } - else { - var getWebcam = navigator.getUserMedia || - navigator.webkitGetUserMedia || - navigator.mozGetUserMedia || - navigator.mozGetUserMedia || - navigator.msGetUserMedia; - if (getWebcam) { - getWebcam(constraints, function (stream) { - setStream(handleSuccess(stream, setNumberOfCameras)); - }, function (err) { - handleError(err, setNotSupported, setPermissionDenied); - }); + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + // stop any active streams in the window + if (stream) { + stream.getTracks().forEach(function (track) { + track.stop(); + }); + } + return [4 /*yield*/, shouldSwitchToCamera(currentFacingMode)]; + case 1: + switchToCamera = _b.sent(); + if (switchToCamera) { + cameraDeviceId = switchToCamera; + } + else { + cameraDeviceId = videoSourceDeviceId ? { exact: videoSourceDeviceId } : undefined; + } + constraints = { + audio: false, + video: { + deviceId: cameraDeviceId, + facingMode: currentFacingMode, + }, + }; + if ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) { + navigator.mediaDevices + .getUserMedia(constraints) + .then(function (stream) { return __awaiter(void 0, void 0, void 0, function () { + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!isMounted) return [3 /*break*/, 2]; + _a = setStream; + return [4 /*yield*/, handleSuccess(stream, setNumberOfCameras)]; + case 1: + _a.apply(void 0, [_b.sent()]); + _b.label = 2; + case 2: return [2 /*return*/]; + } + }); + }); }) + .catch(function (err) { + handleError(err, setNotSupported, setPermissionDenied); + }); + } + else { + getWebcam = navigator.getUserMedia || + navigator.webkitGetUserMedia || + navigator.mozGetUserMedia || + navigator.mozGetUserMedia || + navigator.msGetUserMedia; + if (getWebcam) { + getWebcam(constraints, function (stream) { return __awaiter(void 0, void 0, void 0, function () { + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!isMounted) return [3 /*break*/, 2]; + _a = setStream; + return [4 /*yield*/, handleSuccess(stream, setNumberOfCameras)]; + case 1: + _a.apply(void 0, [_b.sent()]); + _b.label = 2; + case 2: return [2 /*return*/]; + } + }); + }); }, function (err) { + handleError(err, setNotSupported, setPermissionDenied); + }); + } + else { + setNotSupported(true); + } + } + return [2 /*return*/]; } - else { - setNotSupported(true); + }); +}); }; +var handleSuccess = function (stream, setNumberOfCameras) { return __awaiter(void 0, void 0, void 0, function () { + var tr, cp; + var _a, _b; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + tr = stream.getVideoTracks()[0]; + cp = tr.getCapabilities(); + return [4 /*yield*/, tr.applyConstraints({ + width: (_a = cp.width) === null || _a === void 0 ? void 0 : _a.max, + height: (_b = cp.height) === null || _b === void 0 ? void 0 : _b.max, + })]; + case 1: + _c.sent(); + navigator.mediaDevices + .enumerateDevices() + .then(function (r) { return setNumberOfCameras(r.filter(function (i) { return i.kind === 'videoinput'; }).length); }); + return [2 /*return*/, stream]; } - } -}; -var handleSuccess = function (stream, setNumberOfCameras) { - navigator.mediaDevices - .enumerateDevices() - .then(function (r) { return setNumberOfCameras(r.filter(function (i) { return i.kind === 'videoinput'; }).length); }); - return stream; -}; + }); +}); }; var handleError = function (error, setNotSupported, setPermissionDenied) { console.error(error); //https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia @@ -202,5 +375,6 @@ var handleError = function (error, setNotSupported, setPermissionDenied) { setNotSupported(true); } }; +//# sourceMappingURL=Camera.js.map exports.Camera = Camera; diff --git a/dist/index.esm.js b/dist/index.esm.js index 8459a52..eb922a7 100644 --- a/dist/index.esm.js +++ b/dist/index.esm.js @@ -16,6 +16,43 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ +function __awaiter(thisArg, _arguments, P, generator) { + 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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +} + +function __generator(thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +} + function __makeTemplateObject(cooked, raw) { if (Object.defineProperty) { Object.defineProperty(cooked, "raw", { value: raw }); } else { cooked.raw = raw; } return cooked; @@ -36,6 +73,7 @@ var Cam = styled.video(templateObject_4 || (templateObject_4 = __makeTemplateObj }); var Canvas = styled.canvas(templateObject_5 || (templateObject_5 = __makeTemplateObject(["\n display: none;\n"], ["\n display: none;\n"]))); var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5; +//# sourceMappingURL=styles.js.map var Camera = React.forwardRef(function (_a, ref) { var _b = _a.facingMode, facingMode = _b === void 0 ? 'user' : _b, _c = _a.aspectRatio, aspectRatio = _c === void 0 ? 'cover' : _c, _d = _a.numberOfCamerasCallback, numberOfCamerasCallback = _d === void 0 ? function () { return null; } : _d, _e = _a.videoSourceDeviceId, videoSourceDeviceId = _e === void 0 ? undefined : _e, _f = _a.errorMessages, errorMessages = _f === void 0 ? { @@ -46,18 +84,57 @@ var Camera = React.forwardRef(function (_a, ref) { } : _f, _g = _a.videoReadyCallback, videoReadyCallback = _g === void 0 ? function () { return null; } : _g; var player = useRef(null); var canvas = useRef(null); + var context = useRef(null); var container = useRef(null); var _h = useState(0), numberOfCameras = _h[0], setNumberOfCameras = _h[1]; var _j = useState(null), stream = _j[0], setStream = _j[1]; var _k = useState(facingMode), currentFacingMode = _k[0], setFacingMode = _k[1]; var _l = useState(false), notSupported = _l[0], setNotSupported = _l[1]; var _m = useState(false), permissionDenied = _m[0], setPermissionDenied = _m[1]; + var _o = useState(false), torchSupported = _o[0], setTorchSupported = _o[1]; + var _p = useState(false), torch = _p[0], setTorch = _p[1]; + var mounted = useRef(false); + useEffect(function () { + mounted.current = true; + return function () { + mounted.current = false; + }; + }, []); useEffect(function () { numberOfCamerasCallback(numberOfCameras); }, [numberOfCameras]); + var switchTorch = function (on) { + if (on === void 0) { on = false; } + return __awaiter(void 0, void 0, void 0, function () { + var supportedConstraints, track, _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!(stream && (navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) && !!mounted.current)) return [3 /*break*/, 4]; + supportedConstraints = navigator.mediaDevices.getSupportedConstraints(); + track = stream.getTracks()[0]; + if (!(supportedConstraints && 'torch' in supportedConstraints && track)) return [3 /*break*/, 4]; + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + return [4 /*yield*/, track.applyConstraints({ advanced: [{ torch: on }] })]; + case 2: + _b.sent(); + return [2 /*return*/, true]; + case 3: + _a = _b.sent(); + return [2 /*return*/, false]; + case 4: return [2 /*return*/, false]; + } + }); + }); + }; + useEffect(function () { + switchTorch(torch); + }, [torch]); useImperativeHandle(ref, function () { return ({ - takePhoto: function () { - var _a, _b, _c, _d; + takePhoto: function (type) { + var _a, _b, _c, _d, _e; if (numberOfCameras < 1) { throw new Error(errorMessages.noCameraAccessible); } @@ -68,7 +145,7 @@ var Camera = React.forwardRef(function (_a, ref) { var canvasWidth = ((_c = container === null || container === void 0 ? void 0 : container.current) === null || _c === void 0 ? void 0 : _c.offsetWidth) || 1280; var canvasHeight = ((_d = container === null || container === void 0 ? void 0 : container.current) === null || _d === void 0 ? void 0 : _d.offsetHeight) || 1280; var canvasAR = canvasWidth / canvasHeight; - var sX = void 0, sY = void 0, sW = void 0, sH = void 0; + var sX = void 0, sY = void 0, sW = void 0, sH = void 0, imgData = void 0; if (playerAR > canvasAR) { sH = playerHeight; sW = playerHeight * canvasAR; @@ -83,11 +160,20 @@ var Camera = React.forwardRef(function (_a, ref) { } canvas.current.width = sW; canvas.current.height = sH; - var context = canvas.current.getContext('2d'); - if (context && (player === null || player === void 0 ? void 0 : player.current)) { - context.drawImage(player.current, sX, sY, sW, sH, 0, 0, sW, sH); + if (!context.current) { + context.current = canvas.current.getContext('2d', { willReadFrequently: true }); + } + if (context.current && (player === null || player === void 0 ? void 0 : player.current)) { + context.current.drawImage(player.current, sX, sY, sW, sH, 0, 0, sW, sH); + } + switch (type) { + case 'imgData': + imgData = (_e = context.current) === null || _e === void 0 ? void 0 : _e.getImageData(0, 0, sW, sH); + break; + default: /* base64url */ + imgData = canvas.current.toDataURL('image/jpeg'); + break; } - var imgData = canvas.current.toDataURL('image/jpeg'); return imgData; } else { @@ -108,11 +194,18 @@ var Camera = React.forwardRef(function (_a, ref) { getNumberOfCameras: function () { return numberOfCameras; }, + toggleTorch: function () { + var torchVal = !torch; + setTorch(torchVal); + return torchVal; + }, + torchSupported: torchSupported, }); }); useEffect(function () { - initCameraStream(stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied); + initCameraStream(stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied, !!mounted.current); }, [currentFacingMode, videoSourceDeviceId]); useEffect(function () { + switchTorch(false).then(function (success) { return setTorchSupported(success); }); if (stream && player && player.current) { player.current.srcObject = stream; } @@ -134,57 +227,137 @@ var Camera = React.forwardRef(function (_a, ref) { React.createElement(Canvas, { ref: canvas })))); }); Camera.displayName = 'Camera'; -var initCameraStream = function (stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied) { +var shouldSwitchToCamera = function (currentFacingMode) { return __awaiter(void 0, void 0, void 0, function () { + var cameras; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + cameras = []; + if (!(currentFacingMode === 'environment')) return [3 /*break*/, 2]; + return [4 /*yield*/, navigator.mediaDevices.enumerateDevices().then(function (devices) { + var videoDevices = devices.filter(function (i) { return i.kind == 'videoinput'; }); + videoDevices.forEach(function (device) { + var capabilities = device.getCapabilities(); + if (capabilities.facingMode && capabilities.facingMode.indexOf('environment') >= 0 && capabilities.deviceId) { + cameras.push(capabilities.deviceId); + } + }); + })]; + case 1: + _a.sent(); + _a.label = 2; + case 2: + if (cameras.length > 1) { + return [2 /*return*/, cameras.pop()]; + } + return [2 /*return*/, undefined]; + } + }); +}); }; +var initCameraStream = function (stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied, isMounted) { return __awaiter(void 0, void 0, void 0, function () { + var cameraDeviceId, switchToCamera, constraints, getWebcam; var _a; - // stop any active streams in the window - if (stream) { - stream.getTracks().forEach(function (track) { - track.stop(); - }); - } - var constraints = { - audio: false, - video: { - deviceId: videoSourceDeviceId ? { exact: videoSourceDeviceId } : undefined, - facingMode: currentFacingMode, - width: { ideal: 1920 }, - height: { ideal: 1920 }, - }, - }; - if ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) { - navigator.mediaDevices - .getUserMedia(constraints) - .then(function (stream) { - setStream(handleSuccess(stream, setNumberOfCameras)); - }) - .catch(function (err) { - handleError(err, setNotSupported, setPermissionDenied); - }); - } - else { - var getWebcam = navigator.getUserMedia || - navigator.webkitGetUserMedia || - navigator.mozGetUserMedia || - navigator.mozGetUserMedia || - navigator.msGetUserMedia; - if (getWebcam) { - getWebcam(constraints, function (stream) { - setStream(handleSuccess(stream, setNumberOfCameras)); - }, function (err) { - handleError(err, setNotSupported, setPermissionDenied); - }); + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + // stop any active streams in the window + if (stream) { + stream.getTracks().forEach(function (track) { + track.stop(); + }); + } + return [4 /*yield*/, shouldSwitchToCamera(currentFacingMode)]; + case 1: + switchToCamera = _b.sent(); + if (switchToCamera) { + cameraDeviceId = switchToCamera; + } + else { + cameraDeviceId = videoSourceDeviceId ? { exact: videoSourceDeviceId } : undefined; + } + constraints = { + audio: false, + video: { + deviceId: cameraDeviceId, + facingMode: currentFacingMode, + }, + }; + if ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) { + navigator.mediaDevices + .getUserMedia(constraints) + .then(function (stream) { return __awaiter(void 0, void 0, void 0, function () { + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!isMounted) return [3 /*break*/, 2]; + _a = setStream; + return [4 /*yield*/, handleSuccess(stream, setNumberOfCameras)]; + case 1: + _a.apply(void 0, [_b.sent()]); + _b.label = 2; + case 2: return [2 /*return*/]; + } + }); + }); }) + .catch(function (err) { + handleError(err, setNotSupported, setPermissionDenied); + }); + } + else { + getWebcam = navigator.getUserMedia || + navigator.webkitGetUserMedia || + navigator.mozGetUserMedia || + navigator.mozGetUserMedia || + navigator.msGetUserMedia; + if (getWebcam) { + getWebcam(constraints, function (stream) { return __awaiter(void 0, void 0, void 0, function () { + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!isMounted) return [3 /*break*/, 2]; + _a = setStream; + return [4 /*yield*/, handleSuccess(stream, setNumberOfCameras)]; + case 1: + _a.apply(void 0, [_b.sent()]); + _b.label = 2; + case 2: return [2 /*return*/]; + } + }); + }); }, function (err) { + handleError(err, setNotSupported, setPermissionDenied); + }); + } + else { + setNotSupported(true); + } + } + return [2 /*return*/]; } - else { - setNotSupported(true); + }); +}); }; +var handleSuccess = function (stream, setNumberOfCameras) { return __awaiter(void 0, void 0, void 0, function () { + var tr, cp; + var _a, _b; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + tr = stream.getVideoTracks()[0]; + cp = tr.getCapabilities(); + return [4 /*yield*/, tr.applyConstraints({ + width: (_a = cp.width) === null || _a === void 0 ? void 0 : _a.max, + height: (_b = cp.height) === null || _b === void 0 ? void 0 : _b.max, + })]; + case 1: + _c.sent(); + navigator.mediaDevices + .enumerateDevices() + .then(function (r) { return setNumberOfCameras(r.filter(function (i) { return i.kind === 'videoinput'; }).length); }); + return [2 /*return*/, stream]; } - } -}; -var handleSuccess = function (stream, setNumberOfCameras) { - navigator.mediaDevices - .enumerateDevices() - .then(function (r) { return setNumberOfCameras(r.filter(function (i) { return i.kind === 'videoinput'; }).length); }); - return stream; -}; + }); +}); }; var handleError = function (error, setNotSupported, setPermissionDenied) { console.error(error); //https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia @@ -195,5 +368,6 @@ var handleError = function (error, setNotSupported, setPermissionDenied) { setNotSupported(true); } }; +//# sourceMappingURL=Camera.js.map export { Camera }; diff --git a/example/src/Camera/index.js b/example/src/Camera/index.js index 3edb36f..e240f15 100644 --- a/example/src/Camera/index.js +++ b/example/src/Camera/index.js @@ -73,7 +73,8 @@ var Cam = styled.video(templateObject_4 || (templateObject_4 = __makeTemplateObj return (mirrored ? '180deg' : '0deg'); }); var Canvas = styled.canvas(templateObject_5 || (templateObject_5 = __makeTemplateObject(["\n display: none;\n"], ["\n display: none;\n"]))); -var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5; +var templateObject_1, templateObject_2, templateObject_3, templateObject_4, templateObject_5; +//# sourceMappingURL=styles.js.map var Camera = React.forwardRef(function (_a, ref) { var _b = _a.facingMode, facingMode = _b === void 0 ? 'user' : _b, _c = _a.aspectRatio, aspectRatio = _c === void 0 ? 'cover' : _c, _d = _a.numberOfCamerasCallback, numberOfCamerasCallback = _d === void 0 ? function () { return null; } : _d, _e = _a.videoSourceDeviceId, videoSourceDeviceId = _e === void 0 ? undefined : _e, _f = _a.errorMessages, errorMessages = _f === void 0 ? { @@ -93,6 +94,13 @@ var Camera = React.forwardRef(function (_a, ref) { var _m = useState(false), permissionDenied = _m[0], setPermissionDenied = _m[1]; var _o = useState(false), torchSupported = _o[0], setTorchSupported = _o[1]; var _p = useState(false), torch = _p[0], setTorch = _p[1]; + var mounted = useRef(false); + useEffect(function () { + mounted.current = true; + return function () { + mounted.current = false; + }; + }, []); useEffect(function () { numberOfCamerasCallback(numberOfCameras); }, [numberOfCameras]); @@ -103,7 +111,7 @@ var Camera = React.forwardRef(function (_a, ref) { return __generator(this, function (_b) { switch (_b.label) { case 0: - if (!(stream && (navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices))) return [3 /*break*/, 4]; + if (!(stream && (navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) && !!mounted.current)) return [3 /*break*/, 4]; supportedConstraints = navigator.mediaDevices.getSupportedConstraints(); track = stream.getTracks()[0]; if (!(supportedConstraints && 'torch' in supportedConstraints && track)) return [3 /*break*/, 4]; @@ -195,7 +203,7 @@ var Camera = React.forwardRef(function (_a, ref) { torchSupported: torchSupported, }); }); useEffect(function () { - initCameraStream(stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied); + initCameraStream(stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied, !!mounted.current); }, [currentFacingMode, videoSourceDeviceId]); useEffect(function () { switchTorch(false).then(function (success) { return setTorchSupported(success); }); @@ -247,7 +255,7 @@ var shouldSwitchToCamera = function (currentFacingMode) { return __awaiter(void } }); }); }; -var initCameraStream = function (stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied) { return __awaiter(void 0, void 0, void 0, function () { +var initCameraStream = function (stream, setStream, currentFacingMode, videoSourceDeviceId, setNumberOfCameras, setNotSupported, setPermissionDenied, isMounted) { return __awaiter(void 0, void 0, void 0, function () { var cameraDeviceId, switchToCamera, constraints, getWebcam; var _a; return __generator(this, function (_b) { @@ -273,16 +281,26 @@ var initCameraStream = function (stream, setStream, currentFacingMode, videoSour video: { deviceId: cameraDeviceId, facingMode: currentFacingMode, - width: { ideal: 1920 }, - height: { ideal: 1920 }, }, }; if ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.mediaDevices) === null || _a === void 0 ? void 0 : _a.getUserMedia) { navigator.mediaDevices .getUserMedia(constraints) - .then(function (stream) { - setStream(handleSuccess(stream, setNumberOfCameras)); - }) + .then(function (stream) { return __awaiter(void 0, void 0, void 0, function () { + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!isMounted) return [3 /*break*/, 2]; + _a = setStream; + return [4 /*yield*/, handleSuccess(stream, setNumberOfCameras)]; + case 1: + _a.apply(void 0, [_b.sent()]); + _b.label = 2; + case 2: return [2 /*return*/]; + } + }); + }); }) .catch(function (err) { handleError(err, setNotSupported, setPermissionDenied); }); @@ -295,9 +313,18 @@ var initCameraStream = function (stream, setStream, currentFacingMode, videoSour navigator.msGetUserMedia; if (getWebcam) { getWebcam(constraints, function (stream) { return __awaiter(void 0, void 0, void 0, function () { - return __generator(this, function (_a) { - setStream(handleSuccess(stream, setNumberOfCameras)); - return [2 /*return*/]; + var _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!isMounted) return [3 /*break*/, 2]; + _a = setStream; + return [4 /*yield*/, handleSuccess(stream, setNumberOfCameras)]; + case 1: + _a.apply(void 0, [_b.sent()]); + _b.label = 2; + case 2: return [2 /*return*/]; + } }); }); }, function (err) { handleError(err, setNotSupported, setPermissionDenied); @@ -311,12 +338,27 @@ var initCameraStream = function (stream, setStream, currentFacingMode, videoSour } }); }); }; -var handleSuccess = function (stream, setNumberOfCameras) { - navigator.mediaDevices - .enumerateDevices() - .then(function (r) { return setNumberOfCameras(r.filter(function (i) { return i.kind === 'videoinput'; }).length); }); - return stream; -}; +var handleSuccess = function (stream, setNumberOfCameras) { return __awaiter(void 0, void 0, void 0, function () { + var tr, cp; + var _a, _b; + return __generator(this, function (_c) { + switch (_c.label) { + case 0: + tr = stream.getVideoTracks()[0]; + cp = tr.getCapabilities(); + return [4 /*yield*/, tr.applyConstraints({ + width: (_a = cp.width) === null || _a === void 0 ? void 0 : _a.max, + height: (_b = cp.height) === null || _b === void 0 ? void 0 : _b.max, + })]; + case 1: + _c.sent(); + navigator.mediaDevices + .enumerateDevices() + .then(function (r) { return setNumberOfCameras(r.filter(function (i) { return i.kind === 'videoinput'; }).length); }); + return [2 /*return*/, stream]; + } + }); +}); }; var handleError = function (error, setNotSupported, setPermissionDenied) { console.error(error); //https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia @@ -326,6 +368,7 @@ var handleError = function (error, setNotSupported, setPermissionDenied) { else { setNotSupported(true); } -}; +}; +//# sourceMappingURL=Camera.js.map export { Camera };