@microblink/camera-manager 7.2.1 → 7.2.3
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/dist/camera-manager.js +182 -48
- package/dist/camera-manager.js.map +1 -1
- package/package.json +1 -1
- package/types/core/Camera.d.ts +73 -0
- package/types/core/Camera.d.ts.map +1 -1
- package/types/core/CameraManager.d.ts +87 -7
- package/types/core/CameraManager.d.ts.map +1 -1
- package/types/core/VideoFrameProcessor.d.ts +28 -6
- package/types/core/VideoFrameProcessor.d.ts.map +1 -1
- package/types/core/cameraError.d.ts +22 -0
- package/types/core/cameraError.d.ts.map +1 -0
- package/types/core/cameraManagerStore.d.ts +10 -3
- package/types/core/cameraManagerStore.d.ts.map +1 -1
- package/types/core/cameraNames.d.ts +21 -5
- package/types/core/cameraNames.d.ts.map +1 -1
- package/types/core/cameraUtils.d.ts +34 -13
- package/types/core/cameraUtils.d.ts.map +1 -1
- package/types/core/iosCameraNames.d.ts +3 -0
- package/types/core/iosCameraNames.d.ts.map +1 -1
- package/types/core/utils.d.ts +18 -0
- package/types/core/utils.d.ts.map +1 -1
- package/types/index.d.ts +11 -1
- package/types/index.d.ts.map +1 -1
- package/types/index.rollup.d.ts +264 -21
- package/types/media-mock/MediaMocker.d.ts +67 -0
- package/types/media-mock/MediaMocker.d.ts.map +1 -1
- package/types/media-mock/createInputDeviceInfo.d.ts +9 -0
- package/types/media-mock/createInputDeviceInfo.d.ts.map +1 -1
- package/types/media-mock/defineProperty.d.ts +6 -3
- package/types/media-mock/defineProperty.d.ts.map +1 -1
- package/types/media-mock/fake-devices.d.ts +6 -0
- package/types/media-mock/fake-devices.d.ts.map +1 -1
- package/types/ui/CameraErrorModal.d.ts +3 -0
- package/types/ui/CameraErrorModal.d.ts.map +1 -1
- package/types/ui/CameraSelector.d.ts +3 -0
- package/types/ui/CameraSelector.d.ts.map +1 -1
- package/types/ui/CameraUiStoreContext.d.ts +14 -0
- package/types/ui/CameraUiStoreContext.d.ts.map +1 -1
- package/types/ui/CaptureScreen.d.ts +12 -0
- package/types/ui/CaptureScreen.d.ts.map +1 -1
- package/types/ui/Header.d.ts +3 -0
- package/types/ui/Header.d.ts.map +1 -1
- package/types/ui/LocalizationContext.d.ts +12 -0
- package/types/ui/LocalizationContext.d.ts.map +1 -1
- package/types/ui/RootComponent.d.ts +3 -0
- package/types/ui/RootComponent.d.ts.map +1 -1
- package/types/ui/SolidShadowRoot.d.ts +16 -3
- package/types/ui/SolidShadowRoot.d.ts.map +1 -1
- package/types/ui/createCameraManagerUi.d.ts +33 -3
- package/types/ui/createCameraManagerUi.d.ts.map +1 -1
- package/types/ui/determineFitMode.d.ts +7 -6
- package/types/ui/determineFitMode.d.ts.map +1 -1
- package/types/ui/getVisibleVideoArea.d.ts +7 -1
- package/types/ui/getVisibleVideoArea.d.ts.map +1 -1
- package/types/ui/locales/en.d.ts +3 -0
- package/types/ui/locales/en.d.ts.map +1 -1
- package/types/ui/zustandRefStore.d.ts +12 -0
- package/types/ui/zustandRefStore.d.ts.map +1 -1
package/dist/camera-manager.js
CHANGED
|
@@ -48,28 +48,6 @@ if (typeof HTMLVideoElement !== "undefined" && !("requestVideoFrameCallback" in
|
|
|
48
48
|
delete this._rvfcpolyfillmap[handle];
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
-
const initialState$1 = {
|
|
52
|
-
cameras: [],
|
|
53
|
-
facingFilter: void 0,
|
|
54
|
-
videoElement: void 0,
|
|
55
|
-
playbackState: "idle",
|
|
56
|
-
selectedCamera: void 0,
|
|
57
|
-
isSwappingCamera: false,
|
|
58
|
-
isQueryingCameras: false,
|
|
59
|
-
mirrorX: false,
|
|
60
|
-
errorState: void 0
|
|
61
|
-
};
|
|
62
|
-
const cameraManagerStore = createStore()(
|
|
63
|
-
// this is important! Otherwise solid-zustand will start mutating the initial state
|
|
64
|
-
subscribeWithSelector(() => structuredClone(initialState$1))
|
|
65
|
-
);
|
|
66
|
-
const resetCameraManagerStore = () => {
|
|
67
|
-
console.debug("Stopping all cameras and resetting the `cameraManagerStore`.");
|
|
68
|
-
cameraManagerStore.getState().cameras.forEach((camera) => {
|
|
69
|
-
camera.stopStream();
|
|
70
|
-
});
|
|
71
|
-
cameraManagerStore.setState(structuredClone(initialState$1));
|
|
72
|
-
};
|
|
73
51
|
const backCameraKeywords = [
|
|
74
52
|
// English
|
|
75
53
|
"back",
|
|
@@ -538,6 +516,13 @@ const asError = (thrown) => {
|
|
|
538
516
|
};
|
|
539
517
|
class CameraError extends Error {
|
|
540
518
|
code;
|
|
519
|
+
/**
|
|
520
|
+
* Creates a new camera error.
|
|
521
|
+
*
|
|
522
|
+
* @param message - The error message.
|
|
523
|
+
* @param code - The error code.
|
|
524
|
+
* @param cause - The cause of the error.
|
|
525
|
+
*/
|
|
541
526
|
constructor(message, code, cause) {
|
|
542
527
|
super(message);
|
|
543
528
|
this.code = code;
|
|
@@ -741,6 +726,9 @@ function findResolutionKey(videoTrackResolution) {
|
|
|
741
726
|
return resolutionKey;
|
|
742
727
|
}
|
|
743
728
|
class Camera {
|
|
729
|
+
/**
|
|
730
|
+
* The device info.
|
|
731
|
+
*/
|
|
744
732
|
deviceInfo;
|
|
745
733
|
/**
|
|
746
734
|
* Stream capabilities as reported by the stream.
|
|
@@ -774,6 +762,11 @@ class Camera {
|
|
|
774
762
|
* @deprecated Not used. Reconsider using once Firefox and Chrome align on this.
|
|
775
763
|
*/
|
|
776
764
|
#deviceCapabilities;
|
|
765
|
+
/**
|
|
766
|
+
* Creates a new Camera instance.
|
|
767
|
+
*
|
|
768
|
+
* @param deviceInfo - The device info.
|
|
769
|
+
*/
|
|
777
770
|
constructor(deviceInfo) {
|
|
778
771
|
if (deviceInfo.kind !== "videoinput") {
|
|
779
772
|
throw new Error("Device is not a video input device");
|
|
@@ -804,6 +797,12 @@ class Camera {
|
|
|
804
797
|
};
|
|
805
798
|
return proxy;
|
|
806
799
|
}
|
|
800
|
+
/**
|
|
801
|
+
* Starts a stream with the specified resolution.
|
|
802
|
+
*
|
|
803
|
+
* @param resolution - The resolution to start the stream with.
|
|
804
|
+
* @returns The stream.
|
|
805
|
+
*/
|
|
807
806
|
async startStream(resolution) {
|
|
808
807
|
if (this.activeStream) {
|
|
809
808
|
return this.activeStream;
|
|
@@ -827,6 +826,9 @@ class Camera {
|
|
|
827
826
|
/**
|
|
828
827
|
* Acquires a camera stream with the specified resolution.
|
|
829
828
|
* If acquisition fails, it tries a lower resolution as fallback.
|
|
829
|
+
*
|
|
830
|
+
* @param resolution - The resolution to acquire the stream with.
|
|
831
|
+
* @returns The stream.
|
|
830
832
|
*/
|
|
831
833
|
async acquireStreamWithFallback(resolution) {
|
|
832
834
|
try {
|
|
@@ -851,6 +853,8 @@ class Camera {
|
|
|
851
853
|
}
|
|
852
854
|
/**
|
|
853
855
|
* Populates the camera instance with capabilities from the stream.
|
|
856
|
+
*
|
|
857
|
+
* @param stream - The stream to populate the capabilities from.
|
|
854
858
|
*/
|
|
855
859
|
populateCapabilities(stream) {
|
|
856
860
|
this.streamCapabilities = stream.getVideoTracks()[0].getCapabilities();
|
|
@@ -892,6 +896,11 @@ class Camera {
|
|
|
892
896
|
}
|
|
893
897
|
}
|
|
894
898
|
}
|
|
899
|
+
/**
|
|
900
|
+
* Toggles the torch on the camera.
|
|
901
|
+
*
|
|
902
|
+
* @returns The torch status.
|
|
903
|
+
*/
|
|
895
904
|
async toggleTorch() {
|
|
896
905
|
const videoTrack = this.getVideoTrack();
|
|
897
906
|
if (!videoTrack) {
|
|
@@ -917,6 +926,9 @@ class Camera {
|
|
|
917
926
|
}
|
|
918
927
|
return this.torchEnabled;
|
|
919
928
|
}
|
|
929
|
+
/**
|
|
930
|
+
* Stops the stream on the camera.
|
|
931
|
+
*/
|
|
920
932
|
stopStream() {
|
|
921
933
|
if (this.activeStream) {
|
|
922
934
|
console.debug(`Stopping active stream on ${this.name}`);
|
|
@@ -926,6 +938,11 @@ class Camera {
|
|
|
926
938
|
this.torchEnabled = false;
|
|
927
939
|
}
|
|
928
940
|
}
|
|
941
|
+
/**
|
|
942
|
+
* Gets the video track on the camera.
|
|
943
|
+
*
|
|
944
|
+
* @returns The video track.
|
|
945
|
+
*/
|
|
929
946
|
getVideoTrack() {
|
|
930
947
|
if (!this.activeStream) {
|
|
931
948
|
console.warn(`No active stream on Camera instance: ${this.name}.`);
|
|
@@ -934,6 +951,28 @@ class Camera {
|
|
|
934
951
|
return this.activeStream.getVideoTracks()[0];
|
|
935
952
|
}
|
|
936
953
|
}
|
|
954
|
+
const initialState$1 = {
|
|
955
|
+
cameras: [],
|
|
956
|
+
facingFilter: void 0,
|
|
957
|
+
videoElement: void 0,
|
|
958
|
+
playbackState: "idle",
|
|
959
|
+
selectedCamera: void 0,
|
|
960
|
+
isSwappingCamera: false,
|
|
961
|
+
isQueryingCameras: false,
|
|
962
|
+
mirrorX: false,
|
|
963
|
+
errorState: void 0
|
|
964
|
+
};
|
|
965
|
+
const cameraManagerStore = createStore()(
|
|
966
|
+
// this is important! Otherwise solid-zustand will start mutating the initial state
|
|
967
|
+
subscribeWithSelector(() => structuredClone(initialState$1))
|
|
968
|
+
);
|
|
969
|
+
const resetCameraManagerStore = () => {
|
|
970
|
+
console.debug("Stopping all cameras and resetting the `cameraManagerStore`.");
|
|
971
|
+
cameraManagerStore.getState().cameras.forEach((camera) => {
|
|
972
|
+
camera.stopStream();
|
|
973
|
+
});
|
|
974
|
+
cameraManagerStore.setState(structuredClone(initialState$1));
|
|
975
|
+
};
|
|
937
976
|
const ORIGINAL_ATTACH_SHADOW = Element.prototype.attachShadow;
|
|
938
977
|
function isShady() {
|
|
939
978
|
return typeof window.ShadyDOM !== "undefined" && typeof ShadowRoot !== "undefined";
|
|
@@ -1248,6 +1287,11 @@ class VideoFrameProcessor {
|
|
|
1248
1287
|
#cachedHeight = 0;
|
|
1249
1288
|
#canvasRenderingMode;
|
|
1250
1289
|
#requiredPackAlignment = 4;
|
|
1290
|
+
/**
|
|
1291
|
+
* Creates a new VideoFrameProcessor.
|
|
1292
|
+
*
|
|
1293
|
+
* @param options - The options for the VideoFrameProcessor.
|
|
1294
|
+
*/
|
|
1251
1295
|
constructor(options = {}) {
|
|
1252
1296
|
const { canvasRenderingMode = "webgl2", fallbackWebGlTo2d = true } = options;
|
|
1253
1297
|
this.#canvasRenderingMode = canvasRenderingMode;
|
|
@@ -1275,7 +1319,7 @@ class VideoFrameProcessor {
|
|
|
1275
1319
|
}
|
|
1276
1320
|
}
|
|
1277
1321
|
/**
|
|
1278
|
-
* Initializes the 2D canvas context
|
|
1322
|
+
* Initializes the 2D canvas context.
|
|
1279
1323
|
*/
|
|
1280
1324
|
#initialize2dContext() {
|
|
1281
1325
|
const ctx = this.#canvas.getContext("2d", {
|
|
@@ -1286,7 +1330,7 @@ class VideoFrameProcessor {
|
|
|
1286
1330
|
this.#context2d = ctx;
|
|
1287
1331
|
}
|
|
1288
1332
|
/**
|
|
1289
|
-
* Initializes the WebGL2 context and resources
|
|
1333
|
+
* Initializes the WebGL2 context and resources.
|
|
1290
1334
|
*/
|
|
1291
1335
|
#initializeWebGl2Context() {
|
|
1292
1336
|
const ctx = this.#canvas.getContext("webgl2", {
|
|
@@ -1322,9 +1366,12 @@ class VideoFrameProcessor {
|
|
|
1322
1366
|
);
|
|
1323
1367
|
}
|
|
1324
1368
|
/**
|
|
1325
|
-
* Returns ownership of an ArrayBuffer to the processor for reuse
|
|
1326
|
-
*
|
|
1327
|
-
*
|
|
1369
|
+
* Returns ownership of an ArrayBuffer to the processor for reuse.
|
|
1370
|
+
*
|
|
1371
|
+
* This should only be called with ArrayBuffers that were originally from this processor.
|
|
1372
|
+
* Typically used after transferring the buffer to/from a worker.
|
|
1373
|
+
*
|
|
1374
|
+
* @param arrayBuffer - The array buffer to reattach.
|
|
1328
1375
|
*/
|
|
1329
1376
|
reattachArrayBuffer(arrayBuffer) {
|
|
1330
1377
|
const actualBuffer = getBuffer(arrayBuffer);
|
|
@@ -1341,7 +1388,9 @@ class VideoFrameProcessor {
|
|
|
1341
1388
|
}
|
|
1342
1389
|
}
|
|
1343
1390
|
/**
|
|
1344
|
-
* Used to check if the processor owns the buffer
|
|
1391
|
+
* Used to check if the processor owns the buffer.
|
|
1392
|
+
*
|
|
1393
|
+
* @returns true if the processor owns the buffer, false otherwise.
|
|
1345
1394
|
*/
|
|
1346
1395
|
isBufferDetached() {
|
|
1347
1396
|
if (!this.#buffer) {
|
|
@@ -1350,7 +1399,11 @@ class VideoFrameProcessor {
|
|
|
1350
1399
|
return isBufferDetached(this.#buffer.buffer);
|
|
1351
1400
|
}
|
|
1352
1401
|
/**
|
|
1353
|
-
* Extracts image data from a source element
|
|
1402
|
+
* Extracts image data from a source element.
|
|
1403
|
+
*
|
|
1404
|
+
* @param source - The source element to extract image data from.
|
|
1405
|
+
* @param area - The extraction area.
|
|
1406
|
+
* @returns The image data.
|
|
1354
1407
|
*/
|
|
1355
1408
|
getImageData(source, area) {
|
|
1356
1409
|
return this.#canvasRenderingMode === "2d" ? this.#getImageData2d(source, area) : this.#getImageDataWebGl2(source, area);
|
|
@@ -1359,6 +1412,7 @@ class VideoFrameProcessor {
|
|
|
1359
1412
|
* Used to get the current ImageData object with the current buffer. Useful
|
|
1360
1413
|
* when you need to get the same `ImageData` object multiple times after the
|
|
1361
1414
|
* original `ImageData` buffer has been detached
|
|
1415
|
+
*
|
|
1362
1416
|
* @returns ImageData object with the current buffer
|
|
1363
1417
|
*/
|
|
1364
1418
|
getCurrentImageData() {
|
|
@@ -1368,7 +1422,11 @@ class VideoFrameProcessor {
|
|
|
1368
1422
|
return new ImageData(this.#buffer, this.#cachedWidth, this.#cachedHeight);
|
|
1369
1423
|
}
|
|
1370
1424
|
/**
|
|
1371
|
-
* Extract image data using 2D canvas
|
|
1425
|
+
* Extract image data using 2D canvas.
|
|
1426
|
+
*
|
|
1427
|
+
* @param source - The source element to extract image data from.
|
|
1428
|
+
* @param area - The extraction area.
|
|
1429
|
+
* @returns The image data.
|
|
1372
1430
|
*/
|
|
1373
1431
|
#getImageData2d(source, area) {
|
|
1374
1432
|
if (!this.#context2d)
|
|
@@ -1384,7 +1442,11 @@ class VideoFrameProcessor {
|
|
|
1384
1442
|
return this.#context2d.getImageData(0, 0, w, h);
|
|
1385
1443
|
}
|
|
1386
1444
|
/**
|
|
1387
|
-
* Extract image data using WebGL2
|
|
1445
|
+
* Extract image data using WebGL2.
|
|
1446
|
+
*
|
|
1447
|
+
* @param source - The source element to extract image data from.
|
|
1448
|
+
* @param area - The extraction area.
|
|
1449
|
+
* @returns The image data.
|
|
1388
1450
|
*/
|
|
1389
1451
|
#getImageDataWebGl2(source, area) {
|
|
1390
1452
|
if (!this.#contextWebGl2 || !this.#webGl2Texture || !this.#webGl2Framebuffer) {
|
|
@@ -1425,9 +1487,12 @@ class VideoFrameProcessor {
|
|
|
1425
1487
|
return new ImageData(this.#buffer, w, h);
|
|
1426
1488
|
}
|
|
1427
1489
|
/**
|
|
1428
|
-
* Update canvas dimensions if needed
|
|
1490
|
+
* Update canvas dimensions if needed.
|
|
1491
|
+
*
|
|
1492
|
+
* This canvas is the orientation-aware.
|
|
1429
1493
|
*
|
|
1430
|
-
*
|
|
1494
|
+
* @param width - The width of the canvas.
|
|
1495
|
+
* @param height - The height of the canvas.
|
|
1431
1496
|
*/
|
|
1432
1497
|
#updateCanvasSize(width, height) {
|
|
1433
1498
|
if (this.#cachedWidth !== width || this.#cachedHeight !== height) {
|
|
@@ -1440,7 +1505,7 @@ class VideoFrameProcessor {
|
|
|
1440
1505
|
}
|
|
1441
1506
|
}
|
|
1442
1507
|
/**
|
|
1443
|
-
* Clean up resources
|
|
1508
|
+
* Clean up resources.
|
|
1444
1509
|
*/
|
|
1445
1510
|
dispose() {
|
|
1446
1511
|
if (this.#contextWebGl2) {
|
|
@@ -1481,6 +1546,10 @@ class CameraManager {
|
|
|
1481
1546
|
* CameraManager from throwing errors when the user interrupts the process.
|
|
1482
1547
|
*/
|
|
1483
1548
|
#userInitiatedAbort = false;
|
|
1549
|
+
/**
|
|
1550
|
+
* If true, the user has initiated an abort. This will prevent the
|
|
1551
|
+
* CameraManager from throwing errors when the user interrupts the process.
|
|
1552
|
+
*/
|
|
1484
1553
|
get userInitiatedAbort() {
|
|
1485
1554
|
return this.#userInitiatedAbort;
|
|
1486
1555
|
}
|
|
@@ -1489,6 +1558,7 @@ class CameraManager {
|
|
|
1489
1558
|
}
|
|
1490
1559
|
/**
|
|
1491
1560
|
* Sets the area of the video frame that will be extracted.
|
|
1561
|
+
*
|
|
1492
1562
|
* @param extractionArea The area of the video frame that will be extracted.
|
|
1493
1563
|
*/
|
|
1494
1564
|
setExtractionArea(extractionArea) {
|
|
@@ -1499,6 +1569,12 @@ class CameraManager {
|
|
|
1499
1569
|
* "capturing".
|
|
1500
1570
|
*/
|
|
1501
1571
|
#frameCaptureCallbacks = /* @__PURE__ */ new Set();
|
|
1572
|
+
/**
|
|
1573
|
+
* Creates a new CameraManager instance.
|
|
1574
|
+
*
|
|
1575
|
+
* @param options - The options for the CameraManager.
|
|
1576
|
+
* @param videoFrameProcessorOptions - The options for the VideoFrameProcessor.
|
|
1577
|
+
*/
|
|
1502
1578
|
constructor(options = {}, videoFrameProcessorOptions) {
|
|
1503
1579
|
const { mirrorFrontCameras } = {
|
|
1504
1580
|
...defaultCameraManagerOptions,
|
|
@@ -1510,7 +1586,9 @@ class CameraManager {
|
|
|
1510
1586
|
this.#mirrorFrontCameras = mirrorFrontCameras;
|
|
1511
1587
|
}
|
|
1512
1588
|
/**
|
|
1513
|
-
* Sets the resolution of the camera stream
|
|
1589
|
+
* Sets the resolution of the camera stream.
|
|
1590
|
+
*
|
|
1591
|
+
* @param resolution - The resolution to set.
|
|
1514
1592
|
*/
|
|
1515
1593
|
setResolution = async (resolution) => {
|
|
1516
1594
|
this.#resolution = resolution;
|
|
@@ -1521,16 +1599,25 @@ class CameraManager {
|
|
|
1521
1599
|
await this.startCameraStream();
|
|
1522
1600
|
}
|
|
1523
1601
|
};
|
|
1602
|
+
/**
|
|
1603
|
+
* The resolution of the camera stream.
|
|
1604
|
+
*/
|
|
1524
1605
|
get resolution() {
|
|
1525
1606
|
return this.#resolution;
|
|
1526
1607
|
}
|
|
1527
1608
|
/**
|
|
1528
1609
|
* True if there is a video playing or capturing
|
|
1529
|
-
*
|
|
1610
|
+
*
|
|
1611
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/MediaSession/playbackState for more details.
|
|
1530
1612
|
*/
|
|
1531
1613
|
get isActive() {
|
|
1532
1614
|
return cameraManagerStore.getState().playbackState !== "idle";
|
|
1533
1615
|
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Sets the facing filter.
|
|
1618
|
+
*
|
|
1619
|
+
* @param facingFilter - The facing filter.
|
|
1620
|
+
*/
|
|
1534
1621
|
setFacingFilter(facingFilter) {
|
|
1535
1622
|
cameraManagerStore.setState({
|
|
1536
1623
|
facingFilter
|
|
@@ -1539,6 +1626,8 @@ class CameraManager {
|
|
|
1539
1626
|
/**
|
|
1540
1627
|
* Returns the cameras that are available to the user, filtered by the facing mode.
|
|
1541
1628
|
* If no facing mode is set, all cameras are returned.
|
|
1629
|
+
*
|
|
1630
|
+
* @returns The cameras that are available to the user, filtered by the facing mode.
|
|
1542
1631
|
*/
|
|
1543
1632
|
async getCameraDevices() {
|
|
1544
1633
|
let allCameras = cameraManagerStore.getState().cameras;
|
|
@@ -1556,7 +1645,9 @@ class CameraManager {
|
|
|
1556
1645
|
return filteredCameras;
|
|
1557
1646
|
}
|
|
1558
1647
|
/**
|
|
1559
|
-
* Single-time setup for a video element
|
|
1648
|
+
* Single-time setup for a video element.
|
|
1649
|
+
*
|
|
1650
|
+
* @param videoElement - The video element to initialize.
|
|
1560
1651
|
*/
|
|
1561
1652
|
#initVideoElement(videoElement) {
|
|
1562
1653
|
if (!(videoElement instanceof HTMLVideoElement)) {
|
|
@@ -1636,6 +1727,8 @@ class CameraManager {
|
|
|
1636
1727
|
}
|
|
1637
1728
|
/**
|
|
1638
1729
|
* Initializes the CameraManager with a video element.
|
|
1730
|
+
*
|
|
1731
|
+
* @param videoElement - The video element to initialize.
|
|
1639
1732
|
*/
|
|
1640
1733
|
initVideoElement(videoElement) {
|
|
1641
1734
|
try {
|
|
@@ -1652,13 +1745,16 @@ class CameraManager {
|
|
|
1652
1745
|
* Adds a callback that will be triggered on each frame when the playback state
|
|
1653
1746
|
* is "capturing".
|
|
1654
1747
|
*
|
|
1655
|
-
* @param frameCaptureCallback
|
|
1748
|
+
* @param frameCaptureCallback - The callback to add.
|
|
1656
1749
|
* @returns a cleanup function to remove the callback
|
|
1657
1750
|
*/
|
|
1658
1751
|
addFrameCaptureCallback(frameCaptureCallback) {
|
|
1659
1752
|
this.#frameCaptureCallbacks.add(frameCaptureCallback);
|
|
1660
1753
|
return () => this.#frameCaptureCallbacks.delete(frameCaptureCallback);
|
|
1661
1754
|
}
|
|
1755
|
+
/**
|
|
1756
|
+
* Cleans up the video element, and stops the stream.
|
|
1757
|
+
*/
|
|
1662
1758
|
releaseVideoElement() {
|
|
1663
1759
|
this.#eventListenerCleanup?.();
|
|
1664
1760
|
cameraManagerStore.setState({
|
|
@@ -1666,10 +1762,11 @@ class CameraManager {
|
|
|
1666
1762
|
});
|
|
1667
1763
|
this.stopStream();
|
|
1668
1764
|
}
|
|
1765
|
+
// TODO: might become a private method in the future as an implementation detail of `startStream`
|
|
1669
1766
|
/**
|
|
1670
1767
|
* Select a camera device from available ones.
|
|
1671
1768
|
*
|
|
1672
|
-
*
|
|
1769
|
+
* @param camera - The camera to select.
|
|
1673
1770
|
*/
|
|
1674
1771
|
async selectCamera(camera) {
|
|
1675
1772
|
const playbackState = cameraManagerStore.getState().playbackState;
|
|
@@ -1711,6 +1808,8 @@ class CameraManager {
|
|
|
1711
1808
|
}
|
|
1712
1809
|
/**
|
|
1713
1810
|
* Refreshes available devices on the system and updates the state.
|
|
1811
|
+
*
|
|
1812
|
+
* @returns resolves when the camera devices are refreshed
|
|
1714
1813
|
*/
|
|
1715
1814
|
async refreshCameraDevices() {
|
|
1716
1815
|
if (cameraManagerStore.getState().isQueryingCameras || cameraManagerStore.getState().isSwappingCamera) {
|
|
@@ -1797,6 +1896,8 @@ class CameraManager {
|
|
|
1797
1896
|
}
|
|
1798
1897
|
/**
|
|
1799
1898
|
* Starts playback and frame capturing.
|
|
1899
|
+
*
|
|
1900
|
+
* @returns resolves when frame capture starts
|
|
1800
1901
|
*/
|
|
1801
1902
|
async #startFrameCapture() {
|
|
1802
1903
|
const state = cameraManagerStore.getState();
|
|
@@ -1827,6 +1928,8 @@ class CameraManager {
|
|
|
1827
1928
|
}
|
|
1828
1929
|
/**
|
|
1829
1930
|
* Starts capturing frames from the video element.
|
|
1931
|
+
*
|
|
1932
|
+
* @returns resolves when frame capture starts
|
|
1830
1933
|
*/
|
|
1831
1934
|
startFrameCapture = async () => {
|
|
1832
1935
|
try {
|
|
@@ -1839,6 +1942,12 @@ class CameraManager {
|
|
|
1839
1942
|
}
|
|
1840
1943
|
}
|
|
1841
1944
|
};
|
|
1945
|
+
/**
|
|
1946
|
+
* Starts a camera stream.
|
|
1947
|
+
*
|
|
1948
|
+
* @param params - The parameters for the camera stream.
|
|
1949
|
+
* @returns resolves when the camera stream starts
|
|
1950
|
+
*/
|
|
1842
1951
|
async #startCameraStream({
|
|
1843
1952
|
autoplay = true,
|
|
1844
1953
|
preferredCamera,
|
|
@@ -1919,6 +2028,9 @@ class CameraManager {
|
|
|
1919
2028
|
/**
|
|
1920
2029
|
* Starts a best-effort camera stream. Will pick a camera automatically if
|
|
1921
2030
|
* none is selected.
|
|
2031
|
+
*
|
|
2032
|
+
* @param params - The parameters for the camera stream.
|
|
2033
|
+
* @returns resolves when the camera stream starts
|
|
1922
2034
|
*/
|
|
1923
2035
|
async startCameraStream(params = {}) {
|
|
1924
2036
|
try {
|
|
@@ -1931,12 +2043,17 @@ class CameraManager {
|
|
|
1931
2043
|
}
|
|
1932
2044
|
}
|
|
1933
2045
|
}
|
|
2046
|
+
/**
|
|
2047
|
+
* Checks if the error state is a permission error.
|
|
2048
|
+
*
|
|
2049
|
+
* @returns true if the error state is a permission error
|
|
2050
|
+
*/
|
|
1934
2051
|
#hasPermissionError = () => {
|
|
1935
2052
|
const errorState = cameraManagerStore.getState().errorState;
|
|
1936
2053
|
return errorState instanceof CameraError && errorState.code === "PERMISSION_DENIED";
|
|
1937
2054
|
};
|
|
1938
2055
|
/**
|
|
1939
|
-
* Pauses capturing frames without
|
|
2056
|
+
* Pauses capturing frames, without stopping playback.
|
|
1940
2057
|
*/
|
|
1941
2058
|
stopFrameCapture() {
|
|
1942
2059
|
cameraManagerStore.setState({
|
|
@@ -1973,7 +2090,7 @@ class CameraManager {
|
|
|
1973
2090
|
video.pause();
|
|
1974
2091
|
}
|
|
1975
2092
|
/**
|
|
1976
|
-
* The main recognition loop. Do not call this method directly, use
|
|
2093
|
+
* The main recognition loop. Do not call this method directly, use `#queueFrame` instead.
|
|
1977
2094
|
*/
|
|
1978
2095
|
async #loop() {
|
|
1979
2096
|
const state = cameraManagerStore.getState();
|
|
@@ -2014,7 +2131,7 @@ class CameraManager {
|
|
|
2014
2131
|
this.#queueFrame();
|
|
2015
2132
|
}
|
|
2016
2133
|
/**
|
|
2017
|
-
* Queues the next frame to be processed
|
|
2134
|
+
* Queues the next frame to be processed.
|
|
2018
2135
|
*/
|
|
2019
2136
|
#queueFrame() {
|
|
2020
2137
|
const state = cameraManagerStore.getState();
|
|
@@ -2053,6 +2170,8 @@ class CameraManager {
|
|
|
2053
2170
|
}
|
|
2054
2171
|
/**
|
|
2055
2172
|
* If true, the video and captured frames will be mirrored horizontally.
|
|
2173
|
+
*
|
|
2174
|
+
* @param mirrorX - If true, the video and captured frames will be mirrored horizontally.
|
|
2056
2175
|
*/
|
|
2057
2176
|
setCameraMirrorX(mirrorX) {
|
|
2058
2177
|
const currentState = cameraManagerStore.getState();
|
|
@@ -2075,15 +2194,19 @@ class CameraManager {
|
|
|
2075
2194
|
/**
|
|
2076
2195
|
* Allows the user to subscribe to state changes inside the Camera Manager.
|
|
2077
2196
|
* Implemented using Zustand. For usage information, see
|
|
2078
|
-
*
|
|
2197
|
+
* @see https://github.com/pmndrs/zustand#using-subscribe-with-selector for more details.
|
|
2198
|
+
*
|
|
2199
|
+
* @returns a cleanup function to remove the subscription
|
|
2079
2200
|
*/
|
|
2080
2201
|
subscribe = cameraManagerStore.subscribe;
|
|
2081
2202
|
/**
|
|
2082
2203
|
* Gets the current internal state of the CameraManager.
|
|
2204
|
+
*
|
|
2205
|
+
* @returns the current state of the CameraManager
|
|
2083
2206
|
*/
|
|
2084
2207
|
getState = cameraManagerStore.getState;
|
|
2085
2208
|
/**
|
|
2086
|
-
* Resets the CameraManager and
|
|
2209
|
+
* Resets the CameraManager and stops all streams.
|
|
2087
2210
|
*/
|
|
2088
2211
|
reset() {
|
|
2089
2212
|
console.debug("Resetting camera manager");
|
|
@@ -2115,7 +2238,9 @@ const CameraUiStoreProvider = (props) => {
|
|
|
2115
2238
|
// eslint-disable-next-line solid/reactivity
|
|
2116
2239
|
showTorchButton: props.showTorchButton,
|
|
2117
2240
|
// eslint-disable-next-line solid/reactivity
|
|
2118
|
-
showCloseButton: props.showCloseButton
|
|
2241
|
+
showCloseButton: props.showCloseButton,
|
|
2242
|
+
// eslint-disable-next-line solid/reactivity
|
|
2243
|
+
showCameraErrorModal: props.showCameraErrorModal
|
|
2119
2244
|
};
|
|
2120
2245
|
onCleanup(() => {
|
|
2121
2246
|
console.debug("CameraUiStoreProvider cleanup");
|
|
@@ -2942,7 +3067,8 @@ const CAPTURE_SCREEN_SHADOW_ROOT_HOST_ID = "capture-screen-host";
|
|
|
2942
3067
|
const CaptureScreen = () => {
|
|
2943
3068
|
const {
|
|
2944
3069
|
cameraManager,
|
|
2945
|
-
mountTarget
|
|
3070
|
+
mountTarget,
|
|
3071
|
+
showCameraErrorModal
|
|
2946
3072
|
} = useCameraUiStore();
|
|
2947
3073
|
const [videoRef, setVideoRef] = createSignal();
|
|
2948
3074
|
const [feedbackRef, setFeedbackRef] = createSignal();
|
|
@@ -3049,7 +3175,12 @@ const CaptureScreen = () => {
|
|
|
3049
3175
|
use(setVideoRef, _el$6);
|
|
3050
3176
|
use(setFeedbackRef, _el$7);
|
|
3051
3177
|
use(setOverlayLayerRef, _el$8);
|
|
3052
|
-
insert(_el$5, createComponent(
|
|
3178
|
+
insert(_el$5, createComponent(Show, {
|
|
3179
|
+
when: showCameraErrorModal,
|
|
3180
|
+
get children() {
|
|
3181
|
+
return createComponent(CameraErrorModal, {});
|
|
3182
|
+
}
|
|
3183
|
+
}), null);
|
|
3053
3184
|
effect((_$p) => (_$p = fitMode()) != null ? _el$6.style.setProperty("object-fit", _$p) : _el$6.style.removeProperty("object-fit"));
|
|
3054
3185
|
return _el$5;
|
|
3055
3186
|
})()];
|
|
@@ -3135,7 +3266,8 @@ function createCameraManagerUi(cameraManager, target, {
|
|
|
3135
3266
|
localizationStrings,
|
|
3136
3267
|
showMirrorCameraButton = false,
|
|
3137
3268
|
showTorchButton = true,
|
|
3138
|
-
showCloseButton = true
|
|
3269
|
+
showCloseButton = true,
|
|
3270
|
+
showCameraErrorModal = true
|
|
3139
3271
|
} = {}) {
|
|
3140
3272
|
let mountTarget;
|
|
3141
3273
|
const dismountCallbacks = /* @__PURE__ */ new Set();
|
|
@@ -3193,6 +3325,7 @@ function createCameraManagerUi(cameraManager, target, {
|
|
|
3193
3325
|
showMirrorCameraButton,
|
|
3194
3326
|
showTorchButton,
|
|
3195
3327
|
showCloseButton,
|
|
3328
|
+
showCameraErrorModal,
|
|
3196
3329
|
mountTarget,
|
|
3197
3330
|
get children() {
|
|
3198
3331
|
return createComponent(RootComponent, {});
|
|
@@ -3277,6 +3410,7 @@ if (globalThis.__CAMERA_MANAGER__ !== testSymbol) {
|
|
|
3277
3410
|
}
|
|
3278
3411
|
export {
|
|
3279
3412
|
Camera,
|
|
3413
|
+
CameraError,
|
|
3280
3414
|
CameraManager,
|
|
3281
3415
|
MOUNT_POINT_ID,
|
|
3282
3416
|
VideoFrameProcessor,
|