capacitor-camera-view 2.2.0 → 2.3.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 +196 -10
- package/android/build.gradle +1 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraView.kt +309 -3
- package/android/src/main/java/com/michaelwolz/capacitorcameraview/CameraViewPlugin.kt +112 -2
- package/android/src/main/java/com/michaelwolz/capacitorcameraview/model/VideoRecordingQuality.kt +10 -0
- package/android/src/main/java/com/michaelwolz/capacitorcameraview/utils.kt +21 -1
- package/dist/docs.json +200 -8
- package/dist/esm/definitions.d.ts +84 -6
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +20 -4
- package/dist/esm/web.js +157 -16
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +157 -16
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +157 -16
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CameraViewPlugin/CameraError.swift +28 -0
- package/ios/Sources/CameraViewPlugin/CameraSessionConfiguration.swift +8 -8
- package/ios/Sources/CameraViewPlugin/CameraViewManager+PhotoCapture.swift +13 -14
- package/ios/Sources/CameraViewPlugin/CameraViewManager+VideoRecording.swift +302 -0
- package/ios/Sources/CameraViewPlugin/CameraViewManager.swift +159 -150
- package/ios/Sources/CameraViewPlugin/CameraViewPlugin.swift +114 -15
- package/ios/Sources/CameraViewPlugin/TempFileManager.swift +68 -34
- package/package.json +1 -1
package/dist/esm/web.js
CHANGED
|
@@ -47,6 +47,12 @@ export class CameraViewWeb extends WebPlugin {
|
|
|
47
47
|
// Barcode detection support
|
|
48
48
|
this.barcodeDetectionSupported = false;
|
|
49
49
|
this.barcodeDetector = null;
|
|
50
|
+
// Recording state
|
|
51
|
+
this.mediaRecorder = null;
|
|
52
|
+
this.recordedChunks = [];
|
|
53
|
+
this.recordingAudioTrack = null;
|
|
54
|
+
this.recordingResolve = null;
|
|
55
|
+
this.recordingReject = null;
|
|
50
56
|
this.checkBarcodeDetectionSupport();
|
|
51
57
|
}
|
|
52
58
|
/**
|
|
@@ -106,10 +112,25 @@ export class CameraViewWeb extends WebPlugin {
|
|
|
106
112
|
* Stop the camera and release resources
|
|
107
113
|
*/
|
|
108
114
|
async stop() {
|
|
115
|
+
var _a;
|
|
109
116
|
if (!__classPrivateFieldGet(this, _CameraViewWeb_isRunning, "f")) {
|
|
110
117
|
return;
|
|
111
118
|
}
|
|
112
119
|
try {
|
|
120
|
+
// Stop any active recording
|
|
121
|
+
if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
|
|
122
|
+
// Reject any pending stopRecording promise since we're force-stopping
|
|
123
|
+
(_a = this.recordingReject) === null || _a === void 0 ? void 0 : _a.call(this, new Error('Camera session stopped while recording'));
|
|
124
|
+
this.recordingResolve = null;
|
|
125
|
+
this.recordingReject = null;
|
|
126
|
+
this.mediaRecorder.stop();
|
|
127
|
+
this.mediaRecorder = null;
|
|
128
|
+
}
|
|
129
|
+
this.recordedChunks = [];
|
|
130
|
+
if (this.recordingAudioTrack) {
|
|
131
|
+
this.recordingAudioTrack.stop();
|
|
132
|
+
this.recordingAudioTrack = null;
|
|
133
|
+
}
|
|
113
134
|
// Stop all tracks in the stream
|
|
114
135
|
if (this.stream) {
|
|
115
136
|
this.stream.getTracks().forEach((track) => track.stop());
|
|
@@ -174,6 +195,94 @@ export class CameraViewWeb extends WebPlugin {
|
|
|
174
195
|
async captureSample(options) {
|
|
175
196
|
return this.capture(options);
|
|
176
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Start recording video using MediaRecorder API
|
|
200
|
+
*/
|
|
201
|
+
async startRecording(options) {
|
|
202
|
+
if (!__classPrivateFieldGet(this, _CameraViewWeb_isRunning, "f") || !this.videoElement) {
|
|
203
|
+
throw new Error('Camera is not running');
|
|
204
|
+
}
|
|
205
|
+
if (this.mediaRecorder) {
|
|
206
|
+
throw new Error('Recording is already in progress');
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
let stream = this.stream;
|
|
210
|
+
// If audio is requested, get a new stream with audio track
|
|
211
|
+
if ((options === null || options === void 0 ? void 0 : options.enableAudio) && stream) {
|
|
212
|
+
const audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
213
|
+
const audioTrack = audioStream.getAudioTracks()[0];
|
|
214
|
+
this.recordingAudioTrack = audioTrack;
|
|
215
|
+
const videoTracks = stream.getVideoTracks();
|
|
216
|
+
stream = new MediaStream([...videoTracks, audioTrack]);
|
|
217
|
+
}
|
|
218
|
+
if (!stream) {
|
|
219
|
+
throw new Error('No camera stream available');
|
|
220
|
+
}
|
|
221
|
+
this.recordedChunks = [];
|
|
222
|
+
const mimeType = ['video/webm;codecs=vp9', 'video/webm', 'video/mp4'].find((type) => MediaRecorder.isTypeSupported(type));
|
|
223
|
+
if (!mimeType) {
|
|
224
|
+
throw new Error('No supported video recording format found');
|
|
225
|
+
}
|
|
226
|
+
this.mediaRecorder = new MediaRecorder(stream, { mimeType });
|
|
227
|
+
this.mediaRecorder.ondataavailable = (event) => {
|
|
228
|
+
if (event.data && event.data.size > 0) {
|
|
229
|
+
this.recordedChunks.push(event.data);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
this.mediaRecorder.onstop = () => {
|
|
233
|
+
var _a;
|
|
234
|
+
// Stop audio track if it was added for recording
|
|
235
|
+
if (this.recordingAudioTrack) {
|
|
236
|
+
this.recordingAudioTrack.stop();
|
|
237
|
+
this.recordingAudioTrack = null;
|
|
238
|
+
}
|
|
239
|
+
const blob = new Blob(this.recordedChunks, { type: mimeType });
|
|
240
|
+
const url = URL.createObjectURL(blob);
|
|
241
|
+
this.recordedChunks = [];
|
|
242
|
+
this.mediaRecorder = null;
|
|
243
|
+
(_a = this.recordingResolve) === null || _a === void 0 ? void 0 : _a.call(this, { webPath: url });
|
|
244
|
+
this.recordingResolve = null;
|
|
245
|
+
this.recordingReject = null;
|
|
246
|
+
};
|
|
247
|
+
this.mediaRecorder.onerror = (event) => {
|
|
248
|
+
var _a, _b, _c;
|
|
249
|
+
if (this.recordingAudioTrack) {
|
|
250
|
+
this.recordingAudioTrack.stop();
|
|
251
|
+
this.recordingAudioTrack = null;
|
|
252
|
+
}
|
|
253
|
+
this.mediaRecorder = null;
|
|
254
|
+
this.recordedChunks = [];
|
|
255
|
+
const errorMessage = (_b = (_a = event.error) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : 'Unknown recording error';
|
|
256
|
+
(_c = this.recordingReject) === null || _c === void 0 ? void 0 : _c.call(this, new Error('Recording error: ' + errorMessage));
|
|
257
|
+
this.recordingResolve = null;
|
|
258
|
+
this.recordingReject = null;
|
|
259
|
+
};
|
|
260
|
+
this.mediaRecorder.start(100); // Collect data in 100ms chunks
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
if (this.recordingAudioTrack) {
|
|
264
|
+
this.recordingAudioTrack.stop();
|
|
265
|
+
this.recordingAudioTrack = null;
|
|
266
|
+
}
|
|
267
|
+
this.mediaRecorder = null;
|
|
268
|
+
this.recordedChunks = [];
|
|
269
|
+
throw new Error(`Failed to start recording: ${this.formatError(err)}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Stop the current video recording
|
|
274
|
+
*/
|
|
275
|
+
async stopRecording() {
|
|
276
|
+
if (!this.mediaRecorder) {
|
|
277
|
+
throw new Error('No recording is in progress');
|
|
278
|
+
}
|
|
279
|
+
return new Promise((resolve, reject) => {
|
|
280
|
+
var _a;
|
|
281
|
+
this.recordingResolve = resolve;
|
|
282
|
+
this.recordingReject = reject;
|
|
283
|
+
(_a = this.mediaRecorder) === null || _a === void 0 ? void 0 : _a.stop();
|
|
284
|
+
});
|
|
285
|
+
}
|
|
177
286
|
/**
|
|
178
287
|
* Flip between front and back camera
|
|
179
288
|
*/
|
|
@@ -292,45 +401,77 @@ export class CameraViewWeb extends WebPlugin {
|
|
|
292
401
|
throw this.unimplemented('Torch control is not supported in web implementation.');
|
|
293
402
|
}
|
|
294
403
|
/**
|
|
295
|
-
* Check camera permission without requesting
|
|
404
|
+
* Check camera and microphone permission without requesting
|
|
296
405
|
*/
|
|
297
406
|
async checkPermissions() {
|
|
298
407
|
try {
|
|
299
408
|
// Use Permissions API if available
|
|
300
409
|
if (navigator.permissions) {
|
|
301
|
-
const
|
|
410
|
+
const [cameraResult, microphoneResult] = await Promise.all([
|
|
411
|
+
navigator.permissions.query({ name: 'camera' }),
|
|
412
|
+
navigator.permissions.query({ name: 'microphone' }),
|
|
413
|
+
]);
|
|
302
414
|
return {
|
|
303
|
-
camera:
|
|
415
|
+
camera: cameraResult.state === 'granted' ? 'granted' : cameraResult.state === 'denied' ? 'denied' : 'prompt',
|
|
416
|
+
microphone: microphoneResult.state === 'granted'
|
|
417
|
+
? 'granted'
|
|
418
|
+
: microphoneResult.state === 'denied'
|
|
419
|
+
? 'denied'
|
|
420
|
+
: 'prompt',
|
|
304
421
|
};
|
|
305
422
|
}
|
|
306
|
-
// If Permissions API is not available,
|
|
423
|
+
// If Permissions API is not available, fall back to checking the active stream
|
|
307
424
|
return {
|
|
308
425
|
camera: this.stream ? 'granted' : 'prompt',
|
|
426
|
+
microphone: 'prompt',
|
|
309
427
|
};
|
|
310
428
|
}
|
|
311
429
|
catch (err) {
|
|
312
430
|
// If permissions API is not supported or fails
|
|
313
431
|
return {
|
|
314
432
|
camera: 'prompt',
|
|
433
|
+
microphone: 'prompt',
|
|
315
434
|
};
|
|
316
435
|
}
|
|
317
436
|
}
|
|
318
437
|
/**
|
|
319
|
-
* Request camera
|
|
438
|
+
* Request camera and/or microphone permissions from the user.
|
|
439
|
+
* By default, only camera permission is requested.
|
|
320
440
|
*/
|
|
321
|
-
async requestPermissions() {
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
441
|
+
async requestPermissions(options) {
|
|
442
|
+
var _a;
|
|
443
|
+
const permissions = (_a = options === null || options === void 0 ? void 0 : options.permissions) !== null && _a !== void 0 ? _a : ['camera'];
|
|
444
|
+
const result = { camera: 'prompt', microphone: 'prompt' };
|
|
445
|
+
// Request camera permission if included
|
|
446
|
+
if (permissions.includes('camera')) {
|
|
447
|
+
try {
|
|
448
|
+
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
449
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
450
|
+
result.camera = 'granted';
|
|
451
|
+
}
|
|
452
|
+
catch (_b) {
|
|
453
|
+
result.camera = 'denied';
|
|
454
|
+
}
|
|
329
455
|
}
|
|
330
|
-
|
|
331
|
-
//
|
|
332
|
-
|
|
456
|
+
else {
|
|
457
|
+
// Still report current status even if not requesting
|
|
458
|
+
result.camera = (await this.checkPermissions()).camera;
|
|
459
|
+
}
|
|
460
|
+
// Request microphone permission only if explicitly included
|
|
461
|
+
if (permissions.includes('microphone')) {
|
|
462
|
+
try {
|
|
463
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
464
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
465
|
+
result.microphone = 'granted';
|
|
466
|
+
}
|
|
467
|
+
catch (_c) {
|
|
468
|
+
result.microphone = 'denied';
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
else {
|
|
472
|
+
result.microphone = (await this.checkPermissions()).microphone;
|
|
333
473
|
}
|
|
474
|
+
return result;
|
|
334
475
|
}
|
|
335
476
|
/**
|
|
336
477
|
* Start barcode detection if supported
|
package/dist/esm/web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type {\n CameraSessionConfiguration,\n CameraViewPlugin,\n GetAvailableDevicesResponse,\n GetFlashModeResponse,\n GetSupportedFlashModesResponse,\n GetTorchModeResponse,\n GetZoomResponse,\n IsTorchAvailableResponse,\n IsRunningResponse,\n PermissionStatus,\n CaptureResponse,\n FlashMode,\n CaptureOptions,\n BarcodeType,\n} from './definitions';\nimport { calculateVisibleArea, canvasToBase64, drawVisibleAreaToCanvas, transformBarcodeBoundingBox } from './utils';\n\nexport const BARCODE_TYPE_TO_WEB_FORMAT: Readonly<Record<BarcodeType, BarcodeFormat | null>> = {\n qr: 'qr_code',\n code128: 'code_128',\n code39: 'code_39',\n code39Mod43: null,\n code93: 'code_93',\n ean8: 'ean_8',\n ean13: 'ean_13',\n interleaved2of5: 'itf',\n itf14: 'itf',\n pdf417: 'pdf417',\n aztec: 'aztec',\n dataMatrix: 'data_matrix',\n upce: 'upc_e',\n} satisfies Record<BarcodeType, BarcodeFormat | null>;\n\n/**\n * Web implementation of the CameraViewPlugin.\n * Optimized for performance and battery efficiency.\n */\nexport class CameraViewWeb extends WebPlugin implements CameraViewPlugin {\n // DOM elements\n private videoElement: HTMLVideoElement | null = null;\n private canvasElement: HTMLCanvasElement | null = null;\n\n // Stream state\n private stream: MediaStream | null = null;\n #isRunning = false;\n\n // Configuration state\n private currentCamera = 'environment'; // Default to back camera\n private currentZoom = 1.0;\n private currentFlashMode: FlashMode = 'off';\n\n // Barcode detection support\n private barcodeDetectionSupported = false;\n private barcodeDetector: BarcodeDetector | null = null;\n\n constructor() {\n super();\n this.checkBarcodeDetectionSupport();\n }\n\n /**\n * Start the camera with the given configuration\n */\n async start(options?: CameraSessionConfiguration): Promise<void> {\n if (this.#isRunning) {\n return;\n }\n\n const permissionStatus = await this.requestPermissions();\n if (permissionStatus.camera !== 'granted') {\n throw new Error('Camera permission was not granted');\n }\n\n try {\n // Set up video element if it doesn't exist\n if (!this.videoElement) {\n await this.setupVideoElement(options?.containerElementId);\n }\n\n // Set up video constraints based on options\n const videoConstraints: MediaTrackConstraints = {};\n\n // Prefer deviceId if specified\n if (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?.position === 'front' ? 'user' : 'environment';\n } else {\n // Fall back to facing mode\n const facingMode = options?.position === 'front' ? 'user' : 'environment';\n this.currentCamera = facingMode;\n videoConstraints.facingMode = facingMode;\n }\n\n const constraints: MediaStreamConstraints = {\n video: videoConstraints,\n audio: false,\n };\n\n this.stream = await navigator.mediaDevices.getUserMedia(constraints);\n\n if (this.videoElement) {\n this.videoElement.srcObject = this.stream;\n this.videoElement.play();\n this.#isRunning = true;\n\n // If barcode detection is enabled and supported, start detection\n if (options?.enableBarcodeDetection) {\n await this.checkBarcodeDetectionSupport();\n\n if (this.barcodeDetectionSupported) {\n await this.configureBarcodeDetector(options?.barcodeTypes);\n this.startBarcodeDetection();\n }\n }\n }\n } catch (err) {\n throw new Error(`Failed to start camera: ${this.formatError(err)}`);\n }\n }\n\n /**\n * Stop the camera and release resources\n */\n async stop(): Promise<void> {\n if (!this.#isRunning) {\n return;\n }\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\n // Clear video source\n if (this.videoElement) {\n this.videoElement = null;\n }\n\n this.#isRunning = false;\n } catch (err) {\n throw new Error(`Failed to stop camera: ${this.formatError(err)}`);\n }\n }\n\n /**\n * Check if the camera is currently running\n */\n async isRunning(): Promise<IsRunningResponse> {\n return { isRunning: this.#isRunning };\n }\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<T extends CaptureOptions>(options: T): Promise<CaptureResponse<T>> {\n const videoElement = this.videoElement;\n\n if (!this.#isRunning || !videoElement) {\n throw new Error('Camera is not running');\n }\n\n try {\n const canvas = this.getCanvasElement();\n const visibleArea = calculateVisibleArea(videoElement);\n\n drawVisibleAreaToCanvas(canvas, videoElement, visibleArea);\n\n const quality = Math.min(1.0, Math.max(0.1, options.quality / 100));\n\n if (options.saveToFile) {\n // Create a blob from canvas and return a blob URL\n return new Promise((resolve, reject) => {\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error('Failed to create blob from canvas'));\n return;\n }\n\n const url = URL.createObjectURL(blob);\n resolve({ webPath: url } as CaptureResponse<T>);\n },\n 'image/jpeg',\n quality,\n );\n });\n } else {\n // Return base64 data\n const base64Data = canvasToBase64(canvas, quality);\n return { photo: base64Data } as CaptureResponse<T>;\n }\n } catch (err) {\n throw new Error(`Failed to capture photo: ${this.formatError(err)}`);\n }\n }\n\n /**\n * Web implementation already uses images from the video stream, so this is the same as `capture()`\n */\n async captureSample<T extends CaptureOptions>(options: T): Promise<CaptureResponse<T>> {\n return this.capture(options);\n }\n\n /**\n * Flip between front and back camera\n */\n public async flipCamera(): Promise<void> {\n if (!this.#isRunning) {\n throw new Error('Camera is not running');\n }\n\n try {\n // Switch current camera\n this.currentCamera = this.currentCamera === 'user' ? 'environment' : 'user';\n\n // Stop current stream\n if (this.stream) {\n this.stream.getTracks().forEach((track) => track.stop());\n }\n\n // Restart with new facing mode\n const constraints: MediaStreamConstraints = {\n video: {\n facingMode: this.currentCamera,\n },\n audio: false,\n };\n\n this.stream = await navigator.mediaDevices.getUserMedia(constraints);\n\n if (this.videoElement) {\n this.videoElement.srcObject = this.stream;\n }\n } catch (err) {\n throw new Error(`Failed to flip camera: ${this.formatError(err)}`);\n }\n }\n\n /**\n * Get available camera devices\n */\n public async getAvailableDevices(): Promise<GetAvailableDevicesResponse> {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n const videoDevices = devices.filter((device) => device.kind === 'videoinput');\n\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 } catch (err) {\n console.error('Failed to get available devices', err);\n return { devices: [] };\n }\n }\n\n /**\n * Get current zoom information (web has limited zoom support)\n */\n public async getZoom(): Promise<GetZoomResponse> {\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 /**\n * Set zoom level (limited support in web)\n */\n public async setZoom(options: { level: number; ramp?: boolean }): Promise<void> {\n // Store the requested zoom level\n this.currentZoom = options.level;\n\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 /**\n * Get current flash mode\n */\n public async getFlashMode(): Promise<GetFlashModeResponse> {\n return { flashMode: this.currentFlashMode };\n }\n\n /**\n * Get supported flash modes\n */\n public async getSupportedFlashModes(): Promise<GetSupportedFlashModesResponse> {\n // Web has limited flash control\n return { flashModes: ['off'] };\n }\n\n /**\n * Set flash mode (limited support in web)\n */\n public async setFlashMode(options: { mode: FlashMode }): Promise<void> {\n this.currentFlashMode = options.mode;\n console.warn('Flash mode control is not fully supported in the web implementation');\n }\n\n /**\n * Check if torch is available (not supported in web)\n */\n public async isTorchAvailable(): Promise<IsTorchAvailableResponse> {\n // Torch is not supported in web implementation\n return { available: false };\n }\n\n /**\n * Get torch mode (not supported in web)\n */\n public async getTorchMode(): Promise<GetTorchModeResponse> {\n // Torch is not supported in web implementation\n return { enabled: false, level: 0.0 };\n }\n\n /**\n * Set torch mode (not supported in web)\n */\n public async setTorchMode(): Promise<void> {\n // Torch is not supported in web implementation\n throw this.unimplemented('Torch control is not supported in web implementation.');\n }\n\n /**\n * Check camera permission without requesting\n */\n public async checkPermissions(): Promise<PermissionStatus> {\n try {\n // Use Permissions API if available\n if (navigator.permissions) {\n const result = await navigator.permissions.query({ name: 'camera' as PermissionName });\n return {\n camera: result.state === 'granted' ? 'granted' : result.state === 'denied' ? 'denied' : 'prompt',\n };\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 } catch (err) {\n // If permissions API is not supported or fails\n return {\n camera: 'prompt',\n };\n }\n }\n\n /**\n * Request camera permission from the user\n */\n public async requestPermissions(): Promise<PermissionStatus> {\n try {\n // Try to access the camera to trigger the permission prompt\n const stream = await navigator.mediaDevices.getUserMedia({ video: true });\n\n // If we get here, permission was granted\n // Clean up the test stream\n stream.getTracks().forEach((track) => track.stop());\n\n return { camera: 'granted' };\n } catch (err) {\n // Permission denied or other error\n return { camera: 'denied' };\n }\n }\n\n /**\n * Start barcode detection if supported\n */\n private async startBarcodeDetection() {\n const barcodeDetector = this.barcodeDetector;\n const videoElement = this.videoElement;\n\n if (!this.barcodeDetectionSupported || !barcodeDetector || !videoElement) {\n return;\n }\n\n // Make sure video is fully loaded before starting detection\n if (videoElement.readyState < 2) {\n await new Promise<void>((resolve) => {\n const loadHandler = () => {\n videoElement.removeEventListener('loadeddata', loadHandler);\n resolve();\n };\n videoElement.addEventListener('loadeddata', loadHandler);\n });\n }\n\n // Add throttling to reduce CPU usage\n let lastDetectionTime = 0;\n const minTimeBetweenDetections = 100; // ms\n\n // Set up periodic frame analysis for barcode detection\n const detectFrame = async () => {\n if (!this.#isRunning || !videoElement || !barcodeDetector) {\n return;\n }\n\n const now = Date.now();\n if (now - lastDetectionTime >= minTimeBetweenDetections) {\n try {\n const barcodes = await barcodeDetector.detect(videoElement);\n lastDetectionTime = now;\n\n if (barcodes.length > 0) {\n const barcode = barcodes[0];\n\n // Transform barcode coordinates using the utility function\n const boundingRect = transformBarcodeBoundingBox(barcode.boundingBox, videoElement);\n\n this.notifyListeners('barcodeDetected', {\n value: barcode.rawValue,\n type: barcode.format.toLowerCase(),\n boundingRect,\n });\n }\n } catch (err) {\n console.error('Barcode detection error', err);\n }\n }\n\n if (this.#isRunning) {\n requestAnimationFrame(detectFrame);\n }\n };\n\n requestAnimationFrame(detectFrame);\n }\n\n /**\n * Clean up resources when the plugin is disposed\n */\n public async handleOnDestroy(): Promise<void> {\n await this.stop();\n\n // Remove elements from DOM\n if (this.videoElement?.parentNode) {\n this.videoElement.parentNode.removeChild(this.videoElement);\n this.videoElement = null;\n }\n\n if (this.canvasElement) {\n this.canvasElement = null;\n }\n\n this.barcodeDetector = null;\n }\n\n /**\n * Check if barcode detection is supported in this browser\n */\n private async checkBarcodeDetectionSupport() {\n if ('BarcodeDetector' in window) {\n try {\n this.barcodeDetector = new BarcodeDetector();\n this.barcodeDetectionSupported = true;\n } catch (e) {\n console.warn('BarcodeDetector is not supported by this browser.');\n this.barcodeDetectionSupported = false;\n }\n }\n }\n\n /**\n * Configure the barcode detector with requested barcode formats.\n * Unsupported formats are ignored and logged.\n */\n private async configureBarcodeDetector(barcodeTypes?: BarcodeType[]): Promise<void> {\n if (!this.barcodeDetectionSupported) {\n return;\n }\n\n if (!barcodeTypes?.length) {\n this.barcodeDetector = new BarcodeDetector();\n return;\n }\n\n const requestedFormats = barcodeTypes\n .map((barcodeType) => {\n const webFormat = BARCODE_TYPE_TO_WEB_FORMAT[barcodeType];\n if (!webFormat) {\n console.warn(`[CameraView] Barcode type \"${barcodeType}\" is not supported by the web BarcodeDetector API.`);\n }\n return webFormat;\n })\n .filter((format): format is BarcodeFormat => format !== null);\n\n if (!requestedFormats.length) {\n console.warn(\n '[CameraView] No requested barcode types are supported on web. Falling back to all supported formats.',\n );\n this.barcodeDetector = new BarcodeDetector();\n return;\n }\n\n const uniqueRequestedFormats = Array.from(new Set(requestedFormats));\n\n try {\n const supportedFormats = await BarcodeDetector.getSupportedFormats();\n const configuredFormats = uniqueRequestedFormats.filter((format) => supportedFormats.includes(format));\n const ignoredFormats = uniqueRequestedFormats.filter((format) => !supportedFormats.includes(format));\n\n if (ignoredFormats.length) {\n console.warn(\n `[CameraView] Ignoring unsupported barcode formats for this browser: ${ignoredFormats.join(', ')}.`,\n );\n }\n\n if (!configuredFormats.length) {\n console.warn(\n '[CameraView] No requested barcode formats are available in this browser. Falling back to all supported formats.',\n );\n this.barcodeDetector = new BarcodeDetector();\n return;\n }\n\n this.barcodeDetector = new BarcodeDetector({ formats: configuredFormats });\n } catch (error) {\n console.warn(\n '[CameraView] Failed to resolve supported barcode formats; falling back to unfiltered detector.',\n error,\n );\n this.barcodeDetector = new BarcodeDetector();\n }\n }\n\n /**\n * Set up the video element for the camera view\n */\n private async setupVideoElement(containerElementId?: string) {\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\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 } else {\n // Otherwise, append to body as fallback\n document.body.appendChild(this.videoElement);\n }\n }\n\n /**\n * Ensures canvas element exists and returns it\n */\n private getCanvasElement(): HTMLCanvasElement {\n if (!this.canvasElement) {\n this.canvasElement = document.createElement('canvas');\n }\n return this.canvasElement;\n }\n\n /**\n * Format error message\n */\n private formatError(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"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","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type {\n CameraSessionConfiguration,\n CameraViewPlugin,\n CameraPermissionType,\n GetAvailableDevicesResponse,\n GetFlashModeResponse,\n GetSupportedFlashModesResponse,\n GetTorchModeResponse,\n GetZoomResponse,\n IsTorchAvailableResponse,\n IsRunningResponse,\n PermissionStatus,\n CaptureResponse,\n FlashMode,\n CaptureOptions,\n VideoRecordingOptions,\n VideoRecordingResponse,\n BarcodeType,\n} from './definitions';\nimport { calculateVisibleArea, canvasToBase64, drawVisibleAreaToCanvas, transformBarcodeBoundingBox } from './utils';\n\nexport const BARCODE_TYPE_TO_WEB_FORMAT: Readonly<Record<BarcodeType, BarcodeFormat | null>> = {\n qr: 'qr_code',\n code128: 'code_128',\n code39: 'code_39',\n code39Mod43: null,\n code93: 'code_93',\n ean8: 'ean_8',\n ean13: 'ean_13',\n interleaved2of5: 'itf',\n itf14: 'itf',\n pdf417: 'pdf417',\n aztec: 'aztec',\n dataMatrix: 'data_matrix',\n upce: 'upc_e',\n} satisfies Record<BarcodeType, BarcodeFormat | null>;\n\n/**\n * Web implementation of the CameraViewPlugin.\n * Optimized for performance and battery efficiency.\n */\nexport class CameraViewWeb extends WebPlugin implements CameraViewPlugin {\n // DOM elements\n private videoElement: HTMLVideoElement | null = null;\n private canvasElement: HTMLCanvasElement | null = null;\n\n // Stream state\n private stream: MediaStream | null = null;\n #isRunning = false;\n\n // Configuration state\n private currentCamera = 'environment'; // Default to back camera\n private currentZoom = 1.0;\n private currentFlashMode: FlashMode = 'off';\n\n // Barcode detection support\n private barcodeDetectionSupported = false;\n private barcodeDetector: BarcodeDetector | null = null;\n\n // Recording state\n private mediaRecorder: MediaRecorder | null = null;\n private recordedChunks: Blob[] = [];\n private recordingAudioTrack: MediaStreamTrack | null = null;\n private recordingResolve: ((response: VideoRecordingResponse) => void) | null = null;\n private recordingReject: ((error: Error) => void) | null = null;\n\n constructor() {\n super();\n this.checkBarcodeDetectionSupport();\n }\n\n /**\n * Start the camera with the given configuration\n */\n async start(options?: CameraSessionConfiguration): Promise<void> {\n if (this.#isRunning) {\n return;\n }\n\n const permissionStatus = await this.requestPermissions();\n if (permissionStatus.camera !== 'granted') {\n throw new Error('Camera permission was not granted');\n }\n\n try {\n // Set up video element if it doesn't exist\n if (!this.videoElement) {\n await this.setupVideoElement(options?.containerElementId);\n }\n\n // Set up video constraints based on options\n const videoConstraints: MediaTrackConstraints = {};\n\n // Prefer deviceId if specified\n if (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?.position === 'front' ? 'user' : 'environment';\n } else {\n // Fall back to facing mode\n const facingMode = options?.position === 'front' ? 'user' : 'environment';\n this.currentCamera = facingMode;\n videoConstraints.facingMode = facingMode;\n }\n\n const constraints: MediaStreamConstraints = {\n video: videoConstraints,\n audio: false,\n };\n\n this.stream = await navigator.mediaDevices.getUserMedia(constraints);\n\n if (this.videoElement) {\n this.videoElement.srcObject = this.stream;\n this.videoElement.play();\n this.#isRunning = true;\n\n // If barcode detection is enabled and supported, start detection\n if (options?.enableBarcodeDetection) {\n await this.checkBarcodeDetectionSupport();\n\n if (this.barcodeDetectionSupported) {\n await this.configureBarcodeDetector(options?.barcodeTypes);\n this.startBarcodeDetection();\n }\n }\n }\n } catch (err) {\n throw new Error(`Failed to start camera: ${this.formatError(err)}`);\n }\n }\n\n /**\n * Stop the camera and release resources\n */\n async stop(): Promise<void> {\n if (!this.#isRunning) {\n return;\n }\n\n try {\n // Stop any active recording\n if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {\n // Reject any pending stopRecording promise since we're force-stopping\n this.recordingReject?.(new Error('Camera session stopped while recording'));\n this.recordingResolve = null;\n this.recordingReject = null;\n this.mediaRecorder.stop();\n this.mediaRecorder = null;\n }\n this.recordedChunks = [];\n if (this.recordingAudioTrack) {\n this.recordingAudioTrack.stop();\n this.recordingAudioTrack = null;\n }\n\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\n // Clear video source\n if (this.videoElement) {\n this.videoElement = null;\n }\n\n this.#isRunning = false;\n } catch (err) {\n throw new Error(`Failed to stop camera: ${this.formatError(err)}`);\n }\n }\n\n /**\n * Check if the camera is currently running\n */\n async isRunning(): Promise<IsRunningResponse> {\n return { isRunning: this.#isRunning };\n }\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<T extends CaptureOptions>(options: T): Promise<CaptureResponse<T>> {\n const videoElement = this.videoElement;\n\n if (!this.#isRunning || !videoElement) {\n throw new Error('Camera is not running');\n }\n\n try {\n const canvas = this.getCanvasElement();\n const visibleArea = calculateVisibleArea(videoElement);\n\n drawVisibleAreaToCanvas(canvas, videoElement, visibleArea);\n\n const quality = Math.min(1.0, Math.max(0.1, options.quality / 100));\n\n if (options.saveToFile) {\n // Create a blob from canvas and return a blob URL\n return new Promise((resolve, reject) => {\n canvas.toBlob(\n (blob) => {\n if (!blob) {\n reject(new Error('Failed to create blob from canvas'));\n return;\n }\n\n const url = URL.createObjectURL(blob);\n resolve({ webPath: url } as CaptureResponse<T>);\n },\n 'image/jpeg',\n quality,\n );\n });\n } else {\n // Return base64 data\n const base64Data = canvasToBase64(canvas, quality);\n return { photo: base64Data } as CaptureResponse<T>;\n }\n } catch (err) {\n throw new Error(`Failed to capture photo: ${this.formatError(err)}`);\n }\n }\n\n /**\n * Web implementation already uses images from the video stream, so this is the same as `capture()`\n */\n async captureSample<T extends CaptureOptions>(options: T): Promise<CaptureResponse<T>> {\n return this.capture(options);\n }\n\n /**\n * Start recording video using MediaRecorder API\n */\n async startRecording(options?: VideoRecordingOptions): Promise<void> {\n if (!this.#isRunning || !this.videoElement) {\n throw new Error('Camera is not running');\n }\n\n if (this.mediaRecorder) {\n throw new Error('Recording is already in progress');\n }\n\n try {\n let stream = this.stream;\n\n // If audio is requested, get a new stream with audio track\n if (options?.enableAudio && stream) {\n const audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });\n const audioTrack = audioStream.getAudioTracks()[0];\n this.recordingAudioTrack = audioTrack;\n const videoTracks = stream.getVideoTracks();\n stream = new MediaStream([...videoTracks, audioTrack]);\n }\n\n if (!stream) {\n throw new Error('No camera stream available');\n }\n\n this.recordedChunks = [];\n\n const mimeType = ['video/webm;codecs=vp9', 'video/webm', 'video/mp4'].find((type) =>\n MediaRecorder.isTypeSupported(type),\n );\n\n if (!mimeType) {\n throw new Error('No supported video recording format found');\n }\n\n this.mediaRecorder = new MediaRecorder(stream, { mimeType });\n\n this.mediaRecorder.ondataavailable = (event) => {\n if (event.data && event.data.size > 0) {\n this.recordedChunks.push(event.data);\n }\n };\n\n this.mediaRecorder.onstop = () => {\n // Stop audio track if it was added for recording\n if (this.recordingAudioTrack) {\n this.recordingAudioTrack.stop();\n this.recordingAudioTrack = null;\n }\n const blob = new Blob(this.recordedChunks, { type: mimeType });\n const url = URL.createObjectURL(blob);\n this.recordedChunks = [];\n this.mediaRecorder = null;\n this.recordingResolve?.({ webPath: url });\n this.recordingResolve = null;\n this.recordingReject = null;\n };\n\n this.mediaRecorder.onerror = (event) => {\n if (this.recordingAudioTrack) {\n this.recordingAudioTrack.stop();\n this.recordingAudioTrack = null;\n }\n this.mediaRecorder = null;\n this.recordedChunks = [];\n const errorMessage = (event as ErrorEvent).error?.message ?? 'Unknown recording error';\n this.recordingReject?.(new Error('Recording error: ' + errorMessage));\n this.recordingResolve = null;\n this.recordingReject = null;\n };\n\n this.mediaRecorder.start(100); // Collect data in 100ms chunks\n } catch (err) {\n if (this.recordingAudioTrack) {\n this.recordingAudioTrack.stop();\n this.recordingAudioTrack = null;\n }\n this.mediaRecorder = null;\n this.recordedChunks = [];\n throw new Error(`Failed to start recording: ${this.formatError(err)}`);\n }\n }\n\n /**\n * Stop the current video recording\n */\n async stopRecording(): Promise<VideoRecordingResponse> {\n if (!this.mediaRecorder) {\n throw new Error('No recording is in progress');\n }\n\n return new Promise<VideoRecordingResponse>((resolve, reject) => {\n this.recordingResolve = resolve;\n this.recordingReject = reject;\n this.mediaRecorder?.stop();\n });\n }\n\n /**\n * Flip between front and back camera\n */\n public async flipCamera(): Promise<void> {\n if (!this.#isRunning) {\n throw new Error('Camera is not running');\n }\n\n try {\n // Switch current camera\n this.currentCamera = this.currentCamera === 'user' ? 'environment' : 'user';\n\n // Stop current stream\n if (this.stream) {\n this.stream.getTracks().forEach((track) => track.stop());\n }\n\n // Restart with new facing mode\n const constraints: MediaStreamConstraints = {\n video: {\n facingMode: this.currentCamera,\n },\n audio: false,\n };\n\n this.stream = await navigator.mediaDevices.getUserMedia(constraints);\n\n if (this.videoElement) {\n this.videoElement.srcObject = this.stream;\n }\n } catch (err) {\n throw new Error(`Failed to flip camera: ${this.formatError(err)}`);\n }\n }\n\n /**\n * Get available camera devices\n */\n public async getAvailableDevices(): Promise<GetAvailableDevicesResponse> {\n try {\n const devices = await navigator.mediaDevices.enumerateDevices();\n const videoDevices = devices.filter((device) => device.kind === 'videoinput');\n\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 } catch (err) {\n console.error('Failed to get available devices', err);\n return { devices: [] };\n }\n }\n\n /**\n * Get current zoom information (web has limited zoom support)\n */\n public async getZoom(): Promise<GetZoomResponse> {\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 /**\n * Set zoom level (limited support in web)\n */\n public async setZoom(options: { level: number; ramp?: boolean }): Promise<void> {\n // Store the requested zoom level\n this.currentZoom = options.level;\n\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 /**\n * Get current flash mode\n */\n public async getFlashMode(): Promise<GetFlashModeResponse> {\n return { flashMode: this.currentFlashMode };\n }\n\n /**\n * Get supported flash modes\n */\n public async getSupportedFlashModes(): Promise<GetSupportedFlashModesResponse> {\n // Web has limited flash control\n return { flashModes: ['off'] };\n }\n\n /**\n * Set flash mode (limited support in web)\n */\n public async setFlashMode(options: { mode: FlashMode }): Promise<void> {\n this.currentFlashMode = options.mode;\n console.warn('Flash mode control is not fully supported in the web implementation');\n }\n\n /**\n * Check if torch is available (not supported in web)\n */\n public async isTorchAvailable(): Promise<IsTorchAvailableResponse> {\n // Torch is not supported in web implementation\n return { available: false };\n }\n\n /**\n * Get torch mode (not supported in web)\n */\n public async getTorchMode(): Promise<GetTorchModeResponse> {\n // Torch is not supported in web implementation\n return { enabled: false, level: 0.0 };\n }\n\n /**\n * Set torch mode (not supported in web)\n */\n public async setTorchMode(): Promise<void> {\n // Torch is not supported in web implementation\n throw this.unimplemented('Torch control is not supported in web implementation.');\n }\n\n /**\n * Check camera and microphone permission without requesting\n */\n public async checkPermissions(): Promise<PermissionStatus> {\n try {\n // Use Permissions API if available\n if (navigator.permissions) {\n const [cameraResult, microphoneResult] = await Promise.all([\n navigator.permissions.query({ name: 'camera' as PermissionName }),\n navigator.permissions.query({ name: 'microphone' as PermissionName }),\n ]);\n return {\n camera: cameraResult.state === 'granted' ? 'granted' : cameraResult.state === 'denied' ? 'denied' : 'prompt',\n microphone:\n microphoneResult.state === 'granted'\n ? 'granted'\n : microphoneResult.state === 'denied'\n ? 'denied'\n : 'prompt',\n };\n }\n\n // If Permissions API is not available, fall back to checking the active stream\n return {\n camera: this.stream ? 'granted' : 'prompt',\n microphone: 'prompt',\n };\n } catch (err) {\n // If permissions API is not supported or fails\n return {\n camera: 'prompt',\n microphone: 'prompt',\n };\n }\n }\n\n /**\n * Request camera and/or microphone permissions from the user.\n * By default, only camera permission is requested.\n */\n public async requestPermissions(options?: { permissions?: CameraPermissionType[] }): Promise<PermissionStatus> {\n const permissions = options?.permissions ?? ['camera'];\n const result: PermissionStatus = { camera: 'prompt', microphone: 'prompt' };\n\n // Request camera permission if included\n if (permissions.includes('camera')) {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ video: true });\n stream.getTracks().forEach((track) => track.stop());\n result.camera = 'granted';\n } catch {\n result.camera = 'denied';\n }\n } else {\n // Still report current status even if not requesting\n result.camera = (await this.checkPermissions()).camera;\n }\n\n // Request microphone permission only if explicitly included\n if (permissions.includes('microphone')) {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n stream.getTracks().forEach((track) => track.stop());\n result.microphone = 'granted';\n } catch {\n result.microphone = 'denied';\n }\n } else {\n result.microphone = (await this.checkPermissions()).microphone;\n }\n\n return result;\n }\n\n /**\n * Start barcode detection if supported\n */\n private async startBarcodeDetection() {\n const barcodeDetector = this.barcodeDetector;\n const videoElement = this.videoElement;\n\n if (!this.barcodeDetectionSupported || !barcodeDetector || !videoElement) {\n return;\n }\n\n // Make sure video is fully loaded before starting detection\n if (videoElement.readyState < 2) {\n await new Promise<void>((resolve) => {\n const loadHandler = () => {\n videoElement.removeEventListener('loadeddata', loadHandler);\n resolve();\n };\n videoElement.addEventListener('loadeddata', loadHandler);\n });\n }\n\n // Add throttling to reduce CPU usage\n let lastDetectionTime = 0;\n const minTimeBetweenDetections = 100; // ms\n\n // Set up periodic frame analysis for barcode detection\n const detectFrame = async () => {\n if (!this.#isRunning || !videoElement || !barcodeDetector) {\n return;\n }\n\n const now = Date.now();\n if (now - lastDetectionTime >= minTimeBetweenDetections) {\n try {\n const barcodes = await barcodeDetector.detect(videoElement);\n lastDetectionTime = now;\n\n if (barcodes.length > 0) {\n const barcode = barcodes[0];\n\n // Transform barcode coordinates using the utility function\n const boundingRect = transformBarcodeBoundingBox(barcode.boundingBox, videoElement);\n\n this.notifyListeners('barcodeDetected', {\n value: barcode.rawValue,\n type: barcode.format.toLowerCase(),\n boundingRect,\n });\n }\n } catch (err) {\n console.error('Barcode detection error', err);\n }\n }\n\n if (this.#isRunning) {\n requestAnimationFrame(detectFrame);\n }\n };\n\n requestAnimationFrame(detectFrame);\n }\n\n /**\n * Clean up resources when the plugin is disposed\n */\n public async handleOnDestroy(): Promise<void> {\n await this.stop();\n\n // Remove elements from DOM\n if (this.videoElement?.parentNode) {\n this.videoElement.parentNode.removeChild(this.videoElement);\n this.videoElement = null;\n }\n\n if (this.canvasElement) {\n this.canvasElement = null;\n }\n\n this.barcodeDetector = null;\n }\n\n /**\n * Check if barcode detection is supported in this browser\n */\n private async checkBarcodeDetectionSupport() {\n if ('BarcodeDetector' in window) {\n try {\n this.barcodeDetector = new BarcodeDetector();\n this.barcodeDetectionSupported = true;\n } catch (e) {\n console.warn('BarcodeDetector is not supported by this browser.');\n this.barcodeDetectionSupported = false;\n }\n }\n }\n\n /**\n * Configure the barcode detector with requested barcode formats.\n * Unsupported formats are ignored and logged.\n */\n private async configureBarcodeDetector(barcodeTypes?: BarcodeType[]): Promise<void> {\n if (!this.barcodeDetectionSupported) {\n return;\n }\n\n if (!barcodeTypes?.length) {\n this.barcodeDetector = new BarcodeDetector();\n return;\n }\n\n const requestedFormats = barcodeTypes\n .map((barcodeType) => {\n const webFormat = BARCODE_TYPE_TO_WEB_FORMAT[barcodeType];\n if (!webFormat) {\n console.warn(`[CameraView] Barcode type \"${barcodeType}\" is not supported by the web BarcodeDetector API.`);\n }\n return webFormat;\n })\n .filter((format): format is BarcodeFormat => format !== null);\n\n if (!requestedFormats.length) {\n console.warn(\n '[CameraView] No requested barcode types are supported on web. Falling back to all supported formats.',\n );\n this.barcodeDetector = new BarcodeDetector();\n return;\n }\n\n const uniqueRequestedFormats = Array.from(new Set(requestedFormats));\n\n try {\n const supportedFormats = await BarcodeDetector.getSupportedFormats();\n const configuredFormats = uniqueRequestedFormats.filter((format) => supportedFormats.includes(format));\n const ignoredFormats = uniqueRequestedFormats.filter((format) => !supportedFormats.includes(format));\n\n if (ignoredFormats.length) {\n console.warn(\n `[CameraView] Ignoring unsupported barcode formats for this browser: ${ignoredFormats.join(', ')}.`,\n );\n }\n\n if (!configuredFormats.length) {\n console.warn(\n '[CameraView] No requested barcode formats are available in this browser. Falling back to all supported formats.',\n );\n this.barcodeDetector = new BarcodeDetector();\n return;\n }\n\n this.barcodeDetector = new BarcodeDetector({ formats: configuredFormats });\n } catch (error) {\n console.warn(\n '[CameraView] Failed to resolve supported barcode formats; falling back to unfiltered detector.',\n error,\n );\n this.barcodeDetector = new BarcodeDetector();\n }\n }\n\n /**\n * Set up the video element for the camera view\n */\n private async setupVideoElement(containerElementId?: string) {\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\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 } else {\n // Otherwise, append to body as fallback\n document.body.appendChild(this.videoElement);\n }\n }\n\n /**\n * Ensures canvas element exists and returns it\n */\n private getCanvasElement(): HTMLCanvasElement {\n if (!this.canvasElement) {\n this.canvasElement = document.createElement('canvas');\n }\n return this.canvasElement;\n }\n\n /**\n * Format error message\n */\n private formatError(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n }\n}\n"]}
|
package/dist/plugin.cjs.js
CHANGED
|
@@ -164,6 +164,12 @@ class CameraViewWeb extends core.WebPlugin {
|
|
|
164
164
|
// Barcode detection support
|
|
165
165
|
this.barcodeDetectionSupported = false;
|
|
166
166
|
this.barcodeDetector = null;
|
|
167
|
+
// Recording state
|
|
168
|
+
this.mediaRecorder = null;
|
|
169
|
+
this.recordedChunks = [];
|
|
170
|
+
this.recordingAudioTrack = null;
|
|
171
|
+
this.recordingResolve = null;
|
|
172
|
+
this.recordingReject = null;
|
|
167
173
|
this.checkBarcodeDetectionSupport();
|
|
168
174
|
}
|
|
169
175
|
/**
|
|
@@ -223,10 +229,25 @@ class CameraViewWeb extends core.WebPlugin {
|
|
|
223
229
|
* Stop the camera and release resources
|
|
224
230
|
*/
|
|
225
231
|
async stop() {
|
|
232
|
+
var _a;
|
|
226
233
|
if (!__classPrivateFieldGet(this, _CameraViewWeb_isRunning, "f")) {
|
|
227
234
|
return;
|
|
228
235
|
}
|
|
229
236
|
try {
|
|
237
|
+
// Stop any active recording
|
|
238
|
+
if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
|
|
239
|
+
// Reject any pending stopRecording promise since we're force-stopping
|
|
240
|
+
(_a = this.recordingReject) === null || _a === void 0 ? void 0 : _a.call(this, new Error('Camera session stopped while recording'));
|
|
241
|
+
this.recordingResolve = null;
|
|
242
|
+
this.recordingReject = null;
|
|
243
|
+
this.mediaRecorder.stop();
|
|
244
|
+
this.mediaRecorder = null;
|
|
245
|
+
}
|
|
246
|
+
this.recordedChunks = [];
|
|
247
|
+
if (this.recordingAudioTrack) {
|
|
248
|
+
this.recordingAudioTrack.stop();
|
|
249
|
+
this.recordingAudioTrack = null;
|
|
250
|
+
}
|
|
230
251
|
// Stop all tracks in the stream
|
|
231
252
|
if (this.stream) {
|
|
232
253
|
this.stream.getTracks().forEach((track) => track.stop());
|
|
@@ -291,6 +312,94 @@ class CameraViewWeb extends core.WebPlugin {
|
|
|
291
312
|
async captureSample(options) {
|
|
292
313
|
return this.capture(options);
|
|
293
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* Start recording video using MediaRecorder API
|
|
317
|
+
*/
|
|
318
|
+
async startRecording(options) {
|
|
319
|
+
if (!__classPrivateFieldGet(this, _CameraViewWeb_isRunning, "f") || !this.videoElement) {
|
|
320
|
+
throw new Error('Camera is not running');
|
|
321
|
+
}
|
|
322
|
+
if (this.mediaRecorder) {
|
|
323
|
+
throw new Error('Recording is already in progress');
|
|
324
|
+
}
|
|
325
|
+
try {
|
|
326
|
+
let stream = this.stream;
|
|
327
|
+
// If audio is requested, get a new stream with audio track
|
|
328
|
+
if ((options === null || options === void 0 ? void 0 : options.enableAudio) && stream) {
|
|
329
|
+
const audioStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
330
|
+
const audioTrack = audioStream.getAudioTracks()[0];
|
|
331
|
+
this.recordingAudioTrack = audioTrack;
|
|
332
|
+
const videoTracks = stream.getVideoTracks();
|
|
333
|
+
stream = new MediaStream([...videoTracks, audioTrack]);
|
|
334
|
+
}
|
|
335
|
+
if (!stream) {
|
|
336
|
+
throw new Error('No camera stream available');
|
|
337
|
+
}
|
|
338
|
+
this.recordedChunks = [];
|
|
339
|
+
const mimeType = ['video/webm;codecs=vp9', 'video/webm', 'video/mp4'].find((type) => MediaRecorder.isTypeSupported(type));
|
|
340
|
+
if (!mimeType) {
|
|
341
|
+
throw new Error('No supported video recording format found');
|
|
342
|
+
}
|
|
343
|
+
this.mediaRecorder = new MediaRecorder(stream, { mimeType });
|
|
344
|
+
this.mediaRecorder.ondataavailable = (event) => {
|
|
345
|
+
if (event.data && event.data.size > 0) {
|
|
346
|
+
this.recordedChunks.push(event.data);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
this.mediaRecorder.onstop = () => {
|
|
350
|
+
var _a;
|
|
351
|
+
// Stop audio track if it was added for recording
|
|
352
|
+
if (this.recordingAudioTrack) {
|
|
353
|
+
this.recordingAudioTrack.stop();
|
|
354
|
+
this.recordingAudioTrack = null;
|
|
355
|
+
}
|
|
356
|
+
const blob = new Blob(this.recordedChunks, { type: mimeType });
|
|
357
|
+
const url = URL.createObjectURL(blob);
|
|
358
|
+
this.recordedChunks = [];
|
|
359
|
+
this.mediaRecorder = null;
|
|
360
|
+
(_a = this.recordingResolve) === null || _a === void 0 ? void 0 : _a.call(this, { webPath: url });
|
|
361
|
+
this.recordingResolve = null;
|
|
362
|
+
this.recordingReject = null;
|
|
363
|
+
};
|
|
364
|
+
this.mediaRecorder.onerror = (event) => {
|
|
365
|
+
var _a, _b, _c;
|
|
366
|
+
if (this.recordingAudioTrack) {
|
|
367
|
+
this.recordingAudioTrack.stop();
|
|
368
|
+
this.recordingAudioTrack = null;
|
|
369
|
+
}
|
|
370
|
+
this.mediaRecorder = null;
|
|
371
|
+
this.recordedChunks = [];
|
|
372
|
+
const errorMessage = (_b = (_a = event.error) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : 'Unknown recording error';
|
|
373
|
+
(_c = this.recordingReject) === null || _c === void 0 ? void 0 : _c.call(this, new Error('Recording error: ' + errorMessage));
|
|
374
|
+
this.recordingResolve = null;
|
|
375
|
+
this.recordingReject = null;
|
|
376
|
+
};
|
|
377
|
+
this.mediaRecorder.start(100); // Collect data in 100ms chunks
|
|
378
|
+
}
|
|
379
|
+
catch (err) {
|
|
380
|
+
if (this.recordingAudioTrack) {
|
|
381
|
+
this.recordingAudioTrack.stop();
|
|
382
|
+
this.recordingAudioTrack = null;
|
|
383
|
+
}
|
|
384
|
+
this.mediaRecorder = null;
|
|
385
|
+
this.recordedChunks = [];
|
|
386
|
+
throw new Error(`Failed to start recording: ${this.formatError(err)}`);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Stop the current video recording
|
|
391
|
+
*/
|
|
392
|
+
async stopRecording() {
|
|
393
|
+
if (!this.mediaRecorder) {
|
|
394
|
+
throw new Error('No recording is in progress');
|
|
395
|
+
}
|
|
396
|
+
return new Promise((resolve, reject) => {
|
|
397
|
+
var _a;
|
|
398
|
+
this.recordingResolve = resolve;
|
|
399
|
+
this.recordingReject = reject;
|
|
400
|
+
(_a = this.mediaRecorder) === null || _a === void 0 ? void 0 : _a.stop();
|
|
401
|
+
});
|
|
402
|
+
}
|
|
294
403
|
/**
|
|
295
404
|
* Flip between front and back camera
|
|
296
405
|
*/
|
|
@@ -409,45 +518,77 @@ class CameraViewWeb extends core.WebPlugin {
|
|
|
409
518
|
throw this.unimplemented('Torch control is not supported in web implementation.');
|
|
410
519
|
}
|
|
411
520
|
/**
|
|
412
|
-
* Check camera permission without requesting
|
|
521
|
+
* Check camera and microphone permission without requesting
|
|
413
522
|
*/
|
|
414
523
|
async checkPermissions() {
|
|
415
524
|
try {
|
|
416
525
|
// Use Permissions API if available
|
|
417
526
|
if (navigator.permissions) {
|
|
418
|
-
const
|
|
527
|
+
const [cameraResult, microphoneResult] = await Promise.all([
|
|
528
|
+
navigator.permissions.query({ name: 'camera' }),
|
|
529
|
+
navigator.permissions.query({ name: 'microphone' }),
|
|
530
|
+
]);
|
|
419
531
|
return {
|
|
420
|
-
camera:
|
|
532
|
+
camera: cameraResult.state === 'granted' ? 'granted' : cameraResult.state === 'denied' ? 'denied' : 'prompt',
|
|
533
|
+
microphone: microphoneResult.state === 'granted'
|
|
534
|
+
? 'granted'
|
|
535
|
+
: microphoneResult.state === 'denied'
|
|
536
|
+
? 'denied'
|
|
537
|
+
: 'prompt',
|
|
421
538
|
};
|
|
422
539
|
}
|
|
423
|
-
// If Permissions API is not available,
|
|
540
|
+
// If Permissions API is not available, fall back to checking the active stream
|
|
424
541
|
return {
|
|
425
542
|
camera: this.stream ? 'granted' : 'prompt',
|
|
543
|
+
microphone: 'prompt',
|
|
426
544
|
};
|
|
427
545
|
}
|
|
428
546
|
catch (err) {
|
|
429
547
|
// If permissions API is not supported or fails
|
|
430
548
|
return {
|
|
431
549
|
camera: 'prompt',
|
|
550
|
+
microphone: 'prompt',
|
|
432
551
|
};
|
|
433
552
|
}
|
|
434
553
|
}
|
|
435
554
|
/**
|
|
436
|
-
* Request camera
|
|
555
|
+
* Request camera and/or microphone permissions from the user.
|
|
556
|
+
* By default, only camera permission is requested.
|
|
437
557
|
*/
|
|
438
|
-
async requestPermissions() {
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
558
|
+
async requestPermissions(options) {
|
|
559
|
+
var _a;
|
|
560
|
+
const permissions = (_a = options === null || options === void 0 ? void 0 : options.permissions) !== null && _a !== void 0 ? _a : ['camera'];
|
|
561
|
+
const result = { camera: 'prompt', microphone: 'prompt' };
|
|
562
|
+
// Request camera permission if included
|
|
563
|
+
if (permissions.includes('camera')) {
|
|
564
|
+
try {
|
|
565
|
+
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
|
566
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
567
|
+
result.camera = 'granted';
|
|
568
|
+
}
|
|
569
|
+
catch (_b) {
|
|
570
|
+
result.camera = 'denied';
|
|
571
|
+
}
|
|
446
572
|
}
|
|
447
|
-
|
|
448
|
-
//
|
|
449
|
-
|
|
573
|
+
else {
|
|
574
|
+
// Still report current status even if not requesting
|
|
575
|
+
result.camera = (await this.checkPermissions()).camera;
|
|
576
|
+
}
|
|
577
|
+
// Request microphone permission only if explicitly included
|
|
578
|
+
if (permissions.includes('microphone')) {
|
|
579
|
+
try {
|
|
580
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
581
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
582
|
+
result.microphone = 'granted';
|
|
583
|
+
}
|
|
584
|
+
catch (_c) {
|
|
585
|
+
result.microphone = 'denied';
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
else {
|
|
589
|
+
result.microphone = (await this.checkPermissions()).microphone;
|
|
450
590
|
}
|
|
591
|
+
return result;
|
|
451
592
|
}
|
|
452
593
|
/**
|
|
453
594
|
* Start barcode detection if supported
|