@monkvision/camera-web 4.5.6 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -8
- package/lib/Camera/Camera.js +4 -0
- package/lib/Camera/CameraHUD.types.d.ts +19 -0
- package/lib/Camera/hooks/useCameraScreenshot.d.ts +1 -1
- package/lib/Camera/hooks/useCameraScreenshot.js +12 -12
- package/lib/Camera/hooks/useCompression.d.ts +1 -1
- package/lib/Camera/hooks/useCompression.js +13 -13
- package/lib/Camera/hooks/useUserMedia.d.ts +9 -3
- package/lib/Camera/hooks/useUserMedia.js +96 -89
- package/lib/hooks/index.d.ts +1 -0
- package/lib/hooks/index.js +17 -0
- package/lib/hooks/useCameraPermission.d.ts +13 -0
- package/lib/hooks/useCameraPermission.js +72 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/package.json +12 -12
package/README.md
CHANGED
|
@@ -172,11 +172,26 @@ Main component exported by this package, displays a Camera preview and the given
|
|
|
172
172
|
Object passed to Camera HUD components that is used to control the camera
|
|
173
173
|
|
|
174
174
|
### Properties
|
|
175
|
-
| Prop | Type
|
|
176
|
-
|
|
177
|
-
| takePicture | () => Promise<MonkPicture>
|
|
178
|
-
|
|
|
179
|
-
|
|
|
180
|
-
|
|
|
181
|
-
|
|
|
182
|
-
|
|
|
175
|
+
| Prop | Type | Description |
|
|
176
|
+
|-------------------|--------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
177
|
+
| takePicture | () => Promise<MonkPicture> | A function that you can call to ask the camera to take a picture. |
|
|
178
|
+
| getImageData | () => ImageData | Function used to take a raw screenshot of the camera stream for manual image processing. Performance tracking is disabled for this method (only available using the `takePicture` method). |
|
|
179
|
+
| compressImage | (image: ImageData) => Promise<MonkPicture> | Function used to compress raw image data into a MonkPicture object. Performance tracking is disabled for this method (only available using the `takePicture` method). |
|
|
180
|
+
| error | UserMediaError | null | The error details if there has been an error when fetching the camera stream. |
|
|
181
|
+
| isLoading | boolean | Boolean indicating if the camera preview is loading. |
|
|
182
|
+
| retry | () => void | A function to retry the camera stream fetching in case of error. |
|
|
183
|
+
| dimensions | PixelDimensions | null | The Camera stream dimensions (`null` if there is no stream). |
|
|
184
|
+
| previewDimensions | PixelDimensions | null | The effective video dimensions of the Camera stream on the client screen (`null` if there is no stream). |
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
## Hooks
|
|
188
|
+
### useCameraPermission
|
|
189
|
+
```tsx
|
|
190
|
+
import { useCameraPermission } from '@monkvision/camera-web';
|
|
191
|
+
|
|
192
|
+
function TestComponent() {
|
|
193
|
+
const { requestCameraPermission } = useCameraPermission();
|
|
194
|
+
return <button onClick={useCameraPermission}>Request Camera Permissions</button>;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
Custom hook that can be used to request the camera permissions on the current device.
|
package/lib/Camera/Camera.js
CHANGED
|
@@ -56,8 +56,12 @@ function Camera(_a) {
|
|
|
56
56
|
}), takePicture = _h.takePicture, isTakePictureLoading = _h.isLoading;
|
|
57
57
|
var isLoading = isPreviewLoading || isTakePictureLoading;
|
|
58
58
|
var cameraPreview = (0, react_1.useMemo)(function () { return ((0, jsx_runtime_1.jsxs)("div", __assign({ style: Camera_styles_1.styles['container'] }, { children: [(0, jsx_runtime_1.jsx)("video", { style: Camera_styles_1.styles['cameraPreview'], ref: videoRef, autoPlay: true, playsInline: true, controls: false, muted: true, "data-testid": 'camera-video-preview' }), (0, jsx_runtime_1.jsx)("canvas", { ref: canvasRef, style: Camera_styles_1.styles['cameraCanvas'], "data-testid": 'camera-canvas' })] }))); }, []);
|
|
59
|
+
var getImageData = (0, react_1.useCallback)(function () { return takeScreenshot(); }, [takeScreenshot]);
|
|
60
|
+
var compressImage = (0, react_1.useCallback)(function (image) { return compress(image); }, [compress]);
|
|
59
61
|
return HUDComponent ? ((0, jsx_runtime_1.jsx)(HUDComponent, __assign({ handle: {
|
|
60
62
|
takePicture: takePicture,
|
|
63
|
+
getImageData: getImageData,
|
|
64
|
+
compressImage: compressImage,
|
|
61
65
|
error: error,
|
|
62
66
|
retry: retry,
|
|
63
67
|
isLoading: isLoading,
|
|
@@ -9,6 +9,25 @@ export interface CameraHandle {
|
|
|
9
9
|
* A function that you can call to ask the camera to take a picture.
|
|
10
10
|
*/
|
|
11
11
|
takePicture: () => Promise<MonkPicture>;
|
|
12
|
+
/**
|
|
13
|
+
* A function that you can call to get the current raw image data displayed on the camera stream. You can use this
|
|
14
|
+
* function if you need to apply a custom procesing to the image pixels and don't want the automatic compression logic
|
|
15
|
+
* of the Camera component. You can use the `handle.compressImage` method to compress the raw image data using the
|
|
16
|
+
* Camera component's compression configuration. If you just want to take a picture normally, use the
|
|
17
|
+
* `handle.takePicture` method.
|
|
18
|
+
*
|
|
19
|
+
* Note: This method does NOT use any monitoring tracking. The only way to enable monitoring is by taking pictures via
|
|
20
|
+
* the `handle.takePicture` method.
|
|
21
|
+
*/
|
|
22
|
+
getImageData: () => ImageData;
|
|
23
|
+
/**
|
|
24
|
+
* A function that you can call to compress a raw ImageData (taken using the `handle.compressImage` function) into a
|
|
25
|
+
* MonkPicture object. This function will use the compression options passed as parameters to the Camera component.
|
|
26
|
+
*
|
|
27
|
+
* Note: This method does NOT use any monitoring tracking. The only way to enable monitoring is by taking pictures via
|
|
28
|
+
* the `handle.takePicture` method.
|
|
29
|
+
*/
|
|
30
|
+
compressImage: (image: ImageData) => Promise<MonkPicture>;
|
|
12
31
|
/**
|
|
13
32
|
* The error details if there has been an error when fetching the camera stream.
|
|
14
33
|
*/
|
|
@@ -23,7 +23,7 @@ export interface CameraScreenshotConfig {
|
|
|
23
23
|
*
|
|
24
24
|
* @return A ImageData object that contains the raw pixel's data.
|
|
25
25
|
*/
|
|
26
|
-
export type TakeScreenshotFunction = (monitoring
|
|
26
|
+
export type TakeScreenshotFunction = (monitoring?: InternalCameraMonitoringConfig) => ImageData;
|
|
27
27
|
/**
|
|
28
28
|
* Custom hook used to take screenshots of a video element. This hook is aimed to be used in pair with a `<canvas>`
|
|
29
29
|
* element referenced by the returned ref.
|
|
@@ -16,10 +16,10 @@ var react_1 = require("react");
|
|
|
16
16
|
var monitoring_1 = require("@monkvision/monitoring");
|
|
17
17
|
var monitoring_2 = require("../monitoring");
|
|
18
18
|
var utils_1 = require("./utils");
|
|
19
|
-
function startScreenshotMeasurement(
|
|
19
|
+
function startScreenshotMeasurement(dimensions, monitoring) {
|
|
20
20
|
var _a;
|
|
21
21
|
var _b, _c;
|
|
22
|
-
(_b = monitoring.transaction) === null || _b === void 0 ? void 0 : _b.startMeasurement(monitoring_2.ScreenshotMeasurement.operation, {
|
|
22
|
+
(_b = monitoring === null || monitoring === void 0 ? void 0 : monitoring.transaction) === null || _b === void 0 ? void 0 : _b.startMeasurement(monitoring_2.ScreenshotMeasurement.operation, {
|
|
23
23
|
data: monitoring.data,
|
|
24
24
|
tags: __assign((_a = {}, _a[monitoring_2.ScreenshotMeasurement.outputResolutionTagName] = dimensions
|
|
25
25
|
? "".concat(dimensions.width, "x").concat(dimensions.height)
|
|
@@ -27,14 +27,14 @@ function startScreenshotMeasurement(monitoring, dimensions) {
|
|
|
27
27
|
description: monitoring_2.ScreenshotMeasurement.description,
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
|
-
function stopScreenshotMeasurement(
|
|
30
|
+
function stopScreenshotMeasurement(status, monitoring) {
|
|
31
31
|
var _a;
|
|
32
|
-
(_a =
|
|
32
|
+
(_a = monitoring === null || monitoring === void 0 ? void 0 : monitoring.transaction) === null || _a === void 0 ? void 0 : _a.stopMeasurement(monitoring_2.ScreenshotMeasurement.operation, status);
|
|
33
33
|
}
|
|
34
|
-
function setScreeshotSizeMeasurement(
|
|
34
|
+
function setScreeshotSizeMeasurement(image, monitoring) {
|
|
35
35
|
var _a;
|
|
36
36
|
var imageSizeBytes = image.data.length;
|
|
37
|
-
(_a = monitoring.transaction) === null || _a === void 0 ? void 0 : _a.setMeasurement(monitoring_2.ScreenshotSizeMeasurement.name, imageSizeBytes, 'byte');
|
|
37
|
+
(_a = monitoring === null || monitoring === void 0 ? void 0 : monitoring.transaction) === null || _a === void 0 ? void 0 : _a.setMeasurement(monitoring_2.ScreenshotSizeMeasurement.name, imageSizeBytes, 'byte');
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
40
|
* Custom hook used to take screenshots of a video element. This hook is aimed to be used in pair with a `<canvas>`
|
|
@@ -43,22 +43,22 @@ function setScreeshotSizeMeasurement(monitoring, image) {
|
|
|
43
43
|
function useCameraScreenshot(_a) {
|
|
44
44
|
var videoRef = _a.videoRef, canvasRef = _a.canvasRef, dimensions = _a.dimensions;
|
|
45
45
|
return (0, react_1.useCallback)(function (monitoring) {
|
|
46
|
-
startScreenshotMeasurement(
|
|
46
|
+
startScreenshotMeasurement(dimensions, monitoring);
|
|
47
47
|
var context = (0, utils_1.getCanvasHandle)(canvasRef, function () {
|
|
48
|
-
return stopScreenshotMeasurement(
|
|
48
|
+
return stopScreenshotMeasurement(monitoring_1.TransactionStatus.UNKNOWN_ERROR, monitoring);
|
|
49
49
|
}).context;
|
|
50
50
|
if (!dimensions) {
|
|
51
|
-
stopScreenshotMeasurement(
|
|
51
|
+
stopScreenshotMeasurement(monitoring_1.TransactionStatus.UNKNOWN_ERROR, monitoring);
|
|
52
52
|
throw new Error('Unable to take a picture because the video stream has no dimension.');
|
|
53
53
|
}
|
|
54
54
|
if (!videoRef.current) {
|
|
55
|
-
stopScreenshotMeasurement(
|
|
55
|
+
stopScreenshotMeasurement(monitoring_1.TransactionStatus.UNKNOWN_ERROR, monitoring);
|
|
56
56
|
throw new Error('Unable to take a picture because the video element is null.');
|
|
57
57
|
}
|
|
58
58
|
context.drawImage(videoRef.current, 0, 0, dimensions.width, dimensions.height);
|
|
59
59
|
var imageData = context.getImageData(0, 0, dimensions.width, dimensions.height);
|
|
60
|
-
setScreeshotSizeMeasurement(
|
|
61
|
-
stopScreenshotMeasurement(
|
|
60
|
+
setScreeshotSizeMeasurement(imageData, monitoring);
|
|
61
|
+
stopScreenshotMeasurement(monitoring_1.TransactionStatus.OK, monitoring);
|
|
62
62
|
return imageData;
|
|
63
63
|
}, [dimensions]);
|
|
64
64
|
}
|
|
@@ -17,7 +17,7 @@ export interface UseCompressionParams {
|
|
|
17
17
|
/**
|
|
18
18
|
* Function used to compress images and create DataURI objects.
|
|
19
19
|
*/
|
|
20
|
-
export type CompressFunction = (image: ImageData, monitoring
|
|
20
|
+
export type CompressFunction = (image: ImageData, monitoring?: InternalCameraMonitoringConfig) => Promise<MonkPicture>;
|
|
21
21
|
/**
|
|
22
22
|
* Custom hook used to manage the camera <canvas> element used to take video screenshots and encode images.
|
|
23
23
|
*/
|
|
@@ -52,25 +52,25 @@ var monitoring_1 = require("@monkvision/monitoring");
|
|
|
52
52
|
var react_1 = require("react");
|
|
53
53
|
var monitoring_2 = require("../monitoring");
|
|
54
54
|
var utils_1 = require("./utils");
|
|
55
|
-
function startCompressionMeasurement(
|
|
55
|
+
function startCompressionMeasurement(options, image, monitoring) {
|
|
56
56
|
var _a;
|
|
57
57
|
var _b, _c;
|
|
58
|
-
(_b = monitoring.transaction) === null || _b === void 0 ? void 0 : _b.startMeasurement(monitoring_2.CompressionMeasurement.operation, {
|
|
59
|
-
data: monitoring.data,
|
|
60
|
-
tags: __assign((_a = {}, _a[monitoring_2.CompressionMeasurement.formatTagName] = options.format, _a[monitoring_2.CompressionMeasurement.qualityTagName] = options.quality, _a[monitoring_2.CompressionMeasurement.dimensionsTagName] = "".concat(image.width, "x").concat(image.height), _a), ((_c = monitoring.tags) !== null && _c !== void 0 ? _c : {})),
|
|
58
|
+
(_b = monitoring === null || monitoring === void 0 ? void 0 : monitoring.transaction) === null || _b === void 0 ? void 0 : _b.startMeasurement(monitoring_2.CompressionMeasurement.operation, {
|
|
59
|
+
data: monitoring === null || monitoring === void 0 ? void 0 : monitoring.data,
|
|
60
|
+
tags: __assign((_a = {}, _a[monitoring_2.CompressionMeasurement.formatTagName] = options.format, _a[monitoring_2.CompressionMeasurement.qualityTagName] = options.quality, _a[monitoring_2.CompressionMeasurement.dimensionsTagName] = "".concat(image.width, "x").concat(image.height), _a), ((_c = monitoring === null || monitoring === void 0 ? void 0 : monitoring.tags) !== null && _c !== void 0 ? _c : {})),
|
|
61
61
|
description: monitoring_2.CompressionMeasurement.description,
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
|
-
function stopCompressionMeasurement(
|
|
64
|
+
function stopCompressionMeasurement(status, monitoring) {
|
|
65
65
|
var _a;
|
|
66
|
-
(_a = monitoring.transaction) === null || _a === void 0 ? void 0 : _a.stopMeasurement(monitoring_2.CompressionMeasurement.operation, status);
|
|
66
|
+
(_a = monitoring === null || monitoring === void 0 ? void 0 : monitoring.transaction) === null || _a === void 0 ? void 0 : _a.stopMeasurement(monitoring_2.CompressionMeasurement.operation, status);
|
|
67
67
|
}
|
|
68
|
-
function setCustomMeasurements(
|
|
68
|
+
function setCustomMeasurements(image, picture, monitoring) {
|
|
69
69
|
var _a, _b;
|
|
70
70
|
var imageSizeBytes = image.data.length;
|
|
71
71
|
var pictureSizeBytes = picture.blob.size;
|
|
72
|
-
(_a = monitoring.transaction) === null || _a === void 0 ? void 0 : _a.setMeasurement(monitoring_2.CompressionSizeRatioMeasurement.name, pictureSizeBytes / imageSizeBytes, 'ratio');
|
|
73
|
-
(_b = monitoring.transaction) === null || _b === void 0 ? void 0 : _b.setMeasurement(monitoring_2.PictureSizeMeasurement.name, pictureSizeBytes, 'byte');
|
|
72
|
+
(_a = monitoring === null || monitoring === void 0 ? void 0 : monitoring.transaction) === null || _a === void 0 ? void 0 : _a.setMeasurement(monitoring_2.CompressionSizeRatioMeasurement.name, pictureSizeBytes / imageSizeBytes, 'ratio');
|
|
73
|
+
(_b = monitoring === null || monitoring === void 0 ? void 0 : monitoring.transaction) === null || _b === void 0 ? void 0 : _b.setMeasurement(monitoring_2.PictureSizeMeasurement.name, pictureSizeBytes, 'byte');
|
|
74
74
|
}
|
|
75
75
|
function compressUsingBrowser(image, canvasRef, options) {
|
|
76
76
|
var _a = (0, utils_1.getCanvasHandle)(canvasRef), canvas = _a.canvas, context = _a.context;
|
|
@@ -103,19 +103,19 @@ function useCompression(_a) {
|
|
|
103
103
|
return __generator(this, function (_a) {
|
|
104
104
|
switch (_a.label) {
|
|
105
105
|
case 0:
|
|
106
|
-
startCompressionMeasurement(
|
|
106
|
+
startCompressionMeasurement(options, image, monitoring);
|
|
107
107
|
_a.label = 1;
|
|
108
108
|
case 1:
|
|
109
109
|
_a.trys.push([1, 3, , 4]);
|
|
110
110
|
return [4 /*yield*/, compressUsingBrowser(image, canvasRef, options)];
|
|
111
111
|
case 2:
|
|
112
112
|
picture = _a.sent();
|
|
113
|
-
setCustomMeasurements(
|
|
114
|
-
stopCompressionMeasurement(
|
|
113
|
+
setCustomMeasurements(image, picture, monitoring);
|
|
114
|
+
stopCompressionMeasurement(monitoring_1.TransactionStatus.OK, monitoring);
|
|
115
115
|
return [2 /*return*/, picture];
|
|
116
116
|
case 3:
|
|
117
117
|
err_1 = _a.sent();
|
|
118
|
-
stopCompressionMeasurement(
|
|
118
|
+
stopCompressionMeasurement(monitoring_1.TransactionStatus.UNKNOWN_ERROR, monitoring);
|
|
119
119
|
throw err_1;
|
|
120
120
|
case 4: return [2 /*return*/];
|
|
121
121
|
}
|
|
@@ -82,6 +82,10 @@ export interface UserMediaError {
|
|
|
82
82
|
* @see useUserMedia
|
|
83
83
|
*/
|
|
84
84
|
export interface UserMediaResult {
|
|
85
|
+
/**
|
|
86
|
+
* The getUserMedia function that can be used to fetch the stream data manually if no videoRef is passed.
|
|
87
|
+
*/
|
|
88
|
+
getUserMedia: () => Promise<MediaStream>;
|
|
85
89
|
/**
|
|
86
90
|
* The resulting video stream. The stream can be null when not initialized or in case of an error.
|
|
87
91
|
*/
|
|
@@ -117,17 +121,19 @@ export interface UserMediaResult {
|
|
|
117
121
|
/**
|
|
118
122
|
* React hook that wraps the `navigator.mediaDevices.getUserMedia` browser function in order to add React logic layers
|
|
119
123
|
* and utility tools :
|
|
120
|
-
* - Creates an effect for `getUserMedia` that will be run everytime some state parameters are updated
|
|
124
|
+
* - Creates an effect for `getUserMedia` that will be run everytime some state parameters are updated (the effect is
|
|
125
|
+
* run only if the videoRef is passed, if not, the `getUserMedia` function must be called manually).
|
|
121
126
|
* - Will call `track.applyConstraints` when the video contstraints are updated in order to update the video stream.
|
|
122
127
|
* - Makes sure that the `getUserMedia` is only called when it needs to be using memoized state.
|
|
123
128
|
* - Provides various utilities such as error catching, loading information and a retry on failure feature.
|
|
124
129
|
*
|
|
125
130
|
* @param constraints The same media constraints you would pass to the `getUserMedia` function. Note that this hook has
|
|
126
131
|
* been designed for video only, so audio constraints could provoke unexpected behaviour.
|
|
127
|
-
* @param videoRef The ref to the video element displaying the camera preview stream.
|
|
132
|
+
* @param videoRef The ref to the video element displaying the camera preview stream. If the ref is not passed, the
|
|
133
|
+
* effect will not automatically be called.
|
|
128
134
|
* @return The result of this hook contains the resulting video stream, an error object if there has been an error, a
|
|
129
135
|
* loading indicator and a retry function that tries to get a camera stream again. See the `UserMediaResult` interface
|
|
130
136
|
* for more information.
|
|
131
137
|
* @see UserMediaResult
|
|
132
138
|
*/
|
|
133
|
-
export declare function useUserMedia(constraints: MediaStreamConstraints, videoRef: RefObject<HTMLVideoElement>): UserMediaResult;
|
|
139
|
+
export declare function useUserMedia(constraints: MediaStreamConstraints, videoRef: RefObject<HTMLVideoElement> | null): UserMediaResult;
|
|
@@ -179,14 +179,16 @@ function getStreamDimensions(stream, checkOrientation) {
|
|
|
179
179
|
/**
|
|
180
180
|
* React hook that wraps the `navigator.mediaDevices.getUserMedia` browser function in order to add React logic layers
|
|
181
181
|
* and utility tools :
|
|
182
|
-
* - Creates an effect for `getUserMedia` that will be run everytime some state parameters are updated
|
|
182
|
+
* - Creates an effect for `getUserMedia` that will be run everytime some state parameters are updated (the effect is
|
|
183
|
+
* run only if the videoRef is passed, if not, the `getUserMedia` function must be called manually).
|
|
183
184
|
* - Will call `track.applyConstraints` when the video contstraints are updated in order to update the video stream.
|
|
184
185
|
* - Makes sure that the `getUserMedia` is only called when it needs to be using memoized state.
|
|
185
186
|
* - Provides various utilities such as error catching, loading information and a retry on failure feature.
|
|
186
187
|
*
|
|
187
188
|
* @param constraints The same media constraints you would pass to the `getUserMedia` function. Note that this hook has
|
|
188
189
|
* been designed for video only, so audio constraints could provoke unexpected behaviour.
|
|
189
|
-
* @param videoRef The ref to the video element displaying the camera preview stream.
|
|
190
|
+
* @param videoRef The ref to the video element displaying the camera preview stream. If the ref is not passed, the
|
|
191
|
+
* effect will not automatically be called.
|
|
190
192
|
* @return The result of this hook contains the resulting video stream, an error object if there has been an error, a
|
|
191
193
|
* loading indicator and a retry function that tries to get a camera stream again. See the `UserMediaResult` interface
|
|
192
194
|
* for more information.
|
|
@@ -202,17 +204,11 @@ function useUserMedia(constraints, videoRef) {
|
|
|
202
204
|
var _f = (0, react_1.useState)(null), selectedCameraDeviceId = _f[0], setSelectedCameraDeviceId = _f[1];
|
|
203
205
|
var _g = (0, react_1.useState)(null), lastConstraintsApplied = _g[0], setLastConstraintsApplied = _g[1];
|
|
204
206
|
var handleError = (0, monitoring_1.useMonitoring)().handleError;
|
|
205
|
-
var
|
|
206
|
-
var
|
|
207
|
-
(0, react_1.useEffect)(function () {
|
|
208
|
-
return function () {
|
|
209
|
-
isActive.current = false;
|
|
210
|
-
};
|
|
211
|
-
}, []);
|
|
212
|
-
var handleGetUserMediaError = function (err) {
|
|
207
|
+
var isMounted = (0, common_1.useIsMounted)();
|
|
208
|
+
var handleGetUserMediaError = function (err, permissionState) {
|
|
213
209
|
var type = UserMediaErrorType.OTHER;
|
|
214
210
|
if (err instanceof Error && err.name === 'NotAllowedError') {
|
|
215
|
-
switch (
|
|
211
|
+
switch (permissionState) {
|
|
216
212
|
case 'denied':
|
|
217
213
|
type = UserMediaErrorType.WEBPAGE_NOT_ALLOWED;
|
|
218
214
|
break;
|
|
@@ -232,11 +228,13 @@ function useUserMedia(constraints, videoRef) {
|
|
|
232
228
|
setIsLoading(false);
|
|
233
229
|
};
|
|
234
230
|
var onStreamInactive = function () {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
231
|
+
if (isMounted()) {
|
|
232
|
+
setError({
|
|
233
|
+
type: UserMediaErrorType.STREAM_INACTIVE,
|
|
234
|
+
nativeError: new Error('The camera stream was closed.'),
|
|
235
|
+
});
|
|
236
|
+
setIsLoading(false);
|
|
237
|
+
}
|
|
240
238
|
};
|
|
241
239
|
var retry = (0, react_1.useCallback)(function () {
|
|
242
240
|
if (error && !isLoading) {
|
|
@@ -246,89 +244,98 @@ function useUserMedia(constraints, videoRef) {
|
|
|
246
244
|
setLastConstraintsApplied(null);
|
|
247
245
|
}
|
|
248
246
|
}, [error, isLoading]);
|
|
249
|
-
(0, react_1.
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
case 1: return [2 /*return*/, _a.sent()];
|
|
293
|
-
case 2:
|
|
294
|
-
err_1 = _a.sent();
|
|
295
|
-
return [2 /*return*/, null];
|
|
296
|
-
case 3: return [2 /*return*/];
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
}); };
|
|
300
|
-
getUserMedia()
|
|
301
|
-
.catch(function (err) {
|
|
302
|
-
return Promise.all([err, getCameraPermissionState()]);
|
|
303
|
-
})
|
|
304
|
-
.then(function (result) {
|
|
305
|
-
if (!result) {
|
|
306
|
-
return Promise.all([null, getCameraPermissionState()]);
|
|
247
|
+
var getUserMedia = (0, react_1.useCallback)(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
248
|
+
var deviceDetails, updatedConstraints, str;
|
|
249
|
+
return __generator(this, function (_a) {
|
|
250
|
+
switch (_a.label) {
|
|
251
|
+
case 0:
|
|
252
|
+
setIsLoading(true);
|
|
253
|
+
if (stream) {
|
|
254
|
+
stream.removeEventListener('inactive', onStreamInactive);
|
|
255
|
+
stream.getTracks().forEach(function (track) { return track.stop(); });
|
|
256
|
+
}
|
|
257
|
+
return [4 /*yield*/, (0, utils_1.analyzeCameraDevices)(constraints)];
|
|
258
|
+
case 1:
|
|
259
|
+
deviceDetails = _a.sent();
|
|
260
|
+
updatedConstraints = __assign(__assign({}, constraints), { video: __assign(__assign({}, (constraints ? constraints.video : {})), { deviceId: { exact: deviceDetails.validDeviceIds } }) });
|
|
261
|
+
return [4 /*yield*/, navigator.mediaDevices.getUserMedia(updatedConstraints)];
|
|
262
|
+
case 2:
|
|
263
|
+
str = _a.sent();
|
|
264
|
+
str === null || str === void 0 ? void 0 : str.addEventListener('inactive', onStreamInactive);
|
|
265
|
+
if (isMounted()) {
|
|
266
|
+
setStream(str);
|
|
267
|
+
setDimensions(getStreamDimensions(str, true));
|
|
268
|
+
setIsLoading(false);
|
|
269
|
+
setAvailableCameraDevices(deviceDetails.availableDevices);
|
|
270
|
+
setSelectedCameraDeviceId(getStreamDeviceId(str));
|
|
271
|
+
}
|
|
272
|
+
return [2 /*return*/, str];
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}); }, [stream, constraints]);
|
|
276
|
+
var getCameraPermissionState = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
277
|
+
var err_1;
|
|
278
|
+
return __generator(this, function (_a) {
|
|
279
|
+
switch (_a.label) {
|
|
280
|
+
case 0:
|
|
281
|
+
_a.trys.push([0, 2, , 3]);
|
|
282
|
+
return [4 /*yield*/, navigator.permissions.query({
|
|
283
|
+
name: 'camera',
|
|
284
|
+
})];
|
|
285
|
+
case 1: return [2 /*return*/, _a.sent()];
|
|
286
|
+
case 2:
|
|
287
|
+
err_1 = _a.sent();
|
|
288
|
+
return [2 /*return*/, null];
|
|
289
|
+
case 3: return [2 /*return*/];
|
|
307
290
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (err && isActive.current) {
|
|
315
|
-
handleGetUserMediaError(err);
|
|
316
|
-
throw err;
|
|
291
|
+
});
|
|
292
|
+
}); };
|
|
293
|
+
(0, react_1.useEffect)(function () {
|
|
294
|
+
if (videoRef) {
|
|
295
|
+
if (error || isLoading || (0, fast_deep_equal_1.default)(lastConstraintsApplied, constraints)) {
|
|
296
|
+
return;
|
|
317
297
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
298
|
+
setLastConstraintsApplied(constraints);
|
|
299
|
+
var effect = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
300
|
+
var err_2, permissionState;
|
|
301
|
+
var _a, _b;
|
|
302
|
+
return __generator(this, function (_c) {
|
|
303
|
+
switch (_c.label) {
|
|
304
|
+
case 0:
|
|
305
|
+
_c.trys.push([0, 2, , 4]);
|
|
306
|
+
return [4 /*yield*/, getUserMedia()];
|
|
307
|
+
case 1:
|
|
308
|
+
_c.sent();
|
|
309
|
+
return [3 /*break*/, 4];
|
|
310
|
+
case 2:
|
|
311
|
+
err_2 = _c.sent();
|
|
312
|
+
return [4 /*yield*/, getCameraPermissionState()];
|
|
313
|
+
case 3:
|
|
314
|
+
permissionState = (_b = (_a = (_c.sent())) === null || _a === void 0 ? void 0 : _a.state) !== null && _b !== void 0 ? _b : null;
|
|
315
|
+
if (err_2 && isMounted()) {
|
|
316
|
+
handleGetUserMediaError(err_2, permissionState);
|
|
317
|
+
throw err_2;
|
|
318
|
+
}
|
|
319
|
+
return [3 /*break*/, 4];
|
|
320
|
+
case 4: return [2 /*return*/];
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}); };
|
|
324
|
+
effect().catch(handleError);
|
|
325
|
+
}
|
|
326
|
+
}, [constraints, stream, error, isLoading, lastConstraintsApplied, getUserMedia, videoRef]);
|
|
321
327
|
(0, react_1.useEffect)(function () {
|
|
322
|
-
if (stream && videoRef.current) {
|
|
328
|
+
if (stream && videoRef && videoRef.current) {
|
|
323
329
|
// eslint-disable-next-line no-param-reassign
|
|
324
330
|
videoRef.current.onresize = function () {
|
|
325
|
-
if (
|
|
331
|
+
if (isMounted()) {
|
|
326
332
|
setDimensions(getStreamDimensions(stream, false));
|
|
327
333
|
}
|
|
328
334
|
};
|
|
329
335
|
}
|
|
330
|
-
}, [stream]);
|
|
336
|
+
}, [stream, videoRef]);
|
|
331
337
|
return (0, common_1.useObjectMemo)({
|
|
338
|
+
getUserMedia: getUserMedia,
|
|
332
339
|
stream: stream,
|
|
333
340
|
dimensions: dimensions,
|
|
334
341
|
error: error,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './useCameraPermission';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./useCameraPermission"), exports);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handle used to request camera permission on the user device.
|
|
3
|
+
*/
|
|
4
|
+
export interface CameraPermissionHandle {
|
|
5
|
+
/**
|
|
6
|
+
* Callback that can be used to request the camera permission on the current device.
|
|
7
|
+
*/
|
|
8
|
+
requestCameraPermission: () => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Custom hook that can be used to request the camera permissions on the current device.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useCameraPermission(): CameraPermissionHandle;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
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;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.useCameraPermission = void 0;
|
|
40
|
+
var react_1 = require("react");
|
|
41
|
+
var common_1 = require("@monkvision/common");
|
|
42
|
+
var types_1 = require("@monkvision/types");
|
|
43
|
+
var Camera_1 = require("../Camera");
|
|
44
|
+
var utils_1 = require("../Camera/hooks/utils");
|
|
45
|
+
var hooks_1 = require("../Camera/hooks");
|
|
46
|
+
/**
|
|
47
|
+
* Custom hook that can be used to request the camera permissions on the current device.
|
|
48
|
+
*/
|
|
49
|
+
function useCameraPermission() {
|
|
50
|
+
var _this = this;
|
|
51
|
+
var contraints = (0, react_1.useMemo)(function () {
|
|
52
|
+
return (0, utils_1.getMediaConstraints)({
|
|
53
|
+
resolution: (0, common_1.isMobileDevice)() ? types_1.CameraResolution.UHD_4K : types_1.CameraResolution.FHD_1080P,
|
|
54
|
+
facingMode: Camera_1.CameraFacingMode.ENVIRONMENT,
|
|
55
|
+
});
|
|
56
|
+
}, []);
|
|
57
|
+
var getUserMedia = (0, hooks_1.useUserMedia)(contraints, null).getUserMedia;
|
|
58
|
+
var requestCameraPermission = (0, react_1.useCallback)(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
59
|
+
var stream;
|
|
60
|
+
return __generator(this, function (_a) {
|
|
61
|
+
switch (_a.label) {
|
|
62
|
+
case 0: return [4 /*yield*/, getUserMedia()];
|
|
63
|
+
case 1:
|
|
64
|
+
stream = _a.sent();
|
|
65
|
+
stream.getTracks().forEach(function (track) { return track.stop(); });
|
|
66
|
+
return [2 /*return*/];
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}); }, [getUserMedia]);
|
|
70
|
+
return (0, common_1.useObjectMemo)({ requestCameraPermission: requestCameraPermission });
|
|
71
|
+
}
|
|
72
|
+
exports.useCameraPermission = useCameraPermission;
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -16,5 +16,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./Camera"), exports);
|
|
18
18
|
__exportStar(require("./SimpleCameraHUD"), exports);
|
|
19
|
+
__exportStar(require("./hooks"), exports);
|
|
19
20
|
__exportStar(require("./utils"), exports);
|
|
20
21
|
__exportStar(require("./i18n"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monkvision/camera-web",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"license": "BSD-3-Clause-Clear",
|
|
5
5
|
"packageManager": "yarn@3.2.4",
|
|
6
6
|
"description": "MonkJs camera package for React (web) used to display a camera preview and take pictures",
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"lint:fix": "yarn run prettier:fix && yarn run eslint:fix"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@monkvision/common": "
|
|
32
|
-
"@monkvision/common-ui-web": "
|
|
33
|
-
"@monkvision/monitoring": "
|
|
31
|
+
"@monkvision/common": "5.0.0",
|
|
32
|
+
"@monkvision/common-ui-web": "5.0.0",
|
|
33
|
+
"@monkvision/monitoring": "5.0.0",
|
|
34
34
|
"fast-deep-equal": "^3.1.3",
|
|
35
35
|
"i18next": "^23.4.5",
|
|
36
36
|
"react-i18next": "^13.2.0"
|
|
@@ -42,13 +42,13 @@
|
|
|
42
42
|
"react-router-dom": "^6.22.3"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@monkvision/eslint-config-base": "
|
|
46
|
-
"@monkvision/eslint-config-typescript": "
|
|
47
|
-
"@monkvision/eslint-config-typescript-react": "
|
|
48
|
-
"@monkvision/jest-config": "
|
|
49
|
-
"@monkvision/prettier-config": "
|
|
50
|
-
"@monkvision/test-utils": "
|
|
51
|
-
"@monkvision/typescript-config": "
|
|
45
|
+
"@monkvision/eslint-config-base": "5.0.0",
|
|
46
|
+
"@monkvision/eslint-config-typescript": "5.0.0",
|
|
47
|
+
"@monkvision/eslint-config-typescript-react": "5.0.0",
|
|
48
|
+
"@monkvision/jest-config": "5.0.0",
|
|
49
|
+
"@monkvision/prettier-config": "5.0.0",
|
|
50
|
+
"@monkvision/test-utils": "5.0.0",
|
|
51
|
+
"@monkvision/typescript-config": "5.0.0",
|
|
52
52
|
"@testing-library/react": "^12.1.5",
|
|
53
53
|
"@testing-library/react-hooks": "^8.0.1",
|
|
54
54
|
"@types/fscreen": "^1.0.1",
|
|
@@ -90,5 +90,5 @@
|
|
|
90
90
|
"url": "https://github.com/monkvision/monkjs/issues"
|
|
91
91
|
},
|
|
92
92
|
"homepage": "https://github.com/monkvision/monkjs",
|
|
93
|
-
"gitHead": "
|
|
93
|
+
"gitHead": "6426efb1b64fde310666b5556fc76aeb7c8f2584"
|
|
94
94
|
}
|