capacitor-camera-view 1.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.
Files changed (42) hide show
  1. package/CapacitorCameraView.podspec +17 -0
  2. package/LICENSE +201 -0
  3. package/Package.swift +28 -0
  4. package/README.md +654 -0
  5. package/android/build.gradle +79 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraView.kt +555 -0
  8. package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraViewPlugin.kt +227 -0
  9. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/BarcodeDetectionResult.kt +11 -0
  10. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/CameraDevice.kt +14 -0
  11. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/CameraSessionConfiguration.kt +10 -0
  12. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/WebBoundingRect.kt +16 -0
  13. package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/ZoomFactors.kt +14 -0
  14. package/android/src/main/java/com/michaelwolz/capacitorcameraview/utils.kt +86 -0
  15. package/android/src/main/res/.gitkeep +0 -0
  16. package/dist/docs.json +968 -0
  17. package/dist/esm/definitions.d.ts +378 -0
  18. package/dist/esm/definitions.js +2 -0
  19. package/dist/esm/definitions.js.map +1 -0
  20. package/dist/esm/index.d.ts +7 -0
  21. package/dist/esm/index.js +10 -0
  22. package/dist/esm/index.js.map +1 -0
  23. package/dist/esm/utils.d.ts +45 -0
  24. package/dist/esm/utils.js +108 -0
  25. package/dist/esm/utils.js.map +1 -0
  26. package/dist/esm/web.d.ts +108 -0
  27. package/dist/esm/web.js +406 -0
  28. package/dist/esm/web.js.map +1 -0
  29. package/dist/plugin.cjs.js +530 -0
  30. package/dist/plugin.cjs.js.map +1 -0
  31. package/dist/plugin.js +533 -0
  32. package/dist/plugin.js.map +1 -0
  33. package/ios/Sources/CameraViewPlugin/CameraError.swift +39 -0
  34. package/ios/Sources/CameraViewPlugin/CameraSessionConfiguration.swift +32 -0
  35. package/ios/Sources/CameraViewPlugin/CameraViewManager+BarcodeScan.swift +91 -0
  36. package/ios/Sources/CameraViewPlugin/CameraViewManager+PhotoCapture.swift +52 -0
  37. package/ios/Sources/CameraViewPlugin/CameraViewManager+VideoDataOutput.swift +78 -0
  38. package/ios/Sources/CameraViewPlugin/CameraViewManager.swift +633 -0
  39. package/ios/Sources/CameraViewPlugin/CameraViewPlugin.swift +295 -0
  40. package/ios/Sources/CameraViewPlugin/Utils.swift +56 -0
  41. package/ios/Tests/CameraViewPluginTests/CameraViewPluginTests.swift +15 -0
  42. package/package.json +94 -0
package/dist/plugin.js ADDED
@@ -0,0 +1,533 @@
1
+ var capacitorCameraView = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ /**
5
+ * The main Capacitor Camera View plugin instance.
6
+ */
7
+ const CameraView = core.registerPlugin('CameraView', {
8
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.CameraViewWeb()),
9
+ });
10
+
11
+ /**
12
+ * Converts canvas to base64 string
13
+ */
14
+ function canvasToBase64(canvas, quality) {
15
+ const dataUrl = canvas.toDataURL('image/jpeg', quality);
16
+ return dataUrl.split(',')[1];
17
+ }
18
+ /**
19
+ * Calculates the visible area of the video based on object-fit: cover
20
+ */
21
+ function calculateVisibleArea(video) {
22
+ // Get the displayed dimensions of the video element
23
+ const videoRect = video.getBoundingClientRect();
24
+ const displayWidth = videoRect.width;
25
+ const displayHeight = videoRect.height;
26
+ // Get the intrinsic dimensions of the video
27
+ const videoWidth = video.videoWidth;
28
+ const videoHeight = video.videoHeight;
29
+ // Calculate which portion of the video is visible (for object-fit: cover)
30
+ const videoAspect = videoWidth / videoHeight;
31
+ const displayAspect = displayWidth / displayHeight;
32
+ let sourceX = 0;
33
+ let sourceY = 0;
34
+ let sourceWidth = videoWidth;
35
+ let sourceHeight = videoHeight;
36
+ // If video aspect ratio is greater than display aspect ratio,
37
+ // the video is cropped on the sides
38
+ if (videoAspect > displayAspect) {
39
+ sourceWidth = videoHeight * displayAspect;
40
+ sourceX = (videoWidth - sourceWidth) / 2;
41
+ }
42
+ // Otherwise the video is cropped on the top and bottom
43
+ else {
44
+ sourceHeight = videoWidth / displayAspect;
45
+ sourceY = (videoHeight - sourceHeight) / 2;
46
+ }
47
+ return {
48
+ sourceX,
49
+ sourceY,
50
+ sourceWidth,
51
+ sourceHeight,
52
+ displayWidth,
53
+ displayHeight,
54
+ };
55
+ }
56
+ /**
57
+ * Draws the visible area of the video to the canvas
58
+ */
59
+ function drawVisibleAreaToCanvas(canvas, videoElement, area) {
60
+ const { sourceX, sourceY, sourceWidth, sourceHeight, displayWidth, displayHeight } = area;
61
+ // Set canvas size to match the displayed dimensions
62
+ canvas.width = displayWidth;
63
+ canvas.height = displayHeight;
64
+ const ctx = canvas.getContext('2d', { alpha: false });
65
+ if (!ctx) {
66
+ throw new Error('Could not get canvas context');
67
+ }
68
+ // Draw only the visible portion of the video to match what the user sees
69
+ ctx.drawImage(videoElement, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, displayWidth, displayHeight);
70
+ }
71
+ /**
72
+ * Transforms barcode coordinates from the video source space to display space
73
+ * accounting for object-fit: cover scaling and cropping.
74
+ *
75
+ * @param barcodeBoundingBox The original barcode bounding box from the detector
76
+ * @param videoElement The video element with the camera stream
77
+ * @returns The transformed bounding box coordinates in display space
78
+ */
79
+ function transformBarcodeBoundingBox(barcodeBoundingBox, videoElement) {
80
+ // Get the video element's displayed dimensions
81
+ const videoRect = videoElement.getBoundingClientRect();
82
+ const displayWidth = videoRect.width;
83
+ const displayHeight = videoRect.height;
84
+ // Get original video dimensions
85
+ const videoWidth = videoElement.videoWidth;
86
+ const videoHeight = videoElement.videoHeight;
87
+ // Calculate scaling and positioning for object-fit: cover
88
+ const videoAspect = videoWidth / videoHeight;
89
+ const displayAspect = displayWidth / displayHeight;
90
+ let scaledX, scaledY, scaledWidth, scaledHeight;
91
+ if (videoAspect > displayAspect) {
92
+ // Video is wider than display area - height matches, width is centered and cropped
93
+ const scale = displayHeight / videoHeight;
94
+ const scaledVideoWidth = videoWidth * scale;
95
+ const cropX = (scaledVideoWidth - displayWidth) / 2;
96
+ scaledWidth = barcodeBoundingBox.width * scale;
97
+ scaledHeight = barcodeBoundingBox.height * scale;
98
+ scaledX = barcodeBoundingBox.x * scale - cropX;
99
+ scaledY = barcodeBoundingBox.y * scale;
100
+ }
101
+ else {
102
+ // Video is taller than display area - width matches, height is centered and cropped
103
+ const scale = displayWidth / videoWidth;
104
+ const scaledVideoHeight = videoHeight * scale;
105
+ const cropY = (scaledVideoHeight - displayHeight) / 2;
106
+ scaledWidth = barcodeBoundingBox.width * scale;
107
+ scaledHeight = barcodeBoundingBox.height * scale;
108
+ scaledX = barcodeBoundingBox.x * scale;
109
+ scaledY = barcodeBoundingBox.y * scale - cropY;
110
+ }
111
+ return {
112
+ x: scaledX,
113
+ y: scaledY,
114
+ width: scaledWidth,
115
+ height: scaledHeight,
116
+ };
117
+ }
118
+
119
+ var __classPrivateFieldGet = (undefined && undefined.__classPrivateFieldGet) || function (receiver, privateMap) {
120
+ if (!privateMap.has(receiver)) {
121
+ throw new TypeError("attempted to get private field on non-instance");
122
+ }
123
+ return privateMap.get(receiver);
124
+ };
125
+ var __classPrivateFieldSet = (undefined && undefined.__classPrivateFieldSet) || function (receiver, privateMap, value) {
126
+ if (!privateMap.has(receiver)) {
127
+ throw new TypeError("attempted to set private field on non-instance");
128
+ }
129
+ privateMap.set(receiver, value);
130
+ return value;
131
+ };
132
+ var _isRunning;
133
+ /**
134
+ * Web implementation of the CameraViewPlugin.
135
+ * Optimized for performance and battery efficiency.
136
+ */
137
+ class CameraViewWeb extends core.WebPlugin {
138
+ constructor() {
139
+ super();
140
+ // DOM elements
141
+ this.videoElement = null;
142
+ this.canvasElement = null;
143
+ // Stream state
144
+ this.stream = null;
145
+ _isRunning.set(this, false);
146
+ // Configuration state
147
+ this.currentCamera = 'environment'; // Default to back camera
148
+ this.currentZoom = 1.0;
149
+ this.currentFlashMode = 'off';
150
+ // Barcode detection support
151
+ this.barcodeDetectionSupported = false;
152
+ this.barcodeDetector = null;
153
+ this.checkBarcodeDetectionSupport();
154
+ }
155
+ /**
156
+ * Start the camera with the given configuration
157
+ */
158
+ async start(options) {
159
+ if (__classPrivateFieldGet(this, _isRunning)) {
160
+ return;
161
+ }
162
+ const permissionStatus = await this.requestPermissions();
163
+ if (permissionStatus.camera !== 'granted') {
164
+ throw new Error('Camera permission was not granted');
165
+ }
166
+ try {
167
+ // Set up video element if it doesn't exist
168
+ if (!this.videoElement) {
169
+ await this.setupVideoElement(options === null || options === void 0 ? void 0 : options.containerElementId);
170
+ }
171
+ // Set up video constraints based on options
172
+ const videoConstraints = {};
173
+ // Prefer deviceId if specified
174
+ if (options === null || options === void 0 ? void 0 : options.deviceId) {
175
+ videoConstraints.deviceId = { exact: options.deviceId };
176
+ // Remember the current camera mode (though we're using a specific device)
177
+ this.currentCamera = (options === null || options === void 0 ? void 0 : options.position) === 'front' ? 'user' : 'environment';
178
+ }
179
+ else {
180
+ // Fall back to facing mode
181
+ const facingMode = (options === null || options === void 0 ? void 0 : options.position) === 'front' ? 'user' : 'environment';
182
+ this.currentCamera = facingMode;
183
+ videoConstraints.facingMode = facingMode;
184
+ }
185
+ const constraints = {
186
+ video: videoConstraints,
187
+ audio: false,
188
+ };
189
+ this.stream = await navigator.mediaDevices.getUserMedia(constraints);
190
+ if (this.videoElement) {
191
+ this.videoElement.srcObject = this.stream;
192
+ this.videoElement.play();
193
+ __classPrivateFieldSet(this, _isRunning, true);
194
+ // If barcode detection is enabled and supported, start detection
195
+ if ((options === null || options === void 0 ? void 0 : options.enableBarcodeDetection) && this.barcodeDetectionSupported) {
196
+ this.startBarcodeDetection();
197
+ }
198
+ }
199
+ }
200
+ catch (err) {
201
+ throw new Error(`Failed to start camera: ${this.formatError(err)}`);
202
+ }
203
+ }
204
+ /**
205
+ * Stop the camera and release resources
206
+ */
207
+ async stop() {
208
+ if (!__classPrivateFieldGet(this, _isRunning)) {
209
+ return;
210
+ }
211
+ try {
212
+ // Stop all tracks in the stream
213
+ if (this.stream) {
214
+ this.stream.getTracks().forEach((track) => track.stop());
215
+ this.stream = null;
216
+ }
217
+ // Clear video source
218
+ if (this.videoElement) {
219
+ this.videoElement = null;
220
+ }
221
+ __classPrivateFieldSet(this, _isRunning, false);
222
+ }
223
+ catch (err) {
224
+ throw new Error(`Failed to stop camera: ${this.formatError(err)}`);
225
+ }
226
+ }
227
+ /**
228
+ * Check if the camera is currently running
229
+ */
230
+ async isRunning() {
231
+ return { isRunning: __classPrivateFieldGet(this, _isRunning) };
232
+ }
233
+ /**
234
+ * Capture a photo using the camera and return it as a base64-encoded JPEG image.
235
+ * Preserves what the user actually sees in the UI, including cropping from object-fit: cover.
236
+ */
237
+ async capture(options) {
238
+ const videoElement = this.videoElement;
239
+ if (!__classPrivateFieldGet(this, _isRunning) || !videoElement) {
240
+ throw new Error('Camera is not running');
241
+ }
242
+ try {
243
+ const canvas = this.getCanvasElement();
244
+ const visibleArea = calculateVisibleArea(videoElement);
245
+ drawVisibleAreaToCanvas(canvas, videoElement, visibleArea);
246
+ const quality = Math.min(1.0, Math.max(0.1, options.quality / 100));
247
+ const base64Data = canvasToBase64(canvas, quality);
248
+ return { photo: base64Data };
249
+ }
250
+ catch (err) {
251
+ throw new Error(`Failed to capture photo: ${this.formatError(err)}`);
252
+ }
253
+ }
254
+ /**
255
+ * Web implementation already uses images from the video stream, so this is the same as `capture()`
256
+ */
257
+ async captureSample(options) {
258
+ return this.capture(options);
259
+ }
260
+ /**
261
+ * Flip between front and back camera
262
+ */
263
+ async flipCamera() {
264
+ if (!__classPrivateFieldGet(this, _isRunning)) {
265
+ throw new Error('Camera is not running');
266
+ }
267
+ try {
268
+ // Switch current camera
269
+ this.currentCamera = this.currentCamera === 'user' ? 'environment' : 'user';
270
+ // Stop current stream
271
+ if (this.stream) {
272
+ this.stream.getTracks().forEach((track) => track.stop());
273
+ }
274
+ // Restart with new facing mode
275
+ const constraints = {
276
+ video: {
277
+ facingMode: this.currentCamera,
278
+ },
279
+ audio: false,
280
+ };
281
+ this.stream = await navigator.mediaDevices.getUserMedia(constraints);
282
+ if (this.videoElement) {
283
+ this.videoElement.srcObject = this.stream;
284
+ }
285
+ }
286
+ catch (err) {
287
+ throw new Error(`Failed to flip camera: ${this.formatError(err)}`);
288
+ }
289
+ }
290
+ /**
291
+ * Get available camera devices
292
+ */
293
+ async getAvailableDevices() {
294
+ try {
295
+ const devices = await navigator.mediaDevices.enumerateDevices();
296
+ const videoDevices = devices.filter((device) => device.kind === 'videoinput');
297
+ return {
298
+ devices: videoDevices.map((device) => ({
299
+ id: device.deviceId,
300
+ name: device.label || `Camera ${device.deviceId.substring(0, 5)}`,
301
+ position: device.label.toLowerCase().includes('front') ? 'front' : 'back',
302
+ })),
303
+ };
304
+ }
305
+ catch (err) {
306
+ console.error('Failed to get available devices', err);
307
+ return { devices: [] };
308
+ }
309
+ }
310
+ /**
311
+ * Get current zoom information (web has limited zoom support)
312
+ */
313
+ async getZoom() {
314
+ // Web has limited zoom capabilities in most browsers,
315
+ // we fake zoomin by scaling the video element
316
+ return {
317
+ min: 1.0,
318
+ max: 3.0,
319
+ current: this.currentZoom,
320
+ };
321
+ }
322
+ /**
323
+ * Set zoom level (limited support in web)
324
+ */
325
+ async setZoom(options) {
326
+ // Store the requested zoom level
327
+ this.currentZoom = options.level;
328
+ // Apply visual zoom using CSS transform when native zoom isn't supported
329
+ if (this.videoElement) {
330
+ this.videoElement.style.transition = options.ramp ? 'transform 0.2s ease-in-out' : 'none';
331
+ const scale = Math.max(1.0, Math.min(options.level, 3.0)); // Limit scale to reasonable bounds
332
+ this.videoElement.style.transform = `scale(${scale})`;
333
+ this.videoElement.style.transformOrigin = 'center';
334
+ }
335
+ }
336
+ /**
337
+ * Get current flash mode
338
+ */
339
+ async getFlashMode() {
340
+ return { flashMode: this.currentFlashMode };
341
+ }
342
+ /**
343
+ * Get supported flash modes
344
+ */
345
+ async getSupportedFlashModes() {
346
+ // Web has limited flash control
347
+ return { flashModes: ['off'] };
348
+ }
349
+ /**
350
+ * Set flash mode (limited support in web)
351
+ */
352
+ async setFlashMode(options) {
353
+ this.currentFlashMode = options.mode;
354
+ console.warn('Flash mode control is not fully supported in the web implementation');
355
+ }
356
+ /**
357
+ * Check camera permission without requesting
358
+ */
359
+ async checkPermissions() {
360
+ try {
361
+ // Use Permissions API if available
362
+ if (navigator.permissions) {
363
+ const result = await navigator.permissions.query({ name: 'camera' });
364
+ return {
365
+ camera: result.state === 'granted' ? 'granted' : result.state === 'denied' ? 'denied' : 'prompt',
366
+ };
367
+ }
368
+ // If Permissions API is not available, check if we have an active stream
369
+ return {
370
+ camera: this.stream ? 'granted' : 'prompt',
371
+ };
372
+ }
373
+ catch (err) {
374
+ // If permissions API is not supported or fails
375
+ return {
376
+ camera: 'prompt',
377
+ };
378
+ }
379
+ }
380
+ /**
381
+ * Request camera permission from the user
382
+ */
383
+ async requestPermissions() {
384
+ try {
385
+ // Try to access the camera to trigger the permission prompt
386
+ const stream = await navigator.mediaDevices.getUserMedia({ video: true });
387
+ // If we get here, permission was granted
388
+ // Clean up the test stream
389
+ stream.getTracks().forEach((track) => track.stop());
390
+ return { camera: 'granted' };
391
+ }
392
+ catch (err) {
393
+ // Permission denied or other error
394
+ return { camera: 'denied' };
395
+ }
396
+ }
397
+ /**
398
+ * Start barcode detection if supported
399
+ */
400
+ async startBarcodeDetection() {
401
+ const barcodeDetector = this.barcodeDetector;
402
+ const videoElement = this.videoElement;
403
+ if (!this.barcodeDetectionSupported || !barcodeDetector || !videoElement) {
404
+ return;
405
+ }
406
+ // Make sure video is fully loaded before starting detection
407
+ if (videoElement.readyState < 2) {
408
+ await new Promise((resolve) => {
409
+ const loadHandler = () => {
410
+ videoElement.removeEventListener('loadeddata', loadHandler);
411
+ resolve();
412
+ };
413
+ videoElement.addEventListener('loadeddata', loadHandler);
414
+ });
415
+ }
416
+ // Add throttling to reduce CPU usage
417
+ let lastDetectionTime = 0;
418
+ const minTimeBetweenDetections = 100; // ms
419
+ // Set up periodic frame analysis for barcode detection
420
+ const detectFrame = async () => {
421
+ if (!__classPrivateFieldGet(this, _isRunning) || !videoElement || !barcodeDetector) {
422
+ return;
423
+ }
424
+ const now = Date.now();
425
+ if (now - lastDetectionTime >= minTimeBetweenDetections) {
426
+ try {
427
+ const barcodes = await barcodeDetector.detect(videoElement);
428
+ lastDetectionTime = now;
429
+ if (barcodes.length > 0) {
430
+ const barcode = barcodes[0];
431
+ // Transform barcode coordinates using the utility function
432
+ const boundingRect = transformBarcodeBoundingBox(barcode.boundingBox, videoElement);
433
+ this.notifyListeners('barcodeDetected', {
434
+ value: barcode.rawValue,
435
+ type: barcode.format.toLowerCase(),
436
+ boundingRect,
437
+ });
438
+ }
439
+ }
440
+ catch (err) {
441
+ console.error('Barcode detection error', err);
442
+ }
443
+ }
444
+ if (__classPrivateFieldGet(this, _isRunning)) {
445
+ requestAnimationFrame(detectFrame);
446
+ }
447
+ };
448
+ requestAnimationFrame(detectFrame);
449
+ }
450
+ /**
451
+ * Clean up resources when the plugin is disposed
452
+ */
453
+ async handleOnDestroy() {
454
+ var _a;
455
+ await this.stop();
456
+ // Remove elements from DOM
457
+ if ((_a = this.videoElement) === null || _a === void 0 ? void 0 : _a.parentNode) {
458
+ this.videoElement.parentNode.removeChild(this.videoElement);
459
+ this.videoElement = null;
460
+ }
461
+ if (this.canvasElement) {
462
+ this.canvasElement = null;
463
+ }
464
+ this.barcodeDetector = null;
465
+ }
466
+ /**
467
+ * Check if barcode detection is supported in this browser
468
+ */
469
+ async checkBarcodeDetectionSupport() {
470
+ if ('BarcodeDetector' in window) {
471
+ try {
472
+ this.barcodeDetector = new BarcodeDetector();
473
+ this.barcodeDetectionSupported = true;
474
+ }
475
+ catch (e) {
476
+ console.warn('BarcodeDetector is not supported by this browser.');
477
+ this.barcodeDetectionSupported = false;
478
+ }
479
+ }
480
+ }
481
+ /**
482
+ * Set up the video element for the camera view
483
+ */
484
+ async setupVideoElement(containerElementId) {
485
+ this.videoElement = document.createElement('video');
486
+ this.videoElement.playsInline = true;
487
+ this.videoElement.autoplay = true;
488
+ this.videoElement.muted = true;
489
+ this.videoElement.style.width = '100%';
490
+ this.videoElement.style.height = '100%';
491
+ this.videoElement.style.objectFit = 'cover';
492
+ // If a container ID is provided, find that element and append the video to it
493
+ if (containerElementId) {
494
+ const container = document.getElementById(containerElementId);
495
+ if (!container) {
496
+ throw new Error(`Container element with ID ${containerElementId} not found`);
497
+ }
498
+ container.appendChild(this.videoElement);
499
+ }
500
+ else {
501
+ // Otherwise, append to body as fallback
502
+ document.body.appendChild(this.videoElement);
503
+ }
504
+ }
505
+ /**
506
+ * Ensures canvas element exists and returns it
507
+ */
508
+ getCanvasElement() {
509
+ if (!this.canvasElement) {
510
+ this.canvasElement = document.createElement('canvas');
511
+ }
512
+ return this.canvasElement;
513
+ }
514
+ /**
515
+ * Format error message
516
+ */
517
+ formatError(err) {
518
+ return err instanceof Error ? err.message : String(err);
519
+ }
520
+ }
521
+ _isRunning = new WeakMap();
522
+
523
+ var web = /*#__PURE__*/Object.freeze({
524
+ __proto__: null,
525
+ CameraViewWeb: CameraViewWeb
526
+ });
527
+
528
+ exports.CameraView = CameraView;
529
+
530
+ return exports;
531
+
532
+ })({}, capacitorExports);
533
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/utils.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\n/**\n * The main Capacitor Camera View plugin instance.\n */\nconst CameraView = registerPlugin('CameraView', {\n web: () => import('./web').then((m) => new m.CameraViewWeb()),\n});\nexport * from './definitions';\nexport { CameraView };\n//# sourceMappingURL=index.js.map","/**\n * Converts canvas to base64 string\n */\nexport function canvasToBase64(canvas, quality) {\n const dataUrl = canvas.toDataURL('image/jpeg', quality);\n return dataUrl.split(',')[1];\n}\n/**\n * Calculates the visible area of the video based on object-fit: cover\n */\nexport function calculateVisibleArea(video) {\n // Get the displayed dimensions of the video element\n const videoRect = video.getBoundingClientRect();\n const displayWidth = videoRect.width;\n const displayHeight = videoRect.height;\n // Get the intrinsic dimensions of the video\n const videoWidth = video.videoWidth;\n const videoHeight = video.videoHeight;\n // Calculate which portion of the video is visible (for object-fit: cover)\n const videoAspect = videoWidth / videoHeight;\n const displayAspect = displayWidth / displayHeight;\n let sourceX = 0;\n let sourceY = 0;\n let sourceWidth = videoWidth;\n let sourceHeight = videoHeight;\n // If video aspect ratio is greater than display aspect ratio,\n // the video is cropped on the sides\n if (videoAspect > displayAspect) {\n sourceWidth = videoHeight * displayAspect;\n sourceX = (videoWidth - sourceWidth) / 2;\n }\n // Otherwise the video is cropped on the top and bottom\n else {\n sourceHeight = videoWidth / displayAspect;\n sourceY = (videoHeight - sourceHeight) / 2;\n }\n return {\n sourceX,\n sourceY,\n sourceWidth,\n sourceHeight,\n displayWidth,\n displayHeight,\n };\n}\n/**\n * Draws the visible area of the video to the canvas\n */\nexport function drawVisibleAreaToCanvas(canvas, videoElement, area) {\n const { sourceX, sourceY, sourceWidth, sourceHeight, displayWidth, displayHeight } = area;\n // Set canvas size to match the displayed dimensions\n canvas.width = displayWidth;\n canvas.height = displayHeight;\n const ctx = canvas.getContext('2d', { alpha: false });\n if (!ctx) {\n throw new Error('Could not get canvas context');\n }\n // Draw only the visible portion of the video to match what the user sees\n ctx.drawImage(videoElement, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, displayWidth, displayHeight);\n}\n/**\n * Transforms barcode coordinates from the video source space to display space\n * accounting for object-fit: cover scaling and cropping.\n *\n * @param barcodeBoundingBox The original barcode bounding box from the detector\n * @param videoElement The video element with the camera stream\n * @returns The transformed bounding box coordinates in display space\n */\nexport function transformBarcodeBoundingBox(barcodeBoundingBox, videoElement) {\n // Get the video element's displayed dimensions\n const videoRect = videoElement.getBoundingClientRect();\n const displayWidth = videoRect.width;\n const displayHeight = videoRect.height;\n // Get original video dimensions\n const videoWidth = videoElement.videoWidth;\n const videoHeight = videoElement.videoHeight;\n // Calculate scaling and positioning for object-fit: cover\n const videoAspect = videoWidth / videoHeight;\n const displayAspect = displayWidth / displayHeight;\n let scaledX, scaledY, scaledWidth, scaledHeight;\n if (videoAspect > displayAspect) {\n // Video is wider than display area - height matches, width is centered and cropped\n const scale = displayHeight / videoHeight;\n const scaledVideoWidth = videoWidth * scale;\n const cropX = (scaledVideoWidth - displayWidth) / 2;\n scaledWidth = barcodeBoundingBox.width * scale;\n scaledHeight = barcodeBoundingBox.height * scale;\n scaledX = barcodeBoundingBox.x * scale - cropX;\n scaledY = barcodeBoundingBox.y * scale;\n }\n else {\n // Video is taller than display area - width matches, height is centered and cropped\n const scale = displayWidth / videoWidth;\n const scaledVideoHeight = videoHeight * scale;\n const cropY = (scaledVideoHeight - displayHeight) / 2;\n scaledWidth = barcodeBoundingBox.width * scale;\n scaledHeight = barcodeBoundingBox.height * scale;\n scaledX = barcodeBoundingBox.x * scale;\n scaledY = barcodeBoundingBox.y * scale - cropY;\n }\n return {\n x: scaledX,\n y: scaledY,\n width: scaledWidth,\n height: scaledHeight,\n };\n}\n//# sourceMappingURL=utils.js.map","var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {\n if (!privateMap.has(receiver)) {\n throw new TypeError(\"attempted to get private field on non-instance\");\n }\n return privateMap.get(receiver);\n};\nvar __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) {\n if (!privateMap.has(receiver)) {\n throw new TypeError(\"attempted to set private field on non-instance\");\n }\n privateMap.set(receiver, value);\n return value;\n};\nvar _isRunning;\nimport { WebPlugin } from '@capacitor/core';\nimport { calculateVisibleArea, canvasToBase64, drawVisibleAreaToCanvas, transformBarcodeBoundingBox } from './utils';\n/**\n * Web implementation of the CameraViewPlugin.\n * Optimized for performance and battery efficiency.\n */\nexport class CameraViewWeb extends WebPlugin {\n constructor() {\n super();\n // DOM elements\n this.videoElement = null;\n this.canvasElement = null;\n // Stream state\n this.stream = null;\n _isRunning.set(this, false);\n // Configuration state\n this.currentCamera = 'environment'; // Default to back camera\n this.currentZoom = 1.0;\n this.currentFlashMode = 'off';\n // Barcode detection support\n this.barcodeDetectionSupported = false;\n this.barcodeDetector = null;\n this.checkBarcodeDetectionSupport();\n }\n /**\n * Start the camera with the given configuration\n */\n async start(options) {\n if (__classPrivateFieldGet(this, _isRunning)) {\n return;\n }\n const permissionStatus = await this.requestPermissions();\n if (permissionStatus.camera !== 'granted') {\n throw new Error('Camera permission was not granted');\n }\n try {\n // Set up video element if it doesn't exist\n if (!this.videoElement) {\n await this.setupVideoElement(options === null || options === void 0 ? void 0 : options.containerElementId);\n }\n // Set up video constraints based on options\n const videoConstraints = {};\n // Prefer deviceId if specified\n if (options === null || options === void 0 ? void 0 : options.deviceId) {\n videoConstraints.deviceId = { exact: options.deviceId };\n // Remember the current camera mode (though we're using a specific device)\n this.currentCamera = (options === null || options === void 0 ? void 0 : options.position) === 'front' ? 'user' : 'environment';\n }\n else {\n // Fall back to facing mode\n const facingMode = (options === null || options === void 0 ? void 0 : options.position) === 'front' ? 'user' : 'environment';\n this.currentCamera = facingMode;\n videoConstraints.facingMode = facingMode;\n }\n const constraints = {\n video: videoConstraints,\n audio: false,\n };\n this.stream = await navigator.mediaDevices.getUserMedia(constraints);\n if (this.videoElement) {\n this.videoElement.srcObject = this.stream;\n this.videoElement.play();\n __classPrivateFieldSet(this, _isRunning, true);\n // If barcode detection is enabled and supported, start detection\n if ((options === null || options === void 0 ? void 0 : options.enableBarcodeDetection) && this.barcodeDetectionSupported) {\n this.startBarcodeDetection();\n }\n }\n }\n catch (err) {\n throw new Error(`Failed to start camera: ${this.formatError(err)}`);\n }\n }\n /**\n * Stop the camera and release resources\n */\n async stop() {\n if (!__classPrivateFieldGet(this, _isRunning)) {\n return;\n }\n try {\n // Stop all tracks in the stream\n if (this.stream) {\n this.stream.getTracks().forEach((track) => track.stop());\n this.stream = null;\n }\n // Clear video source\n if (this.videoElement) {\n this.videoElement = null;\n }\n __classPrivateFieldSet(this, _isRunning, false);\n }\n catch (err) {\n throw new Error(`Failed to stop camera: ${this.formatError(err)}`);\n }\n }\n /**\n * Check if the camera is currently running\n */\n async isRunning() {\n return { isRunning: __classPrivateFieldGet(this, _isRunning) };\n }\n /**\n * Capture a photo using the camera and return it as a base64-encoded JPEG image.\n * Preserves what the user actually sees in the UI, including cropping from object-fit: cover.\n */\n async capture(options) {\n const videoElement = this.videoElement;\n if (!__classPrivateFieldGet(this, _isRunning) || !videoElement) {\n throw new Error('Camera is not running');\n }\n try {\n const canvas = this.getCanvasElement();\n const visibleArea = calculateVisibleArea(videoElement);\n drawVisibleAreaToCanvas(canvas, videoElement, visibleArea);\n const quality = Math.min(1.0, Math.max(0.1, options.quality / 100));\n const base64Data = canvasToBase64(canvas, quality);\n return { photo: base64Data };\n }\n catch (err) {\n throw new Error(`Failed to capture photo: ${this.formatError(err)}`);\n }\n }\n /**\n * Web implementation already uses images from the video stream, so this is the same as `capture()`\n */\n async captureSample(options) {\n return this.capture(options);\n }\n /**\n * Flip between front and back camera\n */\n async flipCamera() {\n if (!__classPrivateFieldGet(this, _isRunning)) {\n throw new Error('Camera is not running');\n }\n try {\n // Switch current camera\n this.currentCamera = this.currentCamera === 'user' ? 'environment' : 'user';\n // Stop current stream\n if (this.stream) {\n this.stream.getTracks().forEach((track) => track.stop());\n }\n // Restart with new facing mode\n const constraints = {\n video: {\n facingMode: this.currentCamera,\n },\n audio: false,\n };\n this.stream = await navigator.mediaDevices.getUserMedia(constraints);\n if (this.videoElement) {\n this.videoElement.srcObject = this.stream;\n }\n }\n catch (err) {\n throw new Error(`Failed to flip camera: ${this.formatError(err)}`);\n }\n }\n /**\n * Get available camera devices\n */\n async getAvailableDevices() {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n const videoDevices = devices.filter((device) => device.kind === 'videoinput');\n return {\n devices: videoDevices.map((device) => ({\n id: device.deviceId,\n name: device.label || `Camera ${device.deviceId.substring(0, 5)}`,\n position: device.label.toLowerCase().includes('front') ? 'front' : 'back',\n })),\n };\n }\n catch (err) {\n console.error('Failed to get available devices', err);\n return { devices: [] };\n }\n }\n /**\n * Get current zoom information (web has limited zoom support)\n */\n async getZoom() {\n // Web has limited zoom capabilities in most browsers,\n // we fake zoomin by scaling the video element\n return {\n min: 1.0,\n max: 3.0,\n current: this.currentZoom,\n };\n }\n /**\n * Set zoom level (limited support in web)\n */\n async setZoom(options) {\n // Store the requested zoom level\n this.currentZoom = options.level;\n // Apply visual zoom using CSS transform when native zoom isn't supported\n if (this.videoElement) {\n this.videoElement.style.transition = options.ramp ? 'transform 0.2s ease-in-out' : 'none';\n const scale = Math.max(1.0, Math.min(options.level, 3.0)); // Limit scale to reasonable bounds\n this.videoElement.style.transform = `scale(${scale})`;\n this.videoElement.style.transformOrigin = 'center';\n }\n }\n /**\n * Get current flash mode\n */\n async getFlashMode() {\n return { flashMode: this.currentFlashMode };\n }\n /**\n * Get supported flash modes\n */\n async getSupportedFlashModes() {\n // Web has limited flash control\n return { flashModes: ['off'] };\n }\n /**\n * Set flash mode (limited support in web)\n */\n async setFlashMode(options) {\n this.currentFlashMode = options.mode;\n console.warn('Flash mode control is not fully supported in the web implementation');\n }\n /**\n * Check camera permission without requesting\n */\n async checkPermissions() {\n try {\n // Use Permissions API if available\n if (navigator.permissions) {\n const result = await navigator.permissions.query({ name: 'camera' });\n return {\n camera: result.state === 'granted' ? 'granted' : result.state === 'denied' ? 'denied' : 'prompt',\n };\n }\n // If Permissions API is not available, check if we have an active stream\n return {\n camera: this.stream ? 'granted' : 'prompt',\n };\n }\n catch (err) {\n // If permissions API is not supported or fails\n return {\n camera: 'prompt',\n };\n }\n }\n /**\n * Request camera permission from the user\n */\n async requestPermissions() {\n try {\n // Try to access the camera to trigger the permission prompt\n const stream = await navigator.mediaDevices.getUserMedia({ video: true });\n // If we get here, permission was granted\n // Clean up the test stream\n stream.getTracks().forEach((track) => track.stop());\n return { camera: 'granted' };\n }\n catch (err) {\n // Permission denied or other error\n return { camera: 'denied' };\n }\n }\n /**\n * Start barcode detection if supported\n */\n async startBarcodeDetection() {\n const barcodeDetector = this.barcodeDetector;\n const videoElement = this.videoElement;\n if (!this.barcodeDetectionSupported || !barcodeDetector || !videoElement) {\n return;\n }\n // Make sure video is fully loaded before starting detection\n if (videoElement.readyState < 2) {\n await new Promise((resolve) => {\n const loadHandler = () => {\n videoElement.removeEventListener('loadeddata', loadHandler);\n resolve();\n };\n videoElement.addEventListener('loadeddata', loadHandler);\n });\n }\n // Add throttling to reduce CPU usage\n let lastDetectionTime = 0;\n const minTimeBetweenDetections = 100; // ms\n // Set up periodic frame analysis for barcode detection\n const detectFrame = async () => {\n if (!__classPrivateFieldGet(this, _isRunning) || !videoElement || !barcodeDetector) {\n return;\n }\n const now = Date.now();\n if (now - lastDetectionTime >= minTimeBetweenDetections) {\n try {\n const barcodes = await barcodeDetector.detect(videoElement);\n lastDetectionTime = now;\n if (barcodes.length > 0) {\n const barcode = barcodes[0];\n // Transform barcode coordinates using the utility function\n const boundingRect = transformBarcodeBoundingBox(barcode.boundingBox, videoElement);\n this.notifyListeners('barcodeDetected', {\n value: barcode.rawValue,\n type: barcode.format.toLowerCase(),\n boundingRect,\n });\n }\n }\n catch (err) {\n console.error('Barcode detection error', err);\n }\n }\n if (__classPrivateFieldGet(this, _isRunning)) {\n requestAnimationFrame(detectFrame);\n }\n };\n requestAnimationFrame(detectFrame);\n }\n /**\n * Clean up resources when the plugin is disposed\n */\n async handleOnDestroy() {\n var _a;\n await this.stop();\n // Remove elements from DOM\n if ((_a = this.videoElement) === null || _a === void 0 ? void 0 : _a.parentNode) {\n this.videoElement.parentNode.removeChild(this.videoElement);\n this.videoElement = null;\n }\n if (this.canvasElement) {\n this.canvasElement = null;\n }\n this.barcodeDetector = null;\n }\n /**\n * Check if barcode detection is supported in this browser\n */\n async checkBarcodeDetectionSupport() {\n if ('BarcodeDetector' in window) {\n try {\n this.barcodeDetector = new BarcodeDetector();\n this.barcodeDetectionSupported = true;\n }\n catch (e) {\n console.warn('BarcodeDetector is not supported by this browser.');\n this.barcodeDetectionSupported = false;\n }\n }\n }\n /**\n * Set up the video element for the camera view\n */\n async setupVideoElement(containerElementId) {\n this.videoElement = document.createElement('video');\n this.videoElement.playsInline = true;\n this.videoElement.autoplay = true;\n this.videoElement.muted = true;\n this.videoElement.style.width = '100%';\n this.videoElement.style.height = '100%';\n this.videoElement.style.objectFit = 'cover';\n // If a container ID is provided, find that element and append the video to it\n if (containerElementId) {\n const container = document.getElementById(containerElementId);\n if (!container) {\n throw new Error(`Container element with ID ${containerElementId} not found`);\n }\n container.appendChild(this.videoElement);\n }\n else {\n // Otherwise, append to body as fallback\n document.body.appendChild(this.videoElement);\n }\n }\n /**\n * Ensures canvas element exists and returns it\n */\n getCanvasElement() {\n if (!this.canvasElement) {\n this.canvasElement = document.createElement('canvas');\n }\n return this.canvasElement;\n }\n /**\n * Format error message\n */\n formatError(err) {\n return err instanceof Error ? err.message : String(err);\n }\n}\n_isRunning = new WeakMap();\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","this","WebPlugin"],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import Foundation
2
+
3
+ enum CameraError: Error, LocalizedError {
4
+ case cameraUnavailable
5
+ case configurationFailed(Error)
6
+ case frameCaptureError
7
+ case inputAdditionFailed
8
+ case outputAdditionFailed
9
+ case photoOutputError
10
+ case photoOutputNotConfigured
11
+ case sessionNotRunning
12
+ case unsupportedFlashMode
13
+ case zoomFactorOutOfRange
14
+
15
+ var errorDescription: String? {
16
+ switch self {
17
+ case .cameraUnavailable:
18
+ return "No available camera for the requested position."
19
+ case .configurationFailed(let error):
20
+ return "Failed to configure the camera. \(error.localizedDescription)"
21
+ case .frameCaptureError:
22
+ return "Failed to capture a frame from the camera."
23
+ case .inputAdditionFailed:
24
+ return "Failed to add input to the capture session."
25
+ case .outputAdditionFailed:
26
+ return "Failed to add output to the capture session."
27
+ case .photoOutputError:
28
+ return "An error occurred when capturing a photo."
29
+ case .photoOutputNotConfigured:
30
+ return "The photo output has not been configured."
31
+ case .sessionNotRunning:
32
+ return "The capture session is not currently running."
33
+ case .unsupportedFlashMode:
34
+ return "The requested flash mode is not supported by the current camera."
35
+ case .zoomFactorOutOfRange:
36
+ return "The requested zoom factor is out of range."
37
+ }
38
+ }
39
+ }